diff options
Diffstat (limited to 'application/pages/modplatform/twitch')
-rw-r--r-- | application/pages/modplatform/twitch/TwitchData.h | 38 | ||||
-rw-r--r-- | application/pages/modplatform/twitch/TwitchModel.cpp | 312 | ||||
-rw-r--r-- | application/pages/modplatform/twitch/TwitchModel.h | 76 | ||||
-rw-r--r-- | application/pages/modplatform/twitch/TwitchPage.cpp | 111 | ||||
-rw-r--r-- | application/pages/modplatform/twitch/TwitchPage.h | 77 | ||||
-rw-r--r-- | application/pages/modplatform/twitch/TwitchPage.ui | 73 |
6 files changed, 0 insertions, 687 deletions
diff --git a/application/pages/modplatform/twitch/TwitchData.h b/application/pages/modplatform/twitch/TwitchData.h deleted file mode 100644 index dd000b84..00000000 --- a/application/pages/modplatform/twitch/TwitchData.h +++ /dev/null @@ -1,38 +0,0 @@ -#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 deleted file mode 100644 index 5c6c7858..00000000 --- a/application/pages/modplatform/twitch/TwitchModel.cpp +++ /dev/null @@ -1,312 +0,0 @@ -#include "TwitchModel.h" -#include "MultiMC.h" - -#include <MMCStrings.h> -#include <Version.h> - -#include <QtMath> -#include <QLabel> - -#include <RWStorage.h> -#include <Env.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::succeeded, 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 deleted file mode 100644 index ad355c64..00000000 --- a/application/pages/modplatform/twitch/TwitchModel.h +++ /dev/null @@ -1,76 +0,0 @@ -#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 deleted file mode 100644 index 1e9f9dbb..00000000 --- a/application/pages/modplatform/twitch/TwitchPage.cpp +++ /dev/null @@ -1,111 +0,0 @@ -#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/twitch/TwitchPage.h b/application/pages/modplatform/twitch/TwitchPage.h deleted file mode 100644 index 093900ff..00000000 --- a/application/pages/modplatform/twitch/TwitchPage.h +++ /dev/null @@ -1,77 +0,0 @@ -/* Copyright 2013-2021 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" -#include "TwitchData.h" - -namespace Ui -{ -class TwitchPage; -} - -class NewInstanceDialog; - -namespace Twitch { - class ListModel; -} - -class TwitchPage : public QWidget, public BasePage -{ - Q_OBJECT - -public: - explicit TwitchPage(NewInstanceDialog* dialog, QWidget *parent = 0); - virtual ~TwitchPage(); - virtual QString displayName() const override - { - return tr("Twitch"); - } - virtual QIcon icon() const override - { - return MMC->getThemedIcon("twitch"); - } - virtual QString id() const override - { - return "twitch"; - } - virtual QString helpPage() const override - { - return "Twitch-platform"; - } - virtual bool shouldDisplay() const override; - - void openedImpl() override; - - bool eventFilter(QObject * watched, QEvent * event) override; - -private: - void suggestCurrent(); - -private slots: - void triggerSearch(); - void onSelectionChanged(QModelIndex first, QModelIndex second); - -private: - Ui::TwitchPage *ui = nullptr; - NewInstanceDialog* dialog = nullptr; - Twitch::ListModel* model = nullptr; - Twitch::Modpack current; -}; diff --git a/application/pages/modplatform/twitch/TwitchPage.ui b/application/pages/modplatform/twitch/TwitchPage.ui deleted file mode 100644 index c78d8ce0..00000000 --- a/application/pages/modplatform/twitch/TwitchPage.ui +++ /dev/null @@ -1,73 +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>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="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> - <item row="1" column="0" colspan="2"> - <widget class="QListView" 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> - </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> |