[DeckSync] Say the onboarding choice persists and let the user reset it

The first-sync verdict is stored in the sidecar, so "Upload mine" and
"Download server" apply to every later sync, not just the first one.
Say so in the dialog, label the one-way options as one-way, and add a
"Reset sync direction" action to the deck storage tab that writes the
linked (two-way) mode back to the sidecar.
This commit is contained in:
Lukas Brübach 2026-11-08 19:34:41 +01:00
parent b10abad3a3
commit e8ab52a2a3
4 changed files with 51 additions and 7 deletions

View file

@ -35,12 +35,14 @@ DlgSyncOnboarding::DlgSyncOnboarding(int _localCount, int _serverCount, QWidget
void DlgSyncOnboarding::retranslateUi()
{
setWindowTitle(tr("Sync decks to this server"));
detailLabel->setText(tr("You have %1 local decks and %2 decks stored on this server. How should they be combined?")
.arg(localCount)
.arg(serverCount));
detailLabel->setText(
tr("You have %1 local decks and %2 decks stored on this server. This choice sets how decks sync with this "
"server from now on; you can reset it to two-way sync from the deck storage tab.")
.arg(localCount)
.arg(serverCount));
mergeRadio->setText(tr("Merge: keep everything, sync both ways (recommended)"));
uploadRadio->setText(tr("Upload mine: push local decks to the server"));
downloadRadio->setText(tr("Download server: take the server decks"));
uploadRadio->setText(tr("Upload mine only: push local decks to the server, never download"));
downloadRadio->setText(tr("Download server only: take the server decks, never upload"));
separateRadio->setText(tr("Keep separate: do not sync with this server"));
}

View file

@ -17,8 +17,9 @@ class QRadioButton;
* @class DlgSyncOnboarding
* @brief Asks how to combine a non-empty local collection with a non-empty server collection.
*
* Merge (two-way) is the default. Upload and download restrict the run to one
* direction; keeping separate records the profile as sync-disabled.
* Merge (two-way) is the default. Upload and download restrict sync to one
* direction for this server from then on; keeping separate records the profile
* as sync-disabled. The deck storage tab can reset the direction to two-way.
* Rejected dialogs cancel the sync without recording anything.
*/
class DlgSyncOnboarding : public QDialog

View file

@ -5,6 +5,7 @@
#include "../../deck_sync/deck_sync_conflict.h"
#include "../../deck_sync/deck_sync_manager.h"
#include "../../deck_sync/deck_sync_profile.h"
#include "../../deck_sync/deck_sync_sidecar.h"
#include "../../theme_icon.h"
#include "../cards/additional_info/deck_color_identity.h"
#include "../deck_share/deck_share_utils.h"
@ -241,6 +242,9 @@ TabDeckStorage::TabDeckStorage(TabSupervisor *_tabSupervisor,
aSyncNow = new QAction(this);
aSyncNow->setIcon(qApp->style()->standardIcon(QStyle::SP_BrowserReload));
connect(aSyncNow, &QAction::triggered, this, &TabDeckStorage::actSyncNow);
aSyncReset = new QAction(this);
aSyncReset->setIcon(qApp->style()->standardIcon(QStyle::SP_DialogResetButton));
connect(aSyncReset, &QAction::triggered, this, &TabDeckStorage::actResetSyncDirection);
// Add actions to toolbars
leftToolBar->addAction(aOpenLocalDeck);
@ -254,6 +258,7 @@ TabDeckStorage::TabDeckStorage(TabSupervisor *_tabSupervisor,
rightToolBar->addAction(aOpenRemoteDeck);
rightToolBar->addAction(aDownload);
rightToolBar->addAction(aSyncNow);
rightToolBar->addAction(aSyncReset);
rightToolBar->addAction(aShareDecks);
rightToolBar->addAction(aPublishDeck);
rightToolBar->addAction(aNewFolder);
@ -301,6 +306,7 @@ void TabDeckStorage::retranslateUi()
aPublishDeck->setText(tr("Publish/unpublish deck"));
aOpenDecksFolder->setText(tr("Open decks folder"));
aSyncNow->setText(tr("Sync now"));
aSyncReset->setText(tr("Reset sync direction"));
renderSyncStatus();
shareBar->retranslateUi();
if (shareBar->isVisible()) {
@ -356,6 +362,7 @@ void TabDeckStorage::setRemoteEnabled(bool enabled)
aNewFolder->setEnabled(enabled);
aDeleteRemoteDeck->setEnabled(enabled);
aSyncNow->setEnabled(enabled && !syncManager->isSyncing());
aSyncReset->setEnabled(enabled && !syncManager->isSyncing());
if (enabled) {
serverDirView->refreshTree();
@ -565,9 +572,41 @@ void TabDeckStorage::actSyncNow()
syncManager->setProfile(profile);
syncManager->setDeckPath(SettingsCache::instance().paths().getDeckPath());
aSyncNow->setEnabled(false);
aSyncReset->setEnabled(false);
syncManager->syncNow();
}
void TabDeckStorage::actResetSyncDirection()
{
const DeckSyncProfile profile(client->serverName(), client->serverPort(), syncUsername);
if (!profile.isValid()) {
return;
}
const QString deckPath = SettingsCache::instance().paths().getDeckPath();
DeckSyncSidecar sidecar(DeckSyncSidecar::pathForDeckPath(deckPath, profile.key()));
if (!sidecar.load()) {
QMessageBox::warning(this, tr("Deck sync"), tr("Could not read the sync state for this server."));
return;
}
if (sidecar.mode == DeckSyncSidecar::MODE_LINKED) {
QMessageBox::information(this, tr("Deck sync"), tr("This server already syncs decks both ways."));
return;
}
if (QMessageBox::question(this, tr("Deck sync"),
tr("Switch this server back to two-way deck sync? The next sync merges the local and "
"server decks."),
QMessageBox::Yes | QMessageBox::No) != QMessageBox::Yes) {
return;
}
sidecar.mode = DeckSyncSidecar::MODE_LINKED;
if (!sidecar.save()) {
QMessageBox::warning(this, tr("Deck sync"), tr("Could not save the sync direction."));
return;
}
QMessageBox::information(this, tr("Deck sync"),
tr("Deck sync is set to two-way. Run Sync now to merge the decks."));
}
void TabDeckStorage::onFirstSyncDetected(int localCount, int serverCount)
{
DlgSyncOnboarding dialog(localCount, serverCount, this);

View file

@ -55,6 +55,7 @@ private:
QAction *aOpenDecksFolder;
QAction *aOpenRemoteDeck, *aDownload, *aShareDecks, *aPublishDeck, *aNewFolder, *aDeleteRemoteDeck;
QAction *aSyncNow;
QAction *aSyncReset;
QLabel *syncStatusLabel;
DeckSyncManager *syncManager;
QString syncUsername;
@ -130,6 +131,7 @@ private slots:
void deleteDeckFinished(const Response &response, const CommandContainer &commandContainer);
void actSyncNow();
void actResetSyncDirection();
void onFirstSyncDetected(int localCount, int serverCount);
void onConflictRequest(const DeckSyncConflict &conflict);
void onSyncStatusMessage(const QString &message);