Merge pull request #4700 from mempool/natsoni/lightning-pagination
Add pagination to Lightning nodes list and fix table overflow
This commit is contained in:
commit
dab72f669d
@ -64,8 +64,8 @@
|
|||||||
<th class="channels text-right" i18n="lightning.channels">Channels</th>
|
<th class="channels text-right" i18n="lightning.channels">Channels</th>
|
||||||
<th class="city text-right" i18n="lightning.location">Location</th>
|
<th class="city text-right" i18n="lightning.location">Location</th>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody *ngIf="nodes$ | async as countryNodes; else skeleton">
|
<tbody *ngIf="nodesPagination$ | async as countryNodes; else skeleton">
|
||||||
<tr *ngFor="let node of countryNodes.nodes; let i= index; trackBy: trackByPublicKey">
|
<tr *ngFor="let node of countryNodes; let i= index; trackBy: trackByPublicKey">
|
||||||
<td class="alias text-left text-truncate">
|
<td class="alias text-left text-truncate">
|
||||||
<a [routerLink]="['/lightning/node/' | relativeUrl, node.public_key]">{{ node.alias }}</a>
|
<a [routerLink]="['/lightning/node/' | relativeUrl, node.public_key]">{{ node.alias }}</a>
|
||||||
</td>
|
</td>
|
||||||
@ -116,5 +116,10 @@
|
|||||||
</ng-template>
|
</ng-template>
|
||||||
|
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
|
<ngb-pagination *ngIf="nodes$ | async as countryNodes" class="pagination-container float-right mt-2" [class]="isLoading ? 'disabled' : ''"
|
||||||
|
[collectionSize]="countryNodes.nodes.length" [rotate]="true" [maxSize]="maxSize" [pageSize]="pageSize" [(page)]="page"
|
||||||
|
(pageChange)="pageChange(page)" [boundaryLinks]="true" [ellipses]="false">
|
||||||
|
</ngb-pagination>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -22,14 +22,14 @@
|
|||||||
|
|
||||||
.timestamp-first {
|
.timestamp-first {
|
||||||
width: 20%;
|
width: 20%;
|
||||||
@media (max-width: 576px) {
|
@media (max-width: 1060px) {
|
||||||
display: none
|
display: none
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.timestamp-update {
|
.timestamp-update {
|
||||||
width: 16%;
|
width: 16%;
|
||||||
@media (max-width: 576px) {
|
@media (max-width: 1060px) {
|
||||||
display: none
|
display: none
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -50,7 +50,7 @@
|
|||||||
|
|
||||||
.city {
|
.city {
|
||||||
max-width: 150px;
|
max-width: 150px;
|
||||||
@media (max-width: 576px) {
|
@media (max-width: 675px) {
|
||||||
display: none
|
display: none
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,6 +1,6 @@
|
|||||||
import { ChangeDetectionStrategy, Component, Input, OnInit } from '@angular/core';
|
import { ChangeDetectionStrategy, Component, Input, OnInit } from '@angular/core';
|
||||||
import { ActivatedRoute } from '@angular/router';
|
import { ActivatedRoute } from '@angular/router';
|
||||||
import { map, Observable, share } from 'rxjs';
|
import { BehaviorSubject, combineLatest, map, Observable, share, tap } from 'rxjs';
|
||||||
import { ApiService } from '../../services/api.service';
|
import { ApiService } from '../../services/api.service';
|
||||||
import { SeoService } from '../../services/seo.service';
|
import { SeoService } from '../../services/seo.service';
|
||||||
import { getFlagEmoji } from '../../shared/common.utils';
|
import { getFlagEmoji } from '../../shared/common.utils';
|
||||||
@ -15,6 +15,12 @@ import { GeolocationData } from '../../shared/components/geolocation/geolocation
|
|||||||
export class NodesPerCountry implements OnInit {
|
export class NodesPerCountry implements OnInit {
|
||||||
nodes$: Observable<any>;
|
nodes$: Observable<any>;
|
||||||
country: {name: string, flag: string};
|
country: {name: string, flag: string};
|
||||||
|
nodesPagination$: Observable<any>;
|
||||||
|
startingIndexSubject: BehaviorSubject<number> = new BehaviorSubject(0);
|
||||||
|
page = 1;
|
||||||
|
pageSize = 15;
|
||||||
|
maxSize = window.innerWidth <= 767.98 ? 3 : 5;
|
||||||
|
isLoading = true;
|
||||||
|
|
||||||
skeletonLines: number[] = [];
|
skeletonLines: number[] = [];
|
||||||
|
|
||||||
@ -23,7 +29,7 @@ export class NodesPerCountry implements OnInit {
|
|||||||
private seoService: SeoService,
|
private seoService: SeoService,
|
||||||
private route: ActivatedRoute,
|
private route: ActivatedRoute,
|
||||||
) {
|
) {
|
||||||
for (let i = 0; i < 20; ++i) {
|
for (let i = 0; i < this.pageSize; ++i) {
|
||||||
this.skeletonLines.push(i);
|
this.skeletonLines.push(i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -31,6 +37,7 @@ export class NodesPerCountry implements OnInit {
|
|||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
this.nodes$ = this.apiService.getNodeForCountry$(this.route.snapshot.params.country)
|
this.nodes$ = this.apiService.getNodeForCountry$(this.route.snapshot.params.country)
|
||||||
.pipe(
|
.pipe(
|
||||||
|
tap(() => this.isLoading = true),
|
||||||
map(response => {
|
map(response => {
|
||||||
this.seoService.setTitle($localize`Lightning nodes in ${response.country.en}`);
|
this.seoService.setTitle($localize`Lightning nodes in ${response.country.en}`);
|
||||||
this.seoService.setDescription($localize`:@@meta.description.lightning.nodes-country:Explore all the Lightning nodes hosted in ${response.country.en} and see an overview of each node's capacity, number of open channels, and more.`);
|
this.seoService.setDescription($localize`:@@meta.description.lightning.nodes-country:Explore all the Lightning nodes hosted in ${response.country.en} and see an overview of each node's capacity, number of open channels, and more.`);
|
||||||
@ -87,11 +94,21 @@ export class NodesPerCountry implements OnInit {
|
|||||||
ispCount: Object.keys(isps).length
|
ispCount: Object.keys(isps).length
|
||||||
};
|
};
|
||||||
}),
|
}),
|
||||||
|
tap(() => this.isLoading = false),
|
||||||
share()
|
share()
|
||||||
);
|
);
|
||||||
|
|
||||||
|
this.nodesPagination$ = combineLatest([this.nodes$, this.startingIndexSubject]).pipe(
|
||||||
|
map(([response, startingIndex]) => response.nodes.slice(startingIndex, startingIndex + this.pageSize))
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
trackByPublicKey(index: number, node: any): string {
|
trackByPublicKey(index: number, node: any): string {
|
||||||
return node.public_key;
|
return node.public_key;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pageChange(page: number): void {
|
||||||
|
this.startingIndexSubject.next((page - 1) * this.pageSize);
|
||||||
|
this.page = page;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -61,8 +61,8 @@
|
|||||||
<th class="channels text-right" i18n="lightning.channels">Channels</th>
|
<th class="channels text-right" i18n="lightning.channels">Channels</th>
|
||||||
<th class="city text-right" i18n="lightning.location">Location</th>
|
<th class="city text-right" i18n="lightning.location">Location</th>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody *ngIf="nodes$ | async as ispNodes; else skeleton">
|
<tbody *ngIf="nodesPagination$ | async as ispNodes; else skeleton">
|
||||||
<tr *ngFor="let node of ispNodes.nodes; let i= index; trackBy: trackByPublicKey">
|
<tr *ngFor="let node of ispNodes; let i= index; trackBy: trackByPublicKey">
|
||||||
<td class="alias text-left text-truncate">
|
<td class="alias text-left text-truncate">
|
||||||
<a [routerLink]="['/lightning/node/' | relativeUrl, node.public_key]">{{ node.alias }}</a>
|
<a [routerLink]="['/lightning/node/' | relativeUrl, node.public_key]">{{ node.alias }}</a>
|
||||||
</td>
|
</td>
|
||||||
@ -113,5 +113,10 @@
|
|||||||
</ng-template>
|
</ng-template>
|
||||||
|
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
|
<ngb-pagination *ngIf="nodes$ | async as ispNodes" class="pagination-container float-right mt-2" [class]="isLoading ? 'disabled' : ''"
|
||||||
|
[collectionSize]="ispNodes.nodes.length" [rotate]="true" [maxSize]="maxSize" [pageSize]="pageSize" [(page)]="page"
|
||||||
|
(pageChange)="pageChange(page)" [boundaryLinks]="true" [ellipses]="false">
|
||||||
|
</ngb-pagination>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -24,7 +24,7 @@
|
|||||||
.timestamp-first {
|
.timestamp-first {
|
||||||
width: 20%;
|
width: 20%;
|
||||||
|
|
||||||
@media (max-width: 576px) {
|
@media (max-width: 1060px) {
|
||||||
display: none
|
display: none
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -32,7 +32,7 @@
|
|||||||
.timestamp-update {
|
.timestamp-update {
|
||||||
width: 16%;
|
width: 16%;
|
||||||
|
|
||||||
@media (max-width: 576px) {
|
@media (max-width: 1060px) {
|
||||||
display: none
|
display: none
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -56,7 +56,7 @@
|
|||||||
.city {
|
.city {
|
||||||
max-width: 150px;
|
max-width: 150px;
|
||||||
|
|
||||||
@media (max-width: 576px) {
|
@media (max-width: 675px) {
|
||||||
display: none
|
display: none
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core';
|
import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core';
|
||||||
import { ActivatedRoute } from '@angular/router';
|
import { ActivatedRoute } from '@angular/router';
|
||||||
import { map, Observable, share } from 'rxjs';
|
import { BehaviorSubject, combineLatest, map, Observable, share, tap } from 'rxjs';
|
||||||
import { ApiService } from '../../services/api.service';
|
import { ApiService } from '../../services/api.service';
|
||||||
import { SeoService } from '../../services/seo.service';
|
import { SeoService } from '../../services/seo.service';
|
||||||
import { getFlagEmoji } from '../../shared/common.utils';
|
import { getFlagEmoji } from '../../shared/common.utils';
|
||||||
@ -15,6 +15,12 @@ import { GeolocationData } from '../../shared/components/geolocation/geolocation
|
|||||||
export class NodesPerISP implements OnInit {
|
export class NodesPerISP implements OnInit {
|
||||||
nodes$: Observable<any>;
|
nodes$: Observable<any>;
|
||||||
isp: {name: string, id: number};
|
isp: {name: string, id: number};
|
||||||
|
nodesPagination$: Observable<any>;
|
||||||
|
startingIndexSubject: BehaviorSubject<number> = new BehaviorSubject(0);
|
||||||
|
page = 1;
|
||||||
|
pageSize = 15;
|
||||||
|
maxSize = window.innerWidth <= 767.98 ? 3 : 5;
|
||||||
|
isLoading = true;
|
||||||
|
|
||||||
skeletonLines: number[] = [];
|
skeletonLines: number[] = [];
|
||||||
|
|
||||||
@ -23,7 +29,7 @@ export class NodesPerISP implements OnInit {
|
|||||||
private seoService: SeoService,
|
private seoService: SeoService,
|
||||||
private route: ActivatedRoute,
|
private route: ActivatedRoute,
|
||||||
) {
|
) {
|
||||||
for (let i = 0; i < 20; ++i) {
|
for (let i = 0; i < this.pageSize; ++i) {
|
||||||
this.skeletonLines.push(i);
|
this.skeletonLines.push(i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -31,6 +37,7 @@ export class NodesPerISP implements OnInit {
|
|||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
this.nodes$ = this.apiService.getNodeForISP$(this.route.snapshot.params.isp)
|
this.nodes$ = this.apiService.getNodeForISP$(this.route.snapshot.params.isp)
|
||||||
.pipe(
|
.pipe(
|
||||||
|
tap(() => this.isLoading = true),
|
||||||
map(response => {
|
map(response => {
|
||||||
this.isp = {
|
this.isp = {
|
||||||
name: response.isp,
|
name: response.isp,
|
||||||
@ -77,11 +84,21 @@ export class NodesPerISP implements OnInit {
|
|||||||
topCountry: topCountry,
|
topCountry: topCountry,
|
||||||
};
|
};
|
||||||
}),
|
}),
|
||||||
|
tap(() => this.isLoading = false),
|
||||||
share()
|
share()
|
||||||
);
|
);
|
||||||
|
|
||||||
|
this.nodesPagination$ = combineLatest([this.nodes$, this.startingIndexSubject]).pipe(
|
||||||
|
map(([response, startingIndex]) => response.nodes.slice(startingIndex, startingIndex + this.pageSize))
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
trackByPublicKey(index: number, node: any): string {
|
trackByPublicKey(index: number, node: any): string {
|
||||||
return node.public_key;
|
return node.public_key;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pageChange(page: number): void {
|
||||||
|
this.startingIndexSubject.next((page - 1) * this.pageSize);
|
||||||
|
this.page = page;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user