mirror of
https://github.com/Cockatrice/Cockatrice.git
synced 2026-11-09 11:33:25 -08:00
Address review findings
This commit is contained in:
parent
34e28dfa22
commit
885b7f82ae
3 changed files with 80 additions and 14 deletions
|
|
@ -118,8 +118,10 @@ void DeckSyncManager::syncNow()
|
||||||
syncing = true;
|
syncing = true;
|
||||||
uploadedCount = 0;
|
uploadedCount = 0;
|
||||||
downloadedCount = 0;
|
downloadedCount = 0;
|
||||||
|
removedCount = 0;
|
||||||
unchangedCount = 0;
|
unchangedCount = 0;
|
||||||
skippedCount = 0;
|
skippedCount = 0;
|
||||||
|
syncNote.clear();
|
||||||
pendingUploads.clear();
|
pendingUploads.clear();
|
||||||
pendingDownloads.clear();
|
pendingDownloads.clear();
|
||||||
pendingDeletes.clear();
|
pendingDeletes.clear();
|
||||||
|
|
@ -215,6 +217,22 @@ void DeckSyncManager::onListFinished(const Response &response, const CommandCont
|
||||||
DeckSyncSidecar sidecar(sidecarPath);
|
DeckSyncSidecar sidecar(sidecarPath);
|
||||||
sidecar.load();
|
sidecar.load();
|
||||||
const qint64 nowSecs = QDateTime::currentDateTime().toSecsSinceEpoch();
|
const qint64 nowSecs = QDateTime::currentDateTime().toSecsSinceEpoch();
|
||||||
|
// A listing that comes back empty while the sidecar still remembers rows that
|
||||||
|
// were uploaded means the server lost its deck data (a purge, or an account
|
||||||
|
// that was re-created), not that every deck was just deleted. Obeying that
|
||||||
|
// listing would remove the whole collection, so removals are held back.
|
||||||
|
bool sidecarHasRemoteRows = false;
|
||||||
|
// The records as they were before this run starts mutating them; only used to
|
||||||
|
// judge whether an empty listing is credible.
|
||||||
|
const QMap<QString, DeckSyncRecord> sidecarAtStart = sidecar.records();
|
||||||
|
for (auto rit = sidecarAtStart.begin(); rit != sidecarAtStart.end(); ++rit) {
|
||||||
|
if (rit.value().remoteId != -1) {
|
||||||
|
sidecarHasRemoteRows = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const bool remoteStateLost = serverFiles.isEmpty() && sidecarHasRemoteRows;
|
||||||
|
bool heldBackRemovals = false;
|
||||||
// Reuse the sidecar's guid so the server keeps recognizing the row, and mint
|
// Reuse the sidecar's guid so the server keeps recognizing the row, and mint
|
||||||
// one only for a deck that has never been synced.
|
// one only for a deck that has never been synced.
|
||||||
auto guidFor = [&sidecar](const QString &relativePath) {
|
auto guidFor = [&sidecar](const QString &relativePath) {
|
||||||
|
|
@ -292,6 +310,15 @@ void DeckSyncManager::onListFinished(const Response &response, const CommandCont
|
||||||
const bool unchangedSinceSync =
|
const bool unchangedSinceSync =
|
||||||
!sidecarRecord.baseHash.isEmpty() && it.value().hash == sidecarRecord.baseHash;
|
!sidecarRecord.baseHash.isEmpty() && it.value().hash == sidecarRecord.baseHash;
|
||||||
if (knownToServer && unchangedSinceSync) {
|
if (knownToServer && unchangedSinceSync) {
|
||||||
|
if (remoteStateLost) {
|
||||||
|
// The listing is empty although this deck was uploaded, so the
|
||||||
|
// row is not known to be deleted, only missing. Keep the file
|
||||||
|
// and the record; re-uploading it blind could duplicate the
|
||||||
|
// deck once the real listing returns.
|
||||||
|
heldBackRemovals = true;
|
||||||
|
++skippedCount;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
const QString absolutePath = deckPath + "/" + it.key();
|
const QString absolutePath = deckPath + "/" + it.key();
|
||||||
const QString backupPath =
|
const QString backupPath =
|
||||||
absolutePath + BACKUP_SUFFIX + QString::number(QDateTime::currentDateTime().toSecsSinceEpoch());
|
absolutePath + BACKUP_SUFFIX + QString::number(QDateTime::currentDateTime().toSecsSinceEpoch());
|
||||||
|
|
@ -300,7 +327,7 @@ void DeckSyncManager::onListFinished(const Response &response, const CommandCont
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
sidecar.remove(it.key());
|
sidecar.remove(it.key());
|
||||||
++downloadedCount;
|
++removedCount;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
const QString serverPath = serverPathFor(it.key());
|
const QString serverPath = serverPathFor(it.key());
|
||||||
|
|
@ -373,12 +400,30 @@ void DeckSyncManager::onListFinished(const Response &response, const CommandCont
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (it.value().remoteId == -1) {
|
if (it.value().remoteId == -1) {
|
||||||
|
// Never uploaded, so there is no server row to remove.
|
||||||
sidecar.remove(it.key());
|
sidecar.remove(it.key());
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if (matchedServerIds.contains(it.value().remoteId)) {
|
||||||
|
// Another local file (a rename or a copy) now owns this row: drop the
|
||||||
|
// stale record, but never tombstone the row it took over.
|
||||||
|
sidecar.remove(it.key());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (remoteStateLost) {
|
||||||
|
// The whole listing is missing, not this one deck: pushing the delete
|
||||||
|
// would purge rows that are probably still there.
|
||||||
|
heldBackRemovals = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
pendingDeletes.append(it.key());
|
pendingDeletes.append(it.key());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (heldBackRemovals) {
|
||||||
|
syncNote = tr("The server listed no decks although this collection was synced before. "
|
||||||
|
"No deletions were applied this run; the decks were kept.");
|
||||||
|
}
|
||||||
|
|
||||||
sidecar.save();
|
sidecar.save();
|
||||||
processNext();
|
processNext();
|
||||||
}
|
}
|
||||||
|
|
@ -530,7 +575,7 @@ void DeckSyncManager::onUploadFinished(const Response &response,
|
||||||
// upload the deck as new instead of updating a dead id.
|
// upload the deck as new instead of updating a dead id.
|
||||||
const UploadOp retry{.relativePath = relativePath,
|
const UploadOp retry{.relativePath = relativePath,
|
||||||
.serverPath = context.value("serverPath").toString(),
|
.serverPath = context.value("serverPath").toString(),
|
||||||
.serverDir = {},
|
.serverDir = parentDir(context.value("serverPath").toString()),
|
||||||
.deckId = -1,
|
.deckId = -1,
|
||||||
.guid = context.value("guid").toString()};
|
.guid = context.value("guid").toString()};
|
||||||
pendingUploads.prepend(retry);
|
pendingUploads.prepend(retry);
|
||||||
|
|
@ -719,6 +764,7 @@ void DeckSyncManager::onDeleteFinished(const Response &response,
|
||||||
sidecar.load();
|
sidecar.load();
|
||||||
sidecar.remove(relativePath);
|
sidecar.remove(relativePath);
|
||||||
sidecar.save();
|
sidecar.save();
|
||||||
|
++removedCount;
|
||||||
} else {
|
} else {
|
||||||
++skippedCount;
|
++skippedCount;
|
||||||
}
|
}
|
||||||
|
|
@ -757,11 +803,16 @@ void DeckSyncManager::finishSync()
|
||||||
{
|
{
|
||||||
syncing = false;
|
syncing = false;
|
||||||
const SyncSummary summary = currentSummary();
|
const SyncSummary summary = currentSummary();
|
||||||
emit statusMessage(tr("Sync complete: %1 uploaded, %2 downloaded, %3 unchanged, %4 skipped.")
|
QString message = tr("Sync complete: %1 uploaded, %2 downloaded, %3 removed, %4 unchanged, %5 skipped.")
|
||||||
.arg(summary.uploaded)
|
.arg(summary.uploaded)
|
||||||
.arg(summary.downloaded)
|
.arg(summary.downloaded)
|
||||||
.arg(summary.unchanged)
|
.arg(summary.removed)
|
||||||
.arg(summary.skipped));
|
.arg(summary.unchanged)
|
||||||
|
.arg(summary.skipped);
|
||||||
|
if (!summary.note.isEmpty()) {
|
||||||
|
message += QStringLiteral(" ") + summary.note;
|
||||||
|
}
|
||||||
|
emit statusMessage(message);
|
||||||
emit syncFinished(summary, QString());
|
emit syncFinished(summary, QString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -770,7 +821,9 @@ DeckSyncManager::SyncSummary DeckSyncManager::currentSummary() const
|
||||||
return SyncSummary{
|
return SyncSummary{
|
||||||
.uploaded = uploadedCount,
|
.uploaded = uploadedCount,
|
||||||
.downloaded = downloadedCount,
|
.downloaded = downloadedCount,
|
||||||
|
.removed = removedCount,
|
||||||
.unchanged = unchangedCount,
|
.unchanged = unchangedCount,
|
||||||
.skipped = skippedCount,
|
.skipped = skippedCount,
|
||||||
|
.note = syncNote,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -41,7 +41,10 @@ class ServerInfo_DeckStorage_Folder;
|
||||||
*
|
*
|
||||||
* Deletes are tombstones, never silent: a locally deleted deck is pushed with
|
* Deletes are tombstones, never silent: a locally deleted deck is pushed with
|
||||||
* Command_DeckDel, a server-side delete is applied locally only after copying
|
* Command_DeckDel, a server-side delete is applied locally only after copying
|
||||||
* the file to a .sync-backup, and resurrection reuses the stored deck id.
|
* the file to a .sync-backup, and resurrection reuses the stored deck id. A
|
||||||
|
* listing that comes back empty while the sidecar still remembers uploaded rows
|
||||||
|
* is treated as lost server state rather than a collection-wide delete, so that
|
||||||
|
* run applies no removals and says so in its summary.
|
||||||
*/
|
*/
|
||||||
class DeckSyncManager : public QObject
|
class DeckSyncManager : public QObject
|
||||||
{
|
{
|
||||||
|
|
@ -55,8 +58,10 @@ public:
|
||||||
{
|
{
|
||||||
int uploaded = 0;
|
int uploaded = 0;
|
||||||
int downloaded = 0;
|
int downloaded = 0;
|
||||||
|
int removed = 0; ///< Decks deleted on the server, or locally to mirror a server delete.
|
||||||
int unchanged = 0;
|
int unchanged = 0;
|
||||||
int skipped = 0;
|
int skipped = 0;
|
||||||
|
QString note; ///< Non-error caveat about this run, empty when there is none.
|
||||||
};
|
};
|
||||||
|
|
||||||
explicit DeckSyncManager(QObject *parent = nullptr);
|
explicit DeckSyncManager(QObject *parent = nullptr);
|
||||||
|
|
@ -149,8 +154,10 @@ private:
|
||||||
QList<UploadOp> pendingUploads;
|
QList<UploadOp> pendingUploads;
|
||||||
QList<DownloadOp> pendingDownloads;
|
QList<DownloadOp> pendingDownloads;
|
||||||
QList<QString> pendingDeletes;
|
QList<QString> pendingDeletes;
|
||||||
|
QString syncNote;
|
||||||
int uploadedCount = 0;
|
int uploadedCount = 0;
|
||||||
int downloadedCount = 0;
|
int downloadedCount = 0;
|
||||||
|
int removedCount = 0;
|
||||||
int unchangedCount = 0;
|
int unchangedCount = 0;
|
||||||
int skippedCount = 0;
|
int skippedCount = 0;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -591,13 +591,19 @@ void TabDeckStorage::renderSyncStatus()
|
||||||
case SyncStatusType::Failed:
|
case SyncStatusType::Failed:
|
||||||
syncStatusLabel->setText(tr("Sync failed: %1").arg(syncStatusDetail));
|
syncStatusLabel->setText(tr("Sync failed: %1").arg(syncStatusDetail));
|
||||||
break;
|
break;
|
||||||
case SyncStatusType::Finished:
|
case SyncStatusType::Finished: {
|
||||||
syncStatusLabel->setText(tr("Sync complete: %1 uploaded, %2 downloaded, %3 unchanged, %4 skipped.")
|
QString text = tr("Sync complete: %1 uploaded, %2 downloaded, %3 removed, %4 unchanged, %5 skipped.")
|
||||||
.arg(lastSyncSummary.uploaded)
|
.arg(lastSyncSummary.uploaded)
|
||||||
.arg(lastSyncSummary.downloaded)
|
.arg(lastSyncSummary.downloaded)
|
||||||
.arg(lastSyncSummary.unchanged)
|
.arg(lastSyncSummary.removed)
|
||||||
.arg(lastSyncSummary.skipped));
|
.arg(lastSyncSummary.unchanged)
|
||||||
|
.arg(lastSyncSummary.skipped);
|
||||||
|
if (!lastSyncSummary.note.isEmpty()) {
|
||||||
|
text += QStringLiteral("\n") + lastSyncSummary.note;
|
||||||
|
}
|
||||||
|
syncStatusLabel->setText(text);
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
case SyncStatusType::Idle:
|
case SyncStatusType::Idle:
|
||||||
syncStatusLabel->setText(tr("Not synced yet"));
|
syncStatusLabel->setText(tr("Not synced yet"));
|
||||||
break;
|
break;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue