From 36571c5e2237c98e194cff326480ebe3e661c586 Mon Sep 17 00:00:00 2001 From: flow Date: Tue, 20 Dec 2022 12:15:17 -0300 Subject: [PATCH] refactor(RD): clear up sorting methods This refactors the sorting methods to join every bit of it into a single list, easing maintanance. It also removes the weird index contraint on the list of methods by adding an index field to the DS that holds the method. Lastly, it puts the available methods on their respective API, so other resources on the same API can re-use them later on. Signed-off-by: flow --- launcher/modplatform/ResourceAPI.h | 17 +++++++++- launcher/modplatform/flame/FlameAPI.cpp | 15 +++++++++ launcher/modplatform/flame/FlameAPI.h | 17 ++-------- launcher/modplatform/modrinth/ModrinthAPI.cpp | 12 +++++++ launcher/modplatform/modrinth/ModrinthAPI.h | 4 ++- launcher/ui/pages/modplatform/ModModel.cpp | 33 ++++++++++++------- launcher/ui/pages/modplatform/ModModel.h | 5 +-- launcher/ui/pages/modplatform/ModPage.cpp | 2 +- launcher/ui/pages/modplatform/ResourceModel.h | 5 +++ .../ui/pages/modplatform/ResourcePage.cpp | 11 +++++++ launcher/ui/pages/modplatform/ResourcePage.h | 4 +-- .../modplatform/flame/FlameResourceModels.cpp | 3 -- .../modplatform/flame/FlameResourceModels.h | 4 --- .../modplatform/flame/FlameResourcePages.cpp | 8 +---- .../modrinth/ModrinthResourceModels.cpp | 3 -- .../modrinth/ModrinthResourceModels.h | 4 --- .../modrinth/ModrinthResourcePages.cpp | 7 +--- 17 files changed, 93 insertions(+), 61 deletions(-) diff --git a/launcher/modplatform/ResourceAPI.h b/launcher/modplatform/ResourceAPI.h index 78441c34..a2078b94 100644 --- a/launcher/modplatform/ResourceAPI.h +++ b/launcher/modplatform/ResourceAPI.h @@ -54,12 +54,23 @@ class ResourceAPI { enum ModLoaderType { Forge = 1 << 0, Cauldron = 1 << 1, LiteLoader = 1 << 2, Fabric = 1 << 3, Quilt = 1 << 4 }; Q_DECLARE_FLAGS(ModLoaderTypes, ModLoaderType) + struct SortingMethod { + // The index of the sorting method. Used to allow for arbitrary ordering in the list of methods. + // Used by Flame in the API request. + unsigned int index; + // The real name of the sorting, as used in the respective API specification. + // Used by Modrinth in the API request. + QString name; + // The human-readable name of the sorting, used for display in the UI. + QString readable_name; + }; + struct SearchArgs { ModPlatform::ResourceType type{}; int offset = 0; std::optional search; - std::optional sorting; + std::optional sorting; std::optional loaders; std::optional > versions; }; @@ -95,6 +106,10 @@ class ResourceAPI { std::function on_succeed; }; + public: + /** Gets a list of available sorting methods for this API. */ + [[nodiscard]] virtual auto getSortingMethods() const -> QList = 0; + public slots: [[nodiscard]] virtual NetJob::Ptr searchProjects(SearchArgs&&, SearchCallbacks&&) const { diff --git a/launcher/modplatform/flame/FlameAPI.cpp b/launcher/modplatform/flame/FlameAPI.cpp index 89249c41..32729a14 100644 --- a/launcher/modplatform/flame/FlameAPI.cpp +++ b/launcher/modplatform/flame/FlameAPI.cpp @@ -212,3 +212,18 @@ NetJob::Ptr FlameAPI::getFiles(const QStringList& fileIds, QByteArray* response) return netJob; } + +// https://docs.curseforge.com/?python#tocS_ModsSearchSortField +static QList s_sorts = { { 1, "Featured", QObject::tr("Sort by Featured") }, + { 2, "Popularity", QObject::tr("Sort by Popularity") }, + { 3, "LastUpdated", QObject::tr("Sort by Last Updated") }, + { 4, "Name", QObject::tr("Sort by Name") }, + { 5, "Author", QObject::tr("Sort by Author") }, + { 6, "TotalDownloads", QObject::tr("Sort by Downloads") }, + { 7, "Category", QObject::tr("Sort by Category") }, + { 8, "GameVersion", QObject::tr("Sort by Game Version") } }; + +QList FlameAPI::getSortingMethods() const +{ + return s_sorts; +} diff --git a/launcher/modplatform/flame/FlameAPI.h b/launcher/modplatform/flame/FlameAPI.h index f3cc0bbf..2b288564 100644 --- a/launcher/modplatform/flame/FlameAPI.h +++ b/launcher/modplatform/flame/FlameAPI.h @@ -14,20 +14,9 @@ class FlameAPI : public NetworkResourceAPI { NetJob::Ptr matchFingerprints(const QList& fingerprints, QByteArray* response); NetJob::Ptr getFiles(const QStringList& fileIds, QByteArray* response) const; - private: - static int getSortFieldInt(QString const& sortString) - { - return sortString == "Featured" ? 1 - : sortString == "Popularity" ? 2 - : sortString == "LastUpdated" ? 3 - : sortString == "Name" ? 4 - : sortString == "Author" ? 5 - : sortString == "TotalDownloads" ? 6 - : sortString == "Category" ? 7 - : sortString == "GameVersion" ? 8 - : 1; - } + [[nodiscard]] auto getSortingMethods() const -> QList override; + private: static int getClassId(ModPlatform::ResourceType type) { switch (type) { @@ -62,7 +51,7 @@ class FlameAPI : public NetworkResourceAPI { if (args.search.has_value()) get_arguments.append(QString("searchFilter=%1").arg(args.search.value())); if (args.sorting.has_value()) - get_arguments.append(QString("sortField=%1").arg(getSortFieldInt(args.sorting.value()))); + get_arguments.append(QString("sortField=%1").arg(args.sorting.value().index)); get_arguments.append("sortOrder=desc"); if (args.loaders.has_value()) get_arguments.append(QString("modLoaderType=%1").arg(getMappedModLoader(args.loaders.value()))); diff --git a/launcher/modplatform/modrinth/ModrinthAPI.cpp b/launcher/modplatform/modrinth/ModrinthAPI.cpp index 8e64be09..8d7e3acf 100644 --- a/launcher/modplatform/modrinth/ModrinthAPI.cpp +++ b/launcher/modplatform/modrinth/ModrinthAPI.cpp @@ -112,3 +112,15 @@ NetJob::Ptr ModrinthAPI::getProjects(QStringList addonIds, QByteArray* response) return netJob; } + +// https://docs.modrinth.com/api-spec/#tag/projects/operation/searchProjects +static QList s_sorts = { { 1, "relevance", QObject::tr("Sort by Relevance") }, + { 2, "downloads", QObject::tr("Sort by Downloads") }, + { 3, "follows", QObject::tr("Sort by Follows") }, + { 4, "newest", QObject::tr("Sort by Last Updated") }, + { 5, "updated", QObject::tr("Sort by Newest") } }; + +QList ModrinthAPI::getSortingMethods() const +{ + return s_sorts; +} diff --git a/launcher/modplatform/modrinth/ModrinthAPI.h b/launcher/modplatform/modrinth/ModrinthAPI.h index ec38d9ee..949fc46e 100644 --- a/launcher/modplatform/modrinth/ModrinthAPI.h +++ b/launcher/modplatform/modrinth/ModrinthAPI.h @@ -49,6 +49,8 @@ class ModrinthAPI : public NetworkResourceAPI { NetJob::Ptr getProjects(QStringList addonIds, QByteArray* response) const override; public: + [[nodiscard]] auto getSortingMethods() const -> QList override; + inline auto getAuthorURL(const QString& name) const -> QString { return "https://modrinth.com/user/" + name; }; static auto getModLoaderStrings(const ModLoaderTypes types) -> const QStringList @@ -116,7 +118,7 @@ class ModrinthAPI : public NetworkResourceAPI { if (args.search.has_value()) get_arguments.append(QString("query=%1").arg(args.search.value())); if (args.sorting.has_value()) - get_arguments.append(QString("index=%1").arg(args.sorting.value())); + get_arguments.append(QString("index=%1").arg(args.sorting.value().name)); get_arguments.append(QString("facets=%1").arg(createFacets(args))); return BuildConfig.MODRINTH_PROD_URL + "/search?" + get_arguments.join('&'); diff --git a/launcher/ui/pages/modplatform/ModModel.cpp b/launcher/ui/pages/modplatform/ModModel.cpp index c9dee449..5eeac5d5 100644 --- a/launcher/ui/pages/modplatform/ModModel.cpp +++ b/launcher/ui/pages/modplatform/ModModel.cpp @@ -20,12 +20,23 @@ ResourceAPI::SearchArgs ModModel::createSearchArguments() Q_ASSERT(profile); Q_ASSERT(m_filter); - std::optional> versions {}; - if (!m_filter->versions.empty()) - versions = m_filter->versions; + std::optional> versions{}; + std::optional sort{}; - return { ModPlatform::ResourceType::MOD, m_next_search_offset, m_search_term, - getSorts()[currentSort], profile->getModLoaders(), versions }; + { // Version filter + if (!m_filter->versions.empty()) + versions = m_filter->versions; + } + + { // Sorting method + auto sorting_methods = getSortingMethods(); + auto method = std::find_if(sorting_methods.begin(), sorting_methods.end(), + [this](auto const& e) { return m_current_sort_index == e.index; }); + if (method != sorting_methods.end()) + sort = *method; + } + + return { ModPlatform::ResourceType::MOD, m_next_search_offset, m_search_term, sort, profile->getModLoaders(), versions }; } ResourceAPI::SearchCallbacks ModModel::createSearchCallbacks() { @@ -44,8 +55,8 @@ ResourceAPI::VersionSearchArgs ModModel::createVersionsArguments(QModelIndex& en Q_ASSERT(profile); Q_ASSERT(m_filter); - std::optional> versions {}; - if (!m_filter->versions.empty()) + std::optional> versions{}; + if (!m_filter->versions.empty()) versions = m_filter->versions; return { pack, versions, profile->getModLoaders() }; @@ -73,14 +84,14 @@ ResourceAPI::ProjectInfoCallbacks ModModel::createInfoCallbacks(QModelIndex& ent } }; } -void ModModel::searchWithTerm(const QString& term, const int sort, const bool filter_changed) +void ModModel::searchWithTerm(const QString& term, unsigned int sort, bool filter_changed) { - if (m_search_term == term && m_search_term.isNull() == term.isNull() && currentSort == sort && !filter_changed) { + if (m_search_term == term && m_search_term.isNull() == term.isNull() && m_current_sort_index == sort && !filter_changed) { return; } setSearchTerm(term); - currentSort = sort; + m_current_sort_index = sort; refresh(); } @@ -142,7 +153,7 @@ void ModModel::infoRequestFinished(QJsonDocument& doc, ModPlatform::IndexedPack& qWarning() << "Failed to cache mod info!"; return; } - + emit projectInfoUpdated(); } } diff --git a/launcher/ui/pages/modplatform/ModModel.h b/launcher/ui/pages/modplatform/ModModel.h index 39d062f9..3aeba3ef 100644 --- a/launcher/ui/pages/modplatform/ModModel.h +++ b/launcher/ui/pages/modplatform/ModModel.h @@ -21,7 +21,7 @@ class ModModel : public ResourceModel { ModModel(const BaseInstance&, ResourceAPI* api); /* Ask the API for more information */ - void searchWithTerm(const QString& term, const int sort, const bool filter_changed); + void searchWithTerm(const QString& term, unsigned int sort, bool filter_changed); virtual void loadIndexedPack(ModPlatform::IndexedPack& m, QJsonObject& obj) = 0; virtual void loadExtraPackInfo(ModPlatform::IndexedPack& m, QJsonObject& obj) = 0; @@ -46,11 +46,8 @@ class ModModel : public ResourceModel { protected: virtual auto documentToArray(QJsonDocument& obj) const -> QJsonArray = 0; - virtual auto getSorts() const -> const char** = 0; protected: - int currentSort = 0; - std::shared_ptr m_filter = nullptr; }; diff --git a/launcher/ui/pages/modplatform/ModPage.cpp b/launcher/ui/pages/modplatform/ModPage.cpp index 556bd642..04cbddcb 100644 --- a/launcher/ui/pages/modplatform/ModPage.cpp +++ b/launcher/ui/pages/modplatform/ModPage.cpp @@ -100,7 +100,7 @@ void ModPage::triggerSearch() updateSelectionButton(); } - static_cast(m_model)->searchWithTerm(getSearchTerm(), m_ui->sortByBox->currentIndex(), changed); + static_cast(m_model)->searchWithTerm(getSearchTerm(), m_ui->sortByBox->currentData().toUInt(), changed); m_fetch_progress.watch(&m_model->activeJob()); } diff --git a/launcher/ui/pages/modplatform/ResourceModel.h b/launcher/ui/pages/modplatform/ResourceModel.h index d0b9234b..facff91d 100644 --- a/launcher/ui/pages/modplatform/ResourceModel.h +++ b/launcher/ui/pages/modplatform/ResourceModel.h @@ -6,7 +6,9 @@ #include "QObjectPtr.h" #include "BaseInstance.h" + #include "modplatform/ResourceAPI.h" + #include "tasks/ConcurrentTask.h" class NetJob; @@ -41,6 +43,8 @@ class ResourceModel : public QAbstractListModel { inline void addActiveJob(Task::Ptr ptr) { m_current_job.addTask(ptr); if (!m_current_job.isRunning()) m_current_job.start(); } inline Task const& activeJob() { return m_current_job; } + [[nodiscard]] auto getSortingMethods() const { return m_api->getSortingMethods(); } + public slots: void fetchMore(const QModelIndex& parent) override; // NOTE: Can't use [[nodiscard]] here because of https://bugreports.qt.io/browse/QTBUG-58628 on Qt 5.12 @@ -83,6 +87,7 @@ class ResourceModel : public QAbstractListModel { enum class SearchState { None, CanFetchMore, ResetRequested, Finished } m_search_state = SearchState::None; int m_next_search_offset = 0; QString m_search_term; + unsigned int m_current_sort_index = 0; std::unique_ptr m_api; diff --git a/launcher/ui/pages/modplatform/ResourcePage.cpp b/launcher/ui/pages/modplatform/ResourcePage.cpp index 6e6868c5..43b77207 100644 --- a/launcher/ui/pages/modplatform/ResourcePage.cpp +++ b/launcher/ui/pages/modplatform/ResourcePage.cpp @@ -103,6 +103,17 @@ void ResourcePage::setSearchTerm(QString term) m_ui->searchEdit->setText(term); } +void ResourcePage::addSortings() +{ + Q_ASSERT(m_model); + + auto sorts = m_model->getSortingMethods(); + std::sort(sorts.begin(), sorts.end(), [](auto const& l, auto const& r) { return l.index < r.index; }); + + for (auto&& sorting : sorts) + m_ui->sortByBox->addItem(sorting.readable_name, QVariant(sorting.index)); +} + bool ResourcePage::setCurrentPack(ModPlatform::IndexedPack pack) { QVariant v; diff --git a/launcher/ui/pages/modplatform/ResourcePage.h b/launcher/ui/pages/modplatform/ResourcePage.h index b95c5a40..547c4056 100644 --- a/launcher/ui/pages/modplatform/ResourcePage.h +++ b/launcher/ui/pages/modplatform/ResourcePage.h @@ -52,14 +52,14 @@ class ResourcePage : public QWidget, public BasePage { [[nodiscard]] bool setCurrentPack(ModPlatform::IndexedPack); [[nodiscard]] auto getCurrentPack() const -> ModPlatform::IndexedPack; - [[nodiscard]] auto getDialog() const -> const ResourceDownloadDialog* { return m_parent_dialog; } - [[nodiscard]] auto getModel() const -> ResourceModel* { return m_model; } protected: ResourcePage(ResourceDownloadDialog* parent, BaseInstance&); + void addSortings(); + public slots: virtual void updateUi(); virtual void updateSelectionButton(); diff --git a/launcher/ui/pages/modplatform/flame/FlameResourceModels.cpp b/launcher/ui/pages/modplatform/flame/FlameResourceModels.cpp index d0f109de..a1cd1f26 100644 --- a/launcher/ui/pages/modplatform/flame/FlameResourceModels.cpp +++ b/launcher/ui/pages/modplatform/flame/FlameResourceModels.cpp @@ -7,9 +7,6 @@ namespace ResourceDownload { -// NOLINTNEXTLINE(modernize-avoid-c-arrays) -const char* FlameModModel::sorts[6]{ "Featured", "Popularity", "LastUpdated", "Name", "Author", "TotalDownloads" }; - FlameModModel::FlameModModel(BaseInstance const& base) : ModModel(base, new FlameAPI) {} void FlameModModel::loadIndexedPack(ModPlatform::IndexedPack& m, QJsonObject& obj) diff --git a/launcher/ui/pages/modplatform/flame/FlameResourceModels.h b/launcher/ui/pages/modplatform/flame/FlameResourceModels.h index 7b253dce..47fbbe1a 100644 --- a/launcher/ui/pages/modplatform/flame/FlameResourceModels.h +++ b/launcher/ui/pages/modplatform/flame/FlameResourceModels.h @@ -21,10 +21,6 @@ class FlameModModel : public ModModel { void loadIndexedPackVersions(ModPlatform::IndexedPack& m, QJsonArray& arr) override; auto documentToArray(QJsonDocument& obj) const -> QJsonArray override; - - // NOLINTNEXTLINE(modernize-avoid-c-arrays) - static const char* sorts[6]; - inline auto getSorts() const -> const char** override { return sorts; }; }; } // namespace ResourceDownload diff --git a/launcher/ui/pages/modplatform/flame/FlameResourcePages.cpp b/launcher/ui/pages/modplatform/flame/FlameResourcePages.cpp index 67737a76..e34be7fd 100644 --- a/launcher/ui/pages/modplatform/flame/FlameResourcePages.cpp +++ b/launcher/ui/pages/modplatform/flame/FlameResourcePages.cpp @@ -48,13 +48,7 @@ FlameModPage::FlameModPage(ModDownloadDialog* dialog, BaseInstance& instance) m_model = new FlameModModel(instance); m_ui->packView->setModel(m_model); - // index is used to set the sorting with the flame api - m_ui->sortByBox->addItem(tr("Sort by Featured")); - m_ui->sortByBox->addItem(tr("Sort by Popularity")); - m_ui->sortByBox->addItem(tr("Sort by Last Updated")); - m_ui->sortByBox->addItem(tr("Sort by Name")); - m_ui->sortByBox->addItem(tr("Sort by Author")); - m_ui->sortByBox->addItem(tr("Sort by Downloads")); + addSortings(); // sometimes Qt just ignores virtual slots and doesn't work as intended it seems, // so it's best not to connect them in the parent's contructor... diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthResourceModels.cpp b/launcher/ui/pages/modplatform/modrinth/ModrinthResourceModels.cpp index 895e23fd..06b72fd0 100644 --- a/launcher/ui/pages/modplatform/modrinth/ModrinthResourceModels.cpp +++ b/launcher/ui/pages/modplatform/modrinth/ModrinthResourceModels.cpp @@ -23,9 +23,6 @@ namespace ResourceDownload { -// NOLINTNEXTLINE(modernize-avoid-c-arrays) -const char* ModrinthModModel::sorts[5]{ "relevance", "downloads", "follows", "updated", "newest" }; - ModrinthModModel::ModrinthModModel(BaseInstance const& base) : ModModel(base, new ModrinthAPI) {} void ModrinthModModel::loadIndexedPack(ModPlatform::IndexedPack& m, QJsonObject& obj) diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthResourceModels.h b/launcher/ui/pages/modplatform/modrinth/ModrinthResourceModels.h index 798a70e6..2511f5e5 100644 --- a/launcher/ui/pages/modplatform/modrinth/ModrinthResourceModels.h +++ b/launcher/ui/pages/modplatform/modrinth/ModrinthResourceModels.h @@ -41,10 +41,6 @@ class ModrinthModModel : public ModModel { void loadIndexedPackVersions(ModPlatform::IndexedPack& m, QJsonArray& arr) override; auto documentToArray(QJsonDocument& obj) const -> QJsonArray override; - - // NOLINTNEXTLINE(modernize-avoid-c-arrays) - static const char* sorts[5]; - inline auto getSorts() const -> const char** override { return sorts; }; }; } // namespace ResourceDownload diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthResourcePages.cpp b/launcher/ui/pages/modplatform/modrinth/ModrinthResourcePages.cpp index 88621e05..45902d16 100644 --- a/launcher/ui/pages/modplatform/modrinth/ModrinthResourcePages.cpp +++ b/launcher/ui/pages/modplatform/modrinth/ModrinthResourcePages.cpp @@ -50,12 +50,7 @@ ModrinthModPage::ModrinthModPage(ModDownloadDialog* dialog, BaseInstance& instan m_model = new ModrinthModModel(instance); m_ui->packView->setModel(m_model); - // index is used to set the sorting with the modrinth api - m_ui->sortByBox->addItem(tr("Sort by Relevance")); - m_ui->sortByBox->addItem(tr("Sort by Downloads")); - m_ui->sortByBox->addItem(tr("Sort by Follows")); - m_ui->sortByBox->addItem(tr("Sort by Last Updated")); - m_ui->sortByBox->addItem(tr("Sort by Newest")); + addSortings(); // sometimes Qt just ignores virtual slots and doesn't work as intended it seems, // so it's best not to connect them in the parent's constructor...