From 1f8408c79340c7cc93fa1677021f3d0dc3f75c34 Mon Sep 17 00:00:00 2001 From: phit <2097483+phit@users.noreply.github.com> Date: Fri, 26 Mar 2021 23:15:20 +0100 Subject: NOISSUE Curseforge makeover update UI to match other modpack platforms add sorting add version selection, fixes GH-3667 add installing beta versions, fixes GH-3611 --- application/pages/modplatform/flame/FlameData.h | 38 ----- application/pages/modplatform/flame/FlameModel.cpp | 96 +++--------- application/pages/modplatform/flame/FlameModel.h | 7 +- application/pages/modplatform/flame/FlamePage.cpp | 95 ++++++++++-- application/pages/modplatform/flame/FlamePage.h | 9 +- application/pages/modplatform/flame/FlamePage.ui | 171 ++++++++++----------- 6 files changed, 196 insertions(+), 220 deletions(-) delete mode 100644 application/pages/modplatform/flame/FlameData.h (limited to 'application/pages') diff --git a/application/pages/modplatform/flame/FlameData.h b/application/pages/modplatform/flame/FlameData.h deleted file mode 100644 index 9245ba8a..00000000 --- a/application/pages/modplatform/flame/FlameData.h +++ /dev/null @@ -1,38 +0,0 @@ -#pragma once - -#include -#include - -namespace Flame { - -struct ModpackAuthor { - QString name; - QString url; -}; - -struct ModpackFile { - int addonId; - int fileId; - QString version; - QString mcVersion; - QString downloadUrl; -}; - -struct Modpack -{ - bool broken = true; - int addonId = 0; - - QString name; - QString description; - QList authors; - QString mcVersion; - QString logoName; - QString logoUrl; - QString websiteUrl; - - ModpackFile latestFile; -}; -} - -Q_DECLARE_METATYPE(Flame::Modpack) diff --git a/application/pages/modplatform/flame/FlameModel.cpp b/application/pages/modplatform/flame/FlameModel.cpp index 6d9dbda7..228a88c5 100644 --- a/application/pages/modplatform/flame/FlameModel.cpp +++ b/application/pages/modplatform/flame/FlameModel.cpp @@ -1,5 +1,6 @@ #include "FlameModel.h" #include "MultiMC.h" +#include #include #include @@ -38,7 +39,7 @@ QVariant ListModel::data(const QModelIndex &index, int role) const return QString("INVALID INDEX %1").arg(pos); } - Modpack pack = modpacks.at(pos); + IndexedPack pack = modpacks.at(pos); if(role == Qt::DisplayRole) { return pack.name; @@ -163,13 +164,12 @@ void ListModel::performPaginatedSearch() "https://addons-ecs.forgesvc.net/api/v2/addon/search?" "categoryId=0&" "gameId=432&" - //"gameVersion=1.12.2&" "index=%1&" "pageSize=25&" "searchFilter=%2&" "sectionId=4471&" - "sort=0" - ).arg(nextSearchOffset).arg(currentSearchTerm); + "sort=%3" + ).arg(nextSearchOffset).arg(currentSearchTerm).arg(currentSort); netJob->addNetAction(Net::Download::makeByteArray(QUrl(searchUrl), &response)); jobPtr = netJob; jobPtr->start(); @@ -177,12 +177,13 @@ void ListModel::performPaginatedSearch() QObject::connect(netJob, &NetJob::failed, this, &ListModel::searchRequestFailed); } -void ListModel::searchWithTerm(const QString& term) +void ListModel::searchWithTerm(const QString& term, int sort) { - if(currentSearchTerm == term) { + if(currentSearchTerm == term && currentSearchTerm.isNull() == term.isNull() && currentSort == sort) { return; } currentSearchTerm = term; + currentSort = sort; if(jobPtr) { jobPtr->abort(); searchState = ResetRequested; @@ -210,79 +211,24 @@ void Flame::ListModel::searchRequestFinished() return; } - QList newList; - auto objs = doc.array(); - for(auto projectIter: objs) { - Modpack pack; - auto project = projectIter.toObject(); - pack.addonId = project.value("id").toInt(0); - if (pack.addonId == 0) { - qWarning() << "Pack without an ID, skipping: " << pack.name; - continue; - } - pack.name = project.value("name").toString(); - pack.websiteUrl = project.value("websiteUrl").toString(); - pack.description = project.value("summary").toString(); - bool thumbnailFound = false; - auto attachments = project.value("attachments").toArray(); - for(auto attachmentIter: attachments) { - auto attachment = attachmentIter.toObject(); - bool isDefault = attachment.value("isDefault").toBool(false); - if(isDefault) { - thumbnailFound = true; - pack.logoName = attachment.value("title").toString(); - pack.logoUrl = attachment.value("thumbnailUrl").toString(); - break; - } - } - if(!thumbnailFound) { - qWarning() << "Pack without an icon, skipping: " << pack.name; - continue; - } - auto authors = project.value("authors").toArray(); - for(auto authorIter: authors) { - auto author = authorIter.toObject(); - ModpackAuthor packAuthor; - packAuthor.name = author.value("name").toString(); - packAuthor.url = author.value("url").toString(); - pack.authors.append(packAuthor); - } - int defaultFileId = project.value("defaultFileId").toInt(0); - if(defaultFileId == 0) { - qWarning() << "Pack without default file, skipping: " << pack.name; - continue; - } - bool found = false; - auto files = project.value("latestFiles").toArray(); - for(auto fileIter: files) { - auto file = fileIter.toObject(); - int id = file.value("id").toInt(0); - // NOTE: for now, ignore everything that's not the default... - if(id != defaultFileId) { - continue; - } - pack.latestFile.addonId = pack.addonId; - pack.latestFile.fileId = id; - auto versionArray = file.value("gameVersion").toArray(); - if(versionArray.size() < 1) { - continue; - } - - // pick the latest version supported - pack.latestFile.mcVersion = versionArray[0].toString(); - pack.latestFile.version = file.value("displayName").toString(); - pack.latestFile.downloadUrl = file.value("downloadUrl").toString(); - found = true; - break; + QList newList; + auto packs = doc.array(); + for(auto packRaw : packs) { + auto packObj = packRaw.toObject(); + + Flame::IndexedPack pack; + try + { + Flame::loadIndexedPack(pack, packObj); + newList.append(pack); } - if(!found) { - qWarning() << "Pack with no good file, skipping: " << pack.name; + catch(const JSONValidationError &e) + { + qWarning() << "Error while loading pack from CurseForge: " << e.cause(); continue; } - pack.broken = false; - newList.append(pack); } - if(objs.size() < 25) { + if(packs.size() < 25) { searchState = Finished; } else { nextSearchOffset += 25; diff --git a/application/pages/modplatform/flame/FlameModel.h b/application/pages/modplatform/flame/FlameModel.h index b4dded76..24383db0 100644 --- a/application/pages/modplatform/flame/FlameModel.h +++ b/application/pages/modplatform/flame/FlameModel.h @@ -15,7 +15,7 @@ #include #include -#include "FlameData.h" +#include namespace Flame { @@ -39,7 +39,7 @@ public: void fetchMore(const QModelIndex & parent) override; void getLogo(const QString &logo, const QString &logoUrl, LogoCallback callback); - void searchWithTerm(const QString & term); + void searchWithTerm(const QString & term, const int sort); private slots: void performPaginatedSearch(); @@ -54,13 +54,14 @@ private: void requestLogo(QString file, QString url); private: - QList modpacks; + QList modpacks; QStringList m_failedLogos; QStringList m_loadingLogos; LogoMap m_logoMap; QMap waitingCallbacks; QString currentSearchTerm; + int currentSort = 0; int nextSearchOffset = 0; enum SearchState { None, diff --git a/application/pages/modplatform/flame/FlamePage.cpp b/application/pages/modplatform/flame/FlamePage.cpp index 3889f15a..2b7d9004 100644 --- a/application/pages/modplatform/flame/FlamePage.cpp +++ b/application/pages/modplatform/flame/FlamePage.cpp @@ -2,6 +2,7 @@ #include "ui_FlamePage.h" #include "MultiMC.h" +#include #include "dialogs/NewInstanceDialog.h" #include #include "FlameModel.h" @@ -13,9 +14,20 @@ FlamePage::FlamePage(NewInstanceDialog* dialog, QWidget *parent) ui->setupUi(this); connect(ui->searchButton, &QPushButton::clicked, this, &FlamePage::triggerSearch); ui->searchEdit->installEventFilter(this); - model = new Flame::ListModel(this); - ui->packView->setModel(model); + listModel = new Flame::ListModel(this); + ui->packView->setModel(listModel); + + // index is used to set the sorting with the curseforge api + ui->sortByBox->addItem(tr("Sort by featured")); + ui->sortByBox->addItem(tr("Sort by popularity")); + ui->sortByBox->addItem(tr("Sort by last updated")); + ui->sortByBox->addItem(tr("Sort by name")); + ui->sortByBox->addItem(tr("Sort by author")); + ui->sortByBox->addItem(tr("Sort by total downloads")); + + connect(ui->sortByBox, QOverload::of(&QComboBox::currentIndexChanged), this, &FlamePage::triggerSearch); connect(ui->packView->selectionModel(), &QItemSelectionModel::currentChanged, this, &FlamePage::onSelectionChanged); + connect(ui->versionSelectionBox, &QComboBox::currentTextChanged, this, &FlamePage::onVersionSelectionChanged); } FlamePage::~FlamePage() @@ -44,26 +56,28 @@ bool FlamePage::shouldDisplay() const void FlamePage::openedImpl() { suggestCurrent(); + triggerSearch(); } void FlamePage::triggerSearch() { - model->searchWithTerm(ui->searchEdit->text()); + listModel->searchWithTerm(ui->searchEdit->text(), ui->sortByBox->currentIndex()); } void FlamePage::onSelectionChanged(QModelIndex first, QModelIndex second) { + ui->versionSelectionBox->clear(); + if(!first.isValid()) { if(isOpened) { dialog->setSuggestedPack(); } - ui->frame->clear(); return; } - current = model->data(first, Qt::UserRole).value(); + current = listModel->data(first, Qt::UserRole).value(); QString text = ""; QString name = current.name; @@ -82,12 +96,56 @@ void FlamePage::onSelectionChanged(QModelIndex first, QModelIndex second) for(auto & author: current.authors) { authorStrs.push_back(authorToStr(author)); } - text += tr(" by ") + authorStrs.join(", "); + text += "
" + tr(" by ") + authorStrs.join(", "); } + text += "

"; - ui->frame->setModText(text); - ui->frame->setModDescription(current.description); - suggestCurrent(); + ui->packDescription->setHtml(text + current.description); + + if (current.versionsLoaded == false) + { + qDebug() << "Loading flame modpack versions"; + NetJob *netJob = new NetJob(QString("Flame::PackVersions(%1)").arg(current.name)); + std::shared_ptr response = std::make_shared(); + int addonId = current.addonId; + netJob->addNetAction(Net::Download::makeByteArray(QString("https://addons-ecs.forgesvc.net/api/v2/addon/%1/files").arg(addonId), response.get())); + + QObject::connect(netJob, &NetJob::succeeded, this, [this, response] + { + QJsonParseError parse_error; + QJsonDocument doc = QJsonDocument::fromJson(*response, &parse_error); + if(parse_error.error != QJsonParseError::NoError) { + qWarning() << "Error while parsing JSON response from CurseForge at " << parse_error.offset << " reason: " << parse_error.errorString(); + qWarning() << *response; + return; + } + QJsonArray arr = doc.array(); + try + { + Flame::loadIndexedPackVersions(current, arr); + } + catch(const JSONValidationError &e) + { + qDebug() << *response; + qWarning() << "Error while reading flame modpack version: " << e.cause(); + } + + for(auto version : current.versions) { + ui->versionSelectionBox->addItem(version.version, QVariant(version.downloadUrl)); + } + + suggestCurrent(); + }); + netJob->start(); + } + else + { + for(auto version : current.versions) { + ui->versionSelectionBox->addItem(version.version, QVariant(version.downloadUrl)); + } + + suggestCurrent(); + } } void FlamePage::suggestCurrent() @@ -96,16 +154,23 @@ void FlamePage::suggestCurrent() { return; } - if(current.broken) - { - dialog->setSuggestedPack(); - } - dialog->setSuggestedPack(current.name, new InstanceImportTask(current.latestFile.downloadUrl)); + dialog->setSuggestedPack(current.name, new InstanceImportTask(selectedVersion)); QString editedLogoName; editedLogoName = "curseforge_" + current.logoName.section(".", 0, 0); - model->getLogo(current.logoName, current.logoUrl, [this, editedLogoName](QString logo) + listModel->getLogo(current.logoName, current.logoUrl, [this, editedLogoName](QString logo) { dialog->setSuggestedIconFromFile(logo, editedLogoName); }); } + +void FlamePage::onVersionSelectionChanged(QString data) +{ + if(data.isNull() || data.isEmpty()) + { + selectedVersion = ""; + return; + } + selectedVersion = ui->versionSelectionBox->currentData().toString(); + suggestCurrent(); +} \ No newline at end of file diff --git a/application/pages/modplatform/flame/FlamePage.h b/application/pages/modplatform/flame/FlamePage.h index e50186f5..467bb44b 100644 --- a/application/pages/modplatform/flame/FlamePage.h +++ b/application/pages/modplatform/flame/FlamePage.h @@ -20,7 +20,7 @@ #include "pages/BasePage.h" #include #include "tasks/Task.h" -#include "FlameData.h" +#include namespace Ui { @@ -68,10 +68,13 @@ private: private slots: void triggerSearch(); void onSelectionChanged(QModelIndex first, QModelIndex second); + void onVersionSelectionChanged(QString data); private: Ui::FlamePage *ui = nullptr; NewInstanceDialog* dialog = nullptr; - Flame::ListModel* model = nullptr; - Flame::Modpack current; + Flame::ListModel* listModel = nullptr; + Flame::IndexedPack current; + + QString selectedVersion; }; diff --git a/application/pages/modplatform/flame/FlamePage.ui b/application/pages/modplatform/flame/FlamePage.ui index 21e23f1f..9723815a 100644 --- a/application/pages/modplatform/flame/FlamePage.ui +++ b/application/pages/modplatform/flame/FlamePage.ui @@ -1,91 +1,90 @@ - FlamePage - - - - 0 - 0 - 875 - 745 - - - - - - - - 0 - - - 0 - - - 0 - - - 0 - - - + FlamePage + + + + 0 + 0 + 837 + 685 + + + + + + + + + + 48 + 48 + + + + Qt::ScrollBarAlwaysOff + + + true + + + + + + + true + + + true + + + + - - - - Search - - + + + + + + + + + Version selected: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + - - - - - - - Qt::ScrollBarAlwaysOff - - - true - - - - 48 - 48 - - - - - - - - - 0 - 0 - - - - QFrame::StyledPanel - - - QFrame::Raised - - - - - - - - MCModInfoFrame - QFrame -
widgets/MCModInfoFrame.h
- 1 -
-
- - searchEdit - searchButton - packView - - - + + + + Search + + + + + + + Search and filter ... + + + +
+
+ + searchEdit + searchButton + packView + packDescription + sortByBox + versionSelectionBox + + +
-- cgit