From f6dbe1e17f9ea0bb4146ce5b6295fe876418cdbe Mon Sep 17 00:00:00 2001 From: natsoni Date: Tue, 19 Mar 2024 16:20:25 +0900 Subject: [PATCH 1/7] Add page number to URL in blocks table --- .../blocks-list/blocks-list.component.ts | 25 +++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) 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 f451cb3a2..1cb4136e7 100644 --- a/frontend/src/app/components/blocks-list/blocks-list.component.ts +++ b/frontend/src/app/components/blocks-list/blocks-list.component.ts @@ -1,6 +1,7 @@ import { Component, OnInit, ChangeDetectionStrategy, Input, ChangeDetectorRef } from '@angular/core'; -import { BehaviorSubject, combineLatest, Observable, timer, of } from 'rxjs'; -import { delayWhen, map, retryWhen, scan, switchMap, tap } from 'rxjs/operators'; +import { ActivatedRoute, Router } from '@angular/router'; +import { BehaviorSubject, combineLatest, Observable, timer, of, Subscription } from 'rxjs'; +import { delayWhen, filter, map, retryWhen, scan, switchMap, take, tap } from 'rxjs/operators'; import { BlockExtended } from '../../interfaces/node-api.interface'; import { ApiService } from '../../services/api.service'; import { StateService } from '../../services/state.service'; @@ -33,6 +34,8 @@ export class BlocksList implements OnInit { fromHeightSubject: BehaviorSubject = new BehaviorSubject(this.fromBlockHeight); skeletonLines: number[] = []; lastBlockHeight = -1; + blocksCountInitialized$: BehaviorSubject = new BehaviorSubject(false); + blocksCountInitializedSubscription: Subscription; constructor( private apiService: ApiService, @@ -41,6 +44,8 @@ export class BlocksList implements OnInit { private cd: ChangeDetectorRef, private seoService: SeoService, private ogService: OpenGraphService, + private route: ActivatedRoute, + private router: Router ) { this.isMempoolModule = this.stateService.env.BASE_MODULE === 'mempool'; } @@ -52,6 +57,16 @@ 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.page = +params['page'] || 1; + this.pageChange(this.page); + }) + ).subscribe(); } this.skeletonLines = this.widget === true ? [...Array(6).keys()] : [...Array(15).keys()]; @@ -77,6 +92,7 @@ export class BlocksList implements OnInit { tap(blocks => { if (this.blocksCount === undefined) { this.blocksCount = blocks[0].height + 1; + this.blocksCountInitialized$.next(true); } this.isLoading = false; this.lastBlockHeight = Math.max(...blocks.map(o => o.height)); @@ -138,6 +154,7 @@ export class BlocksList implements OnInit { } pageChange(page: number): void { + this.router.navigate([], { queryParams: { page: page } }); this.fromHeightSubject.next((this.blocksCount - 1) - (page - 1) * 15); } @@ -148,4 +165,8 @@ export class BlocksList implements OnInit { isEllipsisActive(e): boolean { return (e.offsetWidth < e.scrollWidth); } + + ngOnDestroy(): void { + this.blocksCountInitializedSubscription?.unsubscribe(); + } } From 6b9159b89c1baea3814cd85c7ef5a17a6cd428d8 Mon Sep 17 00:00:00 2001 From: natsoni Date: Wed, 20 Mar 2024 14:08:44 +0900 Subject: [PATCH 2/7] Add URL pagination to block transaction list --- frontend/src/app/components/block/block.component.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/frontend/src/app/components/block/block.component.ts b/frontend/src/app/components/block/block.component.ts index c61fd0e12..6537faabd 100644 --- a/frontend/src/app/components/block/block.component.ts +++ b/frontend/src/app/components/block/block.component.ts @@ -302,8 +302,9 @@ export class BlockComponent implements OnInit, OnDestroy { throttleTime(300, asyncScheduler, { leading: true, trailing: true }), shareReplay(1) ); - this.transactionSubscription = block$.pipe( - switchMap((block) => this.electrsApiService.getBlockTransactions$(block.id) + this.transactionSubscription = combineLatest([block$, this.route.queryParams]).pipe( + tap(([_, queryParams]) => this.page = +queryParams['page'] || 1), + switchMap(([block, _]) => this.electrsApiService.getBlockTransactions$(block.id, (this.page - 1) * this.itemsPerPage) .pipe( catchError((err) => { this.transactionsError = err; @@ -592,6 +593,7 @@ export class BlockComponent implements OnInit, OnDestroy { this.transactions = null; this.transactionsError = null; target.scrollIntoView(); // works for chrome + this.router.navigate([], { queryParams: { page: page }, queryParamsHandling: 'merge' }); this.electrsApiService.getBlockTransactions$(this.block.id, start) .pipe( From 666165ebe9869898cc1a82ffb6b098af23ce1e0b Mon Sep 17 00:00:00 2001 From: natsoni Date: Wed, 20 Mar 2024 15:10:51 +0900 Subject: [PATCH 3/7] Add key navigation support to blocks list --- .../blocks-list/blocks-list.component.ts | 27 +++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) 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 1cb4136e7..eda9874b0 100644 --- a/frontend/src/app/components/blocks-list/blocks-list.component.ts +++ b/frontend/src/app/components/blocks-list/blocks-list.component.ts @@ -1,4 +1,4 @@ -import { Component, OnInit, ChangeDetectionStrategy, Input, ChangeDetectorRef } from '@angular/core'; +import { Component, OnInit, ChangeDetectionStrategy, Input, ChangeDetectorRef, Inject, LOCALE_ID } from '@angular/core'; import { ActivatedRoute, Router } from '@angular/router'; import { BehaviorSubject, combineLatest, Observable, timer, of, Subscription } from 'rxjs'; import { delayWhen, filter, map, retryWhen, scan, switchMap, take, tap } from 'rxjs/operators'; @@ -36,6 +36,8 @@ export class BlocksList implements OnInit { lastBlockHeight = -1; blocksCountInitialized$: BehaviorSubject = new BehaviorSubject(false); blocksCountInitializedSubscription: Subscription; + keyNavigationSubscription: Subscription; + dir: 'rtl' | 'ltr' = 'ltr'; constructor( private apiService: ApiService, @@ -45,9 +47,13 @@ export class BlocksList implements OnInit { private seoService: SeoService, private ogService: OpenGraphService, private route: ActivatedRoute, - private router: Router + private router: Router, + @Inject(LOCALE_ID) private locale: string, ) { this.isMempoolModule = this.stateService.env.BASE_MODULE === 'mempool'; + if (this.locale.startsWith('ar') || this.locale.startsWith('fa') || this.locale.startsWith('he')) { + this.dir = 'rtl'; + } } ngOnInit(): void { @@ -67,6 +73,22 @@ export class BlocksList implements OnInit { this.pageChange(this.page); }) ).subscribe(); + + this.keyNavigationSubscription = this.stateService.keyNavigation$.subscribe((event) => { + const prevKey = this.dir === 'ltr' ? 'ArrowLeft' : 'ArrowRight'; + const nextKey = this.dir === 'ltr' ? 'ArrowRight' : 'ArrowLeft'; + if (event.key === prevKey && this.page > 1) { + this.page--; + this.pageChange(this.page); + this.cd.markForCheck(); + + } + if (event.key === nextKey && this.page * 15 < this.blocksCount) { + this.page++; + this.pageChange(this.page); + this.cd.markForCheck(); + } + }); } this.skeletonLines = this.widget === true ? [...Array(6).keys()] : [...Array(15).keys()]; @@ -168,5 +190,6 @@ export class BlocksList implements OnInit { ngOnDestroy(): void { this.blocksCountInitializedSubscription?.unsubscribe(); + this.keyNavigationSubscription?.unsubscribe(); } } From 524619f48ed18fc2d590aeba386e6e2d2a991281 Mon Sep 17 00:00:00 2001 From: natsoni Date: Wed, 20 Mar 2024 16:33:19 +0900 Subject: [PATCH 4/7] Add URL pagination and keyboard navigation to Liquid pegs list --- .../recent-pegs-list.component.ts | 52 ++++++++++++++++--- 1 file changed, 45 insertions(+), 7 deletions(-) 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 d8c60113f..d19bf8390 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 @@ -1,6 +1,7 @@ -import { Component, OnInit, ChangeDetectionStrategy, Input } from '@angular/core'; -import { BehaviorSubject, Observable, Subject, combineLatest, of, timer } from 'rxjs'; -import { delayWhen, filter, map, share, shareReplay, switchMap, takeUntil, tap, throttleTime } from 'rxjs/operators'; +import { ActivatedRoute, Router } from '@angular/router'; +import { Component, OnInit, ChangeDetectionStrategy, Input, Inject, LOCALE_ID, ChangeDetectorRef } from '@angular/core'; +import { BehaviorSubject, Observable, Subject, Subscription, combineLatest, of, timer } from 'rxjs'; +import { delayWhen, filter, map, share, shareReplay, switchMap, take, takeUntil, tap, throttleTime } from 'rxjs/operators'; import { ApiService } from '../../../services/api.service'; import { Env, StateService } from '../../../services/state.service'; import { AuditStatus, CurrentPegs, RecentPeg } from '../../../interfaces/node-api.interface'; @@ -29,20 +30,31 @@ export class RecentPegsListComponent implements OnInit { lastReservesBlockUpdate: number = 0; currentPeg$: Observable; pegsCount$: Observable; + pegsCount: number; startingIndexSubject: BehaviorSubject = new BehaviorSubject(0); currentIndex: number = 0; lastPegBlockUpdate: number = 0; lastPegAmount: string = ''; isLoad: boolean = true; + queryParamSubscription: Subscription; + keyNavigationSubscription: Subscription; + dir: 'rtl' | 'ltr' = 'ltr'; private destroy$ = new Subject(); - + constructor( private apiService: ApiService, + private cd: ChangeDetectorRef, public stateService: StateService, private websocketService: WebsocketService, - private seoService: SeoService + private seoService: SeoService, + private route: ActivatedRoute, + private router: Router, + @Inject(LOCALE_ID) private locale: string, ) { + if (this.locale.startsWith('ar') || this.locale.startsWith('fa') || this.locale.startsWith('he')) { + this.dir = 'rtl'; + } } ngOnInit(): void { @@ -53,6 +65,26 @@ export class RecentPegsListComponent implements OnInit { if (!this.widget) { this.seoService.setTitle($localize`:@@a8b0889ea1b41888f1e247f2731cc9322198ca04:Recent Peg-In / Out's`); this.websocketService.want(['blocks']); + + this.queryParamSubscription = this.route.queryParams.pipe( + tap((params) => this.pageChange(+params['page'] || 1)), + ).subscribe(); + + this.keyNavigationSubscription = this.stateService.keyNavigation$.subscribe((event) => { + const prevKey = this.dir === 'ltr' ? 'ArrowLeft' : 'ArrowRight'; + const nextKey = this.dir === 'ltr' ? 'ArrowRight' : 'ArrowLeft'; + if (event.key === prevKey && this.page > 1) { + this.page--; + this.pageChange(this.page); + this.cd.markForCheck(); + } + if (event.key === nextKey && this.page < this.pegsCount / this.pageSize) { + this.page++; + this.pageChange(this.page); + this.cd.markForCheck(); + } + }); + this.auditStatus$ = this.stateService.blocks$.pipe( takeUntil(this.destroy$), throttleTime(40000), @@ -99,7 +131,10 @@ export class RecentPegsListComponent implements OnInit { tap(() => this.isPegCountLoading = true), switchMap(_ => this.apiService.pegsCount$()), map((data) => data.pegs_count), - tap(() => this.isPegCountLoading = false), + tap((pegsCount) => { + this.isPegCountLoading = false; + this.pegsCount = pegsCount; + }), share() ); @@ -122,16 +157,19 @@ export class RecentPegsListComponent implements OnInit { tap(() => this.isLoading = false), share() ); - + } } ngOnDestroy(): void { this.destroy$.next(1); this.destroy$.complete(); + this.queryParamSubscription?.unsubscribe(); + this.keyNavigationSubscription?.unsubscribe(); } pageChange(page: number): void { + this.router.navigate([], { queryParams: { page: page } }); this.startingIndexSubject.next((page - 1) * 15); this.page = page; } From 5deb8c3149dd8bb9826b6652422a7ad154ae6038 Mon Sep 17 00:00:00 2001 From: natsoni Date: Wed, 20 Mar 2024 18:32:39 +0900 Subject: [PATCH 5/7] 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; + } } } From c18779b4e3edde57456075069162009696a5c64e Mon Sep 17 00:00:00 2001 From: softsimon Date: Thu, 21 Mar 2024 17:58:34 +0900 Subject: [PATCH 6/7] Refactoring debounce mechanism --- .../blocks-list/blocks-list.component.ts | 69 +++++-------------- 1 file changed, 19 insertions(+), 50 deletions(-) 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 7c6a9e96e..026b0f076 100644 --- a/frontend/src/app/components/blocks-list/blocks-list.component.ts +++ b/frontend/src/app/components/blocks-list/blocks-list.component.ts @@ -1,7 +1,7 @@ import { Component, OnInit, ChangeDetectionStrategy, Input, ChangeDetectorRef, Inject, LOCALE_ID } from '@angular/core'; import { ActivatedRoute, Router } from '@angular/router'; import { BehaviorSubject, combineLatest, Observable, timer, of, Subscription } from 'rxjs'; -import { delayWhen, filter, map, retryWhen, scan, switchMap, take, tap } from 'rxjs/operators'; +import { debounceTime, delayWhen, filter, map, retryWhen, scan, skip, switchMap, tap, throttleTime } from 'rxjs/operators'; import { BlockExtended } from '../../interfaces/node-api.interface'; import { ApiService } from '../../services/api.service'; import { StateService } from '../../services/state.service'; @@ -28,9 +28,6 @@ export class BlocksList implements OnInit { fromBlockHeight = undefined; lastKeyNavTime = 0; lastBlockHeightFetched = -1; - isArrowKeyPressed = false; - keydownListener: EventListener; - keyupListener: EventListener; paginationMaxSize: number; page = 1; lastPage = 1; @@ -59,10 +56,6 @@ 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 { @@ -81,23 +74,23 @@ export class BlocksList implements OnInit { }) ).subscribe(); - this.keyNavigationSubscription = this.stateService.keyNavigation$.subscribe((event) => { - const prevKey = this.dir === 'ltr' ? 'ArrowLeft' : 'ArrowRight'; - const nextKey = this.dir === 'ltr' ? 'ArrowRight' : 'ArrowLeft'; - if (event.key === prevKey && this.page > 1) { - 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.page >= this.blocksCount / 15 ? this.isArrowKeyPressed = false : null; - this.keyNavPageChange(this.page); - this.lastKeyNavTime = Date.now(); - this.cd.markForCheck(); - } + this.keyNavigationSubscription = this.stateService.keyNavigation$ + .pipe( + tap((event) => { + const prevKey = this.dir === 'ltr' ? 'ArrowLeft' : 'ArrowRight'; + const nextKey = this.dir === 'ltr' ? 'ArrowRight' : 'ArrowLeft'; + if (event.key === prevKey && this.page > 1) { + this.page--; + this.cd.markForCheck(); + } + if (event.key === nextKey && this.page * 15 < this.blocksCount) { + this.page++; + this.cd.markForCheck(); + } + }), + throttleTime(1000, undefined, { leading: true, trailing: true }), + ).subscribe(() => { + this.pageChange(this.page); }); } @@ -192,29 +185,7 @@ export class BlocksList implements OnInit { } 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; - } + this.pageChange(page); } trackByBlock(index: number, block: BlockExtended): number { @@ -228,7 +199,5 @@ export class BlocksList implements OnInit { ngOnDestroy(): void { this.blocksCountInitializedSubscription?.unsubscribe(); this.keyNavigationSubscription?.unsubscribe(); - window.removeEventListener('keydown', this.keydownListener); - window.removeEventListener('keyup', this.keyupListener); } } From adda5118698201d1c39269449254d0c0aab3e2bf Mon Sep 17 00:00:00 2001 From: natsoni Date: Fri, 22 Mar 2024 13:48:01 +0900 Subject: [PATCH 7/7] Refactor key navigation on recent pegs table --- .../blocks-list/blocks-list.component.ts | 6 +- .../recent-pegs-list.component.ts | 72 +++++-------------- 2 files changed, 19 insertions(+), 59 deletions(-) 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 026b0f076..94afb6509 100644 --- a/frontend/src/app/components/blocks-list/blocks-list.component.ts +++ b/frontend/src/app/components/blocks-list/blocks-list.component.ts @@ -26,7 +26,6 @@ export class BlocksList implements OnInit { auditAvailable = false; isLoading = true; fromBlockHeight = undefined; - lastKeyNavTime = 0; lastBlockHeightFetched = -1; paginationMaxSize: number; page = 1; @@ -77,6 +76,7 @@ export class BlocksList implements OnInit { this.keyNavigationSubscription = this.stateService.keyNavigation$ .pipe( tap((event) => { + this.isLoading = true; const prevKey = this.dir === 'ltr' ? 'ArrowLeft' : 'ArrowRight'; const nextKey = this.dir === 'ltr' ? 'ArrowRight' : 'ArrowLeft'; if (event.key === prevKey && this.page > 1) { @@ -184,10 +184,6 @@ export class BlocksList implements OnInit { this.router.navigate([], { queryParams: { page: page } }); } - keyNavPageChange(page: number): void { - this.pageChange(page); - } - trackByBlock(index: number, block: BlockExtended): number { return block.height; } 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 a975e193f..b818dff78 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,10 +39,6 @@ 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(); @@ -59,10 +55,6 @@ 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 { @@ -81,23 +73,24 @@ export class RecentPegsListComponent implements OnInit { }), ).subscribe(); - this.keyNavigationSubscription = this.stateService.keyNavigation$.subscribe((event) => { - const prevKey = this.dir === 'ltr' ? 'ArrowLeft' : 'ArrowRight'; - const nextKey = this.dir === 'ltr' ? 'ArrowRight' : 'ArrowLeft'; - if (event.key === prevKey && this.page > 1) { - 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.page >= this.pegsCount / this.pageSize ? this.isArrowKeyPressed = false : null; - this.keyNavPageChange(this.page); - this.lastKeyNavTime = Date.now(); - this.cd.markForCheck(); - } + this.keyNavigationSubscription = this.stateService.keyNavigation$ + .pipe( + tap((event) => { + this.isLoading = true; + const prevKey = this.dir === 'ltr' ? 'ArrowLeft' : 'ArrowRight'; + const nextKey = this.dir === 'ltr' ? 'ArrowRight' : 'ArrowLeft'; + if (event.key === prevKey && this.page > 1) { + this.page--; + this.cd.markForCheck(); + } + if (event.key === nextKey && this.page < this.pegsCount / this.pageSize) { + this.page++; + this.cd.markForCheck(); + } + }), + throttleTime(1000, undefined, { leading: true, trailing: true }), + ).subscribe(() => { + this.pageChange(this.page); }); this.auditStatus$ = this.stateService.blocks$.pipe( @@ -181,39 +174,10 @@ 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 } }); } - 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; - } - } - }