diff --git a/interface/resources/qml/hifi/commerce/purchases/Purchases.qml b/interface/resources/qml/hifi/commerce/purchases/Purchases.qml index de66be4a88..068403e098 100644 --- a/interface/resources/qml/hifi/commerce/purchases/Purchases.qml +++ b/interface/resources/qml/hifi/commerce/purchases/Purchases.qml @@ -472,6 +472,13 @@ Rectangle { } } } + + onAtYEndChanged: { + if (purchasesContentsList.atYEnd) { + console.log("User scrolled to the bottom of 'My Purchases'."); + // Grab next page of results and append to model + } + } } Item { diff --git a/interface/resources/qml/hifi/commerce/wallet/WalletHome.qml b/interface/resources/qml/hifi/commerce/wallet/WalletHome.qml index 42ee44d584..16ad999441 100644 --- a/interface/resources/qml/hifi/commerce/wallet/WalletHome.qml +++ b/interface/resources/qml/hifi/commerce/wallet/WalletHome.qml @@ -25,8 +25,11 @@ Item { HifiConstants { id: hifi; } id: root; - property bool historyReceived: false; + property bool initialHistoryReceived: false; + property bool historyRequestPending: true; + property bool noMoreHistoryData: false; property int pendingCount: 0; + property int currentHistoryPage: 1; Connections { target: Commerce; @@ -36,32 +39,50 @@ Item { } onHistoryResult : { - historyReceived = true; - if (result.status === 'success') { - var sameItemCount = 0; - tempTransactionHistoryModel.clear(); - - tempTransactionHistoryModel.append(result.data.history); - - for (var i = 0; i < tempTransactionHistoryModel.count; i++) { - if (!transactionHistoryModel.get(i)) { - sameItemCount = -1; - break; - } else if (tempTransactionHistoryModel.get(i).transaction_type === transactionHistoryModel.get(i).transaction_type && - tempTransactionHistoryModel.get(i).text === transactionHistoryModel.get(i).text) { - sameItemCount++; - } - } + root.initialHistoryReceived = true; + root.historyRequestPending = false; - if (sameItemCount !== tempTransactionHistoryModel.count) { - transactionHistoryModel.clear(); + if (result.status === 'success') { + if (result.data.history.length === 0) { + root.noMoreHistoryData = true; + } else if (root.currentHistoryPage === 1) { + var sameItemCount = 0; + tempTransactionHistoryModel.clear(); + + tempTransactionHistoryModel.append(result.data.history); + for (var i = 0; i < tempTransactionHistoryModel.count; i++) { - transactionHistoryModel.append(tempTransactionHistoryModel.get(i)); + if (!transactionHistoryModel.get(i)) { + sameItemCount = -1; + break; + } else if (tempTransactionHistoryModel.get(i).transaction_type === transactionHistoryModel.get(i).transaction_type && + tempTransactionHistoryModel.get(i).text === transactionHistoryModel.get(i).text) { + sameItemCount++; + } + } + + if (sameItemCount !== tempTransactionHistoryModel.count) { + transactionHistoryModel.clear(); + for (var i = 0; i < tempTransactionHistoryModel.count; i++) { + transactionHistoryModel.append(tempTransactionHistoryModel.get(i)); + } + calculatePendingAndInvalidated(); + } + } else { + // This prevents data from being displayed out-of-order, + // but may also result in missing pages of data when scrolling quickly... + if (root.currentHistoryPage === result.current_page) { + transactionHistoryModel.append(result.data.history); + calculatePendingAndInvalidated(); } - calculatePendingAndInvalidated(); } } - refreshTimer.start(); + + // Only auto-refresh if the user hasn't scrolled + // and there is more data to grab + if (root.currentHistoryPage === 1 && !root.noMoreHistoryData) { + refreshTimer.start(); + } } } @@ -134,9 +155,13 @@ Item { onVisibleChanged: { if (visible) { - historyReceived = false; + transactionHistoryModel.clear(); Commerce.balance(); - Commerce.history(); + initialHistoryReceived = false; + root.currentHistoryPage = 1; + root.noMoreHistoryData = false; + root.historyRequestPending = true; + Commerce.history(root.currentHistoryPage); } else { refreshTimer.stop(); } @@ -164,9 +189,10 @@ Item { id: refreshTimer; interval: 4000; onTriggered: { - console.log("Refreshing Wallet Home..."); + console.log("Refreshing 1st Page of Recent Activity..."); + root.historyRequestPending = true; Commerce.balance(); - Commerce.history(); + Commerce.history(1); } } @@ -241,7 +267,7 @@ Item { anchors.right: parent.right; Item { - visible: transactionHistoryModel.count === 0 && root.historyReceived; + visible: transactionHistoryModel.count === 0 && root.initialHistoryReceived; anchors.centerIn: parent; width: parent.width - 12; height: parent.height; @@ -364,7 +390,12 @@ Item { onAtYEndChanged: { if (transactionHistory.atYEnd) { console.log("User scrolled to the bottom of 'Recent Activity'."); - // Grab next page of results and append to model + if (!root.historyRequestPending && !root.noMoreHistoryData) { + // Grab next page of results and append to model + root.historyRequestPending = true; + Commerce.history(++root.currentHistoryPage); + console.log("Fetching Page " + root.currentHistoryPage + " of Recent Activity..."); + } } } } diff --git a/interface/src/commerce/Ledger.cpp b/interface/src/commerce/Ledger.cpp index d7d36dabf6..b2ac92e85a 100644 --- a/interface/src/commerce/Ledger.cpp +++ b/interface/src/commerce/Ledger.cpp @@ -72,11 +72,11 @@ void Ledger::signedSend(const QString& propertyName, const QByteArray& text, con send(endpoint, success, fail, QNetworkAccessManager::PutOperation, AccountManagerAuth::Required, request); } -void Ledger::keysQuery(const QString& endpoint, const QString& success, const QString& fail) { +void Ledger::keysQuery(const QString& endpoint, QJsonObject& requestParams, const QString& success, const QString& fail) { auto wallet = DependencyManager::get(); - QJsonObject request; - request["public_keys"] = QJsonArray::fromStringList(wallet->listPublicKeys()); - send(endpoint, success, fail, QNetworkAccessManager::PostOperation, AccountManagerAuth::Required, request); + requestParams["public_keys"] = QJsonArray::fromStringList(wallet->listPublicKeys()); + + send(endpoint, success, fail, QNetworkAccessManager::PostOperation, AccountManagerAuth::Required, requestParams); } void Ledger::buy(const QString& hfc_key, int cost, const QString& asset_id, const QString& inventory_key, const bool controlled_failure) { @@ -104,11 +104,11 @@ bool Ledger::receiveAt(const QString& hfc_key, const QString& old_key) { } void Ledger::balance(const QStringList& keys) { - keysQuery("balance", "balanceSuccess", "balanceFailure"); + keysQuery("balance", QJsonObject(), "balanceSuccess", "balanceFailure"); } void Ledger::inventory(const QStringList& keys) { - keysQuery("inventory", "inventorySuccess", "inventoryFailure"); + keysQuery("inventory", QJsonObject(), "inventorySuccess", "inventoryFailure"); } QString amountString(const QString& label, const QString&color, const QJsonValue& moneyValue, const QJsonValue& certsValue) { @@ -176,8 +176,11 @@ void Ledger::historyFailure(QNetworkReply& reply) { failResponse("history", reply); } -void Ledger::history(const QStringList& keys) { - keysQuery("history", "historySuccess", "historyFailure"); +void Ledger::history(const QStringList& keys, const QString& pageNumber) { + QJsonObject params; + params["per_page"] = 7; + params["page"] = pageNumber; + keysQuery("history", params, "historySuccess", "historyFailure"); } // The api/failResponse is called just for the side effect of logging. diff --git a/interface/src/commerce/Ledger.h b/interface/src/commerce/Ledger.h index 42eb0ffc49..db3d1f064a 100644 --- a/interface/src/commerce/Ledger.h +++ b/interface/src/commerce/Ledger.h @@ -29,7 +29,7 @@ public: bool receiveAt(const QString& hfc_key, const QString& old_key); void balance(const QStringList& keys); void inventory(const QStringList& keys); - void history(const QStringList& keys); + void history(const QStringList& keys, const QString& pageNumber); void account(); void reset(); void updateLocation(const QString& asset_id, const QString location, const bool controlledFailure = false); @@ -79,7 +79,7 @@ private: QJsonObject apiResponse(const QString& label, QNetworkReply& reply); QJsonObject failResponse(const QString& label, QNetworkReply& reply); void send(const QString& endpoint, const QString& success, const QString& fail, QNetworkAccessManager::Operation method, AccountManagerAuth::Type authType, QJsonObject request); - void keysQuery(const QString& endpoint, const QString& success, const QString& fail); + void keysQuery(const QString& endpoint, QJsonObject& extraRequestParams, const QString& success, const QString& fail); void signedSend(const QString& propertyName, const QByteArray& text, const QString& key, const QString& endpoint, const QString& success, const QString& fail, const bool controlled_failure = false); }; diff --git a/interface/src/commerce/QmlCommerce.cpp b/interface/src/commerce/QmlCommerce.cpp index 62e87f9c66..36608fe0a6 100644 --- a/interface/src/commerce/QmlCommerce.cpp +++ b/interface/src/commerce/QmlCommerce.cpp @@ -96,12 +96,12 @@ void QmlCommerce::inventory() { } } -void QmlCommerce::history() { +void QmlCommerce::history(const QString& pageNumber) { auto ledger = DependencyManager::get(); auto wallet = DependencyManager::get(); QStringList cachedPublicKeys = wallet->listPublicKeys(); if (!cachedPublicKeys.isEmpty()) { - ledger->history(cachedPublicKeys); + ledger->history(cachedPublicKeys, pageNumber); } } diff --git a/interface/src/commerce/QmlCommerce.h b/interface/src/commerce/QmlCommerce.h index c53e73d565..88223aacb0 100644 --- a/interface/src/commerce/QmlCommerce.h +++ b/interface/src/commerce/QmlCommerce.h @@ -60,7 +60,7 @@ protected: Q_INVOKABLE void buy(const QString& assetId, int cost, const bool controlledFailure = false); Q_INVOKABLE void balance(); Q_INVOKABLE void inventory(); - Q_INVOKABLE void history(); + Q_INVOKABLE void history(const QString& pageNumber); Q_INVOKABLE void generateKeyPair(); Q_INVOKABLE void reset(); Q_INVOKABLE void resetLocalWalletOnly();