mirror of
https://github.com/Cockatrice/Cockatrice.git
synced 2026-11-05 01:23:24 -08:00
[Client] Keep Commander Spellbook requests bound to the active deck (#7722)
* [Client] Keep Commander Spellbook requests bound to the active deck [skip ci] * [Client] Restore the bracket info tooltip when idle retranslateUi() used to install the static "Why this bracket?" tooltip, but it now calls displayEstimate(), which returned early while no estimate existed. That left the info button without its idle tooltip. Set the fallback where displayEstimate() owns the button's tooltip. An existing estimate still wins, and a language change refreshes the text. --------- Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
This commit is contained in:
parent
94f7de42f2
commit
624163916f
8 changed files with 182 additions and 45 deletions
|
|
@ -26,6 +26,11 @@ quint64 CommanderBracketService::estimateBracket(const DeckList &deck, QObject *
|
||||||
return CommanderSpellbookApiAccessor::instance().estimateBracket(deck, requester);
|
return CommanderSpellbookApiAccessor::instance().estimateBracket(deck, requester);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CommanderBracketService::cancelEstimate(quint64 requestId)
|
||||||
|
{
|
||||||
|
CommanderSpellbookApiAccessor::instance().cancelEstimate(requestId);
|
||||||
|
}
|
||||||
|
|
||||||
void CommanderBracketService::onEstimateBracketFinished(CommanderSpellbookApiAccessor::RequestId id,
|
void CommanderBracketService::onEstimateBracketFinished(CommanderSpellbookApiAccessor::RequestId id,
|
||||||
QObject *requester,
|
QObject *requester,
|
||||||
const EstimateBracketResult &result)
|
const EstimateBracketResult &result)
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,7 @@ public:
|
||||||
static CommanderBracketService &instance();
|
static CommanderBracketService &instance();
|
||||||
|
|
||||||
quint64 estimateBracket(const DeckList &deck, QObject *requester);
|
quint64 estimateBracket(const DeckList &deck, QObject *requester);
|
||||||
|
void cancelEstimate(quint64 requestId);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void estimateFinished(quint64 requestId, QObject *requester, const CommanderBracketEstimate &estimate);
|
void estimateFinished(quint64 requestId, QObject *requester, const CommanderBracketEstimate &estimate);
|
||||||
|
|
|
||||||
|
|
@ -28,9 +28,18 @@ template <class T> class QSharedPointer;
|
||||||
|
|
||||||
CommanderBracketWidget::CommanderBracketWidget(QWidget *parent) : QWidget(parent)
|
CommanderBracketWidget::CommanderBracketWidget(QWidget *parent) : QWidget(parent)
|
||||||
{
|
{
|
||||||
|
autoEstimateTimer = new QTimer(this);
|
||||||
|
autoEstimateTimer->setSingleShot(true);
|
||||||
|
connect(autoEstimateTimer, &QTimer::timeout, this, [this]() {
|
||||||
|
if (SettingsCache::instance().deckEditor().getCommanderSpellbookIntegrationEnabled() ==
|
||||||
|
commanderSpellbookIntegrationEnabledIndexAutomatic) {
|
||||||
|
requestBracketEstimate();
|
||||||
|
}
|
||||||
|
});
|
||||||
bracketLabel = new QLabel(tr("Bracket:"), this);
|
bracketLabel = new QLabel(tr("Bracket:"), this);
|
||||||
|
|
||||||
bracketValueLabel = new QLabel(this);
|
bracketValueLabel = new QLabel(this);
|
||||||
|
bracketValueLabel->setTextFormat(Qt::PlainText);
|
||||||
bracketValueLabel->setText("-");
|
bracketValueLabel->setText("-");
|
||||||
bracketValueLabel->setObjectName("bracketValueLabel");
|
bracketValueLabel->setObjectName("bracketValueLabel");
|
||||||
|
|
||||||
|
|
@ -65,18 +74,38 @@ CommanderBracketWidget::CommanderBracketWidget(QWidget *parent) : QWidget(parent
|
||||||
this, &CommanderBracketWidget::maybeAutoEstimateBracket);
|
this, &CommanderBracketWidget::maybeAutoEstimateBracket);
|
||||||
connect(&SettingsCache::instance().deckEditor(),
|
connect(&SettingsCache::instance().deckEditor(),
|
||||||
&DeckEditorSettings::commanderSpellbookIntegrationUseOfficialBracketNamesChanged, this,
|
&DeckEditorSettings::commanderSpellbookIntegrationUseOfficialBracketNamesChanged, this,
|
||||||
&CommanderBracketWidget::maybeAutoEstimateBracket);
|
&CommanderBracketWidget::displayEstimate);
|
||||||
|
|
||||||
setVisible(false);
|
setVisible(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CommanderBracketWidget::~CommanderBracketWidget()
|
||||||
|
{
|
||||||
|
cancelEstimate();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CommanderBracketWidget::cancelEstimate()
|
||||||
|
{
|
||||||
|
autoEstimateTimer->stop();
|
||||||
|
const quint64 canceledRequest = requestId;
|
||||||
|
requestId = 0;
|
||||||
|
CommanderBracketService::instance().cancelEstimate(canceledRequest);
|
||||||
|
bracketRefreshButton->setEnabled(true);
|
||||||
|
if (canceledRequest) {
|
||||||
|
bracketValueLabel->setText("-");
|
||||||
|
bracketValueLabel->setToolTip({});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void CommanderBracketWidget::setDeck(const QSharedPointer<DeckList> &_deck)
|
void CommanderBracketWidget::setDeck(const QSharedPointer<DeckList> &_deck)
|
||||||
{
|
{
|
||||||
|
cancelEstimate();
|
||||||
deck = _deck;
|
deck = _deck;
|
||||||
requestId = 0; // invalidate any in-flight estimate for the previous deck
|
hasEstimate = false;
|
||||||
|
|
||||||
// Reset the displayed bracket
|
// Reset the displayed bracket
|
||||||
bracketValueLabel->setText("-");
|
bracketValueLabel->setText("-");
|
||||||
|
bracketValueLabel->setToolTip({});
|
||||||
bracketInfoButton->setToolTip({});
|
bracketInfoButton->setToolTip({});
|
||||||
bracketInfoButton->setEnabled(false);
|
bracketInfoButton->setEnabled(false);
|
||||||
bracketRefreshButton->setEnabled(true);
|
bracketRefreshButton->setEnabled(true);
|
||||||
|
|
@ -144,7 +173,7 @@ bool CommanderBracketWidget::promptCommanderSpellbookIntegration()
|
||||||
dialog.exec();
|
dialog.exec();
|
||||||
|
|
||||||
// Persist naming choice (if not disabled)
|
// Persist naming choice (if not disabled)
|
||||||
if (clickedButton != disableBtn) {
|
if (clickedButton == enableBtn || clickedButton == automaticBtn) {
|
||||||
bool useOfficial = namingCombo->currentIndex() == 1;
|
bool useOfficial = namingCombo->currentIndex() == 1;
|
||||||
SettingsCache::instance().deckEditor().setCommanderSpellbookIntegrationUseOfficialBracketNames(useOfficial);
|
SettingsCache::instance().deckEditor().setCommanderSpellbookIntegrationUseOfficialBracketNames(useOfficial);
|
||||||
}
|
}
|
||||||
|
|
@ -176,9 +205,20 @@ void CommanderBracketWidget::updateBracketVisibility(bool visible)
|
||||||
|
|
||||||
void CommanderBracketWidget::requestBracketEstimate()
|
void CommanderBracketWidget::requestBracketEstimate()
|
||||||
{
|
{
|
||||||
|
const int mode = SettingsCache::instance().deckEditor().getCommanderSpellbookIntegrationEnabled();
|
||||||
|
if (!deck || deck->getGameFormat().compare("commander", Qt::CaseInsensitive) != 0 || requestId ||
|
||||||
|
(mode != commanderSpellbookIntegrationEnabledIndexEnabled &&
|
||||||
|
mode != commanderSpellbookIntegrationEnabledIndexAutomatic)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
autoEstimateTimer->stop();
|
||||||
|
hasEstimate = false;
|
||||||
bracketRefreshButton->setEnabled(false);
|
bracketRefreshButton->setEnabled(false);
|
||||||
bracketInfoButton->setEnabled(false);
|
bracketInfoButton->setEnabled(false);
|
||||||
|
bracketInfoButton->setToolTip({});
|
||||||
bracketValueLabel->setText(tr("Calculating…"));
|
bracketValueLabel->setText(tr("Calculating…"));
|
||||||
|
bracketValueLabel->setToolTip({});
|
||||||
|
|
||||||
requestId = CommanderBracketService::instance().estimateBracket(*deck, this);
|
requestId = CommanderBracketService::instance().estimateBracket(*deck, this);
|
||||||
}
|
}
|
||||||
|
|
@ -191,22 +231,35 @@ void CommanderBracketWidget::onEstimateBracketFinished(quint64 id,
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
requestId = 0;
|
||||||
|
hasEstimate = true;
|
||||||
|
lastEstimate = result;
|
||||||
|
displayEstimate();
|
||||||
|
bracketRefreshButton->setEnabled(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CommanderBracketWidget::displayEstimate()
|
||||||
|
{
|
||||||
|
if (!hasEstimate) {
|
||||||
|
bracketInfoButton->setToolTip(tr("Why this bracket?"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
BracketExplainer explainer;
|
BracketExplainer explainer;
|
||||||
lastBracketExplanation = explainer.explain(result.rawResult);
|
lastBracketExplanation = explainer.explain(lastEstimate.rawResult);
|
||||||
|
|
||||||
// Display bracket
|
// Display bracket
|
||||||
bracketValueLabel->setText(
|
bracketValueLabel->setText(
|
||||||
SettingsCache::instance().deckEditor().getCommanderSpellbookIntegrationUseOfficialBracketNames()
|
SettingsCache::instance().deckEditor().getCommanderSpellbookIntegrationUseOfficialBracketNames()
|
||||||
? result.officialName
|
? lastEstimate.officialName
|
||||||
: result.displayName);
|
: lastEstimate.displayName);
|
||||||
bracketRefreshButton->setEnabled(true);
|
|
||||||
|
|
||||||
// Build tooltip
|
// Build tooltip
|
||||||
QString tooltip;
|
QString tooltip;
|
||||||
for (const auto §ion : lastBracketExplanation.sections) {
|
for (const auto §ion : lastBracketExplanation.sections) {
|
||||||
tooltip += "<b>" + section.title + "</b><br>";
|
tooltip += "<b>" + section.title.toHtmlEscaped() + "</b><br>";
|
||||||
for (const auto &line : section.bulletPoints) {
|
for (const auto &line : section.bulletPoints) {
|
||||||
tooltip += "• " + line + "<br>";
|
tooltip += "• " + line.toHtmlEscaped() + "<br>";
|
||||||
}
|
}
|
||||||
tooltip += "<br>";
|
tooltip += "<br>";
|
||||||
}
|
}
|
||||||
|
|
@ -215,13 +268,17 @@ void CommanderBracketWidget::onEstimateBracketFinished(quint64 id,
|
||||||
bracketInfoButton->setEnabled(!tooltip.isEmpty());
|
bracketInfoButton->setEnabled(!tooltip.isEmpty());
|
||||||
}
|
}
|
||||||
|
|
||||||
void CommanderBracketWidget::onEstimateBracketError(quint64 id, QObject *requester, const QString & /*error*/)
|
void CommanderBracketWidget::onEstimateBracketError(quint64 id, QObject *requester, const QString &error)
|
||||||
{
|
{
|
||||||
if (requester != this || id != requestId) {
|
if (requester != this || id != requestId) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
requestId = 0;
|
||||||
|
hasEstimate = false;
|
||||||
bracketValueLabel->setText("-");
|
bracketValueLabel->setText("-");
|
||||||
|
bracketValueLabel->setToolTip(
|
||||||
|
QStringLiteral("<qt>%1</qt>").arg(tr("Unable to estimate bracket: %1").arg(error).toHtmlEscaped()));
|
||||||
bracketRefreshButton->setEnabled(true);
|
bracketRefreshButton->setEnabled(true);
|
||||||
bracketInfoButton->setToolTip({});
|
bracketInfoButton->setToolTip({});
|
||||||
bracketInfoButton->setEnabled(false);
|
bracketInfoButton->setEnabled(false);
|
||||||
|
|
@ -229,6 +286,15 @@ void CommanderBracketWidget::onEstimateBracketError(quint64 id, QObject *request
|
||||||
|
|
||||||
void CommanderBracketWidget::maybeAutoEstimateBracket()
|
void CommanderBracketWidget::maybeAutoEstimateBracket()
|
||||||
{
|
{
|
||||||
|
if (prompting) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
autoEstimateTimer->stop();
|
||||||
|
if (!deck) {
|
||||||
|
cancelEstimate();
|
||||||
|
updateBracketVisibility(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
const QString formatKey = deck->getGameFormat();
|
const QString formatKey = deck->getGameFormat();
|
||||||
|
|
||||||
const bool isCommander = (formatKey.compare("commander", Qt::CaseInsensitive) == 0);
|
const bool isCommander = (formatKey.compare("commander", Qt::CaseInsensitive) == 0);
|
||||||
|
|
@ -236,17 +302,20 @@ void CommanderBracketWidget::maybeAutoEstimateBracket()
|
||||||
int mode = SettingsCache::instance().deckEditor().getCommanderSpellbookIntegrationEnabled();
|
int mode = SettingsCache::instance().deckEditor().getCommanderSpellbookIntegrationEnabled();
|
||||||
|
|
||||||
if (!isCommander || mode == commanderSpellbookIntegrationEnabledIndexDisabled) {
|
if (!isCommander || mode == commanderSpellbookIntegrationEnabledIndexDisabled) {
|
||||||
|
cancelEstimate();
|
||||||
updateBracketVisibility(false);
|
updateBracketVisibility(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mode == commanderSpellbookIntegrationEnabledIndexUnprompted) {
|
if (mode == commanderSpellbookIntegrationEnabledIndexUnprompted) {
|
||||||
if (prompting) {
|
const auto promptedDeck = deck;
|
||||||
return;
|
|
||||||
}
|
|
||||||
prompting = true;
|
prompting = true;
|
||||||
const bool accepted = promptCommanderSpellbookIntegration();
|
const bool accepted = promptCommanderSpellbookIntegration();
|
||||||
prompting = false;
|
prompting = false;
|
||||||
|
if (deck != promptedDeck || !deck || deck->getGameFormat().compare("commander", Qt::CaseInsensitive) != 0) {
|
||||||
|
maybeAutoEstimateBracket();
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (!accepted) {
|
if (!accepted) {
|
||||||
updateBracketVisibility(false);
|
updateBracketVisibility(false);
|
||||||
return;
|
return;
|
||||||
|
|
@ -261,17 +330,17 @@ void CommanderBracketWidget::maybeAutoEstimateBracket()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Avoid firing if we already have a result or a request in flight
|
// Avoid firing if we already have a result or a request in flight
|
||||||
if (!bracketRefreshButton->isEnabled()) {
|
if (requestId || hasEstimate) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Defer to avoid races during init / model rebuild
|
// Defer to avoid races during init / model rebuild
|
||||||
QTimer::singleShot(0, this, &CommanderBracketWidget::requestBracketEstimate);
|
autoEstimateTimer->start(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CommanderBracketWidget::retranslateUi()
|
void CommanderBracketWidget::retranslateUi()
|
||||||
{
|
{
|
||||||
bracketLabel->setText(tr("Bracket:"));
|
bracketLabel->setText(tr("Bracket:"));
|
||||||
bracketInfoButton->setToolTip(tr("Why this bracket?"));
|
displayEstimate();
|
||||||
bracketRefreshButton->setToolTip(tr("Recalculate bracket"));
|
bracketRefreshButton->setToolTip(tr("Recalculate bracket"));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
#ifndef COCKATRICE_COMMANDER_BRACKET_WIDGET_H
|
#ifndef COCKATRICE_COMMANDER_BRACKET_WIDGET_H
|
||||||
#define COCKATRICE_COMMANDER_BRACKET_WIDGET_H
|
#define COCKATRICE_COMMANDER_BRACKET_WIDGET_H
|
||||||
|
|
||||||
|
#include "commander_bracket_service.h"
|
||||||
#include "commander_spellbook_bracket_explainer.h"
|
#include "commander_spellbook_bracket_explainer.h"
|
||||||
|
|
||||||
#include <QSharedPointer>
|
#include <QSharedPointer>
|
||||||
|
|
@ -11,6 +12,7 @@
|
||||||
|
|
||||||
class QLabel;
|
class QLabel;
|
||||||
class QToolButton;
|
class QToolButton;
|
||||||
|
class QTimer;
|
||||||
struct CommanderBracketEstimate;
|
struct CommanderBracketEstimate;
|
||||||
class DeckList;
|
class DeckList;
|
||||||
class QObject;
|
class QObject;
|
||||||
|
|
@ -22,6 +24,7 @@ class CommanderBracketWidget : public QWidget
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit CommanderBracketWidget(QWidget *parent = nullptr);
|
explicit CommanderBracketWidget(QWidget *parent = nullptr);
|
||||||
|
~CommanderBracketWidget() override;
|
||||||
|
|
||||||
void setDeck(const QSharedPointer<DeckList> &_deck);
|
void setDeck(const QSharedPointer<DeckList> &_deck);
|
||||||
void retranslateUi();
|
void retranslateUi();
|
||||||
|
|
@ -35,10 +38,15 @@ private slots:
|
||||||
private:
|
private:
|
||||||
bool promptCommanderSpellbookIntegration();
|
bool promptCommanderSpellbookIntegration();
|
||||||
void updateBracketVisibility(bool visible);
|
void updateBracketVisibility(bool visible);
|
||||||
|
void cancelEstimate();
|
||||||
|
void displayEstimate();
|
||||||
|
|
||||||
QSharedPointer<DeckList> deck;
|
QSharedPointer<DeckList> deck;
|
||||||
bool prompting = false;
|
bool prompting = false;
|
||||||
quint64 requestId = 0;
|
quint64 requestId = 0;
|
||||||
|
QTimer *autoEstimateTimer;
|
||||||
|
bool hasEstimate = false;
|
||||||
|
CommanderBracketEstimate lastEstimate;
|
||||||
|
|
||||||
QLabel *bracketLabel;
|
QLabel *bracketLabel;
|
||||||
QLabel *bracketValueLabel;
|
QLabel *bracketValueLabel;
|
||||||
|
|
|
||||||
|
|
@ -9,9 +9,10 @@
|
||||||
#include <QJsonParseError>
|
#include <QJsonParseError>
|
||||||
#include <QNetworkReply>
|
#include <QNetworkReply>
|
||||||
#include <QNetworkRequest>
|
#include <QNetworkRequest>
|
||||||
|
#include <QPointer>
|
||||||
#include <QStringLiteral>
|
#include <QStringLiteral>
|
||||||
#include <QUrl>
|
#include <QUrl>
|
||||||
#include <QVariant>
|
#include <libcockatrice/network/network_manager.h>
|
||||||
#include <version_string.h>
|
#include <version_string.h>
|
||||||
|
|
||||||
class DeckList;
|
class DeckList;
|
||||||
|
|
@ -24,13 +25,18 @@ CommanderSpellbookApiAccessor &CommanderSpellbookApiAccessor::instance()
|
||||||
return instance;
|
return instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
CommanderSpellbookApiAccessor::CommanderSpellbookApiAccessor(QObject *parent) : QObject(parent)
|
CommanderSpellbookApiAccessor::CommanderSpellbookApiAccessor(QObject *parent)
|
||||||
|
: QObject(parent), network(NetworkManagerFactory::create(this))
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
CommanderSpellbookApiAccessor::RequestId CommanderSpellbookApiAccessor::estimateBracket(const DeckList &deck,
|
CommanderSpellbookApiAccessor::RequestId CommanderSpellbookApiAccessor::estimateBracket(const DeckList &deck,
|
||||||
QObject *requester)
|
QObject *requester)
|
||||||
{
|
{
|
||||||
|
if (!requester) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
CommanderSpellbookDeckRequest deckRequest = CommanderSpellbookDeckRequest::fromDeckList(deck);
|
CommanderSpellbookDeckRequest deckRequest = CommanderSpellbookDeckRequest::fromDeckList(deck);
|
||||||
|
|
||||||
QJsonDocument doc(deckRequest.toJson());
|
QJsonDocument doc(deckRequest.toJson());
|
||||||
|
|
@ -40,30 +46,39 @@ CommanderSpellbookApiAccessor::RequestId CommanderSpellbookApiAccessor::estimate
|
||||||
req.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
|
req.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
|
||||||
req.setHeader(QNetworkRequest::UserAgentHeader, QString("Cockatrice %1").arg(VERSION_STRING));
|
req.setHeader(QNetworkRequest::UserAgentHeader, QString("Cockatrice %1").arg(VERSION_STRING));
|
||||||
|
|
||||||
QNetworkReply *reply = network.post(req, body);
|
QNetworkReply *reply = network->post(req, body);
|
||||||
|
|
||||||
const RequestId id = nextRequestId++;
|
const RequestId id = nextRequestId++;
|
||||||
|
|
||||||
reply->setProperty("requestId", QVariant::fromValue(id));
|
pendingRequests.insert(id, reply);
|
||||||
reply->setProperty("requester", QVariant::fromValue(requester));
|
const QPointer<QObject> guardedRequester(requester);
|
||||||
|
connect(requester, &QObject::destroyed, reply, [this, id]() { cancelEstimate(id); });
|
||||||
connect(reply, &QNetworkReply::finished, this, [this, reply]() { onEstimateReplyFinished(reply); });
|
connect(reply, &QNetworkReply::finished, this, [this, reply, id, guardedRequester]() {
|
||||||
|
if (pendingRequests.take(id) != reply) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
reply->deleteLater();
|
||||||
|
if (guardedRequester) {
|
||||||
|
onEstimateReplyFinished(reply, id, guardedRequester);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CommanderSpellbookApiAccessor::onEstimateReplyFinished(QNetworkReply *reply)
|
void CommanderSpellbookApiAccessor::cancelEstimate(RequestId id)
|
||||||
{
|
{
|
||||||
reply->deleteLater();
|
const QPointer<QNetworkReply> reply = pendingRequests.take(id);
|
||||||
|
if (!reply) {
|
||||||
const RequestId id = reply->property("requestId").toULongLong();
|
|
||||||
QObject *requester = reply->property("requester").value<QObject *>();
|
|
||||||
|
|
||||||
if (!requester) {
|
|
||||||
// Requester died — silently drop
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
disconnect(reply, nullptr, this, nullptr);
|
||||||
|
reply->abort();
|
||||||
|
reply->deleteLater();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CommanderSpellbookApiAccessor::onEstimateReplyFinished(QNetworkReply *reply, RequestId id, QObject *requester)
|
||||||
|
{
|
||||||
if (reply->error() != QNetworkReply::NoError) {
|
if (reply->error() != QNetworkReply::NoError) {
|
||||||
emit estimateBracketError(id, requester, reply->errorString());
|
emit estimateBracketError(id, requester, reply->errorString());
|
||||||
return;
|
return;
|
||||||
|
|
@ -78,6 +93,10 @@ void CommanderSpellbookApiAccessor::onEstimateReplyFinished(QNetworkReply *reply
|
||||||
}
|
}
|
||||||
|
|
||||||
EstimateBracketResult result = EstimateBracketResult::fromJson(doc.object());
|
EstimateBracketResult result = EstimateBracketResult::fromJson(doc.object());
|
||||||
|
if (result.bracketTag.trimmed().isEmpty()) {
|
||||||
|
emit estimateBracketError(id, requester, QStringLiteral("Missing bracket in response"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
emit estimateBracketFinished(id, requester, result);
|
emit estimateBracketFinished(id, requester, result);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,16 @@
|
||||||
#ifndef COCKATRICE_COMMANDER_SPELLBOOK_API_ACCESSOR_H
|
#ifndef COCKATRICE_COMMANDER_SPELLBOOK_API_ACCESSOR_H
|
||||||
#define COCKATRICE_COMMANDER_SPELLBOOK_API_ACCESSOR_H
|
#define COCKATRICE_COMMANDER_SPELLBOOK_API_ACCESSOR_H
|
||||||
|
|
||||||
#include <QNetworkAccessManager>
|
#include <QHash>
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
|
#include <QPointer>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QtGlobal>
|
#include <QtGlobal>
|
||||||
#include <qtmetamacros.h>
|
#include <qtmetamacros.h>
|
||||||
|
|
||||||
class DeckList;
|
class DeckList;
|
||||||
class QNetworkReply;
|
class QNetworkReply;
|
||||||
|
class QNetworkAccessManager;
|
||||||
struct EstimateBracketResult;
|
struct EstimateBracketResult;
|
||||||
|
|
||||||
class CommanderSpellbookApiAccessor final : public QObject
|
class CommanderSpellbookApiAccessor final : public QObject
|
||||||
|
|
@ -21,6 +23,7 @@ public:
|
||||||
using RequestId = quint64;
|
using RequestId = quint64;
|
||||||
|
|
||||||
RequestId estimateBracket(const DeckList &deck, QObject *requester);
|
RequestId estimateBracket(const DeckList &deck, QObject *requester);
|
||||||
|
void cancelEstimate(RequestId id);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void estimateBracketFinished(RequestId id, QObject *requester, const EstimateBracketResult &result);
|
void estimateBracketFinished(RequestId id, QObject *requester, const EstimateBracketResult &result);
|
||||||
|
|
@ -31,9 +34,10 @@ private:
|
||||||
explicit CommanderSpellbookApiAccessor(QObject *parent = nullptr);
|
explicit CommanderSpellbookApiAccessor(QObject *parent = nullptr);
|
||||||
Q_DISABLE_COPY_MOVE(CommanderSpellbookApiAccessor)
|
Q_DISABLE_COPY_MOVE(CommanderSpellbookApiAccessor)
|
||||||
|
|
||||||
void onEstimateReplyFinished(QNetworkReply *reply);
|
void onEstimateReplyFinished(QNetworkReply *reply, RequestId id, QObject *requester);
|
||||||
|
|
||||||
QNetworkAccessManager network;
|
QNetworkAccessManager *network;
|
||||||
|
QHash<RequestId, QPointer<QNetworkReply>> pendingRequests;
|
||||||
RequestId nextRequestId = 1;
|
RequestId nextRequestId = 1;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,11 +3,14 @@
|
||||||
#include "../../../../../client/settings/cache_settings.h"
|
#include "../../../../../client/settings/cache_settings.h"
|
||||||
|
|
||||||
#include <QByteArray>
|
#include <QByteArray>
|
||||||
|
#include <QJsonArray>
|
||||||
#include <QJsonDocument>
|
#include <QJsonDocument>
|
||||||
|
#include <QJsonObject>
|
||||||
#include <QJsonParseError>
|
#include <QJsonParseError>
|
||||||
#include <QMap>
|
#include <QMap>
|
||||||
#include <QNetworkAccessManager>
|
#include <QNetworkAccessManager>
|
||||||
#include <QNetworkRequest>
|
#include <QNetworkRequest>
|
||||||
|
#include <QSet>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QStringLiteral>
|
#include <QStringLiteral>
|
||||||
#include <QUrl>
|
#include <QUrl>
|
||||||
|
|
@ -22,6 +25,14 @@ HandleCommanderBrackets::HandleCommanderBrackets(QObject *parent)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HandleCommanderBrackets::~HandleCommanderBrackets()
|
||||||
|
{
|
||||||
|
if (reply) {
|
||||||
|
disconnect(reply, nullptr, this, nullptr);
|
||||||
|
reply->abort();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void HandleCommanderBrackets::downloadBracketDefinitions()
|
void HandleCommanderBrackets::downloadBracketDefinitions()
|
||||||
{
|
{
|
||||||
if (reply) {
|
if (reply) {
|
||||||
|
|
@ -35,31 +46,49 @@ void HandleCommanderBrackets::downloadBracketDefinitions()
|
||||||
|
|
||||||
void HandleCommanderBrackets::actFinishParsingDownloadedData()
|
void HandleCommanderBrackets::actFinishParsingDownloadedData()
|
||||||
{
|
{
|
||||||
reply = qobject_cast<QNetworkReply *>(sender());
|
auto *finishedReply = qobject_cast<QNetworkReply *>(sender());
|
||||||
|
if (!finishedReply || finishedReply != reply) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (reply->error() != QNetworkReply::NoError) {
|
// Clear the active request before notifying listeners, which may immediately retry.
|
||||||
emit sigBracketDefinitionsDownloadFailed(reply->error());
|
reply = nullptr;
|
||||||
|
finishedReply->deleteLater();
|
||||||
|
|
||||||
reply->deleteLater();
|
if (finishedReply->error() != QNetworkReply::NoError) {
|
||||||
|
emit sigBracketDefinitionsDownloadFailed(finishedReply->error());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
QJsonParseError parseError;
|
QJsonParseError parseError;
|
||||||
|
|
||||||
auto document = QJsonDocument::fromJson(reply->readAll(), &parseError);
|
auto document = QJsonDocument::fromJson(finishedReply->readAll(), &parseError);
|
||||||
|
|
||||||
if (parseError.error != QJsonParseError::NoError) {
|
if (parseError.error != QJsonParseError::NoError || !document.isObject() ||
|
||||||
|
!document.object().value("brackets").isArray() || document.object().value("brackets").toArray().isEmpty()) {
|
||||||
emit sigBracketDefinitionsDownloadFailed(QNetworkReply::UnknownContentError);
|
emit sigBracketDefinitionsDownloadFailed(QNetworkReply::UnknownContentError);
|
||||||
|
|
||||||
reply->deleteLater();
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Validate the complete replacement before touching the last usable definitions.
|
||||||
|
// Tags are QSettings group names, so path separators must not create nested groups.
|
||||||
|
QSet<QString> tags;
|
||||||
|
for (const auto &value : document.object().value("brackets").toArray()) {
|
||||||
|
const auto definition = value.toObject();
|
||||||
|
const auto tag = definition.value("tag").toString();
|
||||||
|
if (!value.isObject() || tag.trimmed().isEmpty() || tag.contains('/') || tag.contains('\\') ||
|
||||||
|
tags.contains(tag) || definition.value("officialName").toString().trimmed().isEmpty() ||
|
||||||
|
definition.value("displayName").toString().trimmed().isEmpty() ||
|
||||||
|
!definition.value("explanation").isString()) {
|
||||||
|
emit sigBracketDefinitionsDownloadFailed(QNetworkReply::UnknownContentError);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
tags.insert(tag);
|
||||||
|
}
|
||||||
|
|
||||||
updateBracketDefinitions(document.toVariant().toMap());
|
updateBracketDefinitions(document.toVariant().toMap());
|
||||||
|
|
||||||
emit sigBracketDefinitionsDownloaded();
|
emit sigBracketDefinitionsDownloaded();
|
||||||
|
|
||||||
reply->deleteLater();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void HandleCommanderBrackets::updateBracketDefinitions(const QVariantMap &jsonMap)
|
void HandleCommanderBrackets::updateBracketDefinitions(const QVariantMap &jsonMap)
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
#include <QNetworkReply>
|
#include <QNetworkReply>
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
|
#include <QPointer>
|
||||||
#include <QVariantMap>
|
#include <QVariantMap>
|
||||||
#include <qtmetamacros.h>
|
#include <qtmetamacros.h>
|
||||||
|
|
||||||
|
|
@ -14,6 +15,7 @@ class HandleCommanderBrackets : public QObject
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit HandleCommanderBrackets(QObject *parent = nullptr);
|
explicit HandleCommanderBrackets(QObject *parent = nullptr);
|
||||||
|
~HandleCommanderBrackets() override;
|
||||||
|
|
||||||
void downloadBracketDefinitions();
|
void downloadBracketDefinitions();
|
||||||
|
|
||||||
|
|
@ -28,7 +30,7 @@ private:
|
||||||
void updateBracketDefinitions(const QVariantMap &jsonMap);
|
void updateBracketDefinitions(const QVariantMap &jsonMap);
|
||||||
|
|
||||||
QNetworkAccessManager *nam;
|
QNetworkAccessManager *nam;
|
||||||
QNetworkReply *reply;
|
QPointer<QNetworkReply> reply;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // COCKATRICE_HANDLE_COMMANDER_BRACKETS_H
|
#endif // COCKATRICE_HANDLE_COMMANDER_BRACKETS_H
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue