mirror of
https://github.com/Cockatrice/Cockatrice.git
synced 2026-04-27 07:48:01 -07:00
Some checks failed
Build Desktop / Configure (push) Has been cancelled
Build Docker Image / amd64 & arm64 (push) Has been cancelled
Build Desktop / Debian 11 (push) Has been cancelled
Build Desktop / Debian 13 (push) Has been cancelled
Build Desktop / Debian 12 (push) Has been cancelled
Build Desktop / Fedora 43 (push) Has been cancelled
Build Desktop / Fedora 42 (push) Has been cancelled
Build Desktop / Servatrice_Debian 11 (push) Has been cancelled
Build Desktop / Ubuntu 24.04 (push) Has been cancelled
Build Desktop / Ubuntu 26.04 (push) Has been cancelled
Build Desktop / Ubuntu 22.04 (push) Has been cancelled
Build Desktop / Arch (push) Has been cancelled
Build Desktop / macOS 14 (push) Has been cancelled
Build Desktop / macOS 15 (push) Has been cancelled
Build Desktop / macOS 13 Intel (push) Has been cancelled
Build Desktop / macOS 15 Debug (push) Has been cancelled
Build Desktop / Windows 10 (push) Has been cancelled
* fix compiling on arch * redo all the logging in affected files
628 lines
22 KiB
C++
628 lines
22 KiB
C++
#include "tab_replays.h"
|
|
|
|
#include "../../../client/settings/cache_settings.h"
|
|
#include "../interface/widgets/server/remote/remote_replay_list_tree_widget.h"
|
|
#include "tab_game.h"
|
|
|
|
#include <QAction>
|
|
#include <QApplication>
|
|
#include <QClipboard>
|
|
#include <QDesktopServices>
|
|
#include <QFileSystemModel>
|
|
#include <QGroupBox>
|
|
#include <QHBoxLayout>
|
|
#include <QHeaderView>
|
|
#include <QInputDialog>
|
|
#include <QMessageBox>
|
|
#include <QToolBar>
|
|
#include <QTreeView>
|
|
#include <QUrl>
|
|
#include <libcockatrice/network/client/abstract/abstract_client.h>
|
|
#include <libcockatrice/protocol/pb/command_replay_delete_match.pb.h>
|
|
#include <libcockatrice/protocol/pb/command_replay_download.pb.h>
|
|
#include <libcockatrice/protocol/pb/command_replay_get_code.pb.h>
|
|
#include <libcockatrice/protocol/pb/command_replay_modify_match.pb.h>
|
|
#include <libcockatrice/protocol/pb/command_replay_submit_code.pb.h>
|
|
#include <libcockatrice/protocol/pb/event_replay_added.pb.h>
|
|
#include <libcockatrice/protocol/pb/game_replay.pb.h>
|
|
#include <libcockatrice/protocol/pb/response.pb.h>
|
|
#include <libcockatrice/protocol/pb/response_replay_download.pb.h>
|
|
#include <libcockatrice/protocol/pb/response_replay_get_code.pb.h>
|
|
#include <libcockatrice/protocol/pending_command.h>
|
|
|
|
inline Q_LOGGING_CATEGORY(TabReplaysLog, "replays_tab");
|
|
|
|
TabReplays::TabReplays(TabSupervisor *_tabSupervisor, AbstractClient *_client, const ServerInfo_User *currentUserInfo)
|
|
: Tab(_tabSupervisor), client(_client)
|
|
{
|
|
leftGroupBox = createLeftLayout();
|
|
rightGroupBox = createRightLayout();
|
|
|
|
// combine layouts
|
|
QHBoxLayout *hbox = new QHBoxLayout;
|
|
hbox->addWidget(leftGroupBox);
|
|
hbox->addWidget(rightGroupBox);
|
|
|
|
retranslateUi();
|
|
|
|
QWidget *mainWidget = new QWidget(this);
|
|
mainWidget->setLayout(hbox);
|
|
setCentralWidget(mainWidget);
|
|
|
|
connect(client, &AbstractClient::replayAddedEventReceived, this, &TabReplays::replayAddedEventReceived);
|
|
|
|
connect(client, &AbstractClient::userInfoChanged, this, &TabReplays::handleConnected);
|
|
connect(client, &AbstractClient::statusChanged, this, &TabReplays::handleConnectionChanged);
|
|
setRemoteEnabled(currentUserInfo && currentUserInfo->user_level() & ServerInfo_User::IsRegistered);
|
|
}
|
|
|
|
QGroupBox *TabReplays::createLeftLayout()
|
|
{
|
|
localDirModel = new QFileSystemModel(this);
|
|
localDirModel->setRootPath(SettingsCache::instance().getReplaysPath());
|
|
localDirModel->sort(0, Qt::AscendingOrder);
|
|
|
|
localDirView = new QTreeView;
|
|
localDirView->setModel(localDirModel);
|
|
localDirView->setColumnHidden(1, true);
|
|
localDirView->setRootIndex(localDirModel->index(localDirModel->rootPath(), 0));
|
|
localDirView->setSortingEnabled(true);
|
|
localDirView->setSelectionMode(QAbstractItemView::ExtendedSelection);
|
|
localDirView->header()->setSectionResizeMode(QHeaderView::ResizeToContents);
|
|
localDirView->header()->setSortIndicator(0, Qt::AscendingOrder);
|
|
|
|
// Left side layout
|
|
/* put an invisible dummy QToolBar in the leftmost column so that the main toolbar is centered.
|
|
* Really ugly workaround, but I couldn't figure out the proper way to make it centered */
|
|
QToolBar *dummyToolBar = new QToolBar(this);
|
|
QSizePolicy sizePolicy = dummyToolBar->sizePolicy();
|
|
sizePolicy.setRetainSizeWhenHidden(true);
|
|
dummyToolBar->setSizePolicy(sizePolicy);
|
|
dummyToolBar->setVisible(false);
|
|
|
|
QToolBar *toolBar = new QToolBar(this);
|
|
toolBar->setOrientation(Qt::Horizontal);
|
|
toolBar->setIconSize(QSize(32, 32));
|
|
|
|
QToolBar *rightmostToolBar = new QToolBar(this);
|
|
rightmostToolBar->setOrientation(Qt::Horizontal);
|
|
rightmostToolBar->setIconSize(QSize(32, 32));
|
|
|
|
QGridLayout *toolBarLayout = new QGridLayout;
|
|
toolBarLayout->addWidget(dummyToolBar, 0, 0, Qt::AlignLeft);
|
|
toolBarLayout->addWidget(toolBar, 0, 1, Qt::AlignHCenter);
|
|
toolBarLayout->addWidget(rightmostToolBar, 0, 2, Qt::AlignRight);
|
|
|
|
QVBoxLayout *vbox = new QVBoxLayout;
|
|
vbox->addWidget(localDirView);
|
|
vbox->addLayout(toolBarLayout);
|
|
QGroupBox *groupBox = new QGroupBox;
|
|
groupBox->setLayout(vbox);
|
|
|
|
// Left side actions
|
|
aOpenLocalReplay = new QAction(this);
|
|
aOpenLocalReplay->setIcon(QPixmap("theme:icons/view"));
|
|
connect(aOpenLocalReplay, &QAction::triggered, this, &TabReplays::actOpenLocalReplay);
|
|
connect(localDirView, &QTreeView::doubleClicked, this, &TabReplays::actOpenLocalReplay);
|
|
aRenameLocal = new QAction(this);
|
|
aRenameLocal->setIcon(QPixmap("theme:icons/rename"));
|
|
connect(aRenameLocal, &QAction::triggered, this, &TabReplays::actRenameLocal);
|
|
aNewLocalFolder = new QAction(this);
|
|
aNewLocalFolder->setIcon(qApp->style()->standardIcon(QStyle::SP_FileDialogNewFolder));
|
|
connect(aNewLocalFolder, &QAction::triggered, this, &TabReplays::actNewLocalFolder);
|
|
aDeleteLocalReplay = new QAction(this);
|
|
aDeleteLocalReplay->setIcon(QPixmap("theme:icons/remove_row"));
|
|
connect(aDeleteLocalReplay, &QAction::triggered, this, &TabReplays::actDeleteLocalReplay);
|
|
|
|
aOpenReplaysFolder = new QAction(this);
|
|
aOpenReplaysFolder->setIcon(qApp->style()->standardIcon(QStyle::SP_DirOpenIcon));
|
|
connect(aOpenReplaysFolder, &QAction::triggered, this, &TabReplays::actOpenReplaysFolder);
|
|
|
|
// Add actions to toolbars
|
|
toolBar->addAction(aOpenLocalReplay);
|
|
toolBar->addAction(aRenameLocal);
|
|
toolBar->addAction(aNewLocalFolder);
|
|
toolBar->addAction(aDeleteLocalReplay);
|
|
|
|
rightmostToolBar->addAction(aOpenReplaysFolder);
|
|
|
|
return groupBox;
|
|
}
|
|
|
|
QGroupBox *TabReplays::createRightLayout()
|
|
{
|
|
serverDirView = new RemoteReplayList_TreeWidget(client);
|
|
|
|
// Right side layout
|
|
/* put an invisible dummy QToolBar in the leftmost column so that the main toolbar is centered.
|
|
* Really ugly workaround, but I couldn't figure out the proper way to make it centered */
|
|
QToolBar *dummyToolBar = new QToolBar(this);
|
|
QSizePolicy sizePolicy = dummyToolBar->sizePolicy();
|
|
sizePolicy.setRetainSizeWhenHidden(true);
|
|
dummyToolBar->setSizePolicy(sizePolicy);
|
|
dummyToolBar->setVisible(false);
|
|
|
|
QToolBar *toolBar = new QToolBar(this);
|
|
toolBar->setOrientation(Qt::Horizontal);
|
|
toolBar->setIconSize(QSize(32, 32));
|
|
|
|
QToolBar *rightmostToolBar = new QToolBar(this);
|
|
rightmostToolBar->setOrientation(Qt::Horizontal);
|
|
rightmostToolBar->setIconSize(QSize(32, 32));
|
|
|
|
QGridLayout *toolBarLayout = new QGridLayout;
|
|
toolBarLayout->addWidget(dummyToolBar, 0, 0, Qt::AlignLeft);
|
|
toolBarLayout->addWidget(toolBar, 0, 1, Qt::AlignHCenter);
|
|
toolBarLayout->addWidget(rightmostToolBar, 0, 2, Qt::AlignRight);
|
|
|
|
QVBoxLayout *vbox = new QVBoxLayout;
|
|
vbox->addWidget(serverDirView);
|
|
vbox->addLayout(toolBarLayout);
|
|
QGroupBox *groupBox = new QGroupBox;
|
|
groupBox->setLayout(vbox);
|
|
|
|
// Right side actions
|
|
aOpenRemoteReplay = new QAction(this);
|
|
aOpenRemoteReplay->setIcon(QPixmap("theme:icons/view"));
|
|
connect(aOpenRemoteReplay, &QAction::triggered, this, &TabReplays::actOpenRemoteReplay);
|
|
connect(serverDirView, &QTreeView::doubleClicked, this, &TabReplays::actOpenRemoteReplay);
|
|
aDownload = new QAction(this);
|
|
aDownload->setIcon(QPixmap("theme:icons/arrow_left_green"));
|
|
connect(aDownload, &QAction::triggered, this, &TabReplays::actDownload);
|
|
aKeep = new QAction(this);
|
|
aKeep->setIcon(QPixmap("theme:icons/lock"));
|
|
connect(aKeep, &QAction::triggered, this, &TabReplays::actKeepRemoteReplay);
|
|
aDeleteRemoteReplay = new QAction(this);
|
|
aDeleteRemoteReplay->setIcon(QPixmap("theme:icons/remove_row"));
|
|
connect(aDeleteRemoteReplay, &QAction::triggered, this, &TabReplays::actDeleteRemoteReplay);
|
|
aGetReplayCode = new QAction(this);
|
|
aGetReplayCode->setIcon(QPixmap("theme:icons/share"));
|
|
connect(aGetReplayCode, &QAction::triggered, this, &TabReplays::actGetReplayCode);
|
|
|
|
aSubmitReplayCode = new QAction(this);
|
|
aSubmitReplayCode->setIcon(QPixmap("theme:icons/search"));
|
|
connect(aSubmitReplayCode, &QAction::triggered, this, &TabReplays::actSubmitReplayCode);
|
|
|
|
// Add actions to toolbars
|
|
toolBar->addAction(aOpenRemoteReplay);
|
|
toolBar->addAction(aDownload);
|
|
toolBar->addAction(aKeep);
|
|
toolBar->addAction(aDeleteRemoteReplay);
|
|
toolBar->addAction(aGetReplayCode);
|
|
|
|
rightmostToolBar->addAction(aSubmitReplayCode);
|
|
|
|
return groupBox;
|
|
}
|
|
|
|
void TabReplays::retranslateUi()
|
|
{
|
|
leftGroupBox->setTitle(tr("Local file system"));
|
|
rightGroupBox->setTitle(tr("Server replay storage"));
|
|
|
|
aOpenLocalReplay->setText(tr("Watch replay"));
|
|
aRenameLocal->setText(tr("Rename"));
|
|
aNewLocalFolder->setText(tr("New folder"));
|
|
aDeleteLocalReplay->setText(tr("Delete"));
|
|
aOpenReplaysFolder->setText(tr("Open replays folder"));
|
|
aOpenRemoteReplay->setText(tr("Watch replay"));
|
|
aDownload->setText(tr("Download replay"));
|
|
aKeep->setText(tr("Toggle expiration lock"));
|
|
aDeleteRemoteReplay->setText(tr("Delete"));
|
|
aGetReplayCode->setText(tr("Get replay share code"));
|
|
|
|
aSubmitReplayCode->setText(tr("Look up replay by share code"));
|
|
}
|
|
|
|
void TabReplays::handleConnected(const ServerInfo_User &userInfo)
|
|
{
|
|
setRemoteEnabled(userInfo.user_level() & ServerInfo_User::IsRegistered);
|
|
}
|
|
|
|
/**
|
|
* This is only responsible for handling the disconnect. The connect is already handled elsewhere
|
|
*/
|
|
void TabReplays::handleConnectionChanged(ClientStatus status)
|
|
{
|
|
if (status == StatusDisconnected) {
|
|
setRemoteEnabled(false);
|
|
}
|
|
}
|
|
|
|
void TabReplays::setRemoteEnabled(bool enabled)
|
|
{
|
|
aOpenRemoteReplay->setEnabled(enabled);
|
|
aDownload->setEnabled(enabled);
|
|
aKeep->setEnabled(enabled);
|
|
aDeleteRemoteReplay->setEnabled(enabled);
|
|
aGetReplayCode->setEnabled(enabled);
|
|
aSubmitReplayCode->setEnabled(enabled);
|
|
|
|
if (enabled) {
|
|
serverDirView->refreshTree();
|
|
} else {
|
|
serverDirView->clearTree();
|
|
}
|
|
}
|
|
|
|
void TabReplays::actLocalDoubleClick(const QModelIndex &curLeft)
|
|
{
|
|
if (!localDirModel->isDir(curLeft)) {
|
|
actOpenLocalReplay();
|
|
}
|
|
}
|
|
|
|
void TabReplays::actOpenLocalReplay()
|
|
{
|
|
QModelIndexList curLefts = localDirView->selectionModel()->selectedRows();
|
|
for (const auto &curLeft : curLefts) {
|
|
if (localDirModel->isDir(curLeft))
|
|
continue;
|
|
QString filePath = localDirModel->filePath(curLeft);
|
|
|
|
QFile f(filePath);
|
|
if (!f.open(QIODevice::ReadOnly))
|
|
continue;
|
|
QByteArray _data = f.readAll();
|
|
f.close();
|
|
|
|
GameReplay *replay = new GameReplay;
|
|
if (replay->ParseFromArray(_data.data(), _data.size())) {
|
|
emit openReplay(replay);
|
|
} else {
|
|
qCWarning(TabReplaysLog) << "could not parse replay!";
|
|
}
|
|
}
|
|
}
|
|
|
|
void TabReplays::actRenameLocal()
|
|
{
|
|
QModelIndexList curLefts = localDirView->selectionModel()->selectedRows();
|
|
for (const auto &curLeft : curLefts) {
|
|
const QFileInfo info = localDirModel->fileInfo(curLeft);
|
|
|
|
const QString oldName = info.baseName();
|
|
const QString title = info.isDir() ? tr("Rename local folder") : tr("Rename local file");
|
|
|
|
bool ok;
|
|
QString newName = QInputDialog::getText(this, title, tr("New name:"), QLineEdit::Normal, oldName, &ok);
|
|
if (!ok) { // terminate all remaining selections if user cancels
|
|
return;
|
|
}
|
|
if (newName.isEmpty() || oldName == newName) {
|
|
continue;
|
|
}
|
|
|
|
QString newFileName = newName;
|
|
if (!info.suffix().isEmpty()) {
|
|
newFileName += "." + info.suffix();
|
|
}
|
|
const QString newFilePath = QFileInfo(info.dir(), newFileName).filePath();
|
|
|
|
if (!QFile::rename(info.filePath(), newFilePath)) {
|
|
QMessageBox::critical(this, tr("Error"), tr("Rename failed"));
|
|
}
|
|
}
|
|
}
|
|
|
|
void TabReplays::actNewLocalFolder()
|
|
{
|
|
QModelIndex curLeft = localDirView->selectionModel()->currentIndex();
|
|
|
|
QModelIndex dirIndex;
|
|
if (curLeft.isValid() && !localDirModel->isDir(curLeft)) {
|
|
dirIndex = curLeft.parent();
|
|
} else {
|
|
dirIndex = curLeft;
|
|
}
|
|
|
|
bool ok;
|
|
QString folderName =
|
|
QInputDialog::getText(this, tr("New folder"), tr("Name of new folder:"), QLineEdit::Normal, "", &ok);
|
|
if (!ok || folderName.isEmpty())
|
|
return;
|
|
|
|
localDirModel->mkdir(dirIndex, folderName);
|
|
}
|
|
|
|
void TabReplays::actDeleteLocalReplay()
|
|
{
|
|
QModelIndexList curLefts = localDirView->selectionModel()->selectedRows();
|
|
|
|
if (curLefts.isEmpty()) {
|
|
return;
|
|
}
|
|
|
|
if (QMessageBox::warning(this, tr("Delete local file"), tr("Are you sure you want to delete the selected files?"),
|
|
QMessageBox::Yes | QMessageBox::No) != QMessageBox::Yes) {
|
|
return;
|
|
}
|
|
|
|
for (const auto &curLeft : curLefts) {
|
|
if (curLeft.isValid()) {
|
|
localDirModel->remove(curLeft);
|
|
}
|
|
}
|
|
}
|
|
|
|
void TabReplays::actOpenReplaysFolder()
|
|
{
|
|
QString dir = localDirModel->rootPath();
|
|
QDesktopServices::openUrl(QUrl::fromLocalFile(dir));
|
|
}
|
|
|
|
void TabReplays::actRemoteDoubleClick(const QModelIndex &curRight)
|
|
{
|
|
if (serverDirView->getReplay(curRight)) {
|
|
actOpenRemoteReplay();
|
|
}
|
|
}
|
|
|
|
void TabReplays::actOpenRemoteReplay()
|
|
{
|
|
auto const curRights = serverDirView->getSelectedReplays();
|
|
|
|
for (const auto curRight : curRights) {
|
|
if (!curRight) {
|
|
continue;
|
|
}
|
|
|
|
Command_ReplayDownload cmd;
|
|
cmd.set_replay_id(curRight->replay_id());
|
|
|
|
PendingCommand *pend = client->prepareSessionCommand(cmd);
|
|
connect(pend, &PendingCommand::finished, this, &TabReplays::openRemoteReplayFinished);
|
|
client->sendCommand(pend);
|
|
}
|
|
}
|
|
|
|
void TabReplays::openRemoteReplayFinished(const Response &r)
|
|
{
|
|
if (r.response_code() != Response::RespOk)
|
|
return;
|
|
|
|
const Response_ReplayDownload &resp = r.GetExtension(Response_ReplayDownload::ext);
|
|
GameReplay *replay = new GameReplay;
|
|
if (replay->ParseFromString(resp.replay_data())) {
|
|
|
|
emit openReplay(replay);
|
|
} else {
|
|
qCWarning(TabReplaysLog) << "could not parse remote replay!";
|
|
}
|
|
}
|
|
|
|
void TabReplays::actDownload()
|
|
{
|
|
QModelIndex curLeft = localDirView->selectionModel()->currentIndex();
|
|
while (!localDirModel->isDir(curLeft)) {
|
|
curLeft = curLeft.parent();
|
|
}
|
|
|
|
for (const auto curRight : serverDirView->selectionModel()->selectedRows()) {
|
|
downloadNodeAtIndex(curLeft, curRight);
|
|
}
|
|
}
|
|
|
|
void TabReplays::downloadNodeAtIndex(const QModelIndex &curLeft, const QModelIndex &curRight)
|
|
{
|
|
if (const auto replayMatch = serverDirView->getReplayMatch(curRight)) {
|
|
// node at index is a folder
|
|
const QString name =
|
|
QString::number(replayMatch->game_id()) + "_" + QString::fromStdString(replayMatch->game_name());
|
|
|
|
const auto dirIndex = curLeft.isValid() ? curLeft : localDirModel->index(localDirModel->rootPath());
|
|
const auto newDirIndex = localDirModel->mkdir(dirIndex, name);
|
|
|
|
int rows = serverDirView->model()->rowCount(curRight);
|
|
for (int i = 0; i < rows; i++) {
|
|
const auto childIndex = serverDirView->model()->index(i, 0, curRight);
|
|
downloadNodeAtIndex(newDirIndex, childIndex);
|
|
}
|
|
} else if (const auto replay = serverDirView->getReplay(curRight)) {
|
|
// node at index is a replay
|
|
const QString dirPath = curLeft.isValid() ? localDirModel->filePath(curLeft) : localDirModel->rootPath();
|
|
const QString filePath = dirPath + QString("/replay_%1.cor").arg(replay->replay_id());
|
|
|
|
Command_ReplayDownload cmd;
|
|
cmd.set_replay_id(replay->replay_id());
|
|
|
|
PendingCommand *pend = client->prepareSessionCommand(cmd);
|
|
pend->setExtraData(filePath);
|
|
connect(pend, &PendingCommand::finished, this, &TabReplays::downloadFinished);
|
|
client->sendCommand(pend);
|
|
}
|
|
// node at index was invalid
|
|
}
|
|
|
|
void TabReplays::downloadFinished(const Response &r,
|
|
const CommandContainer & /* commandContainer */,
|
|
const QVariant &extraData)
|
|
{
|
|
if (r.response_code() != Response::RespOk)
|
|
return;
|
|
|
|
const Response_ReplayDownload &resp = r.GetExtension(Response_ReplayDownload::ext);
|
|
QString filePath = extraData.toString();
|
|
|
|
const std::string &_data = resp.replay_data();
|
|
QFile f(filePath);
|
|
if (!f.open(QIODevice::WriteOnly)) {
|
|
qWarning() << "failed to open" << filePath << "for writing after downloading replay";
|
|
return;
|
|
}
|
|
f.write((const char *)_data.data(), _data.size());
|
|
f.close();
|
|
}
|
|
|
|
void TabReplays::actKeepRemoteReplay()
|
|
{
|
|
const auto curRights = serverDirView->getSelectedReplayMatches();
|
|
|
|
if (curRights.isEmpty()) {
|
|
return;
|
|
}
|
|
|
|
for (const auto curRight : curRights) {
|
|
Command_ReplayModifyMatch cmd;
|
|
cmd.set_game_id(curRight->game_id());
|
|
cmd.set_do_not_hide(!curRight->do_not_hide());
|
|
|
|
PendingCommand *pend = client->prepareSessionCommand(cmd);
|
|
connect(pend, &PendingCommand::finished, this, &TabReplays::keepRemoteReplayFinished);
|
|
client->sendCommand(pend);
|
|
}
|
|
}
|
|
|
|
void TabReplays::keepRemoteReplayFinished(const Response &r, const CommandContainer &commandContainer)
|
|
{
|
|
if (r.response_code() != Response::RespOk)
|
|
return;
|
|
|
|
const Command_ReplayModifyMatch &cmd =
|
|
commandContainer.session_command(0).GetExtension(Command_ReplayModifyMatch::ext);
|
|
|
|
ServerInfo_ReplayMatch temp;
|
|
temp.set_do_not_hide(cmd.do_not_hide());
|
|
|
|
serverDirView->updateMatchInfo(cmd.game_id(), temp);
|
|
}
|
|
|
|
void TabReplays::actDeleteRemoteReplay()
|
|
{
|
|
const auto curRights = serverDirView->getSelectedReplayMatches();
|
|
|
|
if (curRights.isEmpty()) {
|
|
return;
|
|
}
|
|
|
|
if (QMessageBox::warning(this, tr("Delete remote replay"),
|
|
tr("Are you sure you want to delete the selected replays?"),
|
|
QMessageBox::Yes | QMessageBox::No) != QMessageBox::Yes) {
|
|
return;
|
|
}
|
|
|
|
for (const auto curRight : curRights) {
|
|
Command_ReplayDeleteMatch cmd;
|
|
cmd.set_game_id(curRight->game_id());
|
|
|
|
PendingCommand *pend = client->prepareSessionCommand(cmd);
|
|
connect(pend, &PendingCommand::finished, this, &TabReplays::deleteRemoteReplayFinished);
|
|
client->sendCommand(pend);
|
|
}
|
|
}
|
|
|
|
void TabReplays::deleteRemoteReplayFinished(const Response &r, const CommandContainer &commandContainer)
|
|
{
|
|
if (r.response_code() != Response::RespOk)
|
|
return;
|
|
|
|
const Command_ReplayDeleteMatch &cmd =
|
|
commandContainer.session_command(0).GetExtension(Command_ReplayDeleteMatch::ext);
|
|
serverDirView->removeMatchInfo(cmd.game_id());
|
|
}
|
|
|
|
void TabReplays::actGetReplayCode()
|
|
{
|
|
const auto curRights = serverDirView->getSelectedReplayMatches();
|
|
if (curRights.isEmpty()) {
|
|
return;
|
|
}
|
|
|
|
for (const auto curRight : curRights) {
|
|
Command_ReplayGetCode cmd;
|
|
cmd.set_game_id(curRight->game_id());
|
|
|
|
PendingCommand *pend = client->prepareSessionCommand(cmd);
|
|
connect(pend, &PendingCommand::finished, this, &TabReplays::getReplayCodeFinished);
|
|
client->sendCommand(pend);
|
|
}
|
|
}
|
|
|
|
void TabReplays::getReplayCodeFinished(const Response &r, const CommandContainer & /*commandContainer*/)
|
|
{
|
|
if (r.response_code() == Response::RespFunctionNotAllowed) {
|
|
QMessageBox msgBox;
|
|
msgBox.setIcon(QMessageBox::Warning);
|
|
msgBox.setText(tr("Failed to get code"));
|
|
msgBox.setInformativeText(
|
|
tr("Either this server does not support replay sharing, or does not permit replay sharing for you."));
|
|
msgBox.exec();
|
|
return;
|
|
}
|
|
|
|
if (r.response_code() != Response::RespOk) {
|
|
QMessageBox::warning(this, tr("Failed"), tr("Could not get replay code"));
|
|
return;
|
|
}
|
|
|
|
const Response_ReplayGetCode &resp = r.GetExtension(Response_ReplayGetCode::ext);
|
|
QString code = QString::fromStdString(resp.replay_code());
|
|
|
|
QMessageBox msgBox;
|
|
msgBox.setText(tr("Replay Share Code"));
|
|
msgBox.setInformativeText(
|
|
tr("Others can use this code to add the replay to their list of remote replays:\n%1").arg(code));
|
|
msgBox.setStandardButtons(QMessageBox::Ok);
|
|
QPushButton *copyToClipboardButton = msgBox.addButton(tr("Copy to clipboard"), QMessageBox::ActionRole);
|
|
connect(copyToClipboardButton, &QPushButton::clicked, this, [code] { QApplication::clipboard()->setText(code); });
|
|
msgBox.setDefaultButton(copyToClipboardButton);
|
|
msgBox.exec();
|
|
}
|
|
|
|
void TabReplays::actSubmitReplayCode()
|
|
{
|
|
bool ok;
|
|
QString code = QInputDialog::getText(this, tr("Look up replay by share code"), tr("Replay share code"),
|
|
QLineEdit::Normal, "", &ok);
|
|
|
|
if (!ok) {
|
|
return;
|
|
}
|
|
|
|
Command_ReplaySubmitCode cmd;
|
|
cmd.set_replay_code(code.toStdString());
|
|
|
|
PendingCommand *pend = client->prepareSessionCommand(cmd);
|
|
connect(pend, &PendingCommand::finished, this, &TabReplays::submitReplayCodeFinished);
|
|
client->sendCommand(pend);
|
|
}
|
|
|
|
void TabReplays::submitReplayCodeFinished(const Response &r, const CommandContainer & /*commandContainer*/)
|
|
{
|
|
switch (r.response_code()) {
|
|
case Response::RespOk: {
|
|
QMessageBox msgBox;
|
|
msgBox.setIcon(QMessageBox::Information);
|
|
msgBox.setText(tr("Replay code found"));
|
|
msgBox.setInformativeText(tr("Replay was added, or you already had access to it."));
|
|
msgBox.exec();
|
|
break;
|
|
}
|
|
case Response::RespNameNotFound:
|
|
QMessageBox::warning(this, tr("Failed"), tr("Replay code not found"));
|
|
break;
|
|
case Response::RespFunctionNotAllowed: {
|
|
QMessageBox msgBox;
|
|
msgBox.setIcon(QMessageBox::Warning);
|
|
msgBox.setText(tr("Failed to submit code"));
|
|
msgBox.setInformativeText(
|
|
tr("Either this server does not support replay sharing, or does not permit replay sharing for you."));
|
|
msgBox.exec();
|
|
break;
|
|
}
|
|
default:
|
|
QMessageBox::warning(this, tr("Failed"), tr("Unexpected error"));
|
|
break;
|
|
}
|
|
}
|
|
|
|
void TabReplays::replayAddedEventReceived(const Event_ReplayAdded &event)
|
|
{
|
|
if (event.has_match_info()) {
|
|
// 99.9% of events will have match info (Normal Workflow)
|
|
serverDirView->addMatchInfo(event.match_info());
|
|
} else {
|
|
// When a Moderator force adds a replay or a user submits a replay code, we need to refresh their view
|
|
serverDirView->refreshTree();
|
|
}
|
|
}
|