magpie/models/magpie.cpp

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 &currencies;
}
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);
}
);
}