automatically refresh tx list upon receive or new receivable tx
fix transactions being able to appear simultaneously as "receivable" and "received" due to async nature of requests
This commit is contained in:
parent
e35353c608
commit
1e1e910d38
4 changed files with 233 additions and 19 deletions
|
|
@ -258,12 +258,12 @@
|
|||
<ng-template #notUpdatingTxList>
|
||||
<a
|
||||
class="icon-transactions-refresh"
|
||||
[class.disabled]="!statsRefreshEnabled"
|
||||
[class.uk-text-muted]="!statsRefreshEnabled"
|
||||
[class.disabled]="!manualRefreshAllowed"
|
||||
[class.uk-text-muted]="!manualRefreshAllowed"
|
||||
uk-icon="icon: refresh;"
|
||||
[title]="'general.reload' | transloco"
|
||||
uk-tooltip
|
||||
(click)="loadAccountDetails(true)"
|
||||
(click)="onRefreshButtonClick()"
|
||||
></a>
|
||||
</ng-template>
|
||||
</li>
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
if(this.autoRefreshReasonBlockUpdate !== null) {
|
||||
const isUpdateStillRelevant =
|
||||
this.isReceivableBlockUpdateRelevant(this.autoRefreshReasonBlockUpdate);
|
||||
|
||||
if (isUpdateStillRelevant === false) {
|
||||
this.enableRefreshesEventually();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
this.timeoutIdQueuedAutoRefresh =
|
||||
setTimeout(
|
||||
() => {
|
||||
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() {
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -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<boolean|false>;
|
||||
pendingBlocksUpdate$: BehaviorSubject<ReceivableBlockUpdate|null>;
|
||||
newWallet$: BehaviorSubject<boolean|false>;
|
||||
refresh$: BehaviorSubject<boolean|false>;
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue