feat: add donate links for modrinth mods
This commit is contained in:
		| @@ -7,6 +7,7 @@ | ||||
|  | ||||
| namespace ModPlatform { | ||||
| class ListModel; | ||||
| struct IndexedPack; | ||||
| } | ||||
|  | ||||
| class ModAPI { | ||||
| @@ -35,6 +36,7 @@ class ModAPI { | ||||
|     }; | ||||
|  | ||||
|     virtual void searchMods(CallerType* caller, SearchArgs&& args) const = 0; | ||||
|     virtual void getModInfo(CallerType* caller, ModPlatform::IndexedPack& pack) = 0; | ||||
|  | ||||
|  | ||||
|     struct VersionSearchArgs { | ||||
|   | ||||
| @@ -13,6 +13,12 @@ struct ModpackAuthor { | ||||
|     QString url; | ||||
| }; | ||||
|  | ||||
| struct DonationData { | ||||
|     QString id; | ||||
|     QString platform; | ||||
|     QString url; | ||||
| }; | ||||
|  | ||||
| struct IndexedVersion { | ||||
|     QVariant addonId; | ||||
|     QVariant fileId; | ||||
| @@ -24,6 +30,10 @@ struct IndexedVersion { | ||||
|     QVector<QString> loaders = {}; | ||||
| }; | ||||
|  | ||||
| struct ExtraPackData { | ||||
|     QList<DonationData> donate; | ||||
| }; | ||||
|  | ||||
| struct IndexedPack { | ||||
|     QVariant addonId; | ||||
|     QString name; | ||||
| @@ -35,6 +45,10 @@ struct IndexedPack { | ||||
|  | ||||
|     bool versionsLoaded = false; | ||||
|     QVector<IndexedVersion> versions; | ||||
|  | ||||
|     // Don't load by default, since some modplatform don't have that info | ||||
|     bool extraDataLoaded = true; | ||||
|     ExtraPackData extraData; | ||||
| }; | ||||
|  | ||||
| }  // namespace ModPlatform | ||||
|   | ||||
| @@ -41,6 +41,8 @@ class FlameAPI : public NetworkModAPI { | ||||
|             .arg(gameVersionStr); | ||||
|     }; | ||||
|  | ||||
|     inline auto getModInfoURL(QString& id) const -> QString override { return {}; }; | ||||
|  | ||||
|     inline auto getVersionsURL(VersionSearchArgs& args) const -> QString override | ||||
|     { | ||||
|         QString gameVersionQuery = args.mcVersions.size() == 1 ? QString("gameVersion=%1&").arg(args.mcVersions.front().toString()) : ""; | ||||
|   | ||||
| @@ -31,6 +31,31 @@ void NetworkModAPI::searchMods(CallerType* caller, SearchArgs&& args) const | ||||
|     netJob->start(); | ||||
| } | ||||
|  | ||||
| void NetworkModAPI::getModInfo(CallerType* caller, ModPlatform::IndexedPack& pack) | ||||
| { | ||||
|     auto id_str = pack.addonId.toString(); | ||||
|     auto netJob = new NetJob(QString("%1::ModInfo").arg(id_str), APPLICATION->network()); | ||||
|     auto searchUrl = getModInfoURL(id_str); | ||||
|  | ||||
|     auto response = new QByteArray(); | ||||
|     netJob->addNetAction(Net::Download::makeByteArray(QUrl(searchUrl), response)); | ||||
|  | ||||
|     QObject::connect(netJob, &NetJob::succeeded, [response, &pack, caller] { | ||||
|         QJsonParseError parse_error{}; | ||||
|         auto doc = QJsonDocument::fromJson(*response, &parse_error); | ||||
|         if (parse_error.error != QJsonParseError::NoError) { | ||||
|             qWarning() << "Error while parsing JSON response for " << pack.name << " at " << parse_error.offset | ||||
|                        << " reason: " << parse_error.errorString(); | ||||
|             qWarning() << *response; | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         caller->infoRequestFinished(doc, pack); | ||||
|     }); | ||||
|  | ||||
|     netJob->start(); | ||||
| } | ||||
|  | ||||
| void NetworkModAPI::getVersions(CallerType* caller, VersionSearchArgs&& args) const | ||||
| { | ||||
|     auto netJob = new NetJob(QString("%1::ModVersions(%2)").arg(caller->debugName()).arg(args.addonId), APPLICATION->network()); | ||||
|   | ||||
| @@ -5,9 +5,11 @@ | ||||
| class NetworkModAPI : public ModAPI { | ||||
|    public: | ||||
|     void searchMods(CallerType* caller, SearchArgs&& args) const override; | ||||
|     void getModInfo(CallerType* caller, ModPlatform::IndexedPack& pack) override; | ||||
|     void getVersions(CallerType* caller, VersionSearchArgs&& args) const override; | ||||
|  | ||||
|    protected: | ||||
|     virtual auto getModSearchURL(SearchArgs& args) const -> QString = 0; | ||||
|     virtual auto getModInfoURL(QString& id) const -> QString = 0; | ||||
|     virtual auto getVersionsURL(VersionSearchArgs& args) const -> QString = 0; | ||||
| }; | ||||
|   | ||||
| @@ -75,6 +75,11 @@ class ModrinthAPI : public NetworkModAPI { | ||||
|             .arg(getGameVersionsArray(args.versions)); | ||||
|     }; | ||||
|  | ||||
|     inline auto getModInfoURL(QString& id) const -> QString override | ||||
|     { | ||||
|         return BuildConfig.MODRINTH_PROD_URL + "/project/" + id; | ||||
|     }; | ||||
|  | ||||
|     inline auto getVersionsURL(VersionSearchArgs& args) const -> QString override | ||||
|     { | ||||
|         return QString(BuildConfig.MODRINTH_PROD_URL + | ||||
|   | ||||
| @@ -45,6 +45,27 @@ void Modrinth::loadIndexedPack(ModPlatform::IndexedPack& pack, QJsonObject& obj) | ||||
|     modAuthor.name = Json::requireString(obj, "author"); | ||||
|     modAuthor.url = api.getAuthorURL(modAuthor.name); | ||||
|     pack.authors.append(modAuthor); | ||||
|  | ||||
|     // Modrinth can have more data than what's provided by the basic search :) | ||||
|     pack.extraDataLoaded = false; | ||||
| } | ||||
|  | ||||
| void Modrinth::loadExtraPackData(ModPlatform::IndexedPack& pack, QJsonObject& obj) | ||||
| { | ||||
|     auto donate_arr = Json::ensureArray(obj, "donation_urls"); | ||||
|     for(auto d : donate_arr){ | ||||
|         auto d_obj = Json::requireObject(d); | ||||
|  | ||||
|         ModPlatform::DonationData donate; | ||||
|  | ||||
|         donate.id = Json::ensureString(d_obj, "id"); | ||||
|         donate.platform = Json::ensureString(d_obj, "platform"); | ||||
|         donate.url = Json::ensureString(d_obj, "url"); | ||||
|  | ||||
|         pack.extraData.donate.append(donate); | ||||
|     } | ||||
|  | ||||
|     pack.extraDataLoaded = true; | ||||
| } | ||||
|  | ||||
| void Modrinth::loadIndexedPackVersions(ModPlatform::IndexedPack& pack, | ||||
|   | ||||
| @@ -25,6 +25,7 @@ | ||||
| namespace Modrinth { | ||||
|  | ||||
| void loadIndexedPack(ModPlatform::IndexedPack& m, QJsonObject& obj); | ||||
| void loadExtraPackData(ModPlatform::IndexedPack& m, QJsonObject& obj); | ||||
| void loadIndexedPackVersions(ModPlatform::IndexedPack& pack, | ||||
|                              QJsonArray& arr, | ||||
|                              const shared_qobject_ptr<QNetworkAccessManager>& network, | ||||
|   | ||||
| @@ -79,6 +79,11 @@ void ListModel::performPaginatedSearch() | ||||
|         this, { nextSearchOffset, currentSearchTerm, getSorts()[currentSort], profile->getModLoaders(), getMineVersions() }); | ||||
| } | ||||
|  | ||||
| void ListModel::requestModInfo(ModPlatform::IndexedPack& current) | ||||
| { | ||||
|     m_parent->apiProvider()->getModInfo(this, current); | ||||
| } | ||||
|  | ||||
| void ListModel::refresh() | ||||
| { | ||||
|     if (jobPtr) { | ||||
| @@ -225,6 +230,21 @@ void ListModel::searchRequestFailed(QString reason) | ||||
|     } | ||||
| } | ||||
|  | ||||
| void ListModel::infoRequestFinished(QJsonDocument& doc, ModPlatform::IndexedPack& pack) | ||||
| { | ||||
|     qDebug() << "Loading mod info"; | ||||
|  | ||||
|     try { | ||||
|         auto obj = Json::requireObject(doc); | ||||
|         loadExtraPackInfo(pack, obj); | ||||
|     } catch (const JSONValidationError& e) { | ||||
|         qDebug() << doc; | ||||
|         qWarning() << "Error while reading " << debugName() << " mod info: " << e.cause(); | ||||
|     } | ||||
|  | ||||
|     m_parent->updateUi(); | ||||
| } | ||||
|  | ||||
| void ListModel::versionRequestSucceeded(QJsonDocument doc, QString addonId) | ||||
| { | ||||
|     auto& current = m_parent->getCurrent(); | ||||
|   | ||||
| @@ -36,9 +36,11 @@ class ListModel : public QAbstractListModel { | ||||
|     void fetchMore(const QModelIndex& parent) override; | ||||
|     void refresh(); | ||||
|     void searchWithTerm(const QString& term, const int sort, const bool filter_changed); | ||||
|     void requestModInfo(ModPlatform::IndexedPack& current); | ||||
|     void requestModVersions(const ModPlatform::IndexedPack& current); | ||||
|  | ||||
|     virtual void loadIndexedPack(ModPlatform::IndexedPack& m, QJsonObject& obj) = 0; | ||||
|     virtual void loadExtraPackInfo(ModPlatform::IndexedPack& m, QJsonObject& obj) {}; | ||||
|     virtual void loadIndexedPackVersions(ModPlatform::IndexedPack& m, QJsonArray& arr) = 0; | ||||
|  | ||||
|     void getLogo(const QString& logo, const QString& logoUrl, LogoCallback callback); | ||||
| @@ -49,6 +51,8 @@ class ListModel : public QAbstractListModel { | ||||
|     void searchRequestFinished(QJsonDocument& doc); | ||||
|     void searchRequestFailed(QString reason); | ||||
|  | ||||
|     void infoRequestFinished(QJsonDocument& doc, ModPlatform::IndexedPack& pack); | ||||
|  | ||||
|     void versionRequestSucceeded(QJsonDocument doc, QString addonId); | ||||
|  | ||||
|    protected slots: | ||||
|   | ||||
| @@ -94,28 +94,6 @@ void ModPage::onSelectionChanged(QModelIndex first, QModelIndex second) | ||||
|     if (!first.isValid()) { return; } | ||||
|  | ||||
|     current = listModel->data(first, Qt::UserRole).value<ModPlatform::IndexedPack>(); | ||||
|     QString text = ""; | ||||
|     QString name = current.name; | ||||
|  | ||||
|     if (current.websiteUrl.isEmpty()) | ||||
|         text = name; | ||||
|     else | ||||
|         text = "<a href=\"" + current.websiteUrl + "\">" + name + "</a>"; | ||||
|  | ||||
|     if (!current.authors.empty()) { | ||||
|         auto authorToStr = [](ModPlatform::ModpackAuthor& author) -> QString { | ||||
|             if (author.url.isEmpty()) { return author.name; } | ||||
|             return QString("<a href=\"%1\">%2</a>").arg(author.url, author.name); | ||||
|         }; | ||||
|         QStringList authorStrs; | ||||
|         for (auto& author : current.authors) { | ||||
|             authorStrs.push_back(authorToStr(author)); | ||||
|         } | ||||
|         text += "<br>" + tr(" by ") + authorStrs.join(", "); | ||||
|     } | ||||
|     text += "<br><br>"; | ||||
|  | ||||
|     ui->packDescription->setHtml(text + current.description); | ||||
|  | ||||
|     if (!current.versionsLoaded) { | ||||
|         qDebug() << QString("Loading %1 mod versions").arg(debugName()); | ||||
| @@ -132,6 +110,13 @@ void ModPage::onSelectionChanged(QModelIndex first, QModelIndex second) | ||||
|  | ||||
|         updateSelectionButton(); | ||||
|     } | ||||
|  | ||||
|     if(!current.extraDataLoaded){ | ||||
|         qDebug() << QString("Loading %1 mod info").arg(debugName()); | ||||
|         listModel->requestModInfo(current); | ||||
|     } | ||||
|  | ||||
|     updateUi(); | ||||
| } | ||||
|  | ||||
| void ModPage::onVersionSelectionChanged(QString data) | ||||
| @@ -207,3 +192,42 @@ void ModPage::updateSelectionButton() | ||||
|         ui->modSelectionButton->setText(tr("Deselect mod for download")); | ||||
|     } | ||||
| } | ||||
|  | ||||
| void ModPage::updateUi() | ||||
| { | ||||
|     QString text = ""; | ||||
|     QString name = current.name; | ||||
|  | ||||
|     if (current.websiteUrl.isEmpty()) | ||||
|         text = name; | ||||
|     else | ||||
|         text = "<a href=\"" + current.websiteUrl + "\">" + name + "</a>"; | ||||
|  | ||||
|     if (!current.authors.empty()) { | ||||
|         auto authorToStr = [](ModPlatform::ModpackAuthor& author) -> QString { | ||||
|             if (author.url.isEmpty()) { return author.name; } | ||||
|             return QString("<a href=\"%1\">%2</a>").arg(author.url, author.name); | ||||
|         }; | ||||
|         QStringList authorStrs; | ||||
|         for (auto& author : current.authors) { | ||||
|             authorStrs.push_back(authorToStr(author)); | ||||
|         } | ||||
|         text += "<br>" + tr(" by ") + authorStrs.join(", "); | ||||
|     } | ||||
|  | ||||
|     if(!current.extraData.donate.isEmpty()) { | ||||
|         text += "<br><br>Donation information:<br>"; | ||||
|         auto donateToStr = [](ModPlatform::DonationData& donate) -> QString { | ||||
|             return QString("<a href=\"%1\">%2</a>").arg(donate.url, donate.platform); | ||||
|         }; | ||||
|         QStringList donates; | ||||
|         for (auto& donate : current.extraData.donate) { | ||||
|             donates.append(donateToStr(donate)); | ||||
|         } | ||||
|         text += donates.join(", "); | ||||
|     } | ||||
|  | ||||
|     text += "<br><br>"; | ||||
|  | ||||
|     ui->packDescription->setHtml(text + current.description); | ||||
| } | ||||
|   | ||||
| @@ -36,10 +36,12 @@ class ModPage : public QWidget, public BasePage { | ||||
|  | ||||
|     void retranslate() override; | ||||
|  | ||||
|     void updateUi(); | ||||
|  | ||||
|     auto shouldDisplay() const -> bool override = 0; | ||||
|     virtual auto validateVersion(ModPlatform::IndexedVersion& ver, QString mineVer, ModAPI::ModLoaderTypes loaders = ModAPI::Unspecified) const -> bool = 0; | ||||
|  | ||||
|     auto apiProvider() const -> const ModAPI* { return api.get(); }; | ||||
|     auto apiProvider() -> ModAPI* { return api.get(); }; | ||||
|     auto getFilter() const -> const std::shared_ptr<ModFilterWidget::Filter> { return m_filter; } | ||||
|  | ||||
|     auto getCurrent() -> ModPlatform::IndexedPack& { return current; } | ||||
|   | ||||
| @@ -30,6 +30,11 @@ void ListModel::loadIndexedPack(ModPlatform::IndexedPack& m, QJsonObject& obj) | ||||
|     Modrinth::loadIndexedPack(m, obj); | ||||
| } | ||||
|  | ||||
| void ListModel::loadExtraPackInfo(ModPlatform::IndexedPack& m, QJsonObject& obj) | ||||
| { | ||||
|     Modrinth::loadExtraPackData(m, obj); | ||||
| } | ||||
|  | ||||
| void ListModel::loadIndexedPackVersions(ModPlatform::IndexedPack& m, QJsonArray& arr) | ||||
| { | ||||
|     Modrinth::loadIndexedPackVersions(m, arr, APPLICATION->network(), m_parent->m_instance); | ||||
|   | ||||
| @@ -31,6 +31,7 @@ class ListModel : public ModPlatform::ListModel { | ||||
|  | ||||
|    private: | ||||
|     void loadIndexedPack(ModPlatform::IndexedPack& m, QJsonObject& obj) override; | ||||
|     void loadExtraPackInfo(ModPlatform::IndexedPack& m, QJsonObject& obj) override; | ||||
|     void loadIndexedPackVersions(ModPlatform::IndexedPack& m, QJsonArray& arr) override; | ||||
|      | ||||
|     auto documentToArray(QJsonDocument& obj) const -> QJsonArray override; | ||||
|   | ||||
		Reference in New Issue
	
	Block a user