Merge pull request #2887 from Trial97/filters_modpack
Add filters for modpack search
This commit is contained in:
commit
78ceae9c17
@ -123,8 +123,7 @@ QDebug operator<<(QDebug debug, const Version& v)
|
|||||||
first = false;
|
first = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
debug.nospace() << " ]"
|
debug.nospace() << " ]" << " }";
|
||||||
<< " }";
|
|
||||||
|
|
||||||
return debug;
|
return debug;
|
||||||
}
|
}
|
||||||
|
@ -35,7 +35,7 @@ Q_DECLARE_FLAGS(ModLoaderTypes, ModLoaderType)
|
|||||||
|
|
||||||
enum class ResourceProvider { MODRINTH, FLAME };
|
enum class ResourceProvider { MODRINTH, FLAME };
|
||||||
|
|
||||||
enum class ResourceType { MOD, RESOURCE_PACK, SHADER_PACK };
|
enum class ResourceType { MOD, RESOURCE_PACK, SHADER_PACK, MODPACK };
|
||||||
|
|
||||||
enum class DependencyType { REQUIRED, OPTIONAL, INCOMPATIBLE, EMBEDDED, TOOL, INCLUDE, UNKNOWN };
|
enum class DependencyType { REQUIRED, OPTIONAL, INCOMPATIBLE, EMBEDDED, TOOL, INCLUDE, UNKNOWN };
|
||||||
|
|
||||||
|
@ -221,14 +221,20 @@ QList<ResourceAPI::SortingMethod> FlameAPI::getSortingMethods() const
|
|||||||
{ 8, "GameVersion", QObject::tr("Sort by Game Version") } };
|
{ 8, "GameVersion", QObject::tr("Sort by Game Version") } };
|
||||||
}
|
}
|
||||||
|
|
||||||
Task::Ptr FlameAPI::getModCategories(std::shared_ptr<QByteArray> response)
|
Task::Ptr FlameAPI::getCategories(std::shared_ptr<QByteArray> response, ModPlatform::ResourceType type)
|
||||||
{
|
{
|
||||||
auto netJob = makeShared<NetJob>(QString("Flame::GetCategories"), APPLICATION->network());
|
auto netJob = makeShared<NetJob>(QString("Flame::GetCategories"), APPLICATION->network());
|
||||||
netJob->addNetAction(Net::ApiDownload::makeByteArray(QUrl("https://api.curseforge.com/v1/categories?gameId=432&classId=6"), response));
|
netJob->addNetAction(Net::ApiDownload::makeByteArray(
|
||||||
|
QUrl(QString("https://api.curseforge.com/v1/categories?gameId=432&classId=%1").arg(getClassId(type))), response));
|
||||||
QObject::connect(netJob.get(), &Task::failed, [](QString msg) { qDebug() << "Flame failed to get categories:" << msg; });
|
QObject::connect(netJob.get(), &Task::failed, [](QString msg) { qDebug() << "Flame failed to get categories:" << msg; });
|
||||||
return netJob;
|
return netJob;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Task::Ptr FlameAPI::getModCategories(std::shared_ptr<QByteArray> response)
|
||||||
|
{
|
||||||
|
return getCategories(response, ModPlatform::ResourceType::MOD);
|
||||||
|
}
|
||||||
|
|
||||||
QList<ModPlatform::Category> FlameAPI::loadModCategories(std::shared_ptr<QByteArray> response)
|
QList<ModPlatform::Category> FlameAPI::loadModCategories(std::shared_ptr<QByteArray> response)
|
||||||
{
|
{
|
||||||
QList<ModPlatform::Category> categories;
|
QList<ModPlatform::Category> categories;
|
||||||
|
@ -25,6 +25,7 @@ class FlameAPI : public NetworkResourceAPI {
|
|||||||
Task::Ptr getFiles(const QStringList& fileIds, std::shared_ptr<QByteArray> response) const;
|
Task::Ptr getFiles(const QStringList& fileIds, std::shared_ptr<QByteArray> response) const;
|
||||||
Task::Ptr getFile(const QString& addonId, const QString& fileId, std::shared_ptr<QByteArray> response) const;
|
Task::Ptr getFile(const QString& addonId, const QString& fileId, std::shared_ptr<QByteArray> response) const;
|
||||||
|
|
||||||
|
static Task::Ptr getCategories(std::shared_ptr<QByteArray> response, ModPlatform::ResourceType type);
|
||||||
static Task::Ptr getModCategories(std::shared_ptr<QByteArray> response);
|
static Task::Ptr getModCategories(std::shared_ptr<QByteArray> response);
|
||||||
static QList<ModPlatform::Category> loadModCategories(std::shared_ptr<QByteArray> response);
|
static QList<ModPlatform::Category> loadModCategories(std::shared_ptr<QByteArray> response);
|
||||||
|
|
||||||
@ -46,6 +47,8 @@ class FlameAPI : public NetworkResourceAPI {
|
|||||||
return 12;
|
return 12;
|
||||||
case ModPlatform::ResourceType::SHADER_PACK:
|
case ModPlatform::ResourceType::SHADER_PACK:
|
||||||
return 6552;
|
return 6552;
|
||||||
|
case ModPlatform::ResourceType::MODPACK:
|
||||||
|
return 4471;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -82,12 +85,9 @@ class FlameAPI : public NetworkResourceAPI {
|
|||||||
|
|
||||||
static const QString getModLoaderFilters(ModPlatform::ModLoaderTypes types) { return "[" + getModLoaderStrings(types).join(',') + "]"; }
|
static const QString getModLoaderFilters(ModPlatform::ModLoaderTypes types) { return "[" + getModLoaderStrings(types).join(',') + "]"; }
|
||||||
|
|
||||||
private:
|
public:
|
||||||
[[nodiscard]] std::optional<QString> getSearchURL(SearchArgs const& args) const override
|
[[nodiscard]] std::optional<QString> getSearchURL(SearchArgs const& args) const override
|
||||||
{
|
{
|
||||||
auto gameVersionStr =
|
|
||||||
args.versions.has_value() ? QString("gameVersion=%1").arg(args.versions.value().front().toString()) : QString();
|
|
||||||
|
|
||||||
QStringList get_arguments;
|
QStringList get_arguments;
|
||||||
get_arguments.append(QString("classId=%1").arg(getClassId(args.type)));
|
get_arguments.append(QString("classId=%1").arg(getClassId(args.type)));
|
||||||
get_arguments.append(QString("index=%1").arg(args.offset));
|
get_arguments.append(QString("index=%1").arg(args.offset));
|
||||||
@ -97,20 +97,22 @@ class FlameAPI : public NetworkResourceAPI {
|
|||||||
if (args.sorting.has_value())
|
if (args.sorting.has_value())
|
||||||
get_arguments.append(QString("sortField=%1").arg(args.sorting.value().index));
|
get_arguments.append(QString("sortField=%1").arg(args.sorting.value().index));
|
||||||
get_arguments.append("sortOrder=desc");
|
get_arguments.append("sortOrder=desc");
|
||||||
if (args.loaders.has_value())
|
if (args.loaders.has_value() && args.loaders.value() != 0)
|
||||||
get_arguments.append(QString("modLoaderTypes=%1").arg(getModLoaderFilters(args.loaders.value())));
|
get_arguments.append(QString("modLoaderTypes=%1").arg(getModLoaderFilters(args.loaders.value())));
|
||||||
if (args.categoryIds.has_value() && !args.categoryIds->empty())
|
if (args.categoryIds.has_value() && !args.categoryIds->empty())
|
||||||
get_arguments.append(QString("categoryIds=[%1]").arg(args.categoryIds->join(",")));
|
get_arguments.append(QString("categoryIds=[%1]").arg(args.categoryIds->join(",")));
|
||||||
|
|
||||||
get_arguments.append(gameVersionStr);
|
if (args.versions.has_value() && !args.versions.value().empty())
|
||||||
|
get_arguments.append(QString("gameVersion=%1").arg(args.versions.value().front().toString()));
|
||||||
|
|
||||||
return "https://api.curseforge.com/v1/mods/search?gameId=432&" + get_arguments.join('&');
|
return "https://api.curseforge.com/v1/mods/search?gameId=432&" + get_arguments.join('&');
|
||||||
};
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
[[nodiscard]] std::optional<QString> getInfoURL(QString const& id) const override
|
[[nodiscard]] std::optional<QString> getInfoURL(QString const& id) const override
|
||||||
{
|
{
|
||||||
return QString("https://api.curseforge.com/v1/mods/%1").arg(id);
|
return QString("https://api.curseforge.com/v1/mods/%1").arg(id);
|
||||||
};
|
}
|
||||||
|
|
||||||
[[nodiscard]] std::optional<QString> getVersionsURL(VersionSearchArgs const& args) const override
|
[[nodiscard]] std::optional<QString> getVersionsURL(VersionSearchArgs const& args) const override
|
||||||
{
|
{
|
||||||
@ -125,7 +127,7 @@ class FlameAPI : public NetworkResourceAPI {
|
|||||||
url += QString("&modLoaderType=%1").arg(mappedModLoader);
|
url += QString("&modLoaderType=%1").arg(mappedModLoader);
|
||||||
}
|
}
|
||||||
return url;
|
return url;
|
||||||
};
|
}
|
||||||
|
|
||||||
[[nodiscard]] std::optional<QString> getDependencyURL(DependencySearchArgs const& args) const override
|
[[nodiscard]] std::optional<QString> getDependencyURL(DependencySearchArgs const& args) const override
|
||||||
{
|
{
|
||||||
@ -137,5 +139,5 @@ class FlameAPI : public NetworkResourceAPI {
|
|||||||
url += QString("&modLoaderType=%1").arg(mappedModLoader);
|
url += QString("&modLoaderType=%1").arg(mappedModLoader);
|
||||||
}
|
}
|
||||||
return url;
|
return url;
|
||||||
};
|
}
|
||||||
};
|
};
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
#include <QUrl>
|
#include <QUrl>
|
||||||
|
|
||||||
#include "Json.h"
|
#include "Json.h"
|
||||||
|
#include "modplatform/ModIndex.h"
|
||||||
|
|
||||||
void Flame::loadIndexedPack(Flame::IndexedPack& pack, QJsonObject& obj)
|
void Flame::loadIndexedPack(Flame::IndexedPack& pack, QJsonObject& obj)
|
||||||
{
|
{
|
||||||
@ -88,8 +89,27 @@ void Flame::loadIndexedPackVersions(Flame::IndexedPack& pack, QJsonArray& arr)
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (auto mcVer : versionArray) {
|
||||||
|
auto str = mcVer.toString();
|
||||||
|
|
||||||
|
if (str.contains('.'))
|
||||||
|
file.mcVersion.append(str);
|
||||||
|
|
||||||
|
if (auto loader = str.toLower(); loader == "neoforge")
|
||||||
|
file.loaders |= ModPlatform::NeoForge;
|
||||||
|
else if (loader == "forge")
|
||||||
|
file.loaders |= ModPlatform::Forge;
|
||||||
|
else if (loader == "cauldron")
|
||||||
|
file.loaders |= ModPlatform::Cauldron;
|
||||||
|
else if (loader == "liteloader")
|
||||||
|
file.loaders |= ModPlatform::LiteLoader;
|
||||||
|
else if (loader == "fabric")
|
||||||
|
file.loaders |= ModPlatform::Fabric;
|
||||||
|
else if (loader == "quilt")
|
||||||
|
file.loaders |= ModPlatform::Quilt;
|
||||||
|
}
|
||||||
|
|
||||||
// pick the latest version supported
|
// pick the latest version supported
|
||||||
file.mcVersion = versionArray[0].toString();
|
|
||||||
file.version = Json::requireString(version, "displayName");
|
file.version = Json::requireString(version, "displayName");
|
||||||
|
|
||||||
ModPlatform::IndexedVersionType::VersionType ver_type;
|
ModPlatform::IndexedVersionType::VersionType ver_type;
|
||||||
|
@ -18,6 +18,7 @@ struct IndexedVersion {
|
|||||||
int fileId;
|
int fileId;
|
||||||
QString version;
|
QString version;
|
||||||
ModPlatform::IndexedVersionType version_type;
|
ModPlatform::IndexedVersionType version_type;
|
||||||
|
ModPlatform::ModLoaderTypes loaders = {};
|
||||||
QString mcVersion;
|
QString mcVersion;
|
||||||
QString downloadUrl;
|
QString downloadUrl;
|
||||||
};
|
};
|
||||||
|
@ -129,7 +129,7 @@ Task::Ptr ModrinthAPI::getModCategories(std::shared_ptr<QByteArray> response)
|
|||||||
return netJob;
|
return netJob;
|
||||||
}
|
}
|
||||||
|
|
||||||
QList<ModPlatform::Category> ModrinthAPI::loadModCategories(std::shared_ptr<QByteArray> response)
|
QList<ModPlatform::Category> ModrinthAPI::loadCategories(std::shared_ptr<QByteArray> response, QString projectType)
|
||||||
{
|
{
|
||||||
QList<ModPlatform::Category> categories;
|
QList<ModPlatform::Category> categories;
|
||||||
QJsonParseError parse_error{};
|
QJsonParseError parse_error{};
|
||||||
@ -147,7 +147,7 @@ QList<ModPlatform::Category> ModrinthAPI::loadModCategories(std::shared_ptr<QByt
|
|||||||
for (auto val : arr) {
|
for (auto val : arr) {
|
||||||
auto cat = Json::requireObject(val);
|
auto cat = Json::requireObject(val);
|
||||||
auto name = Json::requireString(cat, "name");
|
auto name = Json::requireString(cat, "name");
|
||||||
if (Json::ensureString(cat, "project_type", "") == "mod")
|
if (Json::ensureString(cat, "project_type", "") == projectType)
|
||||||
categories.push_back({ name, name });
|
categories.push_back({ name, name });
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -157,4 +157,9 @@ QList<ModPlatform::Category> ModrinthAPI::loadModCategories(std::shared_ptr<QByt
|
|||||||
qDebug() << doc;
|
qDebug() << doc;
|
||||||
}
|
}
|
||||||
return categories;
|
return categories;
|
||||||
|
}
|
||||||
|
|
||||||
|
QList<ModPlatform::Category> ModrinthAPI::loadModCategories(std::shared_ptr<QByteArray> response)
|
||||||
|
{
|
||||||
|
return loadCategories(response, "mod");
|
||||||
};
|
};
|
@ -31,6 +31,7 @@ class ModrinthAPI : public NetworkResourceAPI {
|
|||||||
Task::Ptr getProjects(QStringList addonIds, std::shared_ptr<QByteArray> response) const override;
|
Task::Ptr getProjects(QStringList addonIds, std::shared_ptr<QByteArray> response) const override;
|
||||||
|
|
||||||
static Task::Ptr getModCategories(std::shared_ptr<QByteArray> response);
|
static Task::Ptr getModCategories(std::shared_ptr<QByteArray> response);
|
||||||
|
static QList<ModPlatform::Category> loadCategories(std::shared_ptr<QByteArray> response, QString projectType);
|
||||||
static QList<ModPlatform::Category> loadModCategories(std::shared_ptr<QByteArray> response);
|
static QList<ModPlatform::Category> loadModCategories(std::shared_ptr<QByteArray> response);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
@ -90,6 +91,8 @@ class ModrinthAPI : public NetworkResourceAPI {
|
|||||||
return "resourcepack";
|
return "resourcepack";
|
||||||
case ModPlatform::ResourceType::SHADER_PACK:
|
case ModPlatform::ResourceType::SHADER_PACK:
|
||||||
return "shader";
|
return "shader";
|
||||||
|
case ModPlatform::ResourceType::MODPACK:
|
||||||
|
return "modpack";
|
||||||
default:
|
default:
|
||||||
qWarning() << "Invalid resource type for Modrinth API!";
|
qWarning() << "Invalid resource type for Modrinth API!";
|
||||||
break;
|
break;
|
||||||
@ -102,9 +105,9 @@ class ModrinthAPI : public NetworkResourceAPI {
|
|||||||
{
|
{
|
||||||
QStringList facets_list;
|
QStringList facets_list;
|
||||||
|
|
||||||
if (args.loaders.has_value())
|
if (args.loaders.has_value() && args.loaders.value() != 0)
|
||||||
facets_list.append(QString("[%1]").arg(getModLoaderFilters(args.loaders.value())));
|
facets_list.append(QString("[%1]").arg(getModLoaderFilters(args.loaders.value())));
|
||||||
if (args.versions.has_value())
|
if (args.versions.has_value() && !args.versions.value().empty())
|
||||||
facets_list.append(QString("[%1]").arg(getGameVersionsArray(args.versions.value())));
|
facets_list.append(QString("[%1]").arg(getGameVersionsArray(args.versions.value())));
|
||||||
if (args.side.has_value()) {
|
if (args.side.has_value()) {
|
||||||
auto side = getSideFilters(args.side.value());
|
auto side = getSideFilters(args.side.value());
|
||||||
@ -122,7 +125,7 @@ class ModrinthAPI : public NetworkResourceAPI {
|
|||||||
public:
|
public:
|
||||||
[[nodiscard]] inline auto getSearchURL(SearchArgs const& args) const -> std::optional<QString> override
|
[[nodiscard]] inline auto getSearchURL(SearchArgs const& args) const -> std::optional<QString> override
|
||||||
{
|
{
|
||||||
if (args.loaders.has_value()) {
|
if (args.loaders.has_value() && args.loaders.value() != 0) {
|
||||||
if (!validateModLoaders(args.loaders.value())) {
|
if (!validateModLoaders(args.loaders.value())) {
|
||||||
qWarning() << "Modrinth - or our interface - does not support any the provided mod loaders!";
|
qWarning() << "Modrinth - or our interface - does not support any the provided mod loaders!";
|
||||||
return {};
|
return {};
|
||||||
@ -163,7 +166,7 @@ class ModrinthAPI : public NetworkResourceAPI {
|
|||||||
.arg(BuildConfig.MODRINTH_PROD_URL, args.pack.addonId.toString(), 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
|
QString getGameVersionsArray(std::list<Version> mcVersions) const
|
||||||
{
|
{
|
||||||
QString s;
|
QString s;
|
||||||
for (auto& ver : mcVersions) {
|
for (auto& ver : mcVersions) {
|
||||||
|
@ -135,6 +135,21 @@ auto loadIndexedVersion(QJsonObject& obj) -> ModpackVersion
|
|||||||
if (!gameVersions.isEmpty()) {
|
if (!gameVersions.isEmpty()) {
|
||||||
file.gameVersion = Json::ensureString(gameVersions[0]);
|
file.gameVersion = Json::ensureString(gameVersions[0]);
|
||||||
}
|
}
|
||||||
|
auto loaders = Json::requireArray(obj, "loaders");
|
||||||
|
for (auto loader : loaders) {
|
||||||
|
if (loader == "neoforge")
|
||||||
|
file.loaders |= ModPlatform::NeoForge;
|
||||||
|
else if (loader == "forge")
|
||||||
|
file.loaders |= ModPlatform::Forge;
|
||||||
|
else if (loader == "cauldron")
|
||||||
|
file.loaders |= ModPlatform::Cauldron;
|
||||||
|
else if (loader == "liteloader")
|
||||||
|
file.loaders |= ModPlatform::LiteLoader;
|
||||||
|
else if (loader == "fabric")
|
||||||
|
file.loaders |= ModPlatform::Fabric;
|
||||||
|
else if (loader == "quilt")
|
||||||
|
file.loaders |= ModPlatform::Quilt;
|
||||||
|
}
|
||||||
file.version_type = ModPlatform::IndexedVersionType(Json::requireString(obj, "version_type"));
|
file.version_type = ModPlatform::IndexedVersionType(Json::requireString(obj, "version_type"));
|
||||||
file.changelog = Json::ensureString(obj, "changelog");
|
file.changelog = Json::ensureString(obj, "changelog");
|
||||||
|
|
||||||
|
@ -87,6 +87,7 @@ struct ModpackVersion {
|
|||||||
QString gameVersion;
|
QString gameVersion;
|
||||||
ModPlatform::IndexedVersionType version_type;
|
ModPlatform::IndexedVersionType version_type;
|
||||||
QString changelog;
|
QString changelog;
|
||||||
|
ModPlatform::ModLoaderTypes loaders = {};
|
||||||
|
|
||||||
QString id;
|
QString id;
|
||||||
QString project_id;
|
QString project_id;
|
||||||
|
@ -104,18 +104,6 @@ bool checkSide(QString filter, QString value)
|
|||||||
return filter.isEmpty() || value.isEmpty() || filter == "both" || value == "both" || filter == value;
|
return filter.isEmpty() || value.isEmpty() || filter == "both" || value == "both" || filter == value;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool checkMcVersions(std::list<Version> filter, QStringList value)
|
|
||||||
{
|
|
||||||
bool valid = false;
|
|
||||||
for (auto mcVersion : filter) {
|
|
||||||
if (value.contains(mcVersion.toString())) {
|
|
||||||
valid = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return filter.empty() || valid;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ModModel::checkFilters(ModPlatform::IndexedPack::Ptr pack)
|
bool ModModel::checkFilters(ModPlatform::IndexedPack::Ptr pack)
|
||||||
{
|
{
|
||||||
if (!m_filter)
|
if (!m_filter)
|
||||||
@ -135,7 +123,7 @@ bool ModModel::checkVersionFilters(const ModPlatform::IndexedVersion& v)
|
|||||||
checkSide(m_filter->side, v.side) && // side
|
checkSide(m_filter->side, v.side) && // side
|
||||||
(m_filter->releases.empty() || // releases
|
(m_filter->releases.empty() || // releases
|
||||||
std::find(m_filter->releases.cbegin(), m_filter->releases.cend(), v.version_type) != m_filter->releases.cend()) &&
|
std::find(m_filter->releases.cbegin(), m_filter->releases.cend(), v.version_type) != m_filter->releases.cend()) &&
|
||||||
checkMcVersions(m_filter->versions, v.mcVersion)); // mcVersions
|
m_filter->checkMcVersions(v.mcVersion)); // mcVersions
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace ResourceDownload
|
} // namespace ResourceDownload
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
#include "FlameModel.h"
|
#include "FlameModel.h"
|
||||||
#include <Json.h>
|
#include <Json.h>
|
||||||
#include "Application.h"
|
#include "Application.h"
|
||||||
|
#include "modplatform/ModIndex.h"
|
||||||
#include "modplatform/ResourceAPI.h"
|
#include "modplatform/ResourceAPI.h"
|
||||||
#include "modplatform/flame/FlameAPI.h"
|
#include "modplatform/flame/FlameAPI.h"
|
||||||
#include "ui/widgets/ProjectItem.h"
|
#include "ui/widgets/ProjectItem.h"
|
||||||
@ -183,34 +184,28 @@ void ListModel::performPaginatedSearch()
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
auto netJob = makeShared<NetJob>("Flame::Search", APPLICATION->network());
|
ResourceAPI::SortingMethod sort{};
|
||||||
auto searchUrl = QString(
|
sort.index = currentSort + 1;
|
||||||
"https://api.curseforge.com/v1/mods/search?"
|
|
||||||
"gameId=432&"
|
|
||||||
"classId=4471&"
|
|
||||||
"index=%1&"
|
|
||||||
"pageSize=25&"
|
|
||||||
"searchFilter=%2&"
|
|
||||||
"sortField=%3&"
|
|
||||||
"sortOrder=desc")
|
|
||||||
.arg(nextSearchOffset)
|
|
||||||
.arg(currentSearchTerm)
|
|
||||||
.arg(currentSort + 1);
|
|
||||||
|
|
||||||
netJob->addNetAction(Net::ApiDownload::makeByteArray(QUrl(searchUrl), response));
|
auto netJob = makeShared<NetJob>("Flame::Search", APPLICATION->network());
|
||||||
|
auto searchUrl = FlameAPI().getSearchURL({ ModPlatform::ResourceType::MODPACK, nextSearchOffset, currentSearchTerm, sort,
|
||||||
|
m_filter->loaders, m_filter->versions, "", m_filter->categoryIds });
|
||||||
|
|
||||||
|
netJob->addNetAction(Net::ApiDownload::makeByteArray(QUrl(searchUrl.value()), response));
|
||||||
jobPtr = netJob;
|
jobPtr = netJob;
|
||||||
jobPtr->start();
|
jobPtr->start();
|
||||||
QObject::connect(netJob.get(), &NetJob::succeeded, this, &ListModel::searchRequestFinished);
|
QObject::connect(netJob.get(), &NetJob::succeeded, this, &ListModel::searchRequestFinished);
|
||||||
QObject::connect(netJob.get(), &NetJob::failed, this, &ListModel::searchRequestFailed);
|
QObject::connect(netJob.get(), &NetJob::failed, this, &ListModel::searchRequestFailed);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ListModel::searchWithTerm(const QString& term, int sort)
|
void ListModel::searchWithTerm(const QString& term, int sort, std::shared_ptr<ModFilterWidget::Filter> filter, bool filterChanged)
|
||||||
{
|
{
|
||||||
if (currentSearchTerm == term && currentSearchTerm.isNull() == term.isNull() && currentSort == sort) {
|
if (currentSearchTerm == term && currentSearchTerm.isNull() == term.isNull() && currentSort == sort && !filterChanged) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
currentSearchTerm = term;
|
currentSearchTerm = term;
|
||||||
currentSort = sort;
|
currentSort = sort;
|
||||||
|
m_filter = filter;
|
||||||
if (hasActiveSearchJob()) {
|
if (hasActiveSearchJob()) {
|
||||||
jobPtr->abort();
|
jobPtr->abort();
|
||||||
searchState = ResetRequested;
|
searchState = ResetRequested;
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
|
|
||||||
#include <net/NetJob.h>
|
#include <net/NetJob.h>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
#include "ui/widgets/ModFilterWidget.h"
|
||||||
|
|
||||||
#include <modplatform/flame/FlamePackIndex.h>
|
#include <modplatform/flame/FlamePackIndex.h>
|
||||||
|
|
||||||
@ -38,7 +39,7 @@ class ListModel : public QAbstractListModel {
|
|||||||
void fetchMore(const QModelIndex& parent) override;
|
void fetchMore(const QModelIndex& parent) override;
|
||||||
|
|
||||||
void getLogo(const QString& logo, const QString& logoUrl, LogoCallback callback);
|
void getLogo(const QString& logo, const QString& logoUrl, LogoCallback callback);
|
||||||
void searchWithTerm(const QString& term, int sort);
|
void searchWithTerm(const QString& term, int sort, std::shared_ptr<ModFilterWidget::Filter> filter, bool filterChanged);
|
||||||
|
|
||||||
[[nodiscard]] bool hasActiveSearchJob() const { return jobPtr && jobPtr->isRunning(); }
|
[[nodiscard]] bool hasActiveSearchJob() const { return jobPtr && jobPtr->isRunning(); }
|
||||||
[[nodiscard]] Task::Ptr activeSearchJob() { return hasActiveSearchJob() ? jobPtr : nullptr; }
|
[[nodiscard]] Task::Ptr activeSearchJob() { return hasActiveSearchJob() ? jobPtr : nullptr; }
|
||||||
@ -65,6 +66,7 @@ class ListModel : public QAbstractListModel {
|
|||||||
|
|
||||||
QString currentSearchTerm;
|
QString currentSearchTerm;
|
||||||
int currentSort = 0;
|
int currentSort = 0;
|
||||||
|
std::shared_ptr<ModFilterWidget::Filter> m_filter;
|
||||||
int nextSearchOffset = 0;
|
int nextSearchOffset = 0;
|
||||||
enum SearchState { None, CanPossiblyFetchMore, ResetRequested, Finished } searchState = None;
|
enum SearchState { None, CanPossiblyFetchMore, ResetRequested, Finished } searchState = None;
|
||||||
Task::Ptr jobPtr;
|
Task::Ptr jobPtr;
|
||||||
|
@ -34,10 +34,14 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "FlamePage.h"
|
#include "FlamePage.h"
|
||||||
|
#include "Version.h"
|
||||||
|
#include "modplatform/flame/FlamePackIndex.h"
|
||||||
#include "ui/dialogs/CustomMessageBox.h"
|
#include "ui/dialogs/CustomMessageBox.h"
|
||||||
|
#include "ui/widgets/ModFilterWidget.h"
|
||||||
#include "ui_FlamePage.h"
|
#include "ui_FlamePage.h"
|
||||||
|
|
||||||
#include <QKeyEvent>
|
#include <QKeyEvent>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
#include "Application.h"
|
#include "Application.h"
|
||||||
#include "FlameModel.h"
|
#include "FlameModel.h"
|
||||||
@ -88,6 +92,7 @@ FlamePage::FlamePage(NewInstanceDialog* dialog, QWidget* parent)
|
|||||||
|
|
||||||
ui->packView->setItemDelegate(new ProjectItemDelegate(this));
|
ui->packView->setItemDelegate(new ProjectItemDelegate(this));
|
||||||
ui->packDescription->setMetaEntry("FlamePacks");
|
ui->packDescription->setMetaEntry("FlamePacks");
|
||||||
|
createFilterWidget();
|
||||||
}
|
}
|
||||||
|
|
||||||
FlamePage::~FlamePage()
|
FlamePage::~FlamePage()
|
||||||
@ -131,10 +136,25 @@ void FlamePage::openedImpl()
|
|||||||
|
|
||||||
void FlamePage::triggerSearch()
|
void FlamePage::triggerSearch()
|
||||||
{
|
{
|
||||||
listModel->searchWithTerm(ui->searchEdit->text(), ui->sortByBox->currentIndex());
|
ui->packView->selectionModel()->setCurrentIndex({}, QItemSelectionModel::SelectionFlag::ClearAndSelect);
|
||||||
|
ui->packView->clearSelection();
|
||||||
|
ui->packDescription->clear();
|
||||||
|
ui->versionSelectionBox->clear();
|
||||||
|
listModel->searchWithTerm(ui->searchEdit->text(), ui->sortByBox->currentIndex(), m_filterWidget->getFilter(),
|
||||||
|
m_filterWidget->changed());
|
||||||
m_fetch_progress.watch(listModel->activeSearchJob().get());
|
m_fetch_progress.watch(listModel->activeSearchJob().get());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool checkVersionFilters(const Flame::IndexedVersion& v, std::shared_ptr<ModFilterWidget::Filter> filter)
|
||||||
|
{
|
||||||
|
if (!filter)
|
||||||
|
return true;
|
||||||
|
return ((!filter->loaders || !v.loaders || filter->loaders & v.loaders) && // loaders
|
||||||
|
(filter->releases.empty() || // releases
|
||||||
|
std::find(filter->releases.cbegin(), filter->releases.cend(), v.version_type) != filter->releases.cend()) &&
|
||||||
|
filter->checkMcVersions({ v.mcVersion })); // mcVersions}
|
||||||
|
}
|
||||||
|
|
||||||
void FlamePage::onSelectionChanged(QModelIndex curr, [[maybe_unused]] QModelIndex prev)
|
void FlamePage::onSelectionChanged(QModelIndex curr, [[maybe_unused]] QModelIndex prev)
|
||||||
{
|
{
|
||||||
ui->versionSelectionBox->clear();
|
ui->versionSelectionBox->clear();
|
||||||
@ -148,7 +168,7 @@ void FlamePage::onSelectionChanged(QModelIndex curr, [[maybe_unused]] QModelInde
|
|||||||
|
|
||||||
current = listModel->data(curr, Qt::UserRole).value<Flame::IndexedPack>();
|
current = listModel->data(curr, Qt::UserRole).value<Flame::IndexedPack>();
|
||||||
|
|
||||||
if (current.versionsLoaded == false) {
|
if (!current.versionsLoaded || m_filterWidget->changed()) {
|
||||||
qDebug() << "Loading flame modpack versions";
|
qDebug() << "Loading flame modpack versions";
|
||||||
auto netJob = new NetJob(QString("Flame::PackVersions(%1)").arg(current.name), APPLICATION->network());
|
auto netJob = new NetJob(QString("Flame::PackVersions(%1)").arg(current.name), APPLICATION->network());
|
||||||
auto response = std::make_shared<QByteArray>();
|
auto response = std::make_shared<QByteArray>();
|
||||||
@ -176,6 +196,16 @@ void FlamePage::onSelectionChanged(QModelIndex curr, [[maybe_unused]] QModelInde
|
|||||||
qWarning() << "Error while reading flame modpack version: " << e.cause();
|
qWarning() << "Error while reading flame modpack version: " << e.cause();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto pred = [this](const Flame::IndexedVersion& v) { return !checkVersionFilters(v, m_filterWidget->getFilter()); };
|
||||||
|
#if QT_VERSION >= QT_VERSION_CHECK(6, 1, 0)
|
||||||
|
current.versions.removeIf(pred);
|
||||||
|
#else
|
||||||
|
for (auto it = current.versions.begin(); it != current.versions.end();)
|
||||||
|
if (pred(*it))
|
||||||
|
it = current.versions.erase(it);
|
||||||
|
else
|
||||||
|
++it;
|
||||||
|
#endif
|
||||||
for (auto version : current.versions) {
|
for (auto version : current.versions) {
|
||||||
auto release_type = version.version_type.isValid() ? QString(" [%1]").arg(version.version_type.toString()) : "";
|
auto release_type = version.version_type.isValid() ? QString(" [%1]").arg(version.version_type.toString()) : "";
|
||||||
auto mcVersion = !version.mcVersion.isEmpty() && !version.version.contains(version.mcVersion)
|
auto mcVersion = !version.mcVersion.isEmpty() && !version.version.contains(version.mcVersion)
|
||||||
@ -308,3 +338,25 @@ void FlamePage::setSearchTerm(QString term)
|
|||||||
{
|
{
|
||||||
ui->searchEdit->setText(term);
|
ui->searchEdit->setText(term);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void FlamePage::createFilterWidget()
|
||||||
|
{
|
||||||
|
auto widget = ModFilterWidget::create(nullptr, false, this);
|
||||||
|
m_filterWidget.swap(widget);
|
||||||
|
auto old = ui->splitter->replaceWidget(0, m_filterWidget.get());
|
||||||
|
// because we replaced the widget we also need to delete it
|
||||||
|
if (old) {
|
||||||
|
delete old;
|
||||||
|
}
|
||||||
|
|
||||||
|
connect(ui->filterButton, &QPushButton::clicked, this, [this] { m_filterWidget->setHidden(!m_filterWidget->isHidden()); });
|
||||||
|
|
||||||
|
connect(m_filterWidget.get(), &ModFilterWidget::filterChanged, this, &FlamePage::triggerSearch);
|
||||||
|
auto response = std::make_shared<QByteArray>();
|
||||||
|
m_categoriesTask = FlameAPI::getCategories(response, ModPlatform::ResourceType::MODPACK);
|
||||||
|
QObject::connect(m_categoriesTask.get(), &Task::succeeded, [this, response]() {
|
||||||
|
auto categories = FlameAPI::loadModCategories(response);
|
||||||
|
m_filterWidget->setCategories(categories);
|
||||||
|
});
|
||||||
|
m_categoriesTask->start();
|
||||||
|
}
|
||||||
|
@ -41,6 +41,7 @@
|
|||||||
#include <modplatform/flame/FlamePackIndex.h>
|
#include <modplatform/flame/FlamePackIndex.h>
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
#include "ui/pages/modplatform/ModpackProviderBasePage.h"
|
#include "ui/pages/modplatform/ModpackProviderBasePage.h"
|
||||||
|
#include "ui/widgets/ModFilterWidget.h"
|
||||||
#include "ui/widgets/ProgressWidget.h"
|
#include "ui/widgets/ProgressWidget.h"
|
||||||
|
|
||||||
namespace Ui {
|
namespace Ui {
|
||||||
@ -84,6 +85,7 @@ class FlamePage : public QWidget, public ModpackProviderBasePage {
|
|||||||
void triggerSearch();
|
void triggerSearch();
|
||||||
void onSelectionChanged(QModelIndex first, QModelIndex second);
|
void onSelectionChanged(QModelIndex first, QModelIndex second);
|
||||||
void onVersionSelectionChanged(int index);
|
void onVersionSelectionChanged(int index);
|
||||||
|
void createFilterWidget();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Ui::FlamePage* ui = nullptr;
|
Ui::FlamePage* ui = nullptr;
|
||||||
@ -97,4 +99,7 @@ class FlamePage : public QWidget, public ModpackProviderBasePage {
|
|||||||
|
|
||||||
// Used to do instant searching with a delay to cache quick changes
|
// Used to do instant searching with a delay to cache quick changes
|
||||||
QTimer m_search_timer;
|
QTimer m_search_timer;
|
||||||
|
|
||||||
|
unique_qobject_ptr<ModFilterWidget> m_filterWidget;
|
||||||
|
Task::Ptr m_categoriesTask;
|
||||||
};
|
};
|
||||||
|
@ -29,6 +29,8 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item>
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||||
<item>
|
<item>
|
||||||
<widget class="QLineEdit" name="searchEdit">
|
<widget class="QLineEdit" name="searchEdit">
|
||||||
<property name="placeholderText">
|
<property name="placeholderText">
|
||||||
@ -37,8 +39,26 @@
|
|||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<layout class="QHBoxLayout">
|
<widget class="QPushButton" name="filterButton">
|
||||||
|
<property name="text">
|
||||||
|
<string>Filter</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
<item>
|
<item>
|
||||||
|
<widget class="QSplitter" name="splitter">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<widget class="QWidget" name="widget" native="true"/>
|
||||||
<widget class="QListView" name="packView">
|
<widget class="QListView" name="packView">
|
||||||
<property name="horizontalScrollBarPolicy">
|
<property name="horizontalScrollBarPolicy">
|
||||||
<enum>Qt::ScrollBarAlwaysOff</enum>
|
<enum>Qt::ScrollBarAlwaysOff</enum>
|
||||||
@ -53,8 +73,6 @@
|
|||||||
</size>
|
</size>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="ProjectDescriptionPage" name="packDescription">
|
<widget class="ProjectDescriptionPage" name="packDescription">
|
||||||
<property name="openExternalLinks">
|
<property name="openExternalLinks">
|
||||||
<bool>true</bool>
|
<bool>true</bool>
|
||||||
@ -63,8 +81,7 @@
|
|||||||
<bool>true</bool>
|
<bool>true</bool>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</widget>
|
||||||
</layout>
|
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<layout class="QHBoxLayout">
|
<layout class="QHBoxLayout">
|
||||||
|
@ -215,11 +215,11 @@ unique_qobject_ptr<ModFilterWidget> FlameModPage::createFilterWidget()
|
|||||||
void FlameModPage::prepareProviderCategories()
|
void FlameModPage::prepareProviderCategories()
|
||||||
{
|
{
|
||||||
auto response = std::make_shared<QByteArray>();
|
auto response = std::make_shared<QByteArray>();
|
||||||
auto task = FlameAPI::getModCategories(response);
|
m_categoriesTask = FlameAPI::getModCategories(response);
|
||||||
QObject::connect(task.get(), &Task::succeeded, [this, response]() {
|
QObject::connect(m_categoriesTask.get(), &Task::succeeded, [this, response]() {
|
||||||
auto categories = FlameAPI::loadModCategories(response);
|
auto categories = FlameAPI::loadModCategories(response);
|
||||||
m_filter_widget->setCategories(categories);
|
m_filter_widget->setCategories(categories);
|
||||||
});
|
});
|
||||||
task->start();
|
m_categoriesTask->start();
|
||||||
};
|
};
|
||||||
} // namespace ResourceDownload
|
} // namespace ResourceDownload
|
||||||
|
@ -100,6 +100,9 @@ class FlameModPage : public ModPage {
|
|||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual void prepareProviderCategories() override;
|
virtual void prepareProviderCategories() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
Task::Ptr m_categoriesTask;
|
||||||
};
|
};
|
||||||
|
|
||||||
class FlameResourcePackPage : public ResourcePackResourcePage {
|
class FlameResourcePackPage : public ResourcePackResourcePage {
|
||||||
|
@ -152,33 +152,26 @@ void ModpackListModel::performPaginatedSearch()
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
} // TODO: Move to standalone API
|
} // TODO: Move to standalone API
|
||||||
auto netJob = makeShared<NetJob>("Modrinth::SearchModpack", APPLICATION->network());
|
ResourceAPI::SortingMethod sort{};
|
||||||
auto searchAllUrl = QString(BuildConfig.MODRINTH_PROD_URL +
|
sort.name = currentSort;
|
||||||
"/search?"
|
auto searchUrl = ModrinthAPI().getSearchURL({ ModPlatform::ResourceType::MODPACK, nextSearchOffset, currentSearchTerm, sort,
|
||||||
"offset=%1&"
|
m_filter->loaders, m_filter->versions, "", m_filter->categoryIds });
|
||||||
"limit=%2&"
|
|
||||||
"query=%3&"
|
|
||||||
"index=%4&"
|
|
||||||
"facets=[[\"project_type:modpack\"]]")
|
|
||||||
.arg(nextSearchOffset)
|
|
||||||
.arg(m_modpacks_per_page)
|
|
||||||
.arg(currentSearchTerm)
|
|
||||||
.arg(currentSort);
|
|
||||||
|
|
||||||
netJob->addNetAction(Net::ApiDownload::makeByteArray(QUrl(searchAllUrl), m_all_response));
|
auto netJob = makeShared<NetJob>("Modrinth::SearchModpack", APPLICATION->network());
|
||||||
|
netJob->addNetAction(Net::ApiDownload::makeByteArray(QUrl(searchUrl.value()), m_allResponse));
|
||||||
|
|
||||||
QObject::connect(netJob.get(), &NetJob::succeeded, this, [this] {
|
QObject::connect(netJob.get(), &NetJob::succeeded, this, [this] {
|
||||||
QJsonParseError parse_error_all{};
|
QJsonParseError parseError{};
|
||||||
|
|
||||||
QJsonDocument doc_all = QJsonDocument::fromJson(*m_all_response, &parse_error_all);
|
QJsonDocument doc = QJsonDocument::fromJson(*m_allResponse, &parseError);
|
||||||
if (parse_error_all.error != QJsonParseError::NoError) {
|
if (parseError.error != QJsonParseError::NoError) {
|
||||||
qWarning() << "Error while parsing JSON response from " << debugName() << " at " << parse_error_all.offset
|
qWarning() << "Error while parsing JSON response from " << debugName() << " at " << parseError.offset
|
||||||
<< " reason: " << parse_error_all.errorString();
|
<< " reason: " << parseError.errorString();
|
||||||
qWarning() << *m_all_response;
|
qWarning() << *m_allResponse;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
searchRequestFinished(doc_all);
|
searchRequestFinished(doc);
|
||||||
});
|
});
|
||||||
QObject::connect(netJob.get(), &NetJob::failed, this, &ModpackListModel::searchRequestFailed);
|
QObject::connect(netJob.get(), &NetJob::failed, this, &ModpackListModel::searchRequestFailed);
|
||||||
|
|
||||||
@ -220,19 +213,23 @@ static auto sortFromIndex(int index) -> QString
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ModpackListModel::searchWithTerm(const QString& term, const int sort)
|
void ModpackListModel::searchWithTerm(const QString& term,
|
||||||
|
const int sort,
|
||||||
|
std::shared_ptr<ModFilterWidget::Filter> filter,
|
||||||
|
bool filterChanged)
|
||||||
{
|
{
|
||||||
if (sort > 5 || sort < 0)
|
if (sort > 5 || sort < 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
auto sort_str = sortFromIndex(sort);
|
auto sort_str = sortFromIndex(sort);
|
||||||
|
|
||||||
if (currentSearchTerm == term && currentSearchTerm.isNull() == term.isNull() && currentSort == sort_str) {
|
if (currentSearchTerm == term && currentSearchTerm.isNull() == term.isNull() && currentSort == sort_str && !filterChanged) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
currentSearchTerm = term;
|
currentSearchTerm = term;
|
||||||
currentSort = sort_str;
|
currentSort = sort_str;
|
||||||
|
m_filter = filter;
|
||||||
|
|
||||||
refresh();
|
refresh();
|
||||||
}
|
}
|
||||||
|
@ -71,7 +71,7 @@ class ModpackListModel : public QAbstractListModel {
|
|||||||
/* Ask the API for more information */
|
/* Ask the API for more information */
|
||||||
void fetchMore(const QModelIndex& parent) override;
|
void fetchMore(const QModelIndex& parent) override;
|
||||||
void refresh();
|
void refresh();
|
||||||
void searchWithTerm(const QString& term, int sort);
|
void searchWithTerm(const QString& term, int sort, std::shared_ptr<ModFilterWidget::Filter> filter, bool filterChanged);
|
||||||
|
|
||||||
[[nodiscard]] bool hasActiveSearchJob() const { return jobPtr && jobPtr->isRunning(); }
|
[[nodiscard]] bool hasActiveSearchJob() const { return jobPtr && jobPtr->isRunning(); }
|
||||||
[[nodiscard]] Task::Ptr activeSearchJob() { return hasActiveSearchJob() ? jobPtr : nullptr; }
|
[[nodiscard]] Task::Ptr activeSearchJob() { return hasActiveSearchJob() ? jobPtr : nullptr; }
|
||||||
@ -112,12 +112,13 @@ class ModpackListModel : public QAbstractListModel {
|
|||||||
|
|
||||||
QString currentSearchTerm;
|
QString currentSearchTerm;
|
||||||
QString currentSort;
|
QString currentSort;
|
||||||
|
std::shared_ptr<ModFilterWidget::Filter> m_filter;
|
||||||
int nextSearchOffset = 0;
|
int nextSearchOffset = 0;
|
||||||
enum SearchState { None, CanPossiblyFetchMore, ResetRequested, Finished } searchState = None;
|
enum SearchState { None, CanPossiblyFetchMore, ResetRequested, Finished } searchState = None;
|
||||||
|
|
||||||
Task::Ptr jobPtr;
|
Task::Ptr jobPtr;
|
||||||
|
|
||||||
std::shared_ptr<QByteArray> m_all_response = std::make_shared<QByteArray>();
|
std::shared_ptr<QByteArray> m_allResponse = std::make_shared<QByteArray>();
|
||||||
QByteArray m_specific_response;
|
QByteArray m_specific_response;
|
||||||
|
|
||||||
int m_modpacks_per_page = 20;
|
int m_modpacks_per_page = 20;
|
||||||
|
@ -35,6 +35,8 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "ModrinthPage.h"
|
#include "ModrinthPage.h"
|
||||||
|
#include "Version.h"
|
||||||
|
#include "modplatform/modrinth/ModrinthAPI.h"
|
||||||
#include "ui/dialogs/CustomMessageBox.h"
|
#include "ui/dialogs/CustomMessageBox.h"
|
||||||
#include "ui_ModrinthPage.h"
|
#include "ui_ModrinthPage.h"
|
||||||
|
|
||||||
@ -58,6 +60,7 @@ ModrinthPage::ModrinthPage(NewInstanceDialog* dialog, QWidget* parent)
|
|||||||
: QWidget(parent), ui(new Ui::ModrinthPage), dialog(dialog), m_fetch_progress(this, false)
|
: QWidget(parent), ui(new Ui::ModrinthPage), dialog(dialog), m_fetch_progress(this, false)
|
||||||
{
|
{
|
||||||
ui->setupUi(this);
|
ui->setupUi(this);
|
||||||
|
createFilterWidget();
|
||||||
|
|
||||||
ui->searchEdit->installEventFilter(this);
|
ui->searchEdit->installEventFilter(this);
|
||||||
m_model = new Modrinth::ModpackListModel(this);
|
m_model = new Modrinth::ModpackListModel(this);
|
||||||
@ -126,6 +129,16 @@ bool ModrinthPage::eventFilter(QObject* watched, QEvent* event)
|
|||||||
return QObject::eventFilter(watched, event);
|
return QObject::eventFilter(watched, event);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool checkVersionFilters(const Modrinth::ModpackVersion& v, std::shared_ptr<ModFilterWidget::Filter> filter)
|
||||||
|
{
|
||||||
|
if (!filter)
|
||||||
|
return true;
|
||||||
|
return ((!filter->loaders || !v.loaders || filter->loaders & v.loaders) && // loaders
|
||||||
|
(filter->releases.empty() || // releases
|
||||||
|
std::find(filter->releases.cbegin(), filter->releases.cend(), v.version_type) != filter->releases.cend()) &&
|
||||||
|
filter->checkMcVersions({ v.gameVersion })); // gameVersion}
|
||||||
|
}
|
||||||
|
|
||||||
void ModrinthPage::onSelectionChanged(QModelIndex curr, [[maybe_unused]] QModelIndex prev)
|
void ModrinthPage::onSelectionChanged(QModelIndex curr, [[maybe_unused]] QModelIndex prev)
|
||||||
{
|
{
|
||||||
ui->versionSelectionBox->clear();
|
ui->versionSelectionBox->clear();
|
||||||
@ -190,7 +203,7 @@ void ModrinthPage::onSelectionChanged(QModelIndex curr, [[maybe_unused]] QModelI
|
|||||||
} else
|
} else
|
||||||
updateUI();
|
updateUI();
|
||||||
|
|
||||||
if (!current.versionsLoaded) {
|
if (!current.versionsLoaded || m_filterWidget->changed()) {
|
||||||
qDebug() << "Loading modrinth modpack versions";
|
qDebug() << "Loading modrinth modpack versions";
|
||||||
|
|
||||||
auto netJob = new NetJob(QString("Modrinth::PackVersions(%1)").arg(current.name), APPLICATION->network());
|
auto netJob = new NetJob(QString("Modrinth::PackVersions(%1)").arg(current.name), APPLICATION->network());
|
||||||
@ -221,6 +234,16 @@ void ModrinthPage::onSelectionChanged(QModelIndex curr, [[maybe_unused]] QModelI
|
|||||||
qDebug() << *response;
|
qDebug() << *response;
|
||||||
qWarning() << "Error while reading modrinth modpack version: " << e.cause();
|
qWarning() << "Error while reading modrinth modpack version: " << e.cause();
|
||||||
}
|
}
|
||||||
|
auto pred = [this](const Modrinth::ModpackVersion& v) { return !checkVersionFilters(v, m_filterWidget->getFilter()); };
|
||||||
|
#if QT_VERSION >= QT_VERSION_CHECK(6, 1, 0)
|
||||||
|
current.versions.removeIf(pred);
|
||||||
|
#else
|
||||||
|
for (auto it = current.versions.begin(); it != current.versions.end();)
|
||||||
|
if (pred(*it))
|
||||||
|
it = current.versions.erase(it);
|
||||||
|
else
|
||||||
|
++it;
|
||||||
|
#endif
|
||||||
for (auto version : current.versions) {
|
for (auto version : current.versions) {
|
||||||
auto release_type = version.version_type.isValid() ? QString(" [%1]").arg(version.version_type.toString()) : "";
|
auto release_type = version.version_type.isValid() ? QString(" [%1]").arg(version.version_type.toString()) : "";
|
||||||
auto mcVersion = !version.gameVersion.isEmpty() && !version.name.contains(version.gameVersion)
|
auto mcVersion = !version.gameVersion.isEmpty() && !version.name.contains(version.gameVersion)
|
||||||
@ -338,7 +361,11 @@ void ModrinthPage::suggestCurrent()
|
|||||||
|
|
||||||
void ModrinthPage::triggerSearch()
|
void ModrinthPage::triggerSearch()
|
||||||
{
|
{
|
||||||
m_model->searchWithTerm(ui->searchEdit->text(), ui->sortByBox->currentIndex());
|
ui->packView->selectionModel()->setCurrentIndex({}, QItemSelectionModel::SelectionFlag::ClearAndSelect);
|
||||||
|
ui->packView->clearSelection();
|
||||||
|
ui->packDescription->clear();
|
||||||
|
ui->versionSelectionBox->clear();
|
||||||
|
m_model->searchWithTerm(ui->searchEdit->text(), ui->sortByBox->currentIndex(), m_filterWidget->getFilter(), m_filterWidget->changed());
|
||||||
m_fetch_progress.watch(m_model->activeSearchJob().get());
|
m_fetch_progress.watch(m_model->activeSearchJob().get());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -361,3 +388,25 @@ QString ModrinthPage::getSerachTerm() const
|
|||||||
{
|
{
|
||||||
return ui->searchEdit->text();
|
return ui->searchEdit->text();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ModrinthPage::createFilterWidget()
|
||||||
|
{
|
||||||
|
auto widget = ModFilterWidget::create(nullptr, true, this);
|
||||||
|
m_filterWidget.swap(widget);
|
||||||
|
auto old = ui->splitter->replaceWidget(0, m_filterWidget.get());
|
||||||
|
// because we replaced the widget we also need to delete it
|
||||||
|
if (old) {
|
||||||
|
delete old;
|
||||||
|
}
|
||||||
|
|
||||||
|
connect(ui->filterButton, &QPushButton::clicked, this, [this] { m_filterWidget->setHidden(!m_filterWidget->isHidden()); });
|
||||||
|
|
||||||
|
connect(m_filterWidget.get(), &ModFilterWidget::filterChanged, this, &ModrinthPage::triggerSearch);
|
||||||
|
auto response = std::make_shared<QByteArray>();
|
||||||
|
m_categoriesTask = ModrinthAPI::getModCategories(response);
|
||||||
|
QObject::connect(m_categoriesTask.get(), &Task::succeeded, [this, response]() {
|
||||||
|
auto categories = ModrinthAPI::loadCategories(response, "modpack");
|
||||||
|
m_filterWidget->setCategories(categories);
|
||||||
|
});
|
||||||
|
m_categoriesTask->start();
|
||||||
|
}
|
@ -41,6 +41,7 @@
|
|||||||
|
|
||||||
#include "modplatform/modrinth/ModrinthPackManifest.h"
|
#include "modplatform/modrinth/ModrinthPackManifest.h"
|
||||||
#include "ui/pages/modplatform/ModpackProviderBasePage.h"
|
#include "ui/pages/modplatform/ModpackProviderBasePage.h"
|
||||||
|
#include "ui/widgets/ModFilterWidget.h"
|
||||||
#include "ui/widgets/ProgressWidget.h"
|
#include "ui/widgets/ProgressWidget.h"
|
||||||
|
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
@ -87,6 +88,7 @@ class ModrinthPage : public QWidget, public ModpackProviderBasePage {
|
|||||||
void onSelectionChanged(QModelIndex first, QModelIndex second);
|
void onSelectionChanged(QModelIndex first, QModelIndex second);
|
||||||
void onVersionSelectionChanged(int index);
|
void onVersionSelectionChanged(int index);
|
||||||
void triggerSearch();
|
void triggerSearch();
|
||||||
|
void createFilterWidget();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Ui::ModrinthPage* ui;
|
Ui::ModrinthPage* ui;
|
||||||
@ -100,4 +102,7 @@ class ModrinthPage : public QWidget, public ModpackProviderBasePage {
|
|||||||
|
|
||||||
// Used to do instant searching with a delay to cache quick changes
|
// Used to do instant searching with a delay to cache quick changes
|
||||||
QTimer m_search_timer;
|
QTimer m_search_timer;
|
||||||
|
|
||||||
|
unique_qobject_ptr<ModFilterWidget> m_filterWidget;
|
||||||
|
Task::Ptr m_categoriesTask;
|
||||||
};
|
};
|
||||||
|
@ -11,6 +11,8 @@
|
|||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<layout class="QVBoxLayout" name="verticalLayout">
|
<layout class="QVBoxLayout" name="verticalLayout">
|
||||||
|
<item>
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||||
<item>
|
<item>
|
||||||
<widget class="QLineEdit" name="searchEdit">
|
<widget class="QLineEdit" name="searchEdit">
|
||||||
<property name="placeholderText">
|
<property name="placeholderText">
|
||||||
@ -19,8 +21,26 @@
|
|||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<layout class="QHBoxLayout">
|
<widget class="QPushButton" name="filterButton">
|
||||||
|
<property name="text">
|
||||||
|
<string>Filter</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
<item>
|
<item>
|
||||||
|
<widget class="QSplitter" name="splitter">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<widget class="QWidget" name="widget" native="true"/>
|
||||||
<widget class="QListView" name="packView">
|
<widget class="QListView" name="packView">
|
||||||
<property name="horizontalScrollBarPolicy">
|
<property name="horizontalScrollBarPolicy">
|
||||||
<enum>Qt::ScrollBarAlwaysOff</enum>
|
<enum>Qt::ScrollBarAlwaysOff</enum>
|
||||||
@ -35,8 +55,6 @@
|
|||||||
</size>
|
</size>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="ProjectDescriptionPage" name="packDescription">
|
<widget class="ProjectDescriptionPage" name="packDescription">
|
||||||
<property name="openExternalLinks">
|
<property name="openExternalLinks">
|
||||||
<bool>true</bool>
|
<bool>true</bool>
|
||||||
@ -45,8 +63,7 @@
|
|||||||
<bool>true</bool>
|
<bool>true</bool>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</widget>
|
||||||
</layout>
|
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<layout class="QHBoxLayout">
|
<layout class="QHBoxLayout">
|
||||||
|
@ -64,10 +64,49 @@ class VersionBasicModel : public QIdentityProxyModel {
|
|||||||
{
|
{
|
||||||
if (role == Qt::DisplayRole)
|
if (role == Qt::DisplayRole)
|
||||||
return QIdentityProxyModel::data(index, BaseVersionList::VersionIdRole);
|
return QIdentityProxyModel::data(index, BaseVersionList::VersionIdRole);
|
||||||
|
if (role == Qt::UserRole)
|
||||||
|
return QIdentityProxyModel::data(index, BaseVersionList::VersionIdRole);
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class AllVersionProxyModel : public QSortFilterProxyModel {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
AllVersionProxyModel(QObject* parent = nullptr) : QSortFilterProxyModel(parent) {}
|
||||||
|
|
||||||
|
int rowCount(const QModelIndex& parent = QModelIndex()) const override { return QSortFilterProxyModel::rowCount(parent) + 1; }
|
||||||
|
|
||||||
|
QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override
|
||||||
|
{
|
||||||
|
if (!index.isValid()) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (index.row() == 0) {
|
||||||
|
if (role == Qt::DisplayRole) {
|
||||||
|
return tr("All Versions");
|
||||||
|
}
|
||||||
|
if (role == Qt::UserRole) {
|
||||||
|
return "all";
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
QModelIndex newIndex = QSortFilterProxyModel::index(index.row() - 1, index.column());
|
||||||
|
return QSortFilterProxyModel::data(newIndex, role);
|
||||||
|
}
|
||||||
|
|
||||||
|
Qt::ItemFlags flags(const QModelIndex& index) const override
|
||||||
|
{
|
||||||
|
if (index.row() == 0) {
|
||||||
|
return Qt::ItemIsSelectable | Qt::ItemIsEnabled;
|
||||||
|
}
|
||||||
|
return QSortFilterProxyModel::flags(index);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
ModFilterWidget::ModFilterWidget(MinecraftInstance* instance, bool extended, QWidget* parent)
|
ModFilterWidget::ModFilterWidget(MinecraftInstance* instance, bool extended, QWidget* parent)
|
||||||
: QTabWidget(parent), ui(new Ui::ModFilterWidget), m_instance(instance), m_filter(new Filter())
|
: QTabWidget(parent), ui(new Ui::ModFilterWidget), m_instance(instance), m_filter(new Filter())
|
||||||
{
|
{
|
||||||
@ -76,14 +115,21 @@ ModFilterWidget::ModFilterWidget(MinecraftInstance* instance, bool extended, QWi
|
|||||||
m_versions_proxy = new VersionProxyModel(this);
|
m_versions_proxy = new VersionProxyModel(this);
|
||||||
m_versions_proxy->setFilter(BaseVersionList::TypeRole, new ExactFilter("release"));
|
m_versions_proxy->setFilter(BaseVersionList::TypeRole, new ExactFilter("release"));
|
||||||
|
|
||||||
auto proxy = new VersionBasicModel(this);
|
QAbstractProxyModel* proxy = new VersionBasicModel(this);
|
||||||
proxy->setSourceModel(m_versions_proxy);
|
proxy->setSourceModel(m_versions_proxy);
|
||||||
|
|
||||||
if (extended) {
|
if (extended) {
|
||||||
|
if (!m_instance) {
|
||||||
|
ui->environmentGroup->hide();
|
||||||
|
}
|
||||||
ui->versions->setSourceModel(proxy);
|
ui->versions->setSourceModel(proxy);
|
||||||
ui->versions->setSeparator(", ");
|
ui->versions->setSeparator(", ");
|
||||||
|
ui->versions->setDefaultText(tr("All Versions"));
|
||||||
ui->version->hide();
|
ui->version->hide();
|
||||||
} else {
|
} else {
|
||||||
|
auto allVersions = new AllVersionProxyModel(this);
|
||||||
|
allVersions->setSourceModel(proxy);
|
||||||
|
proxy = allVersions;
|
||||||
ui->version->setModel(proxy);
|
ui->version->setModel(proxy);
|
||||||
ui->versions->hide();
|
ui->versions->hide();
|
||||||
ui->showAllVersions->hide();
|
ui->showAllVersions->hide();
|
||||||
@ -162,6 +208,7 @@ void ModFilterWidget::loadVersionList()
|
|||||||
|
|
||||||
void ModFilterWidget::prepareBasicFilter()
|
void ModFilterWidget::prepareBasicFilter()
|
||||||
{
|
{
|
||||||
|
if (m_instance) {
|
||||||
m_filter->hideInstalled = false;
|
m_filter->hideInstalled = false;
|
||||||
m_filter->side = ""; // or "both"
|
m_filter->side = ""; // or "both"
|
||||||
auto loaders = m_instance->getPackProfile()->getSupportedModLoaders().value();
|
auto loaders = m_instance->getPackProfile()->getSupportedModLoaders().value();
|
||||||
@ -174,6 +221,9 @@ void ModFilterWidget::prepareBasicFilter()
|
|||||||
m_filter->versions.emplace_front(def);
|
m_filter->versions.emplace_front(def);
|
||||||
ui->versions->setCheckedItems({ def });
|
ui->versions->setCheckedItems({ def });
|
||||||
ui->version->setCurrentIndex(ui->version->findText(def));
|
ui->version->setCurrentIndex(ui->version->findText(def));
|
||||||
|
} else {
|
||||||
|
ui->hideInstalled->hide();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ModFilterWidget::onShowAllVersionsChanged()
|
void ModFilterWidget::onShowAllVersionsChanged()
|
||||||
@ -249,7 +299,9 @@ void ModFilterWidget::onHideInstalledFilterChanged()
|
|||||||
void ModFilterWidget::onVersionFilterTextChanged(const QString& version)
|
void ModFilterWidget::onVersionFilterTextChanged(const QString& version)
|
||||||
{
|
{
|
||||||
m_filter->versions.clear();
|
m_filter->versions.clear();
|
||||||
|
if (ui->version->currentData(Qt::UserRole) != "all") {
|
||||||
m_filter->versions.emplace_back(version);
|
m_filter->versions.emplace_back(version);
|
||||||
|
}
|
||||||
m_filter_changed = true;
|
m_filter_changed = true;
|
||||||
emit filterChanged();
|
emit filterChanged();
|
||||||
}
|
}
|
||||||
|
@ -71,6 +71,15 @@ class ModFilterWidget : public QTabWidget {
|
|||||||
releases == other.releases && categoryIds == other.categoryIds;
|
releases == other.releases && categoryIds == other.categoryIds;
|
||||||
}
|
}
|
||||||
bool operator!=(const Filter& other) const { return !(*this == other); }
|
bool operator!=(const Filter& other) const { return !(*this == other); }
|
||||||
|
|
||||||
|
bool checkMcVersions(QStringList value)
|
||||||
|
{
|
||||||
|
for (auto mcVersion : versions)
|
||||||
|
if (value.contains(mcVersion.toString()))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return versions.empty();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
static unique_qobject_ptr<ModFilterWidget> create(MinecraftInstance* instance, bool extended, QWidget* parent = nullptr);
|
static unique_qobject_ptr<ModFilterWidget> create(MinecraftInstance* instance, bool extended, QWidget* parent = nullptr);
|
||||||
|
Loading…
Reference in New Issue
Block a user