From 5deb8c3149dd8bb9826b6652422a7ad154ae6038 Mon Sep 17 00:00:00 2001 From: natsoni Date: Wed, 20 Mar 2024 18:32:39 +0900 Subject: [PATCH] Allow smooth key navigation in block table and pegs table --- .../app/components/block/block.component.ts | 13 ---- .../blocks-list/blocks-list.component.ts | 61 +++++++++++++++---- .../recent-pegs-list.component.ts | 52 ++++++++++++++-- 3 files changed, 97 insertions(+), 29 deletions(-) diff --git a/frontend/src/app/components/block/block.component.ts b/frontend/src/app/components/block/block.component.ts index 6537faabd..bb36928b0 100644 --- a/frontend/src/app/components/block/block.component.ts +++ b/frontend/src/app/components/block/block.component.ts @@ -594,19 +594,6 @@ export class BlockComponent implements OnInit, OnDestroy { this.transactionsError = null; target.scrollIntoView(); // works for chrome this.router.navigate([], { queryParams: { page: page }, queryParamsHandling: 'merge' }); - - this.electrsApiService.getBlockTransactions$(this.block.id, start) - .pipe( - catchError((err) => { - this.transactionsError = err; - return of([]); - }) - ) - .subscribe((transactions) => { - this.transactions = transactions; - this.isLoadingTransactions = false; - target.scrollIntoView(); // works for firefox - }); } toggleShowDetails() { diff --git a/frontend/src/app/components/blocks-list/blocks-list.component.ts b/frontend/src/app/components/blocks-list/blocks-list.component.ts index eda9874b0..7c6a9e96e 100644 --- a/frontend/src/app/components/blocks-list/blocks-list.component.ts +++ b/frontend/src/app/components/blocks-list/blocks-list.component.ts @@ -26,6 +26,11 @@ export class BlocksList implements OnInit { auditAvailable = false; isLoading = true; fromBlockHeight = undefined; + lastKeyNavTime = 0; + lastBlockHeightFetched = -1; + isArrowKeyPressed = false; + keydownListener: EventListener; + keyupListener: EventListener; paginationMaxSize: number; page = 1; lastPage = 1; @@ -54,6 +59,10 @@ export class BlocksList implements OnInit { if (this.locale.startsWith('ar') || this.locale.startsWith('fa') || this.locale.startsWith('he')) { this.dir = 'rtl'; } + this.keydownListener = this.onKeyDown.bind(this); + this.keyupListener = this.onKeyUp.bind(this); + window.addEventListener('keydown', this.keydownListener); + window.addEventListener('keyup', this.keyupListener); } ngOnInit(): void { @@ -63,14 +72,12 @@ export class BlocksList implements OnInit { if (!this.widget) { this.websocketService.want(['blocks']); - this.blocksCountInitializedSubscription = this.blocksCountInitialized$.pipe( - filter(blocksCountInitialized => blocksCountInitialized), - take(1), - switchMap(() => this.route.queryParams), - take(1), - tap(params => { + this.blocksCountInitializedSubscription = combineLatest([this.blocksCountInitialized$, this.route.queryParams]).pipe( + filter(([blocksCountInitialized, _]) => blocksCountInitialized), + tap(([_, params]) => { this.page = +params['page'] || 1; - this.pageChange(this.page); + this.page === 1 ? this.fromHeightSubject.next(undefined) : this.fromHeightSubject.next((this.blocksCount - 1) - (this.page - 1) * 15); + this.cd.markForCheck(); }) ).subscribe(); @@ -79,13 +86,16 @@ export class BlocksList implements OnInit { const nextKey = this.dir === 'ltr' ? 'ArrowRight' : 'ArrowLeft'; if (event.key === prevKey && this.page > 1) { this.page--; - this.pageChange(this.page); + this.page === 1 ? this.isArrowKeyPressed = false : null; + this.keyNavPageChange(this.page); + this.lastKeyNavTime = Date.now(); this.cd.markForCheck(); - } if (event.key === nextKey && this.page * 15 < this.blocksCount) { this.page++; - this.pageChange(this.page); + this.page >= this.blocksCount / 15 ? this.isArrowKeyPressed = false : null; + this.keyNavPageChange(this.page); + this.lastKeyNavTime = Date.now(); this.cd.markForCheck(); } }); @@ -107,8 +117,10 @@ export class BlocksList implements OnInit { this.blocks$ = combineLatest([ this.fromHeightSubject.pipe( + filter(fromBlockHeight => fromBlockHeight !== this.lastBlockHeightFetched), switchMap((fromBlockHeight) => { this.isLoading = true; + this.lastBlockHeightFetched = fromBlockHeight; return this.apiService.getBlocks$(this.page === 1 ? undefined : fromBlockHeight) .pipe( tap(blocks => { @@ -177,7 +189,32 @@ export class BlocksList implements OnInit { pageChange(page: number): void { this.router.navigate([], { queryParams: { page: page } }); - this.fromHeightSubject.next((this.blocksCount - 1) - (page - 1) * 15); + } + + keyNavPageChange(page: number): void { + this.isLoading = true; + if (this.isArrowKeyPressed) { + timer(400).pipe( + take(1), + filter(() => Date.now() - this.lastKeyNavTime >= 400 && this.isArrowKeyPressed === false), + ).subscribe(() => { + this.pageChange(page); + }); + } else { + this.pageChange(page); + } + } + + onKeyDown(event: KeyboardEvent) { + if (event.key === 'ArrowLeft' || event.key === 'ArrowRight') { + this.isArrowKeyPressed = true; + } + } + + onKeyUp(event: KeyboardEvent) { + if (event.key === 'ArrowLeft' || event.key === 'ArrowRight') { + this.isArrowKeyPressed = false; + } } trackByBlock(index: number, block: BlockExtended): number { @@ -191,5 +228,7 @@ export class BlocksList implements OnInit { ngOnDestroy(): void { this.blocksCountInitializedSubscription?.unsubscribe(); this.keyNavigationSubscription?.unsubscribe(); + window.removeEventListener('keydown', this.keydownListener); + window.removeEventListener('keyup', this.keyupListener); } } diff --git a/frontend/src/app/components/liquid-reserves-audit/recent-pegs-list/recent-pegs-list.component.ts b/frontend/src/app/components/liquid-reserves-audit/recent-pegs-list/recent-pegs-list.component.ts index d19bf8390..a975e193f 100644 --- a/frontend/src/app/components/liquid-reserves-audit/recent-pegs-list/recent-pegs-list.component.ts +++ b/frontend/src/app/components/liquid-reserves-audit/recent-pegs-list/recent-pegs-list.component.ts @@ -39,6 +39,10 @@ export class RecentPegsListComponent implements OnInit { queryParamSubscription: Subscription; keyNavigationSubscription: Subscription; dir: 'rtl' | 'ltr' = 'ltr'; + lastKeyNavTime = 0; + isArrowKeyPressed = false; + keydownListener: EventListener; + keyupListener: EventListener; private destroy$ = new Subject(); @@ -55,6 +59,10 @@ export class RecentPegsListComponent implements OnInit { if (this.locale.startsWith('ar') || this.locale.startsWith('fa') || this.locale.startsWith('he')) { this.dir = 'rtl'; } + this.keydownListener = this.onKeyDown.bind(this); + this.keyupListener = this.onKeyUp.bind(this); + window.addEventListener('keydown', this.keydownListener); + window.addEventListener('keyup', this.keyupListener); } ngOnInit(): void { @@ -67,7 +75,10 @@ export class RecentPegsListComponent implements OnInit { this.websocketService.want(['blocks']); this.queryParamSubscription = this.route.queryParams.pipe( - tap((params) => this.pageChange(+params['page'] || 1)), + tap((params) => { + this.page = +params['page'] || 1; + this.startingIndexSubject.next((this.page - 1) * 15); + }), ).subscribe(); this.keyNavigationSubscription = this.stateService.keyNavigation$.subscribe((event) => { @@ -75,12 +86,16 @@ export class RecentPegsListComponent implements OnInit { const nextKey = this.dir === 'ltr' ? 'ArrowRight' : 'ArrowLeft'; if (event.key === prevKey && this.page > 1) { this.page--; - this.pageChange(this.page); + this.page === 1 ? this.isArrowKeyPressed = false : null; + this.keyNavPageChange(this.page); + this.lastKeyNavTime = Date.now(); this.cd.markForCheck(); } if (event.key === nextKey && this.page < this.pegsCount / this.pageSize) { this.page++; - this.pageChange(this.page); + this.page >= this.pegsCount / this.pageSize ? this.isArrowKeyPressed = false : null; + this.keyNavPageChange(this.page); + this.lastKeyNavTime = Date.now(); this.cd.markForCheck(); } }); @@ -166,12 +181,39 @@ export class RecentPegsListComponent implements OnInit { this.destroy$.complete(); this.queryParamSubscription?.unsubscribe(); this.keyNavigationSubscription?.unsubscribe(); + window.removeEventListener('keydown', this.keydownListener); + window.removeEventListener('keyup', this.keyupListener); + } pageChange(page: number): void { this.router.navigate([], { queryParams: { page: page } }); - this.startingIndexSubject.next((page - 1) * 15); - this.page = page; + } + + keyNavPageChange(page: number): void { + this.isLoading = true; + if (this.isArrowKeyPressed) { + timer(400).pipe( + take(1), + filter(() => Date.now() - this.lastKeyNavTime >= 400 && this.isArrowKeyPressed === false), + ).subscribe(() => { + this.pageChange(page); + }); + } else { + this.pageChange(page); + } + } + + onKeyDown(event: KeyboardEvent) { + if (event.key === 'ArrowLeft' || event.key === 'ArrowRight') { + this.isArrowKeyPressed = true; + } + } + + onKeyUp(event: KeyboardEvent) { + if (event.key === 'ArrowLeft' || event.key === 'ArrowRight') { + this.isArrowKeyPressed = false; + } } }