Co-Authored-By: selsta <selsta@sent.at>
Co-Authored-By: Gene Peters <gene@telligent-data.com>
This commit is contained in:
dsc 2019-05-01 04:05:16 +02:00 committed by xmrdsc
parent 3c1fe1da8b
commit d4be7634cb
10 changed files with 644 additions and 4 deletions

View file

@ -32,6 +32,7 @@ import QtGraphicalEffects 1.0
import moneroComponents.Wallet 1.0 import moneroComponents.Wallet 1.0
import moneroComponents.NetworkType 1.0 import moneroComponents.NetworkType 1.0
import moneroComponents.Clipboard 1.0 import moneroComponents.Clipboard 1.0
import FontAwesome 1.0
import "components" as MoneroComponents import "components" as MoneroComponents
import "components/effects/" as MoneroEffects import "components/effects/" as MoneroEffects
@ -44,10 +45,13 @@ Rectangle {
property alias unlockedBalanceLabelVisible: unlockedBalanceLabel.visible property alias unlockedBalanceLabelVisible: unlockedBalanceLabel.visible
property alias balanceLabelText: balanceLabel.text property alias balanceLabelText: balanceLabel.text
property alias balanceText: balanceText.text property alias balanceText: balanceText.text
property alias balanceTextFiat: balanceTextFiat.text
property alias unlockedBalanceTextFiat: unlockedBalanceTextFiat.text
property alias networkStatus : networkStatus property alias networkStatus : networkStatus
property alias progressBar : progressBar property alias progressBar : progressBar
property alias daemonProgressBar : daemonProgressBar property alias daemonProgressBar : daemonProgressBar
property alias minutesToUnlockTxt: unlockedBalanceLabel.text property alias minutesToUnlockTxt: unlockedBalanceLabel.text
property bool fiatBalance: false
property int titleBarHeight: 50 property int titleBarHeight: 50
property string copyValue: "" property string copyValue: ""
Clipboard { id: clipboard } Clipboard { id: clipboard }
@ -202,6 +206,26 @@ Rectangle {
} }
} }
} }
MoneroComponents.Label {
fontSize: 20
text: "¥"
color: "white"
visible: persistentSettings.fiatPriceEnabled
anchors.right: parent.right
anchors.rightMargin: 45
anchors.top: parent.top
anchors.topMargin: 28
themeTransition: false
MouseArea{
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: {
fiatBalance = !fiatBalance
}
}
}
} }
Item { Item {
@ -214,7 +238,7 @@ Rectangle {
width: 50 width: 50
MoneroComponents.TextPlain { MoneroComponents.TextPlain {
visible: !isMobile visible: !(fiatBalance && persistentSettings.fiatPriceEnabled)
id: balanceText id: balanceText
themeTransition: false themeTransition: false
anchors.left: parent.left anchors.left: parent.left
@ -255,9 +279,39 @@ Rectangle {
} }
} }
MoneroComponents.TextPlain {
visible: !balanceText.visible
id: balanceTextFiat
themeTransition: false
anchors.left: parent.left
anchors.leftMargin: 20
anchors.top: parent.top
anchors.topMargin: 76
font.family: "Arial"
color: "#FFFFFF"
text: "N/A"
font.pixelSize: balanceText.font.pixelSize
MouseArea {
hoverEnabled: true
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
onEntered: {
parent.color = MoneroComponents.Style.orange
}
onExited: {
parent.color = MoneroComponents.Style.white
}
onClicked: {
console.log("Copied to clipboard");
clipboard.setText(parent.text);
appWindow.showStatusMessage(qsTr("Copied to clipboard"),3)
}
}
}
MoneroComponents.TextPlain { MoneroComponents.TextPlain {
id: unlockedBalanceText id: unlockedBalanceText
visible: true visible: !(fiatBalance && persistentSettings.fiatPriceEnabled)
themeTransition: false themeTransition: false
anchors.left: parent.left anchors.left: parent.left
anchors.leftMargin: 20 anchors.leftMargin: 20
@ -297,6 +351,36 @@ Rectangle {
} }
} }
MoneroComponents.TextPlain {
id: unlockedBalanceTextFiat
themeTransition: false
visible: !unlockedBalanceText.visible
anchors.left: parent.left
anchors.leftMargin: 20
anchors.top: parent.top
anchors.topMargin: 126
font.family: "Arial"
color: "#FFFFFF"
text: "N/A"
font.pixelSize: unlockedBalanceText.font.pixelSize
MouseArea {
hoverEnabled: true
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
onEntered: {
parent.color = MoneroComponents.Style.orange
}
onExited: {
parent.color = MoneroComponents.Style.white
}
onClicked: {
console.log("Copied to clipboard");
clipboard.setText(parent.text);
appWindow.showStatusMessage(qsTr("Copied to clipboard"),3)
}
}
}
MoneroComponents.Label { MoneroComponents.Label {
id: unlockedBalanceLabel id: unlockedBalanceLabel
visible: true visible: true

View file

@ -154,3 +154,20 @@ function qmlEach(item, properties, ignoredObjectNames, arr){
return arr; return arr;
} }
function capitalize(s){
if (typeof s !== 'string') return ''
return s.charAt(0).toUpperCase() + s.slice(1)
}
function formatMoney(n, c, d, t) {
// https://stackoverflow.com/a/149099
var c = isNaN(c = Math.abs(c)) ? 2 : c,
d = d == undefined ? "." : d,
t = t == undefined ? "," : t,
s = n < 0 ? "-" : "",
i = String(parseInt(n = Math.abs(Number(n) || 0).toFixed(c))),
j = (j = i.length) > 3 ? j % 3 : 0;
return s + (j ? i.substr(0, j) + t : "") + i.substr(j).replace(/(\d{3})(?=\d)/g, "$1" + t) + (c ? d + Math.abs(n - i).toFixed(c).slice(2) : "");
};

View file

@ -30,6 +30,7 @@
#include <QQmlApplicationEngine> #include <QQmlApplicationEngine>
#include <QtQml> #include <QtQml>
#include <QStandardPaths> #include <QStandardPaths>
#include <QNetworkAccessManager>
#include <QIcon> #include <QIcon>
#include <QDebug> #include <QDebug>
#include <QDesktopServices> #include <QDesktopServices>
@ -65,6 +66,7 @@
#include "qt/utils.h" #include "qt/utils.h"
#include "qt/mime.h" #include "qt/mime.h"
#include "src/qt/KeysFiles.h" #include "src/qt/KeysFiles.h"
#include "qt/prices.h"
// IOS exclusions // IOS exclusions
#ifndef Q_OS_IOS #ifndef Q_OS_IOS
@ -356,6 +358,11 @@ int main(int argc, char *argv[])
builtWithScanner = true; builtWithScanner = true;
#endif #endif
engine.rootContext()->setContextProperty("builtWithScanner", builtWithScanner); engine.rootContext()->setContextProperty("builtWithScanner", builtWithScanner);
QNetworkAccessManager *manager = new QNetworkAccessManager();
Prices prices(manager);
engine.rootContext()->setContextProperty("Prices", &prices);
// Load main window (context properties needs to be defined obove this line) // Load main window (context properties needs to be defined obove this line)
engine.load(QUrl(QStringLiteral("qrc:///main.qml"))); engine.load(QUrl(QStringLiteral("qrc:///main.qml")));
if (engine.rootObjects().isEmpty()) if (engine.rootObjects().isEmpty())

174
main.qml
View file

@ -91,6 +91,26 @@ ApplicationWindow {
property int appEpoch: Math.floor((new Date).getTime() / 1000) property int appEpoch: Math.floor((new Date).getTime() / 1000)
property bool themeTransition: false property bool themeTransition: false
// fiat price conversion
property int fiatPriceXMRUSD: 0
property int fiatPriceXMREUR: 0
property var fiatPriceAPIs: {
return {
"kraken": {
"xmrusd": "https://api.kraken.com/0/public/Ticker?pair=XMRUSD",
"xmreur": "https://api.kraken.com/0/public/Ticker?pair=XMREUR"
},
"coingecko": {
"xmrusd": "https://api.coingecko.com/api/v3/simple/price?ids=monero&vs_currencies=usd",
"xmreur": "https://api.coingecko.com/api/v3/simple/price?ids=monero&vs_currencies=eur"
},
"cryptocompare": {
"xmrusd": "https://min-api.cryptocompare.com/data/price?fsym=XMR&tsyms=USD",
"xmreur": "https://min-api.cryptocompare.com/data/price?fsym=XMR&tsyms=EUR",
}
}
}
property string remoteNodeService: { property string remoteNodeService: {
// support user-defined remote node aggregators // support user-defined remote node aggregators
if(persistentSettings.remoteNodeService){ if(persistentSettings.remoteNodeService){
@ -394,6 +414,10 @@ ApplicationWindow {
middlePanel.balanceText = balance; middlePanel.balanceText = balance;
leftPanel.balanceText = balance; leftPanel.balanceText = balance;
if (persistentSettings.fiatPriceEnabled) {
appWindow.fiatApiUpdateBalance(balance, balance_unlocked);
}
var accountLabel = currentWallet.getSubaddressLabel(currentWallet.currentSubaddressAccount, 0); var accountLabel = currentWallet.getSubaddressLabel(currentWallet.currentSubaddressAccount, 0);
leftPanel.balanceLabelText = qsTr("Balance (#%1%2)").arg(currentWallet.currentSubaddressAccount).arg(accountLabel === "" ? "" : (" " + accountLabel)); leftPanel.balanceLabelText = qsTr("Balance (#%1%2)").arg(currentWallet.currentSubaddressAccount).arg(accountLabel === "" ? "" : (" " + accountLabel));
} }
@ -1102,6 +1126,7 @@ ApplicationWindow {
rootItem.state = "wizard" rootItem.state = "wizard"
// reset balance // reset balance
leftPanel.balanceText = leftPanel.unlockedBalanceText = walletManager.displayAmount(0); leftPanel.balanceText = leftPanel.unlockedBalanceText = walletManager.displayAmount(0);
fiatApiUpdateBalance(0, 0);
// disable timers // disable timers
userInActivityTimer.running = false; userInActivityTimer.running = false;
simpleModeConnectionTimer.running = false; simpleModeConnectionTimer.running = false;
@ -1126,6 +1151,143 @@ ApplicationWindow {
flags: persistentSettings.customDecorations ? Windows.flagsCustomDecorations : Windows.flags flags: persistentSettings.customDecorations ? Windows.flagsCustomDecorations : Windows.flags
onWidthChanged: x -= 0 onWidthChanged: x -= 0
Timer {
id: fiatPriceTimer
interval: 1000 * 60;
running: persistentSettings.fiatPriceEnabled;
repeat: true
onTriggered: {
if(persistentSettings.fiatPriceEnabled)
appWindow.fiatApiRefresh();
}
triggeredOnStart: false
}
function fiatApiParseTicker(resp, currency){
// parse & validate incoming JSON
if(resp._url.startsWith("https://api.kraken.com/0/")){
if(resp.hasOwnProperty("error") && resp.error.length > 0 || !resp.hasOwnProperty("result")){
appWindow.fiatApiError("Kraken API has error(s)");
return;
}
var key = currency === "xmreur" ? "XXMRZEUR" : "XXMRZUSD";
var ticker = resp.result[key]["o"];
return ticker;
} else if(resp._url.startsWith("https://api.coingecko.com/api/v3/")){
var key = currency === "xmreur" ? "eur" : "usd";
if(!resp.hasOwnProperty("monero") || !resp["monero"].hasOwnProperty(key)){
appWindow.fiatApiError("Coingecko API has error(s)");
return;
}
return resp["monero"][key];
} else if(resp._url.startsWith("https://min-api.cryptocompare.com/data/")){
var key = currency === "xmreur" ? "EUR" : "USD";
if(!resp.hasOwnProperty(key)){
appWindow.fiatApiError("cryptocompare API has error(s)");
return;
}
return resp[key];
}
}
function fiatApiGetCurrency(resp){
// map response to `appWindow.fiatPriceAPIs` object
if (!resp.hasOwnProperty('_url')){
appWindow.fiatApiError("invalid JSON");
return;
}
var apis = appWindow.fiatPriceAPIs;
for (var api in apis){
if (!apis.hasOwnProperty(api))
continue;
for (var cur in apis[api]){
if(!apis[api].hasOwnProperty(cur))
continue;
var url = apis[api][cur];
if(url === resp._url){
return cur;
}
}
}
}
function fiatApiJsonReceived(resp){
// handle incoming JSON, set ticker
var currency = appWindow.fiatApiGetCurrency(resp);
if(typeof currency == "undefined"){
appWindow.fiatApiError("could not get currency");
return;
}
var ticker = appWindow.fiatApiParseTicker(resp, currency);
if(ticker <= 0){
appWindow.fiatApiError("could not get ticker");
return;
}
if(persistentSettings.fiatPriceCurrency === "xmrusd")
appWindow.fiatPriceXMRUSD = ticker;
else if(persistentSettings.fiatPriceCurrency === "xmreur")
appWindow.fiatPriceXMREUR = ticker;
appWindow.updateBalance();
}
function fiatApiRefresh(){
// trigger API call
if(!persistentSettings.fiatPriceEnabled)
return;
var userProvider = persistentSettings.fiatPriceProvider;
if(!appWindow.fiatPriceAPIs.hasOwnProperty(userProvider)){
appWindow.fiatApiError("provider \"" + userProvider + "\" not implemented");
return;
}
var provider = appWindow.fiatPriceAPIs[userProvider];
var userCurrency = persistentSettings.fiatPriceCurrency;
if(!provider.hasOwnProperty(userCurrency)){
appWindow.fiatApiError("currency \"" + userCurrency + "\" not implemented");
}
var url = provider[userCurrency];
Prices.getJSON(url);
}
function fiatApiUpdateBalance(balance, unlocked_balance){
// update balance card
var ticker = persistentSettings.fiatPriceCurrency === "xmrusd" ? appWindow.fiatPriceXMRUSD : appWindow.fiatPriceXMREUR;
var symbol = persistentSettings.fiatPriceCurrency === "xmrusd" ? "$" : "€"
if(ticker <= 0){
console.log(fiatApiError("Could not update balance card; invalid ticker value"));
leftPanel.unlockedBalanceTextFiat = "N/A";
leftPanel.balanceTextFiat = "N/A";
return;
}
var uFiat = Utils.formatMoney(unlocked_balance * ticker);
var bFiat = Utils.formatMoney(balance * ticker);
leftPanel.unlockedBalanceTextFiat = symbol + uFiat;
leftPanel.balanceTextFiat = symbol + bFiat;
}
function fiatTimerStart(){
fiatPriceTimer.start();
}
function fiatTimerStop(){
fiatPriceTimer.stop();
}
function fiatApiError(msg){
console.log("fiatPriceError: " + msg);
}
Component.onCompleted: { Component.onCompleted: {
x = (Screen.width - width) / 2 x = (Screen.width - width) / 2
y = (Screen.height - maxWindowHeight) / 2 y = (Screen.height - maxWindowHeight) / 2
@ -1137,6 +1299,7 @@ ApplicationWindow {
walletManager.checkUpdatesComplete.connect(onWalletCheckUpdatesComplete); walletManager.checkUpdatesComplete.connect(onWalletCheckUpdatesComplete);
walletManager.walletPassphraseNeeded.connect(onWalletPassphraseNeeded); walletManager.walletPassphraseNeeded.connect(onWalletPassphraseNeeded);
IPC.uriHandler.connect(onUriHandler); IPC.uriHandler.connect(onUriHandler);
Prices.priceJsonReceived.connect(appWindow.fiatApiJsonReceived);
if(typeof daemonManager != "undefined") { if(typeof daemonManager != "undefined") {
daemonManager.daemonStarted.connect(onDaemonStarted); daemonManager.daemonStarted.connect(onDaemonStarted);
@ -1144,8 +1307,6 @@ ApplicationWindow {
daemonManager.daemonStopped.connect(onDaemonStopped); daemonManager.daemonStopped.connect(onDaemonStopped);
} }
// Connect app exit to qml window exit handling // Connect app exit to qml window exit handling
mainApp.closing.connect(appWindow.close); mainApp.closing.connect(appWindow.close);
@ -1177,6 +1338,11 @@ ApplicationWindow {
} }
checkUpdates(); checkUpdates();
if(persistentSettings.fiatPriceEnabled){
appWindow.fiatApiRefresh();
appWindow.fiatTimerStart();
}
} }
Settings { Settings {
@ -1223,6 +1389,10 @@ ApplicationWindow {
property bool showPid: false property bool showPid: false
property bool blackTheme: true property bool blackTheme: true
property bool fiatPriceEnabled: false
property string fiatPriceProvider: "kraken"
property string fiatPriceCurrency: "xmrusd"
Component.onCompleted: { Component.onCompleted: {
MoneroComponents.Style.blackTheme = persistentSettings.blackTheme MoneroComponents.Style.blackTheme = persistentSettings.blackTheme
} }

View file

@ -66,6 +66,8 @@ HEADERS += \
src/qt/mime.h \ src/qt/mime.h \
src/qt/KeysFiles.h \ src/qt/KeysFiles.h \
src/qt/utils.h src/qt/utils.h
src/qt/utils.h \
src/qt/prices.h
SOURCES += main.cpp \ SOURCES += main.cpp \
filter.cpp \ filter.cpp \
@ -98,6 +100,8 @@ SOURCES += main.cpp \
src/qt/mime.cpp \ src/qt/mime.cpp \
src/qt/KeysFiles.cpp \ src/qt/KeysFiles.cpp \
src/qt/utils.cpp src/qt/utils.cpp
src/qt/utils.cpp \
src/qt/prices.cpp
CONFIG(DISABLE_PASS_STRENGTH_METER) { CONFIG(DISABLE_PASS_STRENGTH_METER) {
HEADERS -= src/zxcvbn-c/zxcvbn.h HEADERS -= src/zxcvbn-c/zxcvbn.h

View file

@ -157,6 +157,106 @@ Rectangle {
} }
} }
//! Manage pricing
RowLayout {
MoneroComponents.CheckBox {
id: enableConvertCurrency
text: qsTr("Enable displaying balance in other currencies") + translationManager.emptyString
checked: persistentSettings.fiatPriceEnabled
onCheckedChanged: {
if (!checked) {
console.log("Disabled price conversion");
persistentSettings.fiatPriceEnabled = false;
appWindow.fiatTimerStop();
}
}
}
}
GridLayout {
visible: enableConvertCurrency.checked
columns: 2
Layout.fillWidth: true
Layout.leftMargin: 36
columnSpacing: 32
ColumnLayout {
spacing: 10
Layout.fillWidth: true
MoneroComponents.Label {
Layout.fillWidth: true
fontSize: 14
text: qsTr("Price source") + translationManager.emptyString
}
MoneroComponents.StandardDropdown {
id: fiatPriceProviderDropDown
Layout.fillWidth: true
dataModel: fiatPriceProvidersModel
onChanged: {
var obj = dataModel.get(currentIndex);
persistentSettings.fiatPriceProvider = obj.data;
if(persistentSettings.fiatPriceEnabled)
appWindow.fiatApiRefresh();
}
}
}
ColumnLayout {
spacing: 10
Layout.fillWidth: true
MoneroComponents.Label {
Layout.fillWidth: true
fontSize: 14
text: qsTr("Currency") + translationManager.emptyString
}
MoneroComponents.StandardDropdown {
id: fiatPriceCurrencyDropdown
Layout.fillWidth: true
dataModel: fiatPriceCurrencyModel
onChanged: {
var obj = dataModel.get(currentIndex);
persistentSettings.fiatPriceCurrency = obj.data;
if(persistentSettings.fiatPriceEnabled)
appWindow.fiatApiRefresh();
}
}
}
z: parent.z + 1
}
ColumnLayout {
// Feature needs to be double enabled for security purposes (miss-clicks)
visible: enableConvertCurrency.checked && !persistentSettings.fiatPriceEnabled
spacing: 0
Layout.topMargin: 5
Layout.leftMargin: 36
MoneroComponents.WarningBox {
text: qsTr("Enabling price conversion exposes your IP address to the selected price source.") + translationManager.emptyString;
}
MoneroComponents.StandardButton {
Layout.topMargin: 10
Layout.bottomMargin: 10
small: true
text: qsTr("Confirm and enable") + translationManager.emptyString
onClicked: {
console.log("Enabled price conversion");
persistentSettings.fiatPriceEnabled = true;
appWindow.fiatApiRefresh();
appWindow.fiatTimerStart();
}
}
}
MoneroComponents.StandardButton { MoneroComponents.StandardButton {
visible: !persistentSettings.customDecorations visible: !persistentSettings.customDecorations
Layout.topMargin: 10 Layout.topMargin: 10
@ -177,7 +277,43 @@ Rectangle {
} }
} }
ListModel {
id: fiatPriceProvidersModel
}
ListModel {
id: fiatPriceCurrencyModel
ListElement {
data: "xmrusd"
column1: "USD"
}
ListElement {
data: "xmreur"
column1: "EUR"
}
}
Component.onCompleted: { Component.onCompleted: {
// Dynamically fill fiatPrice dropdown based on `appWindow.fiatPriceAPIs`
var apis = appWindow.fiatPriceAPIs;
fiatPriceProvidersModel.clear();
var i = 0;
for (var api in apis){
if (!apis.hasOwnProperty(api))
continue;
fiatPriceProvidersModel.append({"column1": Utils.capitalize(api), "data": api});
if(api === persistentSettings.fiatPriceProvider)
fiatPriceProviderDropDown.currentIndex = i;
i += 1;
}
fiatPriceProviderDropDown.update();
fiatPriceCurrencyDropdown.currentIndex = persistentSettings.fiatPriceCurrency === "xmrusd" ? 0 : 1;
fiatPriceCurrencyDropdown.update();
console.log('SettingsLayout loaded'); console.log('SettingsLayout loaded');
} }
} }

110
src/qt/prices.cpp Normal file
View file

@ -0,0 +1,110 @@
// Copyright (c) 2014-2019, The Monero Project
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other
// materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its contributors may be
// used to endorse or promote products derived from this software without specific
// prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <QtCore>
#include <QNetworkAccessManager>
#include "utils.h"
#include "prices.h"
Prices::Prices(QNetworkAccessManager *networkAccessManager, QObject *parent)
: QObject(parent) {
this->m_networkAccessManager = networkAccessManager;
}
void Prices::getJSON(const QString url) {
qDebug() << QString("Fetching: %1").arg(url);
QNetworkRequest request;
request.setUrl(QUrl(url));
request.setRawHeader("User-Agent", randomUserAgent().toUtf8());
request.setRawHeader("Content-Type", "application/json");
m_reply = this->m_networkAccessManager->get(request);
connect(m_reply, SIGNAL(finished()), this, SLOT(gotJSON()));
}
void Prices::gotJSON() {
// Check connectivity
if (!m_reply || m_reply->error() != QNetworkReply::NoError){
this->gotError();
m_reply->deleteLater();
return;
}
// Check json header
QList<QByteArray> headerList = m_reply->rawHeaderList();
QByteArray headerJson = m_reply->rawHeader("Content-Type");
if(headerJson.length() <= 15){
this->gotError("Bad Content-Type");
m_reply->deleteLater();
return;
}
QString headerJsonStr = QTextCodec::codecForMib(106)->toUnicode(headerJson);
int _contentType = headerList.indexOf("Content-Type");
if (_contentType < 0 || !headerJsonStr.startsWith("application/json")){
this->gotError("Bad Content-Type");
m_reply->deleteLater();
return;
}
// Check valid json document
QByteArray data = m_reply->readAll();
QJsonDocument doc = QJsonDocument::fromJson(data);
QString jsonString = doc.toJson(QJsonDocument::Indented);
if (jsonString.isEmpty()){
this->gotError("Bad JSON");
m_reply->deleteLater();
return;
}
// Insert source url for later reference
QUrl url = m_reply->url();
QJsonObject docobj = doc.object();
docobj["_url"] = url.toString();
doc.setObject(docobj);
qDebug() << QString("Fetched: %1").arg(url.toString());
// Emit signal
QVariantMap vMap = doc.object().toVariantMap();
emit priceJsonReceived(vMap);
m_reply->deleteLater();
}
void Prices::gotError() {
this->gotError("Unknown error");
}
void Prices::gotError(const QString &message) {
qCritical() << __FUNCTION__ << ": Error: " << message;
emit priceJsonError(message);
}

30
src/qt/prices.h Normal file
View file

@ -0,0 +1,30 @@
#ifndef PRICES_H
#define PRICES_H
#include <QCoreApplication>
#include <QtNetwork>
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QDebug>
class Prices : public QObject
{
Q_OBJECT
public:
Prices(QNetworkAccessManager *networkAccessManager, QObject *parent = nullptr);
public slots:
Q_INVOKABLE void getJSON(const QString url);
void gotJSON();
void gotError();
void gotError(const QString &message);
signals:
void priceJsonReceived(QVariantMap document);
void priceJsonError(QString message);
private:
mutable QPointer<QNetworkReply> m_reply;
QNetworkAccessManager *m_networkAccessManager;
};
#endif // PRICES_H

View file

@ -46,3 +46,84 @@ QString getAccountName(){
accountName = "My monero Account"; accountName = "My monero Account";
return accountName; return accountName;
} }
QString randomUserAgent(){
QStringList urand;
urand << "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:40.0) Gecko/20100101 Firefox/40.1"
<< "Mozilla/5.0 (Windows NT 6.3; rv:36.0) Gecko/20100101 Firefox/36.0"
<< "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10; rv:33.0) Gecko/20100101 Firefox/33.0"
<< "Mozilla/5.0 (X11; Linux i586; rv:31.0) Gecko/20100101 Firefox/31.0"
<< "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:31.0) Gecko/20130401 Firefox/31.0"
<< "Mozilla/5.0 (Windows NT 5.1; rv:31.0) Gecko/20100101 Firefox/31.0"
<< "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:29.0) Gecko/20120101 Firefox/29.0"
<< "Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:25.0) Gecko/20100101 Firefox/29.0"
<< "Mozilla/5.0 (X11; OpenBSD amd64; rv:28.0) Gecko/20100101 Firefox/28.0"
<< "Mozilla/5.0 (X11; Linux x86_64; rv:28.0) Gecko/20100101 Firefox/28.0"
<< "Mozilla/5.0 (Windows NT 6.1; rv:27.3) Gecko/20130101 Firefox/27.3"
<< "Mozilla/5.0 (Windows NT 6.2; Win64; x64; rv:27.0) Gecko/20121011 Firefox/27.0"
<< "Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:25.0) Gecko/20100101 Firefox/25.0"
<< "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.6; rv:25.0) Gecko/20100101 Firefox/25.0"
<< "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:24.0) Gecko/20100101 Firefox/24.0"
<< "Mozilla/5.0 (Windows NT 6.0; WOW64; rv:24.0) Gecko/20100101 Firefox/24.0"
<< "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:24.0) Gecko/20100101 Firefox/24.0"
<< "Mozilla/5.0 (Windows NT 6.2; rv:22.0) Gecko/20130405 Firefox/23.0"
<< "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:23.0) Gecko/20130406 Firefox/23.0"
<< "Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:23.0) Gecko/20131011 Firefox/23.0"
<< "Mozilla/5.0 (Windows NT 6.2; rv:22.0) Gecko/20130405 Firefox/22.0"
<< "Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:22.0) Gecko/20130328 Firefox/22.0"
<< "Mozilla/5.0 (Windows NT 6.1; rv:22.0) Gecko/20130405 Firefox/22.0"
<< "Mozilla/5.0 (Microsoft Windows NT 6.2.9200.0); rv:22.0) Gecko/20130405 Firefox/22.0"
<< "Mozilla/5.0 (Windows NT 6.2; Win64; x64; rv:16.0.1) Gecko/20121011 Firefox/21.0.1"
<< "Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:16.0.1) Gecko/20121011 Firefox/21.0.1"
<< "Mozilla/5.0 (Windows NT 6.2; Win64; x64; rv:21.0.0) Gecko/20121011 Firefox/21.0.0"
<< "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:21.0) Gecko/20130331 Firefox/21.0"
<< "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:21.0) Gecko/20100101 Firefox/21.0"
<< "Mozilla/5.0 (X11; Linux i686; rv:21.0) Gecko/20100101 Firefox/21.0"
<< "Mozilla/5.0 (Windows NT 6.2; WOW64; rv:21.0) Gecko/20130514 Firefox/21.0"
<< "Mozilla/5.0 (Windows NT 6.2; rv:21.0) Gecko/20130326 Firefox/21.0"
<< "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:21.0) Gecko/20130401 Firefox/21.0"
<< "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:21.0) Gecko/20130331 Firefox/21.0"
<< "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:21.0) Gecko/20130330 Firefox/21.0"
<< "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:21.0) Gecko/20100101 Firefox/21.0"
<< "Mozilla/5.0 (Windows NT 6.1; rv:21.0) Gecko/20130401 Firefox/21.0"
<< "Mozilla/5.0 (Windows NT 6.1; rv:21.0) Gecko/20130328 Firefox/21.0"
<< "Mozilla/5.0 (Windows NT 6.1; rv:21.0) Gecko/20100101 Firefox/21.0"
<< "Mozilla/5.0 (Windows NT 5.1; rv:21.0) Gecko/20130401 Firefox/21.0"
<< "Mozilla/5.0 (Windows NT 5.1; rv:21.0) Gecko/20130331 Firefox/21.0"
<< "Mozilla/5.0 (Windows NT 5.1; rv:21.0) Gecko/20100101 Firefox/21.0"
<< "Mozilla/5.0 (Windows NT 5.0; rv:21.0) Gecko/20100101 Firefox/21.0"
<< "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:21.0) Gecko/20100101 Firefox/21.0"
<< "Mozilla/5.0 (Windows NT 6.2; Win64; x64;) Gecko/20100101 Firefox/20.0"
<< "Mozilla/5.0 (Windows x86; rv:19.0) Gecko/20100101 Firefox/19.0"
<< "Mozilla/5.0 (Windows NT 6.1; rv:6.0) Gecko/20100101 Firefox/19.0"
<< "Mozilla/5.0 (Windows NT 6.1; rv:14.0) Gecko/20100101 Firefox/18.0.1"
<< "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:18.0) Gecko/20100101 Firefox/18.0"
<< "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:17.0) Gecko/20100101 Firefox/17.0.6"
<< "Mozilla/5.0 (X11; Ubuntu; Linux armv7l; rv:17.0) Gecko/20100101 Firefox/17.0"
<< "Mozilla/6.0 (Windows NT 6.2; WOW64; rv:16.0.1) Gecko/20121011 Firefox/16.0.1"
<< "Mozilla/5.0 (Windows NT 6.2; WOW64; rv:16.0.1) Gecko/20121011 Firefox/16.0.1"
<< "Mozilla/5.0 (Windows NT 6.2; Win64; x64; rv:16.0.1) Gecko/20121011 Firefox/16.0.1"
<< "Mozilla/5.0 (X11; NetBSD amd64; rv:16.0) Gecko/20121102 Firefox/16.0"
<< "Mozilla/5.0 (Windows NT 6.1; rv:15.0) Gecko/20120716 Firefox/15.0a2"
<< "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.1.16) Gecko/20120427 Firefox/15.0a1"
<< "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:15.0) Gecko/20120427 Firefox/15.0a1"
<< "Mozilla/5.0 (Windows NT 6.2; WOW64; rv:15.0) Gecko/20120910144328 Firefox/15.0.2"
<< "Mozilla/5.0 (X11; Ubuntu; Linux i686; rv:15.0) Gecko/20100101 Firefox/15.0.1"
<< "Mozilla/5.0 (Windows; U; Windows NT 5.1; rv:15.0) Gecko/20121011 Firefox/15.0.1"
<< "Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:14.0) Gecko/20120405 Firefox/14.0a1"
<< "Mozilla/5.0 (Windows NT 6.1; rv:14.0) Gecko/20120405 Firefox/14.0a1"
<< "Mozilla/5.0 (Windows NT 5.1; rv:14.0) Gecko/20120405 Firefox/14.0a1"
<< "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:14.0) Gecko/20100101 Firefox/14.0.1"
<< "Mozilla/5.0 (X11; Ubuntu; Linux i686; rv:14.0) Gecko/20100101 Firefox/14.0.1"
<< "Mozilla/5.0 (Windows; U; Windows NT 6.1; WOW64; en-US; rv:2.0.4) Gecko/20120718 AskTbAVR-IDW/3.12.5.17700 Firefox/14.0.1"
<< "Mozilla/5.0 (Windows NT 6.1; rv:12.0) Gecko/20120403211507 Firefox/14.0.1"
<< "Mozilla/5.0 (Windows NT 6.1; rv:12.0) Gecko/ 20120405 Firefox/14.0.1"
<< "Mozilla/5.0 (Windows NT 6.0; rv:14.0) Gecko/20100101 Firefox/14.0.1"
<< "Mozilla/5.0 (Windows NT 5.1; rv:15.0) Gecko/20100101 Firefox/13.0.1"
<< "Mozilla/5.0 (Windows NT 6.1; rv:12.0) Gecko/20120403211507 Firefox/12.0"
<< "Mozilla/5.0 (Windows NT 6.1; de;rv:12.0) Gecko/20120403211507 Firefox/12.0";
// @TODO: Qt 5.10 - QRandomGenerator
int irand = rand() % urand.length();
return urand.at(irand);
}

View file

@ -35,5 +35,6 @@
bool fileExists(QString path); bool fileExists(QString path);
QString getAccountName(); QString getAccountName();
const static QRegExp reURI = QRegExp("^\\w+:\\/\\/([\\w+\\-?\\-_\\-=\\-&]+)"); const static QRegExp reURI = QRegExp("^\\w+:\\/\\/([\\w+\\-?\\-_\\-=\\-&]+)");
QString randomUserAgent();
#endif // UTILS_H #endif // UTILS_H