[DeckStorage] Refresh the visibility column with a guarded timer instead of a latch counter

A dropped visibility reply used to leave the pendingVisibilityChanges
counter permanently positive, so the Public/Private column never refreshed
again and nothing reset it on disconnect. A restartable single-shot timer
with a boolean guard re-reads the tree whenever publishes quiet down and is
stopped on disconnect, so a lost reply costs one stale refresh instead of
killing the column for the session.
This commit is contained in:
Lukas Brübach 2026-09-19 07:46:51 +02:00 committed by GitHub
parent d7cd414750
commit e3a3ce0521
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 28 additions and 7 deletions

View file

@ -44,6 +44,12 @@
#include <libcockatrice/settings/paths_settings.h> #include <libcockatrice/settings/paths_settings.h>
#include <libcockatrice/utility/string_limits.h> #include <libcockatrice/utility/string_limits.h>
namespace {
// How long to wait after the last visibility change before reading back the
// Public/Private column, in milliseconds.
constexpr int VISIBILITY_REFRESH_DELAY = 500;
} // namespace
TabDeckStorage::TabDeckStorage(TabSupervisor *_tabSupervisor, TabDeckStorage::TabDeckStorage(TabSupervisor *_tabSupervisor,
AbstractClient *_client, AbstractClient *_client,
const ServerInfo_User *currentUserInfo) const ServerInfo_User *currentUserInfo)
@ -120,6 +126,15 @@ TabDeckStorage::TabDeckStorage(TabSupervisor *_tabSupervisor,
SettingsCache::instance().network().getKeepAlive() * 1000)); SettingsCache::instance().network().getKeepAlive() * 1000));
connect(shareTimeoutTimer, &QTimer::timeout, this, &TabDeckStorage::onShareFromTreeTimeout); connect(shareTimeoutTimer, &QTimer::timeout, this, &TabDeckStorage::onShareFromTreeTimeout);
// Restartable single-shot refresh for the Public/Private column. A dropped
// visibility reply must not leave the widget permanently stale, so the tree
// is re-read whenever publishes quiet down instead of waiting on a count
// that can get stuck above zero.
visibilityRefreshTimer = new QTimer(this);
visibilityRefreshTimer->setSingleShot(true);
visibilityRefreshTimer->setInterval(VISIBILITY_REFRESH_DELAY);
connect(visibilityRefreshTimer, &QTimer::timeout, this, &TabDeckStorage::onVisibilityRefreshTimeout);
QVBoxLayout *rightVbox = new QVBoxLayout; QVBoxLayout *rightVbox = new QVBoxLayout;
rightVbox->addWidget(shareBar); rightVbox->addWidget(shareBar);
rightVbox->addWidget(serverDirView); rightVbox->addWidget(serverDirView);
@ -255,6 +270,8 @@ void TabDeckStorage::handleConnected(const ServerInfo_User &userInfo)
void TabDeckStorage::handleConnectionChanged(ClientStatus status) void TabDeckStorage::handleConnectionChanged(ClientStatus status)
{ {
if (status == StatusDisconnected) { if (status == StatusDisconnected) {
visibilityRefreshTimer->stop();
visibilityRefreshStarted = false;
setRemoteEnabled(false); setRemoteEnabled(false);
} }
} }
@ -864,7 +881,8 @@ void TabDeckStorage::actPublishDeck()
PendingCommand *pend = client->prepareSessionCommand(cmd); PendingCommand *pend = client->prepareSessionCommand(cmd);
connect(pend, &PendingCommand::finished, this, &TabDeckStorage::setVisibilityFinished); connect(pend, &PendingCommand::finished, this, &TabDeckStorage::setVisibilityFinished);
++pendingVisibilityChanges; visibilityRefreshStarted = true;
visibilityRefreshTimer->start();
client->sendCommand(pend); client->sendCommand(pend);
} }
} }
@ -876,9 +894,10 @@ void TabDeckStorage::setVisibilityFinished(const Response &r, const CommandConta
tr("Failed to change deck visibility on server (response code %1).") tr("Failed to change deck visibility on server (response code %1).")
.arg(QString::number(static_cast<int>(r.response_code())))); .arg(QString::number(static_cast<int>(r.response_code()))));
} }
// Refresh once the last in-flight change has been acknowledged so the }
// Public/Private column reflects every selected node.
if (--pendingVisibilityChanges == 0) { void TabDeckStorage::onVisibilityRefreshTimeout()
serverDirView->refreshTree(); {
} visibilityRefreshStarted = false;
serverDirView->refreshTree();
} }

View file

@ -45,7 +45,8 @@ private:
QAction *aOpenLocalDeck, *aRenameLocal, *aUpload, *aNewLocalFolder, *aDeleteLocalDeck; QAction *aOpenLocalDeck, *aRenameLocal, *aUpload, *aNewLocalFolder, *aDeleteLocalDeck;
QAction *aOpenDecksFolder; QAction *aOpenDecksFolder;
QAction *aOpenRemoteDeck, *aDownload, *aShareDecks, *aPublishDeck, *aNewFolder, *aDeleteRemoteDeck; QAction *aOpenRemoteDeck, *aDownload, *aShareDecks, *aPublishDeck, *aNewFolder, *aDeleteRemoteDeck;
int pendingVisibilityChanges = 0; bool visibilityRefreshStarted = false;
QTimer *visibilityRefreshTimer;
QString getTargetPath() const; QString getTargetPath() const;
void setRemoteEnabled(bool enabled); void setRemoteEnabled(bool enabled);
@ -95,6 +96,7 @@ private slots:
void actPublishDeck(); void actPublishDeck();
void setVisibilityFinished(const Response &r, const CommandContainer &commandContainer); void setVisibilityFinished(const Response &r, const CommandContainer &commandContainer);
void onVisibilityRefreshTimeout();
void actDeleteRemoteDeck(); void actDeleteRemoteDeck();
void deleteFolderFinished(const Response &response, const CommandContainer &commandContainer); void deleteFolderFinished(const Response &response, const CommandContainer &commandContainer);