[Client] Manual deck collection sync with Sync Now action

This commit is contained in:
Lukas Brübach 2026-10-11 14:37:01 +02:00
parent fa4c5766eb
commit 3bed5ae5f5
5 changed files with 753 additions and 0 deletions

View file

@ -2,6 +2,7 @@
#include "../../../client/settings/cache_settings.h"
#include "../../deck_loader/deck_loader.h"
#include "../../deck_sync/deck_sync_manager.h"
#include "../../pixel_map_generator.h"
#include "../cards/additional_info/deck_color_identity.h"
#include "../deck_share/deck_share_utils.h"
@ -29,6 +30,7 @@
#include <QInputDialog>
#include <QItemSelection>
#include <QItemSelectionModel>
#include <QLabel>
#include <QLineEdit>
#include <QMessageBox>
#include <QModelIndex>
@ -177,6 +179,9 @@ TabDeckStorage::TabDeckStorage(TabSupervisor *_tabSupervisor,
rightVbox->addWidget(shareBar);
rightVbox->addWidget(serverDirView);
rightVbox->addLayout(rightToolBarLayout);
syncStatusLabel = new QLabel(this);
syncStatusLabel->setWordWrap(true);
rightVbox->addWidget(syncStatusLabel);
rightGroupBox = new QGroupBox;
rightGroupBox->setLayout(rightVbox);
@ -228,6 +233,10 @@ TabDeckStorage::TabDeckStorage(TabSupervisor *_tabSupervisor,
aPublishDeck->setIcon(QPixmap("theme:icons/lock"));
connect(aPublishDeck, &QAction::triggered, this, &TabDeckStorage::actPublishDeck);
aSyncNow = new QAction(this);
aSyncNow->setIcon(qApp->style()->standardIcon(QStyle::SP_BrowserReload));
connect(aSyncNow, &QAction::triggered, this, &TabDeckStorage::actSyncNow);
// Add actions to toolbars
leftToolBar->addAction(aOpenLocalDeck);
leftToolBar->addAction(aRenameLocal);
@ -239,11 +248,22 @@ TabDeckStorage::TabDeckStorage(TabSupervisor *_tabSupervisor,
rightToolBar->addAction(aOpenRemoteDeck);
rightToolBar->addAction(aDownload);
rightToolBar->addAction(aSyncNow);
rightToolBar->addAction(aShareDecks);
rightToolBar->addAction(aPublishDeck);
rightToolBar->addAction(aNewFolder);
rightToolBar->addAction(aDeleteRemoteDeck);
syncManager = new DeckSyncManager(this);
syncManager->setClient(client);
syncManager->setColorIdentityProvider(
[](const DeckList &deck) { return getDeckColorIdentity(deck, CardDatabaseManager::query()); });
connect(syncManager, &DeckSyncManager::statusMessage, this, &TabDeckStorage::onSyncStatusMessage);
connect(syncManager, &DeckSyncManager::syncFinished, this, &TabDeckStorage::onSyncFinished);
if (currentUserInfo != nullptr) {
syncUsername = QString::fromStdString(currentUserInfo->name());
}
retranslateUi();
QWidget *mainWidget = new QWidget(this);
@ -272,6 +292,8 @@ void TabDeckStorage::retranslateUi()
aShareDecks->setText(tr("Share decks"));
aPublishDeck->setText(tr("Publish/unpublish deck"));
aOpenDecksFolder->setText(tr("Open decks folder"));
aSyncNow->setText(tr("Sync now"));
renderSyncStatus();
shareBar->retranslateUi();
if (shareBar->isVisible()) {
onServerSelectionChanged();
@ -299,6 +321,7 @@ QString TabDeckStorage::getTargetPath() const
void TabDeckStorage::handleConnected(const ServerInfo_User &userInfo)
{
syncUsername = QString::fromStdString(userInfo.name());
setRemoteEnabled(userInfo.user_level() & ServerInfo_User::IsRegistered);
}
@ -316,6 +339,7 @@ void TabDeckStorage::handleConnectionChanged(ClientStatus status)
void TabDeckStorage::setRemoteEnabled(bool enabled)
{
remoteEnabled = enabled;
aUpload->setEnabled(enabled);
aOpenRemoteDeck->setEnabled(enabled);
aDownload->setEnabled(enabled);
@ -323,6 +347,7 @@ void TabDeckStorage::setRemoteEnabled(bool enabled)
aPublishDeck->setEnabled(enabled);
aNewFolder->setEnabled(enabled);
aDeleteRemoteDeck->setEnabled(enabled);
aSyncNow->setEnabled(enabled && !syncManager->isSyncing());
if (enabled) {
serverDirView->refreshTree();
@ -526,6 +551,61 @@ void TabDeckStorage::actOpenDecksFolder()
QtUtils::openPathInFileManager(dir);
}
void TabDeckStorage::actSyncNow()
{
DeckSyncProfile profile(client->serverName(), client->serverPort(), syncUsername);
syncManager->setProfile(profile);
syncManager->setDeckPath(SettingsCache::instance().paths().getDeckPath());
aSyncNow->setEnabled(false);
syncManager->syncNow();
}
void TabDeckStorage::onSyncStatusMessage(const QString &message)
{
syncStatusType = SyncStatusType::Working;
syncStatusDetail = message;
renderSyncStatus();
}
void TabDeckStorage::onSyncFinished(int uploaded, int downloaded, int unchanged, int skipped, const QString &error)
{
aSyncNow->setEnabled(remoteEnabled);
if (!error.isEmpty()) {
syncStatusType = SyncStatusType::Failed;
syncStatusDetail = error;
} else {
syncStatusType = SyncStatusType::Finished;
syncStatusDetail.clear();
lastSyncSummary = {uploaded, downloaded, unchanged, skipped};
}
renderSyncStatus();
if (error.isEmpty()) {
serverDirView->refreshTree();
}
}
void TabDeckStorage::renderSyncStatus()
{
switch (syncStatusType) {
case SyncStatusType::Working:
syncStatusLabel->setText(syncStatusDetail);
break;
case SyncStatusType::Failed:
syncStatusLabel->setText(tr("Sync failed: %1").arg(syncStatusDetail));
break;
case SyncStatusType::Finished:
syncStatusLabel->setText(tr("Sync complete: %1 uploaded, %2 downloaded, %3 unchanged, %4 skipped.")
.arg(lastSyncSummary.uploaded)
.arg(lastSyncSummary.downloaded)
.arg(lastSyncSummary.unchanged)
.arg(lastSyncSummary.skipped));
break;
case SyncStatusType::Idle:
syncStatusLabel->setText(tr("Not synced yet"));
break;
}
}
void TabDeckStorage::actRemoteDoubleClick(const QModelIndex &curRight)
{
if (dynamic_cast<RemoteDeckList_TreeModel::FileNode *>(serverDirView->getNode(curRight))) {

View file

@ -25,9 +25,11 @@ class QFileSystemModel;
class QToolBar;
class QGroupBox;
class QTimer;
class QLabel;
class CommandContainer;
class Response;
class ShareBarWidget;
class DeckSyncManager;
class QAction;
class QModelIndex;
class TabSupervisor;
@ -50,6 +52,29 @@ private:
QAction *aOpenLocalDeck, *aRenameLocal, *aUpload, *aNewLocalFolder, *aDeleteLocalDeck;
QAction *aOpenDecksFolder;
QAction *aOpenRemoteDeck, *aDownload, *aShareDecks, *aPublishDeck, *aNewFolder, *aDeleteRemoteDeck;
QAction *aSyncNow;
QLabel *syncStatusLabel;
DeckSyncManager *syncManager;
QString syncUsername;
bool remoteEnabled = false;
enum class SyncStatusType
{
Idle,
Working,
Finished,
Failed
};
struct SyncSummary
{
int uploaded = 0;
int downloaded = 0;
int unchanged = 0;
int skipped = 0;
};
SyncStatusType syncStatusType = SyncStatusType::Idle;
QString syncStatusDetail;
SyncSummary lastSyncSummary;
void renderSyncStatus();
bool visibilityRefreshStarted = false;
QTimer *visibilityRefreshTimer;
QStringList visibilityFailures;
@ -108,6 +133,10 @@ private slots:
void deleteFolderFinished(const Response &response, const CommandContainer &commandContainer);
void deleteDeckFinished(const Response &response, const CommandContainer &commandContainer);
void actSyncNow();
void onSyncStatusMessage(const QString &message);
void onSyncFinished(int uploaded, int downloaded, int unchanged, int skipped, const QString &error);
public:
TabDeckStorage(TabSupervisor *_tabSupervisor, AbstractClient *_client, const ServerInfo_User *currentUserInfo);
void retranslateUi() override;