271 lines
7.8 KiB
C++
271 lines
7.8 KiB
C++
//SPDX-FileCopyrightText: 2023 Yury Gubich <blue@macaw.me>
|
|
//SPDX-License-Identifier: GPL-3.0-or-later
|
|
|
|
#include "magpie.h"
|
|
|
|
#include <QDebug>
|
|
|
|
#include "API/api.h"
|
|
#include "API/codes.h"
|
|
#include "utils/helpers.h"
|
|
|
|
Models::Magpie::Magpie (QObject* parent):
|
|
QObject(parent),
|
|
currencies(),
|
|
assets(currencies),
|
|
address(),
|
|
state(State::Offline),
|
|
accessToken(),
|
|
renewToken(),
|
|
api(),
|
|
firstPoll(),
|
|
pollRequestId(API::none),
|
|
requestingCurrencies(false)
|
|
{
|
|
firstPoll.setSingleShot(true);
|
|
firstPoll.setInterval(2000);
|
|
connect(&firstPoll, &QTimer::timeout, this, &Magpie::onPositivePoll);
|
|
connect(&assets, &Assets::requestAssets, this, &Magpie::requestAssets);
|
|
}
|
|
|
|
QUrl Models::Magpie::getAddress () const {
|
|
return address;
|
|
}
|
|
|
|
Models::Magpie::State Models::Magpie::getState () const {
|
|
return state;
|
|
}
|
|
|
|
QString Models::Magpie::getAccessToken () const {
|
|
return accessToken;
|
|
}
|
|
|
|
QString Models::Magpie::getRenewToken () const {
|
|
return renewToken;
|
|
}
|
|
|
|
void Models::Magpie::installAPI (const std::shared_ptr<API>& api) {
|
|
Magpie::api = api;
|
|
}
|
|
|
|
void Models::Magpie::setAddress (const QUrl& address) {
|
|
if (Magpie::address == address)
|
|
return;
|
|
|
|
if (state == Authenticated) {
|
|
//do something
|
|
}
|
|
|
|
Magpie::address = address;
|
|
emit addressChanged(address);
|
|
if (address.isEmpty())
|
|
setState(NoServer);
|
|
else
|
|
setState(NotAuthenticated);
|
|
}
|
|
|
|
void Models::Magpie::forceAddress (const QUrl& address) {
|
|
Magpie::address = "";
|
|
setAddress(address);
|
|
}
|
|
|
|
void Models::Magpie::setTokens (const QString access, const QString& renew, bool notify) {
|
|
accessToken = access;
|
|
renewToken = renew;
|
|
setState(Authenticating);
|
|
startPolling();
|
|
|
|
if (notify)
|
|
emit storeTokens(accessToken, renewToken);
|
|
}
|
|
|
|
void Models::Magpie::setState (State newState) {
|
|
if (newState == state)
|
|
return;
|
|
|
|
state = newState;
|
|
emit stateChanged(state);
|
|
}
|
|
|
|
Models::Assets* Models::Magpie::getAssets () {
|
|
return &assets;
|
|
}
|
|
|
|
Models::Currencies* Models::Magpie::getCurrencies () {
|
|
return ¤cies;
|
|
}
|
|
|
|
void Models::Magpie::requestAssets () {
|
|
api->requestAssets(
|
|
[this] (const QVariantList& list) {
|
|
std::deque<Asset> result;
|
|
bool res = Assets::deserialize(list, result);
|
|
if (!res) {
|
|
qDebug() << "Error deserializer received assets";
|
|
result.clear();
|
|
} else {
|
|
qDebug() << "Assets successfully received";
|
|
}
|
|
|
|
assets.add(result);
|
|
},
|
|
[this] (const QString& error, const std::optional<QVariantMap>& data) {
|
|
qDebug() << "Error receiving assets:" << error;
|
|
}
|
|
);
|
|
}
|
|
|
|
void Models::Magpie::onPositivePoll () {
|
|
if (state == Authenticating)
|
|
requestCurrencies();
|
|
}
|
|
|
|
void Models::Magpie::startPolling () {
|
|
qDebug() << "Starting polling...";
|
|
if (state != Authenticating)
|
|
throw std::runtime_error("Can not start polling in this state: " + std::to_string(state));
|
|
|
|
sendPoll();
|
|
firstPoll.start();
|
|
}
|
|
|
|
void Models::Magpie::sendPoll (bool clear) {
|
|
if (pollRequestId != API::none)
|
|
throw std::runtime_error("an attempt to send second poll request while the other one is active");
|
|
|
|
pollRequestId = api->poll(
|
|
std::bind(&Magpie::onPollSuccess, this, std::placeholders::_1),
|
|
std::bind(&Magpie::onPollError, this, std::placeholders::_1, std::placeholders::_2),
|
|
clear
|
|
);
|
|
}
|
|
|
|
void Models::Magpie::onPollSuccess (const QVariantMap& data) {
|
|
pollRequestId = API::none;
|
|
firstPoll.stop();
|
|
|
|
Codes::Poll code = Codes::convertPoll(data.value("result").toInt());
|
|
bool clear = false;
|
|
switch (code) {
|
|
case Codes::Poll::success:
|
|
clear = handleChanges(qast<QVariantMap>(data.value("data")));
|
|
//todo handle the result
|
|
case Codes::Poll::timeout:
|
|
onPositivePoll();
|
|
if (state == Authenticating || state == Authenticated)
|
|
return sendPoll(clear);
|
|
|
|
case Codes::Poll::tokenProblem:
|
|
case Codes::Poll::replace:
|
|
case Codes::Poll::unknownError: //todo this one doesn't actually mean that we can't work for now, the network may be temporarily down or something
|
|
return setState(NotAuthenticated);
|
|
}
|
|
}
|
|
|
|
void Models::Magpie::onPollError (const QString& err, const std::optional<QVariantMap>& data) {
|
|
qDebug() << "Poll error:" << err;
|
|
pollRequestId = API::none;
|
|
firstPoll.stop();
|
|
setState(NotAuthenticated);
|
|
}
|
|
|
|
bool Models::Magpie::handleChanges (const QVariantMap& changes) {
|
|
QVariantMap::ConstIterator itr = changes.constFind("system");
|
|
if (itr != changes.constEnd() && itr.value().canConvert<QVariantMap>()) {
|
|
const QVariantMap& sys = qast<QVariantMap>(itr.value());
|
|
QVariantMap::ConstIterator invItr = sys.constFind("invalidate");
|
|
if (invItr != sys.constEnd()) {
|
|
const QVariant& vinv = invItr.value();
|
|
if (vinv.canConvert<bool>() && vinv.toBool())
|
|
resetAllModels();
|
|
}
|
|
}
|
|
|
|
bool result = true;
|
|
itr = changes.constFind("assets");
|
|
if (itr != changes.constEnd() && itr.value().canConvert<QVariantMap>())
|
|
result = handleAssetChanges(qast<QVariantMap>(itr.value()));
|
|
|
|
itr = changes.constFind("currencies");
|
|
if (itr != changes.constEnd() && itr.value().canConvert<QVariantMap>())
|
|
result = result && handleCurrenciesChanges(qast<QVariantMap>(itr.value()));
|
|
|
|
return result;
|
|
}
|
|
|
|
bool Models::Magpie::handleAssetChanges (const QVariantMap& changes) {
|
|
QVariantMap::ConstIterator aItr = changes.constFind("invalidate");
|
|
if (aItr != changes.constEnd()) {
|
|
const QVariant& vinv = aItr.value();
|
|
if (vinv.canConvert<bool>() && vinv.toBool())
|
|
assets.clear();
|
|
}
|
|
|
|
aItr = changes.constFind("added");
|
|
if (aItr != changes.constEnd() && aItr.value().canConvert<QVariantList>()) {
|
|
std::deque<Models::Asset> added;
|
|
if (!Models::Assets::deserialize(qast<QVariantList>(aItr.value()), added))
|
|
qDebug() << "Error deserializng added assets";
|
|
else
|
|
assets.add(added);
|
|
}
|
|
|
|
aItr = changes.constFind("removed");
|
|
if (aItr != changes.constEnd() && aItr.value().canConvert<QVariantList>()) {
|
|
const QVariantList rem = qast<QVariantList>(aItr.value());
|
|
for (const QVariant& vId : rem) {
|
|
if (vId.isValid() && vId.canConvert<unsigned int>())
|
|
assets.remove(vId.toUInt());
|
|
else
|
|
qDebug() << "Error deserializing removed assets";
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool Models::Magpie::handleCurrenciesChanges (const QVariantMap& changes) {
|
|
QVariantMap::ConstIterator aItr = changes.constFind("invalidate");
|
|
if (aItr != changes.constEnd()) {
|
|
const QVariant& vinv = aItr.value();
|
|
if (vinv.canConvert<bool>() && vinv.toBool())
|
|
requestCurrencies();
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void Models::Magpie::resetAllModels () {
|
|
assets.clear();
|
|
requestCurrencies();
|
|
}
|
|
|
|
void Models::Magpie::requestCurrencies () {
|
|
if (requestingCurrencies)
|
|
return;
|
|
|
|
requestingCurrencies = true;
|
|
currencies.clear();
|
|
api->requestCurrencies(
|
|
[this] (const QVariantList& list) {
|
|
std::deque<Currency> result;
|
|
bool res = Currencies::deserialize(list, result);
|
|
if (!res) {
|
|
qDebug() << "Error deserializer received currencies";
|
|
result.clear();
|
|
} else {
|
|
qDebug() << "Currencies successfully received";
|
|
}
|
|
|
|
currencies.add(result);
|
|
requestingCurrencies = false;
|
|
setState(Authenticated);
|
|
},
|
|
[this] (const QString& error, const std::optional<QVariantMap>& data) {
|
|
qDebug() << "Error receiving currencies:" << error;
|
|
requestingCurrencies = false;
|
|
setState(NotAuthenticated);
|
|
}
|
|
);
|
|
}
|