添加API调用接口,添加读取分类,添加SpkUtils实用函数

使用了SpkSidebarTree子类实现对TreeWidget的特殊要求:不能取消选择,不能拖拽选择。

API调用接口暂时写死。API会获取

配置文件已添加但暂不使用。
This commit is contained in:
RigoLigoRLC 2021-07-17 19:22:31 +08:00
parent 48c9046993
commit e3c43198b9
17 changed files with 317 additions and 110 deletions

View File

@ -46,36 +46,29 @@ add_dependencies(gitver check_git)
set(SOURCE_FILES set(SOURCE_FILES
src/main.cpp src/main.cpp
resource/resource.qrc resource/resource.qrc
inc/gitver.h inc/gitver.h
gui/spkwindow.cpp
inc/spkwindow.h
gui/spktitlebar.cpp
inc/spktitlebar.h
inc/spkui_general.h
gui/spkui_general.cpp
inc/deepinplatform.h inc/deepinplatform.h
inc/dtk/spkdtkplugin.h inc/dtk/spkdtkplugin.h
src/spklogging.cpp inc/spkutils.h src/spkutils.cpp
inc/spklogging.h
inc/spkuimsg.h
src/spkuimsg.cpp
inc/spkmainwindow.h
inc/spkmsgbox.h
gui/spkmsgbox.cpp
inc/spkdialog.h
gui/spkdialog.cpp
inc/spkabout.h
gui/spkabout.cpp
inc/spkstore.h
src/spkstore.cpp
gui/spkmainwindow.cpp
inc/spkpageqsstest.h
gui/spkpageqsstest.cpp
inc/spkconfig.h
inc/telemetry/collectid.h inc/telemetry/collectid.h
gui/spkloading.cpp
inc/spkloading.h inc/spkwindow.h gui/spkwindow.cpp
inc/spktitlebar.h gui/spktitlebar.cpp
inc/spkui_general.h gui/spkui_general.cpp
inc/spkmainwindow.h gui/spkmainwindow.cpp
inc/spkmsgbox.h gui/spkmsgbox.cpp
inc/spkdialog.h gui/spkdialog.cpp
inc/spkabout.h gui/spkabout.cpp
inc/spkpageqsstest.h gui/spkpageqsstest.cpp
inc/spkloading.h gui/spkloading.cpp
inc/spksidebartree.h gui/spksidebartree.cpp
inc/spkstore.h src/spkstore.cpp
inc/spkuimsg.h src/spkuimsg.cpp
inc/spklogging.h src/spklogging.cpp
) )
include(cmake/FindLibNotify.cmake) include(cmake/FindLibNotify.cmake)

View File

@ -5,6 +5,7 @@
#include "spkmsgbox.h" #include "spkmsgbox.h"
#include "spkmainwindow.h" #include "spkmainwindow.h"
#include "spklogging.h" #include "spklogging.h"
#include "spkutils.h"
#include "spkuimsg.h" #include "spkuimsg.h"
SpkMainWindow::SpkMainWindow(QWidget *parent) : SpkWindow(parent) SpkMainWindow::SpkMainWindow(QWidget *parent) : SpkWindow(parent)
@ -14,47 +15,23 @@ SpkMainWindow::SpkMainWindow(QWidget *parent) : SpkWindow(parent)
SetUseTitleBar(false); SetUseTitleBar(false);
SetCentralWidget(ui); SetCentralWidget(ui);
SetTitleBar(ui->TitleBar, false); SetTitleBar(ui->TitleBar, false);
RefreshCategoryData();
auto size = QGuiApplication::primaryScreen()->size() * 0.25; auto size = QGuiApplication::primaryScreen()->size() * 0.25;
resize(QGuiApplication::primaryScreen()->size() * 0.5); resize(QGuiApplication::primaryScreen()->size() * 0.5);
move(size.width(), size.height()); move(size.width(), size.height());
} }
void SpkMainWindow::PopulateCategories(QJsonObject aCategoryData) void SpkMainWindow::PopulateCategories(QJsonArray aCategoryData)
{ {
using SpkUi::SpkSidebarSelector;
QTreeWidgetItem *catg;
auto w = ui->CategoryWidget; auto w = ui->CategoryWidget;
if(!aCategoryData.contains("code")) if(ui->CategoryParentItem->childCount()) // Clear all existing children if there is any
{ foreach(auto &i, ui->CategoryParentItem->takeChildren())
SpkUiMessage::SendStoreNotification(tr("Failed to load categories; return code lost.")); delete i;
return;
}
auto OpRetCode = aCategoryData.value("code");
if(!OpRetCode.isDouble())
{
SpkUiMessage::SendStoreNotification(tr("Failed to load categories; invalid return code."));
return;
}
if(OpRetCode.toInt() != 0)
{
SpkUiMessage::SendStoreNotification(tr("Failed to load categories; operation failed: %1.")
.arg(OpRetCode.toDouble()));
return;
}
if(!aCategoryData.contains("data")) foreach(auto i, aCategoryData)
{
SpkUiMessage::SendStoreNotification(tr("Failed to load categories; data lost."));
return;
}
auto OpData = aCategoryData.value("data");
if(!OpRetCode.isArray())
{
SpkUiMessage::SendStoreNotification(tr("Failed to load categories; invalid data."));
return;
}
auto OpDataArr = OpData.toArray();
foreach(auto i, OpDataArr)
{ {
if(i.isObject()) if(i.isObject())
{ {
@ -67,20 +44,41 @@ void SpkMainWindow::PopulateCategories(QJsonObject aCategoryData)
if(j.contains("type_name") && j.value("type_name").isString()) if(j.contains("type_name") && j.value("type_name").isString())
typeName = j.value("type_name").toString(); typeName = j.value("type_name").toString();
else goto WRONG_CATEGORY; else goto WRONG_CATEGORY;
// TODO catg = new QTreeWidgetItem(ui->CategoryParentItem, QStringList(typeName));
catg->setData(0, SpkSidebarSelector::RoleItemIsCategory, true);
catg->setData(0, SpkSidebarSelector::RoleItemCategoryPageId, typeId);
continue; continue;
WRONG_CATEGORY:;
} }
WRONG_CATEGORY: ui->CategoryParentItem->setExpanded(true);
sLog("One category ignored because of invalid data");
} }
} }
void SpkMainWindow::RefreshCategoryData()
{
// Asynchronously call category API
using namespace SpkUtils;
VerifySingleRequest(mCategoryGetReply);
mCategoryGetReply = STORE->SendApiRequest("type/get_type_list");
DeleteReplyLater(mCategoryGetReply);
connect(mCategoryGetReply, &QNetworkReply::finished, this, &SpkMainWindow::CategoryDataReceived);
}
void SpkMainWindow::CategoryDataReceived()
{
QJsonValue retval;
if(!SpkUtils::VerifyReplyJson(mCategoryGetReply, retval) || !retval.isArray())
{
sErr(tr("Failed to load categories!"));
// TODO: Switch to an error page
}
PopulateCategories(retval.toArray());
}
SpkUi::SpkMainWidget::SpkMainWidget(QWidget *parent) : QFrame(parent) SpkUi::SpkMainWidget::SpkMainWidget(QWidget *parent) : QFrame(parent)
{ {
setObjectName("spk_mainwidget"); setObjectName("spk_mainwidget");
QTreeWidgetItem *item;
Pager = new QStackedWidget(this); Pager = new QStackedWidget(this);
Pager->setObjectName("spk_mw_pager"); Pager->setObjectName("spk_mw_pager");
Pager->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); Pager->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
@ -140,16 +138,19 @@ SpkUi::SpkMainWidget::SpkMainWidget(QWidget *parent) : QFrame(parent)
HLaySideTop->addWidget(BtnSettings); HLaySideTop->addWidget(BtnSettings);
VLaySidebar->addLayout(HLaySideTop); VLaySidebar->addLayout(HLaySideTop);
CategoryWidget = new QTreeWidget(this); using SpkUi::SpkSidebarSelector;
CategoryWidget = new SpkSidebarTree(this);
CategoryWidget->setObjectName("spk_mw_category"); CategoryWidget->setObjectName("spk_mw_category");
CategoryWidget->setAutoFillBackground(true); CategoryWidget->setAutoFillBackground(true);
CategoryWidget->setColumnCount(1); CategoryWidget->setColumnCount(1);
CategoryWidget->setHeaderHidden(true); CategoryWidget->setHeaderHidden(true);
CategoryWidget->setSelectionMode(QAbstractItemView::SelectionMode::SingleSelection); CategoryWidget->setSelectionMode(QAbstractItemView::SelectionMode::SingleSelection);
item = new QTreeWidgetItem(QStringList("Placeholder")); CategoryParentItem = new QTreeWidgetItem(QStringList(tr("Categories")));
item->setData(0, Qt::UserRole + 1, 1); CategoryParentItem->setFlags(CategoryParentItem->flags().setFlag(Qt::ItemIsSelectable, false));
item->setData(0, Qt::UserRole + 2, 1); CategoryParentItem->setExpanded(true);
CategoryWidget->addTopLevelItem(item); SidebarMgr->AddUnusableItem(CategoryParentItem);
CategoryWidget->addTopLevelItem(CategoryParentItem);
// FIXMEIFPOSSIBLE: Fusion adds extra gradient. // FIXMEIFPOSSIBLE: Fusion adds extra gradient.
// Details: https://forum.qt.io/topic/128190/fusion-style-kept-adding-an-extra- // Details: https://forum.qt.io/topic/128190/fusion-style-kept-adding-an-extra-
// layer-of-gradient-to-my-selected-item-of-qtreewidget-even-with-qss // layer-of-gradient-to-my-selected-item-of-qtreewidget-even-with-qss

View File

@ -19,7 +19,7 @@ SpkMsgBox::SpkMsgBox(QWidget *parent)
} }
int SpkMsgBox::StaticExec(QString msg, QString title, QMessageBox::Icon icon, int SpkMsgBox::StaticExec(QString msg, QString title, QMessageBox::Icon icon,
QMessageBox::StandardButtons buttons, QString extra) QMessageBox::StandardButtons buttons, QString extra, bool expanded)
{ {
SpkMsgBox *b = new SpkMsgBox(SpkStore::Instance->GetRootWindow()); SpkMsgBox *b = new SpkMsgBox(SpkStore::Instance->GetRootWindow());
QWidget *wMsgWidget = new QWidget; QWidget *wMsgWidget = new QWidget;
@ -98,8 +98,12 @@ int SpkMsgBox::StaticExec(QString msg, QString title, QMessageBox::Icon icon,
b->AddSpacing(3); b->AddSpacing(3);
AddButtons(b, buttons); AddButtons(b, buttons);
if(hasextra) // Keep conventional buttons centered if(hasextra)
b->mBtnLay->addStretch(); {
b->mBtnLay->addStretch(); // Keep conventional buttons centered
if(expanded)
emit wExpandBtn->clicked();
}
InitialHeight = b->minimumSizeHint().height(); InitialHeight = b->minimumSizeHint().height();
auto pos = (SpkUi::PrimaryScreenSize - b->sizeHint()) / 2; auto pos = (SpkUi::PrimaryScreenSize - b->sizeHint()) / 2;
b->move(pos.width(), pos.height()); b->move(pos.width(), pos.height());

31
gui/spksidebartree.cpp Normal file
View File

@ -0,0 +1,31 @@
#include <QMouseEvent>
#include "spksidebartree.h"
SpkUi::SpkSidebarTree::SpkSidebarTree(QWidget *parent) :
QTreeWidget(parent)
{
}
void SpkUi::SpkSidebarTree::mouseMoveEvent(QMouseEvent *e)
{
// This is solely for forcibly disabling the view to change selection when dragging on the view
// and probably the only reason why this class began its existence
if((e->buttons() & Qt::LeftButton))
setState(NoState);
else
QTreeWidget::mouseMoveEvent(e);
}
void SpkUi::SpkSidebarTree::mousePressEvent(QMouseEvent *e)
{
// Prevent anything being deselected
if(e->modifiers().testFlag(Qt::ControlModifier) && e->buttons().testFlag(Qt::LeftButton))
{
auto i = itemAt(e->pos());
if(i && i->isSelected())
return;
}
QTreeWidget::mousePressEvent(e);
}

View File

@ -115,7 +115,7 @@ namespace SpkUi
// FIXME: Chameleon style kept adding unwanted blue focus indication border // FIXME: Chameleon style kept adding unwanted blue focus indication border
// to widgets that shouldn't have borders. // to widgets that shouldn't have borders.
// We need to eliminate this irritating problem. // We need to eliminate this irritating problem.
if(qgetenv("SPARK_NO_QSTYLE_CHANGE") == "1") if(qgetenv("SPARK_NO_QSTYLE_CHANGE").toInt())
return; return;
OldSystemStyle = QStyleFactory::create("chameleon"); // TreeWidget doesn't work well with Fusion OldSystemStyle = QStyleFactory::create("chameleon"); // TreeWidget doesn't work well with Fusion
auto styles = QStyleFactory::keys(); auto styles = QStyleFactory::keys();

View File

@ -1,6 +0,0 @@
#ifndef SPKCONFIG_H
#define SPKCONFIG_H
#endif // SPKCONFIG_H

View File

@ -41,4 +41,4 @@ class SpkLogger
#define sErr(X) SpkLogger::GetInstance()->Error(X) #define sErr(X) SpkLogger::GetInstance()->Error(X)
#define sErrPop(X) SpkLogger::GetInstance()->Error(X,true) #define sErrPop(X) SpkLogger::GetInstance()->Error(X,true)
#define sCritical(X) SpkLogger::GetInstance()->Critical(X) #define sCritical(X) SpkLogger::GetInstance()->Critical(X)
#define sNotify(X) #define sNotify(X) SpkLogger::GetInstance()->Notify(X)

View File

@ -9,9 +9,12 @@
#include <QStackedWidget> #include <QStackedWidget>
#include <QButtonGroup> #include <QButtonGroup>
#include <QJsonObject> #include <QJsonObject>
#include <QTreeWidget> #include "spksidebartree.h" // In place of #include <QTreeWidget>
#include <QPointer>
#include "spkpageqsstest.h" #include "spkpageqsstest.h"
class QNetworkReply;
namespace SpkUi namespace SpkUi
{ {
class SpkSidebarSelector : public QObject class SpkSidebarSelector : public QObject
@ -21,6 +24,7 @@ namespace SpkUi
QPushButton *mLastCheckedBtn; QPushButton *mLastCheckedBtn;
QTreeWidgetItem *mLastSelectedItem; QTreeWidgetItem *mLastSelectedItem;
QTreeWidget *mCategoryWidget; QTreeWidget *mCategoryWidget;
QVector<QTreeWidgetItem *> mUnusableItems; // Unselectable top level items; never changes
public: public:
SpkSidebarSelector(QObject *parent = nullptr) : QObject(parent) SpkSidebarSelector(QObject *parent = nullptr) : QObject(parent)
@ -40,9 +44,10 @@ namespace SpkUi
void BindCategoryWidget(QTreeWidget* w) void BindCategoryWidget(QTreeWidget* w)
{ {
mCategoryWidget = w; mCategoryWidget = w;
connect(w, &QTreeWidget::itemPressed, this, connect(w, &QTreeWidget::itemClicked, this,
&SpkSidebarSelector::TreeItemSelected); &SpkSidebarSelector::TreeItemSelected);
} }
void AddUnusableItem(QTreeWidgetItem *i) { mUnusableItems.append(i); }
private slots: private slots:
// We assume the objects in interest all have the correct properties // We assume the objects in interest all have the correct properties
@ -73,6 +78,10 @@ namespace SpkUi
} }
void TreeItemSelected(QTreeWidgetItem *item, int column) void TreeItemSelected(QTreeWidgetItem *item, int column)
{ {
if(mUnusableItems.contains(item))
{
UnusableItemSelected(item); return;
}
if(mLastCheckedBtn) if(mLastCheckedBtn)
{ {
mLastCheckedBtn->setChecked(false); mLastCheckedBtn->setChecked(false);
@ -84,6 +93,18 @@ namespace SpkUi
else else
emit SwitchToPage(item->data(column, RoleItemCategoryPageId).toInt()); emit SwitchToPage(item->data(column, RoleItemCategoryPageId).toInt());
} }
void UnusableItemSelected(QTreeWidgetItem *i)
{
i->setSelected(false);
if(mLastSelectedItem)
{
mLastSelectedItem->setSelected(true);
}
else if(mLastCheckedBtn)
{
mLastCheckedBtn->setChecked(true);
}
}
signals: signals:
void SwitchToCategory(int aCategoryId); void SwitchToCategory(int aCategoryId);
@ -109,10 +130,12 @@ namespace SpkUi
QHBoxLayout *HLaySideTop; QHBoxLayout *HLaySideTop;
QLabel *StoreIcon; QLabel *StoreIcon;
QPushButton *BtnSettings, *BtnFeedback, *BtnLogs; QPushButton *BtnSettings, *BtnFeedback, *BtnLogs;
QTreeWidget *CategoryWidget; SpkSidebarTree *CategoryWidget;
QMap<int, QTreeWidgetItem> *CategoryItemMap; QMap<int, QTreeWidgetItem> *CategoryItemMap;
SpkSidebarSelector *SidebarMgr; SpkSidebarSelector *SidebarMgr;
QTreeWidgetItem *CategoryParentItem;
//Pages //Pages
SpkPageQssTest *PageQssTest; SpkPageQssTest *PageQssTest;
}; };
@ -127,5 +150,14 @@ class SpkMainWindow : public SpkWindow
public: public:
SpkMainWindow(QWidget *parent = nullptr); SpkMainWindow(QWidget *parent = nullptr);
void PopulateCategories(QJsonObject); void PopulateCategories(QJsonArray);
private:
QPointer<QNetworkReply> mCategoryGetReply;
public slots:
void RefreshCategoryData();
private slots:
void CategoryDataReceived();
}; };

View File

@ -10,7 +10,8 @@ class SpkMsgBox : public SpkDialog
public: public:
SpkMsgBox(QWidget *parent = nullptr); SpkMsgBox(QWidget *parent = nullptr);
static int StaticExec(QString msg, QString title, QMessageBox::Icon = QMessageBox::NoIcon, static int StaticExec(QString msg, QString title, QMessageBox::Icon = QMessageBox::NoIcon,
QMessageBox::StandardButtons = QMessageBox::Ok, QString extra = ""); QMessageBox::StandardButtons = QMessageBox::Ok, QString extra = "",
bool expanded = false);
private: private:
static void AddButtons(SpkMsgBox *me, QMessageBox::StandardButtons b); static void AddButtons(SpkMsgBox *me, QMessageBox::StandardButtons b);
QList<QMessageBox::StandardButton> mButtonList; QList<QMessageBox::StandardButton> mButtonList;

17
inc/spksidebartree.h Normal file
View File

@ -0,0 +1,17 @@
#pragma once
#include <QTreeWidget>
namespace SpkUi
{
class SpkSidebarTree : public QTreeWidget
{
Q_OBJECT
public:
SpkSidebarTree(QWidget* parent = nullptr);
protected:
void mouseMoveEvent(QMouseEvent *) override;
void mousePressEvent(QMouseEvent *) override;
};
}

View File

@ -1,8 +1,9 @@
#pragma once #pragma once
#include <QMap> #include <QJsonDocument>
#include <QString> #include <QString>
#include <QSettings>
#include <QtNetwork/QNetworkReply> #include <QtNetwork/QNetworkReply>
#include <QtNetwork/QNetworkAccessManager> #include <QtNetwork/QNetworkAccessManager>
@ -20,35 +21,19 @@ class SpkStore : public QObject
Q_OBJECT Q_OBJECT
public: public:
static SpkStore *Instance; static SpkStore *Instance;
QSettings *mCfg;
SpkStore(bool aCli, QString &aLogPath); SpkStore(bool aCli, QString &aLogPath);
~SpkStore(); ~SpkStore();
SpkMainWindow* GetRootWindow() { return mMainWindow; } SpkMainWindow* GetRootWindow() { return mMainWindow; }
void SetApiResuestUrl(QString aUrlStr) { mApiRequestUrl = aUrlStr; }
QString GetApiRequestUrl() { return mApiRequestUrl; }
QNetworkReply *SendApiRequest(QString path, QJsonDocument param = QJsonDocument());
private: private:
SpkLogger *mLogger; SpkLogger *mLogger;
SpkMainWindow *mMainWindow = nullptr; SpkMainWindow *mMainWindow = nullptr;
QNetworkAccessManager *mNetMgr = nullptr; QNetworkAccessManager *mNetMgr = nullptr;
QString mDistroName, mApiRequestUrl, mUserAgentStr;
// Following are stationary signal-slot bindings between UI and Store, mostly for handling
// API calls and resource downloading.
public slots:
// void RequestStoreMetadata(); ///< All required metadata the store needs when launched
// void RequestCategoryPage(int aCategoryId);
// void RequestApplicationMetadata(int aAppId);
// void RequestRefreshApiUrls(QString aCustomUrl);
signals:
void StatusStoreMetadata(QNetworkReply::NetworkError, QString);
void StatusCategoryPage(QNetworkReply::NetworkError, QString);
void StatusApplicationMetadata(QNetworkReply::NetworkError, QString);
void StatusRefreshApiUrls(QNetworkReply::NetworkError, QString);
private:
// Store manages all kinds of possible replies, and the caller can only get JSON they need
QNetworkReply *mReplyStoreMetadata = nullptr,
*mReplyCategory = nullptr,
*mReplyAppMetadata = nullptr,
*mReplyApiUrls = nullptr;
}; };

25
inc/spkutils.h Normal file
View File

@ -0,0 +1,25 @@
#pragma once
#include <QPointer>
#include <QString>
#include <QSettings>
#include <QFile>
#include <QtNetwork/QNetworkReply>
#include "spkstore.h"
#include "spklogging.h"
#define STORE SpkStore::Instance
#define CFG (SpkStore::Instance->mCfg)
namespace SpkUtils
{
QString GetDistroName();
void VerifySingleRequest(QPointer<QNetworkReply> aReply);
void DeleteReplyLater(QNetworkReply *aReply);
bool VerifyReplyJson(QNetworkReply *aReply, QJsonValue& aRetDoc);
}

3
resource/default_config Normal file
View File

@ -0,0 +1,3 @@
[Apis]
MasterApiUrl=https://store.deepinos.org/api

View File

@ -4,6 +4,7 @@
</qresource> </qresource>
<qresource prefix="/info"> <qresource prefix="/info">
<file>lipsum.txt</file> <file>lipsum.txt</file>
<file>default_config</file>
</qresource> </qresource>
<qresource prefix="/"> <qresource prefix="/">
<file>icons/spark-store.svg</file> <file>icons/spark-store.svg</file>

View File

@ -96,12 +96,12 @@ void SpkLogger::Error(QString message, const bool pop)
// .arg(message)); // .arg(message));
// msgbox.exec(); // I don't know whether we need to show it non-modal. // msgbox.exec(); // I don't know whether we need to show it non-modal.
SpkMsgBox::StaticExec(QObject::tr("Spark Store has encountered an error.\n" SpkMsgBox::StaticExec(QObject::tr("Spark Store has encountered an error.\n"
"Parts of the experience is expected to be broken.\n\n" "Parts of the experience is expected to be broken.\n\n"),
"Details:\n%1"),
QObject::tr("Spark Store Error"), QObject::tr("Spark Store Error"),
QMessageBox::Critical, QMessageBox::Critical,
QMessageBox::Ok, QMessageBox::Ok,
message); message,
true);
} }
} }
@ -122,3 +122,8 @@ void SpkLogger::Critical(QString message)
exit(2); exit(2);
} }
void SpkLogger::Notify(QString message)
{
Q_UNUSED(message);
}

View File

@ -1,12 +1,16 @@
#include <spkui_general.h> #include <spkui_general.h>
#include "dtk/spkdtkplugin.h"
#include <QPluginLoader> #include <QPluginLoader>
#include <QDir> #include <QDir>
#include <QApplication> #include <QApplication>
#include <QtNetwork/QNetworkProxy>
#include "dtk/spkdtkplugin.h"
#include "gitver.h"
#include "spkstore.h" #include "spkstore.h"
#include "spkutils.h"
SpkStore *SpkStore::Instance = nullptr; SpkStore *SpkStore::Instance = nullptr;
static void InstallDefaultConfigs();
SpkStore::SpkStore(bool aCli, QString &aLogPath) SpkStore::SpkStore(bool aCli, QString &aLogPath)
{ {
@ -17,8 +21,27 @@ SpkStore::SpkStore(bool aCli, QString &aLogPath)
Instance = this; Instance = this;
// Finish all essential initialization after this. // Finish all essential initialization after this.
if(QFileInfo(QDir::homePath() + "/.config/spark-store/config").exists())
mCfg = new QSettings(QDir::homePath() + "/.config/spark-store/config", QSettings::IniFormat);
else
{
mCfg = new QSettings(":/info/default_config", QSettings::IniFormat);
#ifndef NDEBUG
if(!qgetenv("SPARK_NO_INSTALL_CONFIG").toInt())
{
if(!QFile::copy(":/info/default_config", QDir::homePath() + "/.config/spark-store/config"))
sErrPop(tr("Cannot install default config file!"));
}
#endif
}
mNetMgr = new QNetworkAccessManager(this); mNetMgr = new QNetworkAccessManager(this);
mNetMgr->setProxy(QNetworkProxy(QNetworkProxy::NoProxy)); // FIXME
mDistroName = SpkUtils::GetDistroName();
mApiRequestUrl = "https://store.deepinos.org/api/"; // TODO: CHECK BEFORE 4.0 RELEASE
mUserAgentStr = QString("Spark-Store/%1 Distro/%2")
.arg(GitVer::DescribeTags())
.arg(mDistroName);
// Finish all essential initialization before this. // Finish all essential initialization before this.
if(aCli) if(aCli)
@ -36,3 +59,17 @@ SpkStore::~SpkStore()
delete mMainWindow; delete mMainWindow;
delete mLogger; delete mLogger;
} }
QNetworkReply *SpkStore::SendApiRequest(QString aPath, QJsonDocument aParam)
{
QNetworkRequest request;
request.setUrl(mApiRequestUrl + aPath);
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
request.setHeader(QNetworkRequest::UserAgentHeader, mUserAgentStr);
return mNetMgr->post(request, aParam.isEmpty() ? "{}" : aParam.toJson(QJsonDocument::Compact));
}
static void InstallDefaultConfigs()
{
//TODO:STUB
}

78
src/spkutils.cpp Normal file
View File

@ -0,0 +1,78 @@
#include <QDebug>
#include "spkutils.h"
void SpkUtils::VerifySingleRequest(QPointer<QNetworkReply> aReply)
{
if(aReply.isNull())
return;
aReply->disconnect(SIGNAL(finished()));
aReply->abort();
aReply->deleteLater();
}
QString SpkUtils::GetDistroName()
{
QSettings osRelease("/etc/os-release", QSettings::IniFormat);
return osRelease.value("PRETTY_NAME", "Unknown Distro").toString();
}
bool SpkUtils::VerifyReplyJson(QNetworkReply *aReply, QJsonValue &aRetDoc)
{
QJsonParseError err;
QByteArray rawjson = aReply->readAll();
qDebug() << "Received:" << rawjson;
QJsonDocument ret = QJsonDocument::fromJson(rawjson, &err);
QJsonObject replyObject;
if(err.error != QJsonParseError::NoError)
{
sNotify(QObject::tr("Failed to parse server reply! Error %1.").arg(err.error));
sErr(QObject::tr("VerifyReplyJson: returned JSON of request to %1 is unreadable.")
.arg(aReply->url().toString()));
return false;
}
if(!ret.isObject())
{
sNotify(QObject::tr("Server sent back an invalid response."));
sErr(QObject::tr("VerifyReplyJson: returned JSON of request to %1 is not an Object.")
.arg(aReply->url().toString()));
return false;
}
replyObject = ret.object();
if(!replyObject.contains("code"))
{
sWarn(QObject::tr("VerifyReplyJson: reply of request to %1 doesn't have a code.")
.arg(aReply->url().toString()));
}
else
{
auto OpRetCode = replyObject.value("code");
if(!OpRetCode.isDouble())
{
sWarn(QObject::tr("VerifyReplyJson: Reply of request to %1 has a non-numeric code.")
.arg(aReply->url().toString()));
}
else if(OpRetCode.toInt() != 0)
{
sNotify(QObject::tr("Server sent back an failure message; code: %1.")
.arg(OpRetCode.toInt()));
sErr(QObject::tr("VerifyReplyJson: Request to %1 failed with code %2.")
.arg(aReply->url().toString()).arg(OpRetCode.toInt()));
return false;
}
}
if(!replyObject.contains("data"))
{
sNotify(QObject::tr("Server did not reply with any data."));
sErr(QObject::tr("VerifyReplyJson: Reply of request to %1 didn't include any data.")
.arg(aReply->url().toString()));
return false;
}
aRetDoc = replyObject.value("data");
return true;
}
void SpkUtils::DeleteReplyLater(QNetworkReply *aReply)
{
QObject::connect(aReply, &QNetworkReply::finished, aReply, &QObject::deleteLater);
}