aboutsummaryrefslogtreecommitdiff
path: root/application/pages/modplatform
diff options
context:
space:
mode:
Diffstat (limited to 'application/pages/modplatform')
-rw-r--r--application/pages/modplatform/TechnicPage.cpp26
-rw-r--r--application/pages/modplatform/TechnicPage.h61
-rw-r--r--application/pages/modplatform/TechnicPage.ui113
-rw-r--r--application/pages/modplatform/TwitchPage.cpp60
-rw-r--r--application/pages/modplatform/TwitchPage.ui62
-rw-r--r--application/pages/modplatform/legacy_ftb/ListModel.cpp (renamed from application/pages/modplatform/FtbListModel.cpp)68
-rw-r--r--application/pages/modplatform/legacy_ftb/ListModel.h (renamed from application/pages/modplatform/FtbListModel.h)30
-rw-r--r--application/pages/modplatform/legacy_ftb/Page.cpp (renamed from application/pages/modplatform/FTBPage.cpp)114
-rw-r--r--application/pages/modplatform/legacy_ftb/Page.h (renamed from application/pages/modplatform/FTBPage.h)59
-rw-r--r--application/pages/modplatform/legacy_ftb/Page.ui (renamed from application/pages/modplatform/FTBPage.ui)4
-rw-r--r--application/pages/modplatform/twitch/TwitchData.h38
-rw-r--r--application/pages/modplatform/twitch/TwitchModel.cpp314
-rw-r--r--application/pages/modplatform/twitch/TwitchModel.h76
-rw-r--r--application/pages/modplatform/twitch/TwitchPage.cpp111
-rw-r--r--application/pages/modplatform/twitch/TwitchPage.h (renamed from application/pages/modplatform/TwitchPage.h)20
-rw-r--r--application/pages/modplatform/twitch/TwitchPage.ui88
16 files changed, 787 insertions, 457 deletions
diff --git a/application/pages/modplatform/TechnicPage.cpp b/application/pages/modplatform/TechnicPage.cpp
deleted file mode 100644
index 2f95bec8..00000000
--- a/application/pages/modplatform/TechnicPage.cpp
+++ /dev/null
@@ -1,26 +0,0 @@
-#include "TechnicPage.h"
-#include "ui_TechnicPage.h"
-
-#include "MultiMC.h"
-#include "dialogs/NewInstanceDialog.h"
-
-TechnicPage::TechnicPage(NewInstanceDialog* dialog, QWidget *parent)
- : QWidget(parent), ui(new Ui::TechnicPage), dialog(dialog)
-{
- ui->setupUi(this);
-}
-
-TechnicPage::~TechnicPage()
-{
- delete ui;
-}
-
-bool TechnicPage::shouldDisplay() const
-{
- return true;
-}
-
-void TechnicPage::openedImpl()
-{
- dialog->setSuggestedPack();
-}
diff --git a/application/pages/modplatform/TechnicPage.h b/application/pages/modplatform/TechnicPage.h
deleted file mode 100644
index cc82ddf3..00000000
--- a/application/pages/modplatform/TechnicPage.h
+++ /dev/null
@@ -1,61 +0,0 @@
-/* Copyright 2013-2019 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <QWidget>
-
-#include "pages/BasePage.h"
-#include <MultiMC.h>
-#include "tasks/Task.h"
-
-namespace Ui
-{
-class TechnicPage;
-}
-
-class NewInstanceDialog;
-
-class TechnicPage : public QWidget, public BasePage
-{
- Q_OBJECT
-
-public:
- explicit TechnicPage(NewInstanceDialog* dialog, QWidget *parent = 0);
- virtual ~TechnicPage();
- virtual QString displayName() const override
- {
- return tr("Technic");
- }
- virtual QIcon icon() const override
- {
- return MMC->getThemedIcon("technic");
- }
- virtual QString id() const override
- {
- return "technic";
- }
- virtual QString helpPage() const override
- {
- return "Technic-platform";
- }
- virtual bool shouldDisplay() const override;
-
- void openedImpl() override;
-
-private:
- Ui::TechnicPage *ui = nullptr;
- NewInstanceDialog* dialog = nullptr;
-};
diff --git a/application/pages/modplatform/TechnicPage.ui b/application/pages/modplatform/TechnicPage.ui
deleted file mode 100644
index 702427b5..00000000
--- a/application/pages/modplatform/TechnicPage.ui
+++ /dev/null
@@ -1,113 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<ui version="4.0">
- <class>TechnicPage</class>
- <widget class="QWidget" name="TechnicPage">
- <property name="geometry">
- <rect>
- <x>0</x>
- <y>0</y>
- <width>546</width>
- <height>405</height>
- </rect>
- </property>
- <layout class="QVBoxLayout" name="verticalLayout_5">
- <property name="leftMargin">
- <number>0</number>
- </property>
- <property name="topMargin">
- <number>0</number>
- </property>
- <property name="rightMargin">
- <number>0</number>
- </property>
- <property name="bottomMargin">
- <number>0</number>
- </property>
- <item>
- <spacer name="verticalSpacer">
- <property name="orientation">
- <enum>Qt::Vertical</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>20</width>
- <height>40</height>
- </size>
- </property>
- </spacer>
- </item>
- <item>
- <widget class="QLabel" name="label">
- <property name="font">
- <font>
- <pointsize>40</pointsize>
- </font>
- </property>
- <property name="styleSheet">
- <string notr="true">color:#ffc000</string>
- </property>
- <property name="text">
- <string notr="true">UNDER</string>
- </property>
- <property name="textFormat">
- <enum>Qt::PlainText</enum>
- </property>
- <property name="alignment">
- <set>Qt::AlignCenter</set>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QLabel" name="label_3">
- <property name="text">
- <string notr="true"/>
- </property>
- <property name="pixmap">
- <pixmap resource="../../resources/assets/assets.qrc">:/assets/underconstruction</pixmap>
- </property>
- <property name="alignment">
- <set>Qt::AlignCenter</set>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QLabel" name="label_2">
- <property name="font">
- <font>
- <pointsize>40</pointsize>
- </font>
- </property>
- <property name="styleSheet">
- <string notr="true">color:#7ca32b</string>
- </property>
- <property name="text">
- <string notr="true">CONSTRUCTION</string>
- </property>
- <property name="textFormat">
- <enum>Qt::PlainText</enum>
- </property>
- <property name="alignment">
- <set>Qt::AlignCenter</set>
- </property>
- </widget>
- </item>
- <item>
- <spacer name="verticalSpacer_2">
- <property name="orientation">
- <enum>Qt::Vertical</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>20</width>
- <height>40</height>
- </size>
- </property>
- </spacer>
- </item>
- </layout>
- </widget>
- <resources>
- <include location="../../resources/assets/assets.qrc"/>
- </resources>
- <connections/>
-</ui>
diff --git a/application/pages/modplatform/TwitchPage.cpp b/application/pages/modplatform/TwitchPage.cpp
deleted file mode 100644
index ea0f9267..00000000
--- a/application/pages/modplatform/TwitchPage.cpp
+++ /dev/null
@@ -1,60 +0,0 @@
-#include "TwitchPage.h"
-#include "ui_TwitchPage.h"
-
-#include "MultiMC.h"
-#include "dialogs/NewInstanceDialog.h"
-#include <InstanceImportTask.h>
-
-TwitchPage::TwitchPage(NewInstanceDialog* dialog, QWidget *parent)
- : QWidget(parent), ui(new Ui::TwitchPage), dialog(dialog)
-{
- ui->setupUi(this);
- connect(ui->checkButton, &QPushButton::clicked, this, &TwitchPage::triggerCheck);
-}
-
-TwitchPage::~TwitchPage()
-{
- delete ui;
-}
-
-bool TwitchPage::shouldDisplay() const
-{
- return true;
-}
-
-void TwitchPage::openedImpl()
-{
- dialog->setSuggestedPack();
-}
-
-void TwitchPage::triggerCheck(bool)
-{
- if(m_modIdResolver) {
- return;
- }
- auto task = new Flame::UrlResolvingTask(ui->lineEdit->text());
- connect(task, &Task::finished, this, &TwitchPage::checkDone);
- m_modIdResolver.reset(task);
- task->start();
-}
-
-void TwitchPage::setUrl(const QString& url)
-{
- ui->lineEdit->setText(url);
- triggerCheck(true);
-}
-
-void TwitchPage::checkDone()
-{
- auto result = m_modIdResolver->getResults();
- auto formatted = QString("Project %1, File %2").arg(result.projectId).arg(result.fileId);
- if(result.resolved && result.type == Flame::File::Type::Modpack) {
- ui->twitchLabel->setText(formatted);
- QFileInfo fi(result.fileName);
- dialog->setSuggestedPack(fi.completeBaseName(), new InstanceImportTask(result.url));
- } else {
- ui->twitchLabel->setPixmap(QPixmap(QString::fromUtf8(":/assets/deadglitch")));
- dialog->setSuggestedPack();
- }
- m_modIdResolver.reset();
-}
diff --git a/application/pages/modplatform/TwitchPage.ui b/application/pages/modplatform/TwitchPage.ui
deleted file mode 100644
index 0db2484d..00000000
--- a/application/pages/modplatform/TwitchPage.ui
+++ /dev/null
@@ -1,62 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<ui version="4.0">
- <class>TwitchPage</class>
- <widget class="QWidget" name="TwitchPage">
- <property name="geometry">
- <rect>
- <x>0</x>
- <y>0</y>
- <width>546</width>
- <height>405</height>
- </rect>
- </property>
- <layout class="QGridLayout" name="gridLayout">
- <item row="0" column="1">
- <widget class="QLineEdit" name="lineEdit"/>
- </item>
- <item row="0" column="0">
- <widget class="QLabel" name="label">
- <property name="text">
- <string>Twitch URL:</string>
- </property>
- </widget>
- </item>
- <item row="1" column="0" colspan="3">
- <widget class="QLabel" name="twitchLabel">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Preferred" vsizetype="Expanding">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="font">
- <font>
- <pointsize>40</pointsize>
- </font>
- </property>
- <property name="pixmap">
- <pixmap resource="../../resources/assets/assets.qrc">:/assets/deadglitch</pixmap>
- </property>
- <property name="alignment">
- <set>Qt::AlignCenter</set>
- </property>
- </widget>
- </item>
- <item row="0" column="2">
- <widget class="QPushButton" name="checkButton">
- <property name="text">
- <string>Check</string>
- </property>
- </widget>
- </item>
- </layout>
- </widget>
- <tabstops>
- <tabstop>lineEdit</tabstop>
- <tabstop>checkButton</tabstop>
- </tabstops>
- <resources>
- <include location="../../resources/assets/assets.qrc"/>
- </resources>
- <connections/>
-</ui>
diff --git a/application/pages/modplatform/FtbListModel.cpp b/application/pages/modplatform/legacy_ftb/ListModel.cpp
index 51aec890..105db25a 100644
--- a/application/pages/modplatform/FtbListModel.cpp
+++ b/application/pages/modplatform/legacy_ftb/ListModel.cpp
@@ -1,4 +1,4 @@
-#include "FtbListModel.h"
+#include "ListModel.h"
#include "MultiMC.h"
#include <MMCStrings.h>
@@ -12,17 +12,19 @@
#include "net/URLConstants.h"
-FtbFilterModel::FtbFilterModel(QObject *parent) : QSortFilterProxyModel(parent)
+namespace LegacyFTB {
+
+FilterModel::FilterModel(QObject *parent) : QSortFilterProxyModel(parent)
{
currentSorting = Sorting::ByGameVersion;
sortings.insert(tr("Sort by name"), Sorting::ByName);
sortings.insert(tr("Sort by game version"), Sorting::ByGameVersion);
}
-bool FtbFilterModel::lessThan(const QModelIndex &left, const QModelIndex &right) const
+bool FilterModel::lessThan(const QModelIndex &left, const QModelIndex &right) const
{
- FtbModpack leftPack = sourceModel()->data(left, Qt::UserRole).value<FtbModpack>();
- FtbModpack rightPack = sourceModel()->data(right, Qt::UserRole).value<FtbModpack>();
+ Modpack leftPack = sourceModel()->data(left, Qt::UserRole).value<Modpack>();
+ Modpack rightPack = sourceModel()->data(right, Qt::UserRole).value<Modpack>();
if(currentSorting == Sorting::ByGameVersion) {
Version lv(leftPack.mcVersion);
@@ -38,66 +40,66 @@ bool FtbFilterModel::lessThan(const QModelIndex &left, const QModelIndex &right)
return true;
}
-bool FtbFilterModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const
+bool FilterModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const
{
return true;
}
-const QMap<QString, FtbFilterModel::Sorting> FtbFilterModel::getAvailableSortings()
+const QMap<QString, FilterModel::Sorting> FilterModel::getAvailableSortings()
{
return sortings;
}
-QString FtbFilterModel::translateCurrentSorting()
+QString FilterModel::translateCurrentSorting()
{
return sortings.key(currentSorting);
}
-void FtbFilterModel::setSorting(Sorting s)
+void FilterModel::setSorting(Sorting s)
{
currentSorting = s;
invalidate();
}
-FtbFilterModel::Sorting FtbFilterModel::getCurrentSorting()
+FilterModel::Sorting FilterModel::getCurrentSorting()
{
return currentSorting;
}
-FtbListModel::FtbListModel(QObject *parent) : QAbstractListModel(parent)
+ListModel::ListModel(QObject *parent) : QAbstractListModel(parent)
{
}
-FtbListModel::~FtbListModel()
+ListModel::~ListModel()
{
}
-QString FtbListModel::translatePackType(FtbPackType type) const
+QString ListModel::translatePackType(PackType type) const
{
switch(type)
{
- case FtbPackType::Public:
+ case PackType::Public:
return tr("Public Modpack");
- case FtbPackType::ThirdParty:
+ case PackType::ThirdParty:
return tr("Third Party Modpack");
- case FtbPackType::Private:
+ case PackType::Private:
return tr("Private Modpack");
}
qWarning() << "Unknown FTB modpack type:" << int(type);
return QString();
}
-int FtbListModel::rowCount(const QModelIndex &parent) const
+int ListModel::rowCount(const QModelIndex &parent) const
{
return modpacks.size();
}
-int FtbListModel::columnCount(const QModelIndex &parent) const
+int ListModel::columnCount(const QModelIndex &parent) const
{
return 1;
}
-QVariant FtbListModel::data(const QModelIndex &index, int role) const
+QVariant ListModel::data(const QModelIndex &index, int role) const
{
int pos = index.row();
if(pos >= modpacks.size() || pos < 0 || !index.isValid())
@@ -105,7 +107,7 @@ QVariant FtbListModel::data(const QModelIndex &index, int role) const
return QString("INVALID INDEX %1").arg(pos);
}
- FtbModpack pack = modpacks.at(pos);
+ Modpack pack = modpacks.at(pos);
if(role == Qt::DisplayRole)
{
return pack.name + "\n" + translatePackType(pack.type);
@@ -129,7 +131,7 @@ QVariant FtbListModel::data(const QModelIndex &index, int role) const
return (m_logoMap.value(pack.logo));
}
QIcon icon = MMC->getThemedIcon("screenshot-placeholder");
- ((FtbListModel *)this)->requestLogo(pack.logo);
+ ((ListModel *)this)->requestLogo(pack.logo);
return icon;
}
else if(role == Qt::TextColorRole)
@@ -156,33 +158,33 @@ QVariant FtbListModel::data(const QModelIndex &index, int role) const
return QVariant();
}
-void FtbListModel::fill(FtbModpackList modpacks)
+void ListModel::fill(ModpackList modpacks)
{
beginResetModel();
this->modpacks = modpacks;
endResetModel();
}
-void FtbListModel::addPack(FtbModpack modpack)
+void ListModel::addPack(Modpack modpack)
{
beginResetModel();
this->modpacks.append(modpack);
endResetModel();
}
-void FtbListModel::clear()
+void ListModel::clear()
{
beginResetModel();
modpacks.clear();
endResetModel();
}
-FtbModpack FtbListModel::at(int row)
+Modpack ListModel::at(int row)
{
return modpacks.at(row);
}
-void FtbListModel::remove(int row)
+void ListModel::remove(int row)
{
if(row < 0 || row >= modpacks.size())
{
@@ -194,20 +196,20 @@ void FtbListModel::remove(int row)
endRemoveRows();
}
-void FtbListModel::logoLoaded(QString logo, QIcon out)
+void ListModel::logoLoaded(QString logo, QIcon out)
{
m_loadingLogos.removeAll(logo);
m_logoMap.insert(logo, out);
emit dataChanged(createIndex(0, 0), createIndex(1, 0));
}
-void FtbListModel::logoFailed(QString logo)
+void ListModel::logoFailed(QString logo)
{
m_failedLogos.append(logo);
m_loadingLogos.removeAll(logo);
}
-void FtbListModel::requestLogo(QString file)
+void ListModel::requestLogo(QString file)
{
if(m_loadingLogos.contains(file) || m_failedLogos.contains(file))
{
@@ -216,7 +218,7 @@ void FtbListModel::requestLogo(QString file)
MetaEntryPtr entry = ENV.metacache()->resolveEntry("FTBPacks", QString("logos/%1").arg(file.section(".", 0, 0)));
NetJob *job = new NetJob(QString("FTB Icon Download for %1").arg(file));
- job->addNetAction(Net::Download::makeCached(QUrl(QString(URLConstants::FTB_CDN_BASE_URL + "static/%1").arg(file)), entry));
+ job->addNetAction(Net::Download::makeCached(QUrl(QString(URLConstants::LEGACY_FTB_CDN_BASE_URL + "static/%1").arg(file)), entry));
auto fullPath = entry->getFullPath();
QObject::connect(job, &NetJob::finished, this, [this, file, fullPath]
@@ -238,7 +240,7 @@ void FtbListModel::requestLogo(QString file)
m_loadingLogos.append(file);
}
-void FtbListModel::getLogo(const QString &logo, LogoCallback callback)
+void ListModel::getLogo(const QString &logo, LogoCallback callback)
{
if(m_logoMap.contains(logo))
{
@@ -250,7 +252,9 @@ void FtbListModel::getLogo(const QString &logo, LogoCallback callback)
}
}
-Qt::ItemFlags FtbListModel::flags(const QModelIndex &index) const
+Qt::ItemFlags ListModel::flags(const QModelIndex &index) const
{
return QAbstractListModel::flags(index);
}
+
+}
diff --git a/application/pages/modplatform/FtbListModel.h b/application/pages/modplatform/legacy_ftb/ListModel.h
index 34749b24..c55df000 100644
--- a/application/pages/modplatform/FtbListModel.h
+++ b/application/pages/modplatform/legacy_ftb/ListModel.h
@@ -1,6 +1,6 @@
#pragma once
-#include <modplatform/ftb/PackHelpers.h>
+#include <modplatform/legacy_ftb/PackHelpers.h>
#include <RWStorage.h>
#include <QAbstractListModel>
@@ -11,14 +11,16 @@
#include <functional>
-typedef QMap<QString, QIcon> FtbLogoMap;
+namespace LegacyFTB {
+
+typedef QMap<QString, QIcon> FTBLogoMap;
typedef std::function<void(QString)> LogoCallback;
-class FtbFilterModel : public QSortFilterProxyModel
+class FilterModel : public QSortFilterProxyModel
{
Q_OBJECT
public:
- FtbFilterModel(QObject* parent = Q_NULLPTR);
+ FilterModel(QObject* parent = Q_NULLPTR);
enum Sorting {
ByName,
ByGameVersion
@@ -38,18 +40,18 @@ private:
};
-class FtbListModel : public QAbstractListModel
+class ListModel : public QAbstractListModel
{
Q_OBJECT
private:
- FtbModpackList modpacks;
+ ModpackList modpacks;
QStringList m_failedLogos;
QStringList m_loadingLogos;
- FtbLogoMap m_logoMap;
+ FTBLogoMap m_logoMap;
QMap<QString, LogoCallback> waitingCallbacks;
void requestLogo(QString file);
- QString translatePackType(FtbPackType type) const;
+ QString translatePackType(PackType type) const;
private slots:
@@ -57,18 +59,20 @@ private slots:
void logoLoaded(QString logo, QIcon out);
public:
- FtbListModel(QObject *parent);
- ~FtbListModel();
+ ListModel(QObject *parent);
+ ~ListModel();
int rowCount(const QModelIndex &parent) const override;
int columnCount(const QModelIndex &parent) const override;
QVariant data(const QModelIndex &index, int role) const override;
Qt::ItemFlags flags(const QModelIndex &index) const override;
- void fill(FtbModpackList modpacks);
- void addPack(FtbModpack modpack);
+ void fill(ModpackList modpacks);
+ void addPack(Modpack modpack);
void clear();
void remove(int row);
- FtbModpack at(int row);
+ Modpack at(int row);
void getLogo(const QString &logo, LogoCallback callback);
};
+
+}
diff --git a/application/pages/modplatform/FTBPage.cpp b/application/pages/modplatform/legacy_ftb/Page.cpp
index dca86efd..8e40ba9e 100644
--- a/application/pages/modplatform/FTBPage.cpp
+++ b/application/pages/modplatform/legacy_ftb/Page.cpp
@@ -1,27 +1,29 @@
-#include "FTBPage.h"
-#include "ui_FTBPage.h"
+#include "Page.h"
+#include "ui_Page.h"
#include <QInputDialog>
#include "MultiMC.h"
#include "dialogs/CustomMessageBox.h"
#include "dialogs/NewInstanceDialog.h"
-#include "modplatform/ftb/FtbPackFetchTask.h"
-#include "modplatform/ftb/FtbPackInstallTask.h"
-#include "modplatform/ftb/FtbPrivatePackManager.h"
-#include "FtbListModel.h"
+#include "modplatform/legacy_ftb/PackFetchTask.h"
+#include "modplatform/legacy_ftb/PackInstallTask.h"
+#include "modplatform/legacy_ftb/PrivatePackManager.h"
+#include "ListModel.h"
-FTBPage::FTBPage(NewInstanceDialog* dialog, QWidget *parent)
- : QWidget(parent), dialog(dialog), ui(new Ui::FTBPage)
+namespace LegacyFTB {
+
+Page::Page(NewInstanceDialog* dialog, QWidget *parent)
+ : QWidget(parent), dialog(dialog), ui(new Ui::Page)
{
- ftbFetchTask.reset(new FtbPackFetchTask());
- ftbPrivatePacks.reset(new FtbPrivatePackManager());
+ ftbFetchTask.reset(new PackFetchTask());
+ ftbPrivatePacks.reset(new PrivatePackManager());
ui->setupUi(this);
{
- publicFilterModel = new FtbFilterModel(this);
- publicListModel = new FtbListModel(this);
+ publicFilterModel = new FilterModel(this);
+ publicListModel = new ListModel(this);
publicFilterModel->setSourceModel(publicListModel);
ui->publicPackList->setModel(publicFilterModel);
@@ -39,8 +41,8 @@ FTBPage::FTBPage(NewInstanceDialog* dialog, QWidget *parent)
}
{
- thirdPartyFilterModel = new FtbFilterModel(this);
- thirdPartyModel = new FtbListModel(this);
+ thirdPartyFilterModel = new FilterModel(this);
+ thirdPartyModel = new ListModel(this);
thirdPartyFilterModel->setSourceModel(thirdPartyModel);
ui->thirdPartyPackList->setModel(thirdPartyFilterModel);
@@ -53,8 +55,8 @@ FTBPage::FTBPage(NewInstanceDialog* dialog, QWidget *parent)
}
{
- privateFilterModel = new FtbFilterModel(this);
- privateListModel = new FtbListModel(this);
+ privateFilterModel = new FilterModel(this);
+ privateListModel = new ListModel(this);
privateFilterModel->setSourceModel(privateListModel);
ui->privatePackList->setModel(privateFilterModel);
@@ -69,17 +71,17 @@ FTBPage::FTBPage(NewInstanceDialog* dialog, QWidget *parent)
ui->versionSelectionBox->view()->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
ui->versionSelectionBox->view()->parentWidget()->setMaximumHeight(300);
- connect(ui->sortByBox, &QComboBox::currentTextChanged, this, &FTBPage::onSortingSelectionChanged);
- connect(ui->versionSelectionBox, &QComboBox::currentTextChanged, this, &FTBPage::onVersionSelectionItemChanged);
+ connect(ui->sortByBox, &QComboBox::currentTextChanged, this, &Page::onSortingSelectionChanged);
+ connect(ui->versionSelectionBox, &QComboBox::currentTextChanged, this, &Page::onVersionSelectionItemChanged);
- connect(ui->publicPackList->selectionModel(), &QItemSelectionModel::currentChanged, this, &FTBPage::onPublicPackSelectionChanged);
- connect(ui->thirdPartyPackList->selectionModel(), &QItemSelectionModel::currentChanged, this, &FTBPage::onThirdPartyPackSelectionChanged);
- connect(ui->privatePackList->selectionModel(), &QItemSelectionModel::currentChanged, this, &FTBPage::onPrivatePackSelectionChanged);
+ connect(ui->publicPackList->selectionModel(), &QItemSelectionModel::currentChanged, this, &Page::onPublicPackSelectionChanged);
+ connect(ui->thirdPartyPackList->selectionModel(), &QItemSelectionModel::currentChanged, this, &Page::onThirdPartyPackSelectionChanged);
+ connect(ui->privatePackList->selectionModel(), &QItemSelectionModel::currentChanged, this, &Page::onPrivatePackSelectionChanged);
- connect(ui->addPackBtn, &QPushButton::pressed, this, &FTBPage::onAddPackClicked);
- connect(ui->removePackBtn, &QPushButton::pressed, this, &FTBPage::onRemovePackClicked);
+ connect(ui->addPackBtn, &QPushButton::pressed, this, &Page::onAddPackClicked);
+ connect(ui->removePackBtn, &QPushButton::pressed, this, &Page::onRemovePackClicked);
- connect(ui->tabWidget, &QTabWidget::currentChanged, this, &FTBPage::onTabChanged);
+ connect(ui->tabWidget, &QTabWidget::currentChanged, this, &Page::onTabChanged);
// ui->modpackInfo->setOpenExternalLinks(true);
@@ -90,25 +92,25 @@ FTBPage::FTBPage(NewInstanceDialog* dialog, QWidget *parent)
onTabChanged(ui->tabWidget->currentIndex());
}
-FTBPage::~FTBPage()
+Page::~Page()
{
delete ui;
}
-bool FTBPage::shouldDisplay() const
+bool Page::shouldDisplay() const
{
return true;
}
-void FTBPage::openedImpl()
+void Page::openedImpl()
{
if(!initialized)
{
- connect(ftbFetchTask.get(), &FtbPackFetchTask::finished, this, &FTBPage::ftbPackDataDownloadSuccessfully);
- connect(ftbFetchTask.get(), &FtbPackFetchTask::failed, this, &FTBPage::ftbPackDataDownloadFailed);
+ connect(ftbFetchTask.get(), &PackFetchTask::finished, this, &Page::ftbPackDataDownloadSuccessfully);
+ connect(ftbFetchTask.get(), &PackFetchTask::failed, this, &Page::ftbPackDataDownloadFailed);
- connect(ftbFetchTask.get(), &FtbPackFetchTask::privateFileDownloadFinished, this, &FTBPage::ftbPrivatePackDataDownloadSuccessfully);
- connect(ftbFetchTask.get(), &FtbPackFetchTask::privateFileDownloadFailed, this, &FTBPage::ftbPrivatePackDataDownloadFailed);
+ connect(ftbFetchTask.get(), &PackFetchTask::privateFileDownloadFinished, this, &Page::ftbPrivatePackDataDownloadSuccessfully);
+ connect(ftbFetchTask.get(), &PackFetchTask::privateFileDownloadFailed, this, &Page::ftbPrivatePackDataDownloadFailed);
ftbFetchTask->fetch();
ftbPrivatePacks->load();
@@ -118,13 +120,13 @@ void FTBPage::openedImpl()
suggestCurrent();
}
-void FTBPage::suggestCurrent()
+void Page::suggestCurrent()
{
if(isOpened)
{
if(!selected.broken)
{
- dialog->setSuggestedPack(selected.name, new FtbPackInstallTask(selected, selectedVersion));
+ dialog->setSuggestedPack(selected.name, new PackInstallTask(selected, selectedVersion));
QString editedLogoName;
if(selected.logo.toLower().startsWith("ftb"))
{
@@ -137,21 +139,21 @@ void FTBPage::suggestCurrent()
editedLogoName = editedLogoName.left(editedLogoName.lastIndexOf(".png"));
- if(selected.type == FtbPackType::Public)
+ if(selected.type == PackType::Public)
{
publicListModel->getLogo(selected.logo, [this, editedLogoName](QString logo)
{
dialog->setSuggestedIconFromFile(logo, editedLogoName);
});
}
- else if (selected.type == FtbPackType::ThirdParty)
+ else if (selected.type == PackType::ThirdParty)
{
thirdPartyModel->getLogo(selected.logo, [this, editedLogoName](QString logo)
{
dialog->setSuggestedIconFromFile(logo, editedLogoName);
});
}
- else if (selected.type == FtbPackType::Private)
+ else if (selected.type == PackType::Private)
{
privateListModel->getLogo(selected.logo, [this, editedLogoName](QString logo)
{
@@ -166,23 +168,23 @@ void FTBPage::suggestCurrent()
}
}
-void FTBPage::ftbPackDataDownloadSuccessfully(FtbModpackList publicPacks, FtbModpackList thirdPartyPacks)
+void Page::ftbPackDataDownloadSuccessfully(ModpackList publicPacks, ModpackList thirdPartyPacks)
{
publicListModel->fill(publicPacks);
thirdPartyModel->fill(thirdPartyPacks);
}
-void FTBPage::ftbPackDataDownloadFailed(QString reason)
+void Page::ftbPackDataDownloadFailed(QString reason)
{
//TODO: Display the error
}
-void FTBPage::ftbPrivatePackDataDownloadSuccessfully(FtbModpack pack)
+void Page::ftbPrivatePackDataDownloadSuccessfully(Modpack pack)
{
privateListModel->addPack(pack);
}
-void FTBPage::ftbPrivatePackDataDownloadFailed(QString reason, QString packCode)
+void Page::ftbPrivatePackDataDownloadFailed(QString reason, QString packCode)
{
auto reply = QMessageBox::question(
this,
@@ -195,40 +197,40 @@ void FTBPage::ftbPrivatePackDataDownloadFailed(QString reason, QString packCode)
}
}
-void FTBPage::onPublicPackSelectionChanged(QModelIndex now, QModelIndex prev)
+void Page::onPublicPackSelectionChanged(QModelIndex now, QModelIndex prev)
{
if(!now.isValid())
{
onPackSelectionChanged();
return;
}
- FtbModpack selectedPack = publicFilterModel->data(now, Qt::UserRole).value<FtbModpack>();
+ Modpack selectedPack = publicFilterModel->data(now, Qt::UserRole).value<Modpack>();
onPackSelectionChanged(&selectedPack);
}
-void FTBPage::onThirdPartyPackSelectionChanged(QModelIndex now, QModelIndex prev)
+void Page::onThirdPartyPackSelectionChanged(QModelIndex now, QModelIndex prev)
{
if(!now.isValid())
{
onPackSelectionChanged();
return;
}
- FtbModpack selectedPack = thirdPartyFilterModel->data(now, Qt::UserRole).value<FtbModpack>();
+ Modpack selectedPack = thirdPartyFilterModel->data(now, Qt::UserRole).value<Modpack>();
onPackSelectionChanged(&selectedPack);
}
-void FTBPage::onPrivatePackSelectionChanged(QModelIndex now, QModelIndex prev)
+void Page::onPrivatePackSelectionChanged(QModelIndex now, QModelIndex prev)
{
if(!now.isValid())
{
onPackSelectionChanged();
return;
}
- FtbModpack selectedPack = privateFilterModel->data(now, Qt::UserRole).value<FtbModpack>();
+ Modpack selectedPack = privateFilterModel->data(now, Qt::UserRole).value<Modpack>();
onPackSelectionChanged(&selectedPack);
}
-void FTBPage::onPackSelectionChanged(FtbModpack* pack)
+void Page::onPackSelectionChanged(Modpack* pack)
{
ui->versionSelectionBox->clear();
if(pack)
@@ -266,7 +268,7 @@ void FTBPage::onPackSelectionChanged(FtbModpack* pack)
suggestCurrent();
}
-void FTBPage::onVersionSelectionItemChanged(QString data)
+void Page::onVersionSelectionItemChanged(QString data)
{
if(data.isNull() || data.isEmpty())
{
@@ -278,15 +280,15 @@ void FTBPage::onVersionSelectionItemChanged(QString data)
suggestCurrent();
}
-void FTBPage::onSortingSelectionChanged(QString data)
+void Page::onSortingSelectionChanged(QString data)
{
- FtbFilterModel::Sorting toSet = publicFilterModel->getAvailableSortings().value(data);
+ FilterModel::Sorting toSet = publicFilterModel->getAvailableSortings().value(data);
publicFilterModel->setSorting(toSet);
thirdPartyFilterModel->setSorting(toSet);
privateFilterModel->setSorting(toSet);
}
-void FTBPage::onTabChanged(int tab)
+void Page::onTabChanged(int tab)
{
if(tab == 1)
{
@@ -311,7 +313,7 @@ void FTBPage::onTabChanged(int tab)
QModelIndex idx = currentList->currentIndex();
if(idx.isValid())
{
- auto pack = currentModel->data(idx, Qt::UserRole).value<FtbModpack>();
+ auto pack = currentModel->data(idx, Qt::UserRole).value<Modpack>();
onPackSelectionChanged(&pack);
}
else
@@ -320,7 +322,7 @@ void FTBPage::onTabChanged(int tab)
}
}
-void FTBPage::onAddPackClicked()
+void Page::onAddPackClicked()
{
bool ok;
QString text = QInputDialog::getText(
@@ -338,7 +340,7 @@ void FTBPage::onAddPackClicked()
}
}
-void FTBPage::onRemovePackClicked()
+void Page::onRemovePackClicked()
{
auto index = ui->privatePackList->currentIndex();
if(!index.isValid())
@@ -346,7 +348,7 @@ void FTBPage::onRemovePackClicked()
return;
}
auto row = index.row();
- FtbModpack pack = privateListModel->at(row);
+ Modpack pack = privateListModel->at(row);
auto answer = QMessageBox::question(
this,
tr("Remove pack"),
@@ -362,3 +364,5 @@ void FTBPage::onRemovePackClicked()
privateListModel->remove(row);
onPackSelectionChanged();
}
+
+}
diff --git a/application/pages/modplatform/FTBPage.h b/application/pages/modplatform/legacy_ftb/Page.h
index 215252ee..ed6d1657 100644
--- a/application/pages/modplatform/FTBPage.h
+++ b/application/pages/modplatform/legacy_ftb/Page.h
@@ -22,29 +22,32 @@
#include "pages/BasePage.h"
#include <MultiMC.h>
#include "tasks/Task.h"
-#include "modplatform/ftb/PackHelpers.h"
-#include "modplatform/ftb/FtbPackFetchTask.h"
+#include "modplatform/legacy_ftb/PackHelpers.h"
+#include "modplatform/legacy_ftb/PackFetchTask.h"
#include "QObjectPtr.h"
+class NewInstanceDialog;
+
+namespace LegacyFTB {
+
namespace Ui
{
-class FTBPage;
+class Page;
}
-class FtbListModel;
-class FtbFilterModel;
-class NewInstanceDialog;
-class FtbPrivatePackListModel;
-class FtbPrivatePackFilterModel;
-class FtbPrivatePackManager;
+class ListModel;
+class FilterModel;
+class PrivatePackListModel;
+class PrivatePackFilterModel;
+class PrivatePackManager;
-class FTBPage : public QWidget, public BasePage
+class Page : public QWidget, public BasePage
{
Q_OBJECT
public:
- explicit FTBPage(NewInstanceDialog * dialog, QWidget *parent = 0);
- virtual ~FTBPage();
+ explicit Page(NewInstanceDialog * dialog, QWidget *parent = 0);
+ virtual ~Page();
QString displayName() const override
{
return tr("FTB Legacy");
@@ -55,7 +58,7 @@ public:
}
QString id() const override
{
- return "ftb";
+ return "legacy_ftb";
}
QString helpPage() const override
{
@@ -66,13 +69,13 @@ public:
private:
void suggestCurrent();
- void onPackSelectionChanged(FtbModpack *pack = nullptr);
+ void onPackSelectionChanged(Modpack *pack = nullptr);
private slots:
- void ftbPackDataDownloadSuccessfully(FtbModpackList publicPacks, FtbModpackList thirdPartyPacks);
+ void ftbPackDataDownloadSuccessfully(ModpackList publicPacks, ModpackList thirdPartyPacks);
void ftbPackDataDownloadFailed(QString reason);
- void ftbPrivatePackDataDownloadSuccessfully(FtbModpack pack);
+ void ftbPrivatePackDataDownloadSuccessfully(Modpack pack);
void ftbPrivatePackDataDownloadFailed(QString reason, QString packCode);
void onSortingSelectionChanged(QString data);
@@ -88,27 +91,29 @@ private slots:
void onRemovePackClicked();
private:
- FtbFilterModel* currentModel = nullptr;
+ FilterModel* currentModel = nullptr;
QTreeView* currentList = nullptr;
QTextBrowser* currentModpackInfo = nullptr;
bool initialized = false;
- FtbModpack selected;
+ Modpack selected;
QString selectedVersion;
- FtbListModel* publicListModel = nullptr;
- FtbFilterModel* publicFilterModel = nullptr;
+ ListModel* publicListModel = nullptr;
+ FilterModel* publicFilterModel = nullptr;
- FtbListModel *thirdPartyModel = nullptr;
- FtbFilterModel *thirdPartyFilterModel = nullptr;
+ ListModel *thirdPartyModel = nullptr;
+ FilterModel *thirdPartyFilterModel = nullptr;
- FtbListModel *privateListModel = nullptr;
- FtbFilterModel *privateFilterModel = nullptr;
+ ListModel *privateListModel = nullptr;
+ FilterModel *privateFilterModel = nullptr;
- unique_qobject_ptr<FtbPackFetchTask> ftbFetchTask;
- std::unique_ptr<FtbPrivatePackManager> ftbPrivatePacks;
+ unique_qobject_ptr<PackFetchTask> ftbFetchTask;
+ std::unique_ptr<PrivatePackManager> ftbPrivatePacks;
NewInstanceDialog* dialog = nullptr;
- Ui::FTBPage *ui = nullptr;
+ Ui::Page *ui = nullptr;
};
+
+}
diff --git a/application/pages/modplatform/FTBPage.ui b/application/pages/modplatform/legacy_ftb/Page.ui
index e5ed78cb..36fb2359 100644
--- a/application/pages/modplatform/FTBPage.ui
+++ b/application/pages/modplatform/legacy_ftb/Page.ui
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
- <class>FTBPage</class>
- <widget class="QWidget" name="FTBPage">
+ <class>LegacyFTB::Page</class>
+ <widget class="QWidget" name="LegacyFTB::Page">
<property name="geometry">
<rect>
<x>0</x>
diff --git a/application/pages/modplatform/twitch/TwitchData.h b/application/pages/modplatform/twitch/TwitchData.h
new file mode 100644
index 00000000..dd000b84
--- /dev/null
+++ b/application/pages/modplatform/twitch/TwitchData.h
@@ -0,0 +1,38 @@
+#pragma once
+
+#include <QString>
+#include <QList>
+
+namespace Twitch {
+
+struct ModpackAuthor {
+ QString name;
+ QString url;
+};
+
+struct ModpackFile {
+ int addonId;
+ int fileId;
+ QString version;
+ QString mcVersion;
+ QString downloadUrl;
+};
+
+struct Modpack
+{
+ bool broken = true;
+ int addonId = 0;
+
+ QString name;
+ QString description;
+ QList<ModpackAuthor> authors;
+ QString mcVersion;
+ QString logoName;
+ QString logoUrl;
+ QString websiteUrl;
+
+ ModpackFile latestFile;
+};
+}
+
+Q_DECLARE_METATYPE(Twitch::Modpack)
diff --git a/application/pages/modplatform/twitch/TwitchModel.cpp b/application/pages/modplatform/twitch/TwitchModel.cpp
new file mode 100644
index 00000000..d9358941
--- /dev/null
+++ b/application/pages/modplatform/twitch/TwitchModel.cpp
@@ -0,0 +1,314 @@
+#include "TwitchModel.h"
+#include "MultiMC.h"
+
+#include <MMCStrings.h>
+#include <Version.h>
+
+#include <QtMath>
+#include <QLabel>
+
+#include <RWStorage.h>
+#include <Env.h>
+
+#include "net/URLConstants.h"
+
+namespace Twitch {
+
+ListModel::ListModel(QObject *parent) : QAbstractListModel(parent)
+{
+}
+
+ListModel::~ListModel()
+{
+}
+
+int ListModel::rowCount(const QModelIndex &parent) const
+{
+ return modpacks.size();
+}
+
+int ListModel::columnCount(const QModelIndex &parent) const
+{
+ return 1;
+}
+
+QVariant ListModel::data(const QModelIndex &index, int role) const
+{
+ int pos = index.row();
+ if(pos >= modpacks.size() || pos < 0 || !index.isValid())
+ {
+ return QString("INVALID INDEX %1").arg(pos);
+ }
+
+ Modpack pack = modpacks.at(pos);
+ if(role == Qt::DisplayRole)
+ {
+ return pack.name;
+ }
+ else if (role == Qt::ToolTipRole)
+ {
+ if(pack.description.length() > 100)
+ {
+ //some magic to prevent to long tooltips and replace html linebreaks
+ QString edit = pack.description.left(97);
+ edit = edit.left(edit.lastIndexOf("<br>")).left(edit.lastIndexOf(" ")).append("...");
+ return edit;
+
+ }
+ return pack.description;
+ }
+ else if(role == Qt::DecorationRole)
+ {
+ if(m_logoMap.contains(pack.logoName))
+ {
+ return (m_logoMap.value(pack.logoName));
+ }
+ QIcon icon = MMC->getThemedIcon("screenshot-placeholder");
+ ((ListModel *)this)->requestLogo(pack.logoName, pack.logoUrl);
+ return icon;
+ }
+ else if(role == Qt::UserRole)
+ {
+ QVariant v;
+ v.setValue(pack);
+ return v;
+ }
+
+ return QVariant();
+}
+
+void ListModel::logoLoaded(QString logo, QIcon out)
+{
+ m_loadingLogos.removeAll(logo);
+ m_logoMap.insert(logo, out);
+ for(int i = 0; i < modpacks.size(); i++) {
+ if(modpacks[i].logoName == logo) {
+ emit dataChanged(createIndex(i, 0), createIndex(i, 0), {Qt::DecorationRole});
+ }
+ }
+}
+
+void ListModel::logoFailed(QString logo)
+{
+ m_failedLogos.append(logo);
+ m_loadingLogos.removeAll(logo);
+}
+
+void ListModel::requestLogo(QString logo, QString url)
+{
+ if(m_loadingLogos.contains(logo) || m_failedLogos.contains(logo))
+ {
+ return;
+ }
+
+ MetaEntryPtr entry = ENV.metacache()->resolveEntry("TwitchPacks", QString("logos/%1").arg(logo.section(".", 0, 0)));
+ NetJob *job = new NetJob(QString("Twitch Icon Download %1").arg(logo));
+ job->addNetAction(Net::Download::makeCached(QUrl(url), entry));
+
+ auto fullPath = entry->getFullPath();
+ QObject::connect(job, &NetJob::finished, this, [this, logo, fullPath]
+ {
+ emit logoLoaded(logo, QIcon(fullPath));
+ if(waitingCallbacks.contains(logo))
+ {
+ waitingCallbacks.value(logo)(fullPath);
+ }
+ });
+
+ QObject::connect(job, &NetJob::failed, this, [this, logo]
+ {
+ emit logoFailed(logo);
+ });
+
+ job->start();
+
+ m_loadingLogos.append(logo);
+}
+
+void ListModel::getLogo(const QString &logo, const QString &logoUrl, LogoCallback callback)
+{
+ if(m_logoMap.contains(logo))
+ {
+ callback(ENV.metacache()->resolveEntry("TwitchPacks", QString("logos/%1").arg(logo.section(".", 0, 0)))->getFullPath());
+ }
+ else
+ {
+ requestLogo(logo, logoUrl);
+ }
+}
+
+Qt::ItemFlags ListModel::flags(const QModelIndex &index) const
+{
+ return QAbstractListModel::flags(index);
+}
+
+bool ListModel::canFetchMore(const QModelIndex& parent) const
+{
+ return searchState == CanPossiblyFetchMore;
+}
+
+void ListModel::fetchMore(const QModelIndex& parent)
+{
+ if (parent.isValid())
+ return;
+ if(nextSearchOffset == 0) {
+ qWarning() << "fetchMore with 0 offset is wrong...";
+ return;
+ }
+ performPaginatedSearch();
+}
+
+void ListModel::performPaginatedSearch()
+{
+ NetJob *netJob = new NetJob("Twitch::Search");
+ auto searchUrl = QString(
+ "https://addons-ecs.forgesvc.net/api/v2/addon/search?"
+ "categoryId=0&"
+ "gameId=432&"
+ //"gameVersion=1.12.2&"
+ "index=%1&"
+ "pageSize=25&"
+ "searchFilter=%2&"
+ "sectionId=4471&"
+ "sort=0"
+ ).arg(nextSearchOffset).arg(currentSearchTerm);
+ netJob->addNetAction(Net::Download::makeByteArray(QUrl(searchUrl), &response));
+ jobPtr = netJob;
+ jobPtr->start();
+ QObject::connect(netJob, &NetJob::succeeded, this, &ListModel::searchRequestFinished);
+ QObject::connect(netJob, &NetJob::failed, this, &ListModel::searchRequestFailed);
+}
+
+void ListModel::searchWithTerm(const QString& term)
+{
+ if(currentSearchTerm == term) {
+ return;
+ }
+ currentSearchTerm = term;
+ if(jobPtr) {
+ jobPtr->abort();
+ searchState = ResetRequested;
+ return;
+ }
+ else {
+ beginResetModel();
+ modpacks.clear();
+ endResetModel();
+ searchState = None;
+ }
+ nextSearchOffset = 0;
+ performPaginatedSearch();
+}
+
+void Twitch::ListModel::searchRequestFinished()
+{
+ jobPtr.reset();
+
+ QJsonParseError parse_error;
+ QJsonDocument doc = QJsonDocument::fromJson(response, &parse_error);
+ if(parse_error.error != QJsonParseError::NoError) {
+ qWarning() << "Error while parsing JSON response from Twitch at " << parse_error.offset << " reason: " << parse_error.errorString();
+ qWarning() << response;
+ return;
+ }
+
+ QList<Modpack> newList;
+ auto objs = doc.array();
+ for(auto projectIter: objs) {
+ Modpack pack;
+ auto project = projectIter.toObject();
+ pack.addonId = project.value("id").toInt(0);
+ if (pack.addonId == 0) {
+ qWarning() << "Pack without an ID, skipping: " << pack.name;
+ continue;
+ }
+ pack.name = project.value("name").toString();
+ pack.websiteUrl = project.value("websiteUrl").toString();
+ pack.description = project.value("summary").toString();
+ bool thumbnailFound = false;
+ auto attachments = project.value("attachments").toArray();
+ for(auto attachmentIter: attachments) {
+ auto attachment = attachmentIter.toObject();
+ bool isDefault = attachment.value("isDefault").toBool(false);
+ if(isDefault) {
+ thumbnailFound = true;
+ pack.logoName = attachment.value("title").toString();
+ pack.logoUrl = attachment.value("thumbnailUrl").toString();
+ break;
+ }
+ }
+ if(!thumbnailFound) {
+ qWarning() << "Pack without an icon, skipping: " << pack.name;
+ continue;
+ }
+ auto authors = project.value("authors").toArray();
+ for(auto authorIter: authors) {
+ auto author = authorIter.toObject();
+ ModpackAuthor packAuthor;
+ packAuthor.name = author.value("name").toString();
+ packAuthor.url = author.value("url").toString();
+ pack.authors.append(packAuthor);
+ }
+ int defaultFileId = project.value("defaultFileId").toInt(0);
+ if(defaultFileId == 0) {
+ qWarning() << "Pack without default file, skipping: " << pack.name;
+ continue;
+ }
+ bool found = false;
+ auto files = project.value("latestFiles").toArray();
+ for(auto fileIter: files) {
+ auto file = fileIter.toObject();
+ int id = file.value("id").toInt(0);
+ // NOTE: for now, ignore everything that's not the default...
+ if(id != defaultFileId) {
+ continue;
+ }
+ pack.latestFile.addonId = pack.addonId;
+ pack.latestFile.fileId = id;
+ // FIXME: what to do when there's more than one, or there's no version?
+ auto versionArray = file.value("gameVersion").toArray();
+ if(versionArray.size() != 1) {
+ continue;
+ }
+ pack.latestFile.mcVersion = versionArray[0].toString();
+ pack.latestFile.version = file.value("displayName").toString();
+ pack.latestFile.downloadUrl = file.value("downloadUrl").toString();
+ found = true;
+ break;
+ }
+ if(!found) {
+ qWarning() << "Pack with no good file, skipping: " << pack.name;
+ continue;
+ }
+ pack.broken = false;
+ newList.append(pack);
+ }
+ if(objs.size() < 25) {
+ searchState = Finished;
+ } else {
+ nextSearchOffset += 25;
+ searchState = CanPossiblyFetchMore;
+ }
+ beginInsertRows(QModelIndex(), modpacks.size(), modpacks.size() + newList.size() - 1);
+ modpacks.append(newList);
+ endInsertRows();
+}
+
+void Twitch::ListModel::searchRequestFailed(QString reason)
+{
+ jobPtr.reset();
+
+ if(searchState == ResetRequested) {
+ beginResetModel();
+ modpacks.clear();
+ endResetModel();
+
+ nextSearchOffset = 0;
+ performPaginatedSearch();
+ } else {
+ searchState = Finished;
+ }
+}
+
+}
+
diff --git a/application/pages/modplatform/twitch/TwitchModel.h b/application/pages/modplatform/twitch/TwitchModel.h
new file mode 100644
index 00000000..ad355c64
--- /dev/null
+++ b/application/pages/modplatform/twitch/TwitchModel.h
@@ -0,0 +1,76 @@
+#pragma once
+
+#include <modplatform/legacy_ftb/PackHelpers.h>
+#include <RWStorage.h>
+
+#include <QAbstractListModel>
+#include <QSortFilterProxyModel>
+#include <QThreadPool>
+#include <QIcon>
+#include <QStyledItemDelegate>
+#include <QList>
+#include <QString>
+#include <QStringList>
+#include <QMetaType>
+
+#include <functional>
+#include <net/NetJob.h>
+
+#include "TwitchData.h"
+
+namespace Twitch {
+
+
+typedef QMap<QString, QIcon> LogoMap;
+typedef std::function<void(QString)> LogoCallback;
+
+class ListModel : public QAbstractListModel
+{
+ Q_OBJECT
+
+public:
+ ListModel(QObject *parent);
+ virtual ~ListModel();
+
+ int rowCount(const QModelIndex &parent) const override;
+ int columnCount(const QModelIndex &parent) const override;
+ QVariant data(const QModelIndex &index, int role) const override;
+ Qt::ItemFlags flags(const QModelIndex &index) const override;
+ bool canFetchMore(const QModelIndex & parent) const override;
+ void fetchMore(const QModelIndex & parent) override;
+
+ void getLogo(const QString &logo, const QString &logoUrl, LogoCallback callback);
+ void searchWithTerm(const QString & term);
+
+private slots:
+ void performPaginatedSearch();
+
+ void logoFailed(QString logo);
+ void logoLoaded(QString logo, QIcon out);
+
+ void searchRequestFinished();
+ void searchRequestFailed(QString reason);
+
+private:
+ void requestLogo(QString file, QString url);
+
+private:
+ QList<Modpack> modpacks;
+ QStringList m_failedLogos;
+ QStringList m_loadingLogos;
+ LogoMap m_logoMap;
+ QMap<QString, LogoCallback> waitingCallbacks;
+
+ QString currentSearchTerm;
+ int nextSearchOffset = 0;
+ enum SearchState {
+ None,
+ CanPossiblyFetchMore,
+ ResetRequested,
+ Finished
+ } searchState = None;
+ NetJobPtr jobPtr;
+ QByteArray response;
+};
+
+}
diff --git a/application/pages/modplatform/twitch/TwitchPage.cpp b/application/pages/modplatform/twitch/TwitchPage.cpp
new file mode 100644
index 00000000..1e9f9dbb
--- /dev/null
+++ b/application/pages/modplatform/twitch/TwitchPage.cpp
@@ -0,0 +1,111 @@
+#include "TwitchPage.h"
+#include "ui_TwitchPage.h"
+
+#include "MultiMC.h"
+#include "dialogs/NewInstanceDialog.h"
+#include <InstanceImportTask.h>
+#include "TwitchModel.h"
+#include <QKeyEvent>
+
+TwitchPage::TwitchPage(NewInstanceDialog* dialog, QWidget *parent)
+ : QWidget(parent), ui(new Ui::TwitchPage), dialog(dialog)
+{
+ ui->setupUi(this);
+ connect(ui->searchButton, &QPushButton::clicked, this, &TwitchPage::triggerSearch);
+ ui->searchEdit->installEventFilter(this);
+ model = new Twitch::ListModel(this);
+ ui->packView->setModel(model);
+ connect(ui->packView->selectionModel(), &QItemSelectionModel::currentChanged, this, &TwitchPage::onSelectionChanged);
+}
+
+TwitchPage::~TwitchPage()
+{
+ delete ui;
+}
+
+bool TwitchPage::eventFilter(QObject* watched, QEvent* event)
+{
+ if (watched == ui->searchEdit && event->type() == QEvent::KeyPress) {
+ QKeyEvent* keyEvent = static_cast<QKeyEvent*>(event);
+ if (keyEvent->key() == Qt::Key_Return) {
+ triggerSearch();
+ keyEvent->accept();
+ return true;
+ }
+ }
+ return QWidget::eventFilter(watched, event);
+}
+
+bool TwitchPage::shouldDisplay() const
+{
+ return true;
+}
+
+void TwitchPage::openedImpl()
+{
+ suggestCurrent();
+}
+
+void TwitchPage::triggerSearch()
+{
+ model->searchWithTerm(ui->searchEdit->text());
+}
+
+void TwitchPage::onSelectionChanged(QModelIndex first, QModelIndex second)
+{
+ if(!first.isValid())
+ {
+ if(isOpened)
+ {
+ dialog->setSuggestedPack();
+ }
+ ui->frame->clear();
+ return;
+ }
+
+ current = model->data(first, Qt::UserRole).value<Twitch::Modpack>();
+ QString text = "";
+ QString name = current.name;
+
+ if (current.websiteUrl.isEmpty())
+ text = name;
+ else
+ text = "<a href=\"" + current.websiteUrl + "\">" + name + "</a>";
+ if (!current.authors.empty()) {
+ auto authorToStr = [](Twitch::ModpackAuthor & author) {
+ if(author.url.isEmpty()) {
+ return author.name;
+ }
+ return QString("<a href=\"%1\">%2</a>").arg(author.url, author.name);
+ };
+ QStringList authorStrs;
+ for(auto & author: current.authors) {
+ authorStrs.push_back(authorToStr(author));
+ }
+ text += tr(" by ") + authorStrs.join(", ");
+ }
+
+ ui->frame->setModText(text);
+ ui->frame->setModDescription(current.description);
+ suggestCurrent();
+}
+
+void TwitchPage::suggestCurrent()
+{
+ if(!isOpened)
+ {
+ return;
+ }
+ if(current.broken)
+ {
+ dialog->setSuggestedPack();
+ }
+
+ dialog->setSuggestedPack(current.name, new InstanceImportTask(current.latestFile.downloadUrl));
+ QString editedLogoName;
+ editedLogoName = "twitch_" + current.logoName.section(".", 0, 0);
+ model->getLogo(current.logoName, current.logoUrl, [this, editedLogoName](QString logo)
+ {
+ dialog->setSuggestedIconFromFile(logo, editedLogoName);
+ });
+}
diff --git a/application/pages/modplatform/TwitchPage.h b/application/pages/modplatform/twitch/TwitchPage.h
index 600913cd..04e3a1c6 100644
--- a/application/pages/modplatform/TwitchPage.h
+++ b/application/pages/modplatform/twitch/TwitchPage.h
@@ -20,7 +20,7 @@
#include "pages/BasePage.h"
#include <MultiMC.h>
#include "tasks/Task.h"
-#include "modplatform/flame/UrlResolvingTask.h"
+#include "TwitchData.h"
namespace Ui
{
@@ -29,6 +29,10 @@ class TwitchPage;
class NewInstanceDialog;
+namespace Twitch {
+ class ListModel;
+}
+
class TwitchPage : public QWidget, public BasePage
{
Q_OBJECT
@@ -38,7 +42,7 @@ public:
virtual ~TwitchPage();
virtual QString displayName() const override
{
- return tr("Twitch URL");
+ return tr("Twitch");
}
virtual QIcon icon() const override
{
@@ -56,14 +60,18 @@ public:
void openedImpl() override;
- void setUrl(const QString & url);
+ bool eventFilter(QObject * watched, QEvent * event) override;
+
+private:
+ void suggestCurrent();
private slots:
- void triggerCheck(bool checked);
- void checkDone();
+ void triggerSearch();
+ void onSelectionChanged(QModelIndex first, QModelIndex second);
private:
Ui::TwitchPage *ui = nullptr;
NewInstanceDialog* dialog = nullptr;
- shared_qobject_ptr<Flame::UrlResolvingTask> m_modIdResolver;
+ Twitch::ListModel* model = nullptr;
+ Twitch::Modpack current;
};
diff --git a/application/pages/modplatform/twitch/TwitchPage.ui b/application/pages/modplatform/twitch/TwitchPage.ui
new file mode 100644
index 00000000..29bdc727
--- /dev/null
+++ b/application/pages/modplatform/twitch/TwitchPage.ui
@@ -0,0 +1,88 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>TwitchPage</class>
+ <widget class="QWidget" name="TwitchPage">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>875</width>
+ <height>745</height>
+ </rect>
+ </property>
+ <layout class="QGridLayout" name="gridLayout">
+ <item row="0" column="0">
+ <widget class="QLineEdit" name="searchEdit"/>
+ </item>
+ <item row="0" column="1">
+ <widget class="QPushButton" name="searchButton">
+ <property name="text">
+ <string>Search</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0" colspan="2">
+ <widget class="QTreeView" name="packView">
+ <property name="horizontalScrollBarPolicy">
+ <enum>Qt::ScrollBarAlwaysOff</enum>
+ </property>
+ <property name="alternatingRowColors">
+ <bool>true</bool>
+ </property>
+ <property name="iconSize">
+ <size>
+ <width>48</width>
+ <height>48</height>
+ </size>
+ </property>
+ <property name="rootIsDecorated">
+ <bool>false</bool>
+ </property>
+ <property name="uniformRowHeights">
+ <bool>true</bool>
+ </property>
+ <property name="itemsExpandable">
+ <bool>false</bool>
+ </property>
+ <property name="allColumnsShowFocus">
+ <bool>true</bool>
+ </property>
+ <attribute name="headerVisible">
+ <bool>false</bool>
+ </attribute>
+ </widget>
+ </item>
+ <item row="2" column="0" colspan="2">
+ <widget class="MCModInfoFrame" name="frame">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Minimum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="frameShape">
+ <enum>QFrame::StyledPanel</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>QFrame::Raised</enum>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <customwidgets>
+ <customwidget>
+ <class>MCModInfoFrame</class>
+ <extends>QFrame</extends>
+ <header>widgets/MCModInfoFrame.h</header>
+ <container>1</container>
+ </customwidget>
+ </customwidgets>
+ <tabstops>
+ <tabstop>searchEdit</tabstop>
+ <tabstop>searchButton</tabstop>
+ <tabstop>packView</tabstop>
+ </tabstops>
+ <resources/>
+ <connections/>
+</ui>