mirror of
https://github.com/Cockatrice/Cockatrice.git
synced 2026-11-06 01:53:25 -08:00
[Client] Sync conflict dialog, onboarding and conflict default
This commit is contained in:
parent
b0f5cb4c42
commit
339d712308
21 changed files with 1020 additions and 48 deletions
178
cockatrice/src/interface/widgets/dialogs/dlg_sync_conflict.cpp
Normal file
178
cockatrice/src/interface/widgets/dialogs/dlg_sync_conflict.cpp
Normal file
|
|
@ -0,0 +1,178 @@
|
|||
#include "dlg_sync_conflict.h"
|
||||
|
||||
#include <QCheckBox>
|
||||
#include <QDateTime>
|
||||
#include <QDialogButtonBox>
|
||||
#include <QFile>
|
||||
#include <QLabel>
|
||||
#include <QListWidget>
|
||||
#include <QLocale>
|
||||
#include <QRadioButton>
|
||||
#include <QVBoxLayout>
|
||||
#include <libcockatrice/deck_list/deck_diff.h>
|
||||
#include <libcockatrice/deck_list/deck_list.h>
|
||||
#include <libcockatrice/deck_list/tree/deck_list_card_node.h>
|
||||
#include <libcockatrice/deck_list/tree/inner_deck_list_node.h>
|
||||
|
||||
namespace
|
||||
{
|
||||
QString formatTime(qint64 secsSinceEpoch)
|
||||
{
|
||||
return QLocale().toString(QDateTime::fromSecsSinceEpoch(secsSinceEpoch), QLocale::ShortFormat);
|
||||
}
|
||||
|
||||
int zoneCardCount(const DeckList &deck, const QString &zone)
|
||||
{
|
||||
int count = 0;
|
||||
deck.forEachCard([&count, &zone](InnerDecklistNode *zoneNode, DecklistCardNode *card) {
|
||||
if (zoneNode->getName() == zone) {
|
||||
count += card->getNumber();
|
||||
}
|
||||
});
|
||||
return count;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
DlgSyncConflict::DlgSyncConflict(const DeckSyncConflict &_conflict, QWidget *parent)
|
||||
: QDialog(parent), conflict(_conflict)
|
||||
{
|
||||
auto *layout = new QVBoxLayout(this);
|
||||
|
||||
titleLabel = new QLabel(this);
|
||||
detailLabel = new QLabel(this);
|
||||
detailLabel->setWordWrap(true);
|
||||
keepMineRadio = new QRadioButton(this);
|
||||
keepServerRadio = new QRadioButton(this);
|
||||
keepBothRadio = new QRadioButton(this);
|
||||
mergeRadio = new QRadioButton(this);
|
||||
changesList = new QListWidget(this);
|
||||
mergeSummaryLabel = new QLabel(this);
|
||||
mergeSummaryLabel->setWordWrap(true);
|
||||
rememberSessionBox = new QCheckBox(this);
|
||||
rememberAlwaysBox = new QCheckBox(this);
|
||||
|
||||
layout->addWidget(titleLabel);
|
||||
layout->addWidget(detailLabel);
|
||||
layout->addWidget(keepMineRadio);
|
||||
layout->addWidget(keepServerRadio);
|
||||
layout->addWidget(keepBothRadio);
|
||||
layout->addWidget(mergeRadio);
|
||||
layout->addWidget(changesList);
|
||||
layout->addWidget(mergeSummaryLabel);
|
||||
layout->addWidget(rememberSessionBox);
|
||||
layout->addWidget(rememberAlwaysBox);
|
||||
|
||||
auto *buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this);
|
||||
connect(buttons, &QDialogButtonBox::accepted, this, &QDialog::accept);
|
||||
connect(buttons, &QDialogButtonBox::rejected, this, &QDialog::reject);
|
||||
layout->addWidget(buttons);
|
||||
|
||||
DeckList localDeck;
|
||||
DeckList serverDeck;
|
||||
QFile localFile(conflict.localAbsolutePath);
|
||||
decksParsed = localFile.open(QIODevice::ReadOnly) && localDeck.loadFromFile_Native(&localFile) &&
|
||||
serverDeck.loadFromString_Native(conflict.serverDeckString);
|
||||
localIsNewer = conflict.localSecs >= conflict.serverSecs;
|
||||
|
||||
if (decksParsed) {
|
||||
shownChanges = DeckDiff::diff(localDeck, serverDeck);
|
||||
if (conflict.hasBase) {
|
||||
DeckList baseDeck;
|
||||
if (baseDeck.loadFromString_Native(conflict.baseDeckString)) {
|
||||
const DeckDiffResult localChanges = DeckDiff::diff(baseDeck, localDeck);
|
||||
const DeckDiffResult remoteChanges = DeckDiff::diff(baseDeck, serverDeck);
|
||||
mergeable = DeckDiff::canAutoMerge(localChanges, remoteChanges);
|
||||
if (mergeable) {
|
||||
const DeckList merged = DeckDiff::merge(baseDeck, localChanges, remoteChanges);
|
||||
mergedMainCount = zoneCardCount(merged, "main");
|
||||
mergedSideCount = zoneCardCount(merged, "side");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (localIsNewer) {
|
||||
keepMineRadio->setChecked(true);
|
||||
} else {
|
||||
keepServerRadio->setChecked(true);
|
||||
}
|
||||
mergeRadio->setEnabled(mergeable);
|
||||
|
||||
connect(keepMineRadio, &QRadioButton::toggled, this, &DlgSyncConflict::updateRememberBoxes);
|
||||
connect(keepServerRadio, &QRadioButton::toggled, this, &DlgSyncConflict::updateRememberBoxes);
|
||||
connect(keepBothRadio, &QRadioButton::toggled, this, &DlgSyncConflict::updateRememberBoxes);
|
||||
connect(mergeRadio, &QRadioButton::toggled, this, &DlgSyncConflict::updateRememberBoxes);
|
||||
updateRememberBoxes();
|
||||
retranslateUi();
|
||||
}
|
||||
|
||||
void DlgSyncConflict::retranslateUi()
|
||||
{
|
||||
setWindowTitle(tr("Sync conflict"));
|
||||
titleLabel->setText(conflict.relativePath);
|
||||
if (!decksParsed) {
|
||||
detailLabel->setText(tr("This deck changed on both sides, but one version could not be read."));
|
||||
} else {
|
||||
detailLabel->setText(tr("This deck changed both on this device and on the server."));
|
||||
}
|
||||
|
||||
keepMineRadio->setText(
|
||||
tr("Keep my deck (%1%2)").arg(formatTime(conflict.localSecs), localIsNewer ? tr(", newest") : QString()));
|
||||
keepServerRadio->setText(tr("Keep the server deck (%1%2)")
|
||||
.arg(formatTime(conflict.serverSecs), localIsNewer ? QString() : tr(", newest")));
|
||||
keepBothRadio->setText(tr("Keep both (store the server version aside)"));
|
||||
mergeRadio->setText(mergeable ? tr("Use the auto-merge") : tr("Use the auto-merge (not available)"));
|
||||
|
||||
changesList->clear();
|
||||
for (const DeckCardChange &change : shownChanges.changes) {
|
||||
changesList->addItem(
|
||||
tr("%1: %2 %3 → %4").arg(change.zone).arg(change.cardName).arg(change.oldCount).arg(change.newCount));
|
||||
}
|
||||
|
||||
if (mergeable) {
|
||||
mergeSummaryLabel->setText(tr("Auto-merge proposal: union of both sides (%1 main, %2 side).")
|
||||
.arg(mergedMainCount)
|
||||
.arg(mergedSideCount));
|
||||
} else if (!conflict.hasBase) {
|
||||
mergeSummaryLabel->setText(tr("No base snapshot exists, so the versions cannot be merged automatically."));
|
||||
} else {
|
||||
mergeSummaryLabel->setText(tr("The changes overlap, so they cannot be merged automatically."));
|
||||
}
|
||||
|
||||
rememberSessionBox->setText(tr("Remember for the rest of this sync"));
|
||||
rememberAlwaysBox->setText(tr("Always use this rule"));
|
||||
}
|
||||
|
||||
void DlgSyncConflict::updateRememberBoxes()
|
||||
{
|
||||
const bool single = keepMineRadio->isChecked() || keepServerRadio->isChecked();
|
||||
rememberSessionBox->setEnabled(single);
|
||||
rememberAlwaysBox->setEnabled(single);
|
||||
if (!single) {
|
||||
rememberSessionBox->setChecked(false);
|
||||
rememberAlwaysBox->setChecked(false);
|
||||
}
|
||||
}
|
||||
|
||||
DeckSyncResolutionAction DlgSyncConflict::selectedAction() const
|
||||
{
|
||||
if (keepServerRadio->isChecked()) {
|
||||
return DeckSyncKeepServer;
|
||||
}
|
||||
if (keepBothRadio->isChecked()) {
|
||||
return DeckSyncKeepBoth;
|
||||
}
|
||||
if (mergeRadio->isChecked()) {
|
||||
return DeckSyncUseMerge;
|
||||
}
|
||||
return DeckSyncKeepMine;
|
||||
}
|
||||
|
||||
DeckSyncResolution DlgSyncConflict::resolution() const
|
||||
{
|
||||
DeckSyncResolution result;
|
||||
result.action = selectedAction();
|
||||
result.rememberSession = rememberSessionBox->isEnabled() && rememberSessionBox->isChecked();
|
||||
result.rememberAlways = rememberAlwaysBox->isEnabled() && rememberAlwaysBox->isChecked();
|
||||
return result;
|
||||
}
|
||||
63
cockatrice/src/interface/widgets/dialogs/dlg_sync_conflict.h
Normal file
63
cockatrice/src/interface/widgets/dialogs/dlg_sync_conflict.h
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
/**
|
||||
* @file dlg_sync_conflict.h
|
||||
* @brief Dialog for resolving a deck that changed both locally and on the server.
|
||||
*/
|
||||
|
||||
#ifndef DLG_SYNC_CONFLICT_H
|
||||
#define DLG_SYNC_CONFLICT_H
|
||||
|
||||
#include "../../deck_sync/deck_sync_conflict.h"
|
||||
|
||||
#include <QDialog>
|
||||
#include <QString>
|
||||
#include <libcockatrice/deck_list/deck_diff.h>
|
||||
|
||||
class QCheckBox;
|
||||
class QLabel;
|
||||
class QListWidget;
|
||||
class QRadioButton;
|
||||
|
||||
/**
|
||||
* @class DlgSyncConflict
|
||||
* @brief Shows both versions of a conflicted deck and returns the verdict.
|
||||
*
|
||||
* Radio choice between Keep mine / Keep server (newest preselected), Keep both
|
||||
* and Use merge (enabled only when the changes touch disjoint cards). The
|
||||
* change list shows card-count deltas between the two versions; the merge
|
||||
* summary describes the auto-merge proposal. Rejected dialogs resolve as Skip.
|
||||
*/
|
||||
class DlgSyncConflict : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit DlgSyncConflict(const DeckSyncConflict &conflict, QWidget *parent = nullptr);
|
||||
[[nodiscard]] DeckSyncResolution resolution() const;
|
||||
void retranslateUi();
|
||||
|
||||
private slots:
|
||||
void updateRememberBoxes();
|
||||
|
||||
private:
|
||||
DeckSyncResolutionAction selectedAction() const;
|
||||
|
||||
DeckSyncConflict conflict;
|
||||
bool decksParsed = false;
|
||||
bool localIsNewer = true;
|
||||
DeckDiffResult shownChanges;
|
||||
bool mergeable = false;
|
||||
int mergedMainCount = 0;
|
||||
int mergedSideCount = 0;
|
||||
|
||||
QLabel *titleLabel;
|
||||
QLabel *detailLabel;
|
||||
QRadioButton *keepMineRadio;
|
||||
QRadioButton *keepServerRadio;
|
||||
QRadioButton *keepBothRadio;
|
||||
QRadioButton *mergeRadio;
|
||||
QListWidget *changesList;
|
||||
QLabel *mergeSummaryLabel;
|
||||
QCheckBox *rememberSessionBox;
|
||||
QCheckBox *rememberAlwaysBox;
|
||||
};
|
||||
|
||||
#endif // DLG_SYNC_CONFLICT_H
|
||||
|
|
@ -0,0 +1,59 @@
|
|||
#include "dlg_sync_onboarding.h"
|
||||
|
||||
#include <QDialogButtonBox>
|
||||
#include <QLabel>
|
||||
#include <QRadioButton>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
DlgSyncOnboarding::DlgSyncOnboarding(int _localCount, int _serverCount, QWidget *parent)
|
||||
: QDialog(parent), localCount(_localCount), serverCount(_serverCount)
|
||||
{
|
||||
auto *layout = new QVBoxLayout(this);
|
||||
|
||||
detailLabel = new QLabel(this);
|
||||
detailLabel->setWordWrap(true);
|
||||
mergeRadio = new QRadioButton(this);
|
||||
uploadRadio = new QRadioButton(this);
|
||||
downloadRadio = new QRadioButton(this);
|
||||
separateRadio = new QRadioButton(this);
|
||||
mergeRadio->setChecked(true);
|
||||
|
||||
layout->addWidget(detailLabel);
|
||||
layout->addWidget(mergeRadio);
|
||||
layout->addWidget(uploadRadio);
|
||||
layout->addWidget(downloadRadio);
|
||||
layout->addWidget(separateRadio);
|
||||
|
||||
auto *buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this);
|
||||
connect(buttons, &QDialogButtonBox::accepted, this, &QDialog::accept);
|
||||
connect(buttons, &QDialogButtonBox::rejected, this, &QDialog::reject);
|
||||
layout->addWidget(buttons);
|
||||
|
||||
retranslateUi();
|
||||
}
|
||||
|
||||
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));
|
||||
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"));
|
||||
separateRadio->setText(tr("Keep separate: do not sync with this server"));
|
||||
}
|
||||
|
||||
DeckSyncMode DlgSyncOnboarding::selectedMode() const
|
||||
{
|
||||
if (uploadRadio->isChecked()) {
|
||||
return DeckSyncMode::UploadOnly;
|
||||
}
|
||||
if (downloadRadio->isChecked()) {
|
||||
return DeckSyncMode::DownloadOnly;
|
||||
}
|
||||
if (separateRadio->isChecked()) {
|
||||
return DeckSyncMode::Separate;
|
||||
}
|
||||
return DeckSyncMode::TwoWay;
|
||||
}
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
/**
|
||||
* @file dlg_sync_onboarding.h
|
||||
* @brief First-sync choice when local decks and server decks meet.
|
||||
*/
|
||||
|
||||
#ifndef DLG_SYNC_ONBOARDING_H
|
||||
#define DLG_SYNC_ONBOARDING_H
|
||||
|
||||
#include "../../deck_sync/deck_sync_conflict.h"
|
||||
|
||||
#include <QDialog>
|
||||
|
||||
class QLabel;
|
||||
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.
|
||||
* Rejected dialogs cancel the sync without recording anything.
|
||||
*/
|
||||
class DlgSyncOnboarding : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
DlgSyncOnboarding(int _localCount, int _serverCount, QWidget *parent = nullptr);
|
||||
[[nodiscard]] DeckSyncMode selectedMode() const;
|
||||
void retranslateUi();
|
||||
|
||||
private:
|
||||
int localCount = 0;
|
||||
int serverCount = 0;
|
||||
|
||||
QLabel *detailLabel;
|
||||
QRadioButton *mergeRadio;
|
||||
QRadioButton *uploadRadio;
|
||||
QRadioButton *downloadRadio;
|
||||
QRadioButton *separateRadio;
|
||||
};
|
||||
|
||||
#endif // DLG_SYNC_ONBOARDING_H
|
||||
|
|
@ -27,6 +27,7 @@
|
|||
#include <QtGlobal>
|
||||
#include <libcockatrice/card/card_localization.h>
|
||||
#include <libcockatrice/settings/cards_display_settings.h>
|
||||
#include <libcockatrice/settings/deck_sync_settings.h>
|
||||
#include <libcockatrice/settings/download_settings.h>
|
||||
#include <libcockatrice/settings/paths_settings.h>
|
||||
#include <libcockatrice/settings/personal_settings.h>
|
||||
|
|
@ -359,6 +360,25 @@ GeneralSettingsPage::GeneralSettingsPage()
|
|||
pathsGroupBox = new QGroupBox;
|
||||
pathsGroupBox->setLayout(pathsGrid);
|
||||
|
||||
// deck sync settings
|
||||
conflictDefaultSelector.addItem("", DeckSyncConflictAsk);
|
||||
conflictDefaultSelector.addItem("", DeckSyncConflictKeepMine);
|
||||
conflictDefaultSelector.addItem("", DeckSyncConflictKeepServer);
|
||||
conflictDefaultSelector.addItem("", DeckSyncConflictKeepNewer);
|
||||
int conflictIndex = conflictDefaultSelector.findData(settings.deckSync().getConflictDefault());
|
||||
if (conflictIndex >= 0) {
|
||||
conflictDefaultSelector.setCurrentIndex(conflictIndex);
|
||||
}
|
||||
connect(&conflictDefaultSelector, qOverload<int>(&QComboBox::currentIndexChanged), this, [this](int index) {
|
||||
SettingsCache::instance().deckSync().setConflictDefault(conflictDefaultSelector.itemData(index).toInt());
|
||||
});
|
||||
|
||||
auto *deckSyncGrid = new QGridLayout;
|
||||
deckSyncGrid->addWidget(&conflictDefaultLabel, 0, 0);
|
||||
deckSyncGrid->addWidget(&conflictDefaultSelector, 0, 1);
|
||||
deckSyncGroupBox = new QGroupBox;
|
||||
deckSyncGroupBox->setLayout(deckSyncGrid);
|
||||
|
||||
auto *mainLayout = new QVBoxLayout;
|
||||
mainLayout->addWidget(languageGroupBox);
|
||||
mainLayout->addWidget(versionGroupBox);
|
||||
|
|
@ -366,6 +386,7 @@ GeneralSettingsPage::GeneralSettingsPage()
|
|||
mainLayout->addWidget(cardDatabaseGroupBox);
|
||||
mainLayout->addWidget(startupGroupBox);
|
||||
mainLayout->addWidget(pathsGroupBox);
|
||||
mainLayout->addWidget(deckSyncGroupBox);
|
||||
mainLayout->addStretch();
|
||||
|
||||
GeneralSettingsPage::retranslateUi();
|
||||
|
|
@ -647,5 +668,13 @@ void GeneralSettingsPage::retranslateUi()
|
|||
cardDatabasePathLabel.setText(tr("Card database:"));
|
||||
customCardDatabasePathLabel.setText(tr("Custom database directory:"));
|
||||
tokenDatabasePathLabel.setText(tr("Token database:"));
|
||||
deckSyncGroupBox->setTitle(tr("Deck sync"));
|
||||
conflictDefaultLabel.setText(tr("When a deck changed on both sides:"));
|
||||
conflictDefaultSelector.setItemText(conflictDefaultSelector.findData(DeckSyncConflictAsk), tr("Ask me every time"));
|
||||
conflictDefaultSelector.setItemText(conflictDefaultSelector.findData(DeckSyncConflictKeepMine), tr("Keep my deck"));
|
||||
conflictDefaultSelector.setItemText(conflictDefaultSelector.findData(DeckSyncConflictKeepServer),
|
||||
tr("Keep the server deck"));
|
||||
conflictDefaultSelector.setItemText(conflictDefaultSelector.findData(DeckSyncConflictKeepNewer),
|
||||
tr("Keep the newer deck"));
|
||||
resetAllPathsButton->setText(tr("Reset all paths"));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -52,6 +52,7 @@ private:
|
|||
QGroupBox *cardDatabaseGroupBox;
|
||||
QGroupBox *startupGroupBox;
|
||||
QGroupBox *pathsGroupBox;
|
||||
QGroupBox *deckSyncGroupBox;
|
||||
|
||||
QLabel languageLabel;
|
||||
QComboBox languageBox;
|
||||
|
|
@ -104,6 +105,9 @@ private:
|
|||
QLineEdit *tokenDatabasePathEdit;
|
||||
QPushButton *resetAllPathsButton;
|
||||
QLabel *allPathsResetLabel;
|
||||
|
||||
QLabel conflictDefaultLabel;
|
||||
QComboBox conflictDefaultSelector;
|
||||
};
|
||||
|
||||
#endif // COCKATRICE_GENERAL_SETTINGS_PAGE_H
|
||||
|
|
|
|||
|
|
@ -2,11 +2,15 @@
|
|||
|
||||
#include "../../../client/settings/cache_settings.h"
|
||||
#include "../../deck_loader/deck_loader.h"
|
||||
#include "../../deck_sync/deck_sync_conflict.h"
|
||||
#include "../../deck_sync/deck_sync_manager.h"
|
||||
#include "../../deck_sync/deck_sync_profile.h"
|
||||
#include "../../pixel_map_generator.h"
|
||||
#include "../cards/additional_info/deck_color_identity.h"
|
||||
#include "../deck_share/deck_share_utils.h"
|
||||
#include "../deck_share/share_bar_widget.h"
|
||||
#include "../dialogs/dlg_sync_conflict.h"
|
||||
#include "../dialogs/dlg_sync_onboarding.h"
|
||||
#include "../interface/widgets/server/remote/remote_decklist_tree_widget.h"
|
||||
#include "../interface/widgets/utility/get_text_with_max.h"
|
||||
#include "abstract_client.h"
|
||||
|
|
@ -63,6 +67,7 @@
|
|||
#include <libcockatrice/protocol/pb/response_deck_download.pb.h>
|
||||
#include <libcockatrice/protocol/pb/response_deck_upload.pb.h>
|
||||
#include <libcockatrice/protocol/pending_command.h>
|
||||
#include <libcockatrice/settings/deck_sync_settings.h>
|
||||
#include <libcockatrice/settings/network_settings.h>
|
||||
#include <libcockatrice/settings/paths_settings.h>
|
||||
#include <libcockatrice/utility/qt_utils.h>
|
||||
|
|
@ -256,8 +261,13 @@ TabDeckStorage::TabDeckStorage(TabSupervisor *_tabSupervisor,
|
|||
|
||||
syncManager = new DeckSyncManager(
|
||||
client, [](const DeckList &deck) { return getDeckColorIdentity(deck, CardDatabaseManager::query()); }, this);
|
||||
syncManager->setConflictDefault(SettingsCache::instance().deckSync().getConflictDefault());
|
||||
connect(&SettingsCache::instance().deckSync(), &DeckSyncSettings::conflictDefaultChanged, syncManager,
|
||||
&DeckSyncManager::setConflictDefault);
|
||||
connect(syncManager, &DeckSyncManager::statusMessage, this, &TabDeckStorage::onSyncStatusMessage);
|
||||
connect(syncManager, &DeckSyncManager::syncFinished, this, &TabDeckStorage::onSyncFinished);
|
||||
connect(syncManager, &DeckSyncManager::firstSyncDetected, this, &TabDeckStorage::onFirstSyncDetected);
|
||||
connect(syncManager, &DeckSyncManager::conflictRequest, this, &TabDeckStorage::onConflictRequest);
|
||||
if (currentUserInfo != nullptr) {
|
||||
syncUsername = QString::fromStdString(currentUserInfo->name());
|
||||
}
|
||||
|
|
@ -558,6 +568,34 @@ void TabDeckStorage::actSyncNow()
|
|||
syncManager->syncNow();
|
||||
}
|
||||
|
||||
void TabDeckStorage::onFirstSyncDetected(int localCount, int serverCount)
|
||||
{
|
||||
DlgSyncOnboarding dialog(localCount, serverCount, this);
|
||||
if (dialog.exec() == QDialog::Accepted) {
|
||||
syncManager->continueFirstSync(dialog.selectedMode());
|
||||
} else {
|
||||
syncManager->cancelFirstSync();
|
||||
}
|
||||
}
|
||||
|
||||
void TabDeckStorage::onConflictRequest(const DeckSyncConflict &conflict)
|
||||
{
|
||||
DlgSyncConflict dialog(conflict, this);
|
||||
if (dialog.exec() != QDialog::Accepted) {
|
||||
DeckSyncResolution skip;
|
||||
skip.action = DeckSyncSkipConflict;
|
||||
syncManager->resolveConflict(skip);
|
||||
return;
|
||||
}
|
||||
const DeckSyncResolution resolution = dialog.resolution();
|
||||
if (resolution.rememberAlways &&
|
||||
(resolution.action == DeckSyncKeepMine || resolution.action == DeckSyncKeepServer)) {
|
||||
SettingsCache::instance().deckSync().setConflictDefault(
|
||||
resolution.action == DeckSyncKeepMine ? DeckSyncConflictKeepMine : DeckSyncConflictKeepServer);
|
||||
}
|
||||
syncManager->resolveConflict(resolution);
|
||||
}
|
||||
|
||||
void TabDeckStorage::onSyncStatusMessage(const QString &message)
|
||||
{
|
||||
syncStatusType = SyncStatusType::Working;
|
||||
|
|
|
|||
|
|
@ -20,6 +20,8 @@
|
|||
#include <qtmetamacros.h>
|
||||
|
||||
struct LoadedDeck;
|
||||
struct DeckSyncConflict;
|
||||
struct DeckSyncResolution;
|
||||
class ServerInfo_User;
|
||||
class QTreeView;
|
||||
class QFileSystemModel;
|
||||
|
|
@ -128,6 +130,8 @@ private slots:
|
|||
void deleteDeckFinished(const Response &response, const CommandContainer &commandContainer);
|
||||
|
||||
void actSyncNow();
|
||||
void onFirstSyncDetected(int localCount, int serverCount);
|
||||
void onConflictRequest(const DeckSyncConflict &conflict);
|
||||
void onSyncStatusMessage(const QString &message);
|
||||
void onSyncFinished(const SyncSummary &summary, const QString &error);
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue