diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt index dbbe9e85d..62f6f3f34 100644 --- a/launcher/CMakeLists.txt +++ b/launcher/CMakeLists.txt @@ -1065,6 +1065,8 @@ SET(LAUNCHER_SOURCES ui/dialogs/ImportResourceDialog.h ui/dialogs/MSALoginDialog.cpp ui/dialogs/MSALoginDialog.h + ui/dialogs/NetworkJobFailedDialog.cpp + ui/dialogs/NetworkJobFailedDialog.h ui/dialogs/NewComponentDialog.cpp ui/dialogs/NewComponentDialog.h ui/dialogs/NewInstanceDialog.cpp @@ -1247,6 +1249,7 @@ qt_wrap_ui(LAUNCHER_UI ui/dialogs/ProfileSetupDialog.ui ui/dialogs/ProgressDialog.ui ui/dialogs/NewInstanceDialog.ui + ui/dialogs/NetworkJobFailedDialog.ui ui/dialogs/NewComponentDialog.ui ui/dialogs/NewsDialog.ui ui/dialogs/ProfileSelectDialog.ui diff --git a/launcher/net/NetJob.cpp b/launcher/net/NetJob.cpp index a40219962..dae149ab3 100644 --- a/launcher/net/NetJob.cpp +++ b/launcher/net/NetJob.cpp @@ -42,7 +42,7 @@ #if defined(LAUNCHER_APPLICATION) #include "Application.h" #include "settings/SettingsObject.h" -#include "ui/dialogs/CustomMessageBox.h" +#include "ui/dialogs/NetworkJobFailedDialog.h" #endif NetJob::NetJob(QString job_name, QNetworkAccessManager* network, int max_concurrent) : ConcurrentTask(job_name), m_network(network) @@ -164,23 +164,29 @@ void NetJob::emitFailed(QString reason) if (APPLICATION_DYN && m_ask_retry && m_manual_try < APPLICATION->settings()->get("NumberOfManualRetries").toInt() && isOnline()) { m_manual_try++; - auto response = CustomMessageBox::selectable(nullptr, "Confirm retry", - "The tasks failed.\n" - "Failed urls\n" + - getFailedFiles().join("\n\t") + - ".\n" - "If this continues to happen please check the logs of the application.\n" - "Do you want to retry?", - QMessageBox::Warning, QMessageBox::Yes | QMessageBox::No, QMessageBox::No) - ->exec(); + auto failed = getFailedActions(); + auto dialog = new NetworkJobFailedDialog(objectName(), m_try, m_done.size(), failed.size(), nullptr); + dialog->setAttribute(Qt::WA_DeleteOnClose); - if (response == QMessageBox::Yes) { - m_try = 0; - executeNextSubTask(); - return; + for (const auto& request : failed) { + dialog->addFailedRequest(request->url(), request->errorString()); } + + dialog->open(); + + connect(dialog, &QDialog::finished, this, [this, reason = std::move(reason)](int result) { + if (result == QDialog::Accepted) { + m_try = 0; + executeNextSubTask(); + } else { + ConcurrentTask::emitFailed(reason); + } + }); + + return; } #endif + ConcurrentTask::emitFailed(reason); } diff --git a/launcher/ui/dialogs/NetworkJobFailedDialog.cpp b/launcher/ui/dialogs/NetworkJobFailedDialog.cpp new file mode 100644 index 000000000..2d8efb866 --- /dev/null +++ b/launcher/ui/dialogs/NetworkJobFailedDialog.cpp @@ -0,0 +1,79 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * Copyright (C) 2025 Octol1ttle + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "NetworkJobFailedDialog.h" + +#include +#include +#include +#include + +#include "ui_NetworkJobFailedDialog.h" + +NetworkJobFailedDialog::NetworkJobFailedDialog(const QString& jobName, const int attempts, const int requests, const int failed, QWidget* parent) + : QDialog(parent), m_ui(new Ui::NetworkJobFailedDialog) +{ + m_ui->setupUi(this); + m_ui->failLabel->setText(m_ui->failLabel->text().arg(jobName)); + if (failed == requests) { + m_ui->requestCountLabel->setText(tr("All %1 requests have failed after %2 attempts").arg(failed).arg(attempts)); + } else if (failed < requests / 2) { + m_ui->requestCountLabel->setText( + tr("Out of %1 requests, %2 have failed after %3 attempts").arg(requests).arg(failed).arg(attempts)); + } else { + m_ui->requestCountLabel->setText( + tr("Out of %1 requests, only %2 succeeded after %3 attempts").arg(requests).arg(requests - failed).arg(attempts)); + } + + m_ui->detailsTable->header()->setSectionResizeMode(0, QHeaderView::Stretch); + m_ui->detailsTable->header()->setSectionResizeMode(1, QHeaderView::ResizeToContents); + + const auto* copyShortcut = new QShortcut(QKeySequence::Copy, m_ui->detailsTable); + connect(copyShortcut, &QShortcut::activated, this, &NetworkJobFailedDialog::copyUrl); + + const auto* copyButton = m_ui->dialogButtonBox->addButton(tr("Copy URL"), QDialogButtonBox::ActionRole); + connect(copyButton, &QPushButton::clicked, this, &NetworkJobFailedDialog::copyUrl); + + connect(m_ui->dialogButtonBox, &QDialogButtonBox::accepted, this, &QDialog::accept); + connect(m_ui->dialogButtonBox, &QDialogButtonBox::rejected, this, &QDialog::reject); +} + +NetworkJobFailedDialog::~NetworkJobFailedDialog() +{ + delete m_ui; +} + +void NetworkJobFailedDialog::addFailedRequest(const QUrl& url, QString error) const +{ + auto* item = new QTreeWidgetItem(m_ui->detailsTable, { url.toString(), std::move(error) }); + m_ui->detailsTable->addTopLevelItem(item); + if (m_ui->detailsTable->selectedItems().isEmpty()) { + m_ui->detailsTable->setCurrentItem(item); + } +} + +void NetworkJobFailedDialog::copyUrl() const +{ + auto items = m_ui->detailsTable->selectedItems(); + if (items.isEmpty()) { + return; + } + + auto* clipboard = QGuiApplication::clipboard(); + clipboard->setText(items.first()->text(0)); +} diff --git a/launcher/ui/dialogs/NetworkJobFailedDialog.h b/launcher/ui/dialogs/NetworkJobFailedDialog.h new file mode 100644 index 000000000..9bfb7c439 --- /dev/null +++ b/launcher/ui/dialogs/NetworkJobFailedDialog.h @@ -0,0 +1,43 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * Copyright (C) 2025 Octol1ttle + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include + +QT_BEGIN_NAMESPACE +namespace Ui { +class NetworkJobFailedDialog; +} +QT_END_NAMESPACE + +class NetworkJobFailedDialog : public QDialog { + Q_OBJECT + + public: + explicit NetworkJobFailedDialog(const QString& jobName, int attempts, int requests, int failed, QWidget* parent = nullptr); + ~NetworkJobFailedDialog() override; + + void addFailedRequest(const QUrl& url, QString error) const; + + private slots: + void copyUrl() const; + + private: + Ui::NetworkJobFailedDialog* m_ui; +}; diff --git a/launcher/ui/dialogs/NetworkJobFailedDialog.ui b/launcher/ui/dialogs/NetworkJobFailedDialog.ui new file mode 100644 index 000000000..b133052eb --- /dev/null +++ b/launcher/ui/dialogs/NetworkJobFailedDialog.ui @@ -0,0 +1,99 @@ + + + NetworkJobFailedDialog + + + + 0 + 0 + 450 + 350 + + + + Network error + + + + + + + + + + 0 + 0 + + + + A network operation has failed: %1 + + + + + + + + 0 + 0 + + + + (request count) + + + + + + + QAbstractItemView::EditTrigger::NoEditTriggers + + + true + + + QAbstractItemView::ScrollMode::ScrollPerPixel + + + 0 + + + false + + + + URL + + + + + Error + + + + + + + + + 0 + 0 + + + + What would you like to do? + + + + + + + QDialogButtonBox::StandardButton::Abort|QDialogButtonBox::StandardButton::Retry + + + + + + + +