mirror of
https://github.com/Cockatrice/Cockatrice.git
synced 2026-09-23 01:55:10 -07:00
[DeckShare] Create temporary share links for local and server decks
This commit is contained in:
parent
45fec7fc74
commit
d91fe34d20
26 changed files with 967 additions and 15 deletions
|
|
@ -1,10 +1,23 @@
|
|||
#include "tab_deck_storage_visual.h"
|
||||
|
||||
#include "../../../../main.h"
|
||||
#include "../../../deck_loader/deck_loader.h"
|
||||
#include "../../cards/additional_info/deck_color_identity.h"
|
||||
#include "../../deck_share/deck_share_utils.h"
|
||||
#include "../../interface/widgets/visual_deck_storage/visual_deck_storage_widget.h"
|
||||
#include "../tab_supervisor.h"
|
||||
|
||||
#include <QMainWindow>
|
||||
#include <QMessageBox>
|
||||
#include <libcockatrice/protocol/pb/command_deck_del.pb.h>
|
||||
#include <QStatusBar>
|
||||
#include <QSystemTrayIcon>
|
||||
#include <QVBoxLayout>
|
||||
#include <libcockatrice/deck_list/deck_list.h>
|
||||
#include <libcockatrice/network/client/abstract/abstract_client.h>
|
||||
#include <libcockatrice/protocol/pb/command_deck_share_create.pb.h>
|
||||
#include <libcockatrice/protocol/pb/response.pb.h>
|
||||
#include <libcockatrice/protocol/pb/response_deck_share_create.pb.h>
|
||||
#include <libcockatrice/protocol/pending_command.h>
|
||||
|
||||
TabDeckStorageVisual::TabDeckStorageVisual(TabSupervisor *_tabSupervisor)
|
||||
: Tab(_tabSupervisor), visualDeckStorageWidget(new VisualDeckStorageWidget(this))
|
||||
|
|
@ -14,12 +27,42 @@ TabDeckStorageVisual::TabDeckStorageVisual(TabSupervisor *_tabSupervisor)
|
|||
&TabDeckStorageVisual::actOpenLocalDeck);
|
||||
connect(visualDeckStorageWidget, &VisualDeckStorageWidget::openDeckEditor, this,
|
||||
&TabDeckStorageVisual::openDeckEditor);
|
||||
connect(visualDeckStorageWidget, &VisualDeckStorageWidget::shareDeckRequested, this,
|
||||
&TabDeckStorageVisual::actShareDeck);
|
||||
connect(visualDeckStorageWidget, &VisualDeckStorageWidget::shareSelectionChanged, this,
|
||||
&TabDeckStorageVisual::onShareSelectionChanged);
|
||||
connect(visualDeckStorageWidget, &VisualDeckStorageWidget::shareRequested, this, [this] {
|
||||
if (shareDeckAvailable) {
|
||||
enterShareMode();
|
||||
}
|
||||
});
|
||||
|
||||
AbstractClient *client = tabSupervisor->getClient();
|
||||
connect(client, &AbstractClient::statusChanged, this, &TabDeckStorageVisual::handleConnectionChanged);
|
||||
shareDeckAvailable = (client->getStatus() == StatusLoggedIn);
|
||||
visualDeckStorageWidget->setShareAvailable(shareDeckAvailable);
|
||||
|
||||
auto *widget = new QWidget(this);
|
||||
auto *layout = new QVBoxLayout(widget);
|
||||
layout->setContentsMargins(0, 0, 0, 0);
|
||||
layout->setSpacing(0);
|
||||
widget->setLayout(layout);
|
||||
this->setCentralWidget(widget);
|
||||
layout->addWidget(visualDeckStorageWidget);
|
||||
|
||||
shareBar = new ShareBarWidget(this);
|
||||
connect(shareBar, &ShareBarWidget::createRequested, this, &TabDeckStorageVisual::actShareSelected);
|
||||
connect(shareBar, &ShareBarWidget::cancelRequested, this, &TabDeckStorageVisual::exitShareMode);
|
||||
|
||||
layout->insertWidget(0, shareBar);
|
||||
shareBar->setVisible(false);
|
||||
|
||||
retranslateUi();
|
||||
}
|
||||
|
||||
void TabDeckStorageVisual::retranslateUi()
|
||||
{
|
||||
shareBar->retranslateUi();
|
||||
}
|
||||
|
||||
void TabDeckStorageVisual::actOpenLocalDeck(const QString &filePath)
|
||||
|
|
@ -33,3 +76,118 @@ void TabDeckStorageVisual::actOpenLocalDeck(const QString &filePath)
|
|||
|
||||
emit openDeckEditor(deckOpt.value());
|
||||
}
|
||||
|
||||
void TabDeckStorageVisual::enterShareMode(const QStringList &preselectFiles)
|
||||
{
|
||||
if (!shareDeckAvailable) {
|
||||
return; // sharing is gated on being logged in
|
||||
}
|
||||
visualDeckStorageWidget->setShareSelectable(true);
|
||||
visualDeckStorageWidget->setShareSelectedFiles(preselectFiles);
|
||||
shareBar->setName(tr("Shared decks"));
|
||||
shareBar->setVisible(true);
|
||||
updateShareHint();
|
||||
shareBar->focusName();
|
||||
}
|
||||
|
||||
void TabDeckStorageVisual::exitShareMode()
|
||||
{
|
||||
visualDeckStorageWidget->setShareSelectable(false);
|
||||
visualDeckStorageWidget->clearShareSelection();
|
||||
shareBar->setVisible(false);
|
||||
}
|
||||
|
||||
void TabDeckStorageVisual::actShareDeck(const QString &filePath)
|
||||
{
|
||||
enterShareMode({filePath});
|
||||
}
|
||||
|
||||
void TabDeckStorageVisual::onShareSelectionChanged()
|
||||
{
|
||||
if (shareBar->isVisible()) {
|
||||
updateShareHint();
|
||||
}
|
||||
}
|
||||
|
||||
void TabDeckStorageVisual::updateShareHint()
|
||||
{
|
||||
const int count = visualDeckStorageWidget->selectedFilePaths().size();
|
||||
shareBar->setCountText(tr("%n deck(s)", "", count));
|
||||
if (count == 0) {
|
||||
shareBar->setHintText(tr("Click deck tiles to select the decks you want to share."), true);
|
||||
} else {
|
||||
shareBar->setHintText(tr("%n deck(s) selected. Create the link to share %1 with other players.", "", count)
|
||||
.arg(count == 1 ? tr("it") : tr("them")),
|
||||
true);
|
||||
}
|
||||
}
|
||||
|
||||
void TabDeckStorageVisual::actShareSelected()
|
||||
{
|
||||
const QStringList filePaths = visualDeckStorageWidget->selectedFilePaths();
|
||||
if (filePaths.isEmpty()) {
|
||||
QMessageBox::warning(this, tr("Share decks"), tr("Select at least one deck to share."));
|
||||
return;
|
||||
}
|
||||
|
||||
Command_DeckShareCreate cmd;
|
||||
cmd.set_name(shareBar->name().toStdString());
|
||||
if (cmd.name().empty()) {
|
||||
cmd.set_name(tr("Shared decks").toStdString());
|
||||
}
|
||||
|
||||
for (const QString &filePath : filePaths) {
|
||||
std::optional<LoadedDeck> deckOpt =
|
||||
DeckLoader::loadFromFile(filePath, DeckFileFormat::getFormatFromName(filePath), true);
|
||||
if (!deckOpt) {
|
||||
QMessageBox::warning(this, tr("Share decks"), tr("Unable to load deck file %1").arg(filePath));
|
||||
return;
|
||||
}
|
||||
DeckShareItem *item = cmd.add_items();
|
||||
item->set_deck_list(deckOpt->deckList.writeToString_Native().toStdString());
|
||||
item->set_color_identity(getDeckColorIdentity(deckOpt->deckList).toStdString());
|
||||
}
|
||||
|
||||
PendingCommand *pend = tabSupervisor->getClient()->prepareSessionCommand(cmd);
|
||||
connect(pend, &PendingCommand::finished, this, &TabDeckStorageVisual::shareFinished);
|
||||
tabSupervisor->getClient()->sendCommand(pend);
|
||||
}
|
||||
|
||||
void TabDeckStorageVisual::shareFinished(const Response &response, const CommandContainer & /*commandContainer*/)
|
||||
{
|
||||
if (response.response_code() != Response::RespOk) {
|
||||
showShareNotice(tr("Failed to create the share link (server response code %1).")
|
||||
.arg(QString::number(static_cast<int>(response.response_code()))));
|
||||
exitShareMode();
|
||||
return;
|
||||
}
|
||||
|
||||
const Response_DeckShareCreate &resp = response.GetExtension(Response_DeckShareCreate::ext);
|
||||
const QString token = QString::fromStdString(resp.token());
|
||||
const QDateTime expiry = QDateTime::fromSecsSinceEpoch(resp.expires_at(), QTimeZone::UTC);
|
||||
|
||||
const QString link = DeckShareUtils::buildShareLink(tabSupervisor->getClient(), token);
|
||||
DeckShareUtils::copyShareLinkToClipboard(link);
|
||||
|
||||
showShareNotice(
|
||||
tr("Share link copied to the clipboard.\nExpires on %1.").arg(DeckShareUtils::formatShareExpiry(expiry)));
|
||||
exitShareMode();
|
||||
}
|
||||
|
||||
void TabDeckStorageVisual::handleConnectionChanged(ClientStatus status)
|
||||
{
|
||||
shareDeckAvailable = (status == StatusLoggedIn);
|
||||
visualDeckStorageWidget->setShareAvailable(shareDeckAvailable);
|
||||
if (!shareDeckAvailable && shareBar->isVisible()) {
|
||||
exitShareMode();
|
||||
}
|
||||
}
|
||||
|
||||
void TabDeckStorageVisual::showShareNotice(const QString &message)
|
||||
{
|
||||
if (trayIcon && trayIcon->isVisible()) {
|
||||
trayIcon->showMessage(tr("Deck share"), message);
|
||||
} else if (auto *mainWindow = qobject_cast<QMainWindow *>(window())) {
|
||||
mainWindow->statusBar()->showMessage(message, 10000);
|
||||
}
|
||||
}
|
||||
|
|
@ -7,8 +7,12 @@
|
|||
#ifndef TAB_DECK_STORAGE_VISUAL_H
|
||||
#define TAB_DECK_STORAGE_VISUAL_H
|
||||
|
||||
#include "../../deck_share/share_bar_widget.h"
|
||||
#include "../tab.h"
|
||||
|
||||
#include <QStringList>
|
||||
#include <libcockatrice/network/client/abstract/abstract_client.h>
|
||||
|
||||
struct LoadedDeck;
|
||||
class AbstractClient;
|
||||
class CommandContainer;
|
||||
|
|
@ -27,22 +31,49 @@ class TabDeckStorageVisual final : public Tab
|
|||
Q_OBJECT
|
||||
public:
|
||||
explicit TabDeckStorageVisual(TabSupervisor *_tabSupervisor);
|
||||
void retranslateUi() override
|
||||
{
|
||||
}
|
||||
void retranslateUi() override;
|
||||
|
||||
[[nodiscard]] QString getTabText() const override
|
||||
{
|
||||
return tr("Visual Deck Storage");
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Enters share-selection mode, optionally preselecting the given deck files.
|
||||
*/
|
||||
void enterShareMode(const QStringList &preselectFiles = {});
|
||||
|
||||
/**
|
||||
* @brief Leaves share-selection mode and clears the selection.
|
||||
*/
|
||||
void exitShareMode();
|
||||
|
||||
[[nodiscard]] bool isShareModeActive() const
|
||||
{
|
||||
return shareBar->isVisible();
|
||||
}
|
||||
|
||||
public slots:
|
||||
void actOpenLocalDeck(const QString &filePath);
|
||||
void actShareDeck(const QString &filePath);
|
||||
|
||||
signals:
|
||||
void openDeckEditor(const LoadedDeck &deck);
|
||||
|
||||
private slots:
|
||||
void actShareSelected();
|
||||
void shareFinished(const Response &response, const CommandContainer &commandContainer);
|
||||
void onShareSelectionChanged();
|
||||
void handleConnectionChanged(ClientStatus status);
|
||||
|
||||
private:
|
||||
void showShareNotice(const QString &message);
|
||||
void updateShareHint();
|
||||
|
||||
VisualDeckStorageWidget *visualDeckStorageWidget;
|
||||
|
||||
ShareBarWidget *shareBar;
|
||||
bool shareDeckAvailable = false;
|
||||
};
|
||||
|
||||
#endif
|
||||
#endif
|
||||
Loading…
Add table
Add a link
Reference in a new issue