From e8ab52a2a36ce6129a8cd501a930284057e4b678 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20Br=C3=BCbach?= <44814898+BruebachL@users.noreply.github.com> Date: Sun, 8 Nov 2026 19:34:41 +0100 Subject: [PATCH] [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. --- .../widgets/dialogs/dlg_sync_onboarding.cpp | 12 +++--- .../widgets/dialogs/dlg_sync_onboarding.h | 5 ++- .../widgets/tabs/tab_deck_storage.cpp | 39 +++++++++++++++++++ .../interface/widgets/tabs/tab_deck_storage.h | 2 + 4 files changed, 51 insertions(+), 7 deletions(-) diff --git a/cockatrice/src/interface/widgets/dialogs/dlg_sync_onboarding.cpp b/cockatrice/src/interface/widgets/dialogs/dlg_sync_onboarding.cpp index ae0e9446b..23bf663d5 100644 --- a/cockatrice/src/interface/widgets/dialogs/dlg_sync_onboarding.cpp +++ b/cockatrice/src/interface/widgets/dialogs/dlg_sync_onboarding.cpp @@ -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")); } diff --git a/cockatrice/src/interface/widgets/dialogs/dlg_sync_onboarding.h b/cockatrice/src/interface/widgets/dialogs/dlg_sync_onboarding.h index 9d811a28e..c11577fba 100644 --- a/cockatrice/src/interface/widgets/dialogs/dlg_sync_onboarding.h +++ b/cockatrice/src/interface/widgets/dialogs/dlg_sync_onboarding.h @@ -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 diff --git a/cockatrice/src/interface/widgets/tabs/tab_deck_storage.cpp b/cockatrice/src/interface/widgets/tabs/tab_deck_storage.cpp index 6a5093e0b..2c0fbd4f8 100644 --- a/cockatrice/src/interface/widgets/tabs/tab_deck_storage.cpp +++ b/cockatrice/src/interface/widgets/tabs/tab_deck_storage.cpp @@ -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); diff --git a/cockatrice/src/interface/widgets/tabs/tab_deck_storage.h b/cockatrice/src/interface/widgets/tabs/tab_deck_storage.h index 610cce891..9889e9a51 100644 --- a/cockatrice/src/interface/widgets/tabs/tab_deck_storage.h +++ b/cockatrice/src/interface/widgets/tabs/tab_deck_storage.h @@ -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);