mirror of
https://github.com/overte-org/overte.git
synced 2025-08-09 11:28:03 +02:00
Make API asynchronous, for communicating with server
This commit is contained in:
parent
8f0fd10e2b
commit
551ea921cd
7 changed files with 75 additions and 32 deletions
|
@ -30,6 +30,18 @@ Rectangle {
|
||||||
color: hifi.colors.baseGray;
|
color: hifi.colors.baseGray;
|
||||||
Hifi.QmlCommerce {
|
Hifi.QmlCommerce {
|
||||||
id: commerce;
|
id: commerce;
|
||||||
|
onBuyResult: {
|
||||||
|
if (failureMessage.length) {
|
||||||
|
console.log('buy failed', failureMessage);
|
||||||
|
//fixme sendToScript({method: 'checkout_cancelClicked', params: itemId});
|
||||||
|
} else {
|
||||||
|
console.log('buy ok');
|
||||||
|
//fixme sendToScript({method: 'checkout_buyClicked', success: , itemId: itemId, itemHref: itemHref});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// FIXME: remove these two after testing
|
||||||
|
onBalanceResult: console.log('balance', balance, failureMessage);
|
||||||
|
onInventoryResult: console.log('inventory', inventory, failureMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
|
@ -247,13 +259,13 @@ Rectangle {
|
||||||
width: parent.width/2 - anchors.leftMargin*2;
|
width: parent.width/2 - anchors.leftMargin*2;
|
||||||
text: "Cancel"
|
text: "Cancel"
|
||||||
onClicked: {
|
onClicked: {
|
||||||
sendToScript({method: 'checkout_cancelClicked', params: itemId});
|
sendToScript({method: 'checkout_cancelClicked', params: itemId}); //fixme
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// "Buy" button
|
// "Buy" button
|
||||||
HifiControlsUit.Button {
|
HifiControlsUit.Button {
|
||||||
property bool buyFailed: false;
|
property bool buyFailed: false; // fixme
|
||||||
id: buyButton;
|
id: buyButton;
|
||||||
color: hifi.buttons.black;
|
color: hifi.buttons.black;
|
||||||
colorScheme: hifi.colorSchemes.dark;
|
colorScheme: hifi.colorSchemes.dark;
|
||||||
|
@ -266,11 +278,8 @@ Rectangle {
|
||||||
width: parent.width/2 - anchors.rightMargin*2;
|
width: parent.width/2 - anchors.rightMargin*2;
|
||||||
text: "Buy"
|
text: "Buy"
|
||||||
onClicked: {
|
onClicked: {
|
||||||
if (buyFailed) {
|
// fixme do spinner thing
|
||||||
sendToScript({method: 'checkout_cancelClicked', params: itemId});
|
commerce.buy(itemId, parseInt(itemPriceText.text));
|
||||||
} else {
|
|
||||||
sendToScript({method: 'checkout_buyClicked', success: commerce.buy(itemId, parseInt(itemPriceText.text)), itemId: itemId, itemHref: itemHref});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
#include "Ledger.h"
|
#include "Ledger.h"
|
||||||
#include "CommerceLogging.h"
|
#include "CommerceLogging.h"
|
||||||
|
|
||||||
bool Ledger::buy(const QString& hfc_key, int cost, const QString& asset_id, const QString& inventory_key, const QString& buyerUsername) {
|
void Ledger::buy(const QString& hfc_key, int cost, const QString& asset_id, const QString& inventory_key, const QString& buyerUsername) {
|
||||||
QJsonObject transaction;
|
QJsonObject transaction;
|
||||||
transaction["hfc_key"] = hfc_key;
|
transaction["hfc_key"] = hfc_key;
|
||||||
transaction["hfc"] = cost;
|
transaction["hfc"] = cost;
|
||||||
|
@ -34,32 +34,41 @@ bool Ledger::buy(const QString& hfc_key, int cost, const QString& asset_id, cons
|
||||||
|
|
||||||
qCInfo(commerce) << "Transaction:" << QJsonDocument(request).toJson(QJsonDocument::Compact);
|
qCInfo(commerce) << "Transaction:" << QJsonDocument(request).toJson(QJsonDocument::Compact);
|
||||||
// FIXME: talk to server instead
|
// FIXME: talk to server instead
|
||||||
QStringList keySet{ hfc_key };
|
if (_inventory.contains(asset_id)) {
|
||||||
if (initializedBalance() < cost) return false;
|
// This is here more for testing than as a definition of semantics.
|
||||||
|
// When we have popcerts, you will certainly be able to buy a new instance of an item that you already own a different instance of.
|
||||||
|
// I'm not sure what the server should do for now in this project's MVP.
|
||||||
|
return emit buyResult("Already owned.");
|
||||||
|
}
|
||||||
|
if (initializedBalance() < cost) {
|
||||||
|
return emit buyResult("Insufficient funds.");
|
||||||
|
}
|
||||||
_balance -= cost;
|
_balance -= cost;
|
||||||
_inventory.push_back(asset_id);
|
_inventory.push_back(asset_id);
|
||||||
return true; // FIXME send to server.
|
emit buyResult("");
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Ledger::receiveAt(const QString& hfc_key) {
|
bool Ledger::receiveAt(const QString& hfc_key) {
|
||||||
auto accountManager = DependencyManager::get<AccountManager>();
|
auto accountManager = DependencyManager::get<AccountManager>();
|
||||||
if (!accountManager->isLoggedIn()) {
|
if (!accountManager->isLoggedIn()) {
|
||||||
qCWarning(commerce) << "Cannot set receiveAt when not logged in.";
|
qCWarning(commerce) << "Cannot set receiveAt when not logged in.";
|
||||||
return false;
|
emit receiveAtResult("Not logged in");
|
||||||
|
return false; // We know right away that we will fail, so tell the caller.
|
||||||
}
|
}
|
||||||
auto username = accountManager->getAccountInfo().getUsername();
|
auto username = accountManager->getAccountInfo().getUsername();
|
||||||
qCInfo(commerce) << "Setting default receiving key for" << username;
|
qCInfo(commerce) << "Setting default receiving key for" << username;
|
||||||
return true; // FIXME send to server.
|
emit receiveAtResult(""); // FIXME: talk to server instead.
|
||||||
|
return true; // Note that there may still be an asynchronous signal of failure that callers might be interested in.
|
||||||
}
|
}
|
||||||
|
|
||||||
int Ledger::balance(const QStringList& keys) {
|
void Ledger::balance(const QStringList& keys) {
|
||||||
// FIXME: talk to server instead
|
// FIXME: talk to server instead
|
||||||
qCInfo(commerce) << "Balance:" << initializedBalance();
|
qCInfo(commerce) << "Balance:" << initializedBalance();
|
||||||
return _balance;
|
emit balanceResult(_balance, "");
|
||||||
}
|
}
|
||||||
|
|
||||||
QStringList Ledger::inventory(const QStringList& keys) {
|
void Ledger::inventory(const QStringList& keys) {
|
||||||
// FIXME: talk to server instead
|
// FIXME: talk to server instead
|
||||||
qCInfo(commerce) << "Inventory:" << _inventory;
|
qCInfo(commerce) << "Inventory:" << _inventory;
|
||||||
return _inventory;
|
emit inventoryResult(_inventory, "");
|
||||||
}
|
}
|
|
@ -21,10 +21,16 @@ class Ledger : public QObject, public Dependency {
|
||||||
SINGLETON_DEPENDENCY
|
SINGLETON_DEPENDENCY
|
||||||
|
|
||||||
public:
|
public:
|
||||||
bool buy(const QString& hfc_key, int cost, const QString& asset_id, const QString& inventory_key, const QString& buyerUsername = "");
|
void buy(const QString& hfc_key, int cost, const QString& asset_id, const QString& inventory_key, const QString& buyerUsername = "");
|
||||||
bool receiveAt(const QString& hfc_key);
|
bool receiveAt(const QString& hfc_key);
|
||||||
int balance(const QStringList& keys);
|
void balance(const QStringList& keys);
|
||||||
QStringList inventory(const QStringList& keys);
|
void inventory(const QStringList& keys);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void buyResult(const QString& failureReason);
|
||||||
|
void receiveAtResult(const QString& failureReason);
|
||||||
|
void balanceResult(int balance, const QString& failureReason);
|
||||||
|
void inventoryResult(QStringList inventory, const QString& failureReason);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// These in-memory caches is temporary, until we start sending things to the server.
|
// These in-memory caches is temporary, until we start sending things to the server.
|
||||||
|
|
|
@ -17,29 +17,35 @@
|
||||||
|
|
||||||
HIFI_QML_DEF(QmlCommerce)
|
HIFI_QML_DEF(QmlCommerce)
|
||||||
|
|
||||||
bool QmlCommerce::buy(const QString& assetId, int cost, const QString& buyerUsername) {
|
QmlCommerce::QmlCommerce(QQuickItem* parent) : OffscreenQmlDialog(parent) {
|
||||||
|
auto ledger = DependencyManager::get<Ledger>();
|
||||||
|
connect(ledger.data(), &Ledger::buyResult, this, &QmlCommerce::buyResult);
|
||||||
|
connect(ledger.data(), &Ledger::balanceResult, this, &QmlCommerce::balanceResult);
|
||||||
|
connect(ledger.data(), &Ledger::inventoryResult, this, &QmlCommerce::inventoryResult);
|
||||||
|
}
|
||||||
|
|
||||||
|
void QmlCommerce::buy(const QString& assetId, int cost, const QString& buyerUsername) {
|
||||||
auto ledger = DependencyManager::get<Ledger>();
|
auto ledger = DependencyManager::get<Ledger>();
|
||||||
auto wallet = DependencyManager::get<Wallet>();
|
auto wallet = DependencyManager::get<Wallet>();
|
||||||
QStringList keys = wallet->listPublicKeys();
|
QStringList keys = wallet->listPublicKeys();
|
||||||
if (keys.count() == 0) {
|
if (keys.count() == 0) {
|
||||||
return false;
|
return emit buyResult("Uninitialized Wallet.");
|
||||||
}
|
}
|
||||||
QString key = keys[0];
|
QString key = keys[0];
|
||||||
// For now, we receive at the same key that pays for it.
|
// For now, we receive at the same key that pays for it.
|
||||||
bool success = ledger->buy(key, cost, assetId, key, buyerUsername);
|
ledger->buy(key, cost, assetId, key, buyerUsername);
|
||||||
// FIXME: until we start talking to server, report post-transaction balance and inventory so we can see log for testing.
|
// FIXME: until we start talking to server, report post-transaction balance and inventory so we can see log for testing.
|
||||||
balance();
|
balance();
|
||||||
inventory();
|
inventory();
|
||||||
return success;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int QmlCommerce::balance() {
|
void QmlCommerce::balance() {
|
||||||
auto ledger = DependencyManager::get<Ledger>();
|
auto ledger = DependencyManager::get<Ledger>();
|
||||||
auto wallet = DependencyManager::get<Wallet>();
|
auto wallet = DependencyManager::get<Wallet>();
|
||||||
return ledger->balance(wallet->listPublicKeys());
|
ledger->balance(wallet->listPublicKeys());
|
||||||
}
|
}
|
||||||
QStringList QmlCommerce::inventory() {
|
void QmlCommerce::inventory() {
|
||||||
auto ledger = DependencyManager::get<Ledger>();
|
auto ledger = DependencyManager::get<Ledger>();
|
||||||
auto wallet = DependencyManager::get<Wallet>();
|
auto wallet = DependencyManager::get<Wallet>();
|
||||||
return ledger->inventory(wallet->listPublicKeys());
|
ledger->inventory(wallet->listPublicKeys());
|
||||||
}
|
}
|
|
@ -21,13 +21,20 @@ class QmlCommerce : public OffscreenQmlDialog {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
HIFI_QML_DECL
|
HIFI_QML_DECL
|
||||||
|
|
||||||
|
public:
|
||||||
|
QmlCommerce(QQuickItem* parent = nullptr);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void buyResult(const QString& failureMessage);
|
void buyResult(const QString& failureMessage);
|
||||||
|
// Balance and Inventory are NOT properties, because QML can't change them (without risk of failure), and
|
||||||
|
// because we can't scalably know of out-of-band changes (e.g., another machine interacting with the block chain).
|
||||||
|
void balanceResult(int balance, const QString& failureMessage);
|
||||||
|
void inventoryResult(QStringList inventory, const QString& failureMessage);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
Q_INVOKABLE bool buy(const QString& assetId, int cost, const QString& buyerUsername = "");
|
Q_INVOKABLE void buy(const QString& assetId, int cost, const QString& buyerUsername = "");
|
||||||
Q_INVOKABLE int balance();
|
Q_INVOKABLE void balance();
|
||||||
Q_INVOKABLE QStringList inventory();
|
Q_INVOKABLE void inventory();
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // hifi_QmlCommerce_h
|
#endif // hifi_QmlCommerce_h
|
||||||
|
|
|
@ -25,7 +25,12 @@ bool Wallet::generateKeyPair() {
|
||||||
// FIXME: need private key, too, and persist in file.
|
// FIXME: need private key, too, and persist in file.
|
||||||
qCInfo(commerce) << "Generating keypair.";
|
qCInfo(commerce) << "Generating keypair.";
|
||||||
QString key = QUuid::createUuid().toString();
|
QString key = QUuid::createUuid().toString();
|
||||||
_publicKeys.push_back(key);
|
|
||||||
|
_publicKeys.push_back(key); // Keep in memory for synchronous speed.
|
||||||
|
// It's arguable whether we want to change the receiveAt every time, but:
|
||||||
|
// 1. It's certainly needed the first time, when createIfNeeded answers true.
|
||||||
|
// 2. It is maximally private, and we can step back from that later if desired.
|
||||||
|
// 3. It maximally exercises all the machinery, so we are most likely to surface issues now.
|
||||||
auto ledger = DependencyManager::get<Ledger>();
|
auto ledger = DependencyManager::get<Ledger>();
|
||||||
return ledger->receiveAt(key);
|
return ledger->receiveAt(key);
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,7 @@ class Wallet : public QObject, public Dependency {
|
||||||
SINGLETON_DEPENDENCY
|
SINGLETON_DEPENDENCY
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
// These are currently blocking calls, although they might take a moment.
|
||||||
bool createIfNeeded();
|
bool createIfNeeded();
|
||||||
bool generateKeyPair();
|
bool generateKeyPair();
|
||||||
QStringList listPublicKeys();
|
QStringList listPublicKeys();
|
||||||
|
|
Loading…
Reference in a new issue