diff options
Diffstat (limited to 'application/pages/modplatform/flame')
-rw-r--r-- | application/pages/modplatform/flame/FlameData.h | 38 | ||||
-rw-r--r-- | application/pages/modplatform/flame/FlameModel.cpp | 96 | ||||
-rw-r--r-- | application/pages/modplatform/flame/FlameModel.h | 7 | ||||
-rw-r--r-- | application/pages/modplatform/flame/FlamePage.cpp | 95 | ||||
-rw-r--r-- | application/pages/modplatform/flame/FlamePage.h | 9 | ||||
-rw-r--r-- | application/pages/modplatform/flame/FlamePage.ui | 171 |
6 files changed, 196 insertions, 220 deletions
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 <QString> -#include <QList> - -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<ModpackAuthor> 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 <Json.h> #include <MMCStrings.h> #include <Version.h> @@ -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<Modpack> 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<Flame::IndexedPack> 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 <functional> #include <net/NetJob.h> -#include "FlameData.h" +#include <modplatform/flame/FlamePackIndex.h> 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<Modpack> modpacks; + QList<IndexedPack> modpacks; QStringList m_failedLogos; QStringList m_loadingLogos; LogoMap m_logoMap; QMap<QString, LogoCallback> 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 <Json.h> #include "dialogs/NewInstanceDialog.h" #include <InstanceImportTask.h> #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<int>::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<Flame::Modpack>(); + current = listModel->data(first, Qt::UserRole).value<Flame::IndexedPack>(); 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 += "<br>" + tr(" by ") + authorStrs.join(", "); } + text += "<br><br>"; - 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<QByteArray> response = std::make_shared<QByteArray>(); + 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 <MultiMC.h> #include "tasks/Task.h" -#include "FlameData.h" +#include <modplatform/flame/FlamePackIndex.h> 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 @@ <?xml version="1.0" encoding="UTF-8"?> <ui version="4.0"> - <class>FlamePage</class> - <widget class="QWidget" name="FlamePage"> - <property name="geometry"> - <rect> - <x>0</x> - <y>0</y> - <width>875</width> - <height>745</height> - </rect> - </property> - <layout class="QVBoxLayout" name="verticalLayout"> - <item> - <widget class="QWidget" name="widget" native="true"> - <layout class="QHBoxLayout" name="horizontalLayout"> - <property name="leftMargin"> - <number>0</number> - </property> - <property name="topMargin"> - <number>0</number> - </property> - <property name="rightMargin"> - <number>0</number> - </property> - <property name="bottomMargin"> - <number>0</number> - </property> - <item> - <widget class="QLineEdit" name="searchEdit"/> + <class>FlamePage</class> + <widget class="QWidget" name="FlamePage"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>837</width> + <height>685</height> + </rect> + </property> + <layout class="QGridLayout" name="gridLayout"> + <item row="1" column="0" colspan="2"> + <layout class="QGridLayout" name="gridLayout_3"> + <item row="1" column="0"> + <widget class="QListView" name="packView"> + <property name="iconSize"> + <size> + <width>48</width> + <height>48</height> + </size> + </property> + <property name="horizontalScrollBarPolicy"> + <enum>Qt::ScrollBarAlwaysOff</enum> + </property> + <property name="alternatingRowColors"> + <bool>true</bool> + </property> + </widget> + </item> + <item row="1" column="1"> + <widget class="QTextBrowser" name="packDescription"> + <property name="openExternalLinks"> + <bool>true</bool> + </property> + <property name="openLinks"> + <bool>true</bool> + </property> + </widget> + </item> + </layout> </item> - <item> - <widget class="QPushButton" name="searchButton"> - <property name="text"> - <string>Search</string> - </property> - </widget> + <item row="2" column="0" colspan="2"> + <layout class="QGridLayout" name="gridLayout_4" columnstretch="0,0,0" rowminimumheight="0" columnminimumwidth="0,0,0"> + <item row="0" column="2"> + <widget class="QComboBox" name="versionSelectionBox"/> + </item> + <item row="0" column="1"> + <widget class="QLabel" name="label"> + <property name="text"> + <string>Version selected:</string> + </property> + <property name="alignment"> + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + </widget> + </item> + <item row="0" column="0"> + <widget class="QComboBox" name="sortByBox"/> + </item> + </layout> </item> - </layout> - </widget> - </item> - <item> - <widget class="QListView" name="packView"> - <property name="horizontalScrollBarPolicy"> - <enum>Qt::ScrollBarAlwaysOff</enum> - </property> - <property name="alternatingRowColors"> - <bool>true</bool> - </property> - <property name="iconSize"> - <size> - <width>48</width> - <height>48</height> - </size> - </property> - </widget> - </item> - <item> - <widget class="MCModInfoFrame" name="frame"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Preferred" vsizetype="Minimum"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="frameShape"> - <enum>QFrame::StyledPanel</enum> - </property> - <property name="frameShadow"> - <enum>QFrame::Raised</enum> - </property> - </widget> - </item> - </layout> - </widget> - <customwidgets> - <customwidget> - <class>MCModInfoFrame</class> - <extends>QFrame</extends> - <header>widgets/MCModInfoFrame.h</header> - <container>1</container> - </customwidget> - </customwidgets> - <tabstops> - <tabstop>searchEdit</tabstop> - <tabstop>searchButton</tabstop> - <tabstop>packView</tabstop> - </tabstops> - <resources/> - <connections/> + <item row="0" column="1"> + <widget class="QPushButton" name="searchButton"> + <property name="text"> + <string>Search</string> + </property> + </widget> + </item> + <item row="0" column="0"> + <widget class="QLineEdit" name="searchEdit"> + <property name="placeholderText"> + <string>Search and filter ...</string> + </property> + </widget> + </item> + </layout> + </widget> + <tabstops> + <tabstop>searchEdit</tabstop> + <tabstop>searchButton</tabstop> + <tabstop>packView</tabstop> + <tabstop>packDescription</tabstop> + <tabstop>sortByBox</tabstop> + <tabstop>versionSelectionBox</tabstop> + </tabstops> + <resources/> + <connections/> </ui> |