diff --git a/src/app/components/account-details/account-details.component.html b/src/app/components/account-details/account-details.component.html
index 3bb1028..ec305c8 100644
--- a/src/app/components/account-details/account-details.component.html
+++ b/src/app/components/account-details/account-details.component.html
@@ -258,12 +258,12 @@
diff --git a/src/app/components/account-details/account-details.component.ts b/src/app/components/account-details/account-details.component.ts
index 773be38..92903ca 100644
--- a/src/app/components/account-details/account-details.component.ts
+++ b/src/app/components/account-details/account-details.component.ts
@@ -42,7 +42,9 @@ export class AccountDetailsComponent implements OnInit, OnDestroy {
walletAccount = null;
- timeoutIdAllowingRefresh: any = null;
+ timeoutIdAllowingManualRefresh: any = null;
+ timeoutIdAllowingInstantAutoRefresh: any = null;
+ timeoutIdQueuedAutoRefresh: any = null;
qrModal: any = null;
mobileAccountMenuModal: any = null;
mobileTransactionMenuModal: any = null;
@@ -66,7 +68,11 @@ export class AccountDetailsComponent implements OnInit, OnDestroy {
routerSub = null;
priceSub = null;
- statsRefreshEnabled = true;
+ initialLoadDone = false;
+ manualRefreshAllowed = true;
+ instantAutoRefreshAllowed = true;
+ shouldQueueAutoRefresh = false;
+ autoRefreshReasonBlockUpdate = null;
dateStringToday = '';
dateStringYesterday = '';
@@ -148,6 +154,10 @@ export class AccountDetailsComponent implements OnInit, OnDestroy {
this.account.pendingFiat = this.util.nano.rawToMnano(this.account.pending || 0).times(this.price.price.lastPrice).toNumber();
});
+ this.wallet.wallet.pendingBlocksUpdate$.subscribe(async receivableBlockUpdate => {
+ this.onReceivableBlockUpdate(receivableBlockUpdate);
+ });
+
const UIkit = window['UIkit'];
const qrModal = UIkit.modal('#qr-code-modal');
this.qrModal = qrModal;
@@ -159,6 +169,7 @@ export class AccountDetailsComponent implements OnInit, OnDestroy {
this.mobileTransactionMenuModal = mobileTransactionMenuModal;
await this.loadAccountDetails();
+ this.initialLoadDone = true;
this.addressBook.loadAddressBook();
this.populateRepresentativeList();
@@ -262,14 +273,180 @@ export class AccountDetailsComponent implements OnInit, OnDestroy {
this.repLabel = null;
}
- async loadAccountDetails(refresh= false) {
- if (refresh && !this.statsRefreshEnabled) return;
- this.statsRefreshEnabled = false;
+ onRefreshButtonClick() {
+ if (!this.manualRefreshAllowed) return;
- if (this.timeoutIdAllowingRefresh != null) {
- clearTimeout(this.timeoutIdAllowingRefresh);
+ this.loadAccountDetails();
+ }
+
+ isReceivableBlockUpdateRelevant(receivableBlockUpdate) {
+ let isRelevant = true;
+
+ if (receivableBlockUpdate.account !== this.accountID) {
+ isRelevant = false;
+ return isRelevant;
}
- this.timeoutIdAllowingRefresh = setTimeout(() => this.statsRefreshEnabled = true, 5000);
+
+ const sourceHashToFind = receivableBlockUpdate.sourceHash;
+
+ const alreadyInReceivableBlocks =
+ this.pendingBlocks.some(
+ (knownReceivableBlock) =>
+ (knownReceivableBlock.hash === sourceHashToFind)
+ );
+
+ if (receivableBlockUpdate.hasBeenReceived === true) {
+ const destinationHashToFind = receivableBlockUpdate.destinationHash;
+
+ const alreadyInAccountHistory =
+ this.accountHistory.some(
+ (knownAccountHistoryBlock) =>
+ (knownAccountHistoryBlock.hash === destinationHashToFind)
+ );
+
+ if (
+ (alreadyInAccountHistory === true)
+ && (alreadyInReceivableBlocks === false)
+ ) {
+ isRelevant = false;
+ return isRelevant;
+ }
+ } else {
+ if (alreadyInReceivableBlocks === true) {
+ isRelevant = false;
+ return isRelevant;
+ }
+ }
+
+ return isRelevant;
+ }
+
+ onReceivableBlockUpdate(receivableBlockUpdate) {
+ if (receivableBlockUpdate === null) {
+ return;
+ }
+
+ const isRelevantUpdate =
+ this.isReceivableBlockUpdateRelevant(receivableBlockUpdate);
+
+ if (isRelevantUpdate === false) {
+ return;
+ }
+
+ this.loadAccountDetailsThrottled({ receivableBlockUpdate });
+ }
+
+ loadAccountDetailsThrottled(params) {
+ this.autoRefreshReasonBlockUpdate = (
+ (params.receivableBlockUpdate != null)
+ ? params.receivableBlockUpdate
+ : null
+ );
+
+ if (this.initialLoadDone === false) {
+ return;
+ }
+
+ if (this.instantAutoRefreshAllowed === true) {
+ this.loadAccountDetails();
+ return;
+ }
+
+ if (this.loadingAccountDetails === true) {
+ // Queue refresh once the loading is done
+ this.shouldQueueAutoRefresh = true;
+ } else {
+ // Queue refresh now
+ this.loadAccountDetailsDelayed(3000);
+ }
+ }
+
+ enableManualRefreshDelayed(delayMS) {
+ if (this.timeoutIdAllowingManualRefresh != null) {
+ clearTimeout(this.timeoutIdAllowingManualRefresh);
+ }
+
+ this.timeoutIdAllowingManualRefresh =
+ setTimeout(
+ () => {
+ this.manualRefreshAllowed = true;
+ },
+ delayMS
+ );
+ }
+
+ enableInstantAutoRefreshDelayed(delayMS) {
+ if (this.timeoutIdAllowingInstantAutoRefresh != null) {
+ clearTimeout(this.timeoutIdAllowingInstantAutoRefresh);
+ }
+
+ this.timeoutIdAllowingInstantAutoRefresh =
+ setTimeout(
+ () => {
+ this.instantAutoRefreshAllowed = true;
+ },
+ delayMS
+ );
+ }
+
+ loadAccountDetailsDelayed(delayMS) {
+ if (this.timeoutIdQueuedAutoRefresh != null) {
+ clearTimeout(this.timeoutIdQueuedAutoRefresh);
+ }
+
+ this.timeoutIdQueuedAutoRefresh =
+ setTimeout(
+ () => {
+ if (this.autoRefreshReasonBlockUpdate !== null) {
+ const isUpdateStillRelevant =
+ this.isReceivableBlockUpdateRelevant(this.autoRefreshReasonBlockUpdate);
+
+ if (isUpdateStillRelevant === false) {
+ this.enableRefreshesEventually();
+ return;
+ }
+ }
+
+ this.loadAccountDetails();
+ },
+ delayMS
+ );
+ }
+
+ onAccountDetailsLoadStart() {
+ this.instantAutoRefreshAllowed = false;
+ this.manualRefreshAllowed = false;
+
+ if (this.timeoutIdAllowingManualRefresh != null) {
+ clearTimeout(this.timeoutIdAllowingManualRefresh);
+ }
+
+ if (this.timeoutIdAllowingInstantAutoRefresh != null) {
+ clearTimeout(this.timeoutIdAllowingInstantAutoRefresh);
+ }
+
+ if (this.timeoutIdQueuedAutoRefresh != null) {
+ clearTimeout(this.timeoutIdQueuedAutoRefresh);
+ }
+ }
+
+ enableRefreshesEventually() {
+ this.enableInstantAutoRefreshDelayed(3000);
+ this.enableManualRefreshDelayed(5000);
+ }
+
+ onAccountDetailsLoadDone() {
+ if (this.shouldQueueAutoRefresh === true) {
+ this.shouldQueueAutoRefresh = false;
+ this.loadAccountDetailsDelayed(3000);
+ return;
+ }
+
+ this.enableRefreshesEventually();
+ }
+
+ async loadAccountDetails() {
+ this.onAccountDetailsLoadStart();
this.pendingBlocks = [];
@@ -288,11 +465,13 @@ export class AccountDetailsComponent implements OnInit, OnDestroy {
if (accountID !== this.accountID) {
// Navigated to a different account while account info was loading
+ this.onAccountDetailsLoadDone();
return;
}
if (!this.account) {
this.loadingAccountDetails = false;
+ this.onAccountDetailsLoadDone();
return;
}
@@ -316,6 +495,7 @@ export class AccountDetailsComponent implements OnInit, OnDestroy {
if (accountID !== this.accountID) {
// Navigated to a different account while incoming tx were loading
+ this.onAccountDetailsLoadDone();
return;
}
@@ -380,10 +560,12 @@ export class AccountDetailsComponent implements OnInit, OnDestroy {
if (accountID !== this.accountID) {
// Navigated to a different account while account history was loading
+ this.onAccountDetailsLoadDone();
return;
}
this.loadingAccountDetails = false;
+ this.onAccountDetailsLoadDone();
}
getAccountLabel(accountID, defaultLabel) {
@@ -458,9 +640,18 @@ export class AccountDetailsComponent implements OnInit, OnDestroy {
);
if (h.type === 'state') {
- // For Open and receive blocks, we need to look up block info to get originating account
if (h.subtype === 'open' || h.subtype === 'receive') {
+ // Look up block info to get sender account
additionalBlocksInfo.push({ hash: h.hash, link: h.link });
+
+ // Remove a receivable block if this is a receive for it
+ const sourceHashToFind = h.link;
+
+ this.pendingBlocks =
+ this.pendingBlocks.filter(
+ (knownReceivableBlock) =>
+ (knownReceivableBlock.hash !== sourceHashToFind)
+ );
} else if (h.subtype === 'change') {
h.link_as_account = h.representative;
h.addressBookName = (
@@ -790,6 +981,8 @@ export class AccountDetailsComponent implements OnInit, OnDestroy {
receivableBlock.loading = false;
await this.wallet.reloadBalances();
+
+ this.loadAccountDetailsThrottled({});
}
async generateSend() {
diff --git a/src/app/components/receive/receive.component.ts b/src/app/components/receive/receive.component.ts
index fb86b76..dcdc683 100644
--- a/src/app/components/receive/receive.component.ts
+++ b/src/app/components/receive/receive.component.ts
@@ -85,7 +85,11 @@ export class ReceiveComponent implements OnInit, OnDestroy {
this.selAccountInit = true;
});
- this.walletService.wallet.pendingBlocksUpdate$.subscribe(async acc => {
+ this.walletService.wallet.pendingBlocksUpdate$.subscribe(async receivableBlockUpdate => {
+ if (receivableBlockUpdate === null) {
+ return;
+ }
+
this.updatePendingBlocks();
});
diff --git a/src/app/services/wallet.service.ts b/src/app/services/wallet.service.ts
index 3626982..7909b4d 100644
--- a/src/app/services/wallet.service.ts
+++ b/src/app/services/wallet.service.ts
@@ -38,6 +38,13 @@ export interface Block {
source: string;
}
+export interface ReceivableBlockUpdate {
+ account: string;
+ sourceHash: string;
+ destinationHash: string|null;
+ hasBeenReceived: boolean;
+}
+
export interface FullWallet {
type: WalletType;
seedBytes: any;
@@ -58,7 +65,7 @@ export interface FullWallet {
locked: boolean;
password: string;
pendingBlocks: Block[];
- pendingBlocksUpdate$: BehaviorSubject;
+ pendingBlocksUpdate$: BehaviorSubject;
newWallet$: BehaviorSubject;
refresh$: BehaviorSubject;
}
@@ -106,7 +113,7 @@ export class WalletService {
locked: false,
password: '',
pendingBlocks: [],
- pendingBlocksUpdate$: new BehaviorSubject(false),
+ pendingBlocksUpdate$: new BehaviorSubject(null),
newWallet$: new BehaviorSubject(false),
refresh$: new BehaviorSubject(false),
};
@@ -953,8 +960,13 @@ export class WalletService {
if (existingHash) return false; // Already added
this.wallet.pendingBlocks.push({ account: accountID, hash: blockHash, amount: amount, source: source });
- this.wallet.pendingBlocksUpdate$.next(true);
- this.wallet.pendingBlocksUpdate$.next(false);
+ this.wallet.pendingBlocksUpdate$.next({
+ account: accountID,
+ sourceHash: blockHash,
+ destinationHash: null,
+ hasBeenReceived: false,
+ });
+ this.wallet.pendingBlocksUpdate$.next(null);
return true;
}
@@ -1008,8 +1020,13 @@ export class WalletService {
// list also updated with reloadBalances but not if called too fast
this.removePendingBlock(nextBlock.hash);
await this.reloadBalances();
- this.wallet.pendingBlocksUpdate$.next(true);
- this.wallet.pendingBlocksUpdate$.next(false);
+ this.wallet.pendingBlocksUpdate$.next({
+ account: nextBlock.account,
+ sourceHash: nextBlock.hash,
+ destinationHash: newHash,
+ hasBeenReceived: true,
+ });
+ this.wallet.pendingBlocksUpdate$.next(null);
} else {
if (this.isLedgerWallet()) {
this.processingPending = false;