Merge pull request #4799 from mempool/natsoni/blocks-table-url-pagination

Add page number to URL in blocks table
This commit is contained in:
softsimon 2024-03-22 14:24:56 +09:00 committed by GitHub
commit 00a94f9e3c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 109 additions and 28 deletions

View file

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

View file

@ -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 { 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 { 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';
@ -25,6 +26,7 @@ export class BlocksList implements OnInit {
auditAvailable = false;
isLoading = true;
fromBlockHeight = undefined;
lastBlockHeightFetched = -1;
paginationMaxSize: number;
page = 1;
lastPage = 1;
@ -33,6 +35,10 @@ export class BlocksList implements OnInit {
fromHeightSubject: BehaviorSubject<number> = new BehaviorSubject(this.fromBlockHeight);
skeletonLines: number[] = [];
lastBlockHeight = -1;
blocksCountInitialized$: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
blocksCountInitializedSubscription: Subscription;
keyNavigationSubscription: Subscription;
dir: 'rtl' | 'ltr' = 'ltr';
constructor(
private apiService: ApiService,
@ -41,8 +47,14 @@ export class BlocksList implements OnInit {
private cd: ChangeDetectorRef,
private seoService: SeoService,
private ogService: OpenGraphService,
private route: ActivatedRoute,
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 {
@ -52,6 +64,34 @@ export class BlocksList implements OnInit {
if (!this.widget) {
this.websocketService.want(['blocks']);
this.blocksCountInitializedSubscription = combineLatest([this.blocksCountInitialized$, this.route.queryParams]).pipe(
filter(([blocksCountInitialized, _]) => blocksCountInitialized),
tap(([_, params]) => {
this.page = +params['page'] || 1;
this.page === 1 ? this.fromHeightSubject.next(undefined) : this.fromHeightSubject.next((this.blocksCount - 1) - (this.page - 1) * 15);
this.cd.markForCheck();
})
).subscribe();
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 * 15 < this.blocksCount) {
this.page++;
this.cd.markForCheck();
}
}),
throttleTime(1000, undefined, { leading: true, trailing: true }),
).subscribe(() => {
this.pageChange(this.page);
});
}
this.skeletonLines = this.widget === true ? [...Array(6).keys()] : [...Array(15).keys()];
@ -70,13 +110,16 @@ 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 => {
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,7 +181,7 @@ export class BlocksList implements OnInit {
}
pageChange(page: number): void {
this.fromHeightSubject.next((this.blocksCount - 1) - (page - 1) * 15);
this.router.navigate([], { queryParams: { page: page } });
}
trackByBlock(index: number, block: BlockExtended): number {
@ -148,4 +191,9 @@ export class BlocksList implements OnInit {
isEllipsisActive(e): boolean {
return (e.offsetWidth < e.scrollWidth);
}
ngOnDestroy(): void {
this.blocksCountInitializedSubscription?.unsubscribe();
this.keyNavigationSubscription?.unsubscribe();
}
}

View file

@ -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<CurrentPegs>;
pegsCount$: Observable<number>;
pegsCount: number;
startingIndexSubject: BehaviorSubject<number> = 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,34 @@ 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.page = +params['page'] || 1;
this.startingIndexSubject.next((this.page - 1) * 15);
}),
).subscribe();
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(
takeUntil(this.destroy$),
throttleTime(40000),
@ -99,7 +139,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,18 +165,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.startingIndexSubject.next((page - 1) * 15);
this.page = page;
this.router.navigate([], { queryParams: { page: page } });
}
}