Merge pull request #32 from flowln/modpack_update_page
Closes https://github.com/PrismLauncher/PrismLauncher/issues/180 Closes https://github.com/PrismLauncher/PrismLauncher/issues/170
This commit is contained in:
		@@ -161,6 +161,8 @@ class Config {
 | 
			
		||||
    QString MODRINTH_STAGING_URL = "https://staging-api.modrinth.com/v2";
 | 
			
		||||
    QString MODRINTH_PROD_URL = "https://api.modrinth.com/v2";
 | 
			
		||||
 | 
			
		||||
    QString FLAME_BASE_URL = "https://api.curseforge.com/v1";
 | 
			
		||||
 | 
			
		||||
    QString versionString() const;
 | 
			
		||||
    /**
 | 
			
		||||
     * \brief Converts the Version to a string.
 | 
			
		||||
 
 | 
			
		||||
@@ -680,6 +680,8 @@ SET(LAUNCHER_SOURCES
 | 
			
		||||
    ui/pages/instance/GameOptionsPage.h
 | 
			
		||||
    ui/pages/instance/VersionPage.cpp
 | 
			
		||||
    ui/pages/instance/VersionPage.h
 | 
			
		||||
    ui/pages/instance/ManagedPackPage.cpp
 | 
			
		||||
    ui/pages/instance/ManagedPackPage.h
 | 
			
		||||
    ui/pages/instance/TexturePackPage.h
 | 
			
		||||
    ui/pages/instance/ResourcePackPage.h
 | 
			
		||||
    ui/pages/instance/ShaderPackPage.h
 | 
			
		||||
@@ -919,6 +921,7 @@ qt_wrap_ui(LAUNCHER_UI
 | 
			
		||||
    ui/pages/instance/OtherLogsPage.ui
 | 
			
		||||
    ui/pages/instance/InstanceSettingsPage.ui
 | 
			
		||||
    ui/pages/instance/VersionPage.ui
 | 
			
		||||
    ui/pages/instance/ManagedPackPage.ui
 | 
			
		||||
    ui/pages/instance/WorldListPage.ui
 | 
			
		||||
    ui/pages/instance/ScreenshotsPage.ui
 | 
			
		||||
    ui/pages/modplatform/atlauncher/AtlOptionalModDialog.ui
 | 
			
		||||
 
 | 
			
		||||
@@ -55,11 +55,9 @@
 | 
			
		||||
 | 
			
		||||
#include <quazip/quazipdir.h>
 | 
			
		||||
 | 
			
		||||
InstanceImportTask::InstanceImportTask(const QUrl sourceUrl, QWidget* parent)
 | 
			
		||||
{
 | 
			
		||||
    m_sourceUrl = sourceUrl;
 | 
			
		||||
    m_parent = parent;
 | 
			
		||||
}
 | 
			
		||||
InstanceImportTask::InstanceImportTask(const QUrl sourceUrl, QWidget* parent, QMap<QString, QString>&& extra_info)
 | 
			
		||||
    : m_sourceUrl(sourceUrl), m_extra_info(extra_info), m_parent(parent)
 | 
			
		||||
{}
 | 
			
		||||
 | 
			
		||||
bool InstanceImportTask::abort()
 | 
			
		||||
{
 | 
			
		||||
@@ -259,14 +257,28 @@ void InstanceImportTask::extractAborted()
 | 
			
		||||
 | 
			
		||||
void InstanceImportTask::processFlame()
 | 
			
		||||
{
 | 
			
		||||
    auto* inst_creation_task = new FlameCreationTask(m_stagingPath, m_globalSettings, m_parent);
 | 
			
		||||
    auto pack_id_it = m_extra_info.constFind("pack_id");
 | 
			
		||||
    Q_ASSERT(pack_id_it != m_extra_info.constEnd());
 | 
			
		||||
    auto pack_id = pack_id_it.value();
 | 
			
		||||
 | 
			
		||||
    auto pack_version_id_it = m_extra_info.constFind("pack_version_id");
 | 
			
		||||
    Q_ASSERT(pack_version_id_it != m_extra_info.constEnd());
 | 
			
		||||
    auto pack_version_id = pack_version_id_it.value();
 | 
			
		||||
 | 
			
		||||
    QString original_instance_id;
 | 
			
		||||
    auto original_instance_id_it = m_extra_info.constFind("original_instance_id");
 | 
			
		||||
    if (original_instance_id_it != m_extra_info.constEnd())
 | 
			
		||||
        original_instance_id = original_instance_id_it.value();
 | 
			
		||||
 | 
			
		||||
    auto* inst_creation_task = new FlameCreationTask(m_stagingPath, m_globalSettings, m_parent, pack_id, pack_version_id, original_instance_id);
 | 
			
		||||
 | 
			
		||||
    inst_creation_task->setName(*this);
 | 
			
		||||
    inst_creation_task->setIcon(m_instIcon);
 | 
			
		||||
    inst_creation_task->setGroup(m_instGroup);
 | 
			
		||||
    inst_creation_task->setConfirmUpdate(shouldConfirmUpdate());
 | 
			
		||||
    
 | 
			
		||||
    connect(inst_creation_task, &Task::succeeded, this, [this, inst_creation_task] {
 | 
			
		||||
        setOverride(inst_creation_task->shouldOverride());
 | 
			
		||||
        setOverride(inst_creation_task->shouldOverride(), inst_creation_task->originalInstanceID());
 | 
			
		||||
        emitSucceeded();
 | 
			
		||||
    });
 | 
			
		||||
    connect(inst_creation_task, &Task::failed, this, &InstanceImportTask::emitFailed);
 | 
			
		||||
@@ -323,14 +335,29 @@ void InstanceImportTask::processMultiMC()
 | 
			
		||||
 | 
			
		||||
void InstanceImportTask::processModrinth()
 | 
			
		||||
{
 | 
			
		||||
    auto* inst_creation_task = new ModrinthCreationTask(m_stagingPath, m_globalSettings, m_parent, m_sourceUrl.toString());
 | 
			
		||||
    auto pack_id_it = m_extra_info.constFind("pack_id");
 | 
			
		||||
    Q_ASSERT(pack_id_it != m_extra_info.constEnd());
 | 
			
		||||
    auto pack_id = pack_id_it.value();
 | 
			
		||||
 | 
			
		||||
    QString pack_version_id;
 | 
			
		||||
    auto pack_version_id_it = m_extra_info.constFind("pack_version_id");
 | 
			
		||||
    if (pack_version_id_it != m_extra_info.constEnd())
 | 
			
		||||
        pack_version_id = pack_version_id_it.value();
 | 
			
		||||
 | 
			
		||||
    QString original_instance_id;
 | 
			
		||||
    auto original_instance_id_it = m_extra_info.constFind("original_instance_id");
 | 
			
		||||
    if (original_instance_id_it != m_extra_info.constEnd())
 | 
			
		||||
        original_instance_id = original_instance_id_it.value();
 | 
			
		||||
 | 
			
		||||
    auto* inst_creation_task = new ModrinthCreationTask(m_stagingPath, m_globalSettings, m_parent, pack_id, pack_version_id, original_instance_id);
 | 
			
		||||
 | 
			
		||||
    inst_creation_task->setName(*this);
 | 
			
		||||
    inst_creation_task->setIcon(m_instIcon);
 | 
			
		||||
    inst_creation_task->setGroup(m_instGroup);
 | 
			
		||||
    inst_creation_task->setConfirmUpdate(shouldConfirmUpdate());
 | 
			
		||||
    
 | 
			
		||||
    connect(inst_creation_task, &Task::succeeded, this, [this, inst_creation_task] {
 | 
			
		||||
        setOverride(inst_creation_task->shouldOverride());
 | 
			
		||||
        setOverride(inst_creation_task->shouldOverride(), inst_creation_task->originalInstanceID());
 | 
			
		||||
        emitSucceeded();
 | 
			
		||||
    });
 | 
			
		||||
    connect(inst_creation_task, &Task::failed, this, &InstanceImportTask::emitFailed);
 | 
			
		||||
 
 | 
			
		||||
@@ -56,7 +56,7 @@ class InstanceImportTask : public InstanceTask
 | 
			
		||||
{
 | 
			
		||||
    Q_OBJECT
 | 
			
		||||
public:
 | 
			
		||||
    explicit InstanceImportTask(const QUrl sourceUrl, QWidget* parent = nullptr);
 | 
			
		||||
    explicit InstanceImportTask(const QUrl sourceUrl, QWidget* parent = nullptr, QMap<QString, QString>&& extra_info = {});
 | 
			
		||||
 | 
			
		||||
    bool abort() override;
 | 
			
		||||
    const QVector<Flame::File> &getBlockedFiles() const
 | 
			
		||||
@@ -101,6 +101,10 @@ private: /* data */
 | 
			
		||||
        Modrinth,
 | 
			
		||||
    } m_modpackType = ModpackType::Unknown;
 | 
			
		||||
 | 
			
		||||
    // Extra info we might need, that's available before, but can't be derived from
 | 
			
		||||
    // the source URL / the resource it points to alone.
 | 
			
		||||
    QMap<QString, QString> m_extra_info;
 | 
			
		||||
 | 
			
		||||
    //FIXME: nuke
 | 
			
		||||
    QWidget* m_parent;
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
@@ -816,7 +816,7 @@ class InstanceStaging : public Task {
 | 
			
		||||
    void childSucceded()
 | 
			
		||||
    {
 | 
			
		||||
        unsigned sleepTime = backoff();
 | 
			
		||||
        if (m_parent->commitStagedInstance(m_stagingPath, m_instance_name, m_groupName, m_child->shouldOverride()))
 | 
			
		||||
        if (m_parent->commitStagedInstance(m_stagingPath, m_instance_name, m_groupName, *m_child.get()))
 | 
			
		||||
        {
 | 
			
		||||
            emitSucceeded();
 | 
			
		||||
            return;
 | 
			
		||||
@@ -880,25 +880,22 @@ QString InstanceList::getStagedInstancePath()
 | 
			
		||||
    return path;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool InstanceList::commitStagedInstance(const QString& path, InstanceName const& instanceName, const QString& groupName, bool should_override)
 | 
			
		||||
bool InstanceList::commitStagedInstance(const QString& path, InstanceName const& instanceName, const QString& groupName, InstanceTask const& commiting)
 | 
			
		||||
{
 | 
			
		||||
    QDir dir;
 | 
			
		||||
    QString instID;
 | 
			
		||||
    InstancePtr inst;
 | 
			
		||||
 | 
			
		||||
    auto should_override = commiting.shouldOverride();
 | 
			
		||||
 | 
			
		||||
    if (should_override) {
 | 
			
		||||
        // This is to avoid problems when the instance folder gets manually renamed
 | 
			
		||||
        if ((inst = getInstanceByManagedName(instanceName.originalName()))) {
 | 
			
		||||
            instID = QFileInfo(inst->instanceRoot()).fileName();
 | 
			
		||||
        } else if ((inst = getInstanceByManagedName(instanceName.modifiedName()))) {
 | 
			
		||||
            instID = QFileInfo(inst->instanceRoot()).fileName();
 | 
			
		||||
        } else {
 | 
			
		||||
            instID = FS::RemoveInvalidFilenameChars(instanceName.modifiedName(), '-');
 | 
			
		||||
        }
 | 
			
		||||
        instID = commiting.originalInstanceID();
 | 
			
		||||
    } else {
 | 
			
		||||
        instID = FS::DirNameFromString(instanceName.modifiedName(), m_instDir);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    Q_ASSERT(!instID.isEmpty());
 | 
			
		||||
 | 
			
		||||
    {
 | 
			
		||||
        WatchLock lock(m_watcher, m_instDir);
 | 
			
		||||
        QString destination = FS::PathCombine(m_instDir, instID);
 | 
			
		||||
 
 | 
			
		||||
@@ -133,7 +133,7 @@ public:
 | 
			
		||||
     * should_override is used when another similar instance already exists, and we want to override it
 | 
			
		||||
     * - for instance, when updating it.
 | 
			
		||||
     */
 | 
			
		||||
    bool commitStagedInstance(const QString& keyPath, const InstanceName& instanceName, const QString& groupName, bool should_override);
 | 
			
		||||
    bool commitStagedInstance(const QString& keyPath, const InstanceName& instanceName, const QString& groupName, const InstanceTask&);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Destroy a previously created staging area given by @keyPath - used when creation fails.
 | 
			
		||||
 
 | 
			
		||||
@@ -5,6 +5,7 @@
 | 
			
		||||
#include "ui/pages/BasePageProvider.h"
 | 
			
		||||
#include "ui/pages/instance/LogPage.h"
 | 
			
		||||
#include "ui/pages/instance/VersionPage.h"
 | 
			
		||||
#include "ui/pages/instance/ManagedPackPage.h"
 | 
			
		||||
#include "ui/pages/instance/ModFolderPage.h"
 | 
			
		||||
#include "ui/pages/instance/ResourcePackPage.h"
 | 
			
		||||
#include "ui/pages/instance/TexturePackPage.h"
 | 
			
		||||
@@ -33,6 +34,7 @@ public:
 | 
			
		||||
        values.append(new LogPage(inst));
 | 
			
		||||
        std::shared_ptr<MinecraftInstance> onesix = std::dynamic_pointer_cast<MinecraftInstance>(inst);
 | 
			
		||||
        values.append(new VersionPage(onesix.get()));
 | 
			
		||||
        values.append(ManagedPackPage::createPage(onesix.get()));
 | 
			
		||||
        auto modsPage = new ModFolderPage(onesix.get(), onesix->loaderModList());
 | 
			
		||||
        modsPage->setFilter("%1 (*.zip *.jar *.litemod)");
 | 
			
		||||
        values.append(modsPage);
 | 
			
		||||
 
 | 
			
		||||
@@ -18,6 +18,29 @@ InstanceNameChange askForChangingInstanceName(QWidget* parent, const QString& ol
 | 
			
		||||
    return InstanceNameChange::ShouldKeep;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ShouldUpdate askIfShouldUpdate(QWidget *parent, QString original_version_name)
 | 
			
		||||
{
 | 
			
		||||
    auto info = CustomMessageBox::selectable(
 | 
			
		||||
        parent, QObject::tr("Similar modpack was found!"),
 | 
			
		||||
        QObject::tr("One or more of your instances are from this same modpack%1. Do you want to create a "
 | 
			
		||||
           "separate instance, or update the existing one?\n\nNOTE: Make sure you made a backup of your important instance data before "
 | 
			
		||||
           "updating, as worlds can be corrupted and some configuration may be lost (due to pack overrides).")
 | 
			
		||||
            .arg(original_version_name),
 | 
			
		||||
        QMessageBox::Information, QMessageBox::Ok | QMessageBox::Reset | QMessageBox::Abort);
 | 
			
		||||
    info->setButtonText(QMessageBox::Ok, QObject::tr("Update existing instance"));
 | 
			
		||||
    info->setButtonText(QMessageBox::Abort, QObject::tr("Create new instance"));
 | 
			
		||||
    info->setButtonText(QMessageBox::Reset, QObject::tr("Cancel"));
 | 
			
		||||
 | 
			
		||||
    info->exec();
 | 
			
		||||
 | 
			
		||||
    if (info->clickedButton() == info->button(QMessageBox::Ok))
 | 
			
		||||
        return ShouldUpdate::Update;
 | 
			
		||||
    if (info->clickedButton() == info->button(QMessageBox::Abort))
 | 
			
		||||
        return ShouldUpdate::SkipUpdating;
 | 
			
		||||
    return ShouldUpdate::Cancel;
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
QString InstanceName::name() const
 | 
			
		||||
{
 | 
			
		||||
    if (!m_modified_name.isEmpty())
 | 
			
		||||
 
 | 
			
		||||
@@ -6,6 +6,8 @@
 | 
			
		||||
/* Helpers */
 | 
			
		||||
enum class InstanceNameChange { ShouldChange, ShouldKeep };
 | 
			
		||||
[[nodiscard]] InstanceNameChange askForChangingInstanceName(QWidget* parent, const QString& old_name, const QString& new_name);
 | 
			
		||||
enum class ShouldUpdate { Update, SkipUpdating, Cancel };
 | 
			
		||||
[[nodiscard]] ShouldUpdate askIfShouldUpdate(QWidget* parent, QString original_version_name);
 | 
			
		||||
 | 
			
		||||
struct InstanceName {
 | 
			
		||||
   public:
 | 
			
		||||
@@ -42,10 +44,20 @@ class InstanceTask : public Task, public InstanceName {
 | 
			
		||||
    void setGroup(const QString& group) { m_instGroup = group; }
 | 
			
		||||
    QString group() const { return m_instGroup; }
 | 
			
		||||
 | 
			
		||||
    [[nodiscard]] bool shouldConfirmUpdate() const { return m_confirm_update; }
 | 
			
		||||
    void setConfirmUpdate(bool confirm) { m_confirm_update = confirm; }
 | 
			
		||||
 | 
			
		||||
    bool shouldOverride() const { return m_override_existing; }
 | 
			
		||||
 | 
			
		||||
    [[nodiscard]] QString originalInstanceID() const { return m_original_instance_id; };
 | 
			
		||||
 | 
			
		||||
   protected:
 | 
			
		||||
    void setOverride(bool override) { m_override_existing = override; }
 | 
			
		||||
    void setOverride(bool override, QString instance_id_to_override = {})
 | 
			
		||||
    {
 | 
			
		||||
        m_override_existing = override;
 | 
			
		||||
        if (!instance_id_to_override.isEmpty())
 | 
			
		||||
            m_original_instance_id = instance_id_to_override;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
   protected: /* data */
 | 
			
		||||
    SettingsObjectPtr m_globalSettings;
 | 
			
		||||
@@ -54,4 +66,7 @@ class InstanceTask : public Task, public InstanceName {
 | 
			
		||||
    QString m_stagingPath;
 | 
			
		||||
 | 
			
		||||
    bool m_override_existing = false;
 | 
			
		||||
    bool m_confirm_update = true;
 | 
			
		||||
 | 
			
		||||
    QString m_original_instance_id;
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
@@ -81,13 +81,19 @@ bool FlameCreationTask::updateInstance()
 | 
			
		||||
    auto instance_list = APPLICATION->instances();
 | 
			
		||||
 | 
			
		||||
    // FIXME: How to handle situations when there's more than one install already for a given modpack?
 | 
			
		||||
    auto inst = instance_list->getInstanceByManagedName(originalName());
 | 
			
		||||
    InstancePtr inst;
 | 
			
		||||
    if (auto original_id = originalInstanceID(); !original_id.isEmpty()) {
 | 
			
		||||
        inst = instance_list->getInstanceById(original_id);
 | 
			
		||||
        Q_ASSERT(inst);
 | 
			
		||||
    } else {
 | 
			
		||||
        inst = instance_list->getInstanceByManagedName(originalName());
 | 
			
		||||
 | 
			
		||||
    if (!inst) {
 | 
			
		||||
        inst = instance_list->getInstanceById(originalName());
 | 
			
		||||
        if (!inst) {
 | 
			
		||||
            inst = instance_list->getInstanceById(originalName());
 | 
			
		||||
 | 
			
		||||
        if (!inst)
 | 
			
		||||
            return false;
 | 
			
		||||
            if (!inst)
 | 
			
		||||
                return false;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    QString index_path(FS::PathCombine(m_stagingPath, "manifest.json"));
 | 
			
		||||
@@ -102,25 +108,14 @@ bool FlameCreationTask::updateInstance()
 | 
			
		||||
    auto version_id = inst->getManagedPackVersionName();
 | 
			
		||||
    auto version_str = !version_id.isEmpty() ? tr(" (version %1)").arg(version_id) : "";
 | 
			
		||||
 | 
			
		||||
    auto info = CustomMessageBox::selectable(
 | 
			
		||||
        m_parent, tr("Similar modpack was found!"),
 | 
			
		||||
        tr("One or more of your instances are from this same modpack%1. Do you want to create a "
 | 
			
		||||
           "separate instance, or update the existing one?\n\nNOTE: Make sure you made a backup of your important instance data before "
 | 
			
		||||
           "updating, as worlds can be corrupted and some configuration may be lost (due to pack overrides).")
 | 
			
		||||
            .arg(version_str),
 | 
			
		||||
        QMessageBox::Information, QMessageBox::Ok | QMessageBox::Reset | QMessageBox::Abort);
 | 
			
		||||
    info->setButtonText(QMessageBox::Ok, tr("Update existing instance"));
 | 
			
		||||
    info->setButtonText(QMessageBox::Abort, tr("Create new instance"));
 | 
			
		||||
    info->setButtonText(QMessageBox::Reset, tr("Cancel"));
 | 
			
		||||
 | 
			
		||||
    info->exec();
 | 
			
		||||
 | 
			
		||||
    if (info->clickedButton() == info->button(QMessageBox::Abort))
 | 
			
		||||
        return false;
 | 
			
		||||
 | 
			
		||||
    if (info->clickedButton() == info->button(QMessageBox::Reset)) {
 | 
			
		||||
        m_abort = true;
 | 
			
		||||
        return false;
 | 
			
		||||
    if (shouldConfirmUpdate()) {
 | 
			
		||||
        auto should_update = askIfShouldUpdate(m_parent, version_str);
 | 
			
		||||
        if (should_update == ShouldUpdate::SkipUpdating)
 | 
			
		||||
            return false;
 | 
			
		||||
        if (should_update == ShouldUpdate::Cancel) {
 | 
			
		||||
            m_abort = true;
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    QDir old_inst_dir(inst->instanceRoot());
 | 
			
		||||
@@ -244,7 +239,7 @@ bool FlameCreationTask::updateInstance()
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    setOverride(true);
 | 
			
		||||
    setOverride(true, inst->id());
 | 
			
		||||
    qDebug() << "Will override instance!";
 | 
			
		||||
 | 
			
		||||
    m_instance = inst;
 | 
			
		||||
@@ -366,7 +361,7 @@ bool FlameCreationTask::createInstance()
 | 
			
		||||
        FS::deletePath(jarmodsPath);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    instance.setManagedPack("flame", {}, m_pack.name, {}, m_pack.version);
 | 
			
		||||
    instance.setManagedPack("flame", m_managed_id, m_pack.name, m_managed_version_id, m_pack.version);
 | 
			
		||||
    instance.setName(name());
 | 
			
		||||
 | 
			
		||||
    m_mod_id_resolver = new Flame::FileResolvingTask(APPLICATION->network(), m_pack);
 | 
			
		||||
@@ -390,14 +385,6 @@ bool FlameCreationTask::createInstance()
 | 
			
		||||
        setAbortable(false);
 | 
			
		||||
        auto inst = m_instance.value();
 | 
			
		||||
 | 
			
		||||
        // Only change the name if it didn't use a custom name, so that the previous custom name
 | 
			
		||||
        // is preserved, but if we're using the original one, we update the version string.
 | 
			
		||||
        // NOTE: This needs to come before the copyManagedPack call!
 | 
			
		||||
        if (inst->name().contains(inst->getManagedPackVersionName())) {
 | 
			
		||||
            if (askForChangingInstanceName(m_parent, inst->name(), instance.name()) == InstanceNameChange::ShouldChange)
 | 
			
		||||
                inst->setName(instance.name());
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        inst->copyManagedPack(instance);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -51,11 +51,21 @@ class FlameCreationTask final : public InstanceCreationTask {
 | 
			
		||||
    Q_OBJECT
 | 
			
		||||
 | 
			
		||||
   public:
 | 
			
		||||
    FlameCreationTask(const QString& staging_path, SettingsObjectPtr global_settings, QWidget* parent)
 | 
			
		||||
        : InstanceCreationTask(), m_parent(parent)
 | 
			
		||||
    FlameCreationTask(const QString& staging_path,
 | 
			
		||||
                      SettingsObjectPtr global_settings,
 | 
			
		||||
                      QWidget* parent,
 | 
			
		||||
                      QString id,
 | 
			
		||||
                      QString version_id,
 | 
			
		||||
                      QString original_instance_id = {})
 | 
			
		||||
        : InstanceCreationTask()
 | 
			
		||||
        , m_parent(parent)
 | 
			
		||||
        , m_managed_id(std::move(id))
 | 
			
		||||
        , m_managed_version_id(std::move(version_id))
 | 
			
		||||
    {
 | 
			
		||||
        setStagingPath(staging_path);
 | 
			
		||||
        setParentSettings(global_settings);
 | 
			
		||||
 | 
			
		||||
        m_original_instance_id = std::move(original_instance_id);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bool abort() override;
 | 
			
		||||
@@ -78,5 +88,7 @@ class FlameCreationTask final : public InstanceCreationTask {
 | 
			
		||||
    NetJob* m_process_update_file_info_job = nullptr;
 | 
			
		||||
    NetJob::Ptr m_files_job = nullptr;
 | 
			
		||||
 | 
			
		||||
    QString m_managed_id, m_managed_version_id;
 | 
			
		||||
 | 
			
		||||
    std::optional<InstancePtr> m_instance;
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
@@ -33,13 +33,19 @@ bool ModrinthCreationTask::updateInstance()
 | 
			
		||||
    auto instance_list = APPLICATION->instances();
 | 
			
		||||
 | 
			
		||||
    // FIXME: How to handle situations when there's more than one install already for a given modpack?
 | 
			
		||||
    auto inst = instance_list->getInstanceByManagedName(originalName());
 | 
			
		||||
    InstancePtr inst;
 | 
			
		||||
    if (auto original_id = originalInstanceID(); !original_id.isEmpty()) {
 | 
			
		||||
        inst = instance_list->getInstanceById(original_id);
 | 
			
		||||
        Q_ASSERT(inst);
 | 
			
		||||
    } else {
 | 
			
		||||
        inst = instance_list->getInstanceByManagedName(originalName());
 | 
			
		||||
 | 
			
		||||
    if (!inst) {
 | 
			
		||||
        inst = instance_list->getInstanceById(originalName());
 | 
			
		||||
        if (!inst) {
 | 
			
		||||
            inst = instance_list->getInstanceById(originalName());
 | 
			
		||||
 | 
			
		||||
        if (!inst)
 | 
			
		||||
            return false;
 | 
			
		||||
            if (!inst)
 | 
			
		||||
                return false;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    QString index_path = FS::PathCombine(m_stagingPath, "modrinth.index.json");
 | 
			
		||||
@@ -49,25 +55,14 @@ bool ModrinthCreationTask::updateInstance()
 | 
			
		||||
    auto version_name = inst->getManagedPackVersionName();
 | 
			
		||||
    auto version_str = !version_name.isEmpty() ? tr(" (version %1)").arg(version_name) : "";
 | 
			
		||||
 | 
			
		||||
    auto info = CustomMessageBox::selectable(
 | 
			
		||||
        m_parent, tr("Similar modpack was found!"),
 | 
			
		||||
        tr("One or more of your instances are from this same modpack%1. Do you want to create a "
 | 
			
		||||
           "separate instance, or update the existing one?\n\nNOTE: Make sure you made a backup of your important instance data before "
 | 
			
		||||
           "updating, as worlds can be corrupted and some configuration may be lost (due to pack overrides).")
 | 
			
		||||
            .arg(version_str),
 | 
			
		||||
        QMessageBox::Information, QMessageBox::Ok | QMessageBox::Reset | QMessageBox::Abort);
 | 
			
		||||
    info->setButtonText(QMessageBox::Ok, tr("Create new instance"));
 | 
			
		||||
    info->setButtonText(QMessageBox::Abort, tr("Update existing instance"));
 | 
			
		||||
    info->setButtonText(QMessageBox::Reset, tr("Cancel"));
 | 
			
		||||
 | 
			
		||||
    info->exec();
 | 
			
		||||
 | 
			
		||||
    if (info->clickedButton() == info->button(QMessageBox::Ok))
 | 
			
		||||
        return false;
 | 
			
		||||
 | 
			
		||||
    if (info->clickedButton() == info->button(QMessageBox::Reset)) {
 | 
			
		||||
        m_abort = true;
 | 
			
		||||
        return false;
 | 
			
		||||
    if (shouldConfirmUpdate()) {
 | 
			
		||||
        auto should_update = askIfShouldUpdate(m_parent, version_str);
 | 
			
		||||
        if (should_update == ShouldUpdate::SkipUpdating)
 | 
			
		||||
            return false;
 | 
			
		||||
        if (should_update == ShouldUpdate::Cancel) {
 | 
			
		||||
            m_abort = true;
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Remove repeated files, we don't need to download them!
 | 
			
		||||
@@ -149,7 +144,7 @@ bool ModrinthCreationTask::updateInstance()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    setOverride(true);
 | 
			
		||||
    setOverride(true, inst->id());
 | 
			
		||||
    qDebug() << "Will override instance!";
 | 
			
		||||
 | 
			
		||||
    m_instance = inst;
 | 
			
		||||
@@ -222,7 +217,7 @@ bool ModrinthCreationTask::createInstance()
 | 
			
		||||
        instance.setIconKey("modrinth");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    instance.setManagedPack("modrinth", getManagedPackID(), m_managed_name, m_managed_version_id, version());
 | 
			
		||||
    instance.setManagedPack("modrinth", m_managed_id, m_managed_name, m_managed_version_id, version());
 | 
			
		||||
    instance.setName(name());
 | 
			
		||||
    instance.saveNow();
 | 
			
		||||
 | 
			
		||||
@@ -295,7 +290,8 @@ bool ModrinthCreationTask::parseManifest(const QString& index_path, std::vector<
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (set_managed_info) {
 | 
			
		||||
                m_managed_version_id = Json::ensureString(obj, "versionId", {}, "Managed ID");
 | 
			
		||||
                if (m_managed_version_id.isEmpty())
 | 
			
		||||
                    m_managed_version_id = Json::ensureString(obj, "versionId", {}, "Managed ID");
 | 
			
		||||
                m_managed_name = Json::ensureString(obj, "name", {}, "Managed Name");
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
@@ -395,13 +391,3 @@ bool ModrinthCreationTask::parseManifest(const QString& index_path, std::vector<
 | 
			
		||||
 | 
			
		||||
    return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
QString ModrinthCreationTask::getManagedPackID() const
 | 
			
		||||
{
 | 
			
		||||
    if (!m_source_url.isEmpty()) {
 | 
			
		||||
        QRegularExpression regex(R"(data\/(.*)\/versions)");
 | 
			
		||||
        return regex.match(m_source_url).captured(1);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return {};
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -14,11 +14,21 @@ class ModrinthCreationTask final : public InstanceCreationTask {
 | 
			
		||||
    Q_OBJECT
 | 
			
		||||
 | 
			
		||||
   public:
 | 
			
		||||
    ModrinthCreationTask(QString staging_path, SettingsObjectPtr global_settings, QWidget* parent, QString source_url = {})
 | 
			
		||||
        : InstanceCreationTask(), m_parent(parent), m_source_url(std::move(source_url))
 | 
			
		||||
    ModrinthCreationTask(QString staging_path,
 | 
			
		||||
                         SettingsObjectPtr global_settings,
 | 
			
		||||
                         QWidget* parent,
 | 
			
		||||
                         QString id,
 | 
			
		||||
                         QString version_id = {},
 | 
			
		||||
                         QString original_instance_id = {})
 | 
			
		||||
        : InstanceCreationTask()
 | 
			
		||||
        , m_parent(parent)
 | 
			
		||||
        , m_managed_id(std::move(id))
 | 
			
		||||
        , m_managed_version_id(std::move(version_id))
 | 
			
		||||
    {
 | 
			
		||||
        setStagingPath(staging_path);
 | 
			
		||||
        setParentSettings(global_settings);
 | 
			
		||||
 | 
			
		||||
        m_original_instance_id = std::move(original_instance_id);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bool abort() override;
 | 
			
		||||
@@ -28,14 +38,12 @@ class ModrinthCreationTask final : public InstanceCreationTask {
 | 
			
		||||
 | 
			
		||||
   private:
 | 
			
		||||
    bool parseManifest(const QString&, std::vector<Modrinth::File>&, bool set_managed_info = true, bool show_optional_dialog = true);
 | 
			
		||||
    QString getManagedPackID() const;
 | 
			
		||||
 | 
			
		||||
   private:
 | 
			
		||||
    QWidget* m_parent = nullptr;
 | 
			
		||||
 | 
			
		||||
    QString minecraftVersion, fabricVersion, quiltVersion, forgeVersion;
 | 
			
		||||
    QString m_managed_id, m_managed_version_id, m_managed_name;
 | 
			
		||||
    QString m_source_url;
 | 
			
		||||
 | 
			
		||||
    std::vector<Modrinth::File> m_files;
 | 
			
		||||
    NetJob::Ptr m_files_job;
 | 
			
		||||
 
 | 
			
		||||
@@ -128,6 +128,7 @@ auto loadIndexedVersion(QJsonObject &obj) -> ModpackVersion
 | 
			
		||||
 | 
			
		||||
    file.name = Json::requireString(obj, "name");
 | 
			
		||||
    file.version = Json::requireString(obj, "version_number");
 | 
			
		||||
    file.changelog = Json::ensureString(obj, "changelog");
 | 
			
		||||
 | 
			
		||||
    file.id = Json::requireString(obj, "id");
 | 
			
		||||
    file.project_id = Json::requireString(obj, "project_id");
 | 
			
		||||
 
 | 
			
		||||
@@ -80,6 +80,7 @@ struct ModpackExtra {
 | 
			
		||||
struct ModpackVersion {
 | 
			
		||||
    QString name;
 | 
			
		||||
    QString version;
 | 
			
		||||
    QString changelog;
 | 
			
		||||
 | 
			
		||||
    QString id;
 | 
			
		||||
    QString project_id;
 | 
			
		||||
 
 | 
			
		||||
@@ -132,6 +132,12 @@ InstanceWindow::InstanceWindow(InstancePtr instance, QWidget *parent)
 | 
			
		||||
    {
 | 
			
		||||
        connect(m_instance.get(), &BaseInstance::statusChanged, this, &InstanceWindow::on_instanceStatusChanged);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // add ourself as the modpack page's instance window
 | 
			
		||||
    {
 | 
			
		||||
        static_cast<ManagedPackPage*>(m_container->getPage("managed_pack"))->setInstanceWindow(this);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    show();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,10 +1,13 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
class BasePage;
 | 
			
		||||
 | 
			
		||||
class BasePageContainer
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    virtual ~BasePageContainer(){};
 | 
			
		||||
    virtual bool selectPage(QString pageId) = 0;
 | 
			
		||||
    virtual BasePage* getPage(QString pageId) { return nullptr; };
 | 
			
		||||
    virtual void refreshContainer() = 0;
 | 
			
		||||
    virtual bool requestClose() = 0;
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										432
									
								
								launcher/ui/pages/instance/ManagedPackPage.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										432
									
								
								launcher/ui/pages/instance/ManagedPackPage.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,432 @@
 | 
			
		||||
// SPDX-FileCopyrightText: 2022 flow <flowlnlnln@gmail.com>
 | 
			
		||||
//
 | 
			
		||||
// SPDX-License-Identifier: GPL-3.0-only
 | 
			
		||||
 | 
			
		||||
#include "ManagedPackPage.h"
 | 
			
		||||
#include "ui_ManagedPackPage.h"
 | 
			
		||||
 | 
			
		||||
#include <QListView>
 | 
			
		||||
#include <QProxyStyle>
 | 
			
		||||
 | 
			
		||||
#include <HoeDown.h>
 | 
			
		||||
 | 
			
		||||
#include "Application.h"
 | 
			
		||||
#include "BuildConfig.h"
 | 
			
		||||
#include "InstanceImportTask.h"
 | 
			
		||||
#include "InstanceList.h"
 | 
			
		||||
#include "InstanceTask.h"
 | 
			
		||||
#include "Json.h"
 | 
			
		||||
 | 
			
		||||
#include "modplatform/modrinth/ModrinthPackManifest.h"
 | 
			
		||||
 | 
			
		||||
#include "ui/InstanceWindow.h"
 | 
			
		||||
#include "ui/dialogs/CustomMessageBox.h"
 | 
			
		||||
#include "ui/dialogs/ProgressDialog.h"
 | 
			
		||||
 | 
			
		||||
/** This is just to override the combo box popup behavior so that the combo box doesn't take the whole screen.
 | 
			
		||||
 *  ... thanks Qt.
 | 
			
		||||
 */
 | 
			
		||||
class NoBigComboBoxStyle : public QProxyStyle {
 | 
			
		||||
    Q_OBJECT
 | 
			
		||||
 | 
			
		||||
   public:
 | 
			
		||||
    NoBigComboBoxStyle(QStyle* style) : QProxyStyle(style) {}
 | 
			
		||||
 | 
			
		||||
    // clang-format off
 | 
			
		||||
    int styleHint(QStyle::StyleHint hint, const QStyleOption* option = nullptr, const QWidget* widget = nullptr, QStyleHintReturn* returnData = nullptr) const override
 | 
			
		||||
    {
 | 
			
		||||
        if (hint == QStyle::SH_ComboBox_Popup)
 | 
			
		||||
            return false;
 | 
			
		||||
 | 
			
		||||
        return QProxyStyle::styleHint(hint, option, widget, returnData);
 | 
			
		||||
    }
 | 
			
		||||
    // clang-format on
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
ManagedPackPage* ManagedPackPage::createPage(BaseInstance* inst, QString type, QWidget* parent)
 | 
			
		||||
{
 | 
			
		||||
    if (type == "modrinth")
 | 
			
		||||
        return new ModrinthManagedPackPage(inst, nullptr, parent);
 | 
			
		||||
    if (type == "flame" && (APPLICATION->capabilities() & Application::SupportsFlame))
 | 
			
		||||
        return new FlameManagedPackPage(inst, nullptr, parent);
 | 
			
		||||
 | 
			
		||||
    return new GenericManagedPackPage(inst, nullptr, parent);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ManagedPackPage::ManagedPackPage(BaseInstance* inst, InstanceWindow* instance_window, QWidget* parent)
 | 
			
		||||
    : QWidget(parent), m_instance_window(instance_window), ui(new Ui::ManagedPackPage), m_inst(inst)
 | 
			
		||||
{
 | 
			
		||||
    Q_ASSERT(inst);
 | 
			
		||||
 | 
			
		||||
    ui->setupUi(this);
 | 
			
		||||
 | 
			
		||||
    ui->versionsComboBox->setStyle(new NoBigComboBoxStyle(ui->versionsComboBox->style()));
 | 
			
		||||
 | 
			
		||||
    ui->reloadButton->setVisible(false);
 | 
			
		||||
    connect(ui->reloadButton, &QPushButton::clicked, this, [this](bool){
 | 
			
		||||
        ui->reloadButton->setVisible(false);
 | 
			
		||||
 | 
			
		||||
        m_loaded = false;
 | 
			
		||||
        // Pretend we're opening the page again
 | 
			
		||||
        openedImpl();
 | 
			
		||||
    });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ManagedPackPage::~ManagedPackPage()
 | 
			
		||||
{
 | 
			
		||||
    delete ui;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ManagedPackPage::openedImpl()
 | 
			
		||||
{
 | 
			
		||||
    ui->packName->setText(m_inst->getManagedPackName());
 | 
			
		||||
    ui->packVersion->setText(m_inst->getManagedPackVersionName());
 | 
			
		||||
    ui->packOrigin->setText(tr("Website: <a href=%1>%2</a>    |    Pack ID: %3    |    Version ID: %4")
 | 
			
		||||
                                .arg(url(), displayName(), m_inst->getManagedPackID(), m_inst->getManagedPackVersionID()));
 | 
			
		||||
 | 
			
		||||
    parseManagedPack();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
QString ManagedPackPage::displayName() const
 | 
			
		||||
{
 | 
			
		||||
    auto type = m_inst->getManagedPackType();
 | 
			
		||||
    if (type.isEmpty())
 | 
			
		||||
        return {};
 | 
			
		||||
    if (type == "flame")
 | 
			
		||||
        type = "CurseForge";
 | 
			
		||||
    return type.replace(0, 1, type[0].toUpper());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
QIcon ManagedPackPage::icon() const
 | 
			
		||||
{
 | 
			
		||||
    return APPLICATION->getThemedIcon(m_inst->getManagedPackType());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
QString ManagedPackPage::helpPage() const
 | 
			
		||||
{
 | 
			
		||||
    return {};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ManagedPackPage::retranslate()
 | 
			
		||||
{
 | 
			
		||||
    ui->retranslateUi(this);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool ManagedPackPage::shouldDisplay() const
 | 
			
		||||
{
 | 
			
		||||
    return m_inst->isManagedPack();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool ManagedPackPage::runUpdateTask(InstanceTask* task)
 | 
			
		||||
{
 | 
			
		||||
    Q_ASSERT(task);
 | 
			
		||||
 | 
			
		||||
    unique_qobject_ptr<Task> wrapped_task(APPLICATION->instances()->wrapInstanceTask(task));
 | 
			
		||||
 | 
			
		||||
    connect(task, &Task::failed,
 | 
			
		||||
            [this](QString reason) { CustomMessageBox::selectable(this, tr("Error"), reason, QMessageBox::Critical)->show(); });
 | 
			
		||||
    connect(task, &Task::succeeded, [this, task]() {
 | 
			
		||||
        QStringList warnings = task->warnings();
 | 
			
		||||
        if (warnings.count())
 | 
			
		||||
            CustomMessageBox::selectable(this, tr("Warnings"), warnings.join('\n'), QMessageBox::Warning)->show();
 | 
			
		||||
    });
 | 
			
		||||
    connect(task, &Task::aborted, [this] {
 | 
			
		||||
        CustomMessageBox::selectable(this, tr("Task aborted"), tr("The task has been aborted by the user."), QMessageBox::Information)
 | 
			
		||||
            ->show();
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    ProgressDialog loadDialog(this);
 | 
			
		||||
    loadDialog.setSkipButton(true, tr("Abort"));
 | 
			
		||||
    loadDialog.execWithTask(task);
 | 
			
		||||
 | 
			
		||||
    return task->wasSuccessful();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ManagedPackPage::suggestVersion()
 | 
			
		||||
{
 | 
			
		||||
    ui->updateButton->setText(tr("Update pack"));
 | 
			
		||||
    ui->updateButton->setDisabled(false);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ManagedPackPage::setFailState()
 | 
			
		||||
{
 | 
			
		||||
    qDebug() << "Setting fail state!";
 | 
			
		||||
 | 
			
		||||
    // We block signals here so that suggestVersion() doesn't get called, causing an assertion fail.
 | 
			
		||||
    ui->versionsComboBox->blockSignals(true);
 | 
			
		||||
    ui->versionsComboBox->clear();
 | 
			
		||||
    ui->versionsComboBox->addItem(tr("Failed to search for available versions."), {});
 | 
			
		||||
    ui->versionsComboBox->blockSignals(false);
 | 
			
		||||
 | 
			
		||||
    ui->changelogTextBrowser->setText(tr("Failed to request changelog data for this modpack."));
 | 
			
		||||
 | 
			
		||||
    ui->updateButton->setText(tr("Cannot update!"));
 | 
			
		||||
    ui->updateButton->setDisabled(true);
 | 
			
		||||
 | 
			
		||||
    ui->reloadButton->setVisible(true);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ModrinthManagedPackPage::ModrinthManagedPackPage(BaseInstance* inst, InstanceWindow* instance_window, QWidget* parent)
 | 
			
		||||
    : ManagedPackPage(inst, instance_window, parent)
 | 
			
		||||
{
 | 
			
		||||
    Q_ASSERT(inst->isManagedPack());
 | 
			
		||||
    connect(ui->versionsComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(suggestVersion()));
 | 
			
		||||
    connect(ui->updateButton, &QPushButton::pressed, this, &ModrinthManagedPackPage::update);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// MODRINTH
 | 
			
		||||
 | 
			
		||||
void ModrinthManagedPackPage::parseManagedPack()
 | 
			
		||||
{
 | 
			
		||||
    qDebug() << "Parsing Modrinth pack";
 | 
			
		||||
 | 
			
		||||
    // No need for the extra work because we already have everything we need.
 | 
			
		||||
    if (m_loaded)
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
    if (m_fetch_job && m_fetch_job->isRunning())
 | 
			
		||||
        m_fetch_job->abort();
 | 
			
		||||
 | 
			
		||||
    m_fetch_job.reset(new NetJob(QString("Modrinth::PackVersions(%1)").arg(m_inst->getManagedPackName()), APPLICATION->network()));
 | 
			
		||||
    auto response = std::make_shared<QByteArray>();
 | 
			
		||||
 | 
			
		||||
    QString id = m_inst->getManagedPackID();
 | 
			
		||||
 | 
			
		||||
    m_fetch_job->addNetAction(Net::Download::makeByteArray(QString("%1/project/%2/version").arg(BuildConfig.MODRINTH_PROD_URL, id), response.get()));
 | 
			
		||||
 | 
			
		||||
    QObject::connect(m_fetch_job.get(), &NetJob::succeeded, this, [this, response, id] {
 | 
			
		||||
        QJsonParseError parse_error{};
 | 
			
		||||
        QJsonDocument doc = QJsonDocument::fromJson(*response, &parse_error);
 | 
			
		||||
        if (parse_error.error != QJsonParseError::NoError) {
 | 
			
		||||
            qWarning() << "Error while parsing JSON response from Modrinth at " << parse_error.offset
 | 
			
		||||
                       << " reason: " << parse_error.errorString();
 | 
			
		||||
            qWarning() << *response;
 | 
			
		||||
 | 
			
		||||
            setFailState();
 | 
			
		||||
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
            Modrinth::loadIndexedVersions(m_pack, doc);
 | 
			
		||||
        } catch (const JSONValidationError& e) {
 | 
			
		||||
            qDebug() << *response;
 | 
			
		||||
            qWarning() << "Error while reading modrinth modpack version: " << e.cause();
 | 
			
		||||
 | 
			
		||||
            setFailState();
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // We block signals here so that suggestVersion() doesn't get called, causing an assertion fail.
 | 
			
		||||
        ui->versionsComboBox->blockSignals(true);
 | 
			
		||||
        ui->versionsComboBox->clear();
 | 
			
		||||
        ui->versionsComboBox->blockSignals(false);
 | 
			
		||||
 | 
			
		||||
        for (auto version : m_pack.versions) {
 | 
			
		||||
            QString name;
 | 
			
		||||
 | 
			
		||||
            if (!version.name.contains(version.version))
 | 
			
		||||
                name = QString("%1 — %2").arg(version.name, version.version);
 | 
			
		||||
            else
 | 
			
		||||
                name = version.name;
 | 
			
		||||
 | 
			
		||||
            // NOTE: the id from version isn't the same id in the modpack format spec...
 | 
			
		||||
            // e.g. HexMC's 4.4.0 has versionId 4.0.0 in the modpack index..............
 | 
			
		||||
            if (version.version == m_inst->getManagedPackVersionName())
 | 
			
		||||
                name.append(tr(" (Current)"));
 | 
			
		||||
 | 
			
		||||
            ui->versionsComboBox->addItem(name, QVariant(version.id));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        suggestVersion();
 | 
			
		||||
 | 
			
		||||
        m_loaded = true;
 | 
			
		||||
    });
 | 
			
		||||
    QObject::connect(m_fetch_job.get(), &NetJob::failed, this, &ModrinthManagedPackPage::setFailState);
 | 
			
		||||
    QObject::connect(m_fetch_job.get(), &NetJob::aborted, this, &ModrinthManagedPackPage::setFailState);
 | 
			
		||||
 | 
			
		||||
    ui->changelogTextBrowser->setText(tr("Fetching changelogs..."));
 | 
			
		||||
 | 
			
		||||
    m_fetch_job->start();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
QString ModrinthManagedPackPage::url() const
 | 
			
		||||
{
 | 
			
		||||
    return "https://modrinth.com/mod/" + m_inst->getManagedPackID();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ModrinthManagedPackPage::suggestVersion()
 | 
			
		||||
{
 | 
			
		||||
    auto index = ui->versionsComboBox->currentIndex();
 | 
			
		||||
    auto version = m_pack.versions.at(index);
 | 
			
		||||
 | 
			
		||||
    HoeDown md_parser;
 | 
			
		||||
    ui->changelogTextBrowser->setHtml(md_parser.process(version.changelog.toUtf8()));
 | 
			
		||||
 | 
			
		||||
    ManagedPackPage::suggestVersion();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ModrinthManagedPackPage::update()
 | 
			
		||||
{
 | 
			
		||||
    auto index = ui->versionsComboBox->currentIndex();
 | 
			
		||||
    auto version = m_pack.versions.at(index);
 | 
			
		||||
 | 
			
		||||
    QMap<QString, QString> extra_info;
 | 
			
		||||
    // NOTE: Don't use 'm_pack.id' here, since we didn't completely parse all the metadata for the pack, including this field.
 | 
			
		||||
    extra_info.insert("pack_id", m_inst->getManagedPackID());
 | 
			
		||||
    extra_info.insert("pack_version_id", version.id);
 | 
			
		||||
    extra_info.insert("original_instance_id", m_inst->id());
 | 
			
		||||
 | 
			
		||||
    auto extracted = new InstanceImportTask(version.download_url, this, std::move(extra_info));
 | 
			
		||||
 | 
			
		||||
    InstanceName inst_name(m_inst->getManagedPackName(), version.version);
 | 
			
		||||
    inst_name.setName(m_inst->name().replace(m_inst->getManagedPackVersionName(), version.version));
 | 
			
		||||
    extracted->setName(inst_name);
 | 
			
		||||
 | 
			
		||||
    extracted->setGroup(APPLICATION->instances()->getInstanceGroup(m_inst->id()));
 | 
			
		||||
    extracted->setIcon(m_inst->iconKey());
 | 
			
		||||
    extracted->setConfirmUpdate(false);
 | 
			
		||||
 | 
			
		||||
    auto did_succeed = runUpdateTask(extracted);
 | 
			
		||||
 | 
			
		||||
    if (m_instance_window && did_succeed)
 | 
			
		||||
        m_instance_window->close();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// FLAME
 | 
			
		||||
 | 
			
		||||
FlameManagedPackPage::FlameManagedPackPage(BaseInstance* inst, InstanceWindow* instance_window, QWidget* parent)
 | 
			
		||||
    : ManagedPackPage(inst, instance_window, parent)
 | 
			
		||||
{
 | 
			
		||||
    Q_ASSERT(inst->isManagedPack());
 | 
			
		||||
    connect(ui->versionsComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(suggestVersion()));
 | 
			
		||||
    connect(ui->updateButton, &QPushButton::pressed, this, &FlameManagedPackPage::update);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void FlameManagedPackPage::parseManagedPack()
 | 
			
		||||
{
 | 
			
		||||
    qDebug() << "Parsing Flame pack";
 | 
			
		||||
 | 
			
		||||
    // We need to tell the user to redownload the pack, since we didn't save the required info previously
 | 
			
		||||
    if (m_inst->getManagedPackID().isEmpty()) {
 | 
			
		||||
        setFailState();
 | 
			
		||||
        QString message =
 | 
			
		||||
            tr("<h1>Hey there!</h1>"
 | 
			
		||||
               "<h4>"
 | 
			
		||||
               "It seems like your Pack ID is null. This is because of a bug in older versions of the launcher.<br/>"
 | 
			
		||||
               "Unfortunately, we can't do the proper API requests without this information.<br/>"
 | 
			
		||||
               "<br/>"
 | 
			
		||||
               "So, in order for this feature to work, you will need to re-download the modpack from the built-in downloader.<br/>"
 | 
			
		||||
               "<br/>"
 | 
			
		||||
               "Don't worry though, it will ask you to update this instance instead, so you'll not lose this instance!"
 | 
			
		||||
               "</h4>");
 | 
			
		||||
 | 
			
		||||
        ui->changelogTextBrowser->setHtml(message);
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // No need for the extra work because we already have everything we need.
 | 
			
		||||
    if (m_loaded)
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
    if (m_fetch_job && m_fetch_job->isRunning())
 | 
			
		||||
        m_fetch_job->abort();
 | 
			
		||||
 | 
			
		||||
    m_fetch_job.reset(new NetJob(QString("Flame::PackVersions(%1)").arg(m_inst->getManagedPackName()), APPLICATION->network()));
 | 
			
		||||
    auto response = std::make_shared<QByteArray>();
 | 
			
		||||
 | 
			
		||||
    QString id = m_inst->getManagedPackID();
 | 
			
		||||
 | 
			
		||||
    m_fetch_job->addNetAction(Net::Download::makeByteArray(QString("%1/mods/%2/files").arg(BuildConfig.FLAME_BASE_URL, id), response.get()));
 | 
			
		||||
 | 
			
		||||
    QObject::connect(m_fetch_job.get(), &NetJob::succeeded, this, [this, response, id] {
 | 
			
		||||
        QJsonParseError parse_error{};
 | 
			
		||||
        QJsonDocument doc = QJsonDocument::fromJson(*response, &parse_error);
 | 
			
		||||
        if (parse_error.error != QJsonParseError::NoError) {
 | 
			
		||||
            qWarning() << "Error while parsing JSON response from Flame at " << parse_error.offset
 | 
			
		||||
                       << " reason: " << parse_error.errorString();
 | 
			
		||||
            qWarning() << *response;
 | 
			
		||||
 | 
			
		||||
            setFailState();
 | 
			
		||||
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
            auto obj = doc.object();
 | 
			
		||||
            auto data = Json::ensureArray(obj, "data");
 | 
			
		||||
            Flame::loadIndexedPackVersions(m_pack, data);
 | 
			
		||||
        } catch (const JSONValidationError& e) {
 | 
			
		||||
            qDebug() << *response;
 | 
			
		||||
            qWarning() << "Error while reading flame modpack version: " << e.cause();
 | 
			
		||||
 | 
			
		||||
            setFailState();
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // We block signals here so that suggestVersion() doesn't get called, causing an assertion fail.
 | 
			
		||||
        ui->versionsComboBox->blockSignals(true);
 | 
			
		||||
        ui->versionsComboBox->clear();
 | 
			
		||||
        ui->versionsComboBox->blockSignals(false);
 | 
			
		||||
 | 
			
		||||
        for (auto version : m_pack.versions) {
 | 
			
		||||
            QString name;
 | 
			
		||||
 | 
			
		||||
            name = version.version;
 | 
			
		||||
 | 
			
		||||
            if (version.fileId == m_inst->getManagedPackVersionID().toInt())
 | 
			
		||||
                name.append(tr(" (Current)"));
 | 
			
		||||
 | 
			
		||||
            ui->versionsComboBox->addItem(name, QVariant(version.fileId));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        suggestVersion();
 | 
			
		||||
 | 
			
		||||
        m_loaded = true;
 | 
			
		||||
    });
 | 
			
		||||
    QObject::connect(m_fetch_job.get(), &NetJob::failed, this, &FlameManagedPackPage::setFailState);
 | 
			
		||||
    QObject::connect(m_fetch_job.get(), &NetJob::aborted, this, &FlameManagedPackPage::setFailState);
 | 
			
		||||
 | 
			
		||||
    m_fetch_job->start();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
QString FlameManagedPackPage::url() const
 | 
			
		||||
{
 | 
			
		||||
    // FIXME: We should display the websiteUrl field, but this requires doing the API request first :(
 | 
			
		||||
    return {};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void FlameManagedPackPage::suggestVersion()
 | 
			
		||||
{
 | 
			
		||||
    auto index = ui->versionsComboBox->currentIndex();
 | 
			
		||||
    auto version = m_pack.versions.at(index);
 | 
			
		||||
 | 
			
		||||
    ui->changelogTextBrowser->setHtml(m_api.getModFileChangelog(m_inst->getManagedPackID().toInt(), version.fileId));
 | 
			
		||||
 | 
			
		||||
    ManagedPackPage::suggestVersion();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void FlameManagedPackPage::update()
 | 
			
		||||
{
 | 
			
		||||
    auto index = ui->versionsComboBox->currentIndex();
 | 
			
		||||
    auto version = m_pack.versions.at(index);
 | 
			
		||||
 | 
			
		||||
    QMap<QString, QString> extra_info;
 | 
			
		||||
    extra_info.insert("pack_id", m_inst->getManagedPackID());
 | 
			
		||||
    extra_info.insert("pack_version_id", QString::number(version.fileId));
 | 
			
		||||
    extra_info.insert("original_instance_id", m_inst->id());
 | 
			
		||||
 | 
			
		||||
    auto extracted = new InstanceImportTask(version.downloadUrl, this, std::move(extra_info));
 | 
			
		||||
 | 
			
		||||
    extracted->setName(m_inst->name());
 | 
			
		||||
    extracted->setGroup(APPLICATION->instances()->getInstanceGroup(m_inst->id()));
 | 
			
		||||
    extracted->setIcon(m_inst->iconKey());
 | 
			
		||||
    extracted->setConfirmUpdate(false);
 | 
			
		||||
 | 
			
		||||
    auto did_succeed = runUpdateTask(extracted);
 | 
			
		||||
 | 
			
		||||
    if (m_instance_window && did_succeed)
 | 
			
		||||
        m_instance_window->close();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#include "ManagedPackPage.moc"
 | 
			
		||||
							
								
								
									
										152
									
								
								launcher/ui/pages/instance/ManagedPackPage.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										152
									
								
								launcher/ui/pages/instance/ManagedPackPage.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,152 @@
 | 
			
		||||
// SPDX-FileCopyrightText: 2022 flow <flowlnlnln@gmail.com>
 | 
			
		||||
//
 | 
			
		||||
// SPDX-License-Identifier: GPL-3.0-only
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "BaseInstance.h"
 | 
			
		||||
 | 
			
		||||
#include "modplatform/modrinth/ModrinthAPI.h"
 | 
			
		||||
#include "modplatform/modrinth/ModrinthPackManifest.h"
 | 
			
		||||
 | 
			
		||||
#include "modplatform/flame/FlameAPI.h"
 | 
			
		||||
#include "modplatform/flame/FlamePackIndex.h"
 | 
			
		||||
 | 
			
		||||
#include "ui/pages/BasePage.h"
 | 
			
		||||
 | 
			
		||||
#include <QWidget>
 | 
			
		||||
 | 
			
		||||
namespace Ui {
 | 
			
		||||
class ManagedPackPage;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class InstanceTask;
 | 
			
		||||
class InstanceWindow;
 | 
			
		||||
 | 
			
		||||
class ManagedPackPage : public QWidget, public BasePage {
 | 
			
		||||
    Q_OBJECT
 | 
			
		||||
 | 
			
		||||
   public:
 | 
			
		||||
    inline static ManagedPackPage* createPage(BaseInstance* inst, QWidget* parent = nullptr)
 | 
			
		||||
    {
 | 
			
		||||
        return ManagedPackPage::createPage(inst, inst->getManagedPackType(), parent);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    static ManagedPackPage* createPage(BaseInstance* inst, QString type, QWidget* parent = nullptr);
 | 
			
		||||
    ~ManagedPackPage() override;
 | 
			
		||||
 | 
			
		||||
    [[nodiscard]] QString displayName() const override;
 | 
			
		||||
    [[nodiscard]] QIcon icon() const override;
 | 
			
		||||
    [[nodiscard]] QString helpPage() const override;
 | 
			
		||||
    [[nodiscard]] QString id() const override { return "managed_pack"; }
 | 
			
		||||
    [[nodiscard]] bool shouldDisplay() const override;
 | 
			
		||||
 | 
			
		||||
    void openedImpl() override;
 | 
			
		||||
 | 
			
		||||
    bool apply() override { return true; }
 | 
			
		||||
    void retranslate() override;
 | 
			
		||||
 | 
			
		||||
    /** Gets the necessary information about the managed pack, such as
 | 
			
		||||
     *  available versions*/
 | 
			
		||||
    virtual void parseManagedPack(){};
 | 
			
		||||
 | 
			
		||||
    /** URL of the managed pack.
 | 
			
		||||
     *  Not the version-specific one.
 | 
			
		||||
     */
 | 
			
		||||
    [[nodiscard]] virtual QString url() const { return {}; };
 | 
			
		||||
 | 
			
		||||
    void setInstanceWindow(InstanceWindow* window) { m_instance_window = window; }
 | 
			
		||||
 | 
			
		||||
   public slots:
 | 
			
		||||
    /** Gets the current version selection and update the UI, including the update button and the changelog.
 | 
			
		||||
     */
 | 
			
		||||
    virtual void suggestVersion();
 | 
			
		||||
 | 
			
		||||
    virtual void update(){};
 | 
			
		||||
 | 
			
		||||
   protected slots:
 | 
			
		||||
    /** Does the necessary UI changes for when something failed.
 | 
			
		||||
     *
 | 
			
		||||
     *  This includes:
 | 
			
		||||
     *  - Setting an appropriate text on the version selector to indicate a fail;
 | 
			
		||||
     *  - Setting an appropriate text on the changelog text browser to indicate a fail;
 | 
			
		||||
     *  - Disable the update button.
 | 
			
		||||
     */
 | 
			
		||||
    void setFailState();
 | 
			
		||||
 | 
			
		||||
   protected:
 | 
			
		||||
    ManagedPackPage(BaseInstance* inst, InstanceWindow* instance_window, QWidget* parent = nullptr);
 | 
			
		||||
 | 
			
		||||
    /** Run the InstanceTask, with a progress dialog and all.
 | 
			
		||||
     *  Similar to MainWindow::instanceFromInstanceTask
 | 
			
		||||
     *
 | 
			
		||||
     *  Returns whether the task was successful.
 | 
			
		||||
     */
 | 
			
		||||
    bool runUpdateTask(InstanceTask*);
 | 
			
		||||
 | 
			
		||||
   protected:
 | 
			
		||||
    InstanceWindow* m_instance_window = nullptr;
 | 
			
		||||
 | 
			
		||||
    Ui::ManagedPackPage* ui;
 | 
			
		||||
    BaseInstance* m_inst;
 | 
			
		||||
 | 
			
		||||
    bool m_loaded = false;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/** Simple page for when we aren't a managed pack. */
 | 
			
		||||
class GenericManagedPackPage final : public ManagedPackPage {
 | 
			
		||||
    Q_OBJECT
 | 
			
		||||
 | 
			
		||||
   public:
 | 
			
		||||
    GenericManagedPackPage(BaseInstance* inst, InstanceWindow* instance_window, QWidget* parent = nullptr)
 | 
			
		||||
        : ManagedPackPage(inst, instance_window, parent)
 | 
			
		||||
    {}
 | 
			
		||||
    ~GenericManagedPackPage() override = default;
 | 
			
		||||
 | 
			
		||||
    // TODO: We may want to show this page with some useful info at some point.
 | 
			
		||||
    [[nodiscard]] bool shouldDisplay() const override { return false; };
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class ModrinthManagedPackPage final : public ManagedPackPage {
 | 
			
		||||
    Q_OBJECT
 | 
			
		||||
 | 
			
		||||
   public:
 | 
			
		||||
    ModrinthManagedPackPage(BaseInstance* inst, InstanceWindow* instance_window, QWidget* parent = nullptr);
 | 
			
		||||
    ~ModrinthManagedPackPage() override = default;
 | 
			
		||||
 | 
			
		||||
    void parseManagedPack() override;
 | 
			
		||||
    [[nodiscard]] QString url() const override;
 | 
			
		||||
 | 
			
		||||
   public slots:
 | 
			
		||||
    void suggestVersion() override;
 | 
			
		||||
 | 
			
		||||
    void update() override;
 | 
			
		||||
 | 
			
		||||
   private:
 | 
			
		||||
    NetJob::Ptr m_fetch_job = nullptr;
 | 
			
		||||
 | 
			
		||||
    Modrinth::Modpack m_pack;
 | 
			
		||||
    ModrinthAPI m_api;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class FlameManagedPackPage final : public ManagedPackPage {
 | 
			
		||||
    Q_OBJECT
 | 
			
		||||
 | 
			
		||||
   public:
 | 
			
		||||
    FlameManagedPackPage(BaseInstance* inst, InstanceWindow* instance_window, QWidget* parent = nullptr);
 | 
			
		||||
    ~FlameManagedPackPage() override = default;
 | 
			
		||||
 | 
			
		||||
    void parseManagedPack() override;
 | 
			
		||||
    [[nodiscard]] QString url() const override;
 | 
			
		||||
 | 
			
		||||
   public slots:
 | 
			
		||||
    void suggestVersion() override;
 | 
			
		||||
 | 
			
		||||
    void update() override;
 | 
			
		||||
 | 
			
		||||
   private:
 | 
			
		||||
    NetJob::Ptr m_fetch_job = nullptr;
 | 
			
		||||
 | 
			
		||||
    Flame::IndexedPack m_pack;
 | 
			
		||||
    FlameAPI m_api;
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										193
									
								
								launcher/ui/pages/instance/ManagedPackPage.ui
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										193
									
								
								launcher/ui/pages/instance/ManagedPackPage.ui
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,193 @@
 | 
			
		||||
<?xml version="1.0" encoding="UTF-8"?>
 | 
			
		||||
<ui version="4.0">
 | 
			
		||||
 <class>ManagedPackPage</class>
 | 
			
		||||
 <widget class="QWidget" name="ManagedPackPage">
 | 
			
		||||
  <property name="geometry">
 | 
			
		||||
   <rect>
 | 
			
		||||
    <x>0</x>
 | 
			
		||||
    <y>0</y>
 | 
			
		||||
    <width>731</width>
 | 
			
		||||
    <height>538</height>
 | 
			
		||||
   </rect>
 | 
			
		||||
  </property>
 | 
			
		||||
  <layout class="QGridLayout" name="gridLayout">
 | 
			
		||||
   <property name="leftMargin">
 | 
			
		||||
    <number>9</number>
 | 
			
		||||
   </property>
 | 
			
		||||
   <property name="topMargin">
 | 
			
		||||
    <number>9</number>
 | 
			
		||||
   </property>
 | 
			
		||||
   <property name="rightMargin">
 | 
			
		||||
    <number>9</number>
 | 
			
		||||
   </property>
 | 
			
		||||
   <property name="bottomMargin">
 | 
			
		||||
    <number>9</number>
 | 
			
		||||
   </property>
 | 
			
		||||
   <item row="0" column="0">
 | 
			
		||||
    <layout class="QVBoxLayout" name="verticalLayout_2">
 | 
			
		||||
     <item>
 | 
			
		||||
      <widget class="QGroupBox" name="packInformationBox">
 | 
			
		||||
       <property name="sizePolicy">
 | 
			
		||||
        <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
 | 
			
		||||
         <horstretch>0</horstretch>
 | 
			
		||||
         <verstretch>0</verstretch>
 | 
			
		||||
        </sizepolicy>
 | 
			
		||||
       </property>
 | 
			
		||||
       <property name="title">
 | 
			
		||||
        <string>Pack information</string>
 | 
			
		||||
       </property>
 | 
			
		||||
       <layout class="QFormLayout" name="formLayout_2">
 | 
			
		||||
        <item row="0" column="0">
 | 
			
		||||
         <layout class="QHBoxLayout" name="packNameLayout">
 | 
			
		||||
          <item>
 | 
			
		||||
           <widget class="QLabel" name="packNameLabel">
 | 
			
		||||
            <property name="text">
 | 
			
		||||
             <string>Pack name:</string>
 | 
			
		||||
            </property>
 | 
			
		||||
           </widget>
 | 
			
		||||
          </item>
 | 
			
		||||
          <item>
 | 
			
		||||
           <widget class="QLabel" name="packName">
 | 
			
		||||
            <property name="text">
 | 
			
		||||
             <string notr="true">placeholder</string>
 | 
			
		||||
            </property>
 | 
			
		||||
           </widget>
 | 
			
		||||
          </item>
 | 
			
		||||
         </layout>
 | 
			
		||||
        </item>
 | 
			
		||||
        <item row="1" column="0">
 | 
			
		||||
         <layout class="QHBoxLayout" name="packVersionLayout">
 | 
			
		||||
          <item>
 | 
			
		||||
           <widget class="QLabel" name="packVersionLabel">
 | 
			
		||||
            <property name="text">
 | 
			
		||||
             <string>Current version:</string>
 | 
			
		||||
            </property>
 | 
			
		||||
           </widget>
 | 
			
		||||
          </item>
 | 
			
		||||
          <item>
 | 
			
		||||
           <widget class="QLabel" name="packVersion">
 | 
			
		||||
            <property name="cursor">
 | 
			
		||||
             <cursorShape>IBeamCursor</cursorShape>
 | 
			
		||||
            </property>
 | 
			
		||||
            <property name="text">
 | 
			
		||||
             <string notr="true">placeholder</string>
 | 
			
		||||
            </property>
 | 
			
		||||
            <property name="textInteractionFlags">
 | 
			
		||||
             <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
 | 
			
		||||
            </property>
 | 
			
		||||
           </widget>
 | 
			
		||||
          </item>
 | 
			
		||||
         </layout>
 | 
			
		||||
        </item>
 | 
			
		||||
        <item row="2" column="0">
 | 
			
		||||
         <layout class="QHBoxLayout" name="packOriginLayout">
 | 
			
		||||
          <item>
 | 
			
		||||
           <widget class="QLabel" name="packOriginLabel">
 | 
			
		||||
            <property name="text">
 | 
			
		||||
             <string>Provider information:</string>
 | 
			
		||||
            </property>
 | 
			
		||||
           </widget>
 | 
			
		||||
          </item>
 | 
			
		||||
          <item>
 | 
			
		||||
           <widget class="QLabel" name="packOrigin">
 | 
			
		||||
            <property name="cursor">
 | 
			
		||||
             <cursorShape>IBeamCursor</cursorShape>
 | 
			
		||||
            </property>
 | 
			
		||||
            <property name="text">
 | 
			
		||||
             <string notr="true">placeholder</string>
 | 
			
		||||
            </property>
 | 
			
		||||
            <property name="textFormat">
 | 
			
		||||
             <enum>Qt::RichText</enum>
 | 
			
		||||
            </property>
 | 
			
		||||
            <property name="openExternalLinks">
 | 
			
		||||
             <bool>true</bool>
 | 
			
		||||
            </property>
 | 
			
		||||
            <property name="textInteractionFlags">
 | 
			
		||||
             <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
 | 
			
		||||
            </property>
 | 
			
		||||
           </widget>
 | 
			
		||||
          </item>
 | 
			
		||||
         </layout>
 | 
			
		||||
        </item>
 | 
			
		||||
       </layout>
 | 
			
		||||
      </widget>
 | 
			
		||||
     </item>
 | 
			
		||||
     <item>
 | 
			
		||||
      <widget class="Line" name="line">
 | 
			
		||||
       <property name="orientation">
 | 
			
		||||
        <enum>Qt::Horizontal</enum>
 | 
			
		||||
       </property>
 | 
			
		||||
      </widget>
 | 
			
		||||
     </item>
 | 
			
		||||
     <item>
 | 
			
		||||
      <layout class="QHBoxLayout" name="horizontalLayout">
 | 
			
		||||
       <item>
 | 
			
		||||
        <widget class="QLabel" name="updateToVersionLabel">
 | 
			
		||||
         <property name="sizePolicy">
 | 
			
		||||
          <sizepolicy hsizetype="Fixed" vsizetype="Preferred">
 | 
			
		||||
           <horstretch>0</horstretch>
 | 
			
		||||
           <verstretch>0</verstretch>
 | 
			
		||||
          </sizepolicy>
 | 
			
		||||
         </property>
 | 
			
		||||
         <property name="text">
 | 
			
		||||
          <string>Update to version:</string>
 | 
			
		||||
         </property>
 | 
			
		||||
        </widget>
 | 
			
		||||
       </item>
 | 
			
		||||
       <item>
 | 
			
		||||
        <widget class="QComboBox" name="versionsComboBox"/>
 | 
			
		||||
       </item>
 | 
			
		||||
       <item>
 | 
			
		||||
        <widget class="QPushButton" name="updateButton">
 | 
			
		||||
         <property name="enabled">
 | 
			
		||||
          <bool>false</bool>
 | 
			
		||||
         </property>
 | 
			
		||||
         <property name="sizePolicy">
 | 
			
		||||
          <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
 | 
			
		||||
           <horstretch>0</horstretch>
 | 
			
		||||
           <verstretch>0</verstretch>
 | 
			
		||||
          </sizepolicy>
 | 
			
		||||
         </property>
 | 
			
		||||
         <property name="text">
 | 
			
		||||
          <string>Fetching versions...</string>
 | 
			
		||||
         </property>
 | 
			
		||||
        </widget>
 | 
			
		||||
       </item>
 | 
			
		||||
      </layout>
 | 
			
		||||
     </item>
 | 
			
		||||
     <item>
 | 
			
		||||
      <widget class="QGroupBox" name="changelogBox">
 | 
			
		||||
       <property name="sizePolicy">
 | 
			
		||||
        <sizepolicy hsizetype="Preferred" vsizetype="Expanding">
 | 
			
		||||
         <horstretch>0</horstretch>
 | 
			
		||||
         <verstretch>0</verstretch>
 | 
			
		||||
        </sizepolicy>
 | 
			
		||||
       </property>
 | 
			
		||||
       <property name="title">
 | 
			
		||||
        <string>Changelog</string>
 | 
			
		||||
       </property>
 | 
			
		||||
       <layout class="QVBoxLayout" name="verticalLayout_3">
 | 
			
		||||
        <item>
 | 
			
		||||
         <widget class="QTextBrowser" name="changelogTextBrowser">
 | 
			
		||||
          <property name="placeholderText">
 | 
			
		||||
           <string>No changelog available for this version!</string>
 | 
			
		||||
          </property>
 | 
			
		||||
         </widget>
 | 
			
		||||
        </item>
 | 
			
		||||
       </layout>
 | 
			
		||||
      </widget>
 | 
			
		||||
     </item>
 | 
			
		||||
    </layout>
 | 
			
		||||
   </item>
 | 
			
		||||
   <item row="1" column="0">
 | 
			
		||||
    <widget class="QPushButton" name="reloadButton">
 | 
			
		||||
     <property name="text">
 | 
			
		||||
      <string>Reload page</string>
 | 
			
		||||
     </property>
 | 
			
		||||
    </widget>
 | 
			
		||||
   </item>
 | 
			
		||||
  </layout>
 | 
			
		||||
 </widget>
 | 
			
		||||
 <resources/>
 | 
			
		||||
 <connections/>
 | 
			
		||||
</ui>
 | 
			
		||||
@@ -197,12 +197,18 @@ void FlamePage::suggestCurrent()
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (selectedVersion.isEmpty() || selectedVersion == "-1") {
 | 
			
		||||
    if (m_selected_version_index == -1) {
 | 
			
		||||
        dialog->setSuggestedPack();
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    dialog->setSuggestedPack(current.name, new InstanceImportTask(selectedVersion,this));
 | 
			
		||||
    auto version = current.versions.at(m_selected_version_index);
 | 
			
		||||
 | 
			
		||||
    QMap<QString, QString> extra_info;
 | 
			
		||||
    extra_info.insert("pack_id", QString::number(current.addonId));
 | 
			
		||||
    extra_info.insert("pack_version_id", QString::number(version.fileId));
 | 
			
		||||
 | 
			
		||||
    dialog->setSuggestedPack(current.name, new InstanceImportTask(version.downloadUrl, this, std::move(extra_info)));
 | 
			
		||||
    QString editedLogoName;
 | 
			
		||||
    editedLogoName = "curseforge_" + current.logoName.section(".", 0, 0);
 | 
			
		||||
    listModel->getLogo(current.logoName, current.logoUrl,
 | 
			
		||||
@@ -211,11 +217,18 @@ void FlamePage::suggestCurrent()
 | 
			
		||||
 | 
			
		||||
void FlamePage::onVersionSelectionChanged(QString data)
 | 
			
		||||
{
 | 
			
		||||
    if (data.isNull() || data.isEmpty()) {
 | 
			
		||||
        selectedVersion = "";
 | 
			
		||||
    bool is_blocked = false;
 | 
			
		||||
    ui->versionSelectionBox->currentData().toInt(&is_blocked);
 | 
			
		||||
 | 
			
		||||
    if (data.isNull() || data.isEmpty() || is_blocked) {
 | 
			
		||||
        m_selected_version_index = -1;
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
    selectedVersion = ui->versionSelectionBox->currentData().toString();
 | 
			
		||||
 | 
			
		||||
    m_selected_version_index = ui->versionSelectionBox->currentIndex();
 | 
			
		||||
 | 
			
		||||
    Q_ASSERT(current.versions.at(m_selected_version_index).downloadUrl == ui->versionSelectionBox->currentData().toString());
 | 
			
		||||
 | 
			
		||||
    suggestCurrent();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -99,5 +99,5 @@ private:
 | 
			
		||||
    Flame::ListModel* listModel = nullptr;
 | 
			
		||||
    Flame::IndexedPack current;
 | 
			
		||||
 | 
			
		||||
    QString selectedVersion;
 | 
			
		||||
    int m_selected_version_index = -1;
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
@@ -300,7 +300,11 @@ void ModrinthPage::suggestCurrent()
 | 
			
		||||
 | 
			
		||||
    for (auto& ver : current.versions) {
 | 
			
		||||
        if (ver.id == selectedVersion) {
 | 
			
		||||
            dialog->setSuggestedPack(current.name, ver.version, new InstanceImportTask(ver.download_url, this));
 | 
			
		||||
            QMap<QString, QString> extra_info;
 | 
			
		||||
            extra_info.insert("pack_id", current.id);
 | 
			
		||||
            extra_info.insert("pack_version_id", ver.id);
 | 
			
		||||
 | 
			
		||||
            dialog->setSuggestedPack(current.name, ver.version, new InstanceImportTask(ver.download_url, this, std::move(extra_info)));
 | 
			
		||||
            auto iconName = current.iconName;
 | 
			
		||||
            m_model->getLogo(iconName, current.iconUrl.toString(),
 | 
			
		||||
                             [this, iconName](QString logo) { dialog->setSuggestedIconFromFile(logo, iconName); });
 | 
			
		||||
 
 | 
			
		||||
@@ -130,6 +130,11 @@ bool PageContainer::selectPage(QString pageId)
 | 
			
		||||
    return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
BasePage* PageContainer::getPage(QString pageId)
 | 
			
		||||
{
 | 
			
		||||
    return m_model->findPageEntryById(pageId);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void PageContainer::refreshContainer()
 | 
			
		||||
{
 | 
			
		||||
    m_proxyModel->invalidate();
 | 
			
		||||
 
 | 
			
		||||
@@ -79,6 +79,7 @@ public:
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    virtual bool selectPage(QString pageId) override;
 | 
			
		||||
    BasePage* getPage(QString pageId) override;
 | 
			
		||||
 | 
			
		||||
    void refreshContainer() override;
 | 
			
		||||
    virtual void setParentContainer(BasePageContainer * container)
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user