Add preview for lightning group pages
This commit is contained in:
		
							parent
							
								
									678977a2a0
								
							
						
					
					
						commit
						0e716165e5
					
				| @ -0,0 +1,56 @@ | ||||
| <div class="box preview-box" *ngIf="nodes$ | async as nodes"> | ||||
|   <app-preview-title> | ||||
|     <span i18n="lightning.node">Lightning node group</span> | ||||
|   </app-preview-title> | ||||
|   <div class="row d-flex justify-content-between full-width-row"> | ||||
|     <div class="logo-wrapper"> | ||||
|       <app-svg-images name="officialMempoolSpace" viewBox="0 0 125 126"></app-svg-images> | ||||
|     </div> | ||||
|     <div class="title-wrapper"> | ||||
|       <h1 class="title">{{ group.name }}</h1> | ||||
|     </div> | ||||
|   </div> | ||||
|   <div class="row full-width-row"> | ||||
|     <div class="description-wrapper"> | ||||
|       <div class="description-text">{{ group.description }}</div> | ||||
|     </div> | ||||
|   </div> | ||||
|   <div class="row"> | ||||
|     <div class="col-md"> | ||||
|       <table class="table table-borderless table-striped table-fixed"> | ||||
|         <col span="1" width="250px"> | ||||
|         <tbody> | ||||
|           <tr></tr> | ||||
|           <tr> | ||||
|             <td i18n="lightning.node-count">Nodes</td> | ||||
|             <td>{{ nodes.nodes.length }}</td> | ||||
|           </tr> | ||||
|           <tr> | ||||
|             <td i18n="lightning.liquidity">Liquidity</td> | ||||
|             <td> | ||||
|               <app-amount *ngIf="nodes.sumLiquidity > 100000000; else smallnode" [satoshis]="nodes.sumLiquidity" [digitsInfo]="'1.2-2'" [noFiat]="false"></app-amount> | ||||
|               <ng-template #smallnode> | ||||
|                 {{ nodes.sumLiquidity | amountShortener: 1 }} | ||||
|                 <span class="sats" i18n="shared.sats">sats</span> | ||||
|               </ng-template> | ||||
|               <span class="d-none d-md-inline-block"> </span> | ||||
|             </td> | ||||
|           </tr> | ||||
|           <tr> | ||||
|             <td i18n="lightning.channels">Channels</td> | ||||
|             <td>{{ nodes.sumChannels }}</td> | ||||
|           </tr> | ||||
|           <tr *ngIf="nodes.sumChannels > 0"> | ||||
|             <td i18n="lightning.active-channels-avg">Average size</td> | ||||
|             <td> | ||||
|               <app-sats [satoshis]="nodes.sumLiquidity / nodes.sumChannels"></app-sats> | ||||
|             </td> | ||||
|           </tr> | ||||
|         </tbody> | ||||
|       </table> | ||||
|     </div> | ||||
|     <div class="col-md map-col"> | ||||
|       <app-nodes-map [widget]="true" [nodes]="nodes.nodes" type="isp" [fitContainer]="true" (readyEvent)="onMapReady()"></app-nodes-map> | ||||
|     </div> | ||||
|   </div> | ||||
| </div> | ||||
| @ -0,0 +1,65 @@ | ||||
| .table { | ||||
|   font-size: 32px; | ||||
|   margin-top: 0px; | ||||
| } | ||||
| 
 | ||||
| .logo-wrapper { | ||||
|   position: relative; | ||||
|   width: 62px; | ||||
|   height: 62px; | ||||
|   margin-right: 1em; | ||||
| 
 | ||||
|   img { | ||||
|     position: absolute; | ||||
|     right: 0; | ||||
|     top: 0; | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| .description-wrapper { | ||||
|   width: 100%; | ||||
|   margin: 16px 0 0; | ||||
|   padding: 20px 12px; | ||||
|   background: #181b2d; | ||||
|   font-size: 32px; | ||||
| } | ||||
| 
 | ||||
| .description-text { | ||||
|   width: 100%; | ||||
|   line-height: 36px; | ||||
|   height: 72px; | ||||
|   max-height: 72px; | ||||
|   min-height: 72px; | ||||
|   overflow: hidden; | ||||
|   display: -webkit-box; | ||||
|   -webkit-box-orient: vertical; | ||||
|   -webkit-line-clamp: 2; | ||||
|   text-overflow: ellipsis; | ||||
| } | ||||
| 
 | ||||
| .map-col { | ||||
|   flex-grow: 0; | ||||
|   flex-shrink: 0; | ||||
|   width: 470px; | ||||
|   height: 272px; | ||||
|   min-width: 470px; | ||||
|   min-height: 272px; | ||||
|   max-height: 272px; | ||||
|   padding: 0; | ||||
|   background: #181b2d; | ||||
|   overflow: hidden; | ||||
|   margin-top: 16px; | ||||
| } | ||||
| 
 | ||||
| .row { | ||||
|   margin-right: 0; | ||||
| } | ||||
| 
 | ||||
| .full-width-row { | ||||
|   padding-left: 15px; | ||||
|   flex-wrap: nowrap; | ||||
| } | ||||
| 
 | ||||
| ::ng-deep .symbol { | ||||
|   font-size: 24px; | ||||
| } | ||||
							
								
								
									
										124
									
								
								frontend/src/app/lightning/group/group-preview.component.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										124
									
								
								frontend/src/app/lightning/group/group-preview.component.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,124 @@ | ||||
| import { Component, OnInit } from '@angular/core'; | ||||
| import { ActivatedRoute, ParamMap } from '@angular/router'; | ||||
| import { map, switchMap, Observable, catchError, of } from 'rxjs'; | ||||
| import { SeoService } from '../../services/seo.service'; | ||||
| import { OpenGraphService } from '../../services/opengraph.service'; | ||||
| import { GeolocationData } from '../../shared/components/geolocation/geolocation.component'; | ||||
| import { LightningApiService } from '../lightning-api.service'; | ||||
| 
 | ||||
| interface NodeGroup { | ||||
|   name: string; | ||||
|   description: string; | ||||
| } | ||||
| 
 | ||||
| @Component({ | ||||
|   selector: 'app-group-preview', | ||||
|   templateUrl: './group-preview.component.html', | ||||
|   styleUrls: ['./group-preview.component.scss'] | ||||
| }) | ||||
| export class GroupPreviewComponent implements OnInit { | ||||
|   nodes$: Observable<any>; | ||||
|   group: NodeGroup = { name: '', description: '' }; | ||||
|   slug: string; | ||||
|   groupId: string; | ||||
| 
 | ||||
|   constructor( | ||||
|     private lightningApiService: LightningApiService, | ||||
|     private activatedRoute: ActivatedRoute, | ||||
|     private seoService: SeoService, | ||||
|     private openGraphService: OpenGraphService, | ||||
|   ) { } | ||||
| 
 | ||||
|   ngOnInit(): void { | ||||
|     this.seoService.setTitle(`Mempool.Space Lightning Nodes`); | ||||
| 
 | ||||
|     this.nodes$ = this.activatedRoute.paramMap | ||||
|       .pipe( | ||||
|         switchMap((params: ParamMap) => { | ||||
|           this.slug = params.get('slug'); | ||||
|           this.openGraphService.waitFor('ln-group-map-' + this.slug); | ||||
|           this.openGraphService.waitFor('ln-group-data-' + this.slug); | ||||
| 
 | ||||
|           if (this.slug === 'the-mempool-open-source-project') { | ||||
|             this.groupId = 'mempool.space'; | ||||
|             this.group = { | ||||
|               name: 'The Mempool Open Source Project', | ||||
|               description: 'These are the Lightning nodes operated by The Mempool Open Source Project that provide data for the mempool.space website. Connect to us!', | ||||
|             }; | ||||
|           } else { | ||||
|             this.group = { | ||||
|               name: this.slug.replace(/-/gi, ' '), | ||||
|               description: '', | ||||
|             }; | ||||
|             this.openGraphService.fail('ln-group-map-' + this.slug); | ||||
|             this.openGraphService.fail('ln-group-data-' + this.slug); | ||||
|             return of(null); | ||||
|           } | ||||
| 
 | ||||
|           return this.lightningApiService.getNodGroupNodes$(this.groupId); | ||||
|         }), | ||||
|         map((nodes) => { | ||||
|           for (const node of nodes) { | ||||
|             const socketsObject = []; | ||||
|             for (const socket of node.sockets.split(',')) { | ||||
|               if (socket === '') { | ||||
|                 continue; | ||||
|               } | ||||
|               let label = ''; | ||||
|               if (socket.match(/(?:[0-9]{1,3}\.){3}[0-9]{1,3}/)) { | ||||
|                 label = 'IPv4'; | ||||
|               } else if (socket.indexOf('[') > -1) { | ||||
|                 label = 'IPv6'; | ||||
|               } else if (socket.indexOf('onion') > -1) { | ||||
|                 label = 'Tor'; | ||||
|               } | ||||
|               socketsObject.push({ | ||||
|                 label: label, | ||||
|                 socket: node.public_key + '@' + socket, | ||||
|               }); | ||||
|             } | ||||
|             // @ts-ignore
 | ||||
|             node.socketsObject = socketsObject; | ||||
| 
 | ||||
|             if (!node?.country && !node?.city && | ||||
|               !node?.subdivision) { | ||||
|                 // @ts-ignore
 | ||||
|                 node.geolocation = null; | ||||
|             } else { | ||||
|               // @ts-ignore
 | ||||
|               node.geolocation = <GeolocationData>{ | ||||
|                 country: node.country?.en, | ||||
|                 city: node.city?.en, | ||||
|                 subdivision: node.subdivision?.en, | ||||
|                 iso: node.iso_code, | ||||
|               }; | ||||
|             } | ||||
|           } | ||||
|           const sumLiquidity = nodes.reduce((partialSum, a) => partialSum + parseInt(a.capacity, 10), 0); | ||||
|           const sumChannels = nodes.reduce((partialSum, a) => partialSum + a.opened_channel_count, 0); | ||||
| 
 | ||||
|           this.openGraphService.waitOver('ln-group-data-' + this.slug); | ||||
| 
 | ||||
|           return { | ||||
|             nodes: nodes, | ||||
|             sumLiquidity: sumLiquidity, | ||||
|             sumChannels: sumChannels, | ||||
|           }; | ||||
|         }), | ||||
|         catchError(() => { | ||||
|           this.openGraphService.fail('ln-group-map-' + this.slug); | ||||
|           this.openGraphService.fail('ln-group-data-' + this.slug); | ||||
|           return of({ | ||||
|             nodes: [], | ||||
|             sumLiquidity: 0, | ||||
|             sumChannels: 0, | ||||
|           }); | ||||
|         }) | ||||
|       ); | ||||
|   } | ||||
| 
 | ||||
|   onMapReady(): void { | ||||
|     this.openGraphService.waitOver('ln-group-map-' + this.slug); | ||||
|   } | ||||
| 
 | ||||
| } | ||||
| @ -9,11 +9,13 @@ import { NodePreviewComponent } from './node/node-preview.component'; | ||||
| import { LightningPreviewsRoutingModule } from './lightning-previews.routing.module'; | ||||
| import { ChannelPreviewComponent } from './channel/channel-preview.component'; | ||||
| import { NodesPerISPPreview } from './nodes-per-isp/nodes-per-isp-preview.component'; | ||||
| import { GroupPreviewComponent } from './group/group-preview.component'; | ||||
| @NgModule({ | ||||
|   declarations: [ | ||||
|     NodePreviewComponent, | ||||
|     ChannelPreviewComponent, | ||||
|     NodesPerISPPreview, | ||||
|     GroupPreviewComponent, | ||||
|   ], | ||||
|   imports: [ | ||||
|     CommonModule, | ||||
|  | ||||
| @ -3,6 +3,7 @@ import { RouterModule, Routes } from '@angular/router'; | ||||
| import { NodePreviewComponent } from './node/node-preview.component'; | ||||
| import { ChannelPreviewComponent } from './channel/channel-preview.component'; | ||||
| import { NodesPerISPPreview } from './nodes-per-isp/nodes-per-isp-preview.component'; | ||||
| import { GroupPreviewComponent } from './group/group-preview.component'; | ||||
| 
 | ||||
| const routes: Routes = [ | ||||
|     { | ||||
| @ -17,6 +18,10 @@ const routes: Routes = [ | ||||
|       path: 'nodes/isp/:isp', | ||||
|       component: NodesPerISPPreview, | ||||
|     }, | ||||
|     { | ||||
|       path: 'group/:slug', | ||||
|       component: GroupPreviewComponent, | ||||
|     }, | ||||
|     { | ||||
|       path: '**', | ||||
|       redirectTo: '' | ||||
|  | ||||
| @ -56,6 +56,13 @@ const routes = { | ||||
|             } | ||||
|           } | ||||
|         } | ||||
|       }, | ||||
|       group: { | ||||
|         render: true, | ||||
|         params: 1, | ||||
|         getTitle(path) { | ||||
|           return `Lightning Node Group: ${path[0]}`; | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|   }, | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user