Allow smooth key navigation in block table and pegs table

This commit is contained in:
natsoni 2024-03-20 18:32:39 +09:00
parent 524619f48e
commit 5deb8c3149
No known key found for this signature in database
GPG Key ID: C65917583181743B
3 changed files with 97 additions and 29 deletions

View File

@ -594,19 +594,6 @@ export class BlockComponent implements OnInit, OnDestroy {
this.transactionsError = null; this.transactionsError = null;
target.scrollIntoView(); // works for chrome target.scrollIntoView(); // works for chrome
this.router.navigate([], { queryParams: { page: page }, queryParamsHandling: 'merge' }); 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() { toggleShowDetails() {

View File

@ -26,6 +26,11 @@ export class BlocksList implements OnInit {
auditAvailable = false; auditAvailable = false;
isLoading = true; isLoading = true;
fromBlockHeight = undefined; fromBlockHeight = undefined;
lastKeyNavTime = 0;
lastBlockHeightFetched = -1;
isArrowKeyPressed = false;
keydownListener: EventListener;
keyupListener: EventListener;
paginationMaxSize: number; paginationMaxSize: number;
page = 1; page = 1;
lastPage = 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')) { if (this.locale.startsWith('ar') || this.locale.startsWith('fa') || this.locale.startsWith('he')) {
this.dir = 'rtl'; 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 { ngOnInit(): void {
@ -63,14 +72,12 @@ export class BlocksList implements OnInit {
if (!this.widget) { if (!this.widget) {
this.websocketService.want(['blocks']); this.websocketService.want(['blocks']);
this.blocksCountInitializedSubscription = this.blocksCountInitialized$.pipe( this.blocksCountInitializedSubscription = combineLatest([this.blocksCountInitialized$, this.route.queryParams]).pipe(
filter(blocksCountInitialized => blocksCountInitialized), filter(([blocksCountInitialized, _]) => blocksCountInitialized),
take(1), tap(([_, params]) => {
switchMap(() => this.route.queryParams),
take(1),
tap(params => {
this.page = +params['page'] || 1; 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(); ).subscribe();
@ -79,13 +86,16 @@ export class BlocksList implements OnInit {
const nextKey = this.dir === 'ltr' ? 'ArrowRight' : 'ArrowLeft'; const nextKey = this.dir === 'ltr' ? 'ArrowRight' : 'ArrowLeft';
if (event.key === prevKey && this.page > 1) { if (event.key === prevKey && this.page > 1) {
this.page--; this.page--;
this.pageChange(this.page); this.page === 1 ? this.isArrowKeyPressed = false : null;
this.keyNavPageChange(this.page);
this.lastKeyNavTime = Date.now();
this.cd.markForCheck(); this.cd.markForCheck();
} }
if (event.key === nextKey && this.page * 15 < this.blocksCount) { if (event.key === nextKey && this.page * 15 < this.blocksCount) {
this.page++; 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(); this.cd.markForCheck();
} }
}); });
@ -107,8 +117,10 @@ export class BlocksList implements OnInit {
this.blocks$ = combineLatest([ this.blocks$ = combineLatest([
this.fromHeightSubject.pipe( this.fromHeightSubject.pipe(
filter(fromBlockHeight => fromBlockHeight !== this.lastBlockHeightFetched),
switchMap((fromBlockHeight) => { switchMap((fromBlockHeight) => {
this.isLoading = true; this.isLoading = true;
this.lastBlockHeightFetched = fromBlockHeight;
return this.apiService.getBlocks$(this.page === 1 ? undefined : fromBlockHeight) return this.apiService.getBlocks$(this.page === 1 ? undefined : fromBlockHeight)
.pipe( .pipe(
tap(blocks => { tap(blocks => {
@ -177,7 +189,32 @@ export class BlocksList implements OnInit {
pageChange(page: number): void { pageChange(page: number): void {
this.router.navigate([], { queryParams: { page: page } }); 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 { trackByBlock(index: number, block: BlockExtended): number {
@ -191,5 +228,7 @@ export class BlocksList implements OnInit {
ngOnDestroy(): void { ngOnDestroy(): void {
this.blocksCountInitializedSubscription?.unsubscribe(); this.blocksCountInitializedSubscription?.unsubscribe();
this.keyNavigationSubscription?.unsubscribe(); this.keyNavigationSubscription?.unsubscribe();
window.removeEventListener('keydown', this.keydownListener);
window.removeEventListener('keyup', this.keyupListener);
} }
} }

View File

@ -39,6 +39,10 @@ export class RecentPegsListComponent implements OnInit {
queryParamSubscription: Subscription; queryParamSubscription: Subscription;
keyNavigationSubscription: Subscription; keyNavigationSubscription: Subscription;
dir: 'rtl' | 'ltr' = 'ltr'; dir: 'rtl' | 'ltr' = 'ltr';
lastKeyNavTime = 0;
isArrowKeyPressed = false;
keydownListener: EventListener;
keyupListener: EventListener;
private destroy$ = new Subject(); 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')) { if (this.locale.startsWith('ar') || this.locale.startsWith('fa') || this.locale.startsWith('he')) {
this.dir = 'rtl'; 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 { ngOnInit(): void {
@ -67,7 +75,10 @@ export class RecentPegsListComponent implements OnInit {
this.websocketService.want(['blocks']); this.websocketService.want(['blocks']);
this.queryParamSubscription = this.route.queryParams.pipe( 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(); ).subscribe();
this.keyNavigationSubscription = this.stateService.keyNavigation$.subscribe((event) => { this.keyNavigationSubscription = this.stateService.keyNavigation$.subscribe((event) => {
@ -75,12 +86,16 @@ export class RecentPegsListComponent implements OnInit {
const nextKey = this.dir === 'ltr' ? 'ArrowRight' : 'ArrowLeft'; const nextKey = this.dir === 'ltr' ? 'ArrowRight' : 'ArrowLeft';
if (event.key === prevKey && this.page > 1) { if (event.key === prevKey && this.page > 1) {
this.page--; this.page--;
this.pageChange(this.page); this.page === 1 ? this.isArrowKeyPressed = false : null;
this.keyNavPageChange(this.page);
this.lastKeyNavTime = Date.now();
this.cd.markForCheck(); this.cd.markForCheck();
} }
if (event.key === nextKey && this.page < this.pegsCount / this.pageSize) { if (event.key === nextKey && this.page < this.pegsCount / this.pageSize) {
this.page++; 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(); this.cd.markForCheck();
} }
}); });
@ -166,12 +181,39 @@ export class RecentPegsListComponent implements OnInit {
this.destroy$.complete(); this.destroy$.complete();
this.queryParamSubscription?.unsubscribe(); this.queryParamSubscription?.unsubscribe();
this.keyNavigationSubscription?.unsubscribe(); this.keyNavigationSubscription?.unsubscribe();
window.removeEventListener('keydown', this.keydownListener);
window.removeEventListener('keyup', this.keyupListener);
} }
pageChange(page: number): void { pageChange(page: number): void {
this.router.navigate([], { queryParams: { page: page } }); 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;
}
} }
} }