refactor(RD): decouple ResourceModels from ResourcePages
This makes it so that we don't need a reference to the parent page in the model. It will be useful once we change the page from a widget-based one to a QML page. It also makes tasks be created in the dialog instead of the page, so that the dialog can also have the necessary information to mark versions as selected / deselected easily. It also makes the task pointers into smart pointers. Signed-off-by: flow <flowlnlnln@gmail.com>
This commit is contained in:
		| @@ -32,6 +32,7 @@ class ResourceDownloadTask : public SequentialTask { | ||||
| public: | ||||
|     explicit ResourceDownloadTask(ModPlatform::IndexedPack pack, ModPlatform::IndexedVersion version, const std::shared_ptr<ResourceFolderModel> packs, bool is_indexed = true); | ||||
|     const QString& getFilename() const { return m_pack_version.fileName; } | ||||
|     const QVariant& getVersionID() const { return m_pack_version.fileId; } | ||||
|  | ||||
| private: | ||||
|     ModPlatform::IndexedPack m_pack; | ||||
|   | ||||
| @@ -65,6 +65,9 @@ struct IndexedVersion { | ||||
|     QString hash; | ||||
|     bool is_preferred = true; | ||||
|     QString changelog; | ||||
|  | ||||
|     // For internal use, not provided by APIs | ||||
|     bool is_currently_selected = false; | ||||
| }; | ||||
|  | ||||
| struct ExtraPackData { | ||||
| @@ -95,6 +98,23 @@ struct IndexedPack { | ||||
|     // Don't load by default, since some modplatform don't have that info | ||||
|     bool extraDataLoaded = true; | ||||
|     ExtraPackData extraData; | ||||
|  | ||||
|     // For internal use, not provided by APIs | ||||
|     [[nodiscard]] bool isVersionSelected(size_t index) const | ||||
|     { | ||||
|         if (!versionsLoaded) | ||||
|             return false; | ||||
|  | ||||
|         return versions.at(index).is_currently_selected; | ||||
|     } | ||||
|     [[nodiscard]] bool isAnyVersionSelected() const | ||||
|     { | ||||
|         if (!versionsLoaded) | ||||
|             return false; | ||||
|  | ||||
|         return std::any_of(versions.constBegin(), versions.constEnd(), | ||||
|                 [](auto const& v) { return v.is_currently_selected; }); | ||||
|     } | ||||
| }; | ||||
|  | ||||
| }  // namespace ModPlatform | ||||
|   | ||||
| @@ -69,13 +69,20 @@ class ResourceAPI { | ||||
|     }; | ||||
|  | ||||
|     struct VersionSearchArgs { | ||||
|         QString addonId; | ||||
|         ModPlatform::IndexedPack& pack; | ||||
|  | ||||
|         std::optional<std::list<Version> > mcVersions; | ||||
|         std::optional<ModLoaderTypes> loaders; | ||||
|  | ||||
|         void operator=(VersionSearchArgs other) | ||||
|         { | ||||
|             pack = other.pack; | ||||
|             mcVersions = other.mcVersions; | ||||
|             loaders = other.loaders; | ||||
|         } | ||||
|     }; | ||||
|     struct VersionSearchCallbacks { | ||||
|         std::function<void(QJsonDocument&, QString)> on_succeed; | ||||
|         std::function<void(QJsonDocument&, ModPlatform::IndexedPack&)> on_succeed; | ||||
|     }; | ||||
|  | ||||
|     struct ProjectInfoArgs { | ||||
|   | ||||
| @@ -114,7 +114,7 @@ auto FlameAPI::getLatestVersion(VersionSearchArgs&& args) -> ModPlatform::Indexe | ||||
|  | ||||
|     QEventLoop loop; | ||||
|  | ||||
|     auto netJob = new NetJob(QString("Flame::GetLatestVersion(%1)").arg(args.addonId), APPLICATION->network()); | ||||
|     auto netJob = new NetJob(QString("Flame::GetLatestVersion(%1)").arg(args.pack.name), APPLICATION->network()); | ||||
|     auto response = new QByteArray(); | ||||
|     ModPlatform::IndexedVersion ver; | ||||
|  | ||||
|   | ||||
| @@ -78,7 +78,7 @@ class FlameAPI : public NetworkResourceAPI { | ||||
|  | ||||
|     [[nodiscard]] std::optional<QString> getVersionsURL(VersionSearchArgs const& args) const override | ||||
|     { | ||||
|         QString url{QString("https://api.curseforge.com/v1/mods/%1/files?pageSize=10000&").arg(args.addonId)}; | ||||
|         QString url{QString("https://api.curseforge.com/v1/mods/%1/files?pageSize=10000&").arg(args.pack.addonId.toString())}; | ||||
|  | ||||
|         QStringList get_parameters; | ||||
|         if (args.mcVersions.has_value()) | ||||
|   | ||||
| @@ -129,7 +129,8 @@ void FlameCheckUpdate::executeTask() | ||||
|         setStatus(tr("Getting API response from CurseForge for '%1'...").arg(mod->name())); | ||||
|         setProgress(i++, m_mods.size()); | ||||
|  | ||||
|         auto latest_ver = api.getLatestVersion({ mod->metadata()->project_id.toString(), m_game_versions, m_loaders }); | ||||
|         ModPlatform::IndexedPack pack{ mod->metadata()->project_id.toString() }; | ||||
|         auto latest_ver = api.getLatestVersion({ pack, m_game_versions, m_loaders }); | ||||
|  | ||||
|         // Check if we were aborted while getting the latest version | ||||
|         if (m_was_aborted) { | ||||
|   | ||||
| @@ -76,10 +76,10 @@ static QString enumToString(int hash_algorithm) | ||||
| void FlameMod::loadIndexedPackVersions(ModPlatform::IndexedPack& pack, | ||||
|                                        QJsonArray& arr, | ||||
|                                        const shared_qobject_ptr<QNetworkAccessManager>& network, | ||||
|                                        BaseInstance* inst) | ||||
|                                        const BaseInstance* inst) | ||||
| { | ||||
|     QVector<ModPlatform::IndexedVersion> unsortedVersions; | ||||
|     auto profile = (dynamic_cast<MinecraftInstance*>(inst))->getPackProfile(); | ||||
|     auto profile = (dynamic_cast<const MinecraftInstance*>(inst))->getPackProfile(); | ||||
|     QString mcVersion = profile->getComponentVersion("net.minecraft"); | ||||
|  | ||||
|     for (auto versionIter : arr) { | ||||
|   | ||||
| @@ -17,7 +17,7 @@ void loadBody(ModPlatform::IndexedPack& m, QJsonObject& obj); | ||||
| void loadIndexedPackVersions(ModPlatform::IndexedPack& pack, | ||||
|                              QJsonArray& arr, | ||||
|                              const shared_qobject_ptr<QNetworkAccessManager>& network, | ||||
|                              BaseInstance* inst); | ||||
|                              const BaseInstance* inst); | ||||
| auto loadIndexedPackVersion(QJsonObject& obj, bool load_changelog = false) -> ModPlatform::IndexedVersion; | ||||
|  | ||||
| }  // namespace FlameMod | ||||
|   | ||||
| @@ -79,7 +79,7 @@ NetJob::Ptr NetworkResourceAPI::getProjectVersions(VersionSearchArgs&& args, Ver | ||||
|  | ||||
|     auto versions_url = versions_url_optional.value(); | ||||
|  | ||||
|     auto netJob = new NetJob(QString("%1::Versions").arg(args.addonId), APPLICATION->network()); | ||||
|     auto netJob = new NetJob(QString("%1::Versions").arg(args.pack.name), APPLICATION->network()); | ||||
|     auto response = new QByteArray(); | ||||
|  | ||||
|     netJob->addNetAction(Net::Download::makeByteArray(versions_url, response)); | ||||
| @@ -94,7 +94,7 @@ NetJob::Ptr NetworkResourceAPI::getProjectVersions(VersionSearchArgs&& args, Ver | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         callbacks.on_succeed(doc, args.addonId); | ||||
|         callbacks.on_succeed(doc, args.pack); | ||||
|     }); | ||||
|  | ||||
|     QObject::connect(netJob, &NetJob::finished, [response] { | ||||
|   | ||||
| @@ -141,7 +141,7 @@ class ModrinthAPI : public NetworkResourceAPI { | ||||
|             get_arguments.append(QString("loaders=[\"%1\"]").arg(getModLoaderStrings(args.loaders.value()).join("\",\""))); | ||||
|  | ||||
|         return QString("%1/project/%2/version%3%4") | ||||
|             .arg(BuildConfig.MODRINTH_PROD_URL, args.addonId, get_arguments.isEmpty() ? "" : "?", get_arguments.join('&')); | ||||
|             .arg(BuildConfig.MODRINTH_PROD_URL, args.pack.addonId.toString(), get_arguments.isEmpty() ? "" : "?", get_arguments.join('&')); | ||||
|     }; | ||||
|  | ||||
|     auto getGameVersionsArray(std::list<Version> mcVersions) const -> QString | ||||
|   | ||||
| @@ -95,10 +95,10 @@ void Modrinth::loadExtraPackData(ModPlatform::IndexedPack& pack, QJsonObject& ob | ||||
| void Modrinth::loadIndexedPackVersions(ModPlatform::IndexedPack& pack, | ||||
|                                        QJsonArray& arr, | ||||
|                                        const shared_qobject_ptr<QNetworkAccessManager>& network, | ||||
|                                        BaseInstance* inst) | ||||
|                                        const BaseInstance* inst) | ||||
| { | ||||
|     QVector<ModPlatform::IndexedVersion> unsortedVersions; | ||||
|     QString mcVersion = (static_cast<MinecraftInstance*>(inst))->getPackProfile()->getComponentVersion("net.minecraft"); | ||||
|     QString mcVersion = (static_cast<const MinecraftInstance*>(inst))->getPackProfile()->getComponentVersion("net.minecraft"); | ||||
|  | ||||
|     for (auto versionIter : arr) { | ||||
|         auto obj = versionIter.toObject(); | ||||
|   | ||||
| @@ -29,7 +29,7 @@ void loadExtraPackData(ModPlatform::IndexedPack& m, QJsonObject& obj); | ||||
| void loadIndexedPackVersions(ModPlatform::IndexedPack& pack, | ||||
|                              QJsonArray& arr, | ||||
|                              const shared_qobject_ptr<QNetworkAccessManager>& network, | ||||
|                              BaseInstance* inst); | ||||
|                              const BaseInstance* inst); | ||||
| auto loadIndexedPackVersion(QJsonObject& obj, QString hash_type = "sha512", QString filename_prefer = "") -> ModPlatform::IndexedVersion; | ||||
|  | ||||
| }  // namespace Modrinth | ||||
|   | ||||
| @@ -141,38 +141,44 @@ ResourcePage* ResourceDownloadDialog::getSelectedPage() | ||||
|     return m_selectedPage; | ||||
| } | ||||
|  | ||||
| void ResourceDownloadDialog::addResource(QString name, ResourceDownloadTask* task) | ||||
| void ResourceDownloadDialog::addResource(ModPlatform::IndexedPack& pack, ModPlatform::IndexedVersion& ver, bool is_indexed) | ||||
| { | ||||
|     removeResource(name); | ||||
|     m_selected.insert(name, task); | ||||
|     removeResource(pack, ver); | ||||
|  | ||||
|     ver.is_currently_selected = true; | ||||
|     m_selected.insert(pack.name, new ResourceDownloadTask(pack, ver, getBaseModel(), is_indexed)); | ||||
|  | ||||
|     m_buttons.button(QDialogButtonBox::Ok)->setEnabled(!m_selected.isEmpty()); | ||||
| } | ||||
|  | ||||
| void ResourceDownloadDialog::removeResource(QString name) | ||||
| static ModPlatform::IndexedVersion& getVersionWithID(ModPlatform::IndexedPack& pack, QVariant id) | ||||
| { | ||||
|     if (m_selected.contains(name)) | ||||
|         m_selected.find(name).value()->deleteLater(); | ||||
|     m_selected.remove(name); | ||||
|     Q_ASSERT(pack.versionsLoaded); | ||||
|     auto it = std::find_if(pack.versions.begin(), pack.versions.end(), [id](auto const& v) { return v.fileId == id; }); | ||||
|     Q_ASSERT(it != pack.versions.end()); | ||||
|     return *it; | ||||
| } | ||||
|  | ||||
| void ResourceDownloadDialog::removeResource(ModPlatform::IndexedPack& pack, ModPlatform::IndexedVersion& ver) | ||||
| { | ||||
|     if (auto selected_task_it = m_selected.find(pack.name); selected_task_it != m_selected.end()) { | ||||
|         auto selected_task = *selected_task_it; | ||||
|         auto old_version_id = selected_task->getVersionID(); | ||||
|  | ||||
|         // If the new and old version IDs don't match, search for the old one and deselect it. | ||||
|         if (ver.fileId != old_version_id) | ||||
|             getVersionWithID(pack, old_version_id).is_currently_selected = false; | ||||
|     } | ||||
|  | ||||
|     // Deselect the new version too, since all versions of that pack got removed. | ||||
|     ver.is_currently_selected = false; | ||||
|  | ||||
|     m_selected.remove(pack.name); | ||||
|  | ||||
|     m_buttons.button(QDialogButtonBox::Ok)->setEnabled(!m_selected.isEmpty()); | ||||
| } | ||||
|  | ||||
| bool ResourceDownloadDialog::isSelected(QString name, QString filename) const | ||||
| { | ||||
|     auto iter = m_selected.constFind(name); | ||||
|     if (iter == m_selected.constEnd()) | ||||
|         return false; | ||||
|  | ||||
|     // FIXME: Is there a way to check for versions without checking the filename | ||||
|     //        as a heuristic, other than adding such info to ResourceDownloadTask itself? | ||||
|     if (!filename.isEmpty()) | ||||
|         return iter.value()->getFilename() == filename; | ||||
|  | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| const QList<ResourceDownloadTask*> ResourceDownloadDialog::getTasks() | ||||
| const QList<ResourceDownloadDialog::DownloadTaskPtr> ResourceDownloadDialog::getTasks() | ||||
| { | ||||
|     return m_selected.values(); | ||||
| } | ||||
|   | ||||
| @@ -23,6 +23,8 @@ | ||||
| #include <QDialogButtonBox> | ||||
| #include <QLayout> | ||||
|  | ||||
| #include "QObjectPtr.h" | ||||
| #include "modplatform/ModIndex.h" | ||||
| #include "ui/pages/BasePageProvider.h" | ||||
|  | ||||
| class BaseInstance; | ||||
| @@ -41,6 +43,8 @@ class ResourceDownloadDialog : public QDialog, public BasePageProvider { | ||||
|     Q_OBJECT | ||||
|  | ||||
|    public: | ||||
|     using DownloadTaskPtr = shared_qobject_ptr<ResourceDownloadTask>; | ||||
|  | ||||
|     ResourceDownloadDialog(QWidget* parent, const std::shared_ptr<ResourceFolderModel> base_model); | ||||
|  | ||||
|     void initializeContainer(); | ||||
| @@ -54,11 +58,10 @@ class ResourceDownloadDialog : public QDialog, public BasePageProvider { | ||||
|     bool selectPage(QString pageId); | ||||
|     ResourcePage* getSelectedPage(); | ||||
|  | ||||
|     void addResource(QString name, ResourceDownloadTask* task); | ||||
|     void removeResource(QString name); | ||||
|     [[nodiscard]] bool isSelected(QString name, QString filename = "") const; | ||||
|     void addResource(ModPlatform::IndexedPack&, ModPlatform::IndexedVersion&, bool is_indexed = false); | ||||
|     void removeResource(ModPlatform::IndexedPack&, ModPlatform::IndexedVersion&); | ||||
|  | ||||
|     const QList<ResourceDownloadTask*> getTasks(); | ||||
|     const QList<DownloadTaskPtr> getTasks(); | ||||
|     [[nodiscard]] const std::shared_ptr<ResourceFolderModel> getBaseModel() const { return m_base_model; } | ||||
|  | ||||
|    public slots: | ||||
| @@ -82,7 +85,7 @@ class ResourceDownloadDialog : public QDialog, public BasePageProvider { | ||||
|     QDialogButtonBox m_buttons; | ||||
|     QVBoxLayout m_vertical_layout; | ||||
|  | ||||
|     QHash<QString, ResourceDownloadTask*> m_selected; | ||||
|     QHash<QString, DownloadTaskPtr> m_selected; | ||||
| }; | ||||
|  | ||||
|  | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| #include "ModModel.h" | ||||
|  | ||||
| #include "Json.h" | ||||
| #include "ModPage.h" | ||||
|  | ||||
| #include "minecraft/MinecraftInstance.h" | ||||
| #include "minecraft/PackProfile.h" | ||||
|  | ||||
| @@ -9,15 +9,23 @@ | ||||
|  | ||||
| namespace ResourceDownload { | ||||
|  | ||||
| ModModel::ModModel(ModPage* parent, ResourceAPI* api) : ResourceModel(parent, api) {} | ||||
| ModModel::ModModel(BaseInstance const& base_inst, ResourceAPI* api) : ResourceModel(base_inst, api) {} | ||||
|  | ||||
| /******** Make data requests ********/ | ||||
|  | ||||
| ResourceAPI::SearchArgs ModModel::createSearchArguments() | ||||
| { | ||||
|     auto profile = static_cast<MinecraftInstance&>(m_associated_page->m_base_instance).getPackProfile(); | ||||
|     auto profile = static_cast<MinecraftInstance const&>(m_base_instance).getPackProfile(); | ||||
|  | ||||
|     Q_ASSERT(profile); | ||||
|     Q_ASSERT(m_filter); | ||||
|  | ||||
|     std::optional<std::list<Version>> versions {}; | ||||
|     if (!m_filter->versions.empty()) | ||||
|         versions = m_filter->versions; | ||||
|  | ||||
|     return { ModPlatform::ResourceType::MOD, m_next_search_offset,     m_search_term, | ||||
|              getSorts()[currentSort],        profile->getModLoaders(), getMineVersions() }; | ||||
|              getSorts()[currentSort],        profile->getModLoaders(), versions }; | ||||
| } | ||||
| ResourceAPI::SearchCallbacks ModModel::createSearchCallbacks() | ||||
| { | ||||
| @@ -30,19 +38,24 @@ ResourceAPI::SearchCallbacks ModModel::createSearchCallbacks() | ||||
|  | ||||
| ResourceAPI::VersionSearchArgs ModModel::createVersionsArguments(QModelIndex& entry) | ||||
| { | ||||
|     auto const& pack = m_packs[entry.row()]; | ||||
|     auto profile = static_cast<MinecraftInstance&>(m_associated_page->m_base_instance).getPackProfile(); | ||||
|     auto& pack = m_packs[entry.row()]; | ||||
|     auto profile = static_cast<MinecraftInstance const&>(m_base_instance).getPackProfile(); | ||||
|  | ||||
|     return { pack.addonId.toString(), getMineVersions(), profile->getModLoaders() }; | ||||
|     Q_ASSERT(profile); | ||||
|     Q_ASSERT(m_filter); | ||||
|  | ||||
|     std::optional<std::list<Version>> versions {}; | ||||
|     if (!m_filter->versions.empty())  | ||||
|         versions = m_filter->versions; | ||||
|  | ||||
|     return { pack, versions, profile->getModLoaders() }; | ||||
| } | ||||
| ResourceAPI::VersionSearchCallbacks ModModel::createVersionsCallbacks(QModelIndex& entry) | ||||
| { | ||||
|     auto const& pack = m_packs[entry.row()]; | ||||
|  | ||||
|     return { [this, pack, entry](auto& doc, auto addonId) { | ||||
|     return { [this, entry](auto& doc, auto& pack) { | ||||
|         if (!s_running_models.constFind(this).value()) | ||||
|             return; | ||||
|         versionRequestSucceeded(doc, addonId, entry); | ||||
|         versionRequestSucceeded(doc, pack, entry); | ||||
|     } }; | ||||
| } | ||||
|  | ||||
| @@ -87,7 +100,7 @@ void ModModel::searchRequestFinished(QJsonDocument& doc) | ||||
|             loadIndexedPack(pack, packObj); | ||||
|             newList.append(pack); | ||||
|         } catch (const JSONValidationError& e) { | ||||
|             qWarning() << "Error while loading mod from " << m_associated_page->debugName() << ": " << e.cause(); | ||||
|             qWarning() << "Error while loading mod from " << debugName() << ": " << e.cause(); | ||||
|             continue; | ||||
|         } | ||||
|     } | ||||
| @@ -127,48 +140,36 @@ void ModModel::infoRequestFinished(QJsonDocument& doc, ModPlatform::IndexedPack& | ||||
|         new_pack.setValue(pack); | ||||
|         if (!setData(index, new_pack, Qt::UserRole)) { | ||||
|             qWarning() << "Failed to cache mod info!"; | ||||
|             return; | ||||
|         } | ||||
|          | ||||
|         emit projectInfoUpdated(); | ||||
|     } | ||||
|  | ||||
|     m_associated_page->updateUi(); | ||||
| } | ||||
|  | ||||
| void ModModel::versionRequestSucceeded(QJsonDocument doc, QString addonId, const QModelIndex& index) | ||||
| void ModModel::versionRequestSucceeded(QJsonDocument doc, ModPlatform::IndexedPack& pack, const QModelIndex& index) | ||||
| { | ||||
|     auto current = m_associated_page->getCurrentPack(); | ||||
|     if (addonId != current.addonId) { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     auto arr = doc.isObject() ? Json::ensureArray(doc.object(), "data") : doc.array(); | ||||
|  | ||||
|     try { | ||||
|         loadIndexedPackVersions(current, arr); | ||||
|         loadIndexedPackVersions(pack, arr); | ||||
|     } catch (const JSONValidationError& e) { | ||||
|         qDebug() << doc; | ||||
|         qWarning() << "Error while reading " << debugName() << " mod version: " << e.cause(); | ||||
|     } | ||||
|  | ||||
|     // Cache info :^) | ||||
|     QVariant new_pack; | ||||
|     new_pack.setValue(current); | ||||
|     if (!setData(index, new_pack, Qt::UserRole)) { | ||||
|         qWarning() << "Failed to cache mod versions!"; | ||||
|     // Check if the index is still valid for this mod or not | ||||
|     if (pack.addonId == data(index, Qt::UserRole).value<ModPlatform::IndexedPack>().addonId) { | ||||
|         // Cache info :^) | ||||
|         QVariant new_pack; | ||||
|         new_pack.setValue(pack); | ||||
|         if (!setData(index, new_pack, Qt::UserRole)) { | ||||
|             qWarning() << "Failed to cache mod versions!"; | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         emit versionListUpdated(); | ||||
|     } | ||||
|  | ||||
|     m_associated_page->updateVersionList(); | ||||
| } | ||||
|  | ||||
| /******** Helpers ********/ | ||||
|  | ||||
| #define MOD_PAGE(x) static_cast<ModPage*>(x) | ||||
|  | ||||
| auto ModModel::getMineVersions() const -> std::optional<std::list<Version>> | ||||
| { | ||||
|     auto versions = MOD_PAGE(m_associated_page)->getFilter()->versions; | ||||
|     if (!versions.empty()) | ||||
|         return versions; | ||||
|     return {}; | ||||
| } | ||||
|  | ||||
| }  // namespace ResourceDownload | ||||
|   | ||||
| @@ -6,6 +6,7 @@ | ||||
| #include "modplatform/ResourceAPI.h" | ||||
|  | ||||
| #include "ui/pages/modplatform/ResourceModel.h" | ||||
| #include "ui/widgets/ModFilterWidget.h" | ||||
|  | ||||
| class Version; | ||||
|  | ||||
| @@ -17,7 +18,7 @@ class ModModel : public ResourceModel { | ||||
|     Q_OBJECT | ||||
|  | ||||
|    public: | ||||
|     ModModel(ModPage* parent, ResourceAPI* api); | ||||
|     ModModel(const BaseInstance&, ResourceAPI* api); | ||||
|  | ||||
|     /* Ask the API for more information */ | ||||
|     void searchWithTerm(const QString& term, const int sort, const bool filter_changed); | ||||
| @@ -26,12 +27,12 @@ class ModModel : public ResourceModel { | ||||
|     virtual void loadExtraPackInfo(ModPlatform::IndexedPack& m, QJsonObject& obj) = 0; | ||||
|     virtual void loadIndexedPackVersions(ModPlatform::IndexedPack& m, QJsonArray& arr) = 0; | ||||
|  | ||||
|     void setFilter(std::shared_ptr<ModFilterWidget::Filter> filter) { m_filter = filter; } | ||||
|  | ||||
|    public slots: | ||||
|     void searchRequestFinished(QJsonDocument& doc); | ||||
|  | ||||
|     void infoRequestFinished(QJsonDocument& doc, ModPlatform::IndexedPack& pack, const QModelIndex& index); | ||||
|  | ||||
|     void versionRequestSucceeded(QJsonDocument doc, QString addonId, const QModelIndex& index); | ||||
|     void versionRequestSucceeded(QJsonDocument doc, ModPlatform::IndexedPack& pack, const QModelIndex& index); | ||||
|  | ||||
|    public slots: | ||||
|     ResourceAPI::SearchArgs createSearchArguments() override; | ||||
| @@ -47,10 +48,10 @@ class ModModel : public ResourceModel { | ||||
|     virtual auto documentToArray(QJsonDocument& obj) const -> QJsonArray = 0; | ||||
|     virtual auto getSorts() const -> const char** = 0; | ||||
|  | ||||
|     inline auto getMineVersions() const -> std::optional<std::list<Version>>; | ||||
|  | ||||
|    protected: | ||||
|     int currentSort = 0; | ||||
|  | ||||
|     std::shared_ptr<ModFilterWidget::Filter> m_filter = nullptr; | ||||
| }; | ||||
|  | ||||
| }  // namespace ResourceDownload | ||||
|   | ||||
| @@ -51,8 +51,6 @@ | ||||
|  | ||||
| #include "ui/dialogs/ResourceDownloadDialog.h" | ||||
|  | ||||
| #include "ui/pages/modplatform/ModModel.h" | ||||
|  | ||||
| namespace ResourceDownload { | ||||
|  | ||||
| ModPage::ModPage(ModDownloadDialog* dialog, BaseInstance& instance) | ||||
| @@ -151,7 +149,7 @@ void ModPage::updateVersionList() | ||||
| void ModPage::addResourceToDialog(ModPlatform::IndexedPack& pack, ModPlatform::IndexedVersion& version) | ||||
| { | ||||
|     bool is_indexed = !APPLICATION->settings()->get("ModMetadataDisabled").toBool(); | ||||
|     m_parent_dialog->addResource(pack.name, new ResourceDownloadTask(pack, version, m_parent_dialog->getBaseModel(), is_indexed)); | ||||
|     m_parent_dialog->addResource(pack, version, is_indexed); | ||||
| } | ||||
|  | ||||
| }  // namespace ResourceDownload | ||||
|   | ||||
| @@ -5,6 +5,7 @@ | ||||
| #include "modplatform/ModIndex.h" | ||||
|  | ||||
| #include "ui/pages/modplatform/ResourcePage.h" | ||||
| #include "ui/pages/modplatform/ModModel.h" | ||||
| #include "ui/widgets/ModFilterWidget.h" | ||||
|  | ||||
| namespace Ui { | ||||
| @@ -24,9 +25,14 @@ class ModPage : public ResourcePage { | ||||
|     static T* create(ModDownloadDialog* dialog, BaseInstance& instance) | ||||
|     { | ||||
|         auto page = new T(dialog, instance); | ||||
|         auto model = static_cast<ModModel*>(page->getModel()); | ||||
|  | ||||
|         auto filter_widget = ModFilterWidget::create(static_cast<MinecraftInstance&>(instance).getPackProfile()->getComponentVersion("net.minecraft"), page); | ||||
|         page->setFilterWidget(filter_widget); | ||||
|         model->setFilter(page->getFilter()); | ||||
|  | ||||
|         connect(model, &ResourceModel::versionListUpdated, page, &ResourcePage::updateVersionList); | ||||
|         connect(model, &ResourceModel::projectInfoUpdated, page, &ResourcePage::updateUi); | ||||
|  | ||||
|         return page; | ||||
|     } | ||||
|   | ||||
| @@ -17,14 +17,13 @@ | ||||
|  | ||||
| #include "modplatform/ModIndex.h" | ||||
|  | ||||
| #include "ui/pages/modplatform/ResourcePage.h" | ||||
| #include "ui/widgets/ProjectItem.h" | ||||
|  | ||||
| namespace ResourceDownload { | ||||
|  | ||||
| QHash<ResourceModel*, bool> ResourceModel::s_running_models; | ||||
|  | ||||
| ResourceModel::ResourceModel(ResourcePage* parent, ResourceAPI* api) : QAbstractListModel(), m_api(api), m_associated_page(parent) | ||||
| ResourceModel::ResourceModel(BaseInstance const& base_inst, ResourceAPI* api) : QAbstractListModel(), m_base_instance(base_inst), m_api(api) | ||||
| { | ||||
|     s_running_models.insert(this, true); | ||||
| } | ||||
| @@ -72,7 +71,7 @@ auto ResourceModel::data(const QModelIndex& index, int role) const -> QVariant | ||||
|         case UserDataTypes::DESCRIPTION: | ||||
|             return pack.description; | ||||
|         case UserDataTypes::SELECTED: | ||||
|             return isPackSelected(pack); | ||||
|             return pack.isAnyVersionSelected(); | ||||
|         default: | ||||
|             break; | ||||
|     } | ||||
| @@ -87,13 +86,14 @@ bool ResourceModel::setData(const QModelIndex& index, const QVariant& value, int | ||||
|         return false; | ||||
|  | ||||
|     m_packs[pos] = value.value<ModPlatform::IndexedPack>(); | ||||
|     emit dataChanged(index, index); | ||||
|  | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| QString ResourceModel::debugName() const | ||||
| { | ||||
|     return m_associated_page->debugName() + " (Model)"; | ||||
|     return "ResourceDownload (Model)"; | ||||
| } | ||||
|  | ||||
| void ResourceModel::fetchMore(const QModelIndex& parent) | ||||
| @@ -195,7 +195,7 @@ std::optional<QIcon> ResourceModel::getIcon(QModelIndex& index, const QUrl& url) | ||||
|         return {}; | ||||
|  | ||||
|     auto cache_entry = APPLICATION->metacache()->resolveEntry( | ||||
|         m_associated_page->metaEntryBase(), | ||||
|         metaEntryBase(), | ||||
|         QString("logos/%1").arg(QString(QCryptographicHash::hash(url.toEncoded(), QCryptographicHash::Algorithm::Sha1).toHex()))); | ||||
|     auto icon_fetch_action = Net::Download::makeCached(url, cache_entry); | ||||
|  | ||||
| @@ -222,11 +222,6 @@ std::optional<QIcon> ResourceModel::getIcon(QModelIndex& index, const QUrl& url) | ||||
|     return {}; | ||||
| } | ||||
|  | ||||
| bool ResourceModel::isPackSelected(const ModPlatform::IndexedPack& pack) const | ||||
| { | ||||
|     return m_associated_page->isPackSelected(pack); | ||||
| } | ||||
|  | ||||
| void ResourceModel::searchRequestFailed(QString reason, int network_error_code) | ||||
| { | ||||
|     switch (network_error_code) { | ||||
| @@ -237,9 +232,7 @@ void ResourceModel::searchRequestFailed(QString reason, int network_error_code) | ||||
|         case 409: | ||||
|             // 409 Gone, notify user to update | ||||
|             QMessageBox::critical(nullptr, tr("Error"), | ||||
|                                   //: %1 refers to the launcher itself | ||||
|                                   QString("%1 %2") | ||||
|                                       .arg(m_associated_page->displayName()) | ||||
|                                   QString("%1") | ||||
|                                       .arg(tr("API version too old!\nPlease update %1!").arg(BuildConfig.LAUNCHER_DISPLAYNAME))); | ||||
|             break; | ||||
|     } | ||||
|   | ||||
| @@ -5,6 +5,7 @@ | ||||
| #include <QAbstractListModel> | ||||
|  | ||||
| #include "QObjectPtr.h" | ||||
| #include "BaseInstance.h" | ||||
| #include "modplatform/ResourceAPI.h" | ||||
| #include "tasks/ConcurrentTask.h" | ||||
|  | ||||
| @@ -17,19 +18,18 @@ struct IndexedPack; | ||||
|  | ||||
| namespace ResourceDownload { | ||||
|  | ||||
| class ResourcePage; | ||||
|  | ||||
| class ResourceModel : public QAbstractListModel { | ||||
|     Q_OBJECT | ||||
|  | ||||
|    public: | ||||
|     ResourceModel(ResourcePage* parent, ResourceAPI* api); | ||||
|     ResourceModel(BaseInstance const&, ResourceAPI* api); | ||||
|     ~ResourceModel() override; | ||||
|  | ||||
|     [[nodiscard]] auto data(const QModelIndex&, int role) const -> QVariant override; | ||||
|     bool setData(const QModelIndex& index, const QVariant& value, int role) override; | ||||
|  | ||||
|     [[nodiscard]] auto debugName() const -> QString; | ||||
|     [[nodiscard]] virtual auto debugName() const -> QString; | ||||
|     [[nodiscard]] virtual auto metaEntryBase() const -> QString = 0; | ||||
|  | ||||
|     [[nodiscard]] inline int rowCount(const QModelIndex& parent) const override { return parent.isValid() ? 0 : m_packs.size(); } | ||||
|     [[nodiscard]] inline int columnCount(const QModelIndex& parent) const override { return parent.isValid() ? 0 : 1; }; | ||||
| @@ -38,6 +38,10 @@ 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; } | ||||
|  | ||||
|    signals: | ||||
|     void versionListUpdated(); | ||||
|     void projectInfoUpdated(); | ||||
|  | ||||
|    public slots: | ||||
|     void fetchMore(const QModelIndex& parent) override; | ||||
|     [[nodiscard]] inline bool canFetchMore(const QModelIndex& parent) const override | ||||
| @@ -72,9 +76,9 @@ class ResourceModel : public QAbstractListModel { | ||||
|     /** Resets the model's data. */ | ||||
|     void clearData(); | ||||
|  | ||||
|     [[nodiscard]] bool isPackSelected(const ModPlatform::IndexedPack&) const; | ||||
|  | ||||
|    protected: | ||||
|     const BaseInstance& m_base_instance; | ||||
|  | ||||
|     /* Basic search parameters */ | ||||
|     enum class SearchState { None, CanFetchMore, ResetRequested, Finished } m_search_state = SearchState::None; | ||||
|     int m_next_search_offset = 0; | ||||
| @@ -88,8 +92,6 @@ class ResourceModel : public QAbstractListModel { | ||||
|     QSet<QUrl> m_currently_running_icon_actions; | ||||
|     QSet<QUrl> m_failed_icon_actions; | ||||
|  | ||||
|     ResourcePage* m_associated_page = nullptr; | ||||
|  | ||||
|     QList<ModPlatform::IndexedPack> m_packs; | ||||
|  | ||||
|     // HACK: We need this to prevent callbacks from calling the model after it has already been deleted. | ||||
|   | ||||
| @@ -103,19 +103,18 @@ void ResourcePage::setSearchTerm(QString term) | ||||
|     m_ui->searchEdit->setText(term); | ||||
| } | ||||
|  | ||||
| bool ResourcePage::setCurrentPack(ModPlatform::IndexedPack pack) | ||||
| { | ||||
|     QVariant v; | ||||
|     v.setValue(pack); | ||||
|     return m_model->setData(m_ui->packView->currentIndex(), v, Qt::UserRole); | ||||
| } | ||||
|  | ||||
| ModPlatform::IndexedPack ResourcePage::getCurrentPack() const | ||||
| { | ||||
|     return m_model->data(m_ui->packView->currentIndex(), Qt::UserRole).value<ModPlatform::IndexedPack>(); | ||||
| } | ||||
|  | ||||
| bool ResourcePage::isPackSelected(const ModPlatform::IndexedPack& pack, int version) const | ||||
| { | ||||
|     if (version < 0 || !pack.versionsLoaded) | ||||
|         return m_parent_dialog->isSelected(pack.name); | ||||
|  | ||||
|     return m_parent_dialog->isSelected(pack.name, pack.versions[version].fileName); | ||||
| } | ||||
|  | ||||
| void ResourcePage::updateUi() | ||||
| { | ||||
|     auto current_pack = getCurrentPack(); | ||||
| @@ -185,7 +184,7 @@ void ResourcePage::updateSelectionButton() | ||||
|     } | ||||
|  | ||||
|     m_ui->resourceSelectionButton->setEnabled(true); | ||||
|     if (!isPackSelected(getCurrentPack(), m_selected_version_index)) { | ||||
|     if (!getCurrentPack().isVersionSelected(m_selected_version_index)) { | ||||
|         m_ui->resourceSelectionButton->setText(tr("Select %1 for download").arg(resourceString())); | ||||
|     } else { | ||||
|         m_ui->resourceSelectionButton->setText(tr("Deselect %1 for download").arg(resourceString())); | ||||
| @@ -256,12 +255,12 @@ void ResourcePage::onVersionSelectionChanged(QString data) | ||||
|  | ||||
| void ResourcePage::addResourceToDialog(ModPlatform::IndexedPack& pack, ModPlatform::IndexedVersion& version) | ||||
| { | ||||
|     m_parent_dialog->addResource(pack.name, new ResourceDownloadTask(pack, version, m_parent_dialog->getBaseModel())); | ||||
|     m_parent_dialog->addResource(pack, version); | ||||
| } | ||||
|  | ||||
| void ResourcePage::removeResourceFromDialog(ModPlatform::IndexedPack& pack, ModPlatform::IndexedVersion&) | ||||
| void ResourcePage::removeResourceFromDialog(ModPlatform::IndexedPack& pack, ModPlatform::IndexedVersion& version) | ||||
| { | ||||
|     m_parent_dialog->removeResource(pack.name); | ||||
|     m_parent_dialog->removeResource(pack, version); | ||||
| } | ||||
|  | ||||
| void ResourcePage::onResourceSelected() | ||||
| @@ -270,13 +269,19 @@ void ResourcePage::onResourceSelected() | ||||
|         return; | ||||
|  | ||||
|     auto current_pack = getCurrentPack(); | ||||
|     if (!current_pack.versionsLoaded) | ||||
|         return; | ||||
|  | ||||
|     auto& version = current_pack.versions[m_selected_version_index]; | ||||
|     if (m_parent_dialog->isSelected(current_pack.name, version.fileName)) | ||||
|     if (version.is_currently_selected) | ||||
|         removeResourceFromDialog(current_pack, version); | ||||
|     else | ||||
|         addResourceToDialog(current_pack, version); | ||||
|  | ||||
|     // Save the modified pack (and prevent warning in release build) | ||||
|     [[maybe_unused]] bool set = setCurrentPack(current_pack); | ||||
|     Q_ASSERT(set); | ||||
|  | ||||
|     updateSelectionButton(); | ||||
|  | ||||
|     /* Force redraw on the resource list when the selection changes */ | ||||
|   | ||||
| @@ -50,11 +50,13 @@ class ResourcePage : public QWidget, public BasePage { | ||||
|     /** Programatically set the term in the search bar. */ | ||||
|     void setSearchTerm(QString); | ||||
|  | ||||
|     [[nodiscard]] bool isPackSelected(const ModPlatform::IndexedPack&, int version = -1) const; | ||||
|     [[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&); | ||||
|  | ||||
|   | ||||
| @@ -2,6 +2,7 @@ | ||||
|  | ||||
| #include "Json.h" | ||||
|  | ||||
| #include "modplatform/flame/FlameAPI.h" | ||||
| #include "modplatform/flame/FlameModIndex.h" | ||||
|  | ||||
| namespace ResourceDownload { | ||||
| @@ -9,7 +10,7 @@ namespace ResourceDownload { | ||||
| // NOLINTNEXTLINE(modernize-avoid-c-arrays) | ||||
| const char* FlameModModel::sorts[6]{ "Featured", "Popularity", "LastUpdated", "Name", "Author", "TotalDownloads" }; | ||||
|  | ||||
| FlameModModel::FlameModModel(FlameModPage* parent) : ModModel(parent, new FlameAPI) {} | ||||
| FlameModModel::FlameModModel(BaseInstance const& base) : ModModel(base, new FlameAPI) {} | ||||
|  | ||||
| void FlameModModel::loadIndexedPack(ModPlatform::IndexedPack& m, QJsonObject& obj) | ||||
| { | ||||
| @@ -24,7 +25,7 @@ void FlameModModel::loadExtraPackInfo(ModPlatform::IndexedPack& m, QJsonObject& | ||||
|  | ||||
| void FlameModModel::loadIndexedPackVersions(ModPlatform::IndexedPack& m, QJsonArray& arr) | ||||
| { | ||||
|     FlameMod::loadIndexedPackVersions(m, arr, APPLICATION->network(), &m_associated_page->m_base_instance); | ||||
|     FlameMod::loadIndexedPackVersions(m, arr, APPLICATION->network(), &m_base_instance); | ||||
| } | ||||
|  | ||||
| auto FlameModModel::documentToArray(QJsonDocument& obj) const -> QJsonArray | ||||
|   | ||||
| @@ -1,9 +1,6 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include "modplatform/flame/FlameAPI.h" | ||||
|  | ||||
| #include "ui/pages/modplatform/ModModel.h" | ||||
|  | ||||
| #include "ui/pages/modplatform/flame/FlameResourcePages.h" | ||||
|  | ||||
| namespace ResourceDownload { | ||||
| @@ -12,10 +9,13 @@ class FlameModModel : public ModModel { | ||||
|     Q_OBJECT | ||||
|  | ||||
|    public: | ||||
|     FlameModModel(FlameModPage* parent); | ||||
|     FlameModModel(const BaseInstance&); | ||||
|     ~FlameModModel() override = default; | ||||
|  | ||||
|    private: | ||||
|     [[nodiscard]] QString debugName() const override { return Flame::debugName() + " (Model)"; } | ||||
|     [[nodiscard]] QString metaEntryBase() const override { return Flame::metaEntryBase(); } | ||||
|  | ||||
|     void loadIndexedPack(ModPlatform::IndexedPack& m, QJsonObject& obj) override; | ||||
|     void loadExtraPackInfo(ModPlatform::IndexedPack& m, QJsonObject& obj) override; | ||||
|     void loadIndexedPackVersions(ModPlatform::IndexedPack& m, QJsonArray& arr) override; | ||||
|   | ||||
| @@ -45,7 +45,7 @@ namespace ResourceDownload { | ||||
| FlameModPage::FlameModPage(ModDownloadDialog* dialog, BaseInstance& instance) | ||||
|     : ModPage(dialog, instance) | ||||
| { | ||||
|     m_model = new FlameModModel(this); | ||||
|     m_model = new FlameModModel(instance); | ||||
|     m_ui->packView->setModel(m_model); | ||||
|  | ||||
|     // index is used to set the sorting with the flame api | ||||
|   | ||||
| @@ -18,8 +18,6 @@ | ||||
|  | ||||
| #include "ModrinthResourceModels.h" | ||||
|  | ||||
| #include "ui/pages/modplatform/modrinth/ModrinthResourcePages.h" | ||||
|  | ||||
| #include "modplatform/modrinth/ModrinthAPI.h" | ||||
| #include "modplatform/modrinth/ModrinthPackIndex.h" | ||||
|  | ||||
| @@ -28,7 +26,7 @@ namespace ResourceDownload { | ||||
| // NOLINTNEXTLINE(modernize-avoid-c-arrays) | ||||
| const char* ModrinthModModel::sorts[5]{ "relevance", "downloads", "follows", "updated", "newest" }; | ||||
|  | ||||
| ModrinthModModel::ModrinthModModel(ModrinthModPage* parent) : ModModel(parent, new ModrinthAPI){}; | ||||
| ModrinthModModel::ModrinthModModel(BaseInstance const& base) : ModModel(base, new ModrinthAPI){}; | ||||
|  | ||||
| void ModrinthModModel::loadIndexedPack(ModPlatform::IndexedPack& m, QJsonObject& obj) | ||||
| { | ||||
| @@ -42,7 +40,7 @@ void ModrinthModModel::loadExtraPackInfo(ModPlatform::IndexedPack& m, QJsonObjec | ||||
|  | ||||
| void ModrinthModModel::loadIndexedPackVersions(ModPlatform::IndexedPack& m, QJsonArray& arr) | ||||
| { | ||||
|     ::Modrinth::loadIndexedPackVersions(m, arr, APPLICATION->network(), &m_associated_page->m_base_instance); | ||||
|     ::Modrinth::loadIndexedPackVersions(m, arr, APPLICATION->network(), &m_base_instance); | ||||
| } | ||||
|  | ||||
| auto ModrinthModModel::documentToArray(QJsonDocument& obj) const -> QJsonArray | ||||
|   | ||||
| @@ -19,6 +19,7 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include "ui/pages/modplatform/ModModel.h" | ||||
| #include "ui/pages/modplatform/modrinth/ModrinthResourcePages.h" | ||||
|  | ||||
| namespace ResourceDownload { | ||||
|  | ||||
| @@ -28,10 +29,13 @@ class ModrinthModModel : public ModModel { | ||||
|     Q_OBJECT | ||||
|  | ||||
|    public: | ||||
|     ModrinthModModel(ModrinthModPage* parent); | ||||
|     ModrinthModModel(const BaseInstance&); | ||||
|     ~ModrinthModModel() override = default; | ||||
|  | ||||
|    private: | ||||
|     [[nodiscard]] QString debugName() const override { return Modrinth::debugName() + " (Model)"; } | ||||
|     [[nodiscard]] QString metaEntryBase() const override { return Modrinth::metaEntryBase(); } | ||||
|  | ||||
|     void loadIndexedPack(ModPlatform::IndexedPack& m, QJsonObject& obj) override; | ||||
|     void loadExtraPackInfo(ModPlatform::IndexedPack& m, QJsonObject& obj) override; | ||||
|     void loadIndexedPackVersions(ModPlatform::IndexedPack& m, QJsonArray& arr) override; | ||||
|   | ||||
| @@ -47,7 +47,7 @@ namespace ResourceDownload { | ||||
| ModrinthModPage::ModrinthModPage(ModDownloadDialog* dialog, BaseInstance& instance) | ||||
|     : ModPage(dialog, instance) | ||||
| { | ||||
|     m_model = new ModrinthModModel(this); | ||||
|     m_model = new ModrinthModModel(instance); | ||||
|     m_ui->packView->setModel(m_model); | ||||
|  | ||||
|     // index is used to set the sorting with the modrinth api | ||||
|   | ||||
		Reference in New Issue
	
	Block a user