From 4dfc01899a94ce365284ee1ba57c11afa823c55a Mon Sep 17 00:00:00 2001 From: Gingeh <39150378+Gingeh@users.noreply.github.com> Date: Fri, 15 Jul 2022 11:35:03 +1000 Subject: Make skin upload optional Signed-off-by: Gingeh <39150378+Gingeh@users.noreply.github.com> --- launcher/ui/dialogs/SkinUploadDialog.cpp | 90 +++++++++++++++++--------------- launcher/ui/dialogs/SkinUploadDialog.ui | 6 ++- 2 files changed, 52 insertions(+), 44 deletions(-) (limited to 'launcher/ui/dialogs') diff --git a/launcher/ui/dialogs/SkinUploadDialog.cpp b/launcher/ui/dialogs/SkinUploadDialog.cpp index b5b78690..8180ac1f 100644 --- a/launcher/ui/dialogs/SkinUploadDialog.cpp +++ b/launcher/ui/dialogs/SkinUploadDialog.cpp @@ -57,68 +57,72 @@ void SkinUploadDialog::on_buttonBox_accepted() { QString fileName; QString input = ui->skinPathTextBox->text(); - QRegularExpression urlPrefixMatcher(QRegularExpression::anchoredPattern("^([a-z]+)://.+$")); - bool isLocalFile = false; - // it has an URL prefix -> it is an URL - if(urlPrefixMatcher.match(input).hasMatch()) - { - QUrl fileURL = input; - if(fileURL.isValid()) + ProgressDialog prog(this); + SequentialTask skinUpload; + + if (!input.isEmpty()) { + QRegularExpression urlPrefixMatcher(QRegularExpression::anchoredPattern("^([a-z]+)://.+$")); + bool isLocalFile = false; + // it has an URL prefix -> it is an URL + if(urlPrefixMatcher.match(input).hasMatch()) { - // local? - if(fileURL.isLocalFile()) + QUrl fileURL = input; + if(fileURL.isValid()) { - isLocalFile = true; - fileName = fileURL.toLocalFile(); + // local? + if(fileURL.isLocalFile()) + { + isLocalFile = true; + fileName = fileURL.toLocalFile(); + } + else + { + CustomMessageBox::selectable( + this, + tr("Skin Upload"), + tr("Using remote URLs for setting skins is not implemented yet."), + QMessageBox::Warning + )->exec(); + close(); + return; + } } else { CustomMessageBox::selectable( this, tr("Skin Upload"), - tr("Using remote URLs for setting skins is not implemented yet."), + tr("You cannot use an invalid URL for uploading skins."), QMessageBox::Warning - )->exec(); + )->exec(); close(); return; } } else { - CustomMessageBox::selectable( - this, - tr("Skin Upload"), - tr("You cannot use an invalid URL for uploading skins."), - QMessageBox::Warning - )->exec(); + // just assume it's a path then + isLocalFile = true; + fileName = ui->skinPathTextBox->text(); + } + if (isLocalFile && !QFile::exists(fileName)) + { + CustomMessageBox::selectable(this, tr("Skin Upload"), tr("Skin file does not exist!"), QMessageBox::Warning)->exec(); close(); return; } + SkinUpload::Model model = SkinUpload::STEVE; + if (ui->steveBtn->isChecked()) + { + model = SkinUpload::STEVE; + } + else if (ui->alexBtn->isChecked()) + { + model = SkinUpload::ALEX; + } + skinUpload.addTask(shared_qobject_ptr(new SkinUpload(this, m_acct->accessToken(), FS::read(fileName), model))); } - else - { - // just assume it's a path then - isLocalFile = true; - fileName = ui->skinPathTextBox->text(); - } - if (isLocalFile && !QFile::exists(fileName)) - { - CustomMessageBox::selectable(this, tr("Skin Upload"), tr("Skin file does not exist!"), QMessageBox::Warning)->exec(); - close(); - return; - } - SkinUpload::Model model = SkinUpload::STEVE; - if (ui->steveBtn->isChecked()) - { - model = SkinUpload::STEVE; - } - else if (ui->alexBtn->isChecked()) - { - model = SkinUpload::ALEX; - } - ProgressDialog prog(this); - SequentialTask skinUpload; - skinUpload.addTask(shared_qobject_ptr(new SkinUpload(this, m_acct->accessToken(), FS::read(fileName), model))); + auto selectedCape = ui->capeCombo->currentData().toString(); if(selectedCape != m_acct->accountData()->minecraftProfile.currentCape) { skinUpload.addTask(shared_qobject_ptr(new CapeChange(this, m_acct->accessToken(), selectedCape))); diff --git a/launcher/ui/dialogs/SkinUploadDialog.ui b/launcher/ui/dialogs/SkinUploadDialog.ui index f4b0ed0a..c7b16645 100644 --- a/launcher/ui/dialogs/SkinUploadDialog.ui +++ b/launcher/ui/dialogs/SkinUploadDialog.ui @@ -21,7 +21,11 @@ - + + + Leave empty to keep current skin + + -- cgit From dd6aabf9ab2c974816aef4e889e059fa0cdad53b Mon Sep 17 00:00:00 2001 From: flow Date: Mon, 30 May 2022 13:30:39 -0300 Subject: feat: add ChooseProviderDialog Allows you to prompt the user for choosing a (mod) provider. This should be fairly independent of the mod updater logic, so it can be used for other ends later down the road :^) Signed-off-by: flow --- launcher/ui/dialogs/ChooseProviderDialog.cpp | 96 ++++++++++++++++++++++++++++ launcher/ui/dialogs/ChooseProviderDialog.h | 56 ++++++++++++++++ launcher/ui/dialogs/ChooseProviderDialog.ui | 89 ++++++++++++++++++++++++++ 3 files changed, 241 insertions(+) create mode 100644 launcher/ui/dialogs/ChooseProviderDialog.cpp create mode 100644 launcher/ui/dialogs/ChooseProviderDialog.h create mode 100644 launcher/ui/dialogs/ChooseProviderDialog.ui (limited to 'launcher/ui/dialogs') diff --git a/launcher/ui/dialogs/ChooseProviderDialog.cpp b/launcher/ui/dialogs/ChooseProviderDialog.cpp new file mode 100644 index 00000000..89935d9a --- /dev/null +++ b/launcher/ui/dialogs/ChooseProviderDialog.cpp @@ -0,0 +1,96 @@ +#include "ChooseProviderDialog.h" +#include "ui_ChooseProviderDialog.h" + +#include +#include + +#include "modplatform/ModIndex.h" + +static ModPlatform::ProviderCapabilities ProviderCaps; + +ChooseProviderDialog::ChooseProviderDialog(QWidget* parent, bool single_choice, bool allow_skipping) + : QDialog(parent), ui(new Ui::ChooseProviderDialog) +{ + ui->setupUi(this); + + addProviders(); + m_providers.button(0)->click(); + + connect(ui->skipOneButton, &QPushButton::clicked, this, &ChooseProviderDialog::skipOne); + connect(ui->skipAllButton, &QPushButton::clicked, this, &ChooseProviderDialog::skipAll); + + connect(ui->confirmOneButton, &QPushButton::clicked, this, &ChooseProviderDialog::confirmOne); + connect(ui->confirmAllButton, &QPushButton::clicked, this, &ChooseProviderDialog::confirmAll); + + if (single_choice) { + ui->providersLayout->removeWidget(ui->skipAllButton); + ui->providersLayout->removeWidget(ui->confirmAllButton); + } + + if (!allow_skipping) { + ui->providersLayout->removeWidget(ui->skipOneButton); + ui->providersLayout->removeWidget(ui->skipAllButton); + } +} + +ChooseProviderDialog::~ChooseProviderDialog() +{ + delete ui; +} + +void ChooseProviderDialog::setDescription(QString desc) +{ + ui->explanationLabel->setText(desc); +} + +void ChooseProviderDialog::skipOne() +{ + reject(); +} +void ChooseProviderDialog::skipAll() +{ + m_response.skip_all = true; + reject(); +} + +void ChooseProviderDialog::confirmOne() +{ + m_response.chosen = getSelectedProvider(); + m_response.try_others = ui->tryOthersCheckbox->isChecked(); + accept(); +} +void ChooseProviderDialog::confirmAll() +{ + m_response.chosen = getSelectedProvider(); + m_response.confirm_all = true; + m_response.try_others = ui->tryOthersCheckbox->isChecked(); + accept(); +} + +auto ChooseProviderDialog::getSelectedProvider() const -> ModPlatform::Provider +{ + return ModPlatform::Provider(m_providers.checkedId()); +} + +void ChooseProviderDialog::addProviders() +{ + int btn_index = 0; + QRadioButton* btn; + + for (auto& provider : { ModPlatform::Provider::MODRINTH, ModPlatform::Provider::FLAME }) { + btn = new QRadioButton(ProviderCaps.readableName(provider), this); + m_providers.addButton(btn, btn_index++); + ui->providersLayout->addWidget(btn); + } +} + +void ChooseProviderDialog::disableInput() +{ + for (auto& btn : m_providers.buttons()) + btn->setEnabled(false); + + ui->skipOneButton->setEnabled(false); + ui->skipAllButton->setEnabled(false); + ui->confirmOneButton->setEnabled(false); + ui->confirmAllButton->setEnabled(false); +} diff --git a/launcher/ui/dialogs/ChooseProviderDialog.h b/launcher/ui/dialogs/ChooseProviderDialog.h new file mode 100644 index 00000000..4a3b9f29 --- /dev/null +++ b/launcher/ui/dialogs/ChooseProviderDialog.h @@ -0,0 +1,56 @@ +#pragma once + +#include +#include + +namespace Ui { +class ChooseProviderDialog; +} + +namespace ModPlatform { +enum class Provider; +} + +class Mod; +class NetJob; +class ModUpdateDialog; + +class ChooseProviderDialog : public QDialog { + Q_OBJECT + + struct Response { + bool skip_all = false; + bool confirm_all = false; + + bool try_others = false; + + ModPlatform::Provider chosen; + }; + + public: + explicit ChooseProviderDialog(QWidget* parent, bool single_choice = false, bool allow_skipping = true); + ~ChooseProviderDialog(); + + auto getResponse() const -> Response { return m_response; } + + void setDescription(QString desc); + + private slots: + void skipOne(); + void skipAll(); + void confirmOne(); + void confirmAll(); + + private: + void addProviders(); + void disableInput(); + + auto getSelectedProvider() const -> ModPlatform::Provider; + + private: + Ui::ChooseProviderDialog* ui; + + QButtonGroup m_providers; + + Response m_response; +}; diff --git a/launcher/ui/dialogs/ChooseProviderDialog.ui b/launcher/ui/dialogs/ChooseProviderDialog.ui new file mode 100644 index 00000000..78cd9613 --- /dev/null +++ b/launcher/ui/dialogs/ChooseProviderDialog.ui @@ -0,0 +1,89 @@ + + + ChooseProviderDialog + + + + 0 + 0 + 453 + 197 + + + + Choose a mod provider + + + + + + Qt::AlignJustify|Qt::AlignTop + + + true + + + -1 + + + + + + + Qt::AlignHCenter|Qt::AlignTop + + + Qt::AlignHCenter|Qt::AlignTop + + + + + + + + + Skip this mod + + + + + + + Skip all + + + + + + + Confirm for all + + + + + + + Confirm + + + true + + + + + + + + + Try to automatically use other providers if the chosen one fails + + + true + + + + + + + + -- cgit From 6e2869834f59ae4863e63a16de97aa3019723b26 Mon Sep 17 00:00:00 2001 From: flow Date: Sat, 4 Jun 2022 16:22:46 -0300 Subject: feat: add mod update dialog This subclasses the Review mods dialog to make a "Update review" one. Also, all the necessary components built until now are put together in a coherent unity that checks and generates metadata on-the-fly and checks for mod updates, while giving and receiving feedback to the user. Signed-off-by: flow --- launcher/ui/dialogs/ModUpdateDialog.cpp | 317 ++++++++++++++++++++++++++++++++ launcher/ui/dialogs/ModUpdateDialog.h | 61 ++++++ launcher/ui/dialogs/ScrollMessageBox.ui | 2 +- 3 files changed, 379 insertions(+), 1 deletion(-) create mode 100644 launcher/ui/dialogs/ModUpdateDialog.cpp create mode 100644 launcher/ui/dialogs/ModUpdateDialog.h (limited to 'launcher/ui/dialogs') diff --git a/launcher/ui/dialogs/ModUpdateDialog.cpp b/launcher/ui/dialogs/ModUpdateDialog.cpp new file mode 100644 index 00000000..b60fd304 --- /dev/null +++ b/launcher/ui/dialogs/ModUpdateDialog.cpp @@ -0,0 +1,317 @@ +#include "ModUpdateDialog.h" +#include "ChooseProviderDialog.h" +#include "CustomMessageBox.h" +#include "ProgressDialog.h" +#include "ScrollMessageBox.h" +#include "ui_ReviewMessageBox.h" + +#include "FileSystem.h" +#include "Json.h" + +#include "minecraft/MinecraftInstance.h" +#include "minecraft/PackProfile.h" + +#include "modplatform/EnsureMetadataTask.h" +#include "modplatform/flame/FlameCheckUpdate.h" +#include "modplatform/modrinth/ModrinthCheckUpdate.h" + +#include + +#include +#include + +static ModPlatform::ProviderCapabilities ProviderCaps; + +static std::list mcVersions(BaseInstance* inst) +{ + return { static_cast(inst)->getPackProfile()->getComponent("net.minecraft")->getVersion() }; +} + +static ModAPI::ModLoaderTypes mcLoaders(BaseInstance* inst) +{ + return { static_cast(inst)->getPackProfile()->getModLoaders() }; +} + +ModUpdateDialog::ModUpdateDialog(QWidget* parent, + BaseInstance* instance, + const std::shared_ptr& mods, + std::list& search_for) + : ReviewMessageBox(parent, tr("Confirm mods to update"), "") + , m_parent(parent) + , m_mod_model(mods) + , m_candidates(search_for) + , m_instance(instance) +{ + ReviewMessageBox::setGeometry(0, 0, 800, 600); + + ui->explainLabel->setText(tr("You're about to update the following mods:")); + ui->onlyCheckedLabel->setText(tr("Only mods with a check will be updated!")); + + connect(&m_check_task, &Task::failed, this, + [&](QString reason) { CustomMessageBox::selectable(this, tr("Error"), reason, QMessageBox::Critical)->exec(); }); + + connect(&m_check_task, &Task::succeeded, this, [&]() { + QStringList warnings = m_check_task.warnings(); + if (warnings.count()) { + CustomMessageBox::selectable(this, tr("Warnings"), warnings.join('\n'), QMessageBox::Warning)->exec(); + } + }); +} + +void ModUpdateDialog::checkCandidates() +{ + // Ensure mods have valid metadata + auto went_well = ensureMetadata(); + if (!went_well) { + m_aborted = true; + return; + } + + // Report failed metadata generation + if (!m_failed_metadata.empty()) { + QString text; + for (const auto& mod : m_failed_metadata) { + text += tr("Mod name: %1
File name: %2

").arg(mod.name(), mod.fileinfo().fileName()); + } + + ScrollMessageBox message_dialog(m_parent, tr("Metadata generation failed"), + tr("Could not generate metadata for the following mods:
" + "Do you wish to proceed without those mods?"), + text); + message_dialog.setModal(true); + if (message_dialog.exec() == QDialog::Rejected) { + m_aborted = true; + QMetaObject::invokeMethod(this, "reject", Qt::QueuedConnection); + return; + } + } + + auto versions = mcVersions(m_instance); + auto loaders = mcLoaders(m_instance); + + if (!m_modrinth_to_update.empty()) { + m_modrinth_check_task = new ModrinthCheckUpdate(m_modrinth_to_update, versions, loaders, m_mod_model); + connect(m_modrinth_check_task, &CheckUpdateTask::checkFailed, this, + [this](Mod mod, QString reason, QUrl recover_url) { m_failed_check_update.emplace_back(mod, reason, recover_url); }); + m_check_task.addTask(m_modrinth_check_task); + } + + if (!m_flame_to_update.empty()) { + m_flame_check_task = new FlameCheckUpdate(m_flame_to_update, versions, loaders, m_mod_model); + connect(m_flame_check_task, &CheckUpdateTask::checkFailed, this, + [this](Mod mod, QString reason, QUrl recover_url) { m_failed_check_update.emplace_back(mod, reason, recover_url); }); + m_check_task.addTask(m_flame_check_task); + } + + // Check for updates + ProgressDialog progress_dialog(m_parent); + progress_dialog.setSkipButton(true, tr("Abort")); + progress_dialog.setVisible(true); + progress_dialog.setWindowTitle(tr("Checking for updates...")); + auto ret = progress_dialog.execWithTask(&m_check_task); + + // If the dialog was skipped / some download error happened + if (ret == QDialog::DialogCode::Rejected) { + m_aborted = true; + QMetaObject::invokeMethod(this, "reject", Qt::QueuedConnection); + return; + } + + // Add found updates for Modrinth + if (m_modrinth_check_task) { + auto modrinth_updates = m_modrinth_check_task->getUpdatable(); + for (auto& updatable : modrinth_updates) { + qDebug() << QString("Mod %1 has an update available!").arg(updatable.name); + + appendMod(updatable); + m_tasks.insert(updatable.name, updatable.download); + } + } + + // Add found updated for Flame + if (m_flame_check_task) { + auto flame_updates = m_flame_check_task->getUpdatable(); + for (auto& updatable : flame_updates) { + qDebug() << QString("Mod %1 has an update available!").arg(updatable.name); + + appendMod(updatable); + m_tasks.insert(updatable.name, updatable.download); + } + } + + // Report failed update checking + if (!m_failed_check_update.empty()) { + QString text; + for (const auto& failed : m_failed_check_update) { + const auto& mod = std::get<0>(failed); + const auto& reason = std::get<1>(failed); + const auto& recover_url = std::get<2>(failed); + + qDebug() << mod.name() << " failed to check for updates!"; + + text += tr("Mod name: %1").arg(mod.name()) + "
"; + if (!reason.isEmpty()) + text += tr("Reason: %1").arg(reason) + "
"; + if (!recover_url.isEmpty()) + text += tr("Possible solution: ") + tr("Getting the latest version manually:") + "
" + + QString("").arg(recover_url.toString()) + recover_url.toString() + "
"; + text += "
"; + } + + ScrollMessageBox message_dialog(m_parent, tr("Failed to check for updates"), + tr("Could not check or get the following mods for updates:
" + "Do you wish to proceed without those mods?"), + text); + message_dialog.setModal(true); + if (message_dialog.exec() == QDialog::Rejected) { + m_aborted = true; + QMetaObject::invokeMethod(this, "reject", Qt::QueuedConnection); + return; + } + } + + // If there's no mod to be updated + if (ui->modTreeWidget->topLevelItemCount() == 0) + m_no_updates = true; + + if (m_aborted || m_no_updates) + QMetaObject::invokeMethod(this, "reject", Qt::QueuedConnection); +} + +// Part 1: Ensure we have a valid metadata +auto ModUpdateDialog::ensureMetadata() -> bool +{ + auto index_dir = indexDir(); + + auto* seq = new SequentialTask(m_parent, tr("Looking for metadata")); + + bool confirm_rest = false; + bool try_others_rest = false; + bool skip_rest = false; + ModPlatform::Provider provider_rest = ModPlatform::Provider::MODRINTH; + + for (auto& candidate : m_candidates) { + if (candidate.status() != ModStatus::NoMetadata) { + onMetadataEnsured(candidate); + continue; + } + + if (skip_rest) + continue; + + if (confirm_rest) { + auto* task = new EnsureMetadataTask(candidate, index_dir, try_others_rest, provider_rest); + connect(task, &EnsureMetadataTask::metadataReady, [this, &candidate] { onMetadataEnsured(candidate); }); + connect(task, &EnsureMetadataTask::metadataFailed, [this, &candidate] { onMetadataFailed(candidate); }); + seq->addTask(task); + continue; + } + + ChooseProviderDialog chooser(this); + chooser.setDescription(tr("This mod (%1) does not have a metadata yet. We need to create one in order to keep relevant " + "information on how to update this " + "mod. To do this, please select a mod provider from which we can search for updates for %1.") + .arg(candidate.name())); + auto confirmed = chooser.exec() == QDialog::DialogCode::Accepted; + + auto response = chooser.getResponse(); + + if (response.skip_all) + skip_rest = true; + if (response.confirm_all) { + confirm_rest = true; + provider_rest = response.chosen; + try_others_rest = response.try_others; + } + + if (confirmed) { + auto* task = new EnsureMetadataTask(candidate, index_dir, response.try_others, response.chosen); + connect(task, &EnsureMetadataTask::metadataReady, [this, &candidate] { onMetadataEnsured(candidate); }); + connect(task, &EnsureMetadataTask::metadataFailed, [this, &candidate] { onMetadataFailed(candidate); }); + seq->addTask(task); + } + } + + ProgressDialog checking_dialog(m_parent); + checking_dialog.setSkipButton(true, tr("Abort")); + checking_dialog.setWindowTitle(tr("Generating metadata...")); + auto ret_metadata = checking_dialog.execWithTask(seq); + + return (ret_metadata != QDialog::DialogCode::Rejected); +} + +void ModUpdateDialog::onMetadataEnsured(Mod& mod) +{ + // When the mod is a folder, for instance + if (!mod.metadata()) + return; + + switch (mod.metadata()->provider) { + case ModPlatform::Provider::MODRINTH: + m_modrinth_to_update.push_back(mod); + break; + case ModPlatform::Provider::FLAME: + m_flame_to_update.push_back(mod); + break; + } +} + +void ModUpdateDialog::onMetadataFailed(Mod& mod) +{ + m_failed_metadata.push_back(mod); +} + +void ModUpdateDialog::appendMod(CheckUpdateTask::UpdatableMod const& info) +{ + auto item_top = new QTreeWidgetItem(ui->modTreeWidget); + item_top->setCheckState(0, Qt::CheckState::Checked); + item_top->setText(0, info.name); + item_top->setExpanded(true); + + auto provider_item = new QTreeWidgetItem(item_top); + provider_item->setText(0, tr("Provider: %1").arg(ProviderCaps.readableName(info.provider))); + + auto old_version_item = new QTreeWidgetItem(item_top); + old_version_item->setText(0, tr("Old version: %1").arg(info.old_version.isEmpty() ? tr("Not installed") : info.old_version)); + + auto new_version_item = new QTreeWidgetItem(item_top); + new_version_item->setText(0, tr("New version: %1").arg(info.new_version)); + + auto changelog_item = new QTreeWidgetItem(item_top); + changelog_item->setText(0, tr("Changelog of the latest version")); + + auto changelog = new QTreeWidgetItem(changelog_item); + + auto changelog_area = new QTextEdit(); + HoeDown h; + changelog_area->setText(h.process(info.changelog.toUtf8())); + changelog_area->setReadOnly(true); + if (info.changelog.size() < 250) // heuristic + changelog_area->setSizeAdjustPolicy(QTextEdit::SizeAdjustPolicy::AdjustToContents); + + ui->modTreeWidget->setItemWidget(changelog, 0, changelog_area); + changelog_item->insertChildren(0, { changelog }); + + item_top->insertChildren(0, { old_version_item }); + item_top->insertChildren(1, { new_version_item }); + item_top->insertChildren(2, { changelog_item }); + + ui->modTreeWidget->addTopLevelItem(item_top); +} + +auto ModUpdateDialog::getTasks() -> const std::list +{ + std::list list; + + auto* item = ui->modTreeWidget->topLevelItem(0); + + for (int i = 0; item != nullptr; ++i) { + if (item->checkState(0) == Qt::CheckState::Checked) { + list.push_back(m_tasks.find(item->text(0)).value()); + } + + item = ui->modTreeWidget->topLevelItem(i); + } + + return list; +} diff --git a/launcher/ui/dialogs/ModUpdateDialog.h b/launcher/ui/dialogs/ModUpdateDialog.h new file mode 100644 index 00000000..30cd5cbd --- /dev/null +++ b/launcher/ui/dialogs/ModUpdateDialog.h @@ -0,0 +1,61 @@ +#pragma once + +#include "BaseInstance.h" +#include "ModDownloadTask.h" +#include "ReviewMessageBox.h" + +#include "minecraft/mod/ModFolderModel.h" + +#include "modplatform/CheckUpdateTask.h" + +class Mod; +class ModrinthCheckUpdate; +class FlameCheckUpdate; + +class ModUpdateDialog final : public ReviewMessageBox { + Q_OBJECT + public: + explicit ModUpdateDialog(QWidget* parent, + BaseInstance* instance, + const std::shared_ptr& mod_model, + std::list& search_for); + + void checkCandidates(); + + void appendMod(const CheckUpdateTask::UpdatableMod& info); + + const std::list getTasks(); + auto indexDir() const -> QDir { return m_mod_model->indexDir(); } + + auto noUpdates() const -> bool { return m_no_updates; }; + auto aborted() const -> bool { return m_aborted; }; + + private: + auto ensureMetadata() -> bool; + + private slots: + void onMetadataEnsured(Mod&); + void onMetadataFailed(Mod&); + + private: + QWidget* m_parent; + + SequentialTask m_check_task; + ModrinthCheckUpdate* m_modrinth_check_task = nullptr; + FlameCheckUpdate* m_flame_check_task = nullptr; + + const std::shared_ptr& m_mod_model; + + std::list& m_candidates; + std::list m_modrinth_to_update; + std::list m_flame_to_update; + + std::list m_failed_metadata; + std::list> m_failed_check_update; + + QHash m_tasks; + BaseInstance* m_instance; + + bool m_no_updates = false; + bool m_aborted = false; +}; diff --git a/launcher/ui/dialogs/ScrollMessageBox.ui b/launcher/ui/dialogs/ScrollMessageBox.ui index 299d2ecc..e684185f 100644 --- a/launcher/ui/dialogs/ScrollMessageBox.ui +++ b/launcher/ui/dialogs/ScrollMessageBox.ui @@ -6,7 +6,7 @@ 0 0 - 400 + 500 455 -- cgit From 1709b47bb7fd325c1b6dd482524fffa428b0f5a9 Mon Sep 17 00:00:00 2001 From: flow Date: Fri, 10 Jun 2022 16:40:39 -0300 Subject: fix: don't double add mods in mod downloader/updater Signed-off-by: flow --- launcher/ui/dialogs/ModUpdateDialog.cpp | 2 +- launcher/ui/dialogs/ReviewMessageBox.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'launcher/ui/dialogs') diff --git a/launcher/ui/dialogs/ModUpdateDialog.cpp b/launcher/ui/dialogs/ModUpdateDialog.cpp index b60fd304..a4d83483 100644 --- a/launcher/ui/dialogs/ModUpdateDialog.cpp +++ b/launcher/ui/dialogs/ModUpdateDialog.cpp @@ -305,7 +305,7 @@ auto ModUpdateDialog::getTasks() -> const std::list auto* item = ui->modTreeWidget->topLevelItem(0); - for (int i = 0; item != nullptr; ++i) { + for (int i = 1; item != nullptr; ++i) { if (item->checkState(0) == Qt::CheckState::Checked) { list.push_back(m_tasks.find(item->text(0)).value()); } diff --git a/launcher/ui/dialogs/ReviewMessageBox.cpp b/launcher/ui/dialogs/ReviewMessageBox.cpp index c92234a4..e664e566 100644 --- a/launcher/ui/dialogs/ReviewMessageBox.cpp +++ b/launcher/ui/dialogs/ReviewMessageBox.cpp @@ -40,7 +40,7 @@ auto ReviewMessageBox::deselectedMods() -> QStringList auto* item = ui->modTreeWidget->topLevelItem(0); - for (int i = 0; item != nullptr; ++i) { + for (int i = 1; item != nullptr; ++i) { if (item->checkState(0) == Qt::CheckState::Unchecked) { list.append(item->text(0)); } -- cgit From 4e6978ff6f61777a2e2e989cba58a9f0c48d2782 Mon Sep 17 00:00:00 2001 From: flow Date: Thu, 16 Jun 2022 11:45:29 -0300 Subject: feat: improve metadata gen. networking and performance This makes the metadata generation code a lot messier and harder to use, but there's not really much else that can be done about it while preserving all it's capabilities :( At least we now have speed Signed-off-by: flow --- launcher/ui/dialogs/ModUpdateDialog.cpp | 113 ++++++++++++++++++++++++-------- launcher/ui/dialogs/ModUpdateDialog.h | 8 +-- 2 files changed, 90 insertions(+), 31 deletions(-) (limited to 'launcher/ui/dialogs') diff --git a/launcher/ui/dialogs/ModUpdateDialog.cpp b/launcher/ui/dialogs/ModUpdateDialog.cpp index a4d83483..7584621a 100644 --- a/launcher/ui/dialogs/ModUpdateDialog.cpp +++ b/launcher/ui/dialogs/ModUpdateDialog.cpp @@ -34,12 +34,13 @@ static ModAPI::ModLoaderTypes mcLoaders(BaseInstance* inst) ModUpdateDialog::ModUpdateDialog(QWidget* parent, BaseInstance* instance, - const std::shared_ptr& mods, + const std::shared_ptr mods, std::list& search_for) : ReviewMessageBox(parent, tr("Confirm mods to update"), "") , m_parent(parent) , m_mod_model(mods) , m_candidates(search_for) + , m_second_try_metadata(new SequentialTask()) , m_instance(instance) { ReviewMessageBox::setGeometry(0, 0, 800, 600); @@ -47,15 +48,6 @@ ModUpdateDialog::ModUpdateDialog(QWidget* parent, ui->explainLabel->setText(tr("You're about to update the following mods:")); ui->onlyCheckedLabel->setText(tr("Only mods with a check will be updated!")); - connect(&m_check_task, &Task::failed, this, - [&](QString reason) { CustomMessageBox::selectable(this, tr("Error"), reason, QMessageBox::Critical)->exec(); }); - - connect(&m_check_task, &Task::succeeded, this, [&]() { - QStringList warnings = m_check_task.warnings(); - if (warnings.count()) { - CustomMessageBox::selectable(this, tr("Warnings"), warnings.join('\n'), QMessageBox::Warning)->exec(); - } - }); } void ModUpdateDialog::checkCandidates() @@ -89,26 +81,39 @@ void ModUpdateDialog::checkCandidates() auto versions = mcVersions(m_instance); auto loaders = mcLoaders(m_instance); + SequentialTask check_task (m_parent, tr("Checking for updates")); + if (!m_modrinth_to_update.empty()) { m_modrinth_check_task = new ModrinthCheckUpdate(m_modrinth_to_update, versions, loaders, m_mod_model); connect(m_modrinth_check_task, &CheckUpdateTask::checkFailed, this, [this](Mod mod, QString reason, QUrl recover_url) { m_failed_check_update.emplace_back(mod, reason, recover_url); }); - m_check_task.addTask(m_modrinth_check_task); + check_task.addTask(m_modrinth_check_task); } if (!m_flame_to_update.empty()) { m_flame_check_task = new FlameCheckUpdate(m_flame_to_update, versions, loaders, m_mod_model); connect(m_flame_check_task, &CheckUpdateTask::checkFailed, this, [this](Mod mod, QString reason, QUrl recover_url) { m_failed_check_update.emplace_back(mod, reason, recover_url); }); - m_check_task.addTask(m_flame_check_task); + check_task.addTask(m_flame_check_task); } + connect(&check_task, &Task::failed, this, + [&](QString reason) { CustomMessageBox::selectable(this, tr("Error"), reason, QMessageBox::Critical)->exec(); }); + + connect(&check_task, &Task::succeeded, this, [&]() { + QStringList warnings = check_task.warnings(); + if (warnings.count()) { + CustomMessageBox::selectable(this, tr("Warnings"), warnings.join('\n'), QMessageBox::Warning)->exec(); + } + }); + // Check for updates + // FIXME: SOMEHOW THIS IS NOT MODAL??????? ProgressDialog progress_dialog(m_parent); progress_dialog.setSkipButton(true, tr("Abort")); progress_dialog.setVisible(true); progress_dialog.setWindowTitle(tr("Checking for updates...")); - auto ret = progress_dialog.execWithTask(&m_check_task); + auto ret = progress_dialog.execWithTask(&check_task); // If the dialog was skipped / some download error happened if (ret == QDialog::DialogCode::Rejected) { @@ -183,13 +188,29 @@ auto ModUpdateDialog::ensureMetadata() -> bool { auto index_dir = indexDir(); - auto* seq = new SequentialTask(m_parent, tr("Looking for metadata")); + SequentialTask seq(m_parent, tr("Looking for metadata")); + + // A better use of data structures here could remove the need for this QHash + QHash should_try_others; + std::list modrinth_tmp; + std::list flame_tmp; bool confirm_rest = false; bool try_others_rest = false; bool skip_rest = false; ModPlatform::Provider provider_rest = ModPlatform::Provider::MODRINTH; + auto addToTmp = [&](Mod& m, ModPlatform::Provider p) { + switch (p) { + case ModPlatform::Provider::MODRINTH: + modrinth_tmp.push_back(m); + break; + case ModPlatform::Provider::FLAME: + flame_tmp.push_back(m); + break; + } + }; + for (auto& candidate : m_candidates) { if (candidate.status() != ModStatus::NoMetadata) { onMetadataEnsured(candidate); @@ -200,10 +221,8 @@ auto ModUpdateDialog::ensureMetadata() -> bool continue; if (confirm_rest) { - auto* task = new EnsureMetadataTask(candidate, index_dir, try_others_rest, provider_rest); - connect(task, &EnsureMetadataTask::metadataReady, [this, &candidate] { onMetadataEnsured(candidate); }); - connect(task, &EnsureMetadataTask::metadataFailed, [this, &candidate] { onMetadataFailed(candidate); }); - seq->addTask(task); + addToTmp(candidate, provider_rest); + should_try_others.insert(candidate.internal_id(), try_others_rest); continue; } @@ -224,18 +243,36 @@ auto ModUpdateDialog::ensureMetadata() -> bool try_others_rest = response.try_others; } - if (confirmed) { - auto* task = new EnsureMetadataTask(candidate, index_dir, response.try_others, response.chosen); - connect(task, &EnsureMetadataTask::metadataReady, [this, &candidate] { onMetadataEnsured(candidate); }); - connect(task, &EnsureMetadataTask::metadataFailed, [this, &candidate] { onMetadataFailed(candidate); }); - seq->addTask(task); - } + should_try_others.insert(candidate.internal_id(), response.try_others); + + if (confirmed) + addToTmp(candidate, response.chosen); } + if (!modrinth_tmp.empty()) { + auto* modrinth_task = new EnsureMetadataTask(modrinth_tmp, index_dir, ModPlatform::Provider::MODRINTH); + connect(modrinth_task, &EnsureMetadataTask::metadataReady, [this](Mod& candidate) { onMetadataEnsured(candidate); }); + connect(modrinth_task, &EnsureMetadataTask::metadataFailed, [this, &should_try_others](Mod& candidate) { + onMetadataFailed(candidate, should_try_others.find(candidate.internal_id()).value(), ModPlatform::Provider::MODRINTH); + }); + seq.addTask(modrinth_task); + } + + if (!flame_tmp.empty()) { + auto* flame_task = new EnsureMetadataTask(flame_tmp, index_dir, ModPlatform::Provider::FLAME); + connect(flame_task, &EnsureMetadataTask::metadataReady, [this](Mod& candidate) { onMetadataEnsured(candidate); }); + connect(flame_task, &EnsureMetadataTask::metadataFailed, [this, &should_try_others](Mod& candidate) { + onMetadataFailed(candidate, should_try_others.find(candidate.internal_id()).value(), ModPlatform::Provider::FLAME); + }); + seq.addTask(flame_task); + } + + seq.addTask(m_second_try_metadata); + ProgressDialog checking_dialog(m_parent); checking_dialog.setSkipButton(true, tr("Abort")); checking_dialog.setWindowTitle(tr("Generating metadata...")); - auto ret_metadata = checking_dialog.execWithTask(seq); + auto ret_metadata = checking_dialog.execWithTask(&seq); return (ret_metadata != QDialog::DialogCode::Rejected); } @@ -256,9 +293,31 @@ void ModUpdateDialog::onMetadataEnsured(Mod& mod) } } -void ModUpdateDialog::onMetadataFailed(Mod& mod) +ModPlatform::Provider next(ModPlatform::Provider p) { - m_failed_metadata.push_back(mod); + switch (p) { + case ModPlatform::Provider::MODRINTH: + return ModPlatform::Provider::FLAME; + case ModPlatform::Provider::FLAME: + return ModPlatform::Provider::MODRINTH; + } + + return ModPlatform::Provider::FLAME; +} + +void ModUpdateDialog::onMetadataFailed(Mod& mod, bool try_others, ModPlatform::Provider first_choice) +{ + if (try_others) { + auto index_dir = indexDir(); + + auto* task = new EnsureMetadataTask(mod, index_dir, next(first_choice)); + connect(task, &EnsureMetadataTask::metadataReady, [this](Mod& candidate) { onMetadataEnsured(candidate); }); + connect(task, &EnsureMetadataTask::metadataFailed, [this](Mod& candidate) { onMetadataFailed(candidate, false); }); + + m_second_try_metadata->addTask(task); + } else { + m_failed_metadata.push_back(mod); + } } void ModUpdateDialog::appendMod(CheckUpdateTask::UpdatableMod const& info) diff --git a/launcher/ui/dialogs/ModUpdateDialog.h b/launcher/ui/dialogs/ModUpdateDialog.h index 30cd5cbd..f40fc594 100644 --- a/launcher/ui/dialogs/ModUpdateDialog.h +++ b/launcher/ui/dialogs/ModUpdateDialog.h @@ -17,7 +17,7 @@ class ModUpdateDialog final : public ReviewMessageBox { public: explicit ModUpdateDialog(QWidget* parent, BaseInstance* instance, - const std::shared_ptr& mod_model, + const std::shared_ptr mod_model, std::list& search_for); void checkCandidates(); @@ -35,21 +35,21 @@ class ModUpdateDialog final : public ReviewMessageBox { private slots: void onMetadataEnsured(Mod&); - void onMetadataFailed(Mod&); + void onMetadataFailed(Mod&, bool try_others = false, ModPlatform::Provider first_choice = ModPlatform::Provider::MODRINTH); private: QWidget* m_parent; - SequentialTask m_check_task; ModrinthCheckUpdate* m_modrinth_check_task = nullptr; FlameCheckUpdate* m_flame_check_task = nullptr; - const std::shared_ptr& m_mod_model; + const std::shared_ptr m_mod_model; std::list& m_candidates; std::list m_modrinth_to_update; std::list m_flame_to_update; + SequentialTask* m_second_try_metadata; std::list m_failed_metadata; std::list> m_failed_check_update; -- cgit From 5f75e531e61e1f2cb5d602e084e9a0ddd1c85a5c Mon Sep 17 00:00:00 2001 From: flow Date: Mon, 20 Jun 2022 08:55:35 -0300 Subject: fix: handling around disabled mods Don't update disabled mods to prevent mod duplication. Also, chop filename in the metadata with a '.disabled'. Signed-off-by: flow --- launcher/ui/dialogs/ModUpdateDialog.cpp | 10 +++++++--- launcher/ui/dialogs/ModUpdateDialog.h | 2 +- 2 files changed, 8 insertions(+), 4 deletions(-) (limited to 'launcher/ui/dialogs') diff --git a/launcher/ui/dialogs/ModUpdateDialog.cpp b/launcher/ui/dialogs/ModUpdateDialog.cpp index 7584621a..2e1fbb08 100644 --- a/launcher/ui/dialogs/ModUpdateDialog.cpp +++ b/launcher/ui/dialogs/ModUpdateDialog.cpp @@ -62,8 +62,10 @@ void ModUpdateDialog::checkCandidates() // Report failed metadata generation if (!m_failed_metadata.empty()) { QString text; - for (const auto& mod : m_failed_metadata) { - text += tr("Mod name: %1
File name: %2

").arg(mod.name(), mod.fileinfo().fileName()); + for (const auto& failed : m_failed_metadata) { + const auto& mod = std::get<0>(failed); + const auto& reason = std::get<1>(failed); + text += tr("Mod name: %1
File name: %2
Reason: %3

").arg(mod.name(), mod.fileinfo().fileName(), reason); } ScrollMessageBox message_dialog(m_parent, tr("Metadata generation failed"), @@ -316,7 +318,9 @@ void ModUpdateDialog::onMetadataFailed(Mod& mod, bool try_others, ModPlatform::P m_second_try_metadata->addTask(task); } else { - m_failed_metadata.push_back(mod); + QString reason { tr("Didn't find a valid version on the selected mod provider(s)") }; + + m_failed_metadata.emplace_back(mod, reason); } } diff --git a/launcher/ui/dialogs/ModUpdateDialog.h b/launcher/ui/dialogs/ModUpdateDialog.h index f40fc594..336fbba2 100644 --- a/launcher/ui/dialogs/ModUpdateDialog.h +++ b/launcher/ui/dialogs/ModUpdateDialog.h @@ -50,7 +50,7 @@ class ModUpdateDialog final : public ReviewMessageBox { std::list m_flame_to_update; SequentialTask* m_second_try_metadata; - std::list m_failed_metadata; + std::list> m_failed_metadata; std::list> m_failed_check_update; QHash m_tasks; -- cgit From dfd6cb29be99ed9efb73a78eb7c31b6070c2d3c9 Mon Sep 17 00:00:00 2001 From: flow Date: Mon, 20 Jun 2022 13:01:49 -0300 Subject: feat: improve changelog and sort updatable mods Signed-off-by: flow --- launcher/ui/dialogs/ModUpdateDialog.cpp | 60 ++++++++++++++++++++------------- 1 file changed, 37 insertions(+), 23 deletions(-) (limited to 'launcher/ui/dialogs') diff --git a/launcher/ui/dialogs/ModUpdateDialog.cpp b/launcher/ui/dialogs/ModUpdateDialog.cpp index 2e1fbb08..2d969828 100644 --- a/launcher/ui/dialogs/ModUpdateDialog.cpp +++ b/launcher/ui/dialogs/ModUpdateDialog.cpp @@ -16,8 +16,7 @@ #include "modplatform/modrinth/ModrinthCheckUpdate.h" #include - -#include +#include #include static ModPlatform::ProviderCapabilities ProviderCaps; @@ -47,7 +46,6 @@ ModUpdateDialog::ModUpdateDialog(QWidget* parent, ui->explainLabel->setText(tr("You're about to update the following mods:")); ui->onlyCheckedLabel->setText(tr("Only mods with a check will be updated!")); - } void ModUpdateDialog::checkCandidates() @@ -69,9 +67,9 @@ void ModUpdateDialog::checkCandidates() } ScrollMessageBox message_dialog(m_parent, tr("Metadata generation failed"), - tr("Could not generate metadata for the following mods:
" - "Do you wish to proceed without those mods?"), - text); + tr("Could not generate metadata for the following mods:
" + "Do you wish to proceed without those mods?"), + text); message_dialog.setModal(true); if (message_dialog.exec() == QDialog::Rejected) { m_aborted = true; @@ -83,7 +81,7 @@ void ModUpdateDialog::checkCandidates() auto versions = mcVersions(m_instance); auto loaders = mcLoaders(m_instance); - SequentialTask check_task (m_parent, tr("Checking for updates")); + SequentialTask check_task(m_parent, tr("Checking for updates")); if (!m_modrinth_to_update.empty()) { m_modrinth_check_task = new ModrinthCheckUpdate(m_modrinth_to_update, versions, loaders, m_mod_model); @@ -166,9 +164,9 @@ void ModUpdateDialog::checkCandidates() } ScrollMessageBox message_dialog(m_parent, tr("Failed to check for updates"), - tr("Could not check or get the following mods for updates:
" - "Do you wish to proceed without those mods?"), - text); + tr("Could not check or get the following mods for updates:
" + "Do you wish to proceed without those mods?"), + text); message_dialog.setModal(true); if (message_dialog.exec() == QDialog::Rejected) { m_aborted = true; @@ -178,8 +176,21 @@ void ModUpdateDialog::checkCandidates() } // If there's no mod to be updated - if (ui->modTreeWidget->topLevelItemCount() == 0) + if (ui->modTreeWidget->topLevelItemCount() == 0) { m_no_updates = true; + } else { + // FIXME: Find a more efficient way of doing this! + + // Sort major items in alphabetical order (also sorts the children unfortunately) + ui->modTreeWidget->sortItems(0, Qt::SortOrder::AscendingOrder); + + // Re-sort the children + auto* item = ui->modTreeWidget->topLevelItem(0); + for (int i = 1; item != nullptr; ++i) { + item->sortChildren(0, Qt::SortOrder::DescendingOrder); + item = ui->modTreeWidget->topLevelItem(i); + } + } if (m_aborted || m_no_updates) QMetaObject::invokeMethod(this, "reject", Qt::QueuedConnection); @@ -318,7 +329,7 @@ void ModUpdateDialog::onMetadataFailed(Mod& mod, bool try_others, ModPlatform::P m_second_try_metadata->addTask(task); } else { - QString reason { tr("Didn't find a valid version on the selected mod provider(s)") }; + QString reason{ tr("Didn't find a valid version on the selected mod provider(s)") }; m_failed_metadata.emplace_back(mod, reason); } @@ -344,20 +355,23 @@ void ModUpdateDialog::appendMod(CheckUpdateTask::UpdatableMod const& info) changelog_item->setText(0, tr("Changelog of the latest version")); auto changelog = new QTreeWidgetItem(changelog_item); + auto changelog_area = new QTextBrowser(); - auto changelog_area = new QTextEdit(); - HoeDown h; - changelog_area->setText(h.process(info.changelog.toUtf8())); - changelog_area->setReadOnly(true); - if (info.changelog.size() < 250) // heuristic - changelog_area->setSizeAdjustPolicy(QTextEdit::SizeAdjustPolicy::AdjustToContents); + switch (info.provider) { + case ModPlatform::Provider::MODRINTH: { + HoeDown h; + changelog_area->setHtml(h.process(info.changelog.toUtf8())); + break; + } + case ModPlatform::Provider::FLAME: { + changelog_area->setHtml(info.changelog); + break; + } + } - ui->modTreeWidget->setItemWidget(changelog, 0, changelog_area); - changelog_item->insertChildren(0, { changelog }); + changelog_area->setOpenExternalLinks(true); - item_top->insertChildren(0, { old_version_item }); - item_top->insertChildren(1, { new_version_item }); - item_top->insertChildren(2, { changelog_item }); + ui->modTreeWidget->setItemWidget(changelog, 0, changelog_area); ui->modTreeWidget->addTopLevelItem(item_top); } -- cgit From fac63541a4831414b052de6400e7543bbc611db0 Mon Sep 17 00:00:00 2001 From: flow Date: Fri, 24 Jun 2022 20:59:17 -0300 Subject: fix: work around HoeDown bug(?) in changelog line breaks Signed-off-by: flow --- launcher/ui/dialogs/ModUpdateDialog.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'launcher/ui/dialogs') diff --git a/launcher/ui/dialogs/ModUpdateDialog.cpp b/launcher/ui/dialogs/ModUpdateDialog.cpp index 2d969828..51e37bcf 100644 --- a/launcher/ui/dialogs/ModUpdateDialog.cpp +++ b/launcher/ui/dialogs/ModUpdateDialog.cpp @@ -357,10 +357,12 @@ void ModUpdateDialog::appendMod(CheckUpdateTask::UpdatableMod const& info) auto changelog = new QTreeWidgetItem(changelog_item); auto changelog_area = new QTextBrowser(); + switch (info.provider) { case ModPlatform::Provider::MODRINTH: { HoeDown h; - changelog_area->setHtml(h.process(info.changelog.toUtf8())); + // HoeDown bug?: \n aren't converted to
+ changelog_area->setHtml(h.process(info.changelog.toUtf8()).replace('\n', "
")); break; } case ModPlatform::Provider::FLAME: { -- cgit From c4316e81e64ad4ac63b0b50106b324a73abdc150 Mon Sep 17 00:00:00 2001 From: flow Date: Sun, 26 Jun 2022 14:17:15 -0300 Subject: change: make Mod a QObject used as a pointer Prevents problems when copying it around! Signed-off-by: flow --- launcher/ui/dialogs/ModUpdateDialog.cpp | 59 +++++++++++++++++---------------- launcher/ui/dialogs/ModUpdateDialog.h | 16 ++++----- 2 files changed, 38 insertions(+), 37 deletions(-) (limited to 'launcher/ui/dialogs') diff --git a/launcher/ui/dialogs/ModUpdateDialog.cpp b/launcher/ui/dialogs/ModUpdateDialog.cpp index 51e37bcf..3dc2a85f 100644 --- a/launcher/ui/dialogs/ModUpdateDialog.cpp +++ b/launcher/ui/dialogs/ModUpdateDialog.cpp @@ -34,7 +34,7 @@ static ModAPI::ModLoaderTypes mcLoaders(BaseInstance* inst) ModUpdateDialog::ModUpdateDialog(QWidget* parent, BaseInstance* instance, const std::shared_ptr mods, - std::list& search_for) + std::list& search_for) : ReviewMessageBox(parent, tr("Confirm mods to update"), "") , m_parent(parent) , m_mod_model(mods) @@ -63,7 +63,7 @@ void ModUpdateDialog::checkCandidates() for (const auto& failed : m_failed_metadata) { const auto& mod = std::get<0>(failed); const auto& reason = std::get<1>(failed); - text += tr("Mod name: %1
File name: %2
Reason: %3

").arg(mod.name(), mod.fileinfo().fileName(), reason); + text += tr("Mod name: %1
File name: %2
Reason: %3

").arg(mod->name(), mod->fileinfo().fileName(), reason); } ScrollMessageBox message_dialog(m_parent, tr("Metadata generation failed"), @@ -86,14 +86,14 @@ void ModUpdateDialog::checkCandidates() if (!m_modrinth_to_update.empty()) { m_modrinth_check_task = new ModrinthCheckUpdate(m_modrinth_to_update, versions, loaders, m_mod_model); connect(m_modrinth_check_task, &CheckUpdateTask::checkFailed, this, - [this](Mod mod, QString reason, QUrl recover_url) { m_failed_check_update.emplace_back(mod, reason, recover_url); }); + [this](Mod* mod, QString reason, QUrl recover_url) { m_failed_check_update.emplace_back(mod, reason, recover_url); }); check_task.addTask(m_modrinth_check_task); } if (!m_flame_to_update.empty()) { m_flame_check_task = new FlameCheckUpdate(m_flame_to_update, versions, loaders, m_mod_model); connect(m_flame_check_task, &CheckUpdateTask::checkFailed, this, - [this](Mod mod, QString reason, QUrl recover_url) { m_failed_check_update.emplace_back(mod, reason, recover_url); }); + [this](Mod* mod, QString reason, QUrl recover_url) { m_failed_check_update.emplace_back(mod, reason, recover_url); }); check_task.addTask(m_flame_check_task); } @@ -152,9 +152,9 @@ void ModUpdateDialog::checkCandidates() const auto& reason = std::get<1>(failed); const auto& recover_url = std::get<2>(failed); - qDebug() << mod.name() << " failed to check for updates!"; + qDebug() << mod->name() << " failed to check for updates!"; - text += tr("Mod name: %1").arg(mod.name()) + "
"; + text += tr("Mod name: %1").arg(mod->name()) + "
"; if (!reason.isEmpty()) text += tr("Reason: %1").arg(reason) + "
"; if (!recover_url.isEmpty()) @@ -205,15 +205,15 @@ auto ModUpdateDialog::ensureMetadata() -> bool // A better use of data structures here could remove the need for this QHash QHash should_try_others; - std::list modrinth_tmp; - std::list flame_tmp; + std::list modrinth_tmp; + std::list flame_tmp; bool confirm_rest = false; bool try_others_rest = false; bool skip_rest = false; ModPlatform::Provider provider_rest = ModPlatform::Provider::MODRINTH; - auto addToTmp = [&](Mod& m, ModPlatform::Provider p) { + auto addToTmp = [&](Mod* m, ModPlatform::Provider p) { switch (p) { case ModPlatform::Provider::MODRINTH: modrinth_tmp.push_back(m); @@ -224,9 +224,10 @@ auto ModUpdateDialog::ensureMetadata() -> bool } }; - for (auto& candidate : m_candidates) { - if (candidate.status() != ModStatus::NoMetadata) { - onMetadataEnsured(candidate); + for (auto candidate : m_candidates) { + auto* candidate_ptr = candidate.get(); + if (candidate->status() != ModStatus::NoMetadata) { + onMetadataEnsured(candidate_ptr); continue; } @@ -234,8 +235,8 @@ auto ModUpdateDialog::ensureMetadata() -> bool continue; if (confirm_rest) { - addToTmp(candidate, provider_rest); - should_try_others.insert(candidate.internal_id(), try_others_rest); + addToTmp(candidate_ptr, provider_rest); + should_try_others.insert(candidate->internal_id(), try_others_rest); continue; } @@ -243,7 +244,7 @@ auto ModUpdateDialog::ensureMetadata() -> bool chooser.setDescription(tr("This mod (%1) does not have a metadata yet. We need to create one in order to keep relevant " "information on how to update this " "mod. To do this, please select a mod provider from which we can search for updates for %1.") - .arg(candidate.name())); + .arg(candidate->name())); auto confirmed = chooser.exec() == QDialog::DialogCode::Accepted; auto response = chooser.getResponse(); @@ -256,26 +257,26 @@ auto ModUpdateDialog::ensureMetadata() -> bool try_others_rest = response.try_others; } - should_try_others.insert(candidate.internal_id(), response.try_others); + should_try_others.insert(candidate->internal_id(), response.try_others); if (confirmed) - addToTmp(candidate, response.chosen); + addToTmp(candidate_ptr, response.chosen); } if (!modrinth_tmp.empty()) { auto* modrinth_task = new EnsureMetadataTask(modrinth_tmp, index_dir, ModPlatform::Provider::MODRINTH); - connect(modrinth_task, &EnsureMetadataTask::metadataReady, [this](Mod& candidate) { onMetadataEnsured(candidate); }); - connect(modrinth_task, &EnsureMetadataTask::metadataFailed, [this, &should_try_others](Mod& candidate) { - onMetadataFailed(candidate, should_try_others.find(candidate.internal_id()).value(), ModPlatform::Provider::MODRINTH); + connect(modrinth_task, &EnsureMetadataTask::metadataReady, [this](Mod* candidate) { onMetadataEnsured(candidate); }); + connect(modrinth_task, &EnsureMetadataTask::metadataFailed, [this, &should_try_others](Mod* candidate) { + onMetadataFailed(candidate, should_try_others.find(candidate->internal_id()).value(), ModPlatform::Provider::MODRINTH); }); seq.addTask(modrinth_task); } if (!flame_tmp.empty()) { auto* flame_task = new EnsureMetadataTask(flame_tmp, index_dir, ModPlatform::Provider::FLAME); - connect(flame_task, &EnsureMetadataTask::metadataReady, [this](Mod& candidate) { onMetadataEnsured(candidate); }); - connect(flame_task, &EnsureMetadataTask::metadataFailed, [this, &should_try_others](Mod& candidate) { - onMetadataFailed(candidate, should_try_others.find(candidate.internal_id()).value(), ModPlatform::Provider::FLAME); + connect(flame_task, &EnsureMetadataTask::metadataReady, [this](Mod* candidate) { onMetadataEnsured(candidate); }); + connect(flame_task, &EnsureMetadataTask::metadataFailed, [this, &should_try_others](Mod* candidate) { + onMetadataFailed(candidate, should_try_others.find(candidate->internal_id()).value(), ModPlatform::Provider::FLAME); }); seq.addTask(flame_task); } @@ -290,13 +291,13 @@ auto ModUpdateDialog::ensureMetadata() -> bool return (ret_metadata != QDialog::DialogCode::Rejected); } -void ModUpdateDialog::onMetadataEnsured(Mod& mod) +void ModUpdateDialog::onMetadataEnsured(Mod* mod) { // When the mod is a folder, for instance - if (!mod.metadata()) + if (!mod->metadata()) return; - switch (mod.metadata()->provider) { + switch (mod->metadata()->provider) { case ModPlatform::Provider::MODRINTH: m_modrinth_to_update.push_back(mod); break; @@ -318,14 +319,14 @@ ModPlatform::Provider next(ModPlatform::Provider p) return ModPlatform::Provider::FLAME; } -void ModUpdateDialog::onMetadataFailed(Mod& mod, bool try_others, ModPlatform::Provider first_choice) +void ModUpdateDialog::onMetadataFailed(Mod* mod, bool try_others, ModPlatform::Provider first_choice) { if (try_others) { auto index_dir = indexDir(); auto* task = new EnsureMetadataTask(mod, index_dir, next(first_choice)); - connect(task, &EnsureMetadataTask::metadataReady, [this](Mod& candidate) { onMetadataEnsured(candidate); }); - connect(task, &EnsureMetadataTask::metadataFailed, [this](Mod& candidate) { onMetadataFailed(candidate, false); }); + connect(task, &EnsureMetadataTask::metadataReady, [this](Mod* candidate) { onMetadataEnsured(candidate); }); + connect(task, &EnsureMetadataTask::metadataFailed, [this](Mod* candidate) { onMetadataFailed(candidate, false); }); m_second_try_metadata->addTask(task); } else { diff --git a/launcher/ui/dialogs/ModUpdateDialog.h b/launcher/ui/dialogs/ModUpdateDialog.h index 336fbba2..b598447d 100644 --- a/launcher/ui/dialogs/ModUpdateDialog.h +++ b/launcher/ui/dialogs/ModUpdateDialog.h @@ -18,7 +18,7 @@ class ModUpdateDialog final : public ReviewMessageBox { explicit ModUpdateDialog(QWidget* parent, BaseInstance* instance, const std::shared_ptr mod_model, - std::list& search_for); + std::list& search_for); void checkCandidates(); @@ -34,8 +34,8 @@ class ModUpdateDialog final : public ReviewMessageBox { auto ensureMetadata() -> bool; private slots: - void onMetadataEnsured(Mod&); - void onMetadataFailed(Mod&, bool try_others = false, ModPlatform::Provider first_choice = ModPlatform::Provider::MODRINTH); + void onMetadataEnsured(Mod*); + void onMetadataFailed(Mod*, bool try_others = false, ModPlatform::Provider first_choice = ModPlatform::Provider::MODRINTH); private: QWidget* m_parent; @@ -45,13 +45,13 @@ class ModUpdateDialog final : public ReviewMessageBox { const std::shared_ptr m_mod_model; - std::list& m_candidates; - std::list m_modrinth_to_update; - std::list m_flame_to_update; + std::list& m_candidates; + std::list m_modrinth_to_update; + std::list m_flame_to_update; SequentialTask* m_second_try_metadata; - std::list> m_failed_metadata; - std::list> m_failed_check_update; + std::list> m_failed_metadata; + std::list> m_failed_check_update; QHash m_tasks; BaseInstance* m_instance; -- cgit From 2b65ee433fa594173205ceed104d5261052cac9a Mon Sep 17 00:00:00 2001 From: flow Date: Fri, 1 Jul 2022 09:42:15 -0300 Subject: fix: changelogs with too much space between lines Signed-off-by: flow --- launcher/ui/dialogs/ModUpdateDialog.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'launcher/ui/dialogs') diff --git a/launcher/ui/dialogs/ModUpdateDialog.cpp b/launcher/ui/dialogs/ModUpdateDialog.cpp index 3dc2a85f..24fa229d 100644 --- a/launcher/ui/dialogs/ModUpdateDialog.cpp +++ b/launcher/ui/dialogs/ModUpdateDialog.cpp @@ -363,7 +363,13 @@ void ModUpdateDialog::appendMod(CheckUpdateTask::UpdatableMod const& info) case ModPlatform::Provider::MODRINTH: { HoeDown h; // HoeDown bug?: \n aren't converted to
- changelog_area->setHtml(h.process(info.changelog.toUtf8()).replace('\n', "
")); + auto text = h.process(info.changelog.toUtf8()); + + // Don't convert if there's an HTML tag right after (Qt rendering weirdness) + text.remove(QRegularExpression("(\n+)(?=<)")); + text.replace('\n', "
"); + + changelog_area->setHtml(text); break; } case ModPlatform::Provider::FLAME: { -- cgit From 79b0a16f7a74aa8184c41f0574865e1cf6db0519 Mon Sep 17 00:00:00 2001 From: flow Date: Fri, 1 Jul 2022 11:54:32 -0300 Subject: fix: try finding a good height for short changelogs Signed-off-by: flow --- launcher/ui/dialogs/ModUpdateDialog.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'launcher/ui/dialogs') diff --git a/launcher/ui/dialogs/ModUpdateDialog.cpp b/launcher/ui/dialogs/ModUpdateDia