This commit is contained in:
BruebachL 2026-11-04 10:30:38 -07:00 • committed by GitHub
commit 07bc27d2b8
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
17 changed files with 437 additions and 0 deletions

View file

@ -34,6 +34,7 @@ set(cockatrice_SOURCES
src/interface/widgets/dialogs/dlg_convert_deck_to_cod_format.cpp
src/interface/widgets/dialogs/dlg_create_game.cpp
src/interface/widgets/dialogs/dlg_crash_report.cpp
src/interface/widgets/dialogs/dlg_deck_history.cpp
src/interface/widgets/dialogs/dlg_default_tags_editor.cpp
src/interface/widgets/dialogs/dlg_edit_avatar.cpp
src/interface/widgets/dialogs/dlg_edit_password.cpp

View file

@ -0,0 +1,176 @@
#include "dlg_deck_history.h"
#include <QDateTime>
#include <QDialogButtonBox>
#include <QLabel>
#include <QListWidget>
#include <QLocale>
#include <QMessageBox>
#include <QPushButton>
#include <QVBoxLayout>
#include <libcockatrice/deck_list/deck_diff.h>
#include <libcockatrice/deck_list/deck_list.h>
#include <libcockatrice/network/client/abstract/abstract_client.h>
#include <libcockatrice/protocol/pb/command_deck_download.pb.h>
#include <libcockatrice/protocol/pb/command_deck_history_list.pb.h>
#include <libcockatrice/protocol/pb/response.pb.h>
#include <libcockatrice/protocol/pb/response_deck_download.pb.h>
#include <libcockatrice/protocol/pb/response_deck_history_list.pb.h>
#include <libcockatrice/protocol/pending_command.h>
namespace
{
QString formatVersionTime(qint64 secsSinceEpoch)
{
return QLocale().toString(QDateTime::fromSecsSinceEpoch(secsSinceEpoch), QLocale::ShortFormat);
}
} // namespace
DlgDeckHistory::DlgDeckHistory(int _deckId,
const QString &_deckName,
int _limit,
AbstractClient *_client,
QWidget *parent)
: QDialog(parent), deckId(_deckId), deckName(_deckName), limit(_limit), client(_client)
{
auto *layout = new QVBoxLayout(this);
detailLabel = new QLabel(this);
detailLabel->setWordWrap(true);
versionsList = new QListWidget(this);
previewList = new QListWidget(this);
restoreButton = new QPushButton(this);
restoreButton->setEnabled(false);
layout->addWidget(detailLabel);
layout->addWidget(versionsList);
layout->addWidget(previewList);
layout->addWidget(restoreButton);
auto *buttons = new QDialogButtonBox(QDialogButtonBox::Close, this);
connect(buttons, &QDialogButtonBox::rejected, this, &QDialog::reject);
layout->addWidget(buttons);
connect(versionsList, &QListWidget::currentRowChanged, this, &DlgDeckHistory::onSelectionChanged);
connect(restoreButton, &QPushButton::clicked, this, &DlgDeckHistory::onRestoreClicked);
Command_DeckHistoryList listCmd;
listCmd.set_deck_id(deckId);
listCmd.set_limit(static_cast<google::protobuf::uint32>(limit));
PendingCommand *listPend = AbstractClient::prepareSessionCommand(listCmd);
connect(listPend, &PendingCommand::finished, this, &DlgDeckHistory::onHistoryFinished);
client->sendCommand(listPend);
Command_DeckDownload downloadCmd;
downloadCmd.set_deck_id(deckId);
PendingCommand *downloadPend = AbstractClient::prepareSessionCommand(downloadCmd);
connect(downloadPend, &PendingCommand::finished, this, &DlgDeckHistory::onCurrentFinished);
client->sendCommand(downloadPend);
retranslateUi();
}
void DlgDeckHistory::retranslateUi()
{
setWindowTitle(tr("Deck history"));
detailLabel->setText(
tr("Stored versions of %1 (newest first). Restoring uploads the version as new content.").arg(deckName));
restoreButton->setText(tr("Restore selected version"));
renderVersions();
renderPreview();
}
void DlgDeckHistory::onHistoryFinished(const Response &response, const CommandContainer & /*commandContainer*/)
{
if (response.response_code() != Response::RespOk) {
detailLabel->setText(tr("Could not load version history."));
return;
}
const Response_DeckHistoryList &list = response.GetExtension(Response_DeckHistoryList::ext);
versions.clear();
for (const DeckVersionInfo &version : list.versions()) {
DeckHistoryVersion entry;
entry.versionNo = version.version_no();
entry.createdAtSecs = static_cast<qint64>(version.created_at());
entry.createdByClientId = QString::fromStdString(version.created_by_clientid());
entry.hash = QString::fromStdString(version.hash());
entry.content = QString::fromStdString(version.content());
versions.append(entry);
}
historyArrived = true;
renderVersions();
renderPreview();
}
void DlgDeckHistory::onCurrentFinished(const Response &response, const CommandContainer & /*commandContainer*/)
{
if (response.response_code() != Response::RespOk) {
detailLabel->setText(tr("Could not load the current deck."));
return;
}
const Response_DeckDownload &download = response.GetExtension(Response_DeckDownload::ext);
currentContent = QString::fromStdString(download.deck());
currentArrived = true;
renderPreview();
}
void DlgDeckHistory::renderVersions()
{
const int row = versionsList->currentRow();
versionsList->clear();
for (const DeckHistoryVersion &version : versions) {
QString label = tr("Version %1 — %2").arg(version.versionNo).arg(formatVersionTime(version.createdAtSecs));
if (!version.createdByClientId.isEmpty()) {
label += tr(" — %1").arg(version.createdByClientId);
}
versionsList->addItem(label);
}
if (row >= 0 && row < versionsList->count()) {
versionsList->setCurrentRow(row);
} else if (versionsList->count() > 0) {
versionsList->setCurrentRow(0);
}
onSelectionChanged();
}
void DlgDeckHistory::renderPreview()
{
previewList->clear();
restoreButton->setEnabled(false);
const int row = versionsList->currentRow();
if (!historyArrived || !currentArrived || row < 0 || row >= versions.size()) {
return;
}
DeckList currentDeck;
DeckList selectedDeck;
if (!currentDeck.loadFromString_Native(currentContent) ||
!selectedDeck.loadFromString_Native(versions.at(row).content)) {
return;
}
const DeckDiffResult shown = DeckDiff::diff(currentDeck, selectedDeck);
for (const DeckCardChange &change : shown.changes) {
previewList->addItem(
tr("%1: %2 %3 → %4").arg(change.zone).arg(change.cardName).arg(change.oldCount).arg(change.newCount));
}
restoreButton->setEnabled(true);
}
void DlgDeckHistory::onSelectionChanged()
{
renderPreview();
}
void DlgDeckHistory::onRestoreClicked()
{
const int row = versionsList->currentRow();
if (row < 0 || row >= versions.size()) {
return;
}
if (QMessageBox::question(this, tr("Restore version"),
tr("Restore version %1 as the current deck content? This becomes a new version.")
.arg(versions.at(row).versionNo)) != QMessageBox::Yes) {
return;
}
emit restoreRequested(deckId, versions.at(row).content);
accept();
}

View file

@ -0,0 +1,73 @@
/**
* @file dlg_deck_history.h
* @brief Browser for the stored versions of one server deck.
*/
#ifndef DLG_DECK_HISTORY_H
#define DLG_DECK_HISTORY_H
#include <QDialog>
#include <QList>
#include <QString>
class AbstractClient;
class CommandContainer;
class QLabel;
class QListWidget;
class QPushButton;
class Response;
/** @brief One stored deck version for display. */
struct DeckHistoryVersion
{
unsigned int versionNo = 0;
qint64 createdAtSecs = 0;
QString createdByClientId;
QString hash;
QString content;
};
/**
* @class DlgDeckHistory
* @brief Lists stored versions with a diff preview against the current deck.
*
* Restoring uploads the selected version as the new current content through
* the regular deck-upload path, so it becomes a version itself instead of
* rewriting history. Rejected dialogs restore nothing.
*/
class DlgDeckHistory : public QDialog
{
Q_OBJECT
public:
DlgDeckHistory(int deckId, const QString &deckName, int limit, AbstractClient *client, QWidget *parent = nullptr);
void retranslateUi();
signals:
void restoreRequested(int deckId, const QString &content);
private slots:
void onHistoryFinished(const Response &response, const CommandContainer &commandContainer);
void onCurrentFinished(const Response &response, const CommandContainer &commandContainer);
void onSelectionChanged();
void onRestoreClicked();
private:
void renderVersions();
void renderPreview();
int deckId = -1;
QString deckName;
int limit = 20;
AbstractClient *client = nullptr;
QList<DeckHistoryVersion> versions;
QString currentContent;
bool historyArrived = false;
bool currentArrived = false;
QLabel *detailLabel;
QListWidget *versionsList;
QListWidget *previewList;
QPushButton *restoreButton;
};
#endif // DLG_DECK_HISTORY_H

View file

@ -383,12 +383,20 @@ GeneralSettingsPage::GeneralSettingsPage()
connect(&syncIntervalEdit, qOverload<int>(&QSpinBox::valueChanged), &SettingsCache::instance().deckSync(),
&DeckSyncSettings::setSyncIntervalMinutes);
historyLimitEdit.setMinimum(1);
historyLimitEdit.setMaximum(100);
historyLimitEdit.setValue(settings.deckSync().getHistoryLimit());
connect(&historyLimitEdit, qOverload<int>(&QSpinBox::valueChanged), &SettingsCache::instance().deckSync(),
&DeckSyncSettings::setHistoryLimit);
auto *deckSyncGrid = new QGridLayout;
deckSyncGrid->addWidget(&conflictDefaultLabel, 0, 0);
deckSyncGrid->addWidget(&conflictDefaultSelector, 0, 1);
deckSyncGrid->addWidget(&autoSyncCheckBox, 1, 0, 1, 2);
deckSyncGrid->addWidget(&syncIntervalLabel, 2, 0);
deckSyncGrid->addWidget(&syncIntervalEdit, 2, 1);
deckSyncGrid->addWidget(&historyLimitLabel, 3, 0);
deckSyncGrid->addWidget(&historyLimitEdit, 3, 1);
deckSyncGroupBox = new QGroupBox;
deckSyncGroupBox->setLayout(deckSyncGrid);
@ -692,5 +700,6 @@ void GeneralSettingsPage::retranslateUi()
autoSyncCheckBox.setText(tr("Sync decks automatically"));
syncIntervalLabel.setText(tr("Check every:"));
syncIntervalEdit.setSuffix(" " + tr("min"));
historyLimitLabel.setText(tr("Kept versions per deck:"));
resetAllPathsButton->setText(tr("Reset all paths"));
}

View file

@ -111,6 +111,8 @@ private:
QCheckBox autoSyncCheckBox;
QLabel syncIntervalLabel;
QSpinBox syncIntervalEdit;
QLabel historyLimitLabel;
QSpinBox historyLimitEdit;
};
#endif // COCKATRICE_GENERAL_SETTINGS_PAGE_H

View file

@ -8,6 +8,7 @@
#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_deck_history.h"
#include "../dialogs/dlg_sync_conflict.h"
#include "../dialogs/dlg_sync_onboarding.h"
#include "../interface/widgets/server/remote/remote_decklist_tree_widget.h"
@ -241,6 +242,10 @@ TabDeckStorage::TabDeckStorage(TabSupervisor *_tabSupervisor,
aSyncNow->setIcon(qApp->style()->standardIcon(QStyle::SP_BrowserReload));
connect(aSyncNow, &QAction::triggered, this, &TabDeckStorage::actSyncNow);
aDeckHistory = new QAction(this);
aDeckHistory->setIcon(qApp->style()->standardIcon(QStyle::SP_FileDialogDetailedView));
connect(aDeckHistory, &QAction::triggered, this, &TabDeckStorage::actDeckHistory);
// Add actions to toolbars
leftToolBar->addAction(aOpenLocalDeck);
leftToolBar->addAction(aRenameLocal);
@ -253,6 +258,7 @@ TabDeckStorage::TabDeckStorage(TabSupervisor *_tabSupervisor,
rightToolBar->addAction(aOpenRemoteDeck);
rightToolBar->addAction(aDownload);
rightToolBar->addAction(aSyncNow);
rightToolBar->addAction(aDeckHistory);
rightToolBar->addAction(aShareDecks);
rightToolBar->addAction(aPublishDeck);
rightToolBar->addAction(aNewFolder);
@ -314,6 +320,7 @@ void TabDeckStorage::retranslateUi()
aPublishDeck->setText(tr("Publish/unpublish deck"));
aOpenDecksFolder->setText(tr("Open decks folder"));
aSyncNow->setText(tr("Sync now"));
aDeckHistory->setText(tr("Version history"));
renderSyncStatus();
shareBar->retranslateUi();
if (shareBar->isVisible()) {
@ -369,6 +376,7 @@ void TabDeckStorage::setRemoteEnabled(bool enabled)
aPublishDeck->setEnabled(enabled);
aNewFolder->setEnabled(enabled);
aDeleteRemoteDeck->setEnabled(enabled);
aDeckHistory->setEnabled(enabled);
aSyncNow->setEnabled(enabled && !syncManager->isSyncing());
if (enabled) {
@ -654,6 +662,58 @@ void TabDeckStorage::renderSyncStatus()
}
}
void TabDeckStorage::actDeckHistory()
{
QList<RemoteDeckList_TreeModel::FileNode *> files;
for (const auto curRight : serverDirView->getCurrentSelection()) {
if (auto *file = dynamic_cast<RemoteDeckList_TreeModel::FileNode *>(curRight)) {
files.append(file);
}
}
if (files.size() != 1) {
QMessageBox::information(this, tr("Version history"), tr("Select a single server deck to view its history."));
return;
}
auto *dialog = new DlgDeckHistory(files.first()->getId(), files.first()->getName(),
SettingsCache::instance().deckSync().getHistoryLimit(), client, this);
connect(dialog, &DlgDeckHistory::restoreRequested, this, &TabDeckStorage::onHistoryRestoreRequested);
dialog->setAttribute(Qt::WA_DeleteOnClose);
dialog->show();
}
void TabDeckStorage::onHistoryRestoreRequested(int deckId, const QString &content)
{
DeckList deck;
if (!deck.loadFromString_Native(content)) {
return;
}
const QString deckString = deck.writeToString_Native();
if (deckString.length() > MAX_FILE_LENGTH) {
QMessageBox::critical(this, tr("Error"), tr("Restored deck is too large"));
return;
}
Command_DeckUpload cmd;
cmd.set_deck_id(deckId);
cmd.set_deck_list(deckString.toStdString());
cmd.set_color_identity(getDeckColorIdentity(deck, CardDatabaseManager::query()).toStdString());
PendingCommand *pend = client->prepareSessionCommand(cmd);
connect(pend, &PendingCommand::finished, this, &TabDeckStorage::onHistoryRestoreFinished);
client->sendCommand(pend);
}
void TabDeckStorage::onHistoryRestoreFinished(const Response &response, const CommandContainer & /*commandContainer*/)
{
if (response.response_code() != Response::RespOk) {
QMessageBox::critical(this, tr("Error"), tr("Failed to restore deck version"));
return;
}
onSyncStatusMessage(tr("Version restored."));
serverDirView->refreshTree();
}
void TabDeckStorage::actRemoteDoubleClick(const QModelIndex &curRight)
{
if (dynamic_cast<RemoteDeckList_TreeModel::FileNode *>(serverDirView->getNode(curRight))) {

View file

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

View file

@ -16,6 +16,7 @@ set(PROTO_FILES
command_deck_del_dir.proto
command_deck_download.proto
command_deck_download_public.proto
command_deck_history_list.proto
command_deck_list.proto
command_deck_list_other_user.proto
command_deck_new_dir.proto
@ -149,6 +150,7 @@ set(PROTO_FILES
response_ban_history.proto
response_card_art_rule_entry.proto
response_deck_download.proto
response_deck_history_list.proto
response_deck_list.proto
response_deck_share_create.proto
response_deck_share_download.proto

View file

@ -0,0 +1,12 @@
syntax = "proto2";
import "session_commands.proto";
message Command_DeckHistoryList {
extend SessionCommand {
optional Command_DeckHistoryList ext = 1035;
}
optional sint32 deck_id = 1 [default = -1];
// Maximum versions to return, newest first. Capped server-side by
// deck_sync/max_versions.
optional uint32 limit = 2 [default = 20];
}

View file

@ -86,6 +86,7 @@ message Response {
DECK_SHARE_DOWNLOAD = 1105; // Response for shared deck download
DECK_SHARE_LIST_MINE = 1106; // Response listing the caller's own shares
DECK_SYNC_STATUS = 1107; // Response with changed decks since a timestamp
DECK_HISTORY_LIST = 1108; // Response listing deck versions
CARD_ART_RULE_LIST = 1200; // Response containing a list of card art rules
}

View file

@ -0,0 +1,19 @@
syntax = "proto2";
import "response.proto";
message DeckVersionInfo {
optional uint32 version_no = 1;
optional uint64 created_at = 2;
optional string created_by_clientid = 3;
optional string hash = 4;
// Full deck content in native format, so the client can diff and
// restore without further round trips.
optional string content = 5;
}
message Response_DeckHistoryList {
extend Response {
optional Response_DeckHistoryList ext = 1108;
}
repeated DeckVersionInfo versions = 1;
}

View file

@ -38,6 +38,7 @@ message SessionCommand {
DECK_SHARE_REMOVE = 1033;
SUBMIT_PASSWORD_VERIFIER = 1034;
DECK_SYNC_CHECK = 1035;
DECK_HISTORY_LIST = 1036;
REPLAY_LIST = 1100;
REPLAY_DOWNLOAD = 1101;
REPLAY_MODIFY_MATCH = 1102;

View file

@ -42,3 +42,14 @@ void DeckSyncSettings::setSyncIntervalMinutes(int _syncIntervalMinutes)
setValue(_syncIntervalMinutes, "syncIntervalMinutes");
emit syncIntervalMinutesChanged(_syncIntervalMinutes);
}
int DeckSyncSettings::getHistoryLimit() const
{
return qBound(1, getValue("historyLimit", QString(), QString(), 20).toInt(), 100);
}
void DeckSyncSettings::setHistoryLimit(int _historyLimit)
{
setValue(_historyLimit, "historyLimit");
emit historyLimitChanged(_historyLimit);
}

View file

@ -30,11 +30,14 @@ public:
void setAutoSync(bool _autoSync);
[[nodiscard]] int getSyncIntervalMinutes() const;
void setSyncIntervalMinutes(int _syncIntervalMinutes);
[[nodiscard]] int getHistoryLimit() const;
void setHistoryLimit(int _historyLimit);
signals:
void conflictDefaultChanged(int conflictDefault);
void autoSyncChanged(bool autoSync);
void syncIntervalMinutesChanged(int syncIntervalMinutes);
void historyLimitChanged(int historyLimit);
private:
DeckSyncSettings(const DeckSyncSettings & /*other*/);

View file

@ -89,6 +89,7 @@
#include <libcockatrice/protocol/pb/command_deck_del_dir.pb.h>
#include <libcockatrice/protocol/pb/command_deck_download.pb.h>
#include <libcockatrice/protocol/pb/command_deck_download_public.pb.h>
#include <libcockatrice/protocol/pb/command_deck_history_list.pb.h>
#include <libcockatrice/protocol/pb/command_deck_list.pb.h>
#include <libcockatrice/protocol/pb/command_deck_list_other_user.pb.h>
#include <libcockatrice/protocol/pb/command_deck_new_dir.pb.h>
@ -130,6 +131,7 @@
#include <libcockatrice/protocol/pb/response_card_art_rule_entry.pb.h>
#include <libcockatrice/protocol/pb/response_clear_blacklisted_banners.pb.h>
#include <libcockatrice/protocol/pb/response_deck_download.pb.h>
#include <libcockatrice/protocol/pb/response_deck_history_list.pb.h>
#include <libcockatrice/protocol/pb/response_deck_list.pb.h>
#include <libcockatrice/protocol/pb/response_deck_share_create.pb.h>
#include <libcockatrice/protocol/pb/response_deck_share_download.pb.h>
@ -376,6 +378,8 @@ Response::ResponseCode AbstractServerSocketInterface::processExtendedSessionComm
return cmdDeckDownload(cmd.GetExtension(Command_DeckDownload::ext), rc);
case SessionCommand::DECK_SYNC_CHECK:
return cmdDeckSyncCheck(cmd.GetExtension(Command_DeckSyncCheck::ext), rc);
case SessionCommand::DECK_HISTORY_LIST:
return cmdDeckHistoryList(cmd.GetExtension(Command_DeckHistoryList::ext), rc);
case SessionCommand::REPLAY_LIST:
return cmdReplayList(cmd.GetExtension(Command_ReplayList::ext), rc);
case SessionCommand::REPLAY_DOWNLOAD:
@ -954,6 +958,50 @@ Response::ResponseCode AbstractServerSocketInterface::cmdDeckSyncCheck(const Com
return Response::RespOk;
}
Response::ResponseCode AbstractServerSocketInterface::cmdDeckHistoryList(const Command_DeckHistoryList &cmd,
ResponseContainer &rc)
{
if (authState != PasswordRight) {
return Response::RespFunctionNotAllowed;
}
sqlInterface->checkSql();
QSqlQuery *ownerQuery =
sqlInterface->prepareQuery("select 1 from {prefix}_decklist_files where id = :id and id_user = :id_user");
ownerQuery->bindValue(":id", cmd.deck_id());
ownerQuery->bindValue(":id_user", userInfo->id());
if (!sqlInterface->execSqlQuery(ownerQuery)) {
return Response::RespContextError;
}
if (!ownerQuery->next()) {
return Response::RespNameNotFound;
}
const int limit = qMin(cmd.has_limit() ? static_cast<int>(cmd.limit()) : 20, servatrice->getDeckSyncMaxVersions());
QSqlQuery *query =
sqlInterface->prepareQuery("select version_no, unix_timestamp(created_at), created_by_clientid, hash, "
"content from {prefix}_deck_versions where deck_id = :deck_id order by version_no "
"desc limit :limit");
query->bindValue(":deck_id", cmd.deck_id());
query->bindValue(":limit", qMax(1, limit));
if (!sqlInterface->execSqlQuery(query)) {
return Response::RespContextError;
}
Response_DeckHistoryList *re = new Response_DeckHistoryList;
while (query->next()) {
DeckVersionInfo *version = re->add_versions();
version->set_version_no(query->value(0).toUInt());
version->set_created_at(query->value(1).toULongLong());
version->set_created_by_clientid(query->value(2).toString().toStdString());
version->set_hash(query->value(3).toString().toStdString());
version->set_content(query->value(4).toString().toStdString());
}
rc.setResponseExtension(re);
return Response::RespOk;
}
Response::ResponseCode AbstractServerSocketInterface::cmdDeckDownloadPublic(const Command_DeckDownloadPublic &cmd,
ResponseContainer &rc)
{

View file

@ -56,6 +56,7 @@ class Command_DeckShareList;
class Command_DeckShareListMine;
class Command_DeckShareRemove;
class Command_DeckShareDownload;
class Command_DeckHistoryList;
class Command_DeckSyncCheck;
class Command_ReplayList;
class Command_ReplayDownload;
@ -112,6 +113,7 @@ class Command_ViewLogHistory;
class Command_WarnUser;
class QObject;
class ResponseContainer;
class Response_DeckHistoryList;
class Response_DeckSyncStatus;
class AbstractServerSocketInterface : public Server_ProtocolHandler
@ -167,6 +169,7 @@ private:
Response::ResponseCode cmdDeckDownload(const Command_DeckDownload &cmd, ResponseContainer &rc);
bool syncCheckHelper(int userId, qint64 sinceSecs, Response_DeckSyncStatus *status);
Response::ResponseCode cmdDeckSyncCheck(const Command_DeckSyncCheck &cmd, ResponseContainer &rc);
Response::ResponseCode cmdDeckHistoryList(const Command_DeckHistoryList &cmd, ResponseContainer &rc);
Response::ResponseCode cmdDeckDownloadPublic(const Command_DeckDownloadPublic &cmd, ResponseContainer &rc);
Response::ResponseCode cmdDeckShareCreate(const Command_DeckShareCreate &cmd, ResponseContainer &rc);
Response::ResponseCode cmdDeckShareList(const Command_DeckShareList &cmd, ResponseContainer &rc);

View file

@ -633,6 +633,18 @@ TEST_F(SettingsDefaultsTest, DeckSync_SyncIntervalMinutes_DefaultAndBounds)
ASSERT_EQ(s.getSyncIntervalMinutes(), 60);
}
TEST_F(SettingsDefaultsTest, DeckSync_HistoryLimit_DefaultAndBounds)
{
DeckSyncSettings s(settingsPath, nullptr);
ASSERT_EQ(s.getHistoryLimit(), 20);
s.setHistoryLimit(50);
ASSERT_EQ(s.getHistoryLimit(), 50);
s.setHistoryLimit(0);
ASSERT_EQ(s.getHistoryLimit(), 1);
s.setHistoryLimit(500);
ASSERT_EQ(s.getHistoryLimit(), 100);
}
// --- NetworkSettings ---
TEST_F(SettingsDefaultsTest, Network_ClientID_Default)