mirror of
https://github.com/Cockatrice/Cockatrice.git
synced 2026-11-06 01:53:25 -08:00
[Spoilers] Preserve spoiler files through update and reload failures (#7707)
* Preserve spoiler files and serialize update reloads * Surface spoiler removal reload failures and allow disabled-state retry * Address review feedback on spoiler update lifecycle Only remove the spoiler file when the user unchecks the setting; opening Settings no longer queues an unrequested reload for a leftover file. Remove the spoilersUpdatedSuccessfully signal, which had no receiver. * Fix spoiler settings state after reopen and while disabled Reopen Settings while an application-owned update is running: the new page now observes that updater, so the button reads Updating... and re-enables on completion instead of offering a second update that cancels the first. Observation does not take ownership: only updaters the page creates are retired with deleteLater, because the launch check is stack-allocated. When spoilers are off, the hint now describes what Reload Card Database actually does, stays readable, and wraps instead of clipping. --------- Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
This commit is contained in:
parent
3a656c9c4a
commit
0de92c06f8
9 changed files with 648 additions and 229 deletions
|
|
@ -8,6 +8,7 @@
|
|||
#include <QAbstractItemModel>
|
||||
#include <QAbstractItemView>
|
||||
#include <QAction>
|
||||
#include <QCoreApplication>
|
||||
#include <QDateTime>
|
||||
#include <QDebug>
|
||||
#include <QDir>
|
||||
|
|
@ -61,7 +62,7 @@ DeckEditorSettingsPage::DeckEditorSettingsPage()
|
|||
connect(mpSpoilerPathButton, &QPushButton::clicked, this, &DeckEditorSettingsPage::spoilerPathButtonClicked);
|
||||
|
||||
updateNowButton = new QPushButton;
|
||||
updateNowButton->setFixedWidth(150);
|
||||
updateNowButton->setMinimumWidth(150);
|
||||
connect(updateNowButton, &QPushButton::clicked, this, &DeckEditorSettingsPage::updateSpoilers);
|
||||
|
||||
// Update the GUI depending on if the box is ticked or not
|
||||
|
|
@ -120,12 +121,19 @@ DeckEditorSettingsPage::DeckEditorSettingsPage()
|
|||
lpSpoilerGrid->addWidget(&lastUpdatedLabel, 2, 0);
|
||||
lpSpoilerGrid->addWidget(updateNowButton, 2, 1);
|
||||
lpSpoilerGrid->addWidget(&infoOnSpoilersLabel, 3, 0, 1, 3, Qt::AlignTop);
|
||||
infoOnSpoilersLabel.setWordWrap(true);
|
||||
|
||||
// On a change to the checkbox, hide/un-hide the other fields
|
||||
connect(&mcDownloadSpoilersCheckBox, &QCheckBox::toggled, &SettingsCache::instance().downloads(),
|
||||
&DownloadSettings::setDownloadSpoilerStatus);
|
||||
connect(&mcDownloadSpoilersCheckBox, &QCheckBox::toggled, this, &DeckEditorSettingsPage::setSpoilersEnabled);
|
||||
|
||||
// The updater is application-owned and outlives this page, so a Settings page
|
||||
// reopened mid-update must observe the operation that is still running.
|
||||
if (auto *runningUpdater = SpoilerBackgroundUpdater::current()) {
|
||||
observeSpoilerUpdater(runningUpdater);
|
||||
}
|
||||
|
||||
mpGeneralGroupBox = new QGroupBox;
|
||||
mpGeneralGroupBox->setLayout(lpGeneralGrid);
|
||||
|
||||
|
|
@ -328,20 +336,60 @@ void DeckEditorSettingsPage::urlListChanged(const QModelIndex &, int, int, const
|
|||
|
||||
void DeckEditorSettingsPage::updateSpoilers()
|
||||
{
|
||||
// Disable the button so the user can only press it once at a time
|
||||
updateNowButton->setDisabled(true);
|
||||
updateNowButton->setText(tr("Updating..."));
|
||||
if (spoilerUpdater) {
|
||||
return;
|
||||
}
|
||||
// Application ownership lets either operation finish after settings closes.
|
||||
// When disabled, the same button retries file removal and the local reload.
|
||||
SpoilerBackgroundUpdater *sbu;
|
||||
if (SettingsCache::instance().downloads().getDownloadSpoilersStatus()) {
|
||||
sbu = new SpoilerBackgroundUpdater(QCoreApplication::instance(), true);
|
||||
// Only an updater this page creates is retired by deleteLater: observation
|
||||
// may attach to the stack-allocated launch updater, which must not be deleted.
|
||||
connect(sbu, &SpoilerBackgroundUpdater::spoilerCheckerDone, sbu, &QObject::deleteLater);
|
||||
} else {
|
||||
// deleteSpoilerFile() wires its own deleteLater.
|
||||
sbu = SpoilerBackgroundUpdater::deleteSpoilerFile();
|
||||
}
|
||||
observeSpoilerUpdater(sbu);
|
||||
}
|
||||
|
||||
// Create a new SBU that will act as if the client was just reloaded
|
||||
auto *sbu = new SpoilerBackgroundUpdater();
|
||||
connect(sbu, &SpoilerBackgroundUpdater::spoilerCheckerDone, this, &DeckEditorSettingsPage::unlockSettings);
|
||||
connect(sbu, &SpoilerBackgroundUpdater::spoilersUpdatedSuccessfully, this, &DeckEditorSettingsPage::unlockSettings);
|
||||
void DeckEditorSettingsPage::observeSpoilerUpdater(SpoilerBackgroundUpdater *sbu)
|
||||
{
|
||||
spoilerUpdater = sbu;
|
||||
unlockSettings();
|
||||
connect(sbu, &SpoilerBackgroundUpdater::spoilerCheckerDone, this, [this, sbu] {
|
||||
if (spoilerUpdater == sbu) {
|
||||
spoilerUpdater.clear();
|
||||
unlockSettings();
|
||||
}
|
||||
});
|
||||
connect(sbu, &SpoilerBackgroundUpdater::spoilerUpdateFailed, this, [this, sbu](const QString &message) {
|
||||
if (spoilerUpdater != sbu) {
|
||||
return;
|
||||
}
|
||||
auto *notice = new QMessageBox(QMessageBox::Warning, tr("Spoiler update"), message, QMessageBox::Ok, this);
|
||||
notice->setTextFormat(Qt::PlainText);
|
||||
notice->setAttribute(Qt::WA_DeleteOnClose);
|
||||
notice->open();
|
||||
});
|
||||
}
|
||||
|
||||
void DeckEditorSettingsPage::unlockSettings()
|
||||
{
|
||||
updateNowButton->setDisabled(false);
|
||||
updateNowButton->setText(tr("Update Spoilers"));
|
||||
updateNowButton->setEnabled(!spoilerUpdater);
|
||||
updateNowButton->setText(spoilerUpdater ? tr("Updating...")
|
||||
: SettingsCache::instance().downloads().getDownloadSpoilersStatus()
|
||||
? tr("Update Spoilers")
|
||||
: tr("Reload Card Database"));
|
||||
lastUpdatedLabel.setText(tr("Last Change") + ": " + getLastUpdateTime());
|
||||
infoOnSpoilersLabel.setText(
|
||||
SettingsCache::instance().downloads().getDownloadSpoilersStatus()
|
||||
? tr("Spoilers download automatically on launch") + "\n" +
|
||||
tr("Press the button to manually update without relaunching") + "\n\n" +
|
||||
tr("Updates continue if you close settings")
|
||||
: tr("Spoilers are off. Reload Card Database removes any downloaded spoiler file and reloads your "
|
||||
"card data."));
|
||||
}
|
||||
|
||||
QString DeckEditorSettingsPage::getLastUpdateTime()
|
||||
|
|
@ -379,12 +427,17 @@ void DeckEditorSettingsPage::setSpoilersEnabled(bool anInput)
|
|||
mpSpoilerSavePathLineEdit->setEnabled(anInput);
|
||||
mpSpoilerPathButton->setEnabled(anInput);
|
||||
lastUpdatedLabel.setEnabled(anInput);
|
||||
updateNowButton->setEnabled(anInput);
|
||||
infoOnSpoilersLabel.setEnabled(anInput);
|
||||
// The hint explains the button in both states, especially the disabled-state
|
||||
// Reload Card Database action, so keep it readable while spoilers are off.
|
||||
infoOnSpoilersLabel.setEnabled(true);
|
||||
|
||||
if (!anInput) {
|
||||
SpoilerBackgroundUpdater::deleteSpoilerFile();
|
||||
// Only react to the user disabling the setting. The constructor calls this to
|
||||
// initialize the controls, and a leftover spoiler file is recovered through the
|
||||
// Reload Card Database button rather than an unrequested reload on page open.
|
||||
if (!anInput && sender() == &mcDownloadSpoilersCheckBox) {
|
||||
observeSpoilerUpdater(SpoilerBackgroundUpdater::deleteSpoilerFile());
|
||||
}
|
||||
unlockSettings();
|
||||
}
|
||||
|
||||
void DeckEditorSettingsPage::retranslateUi()
|
||||
|
|
@ -394,13 +447,10 @@ void DeckEditorSettingsPage::retranslateUi()
|
|||
mcDownloadSpoilersCheckBox.setText(tr("Download Spoilers Automatically"));
|
||||
mcSpoilerSaveLabel.setText(tr("Spoiler Location:"));
|
||||
lastUpdatedLabel.setText(tr("Last Change") + ": " + getLastUpdateTime());
|
||||
infoOnSpoilersLabel.setText(tr("Spoilers download automatically on launch") + "\n" +
|
||||
tr("Press the button to manually update without relaunching") + "\n\n" +
|
||||
tr("Do not close settings until manual update is complete"));
|
||||
picDownloadCheckBox.setText(tr("Download card pictures on the fly"));
|
||||
urlLinkLabel.setText(QString("<a href='%1'>%2</a>").arg(WIKI_CUSTOM_PIC_URL).arg(tr("How to add a custom URL")));
|
||||
resetDownloadURLs.setText(tr("Reset Download URLs"));
|
||||
updateNowButton->setText(tr("Update Spoilers"));
|
||||
unlockSettings();
|
||||
aAdd->setText(tr("Add New URL"));
|
||||
aEdit->setText(tr("Edit URL"));
|
||||
aRemove->setText(tr("Remove URL"));
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
|
||||
#include <QCheckBox>
|
||||
#include <QLabel>
|
||||
#include <QPointer>
|
||||
#include <QPushButton>
|
||||
#include <QString>
|
||||
#include <qtmetamacros.h>
|
||||
|
|
@ -15,6 +16,7 @@ class QLineEdit;
|
|||
class QListWidget;
|
||||
class QListWidgetItem;
|
||||
class QModelIndex;
|
||||
class SpoilerBackgroundUpdater;
|
||||
|
||||
class DeckEditorSettingsPage : public AbstractSettingsPage
|
||||
{
|
||||
|
|
@ -54,6 +56,7 @@ private:
|
|||
QLabel infoOnSpoilersLabel;
|
||||
QPushButton *mpSpoilerPathButton;
|
||||
QPushButton *updateNowButton;
|
||||
QPointer<SpoilerBackgroundUpdater> spoilerUpdater;
|
||||
|
||||
/** @brief Adds a list item for the given URL, storing the raw URL alongside its displayed label. */
|
||||
QListWidgetItem *addUrlItem(const QString &url);
|
||||
|
|
@ -66,6 +69,8 @@ private:
|
|||
|
||||
/** @brief Refreshes the displayed label of every URL item after limits or settings change. */
|
||||
void refreshUrlItems();
|
||||
|
||||
void observeSpoilerUpdater(SpoilerBackgroundUpdater *updater);
|
||||
};
|
||||
|
||||
#endif // COCKATRICE_DECK_EDITOR_SETTINGS_PAGE_H
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue