Merge branch 'master' into nymkappa/no-db-blocks-list
This commit is contained in:
		
						commit
						8c2d72ad58
					
				| @ -42,6 +42,12 @@ class NodesRoutes { | |||||||
|       switch (config.MEMPOOL.NETWORK) { |       switch (config.MEMPOOL.NETWORK) { | ||||||
|         case 'testnet': |         case 'testnet': | ||||||
|           nodesList = [ |           nodesList = [ | ||||||
|  |             '0259db43b4e4ac0ff12a805f2d81e521253ba2317f6739bc611d8e2fa156d64256', | ||||||
|  |             '0352b9944b9a52bd2116c91f1ba70c4ef851ac5ba27e1b20f1d92da3ade010dd10', | ||||||
|  |             '03424f5a7601eaa47482cb17100b31a84a04d14fb44b83a57eeceffd8e299878e3', | ||||||
|  |             '032850492ee61a5f7006a2fda6925e4b4ec3782f2b6de2ff0e439ef5a38c3b2470', | ||||||
|  |             '022c80bace98831c44c32fb69755f2b353434e0ee9e7fbda29507f7ef8abea1421', | ||||||
|  |             '02c3559c833e6f99f9ca05fe503e0b4e7524dea9121344edfd3e811101e0c28680', | ||||||
|             '032c7c7819276c4f706a04df1a0f1e10a5495994a7be4c1d3d28ca766e5a2b957b', |             '032c7c7819276c4f706a04df1a0f1e10a5495994a7be4c1d3d28ca766e5a2b957b', | ||||||
|             '025a7e38c2834dd843591a4d23d5f09cdeb77ddca85f673c2d944a14220ff14cf7', |             '025a7e38c2834dd843591a4d23d5f09cdeb77ddca85f673c2d944a14220ff14cf7', | ||||||
|             '0395e2731a1673ef21d7a16a727c4fc4d4c35a861c428ce2c819c53d2b81c8bd55', |             '0395e2731a1673ef21d7a16a727c4fc4d4c35a861c428ce2c819c53d2b81c8bd55', | ||||||
| @ -64,6 +70,12 @@ class NodesRoutes { | |||||||
|           break; |           break; | ||||||
|         case 'signet': |         case 'signet': | ||||||
|           nodesList = [ |           nodesList = [ | ||||||
|  |             '029fe3621fc0c6e08056a14b868f8fb9acca1aa28a129512f6cea0f0d7654d9f92', | ||||||
|  |             '02f60cd7a3a4f1c953dd9554a6ebd51a34f8b10b8124b7fc43a0b381139b55c883', | ||||||
|  |             '03cbbf581774700865eebd1be42d022bc004ba30881274ab304e088a25d70e773d', | ||||||
|  |             '0243348cb3741cfe2d8485fa8375c29c7bc7cbb67577c363cb6987a5e5fd0052cc', | ||||||
|  |             '02cb73e631af44bee600d80f8488a9194c9dc5c7590e575c421a070d1be05bc8e9', | ||||||
|  |             '0306f55ee631aa1e2cd4d9b2bfcbc14404faec5c541cef8b2e6f779061029d09c4', | ||||||
|             '03ddab321b760433cbf561b615ef62ac7d318630c5f51d523aaf5395b90b751956', |             '03ddab321b760433cbf561b615ef62ac7d318630c5f51d523aaf5395b90b751956', | ||||||
|             '033d92c7bfd213ef1b34c90e985fb5dc77f9ec2409d391492484e57a44c4aca1de', |             '033d92c7bfd213ef1b34c90e985fb5dc77f9ec2409d391492484e57a44c4aca1de', | ||||||
|             '02ad010dda54253c1eb9efe38b0760657a3b43ecad62198c359c051c9d99d45781', |             '02ad010dda54253c1eb9efe38b0760657a3b43ecad62198c359c051c9d99d45781', | ||||||
| @ -86,6 +98,12 @@ class NodesRoutes { | |||||||
|           break; |           break; | ||||||
|         default: |         default: | ||||||
|           nodesList = [ |           nodesList = [ | ||||||
|  |             '02b12b889fe3c943cb05645921040ef13d6d397a2e7a4ad000e28500c505ff26d6', | ||||||
|  |             '0302240ac9d71b39617cbde2764837ec3d6198bd6074b15b75d2ff33108e89d2e1', | ||||||
|  |             '03364a8ace313376e5e4b68c954e287c6388e16df9e9fdbaf0363ecac41105cbf6', | ||||||
|  |             '03229ab4b7f692753e094b93df90530150680f86b535b5183b0cffd75b3df583fc', | ||||||
|  |             '03a696eb7acde991c1be97a58a9daef416659539ae462b897f5e9ae361f990228e', | ||||||
|  |             '0248bf26cf3a63ab8870f34dc0ec9e6c8c6288cdba96ba3f026f34ec0f13ac4055', | ||||||
|             '03fbc17549ec667bccf397ababbcb4cdc0e3394345e4773079ab2774612ec9be61', |             '03fbc17549ec667bccf397ababbcb4cdc0e3394345e4773079ab2774612ec9be61', | ||||||
|             '03da9a8623241ccf95f19cd645c6cecd4019ac91570e976eb0a128bebbc4d8a437', |             '03da9a8623241ccf95f19cd645c6cecd4019ac91570e976eb0a128bebbc4d8a437', | ||||||
|             '03ca5340cf85cb2e7cf076e489f785410838de174e40be62723e8a60972ad75144', |             '03ca5340cf85cb2e7cf076e489f785410838de174e40be62723e8a60972ad75144', | ||||||
|  | |||||||
| @ -171,6 +171,7 @@ class StatisticsApi { | |||||||
|   private getQueryForDaysAvg(div: number, interval: string) { |   private getQueryForDaysAvg(div: number, interval: string) { | ||||||
|     return `SELECT
 |     return `SELECT
 | ||||||
|       UNIX_TIMESTAMP(added) as added, |       UNIX_TIMESTAMP(added) as added, | ||||||
|  |       CAST(avg(unconfirmed_transactions) as DOUBLE) as unconfirmed_transactions, | ||||||
|       CAST(avg(vbytes_per_second) as DOUBLE) as vbytes_per_second, |       CAST(avg(vbytes_per_second) as DOUBLE) as vbytes_per_second, | ||||||
|       CAST(avg(vsize_1) as DOUBLE) as vsize_1, |       CAST(avg(vsize_1) as DOUBLE) as vsize_1, | ||||||
|       CAST(avg(vsize_2) as DOUBLE) as vsize_2, |       CAST(avg(vsize_2) as DOUBLE) as vsize_2, | ||||||
| @ -219,6 +220,7 @@ class StatisticsApi { | |||||||
|   private getQueryForDays(div: number, interval: string) { |   private getQueryForDays(div: number, interval: string) { | ||||||
|     return `SELECT
 |     return `SELECT
 | ||||||
|       UNIX_TIMESTAMP(added) as added, |       UNIX_TIMESTAMP(added) as added, | ||||||
|  |       CAST(avg(unconfirmed_transactions) as DOUBLE) as unconfirmed_transactions, | ||||||
|       CAST(avg(vbytes_per_second) as DOUBLE) as vbytes_per_second, |       CAST(avg(vbytes_per_second) as DOUBLE) as vbytes_per_second, | ||||||
|       vsize_1, |       vsize_1, | ||||||
|       vsize_2, |       vsize_2, | ||||||
| @ -401,6 +403,7 @@ class StatisticsApi { | |||||||
|     return statistic.map((s) => { |     return statistic.map((s) => { | ||||||
|       return { |       return { | ||||||
|         added: s.added, |         added: s.added, | ||||||
|  |         count: s.unconfirmed_transactions, | ||||||
|         vbytes_per_second: s.vbytes_per_second, |         vbytes_per_second: s.vbytes_per_second, | ||||||
|         mempool_byte_weight: s.mempool_byte_weight, |         mempool_byte_weight: s.mempool_byte_weight, | ||||||
|         total_fee: s.total_fee, |         total_fee: s.total_fee, | ||||||
|  | |||||||
| @ -486,6 +486,7 @@ class WebsocketHandler { | |||||||
| 
 | 
 | ||||||
|     // pre-compute address transactions
 |     // pre-compute address transactions
 | ||||||
|     const addressCache = this.makeAddressCache(newTransactions); |     const addressCache = this.makeAddressCache(newTransactions); | ||||||
|  |     const removedAddressCache = this.makeAddressCache(deletedTransactions); | ||||||
| 
 | 
 | ||||||
|     this.wss.clients.forEach(async (client) => { |     this.wss.clients.forEach(async (client) => { | ||||||
|       if (client.readyState !== WebSocket.OPEN) { |       if (client.readyState !== WebSocket.OPEN) { | ||||||
| @ -526,11 +527,15 @@ class WebsocketHandler { | |||||||
|       } |       } | ||||||
| 
 | 
 | ||||||
|       if (client['track-address']) { |       if (client['track-address']) { | ||||||
|         const foundTransactions = Array.from(addressCache[client['track-address']]?.values() || []); |         const newTransactions = Array.from(addressCache[client['track-address']]?.values() || []); | ||||||
|  |         const removedTransactions = Array.from(removedAddressCache[client['track-address']]?.values() || []); | ||||||
|         // txs may be missing prevouts in non-esplora backends
 |         // txs may be missing prevouts in non-esplora backends
 | ||||||
|         // so fetch the full transactions now
 |         // so fetch the full transactions now
 | ||||||
|         const fullTransactions = (config.MEMPOOL.BACKEND !== 'esplora') ? await this.getFullTransactions(foundTransactions) : foundTransactions; |         const fullTransactions = (config.MEMPOOL.BACKEND !== 'esplora') ? await this.getFullTransactions(newTransactions) : newTransactions; | ||||||
| 
 | 
 | ||||||
|  |         if (removedTransactions.length) { | ||||||
|  |           response['address-removed-transactions'] = JSON.stringify(removedTransactions); | ||||||
|  |         } | ||||||
|         if (fullTransactions.length) { |         if (fullTransactions.length) { | ||||||
|           response['address-transactions'] = JSON.stringify(fullTransactions); |           response['address-transactions'] = JSON.stringify(fullTransactions); | ||||||
|         } |         } | ||||||
|  | |||||||
							
								
								
									
										3
									
								
								contributors/orangesurf.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								contributors/orangesurf.txt
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,3 @@ | |||||||
|  | I hereby accept the terms of the Contributor License Agreement in the CONTRIBUTING.md file of the mempool/mempool git repository as of September 12, 2023. | ||||||
|  | 
 | ||||||
|  | Signed: orange surf | ||||||
| @ -69,6 +69,7 @@ export class BisqBlockComponent implements OnInit, OnDestroy { | |||||||
|                   this.location.replaceState( |                   this.location.replaceState( | ||||||
|                     this.router.createUrlTree(['/bisq/block/', hash]).toString() |                     this.router.createUrlTree(['/bisq/block/', hash]).toString() | ||||||
|                   ); |                   ); | ||||||
|  |                   this.seoService.updateCanonical(this.location.path()); | ||||||
|                   return this.bisqApiService.getBlock$(this.blockHash) |                   return this.bisqApiService.getBlock$(this.blockHash) | ||||||
|                     .pipe(catchError(this.caughtHttpError.bind(this))); |                     .pipe(catchError(this.caughtHttpError.bind(this))); | ||||||
|                 }), |                 }), | ||||||
|  | |||||||
| @ -10,7 +10,7 @@ | |||||||
|   </div> |   </div> | ||||||
| 
 | 
 | ||||||
|   <div class="about-text"> |   <div class="about-text"> | ||||||
|     <h5><ng-container i18n="about.about-the-project">The Mempool Open Source Project</ng-container><ng-template [ngIf]="locale.substr(0, 2) === 'en'"> ™</ng-template></h5> |     <h5><ng-container i18n="about.about-the-project">The Mempool Open Source Project</ng-container><ng-template [ngIf]="locale.substr(0, 2) === 'en'"> ®</ng-template></h5> | ||||||
|     <p i18n>Our mempool and blockchain explorer for the Bitcoin community, focusing on the transaction fee market and multi-layer ecosystem, completely self-hosted without any trusted third-parties.</p> |     <p i18n>Our mempool and blockchain explorer for the Bitcoin community, focusing on the transaction fee market and multi-layer ecosystem, completely self-hosted without any trusted third-parties.</p> | ||||||
|   </div> |   </div> | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -43,7 +43,7 @@ export class AboutComponent implements OnInit { | |||||||
|   ngOnInit() { |   ngOnInit() { | ||||||
|     this.backendInfo$ = this.stateService.backendInfo$; |     this.backendInfo$ = this.stateService.backendInfo$; | ||||||
|     this.seoService.setTitle($localize`:@@004b222ff9ef9dd4771b777950ca1d0e4cd4348a:About`); |     this.seoService.setTitle($localize`:@@004b222ff9ef9dd4771b777950ca1d0e4cd4348a:About`); | ||||||
|     this.seoService.setDescription($localize`:@@meta.description.about:Learn more about The Mempool Open Source Project™\: enterprise sponsors, individual sponsors, integrations, who contributes, FOSS licensing, and more.`); |     this.seoService.setDescription($localize`:@@meta.description.about:Learn more about The Mempool Open Source Project®\: enterprise sponsors, individual sponsors, integrations, who contributes, FOSS licensing, and more.`); | ||||||
|     this.websocketService.want(['blocks']); |     this.websocketService.want(['blocks']); | ||||||
| 
 | 
 | ||||||
|     this.profiles$ = this.apiService.getAboutPageProfiles$().pipe( |     this.profiles$ = this.apiService.getAboutPageProfiles$().pipe( | ||||||
|  | |||||||
| @ -174,6 +174,11 @@ export class AddressComponent implements OnInit, OnDestroy { | |||||||
|         this.addTransaction(tx); |         this.addTransaction(tx); | ||||||
|       }); |       }); | ||||||
| 
 | 
 | ||||||
|  |     this.stateService.mempoolRemovedTransactions$ | ||||||
|  |       .subscribe(tx => { | ||||||
|  |         this.removeTransaction(tx); | ||||||
|  |       }); | ||||||
|  | 
 | ||||||
|     this.stateService.blockTransactions$ |     this.stateService.blockTransactions$ | ||||||
|       .subscribe((transaction) => { |       .subscribe((transaction) => { | ||||||
|         const tx = this.transactions.find((t) => t.txid === transaction.txid); |         const tx = this.transactions.find((t) => t.txid === transaction.txid); | ||||||
| @ -222,6 +227,30 @@ export class AddressComponent implements OnInit, OnDestroy { | |||||||
|     return true; |     return true; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  |   removeTransaction(transaction: Transaction): boolean { | ||||||
|  |     const index = this.transactions.findIndex(((tx) => tx.txid === transaction.txid)); | ||||||
|  |     if (index === -1) { | ||||||
|  |       return false; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     this.transactions.splice(index, 1); | ||||||
|  |     this.transactions = this.transactions.slice(); | ||||||
|  |     this.txCount--; | ||||||
|  | 
 | ||||||
|  |     transaction.vin.forEach((vin) => { | ||||||
|  |       if (vin?.prevout?.scriptpubkey_address === this.address.address) { | ||||||
|  |         this.sent -= vin.prevout.value; | ||||||
|  |       } | ||||||
|  |     }); | ||||||
|  |     transaction.vout.forEach((vout) => { | ||||||
|  |       if (vout?.scriptpubkey_address === this.address.address) { | ||||||
|  |         this.received -= vout.value; | ||||||
|  |       } | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     return true; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|   loadMore() { |   loadMore() { | ||||||
|     if (this.isLoadingTransactions || !this.totalConfirmedTxCount || this.loadedConfirmedTxCount >= this.totalConfirmedTxCount) { |     if (this.isLoadingTransactions || !this.totalConfirmedTxCount || this.loadedConfirmedTxCount >= this.totalConfirmedTxCount) { | ||||||
|       return; |       return; | ||||||
|  | |||||||
| @ -166,7 +166,6 @@ export class BlockComponent implements OnInit, OnDestroy { | |||||||
|         this.page = 1; |         this.page = 1; | ||||||
|         this.error = undefined; |         this.error = undefined; | ||||||
|         this.fees = undefined; |         this.fees = undefined; | ||||||
|         this.stateService.markBlock$.next({}); |  | ||||||
| 
 | 
 | ||||||
|         if (history.state.data && history.state.data.blockHeight) { |         if (history.state.data && history.state.data.blockHeight) { | ||||||
|           this.blockHeight = history.state.data.blockHeight; |           this.blockHeight = history.state.data.blockHeight; | ||||||
| @ -176,6 +175,7 @@ export class BlockComponent implements OnInit, OnDestroy { | |||||||
|         let isBlockHeight = false; |         let isBlockHeight = false; | ||||||
|         if (/^[0-9]+$/.test(blockHash)) { |         if (/^[0-9]+$/.test(blockHash)) { | ||||||
|           isBlockHeight = true; |           isBlockHeight = true; | ||||||
|  |           this.stateService.markBlock$.next({ blockHeight: parseInt(blockHash, 10)}); | ||||||
|         } else { |         } else { | ||||||
|           this.blockHash = blockHash; |           this.blockHash = blockHash; | ||||||
|         } |         } | ||||||
| @ -202,6 +202,7 @@ export class BlockComponent implements OnInit, OnDestroy { | |||||||
|                   this.location.replaceState( |                   this.location.replaceState( | ||||||
|                     this.router.createUrlTree([(this.network ? '/' + this.network : '') + '/block/', hash]).toString() |                     this.router.createUrlTree([(this.network ? '/' + this.network : '') + '/block/', hash]).toString() | ||||||
|                   ); |                   ); | ||||||
|  |                   this.seoService.updateCanonical(this.location.path()); | ||||||
|                   return this.apiService.getBlock$(hash).pipe( |                   return this.apiService.getBlock$(hash).pipe( | ||||||
|                     catchError((err) => { |                     catchError((err) => { | ||||||
|                       this.error = err; |                       this.error = err; | ||||||
|  | |||||||
| @ -1,5 +1,5 @@ | |||||||
| <div class="text-center" class="blockchain-wrapper" [class.time-ltr]="timeLtr" [class.ltr-transition]="ltrTransitionEnabled" #container> | <div class="text-center" class="blockchain-wrapper" [class.time-ltr]="timeLtr" [class.ltr-transition]="ltrTransitionEnabled" #container> | ||||||
|   <div class="position-container" [ngClass]="network ? network : ''" [style.--divider-offset]="dividerOffset + 'px'" [style.--mempool-offset]="mempoolOffset + 'px'"> |   <div #positionContainer class="position-container" [ngClass]="network ? network : ''" [style]="positionStyle"> | ||||||
|     <span> |     <span> | ||||||
|       <div class="blocks-wrapper"> |       <div class="blocks-wrapper"> | ||||||
|         <div class="scroll-spacer" *ngIf="minScrollWidth" [style.left]="minScrollWidth + 'px'"></div> |         <div class="scroll-spacer" *ngIf="minScrollWidth" [style.left]="minScrollWidth + 'px'"></div> | ||||||
|  | |||||||
| @ -26,15 +26,7 @@ | |||||||
|   position: absolute; |   position: absolute; | ||||||
|   left: 0; |   left: 0; | ||||||
|   top: 75px; |   top: 75px; | ||||||
|   --divider-offset: 50vw; |   transform: translateX(1280px); | ||||||
|   --mempool-offset: 0px; |  | ||||||
|   transform: translateX(calc(var(--divider-offset) + var(--mempool-offset))); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| .blockchain-wrapper.time-ltr { |  | ||||||
|   .position-container { |  | ||||||
|     transform: translateX(calc(100vw - var(--divider-offset) - var(--mempool-offset))); |  | ||||||
|   } |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| .black-background { | .black-background { | ||||||
|  | |||||||
| @ -1,4 +1,4 @@ | |||||||
| import { Component, OnInit, OnDestroy, ChangeDetectionStrategy, Input, Output, EventEmitter, HostListener, ChangeDetectorRef, OnChanges, SimpleChanges } from '@angular/core'; | import { Component, OnInit, OnDestroy, ChangeDetectionStrategy, Input, Output, EventEmitter, ChangeDetectorRef, OnChanges, SimpleChanges } from '@angular/core'; | ||||||
| import { firstValueFrom, Subscription } from 'rxjs'; | import { firstValueFrom, Subscription } from 'rxjs'; | ||||||
| import { StateService } from '../../services/state.service'; | import { StateService } from '../../services/state.service'; | ||||||
| 
 | 
 | ||||||
| @ -27,8 +27,11 @@ export class BlockchainComponent implements OnInit, OnDestroy, OnChanges { | |||||||
|   loadingTip: boolean = true; |   loadingTip: boolean = true; | ||||||
|   connected: boolean = true; |   connected: boolean = true; | ||||||
| 
 | 
 | ||||||
|   dividerOffset: number = 0; |   dividerOffset: number | null = null; | ||||||
|   mempoolOffset: number = 0; |   mempoolOffset: number | null = null; | ||||||
|  |   positionStyle = { | ||||||
|  |     transform: "translateX(1280px)", | ||||||
|  |   }; | ||||||
| 
 | 
 | ||||||
|   constructor( |   constructor( | ||||||
|     public stateService: StateService, |     public stateService: StateService, | ||||||
| @ -40,6 +43,7 @@ export class BlockchainComponent implements OnInit, OnDestroy, OnChanges { | |||||||
|     this.network = this.stateService.network; |     this.network = this.stateService.network; | ||||||
|     this.timeLtrSubscription = this.stateService.timeLtr.subscribe((ltr) => { |     this.timeLtrSubscription = this.stateService.timeLtr.subscribe((ltr) => { | ||||||
|       this.timeLtr = !!ltr; |       this.timeLtr = !!ltr; | ||||||
|  |       this.updateStyle(); | ||||||
|     }); |     }); | ||||||
|     this.connectionStateSubscription = this.stateService.connectionState$.subscribe(state => { |     this.connectionStateSubscription = this.stateService.connectionState$.subscribe(state => { | ||||||
|       this.connected = (state === 2); |       this.connected = (state === 2); | ||||||
| @ -63,29 +67,47 @@ export class BlockchainComponent implements OnInit, OnDestroy, OnChanges { | |||||||
|     const prevOffset = this.mempoolOffset; |     const prevOffset = this.mempoolOffset; | ||||||
|     this.mempoolOffset = 0; |     this.mempoolOffset = 0; | ||||||
|     this.mempoolOffsetChange.emit(0); |     this.mempoolOffsetChange.emit(0); | ||||||
|  |     this.updateStyle(); | ||||||
|     setTimeout(() => { |     setTimeout(() => { | ||||||
|       this.ltrTransitionEnabled = true; |       this.ltrTransitionEnabled = true; | ||||||
|       this.flipping = true; |       this.flipping = true; | ||||||
|       this.stateService.timeLtr.next(!this.timeLtr); |       this.stateService.timeLtr.next(!this.timeLtr); | ||||||
|  |       this.cd.markForCheck(); | ||||||
|       setTimeout(() => { |       setTimeout(() => { | ||||||
|         this.ltrTransitionEnabled = false; |         this.ltrTransitionEnabled = false; | ||||||
|         this.flipping = false; |         this.flipping = false; | ||||||
|         this.mempoolOffset = prevOffset; |         this.mempoolOffset = prevOffset; | ||||||
|         this.mempoolOffsetChange.emit(this.mempoolOffset); |         this.mempoolOffsetChange.emit((this.mempoolOffset || 0)); | ||||||
|  |         this.updateStyle(); | ||||||
|  |         this.cd.markForCheck(); | ||||||
|       },  1000); |       },  1000); | ||||||
|     }, 0); |     }, 0); | ||||||
|     this.cd.markForCheck(); |  | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   onMempoolWidthChange(width): void { |   onMempoolWidthChange(width): void { | ||||||
|     if (this.flipping) { |     if (this.flipping) { | ||||||
|       return; |       return; | ||||||
|     } |     } | ||||||
|     this.mempoolOffset = Math.max(0, width - this.dividerOffset); |     this.mempoolOffset = Math.max(0, width - (this.dividerOffset || 0)); | ||||||
|     this.cd.markForCheck(); |     this.updateStyle(); | ||||||
|     this.mempoolOffsetChange.emit(this.mempoolOffset); |     this.mempoolOffsetChange.emit(this.mempoolOffset); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  |   updateStyle(): void { | ||||||
|  |     if (this.dividerOffset == null || this.mempoolOffset == null) { | ||||||
|  |       return; | ||||||
|  |     } | ||||||
|  |     const oldTransform = this.positionStyle.transform; | ||||||
|  |     this.positionStyle = this.timeLtr ? { | ||||||
|  |       transform: `translateX(calc(100vw - ${this.dividerOffset + this.mempoolOffset}px)`, | ||||||
|  |     } : { | ||||||
|  |       transform: `translateX(${this.dividerOffset + this.mempoolOffset}px)`, | ||||||
|  |     }; | ||||||
|  |     if (oldTransform !== this.positionStyle.transform) { | ||||||
|  |       this.cd.detectChanges(); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|   ngOnChanges(changes: SimpleChanges): void { |   ngOnChanges(changes: SimpleChanges): void { | ||||||
|     if (changes.containerWidth) { |     if (changes.containerWidth) { | ||||||
|       this.onResize(); |       this.onResize(); | ||||||
| @ -107,6 +129,6 @@ export class BlockchainComponent implements OnInit, OnDestroy, OnChanges { | |||||||
|         this.dividerOffset = width * 0.95; |         this.dividerOffset = width * 0.95; | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
|     this.cd.markForCheck(); |     this.updateStyle(); | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  | |||||||
| @ -194,7 +194,7 @@ export class DifficultyComponent implements OnInit { | |||||||
| 
 | 
 | ||||||
|   @HostListener('pointerdown', ['$event']) |   @HostListener('pointerdown', ['$event']) | ||||||
|   onPointerDown(event): void { |   onPointerDown(event): void { | ||||||
|     if (this.epochSvgElement.nativeElement?.contains(event.target)) { |     if (this.epochSvgElement?.nativeElement?.contains(event.target)) { | ||||||
|       this.onPointerMove(event); |       this.onPointerMove(event); | ||||||
|       event.preventDefault(); |       event.preventDefault(); | ||||||
|     } |     } | ||||||
| @ -202,7 +202,7 @@ export class DifficultyComponent implements OnInit { | |||||||
| 
 | 
 | ||||||
|   @HostListener('pointermove', ['$event']) |   @HostListener('pointermove', ['$event']) | ||||||
|   onPointerMove(event): void { |   onPointerMove(event): void { | ||||||
|     if (this.epochSvgElement.nativeElement?.contains(event.target)) { |     if (this.epochSvgElement?.nativeElement?.contains(event.target)) { | ||||||
|       this.tooltipPosition = { x: event.clientX, y: event.clientY }; |       this.tooltipPosition = { x: event.clientX, y: event.clientY }; | ||||||
|       this.cd.markForCheck(); |       this.cd.markForCheck(); | ||||||
|     } |     } | ||||||
|  | |||||||
| @ -97,6 +97,10 @@ export class MempoolBlocksComponent implements OnInit, OnChanges, OnDestroy { | |||||||
|   ngOnInit() { |   ngOnInit() { | ||||||
|     this.chainTip = this.stateService.latestBlockHeight; |     this.chainTip = this.stateService.latestBlockHeight; | ||||||
| 
 | 
 | ||||||
|  |     const width = this.containerOffset + (this.stateService.env.MEMPOOL_BLOCKS_AMOUNT) * this.blockOffset; | ||||||
|  |     this.mempoolWidth = width; | ||||||
|  |     this.widthChange.emit(this.mempoolWidth); | ||||||
|  | 
 | ||||||
|     if (['', 'testnet', 'signet'].includes(this.stateService.network)) { |     if (['', 'testnet', 'signet'].includes(this.stateService.network)) { | ||||||
|       this.enabledMiningInfoIfNeeded(this.location.path()); |       this.enabledMiningInfoIfNeeded(this.location.path()); | ||||||
|       this.location.onUrlChange((url) => this.enabledMiningInfoIfNeeded(url)); |       this.location.onUrlChange((url) => this.enabledMiningInfoIfNeeded(url)); | ||||||
| @ -161,11 +165,11 @@ export class MempoolBlocksComponent implements OnInit, OnChanges, OnDestroy { | |||||||
|           return this.mempoolBlocks; |           return this.mempoolBlocks; | ||||||
|         }), |         }), | ||||||
|         tap(() => { |         tap(() => { | ||||||
|           this.cd.markForCheck(); |  | ||||||
|           const width = this.containerOffset + this.mempoolBlocks.length * this.blockOffset; |           const width = this.containerOffset + this.mempoolBlocks.length * this.blockOffset; | ||||||
|           if (this.mempoolWidth !== width) { |           if (this.mempoolWidth !== width) { | ||||||
|             this.mempoolWidth = width; |             this.mempoolWidth = width; | ||||||
|             this.widthChange.emit(this.mempoolWidth); |             this.widthChange.emit(this.mempoolWidth); | ||||||
|  |             this.cd.markForCheck(); | ||||||
|           } |           } | ||||||
|         }) |         }) | ||||||
|       ); |       ); | ||||||
| @ -215,11 +219,13 @@ export class MempoolBlocksComponent implements OnInit, OnChanges, OnDestroy { | |||||||
|         if (isNewBlock && (block?.extras?.similarity == null || block?.extras?.similarity > 0.5) && !this.tabHidden) { |         if (isNewBlock && (block?.extras?.similarity == null || block?.extras?.similarity > 0.5) && !this.tabHidden) { | ||||||
|           this.blockIndex++; |           this.blockIndex++; | ||||||
|         } |         } | ||||||
|  |         this.cd.markForCheck(); | ||||||
|       }); |       }); | ||||||
| 
 | 
 | ||||||
|     this.chainTipSubscription = this.stateService.chainTip$.subscribe((height) => { |     this.chainTipSubscription = this.stateService.chainTip$.subscribe((height) => { | ||||||
|       if (this.chainTip === -1) { |       if (this.chainTip === -1) { | ||||||
|         this.chainTip = height; |         this.chainTip = height; | ||||||
|  |         this.cd.markForCheck(); | ||||||
|       } |       } | ||||||
|     }); |     }); | ||||||
| 
 | 
 | ||||||
| @ -257,6 +263,7 @@ export class MempoolBlocksComponent implements OnInit, OnChanges, OnDestroy { | |||||||
|       this.blockPadding = 0.24 * this.blockWidth; |       this.blockPadding = 0.24 * this.blockWidth; | ||||||
|       this.containerOffset = 0.32 * this.blockWidth; |       this.containerOffset = 0.32 * this.blockWidth; | ||||||
|       this.blockOffset = this.blockWidth + this.blockPadding; |       this.blockOffset = this.blockWidth + this.blockPadding; | ||||||
|  |       this.cd.markForCheck(); | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
| @ -275,6 +282,7 @@ export class MempoolBlocksComponent implements OnInit, OnChanges, OnDestroy { | |||||||
|   onResize(): void { |   onResize(): void { | ||||||
|     this.animateEntry = false; |     this.animateEntry = false; | ||||||
|     this.reduceEmptyBlocksToFitScreen(this.mempoolEmptyBlocks); |     this.reduceEmptyBlocksToFitScreen(this.mempoolEmptyBlocks); | ||||||
|  |     this.cd.markForCheck(); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   trackByFn(index: number, block: MempoolBlock) { |   trackByFn(index: number, block: MempoolBlock) { | ||||||
|  | |||||||
| @ -1,6 +1,7 @@ | |||||||
| import { Component, OnInit, Input, Inject, LOCALE_ID, ChangeDetectionStrategy, OnChanges } from '@angular/core'; | import { Component, OnInit, Input, Inject, LOCALE_ID, ChangeDetectionStrategy, OnChanges } from '@angular/core'; | ||||||
| import { VbytesPipe } from '../../shared/pipes/bytes-pipe/vbytes.pipe'; | import { VbytesPipe } from '../../shared/pipes/bytes-pipe/vbytes.pipe'; | ||||||
| import { WuBytesPipe } from '../../shared/pipes/bytes-pipe/wubytes.pipe'; | import { WuBytesPipe } from '../../shared/pipes/bytes-pipe/wubytes.pipe'; | ||||||
|  | import { AmountShortenerPipe } from '../../shared/pipes/amount-shortener.pipe'; | ||||||
| import { formatNumber } from '@angular/common'; | import { formatNumber } from '@angular/common'; | ||||||
| import { OptimizedMempoolStats } from '../../interfaces/node-api.interface'; | import { OptimizedMempoolStats } from '../../interfaces/node-api.interface'; | ||||||
| import { StateService } from '../../services/state.service'; | import { StateService } from '../../services/state.service'; | ||||||
| @ -26,6 +27,7 @@ export class MempoolGraphComponent implements OnInit, OnChanges { | |||||||
|   @Input() data: any[]; |   @Input() data: any[]; | ||||||
|   @Input() filterSize = 100000; |   @Input() filterSize = 100000; | ||||||
|   @Input() limitFilterFee = 1; |   @Input() limitFilterFee = 1; | ||||||
|  |   @Input() hideCount: boolean = false; | ||||||
|   @Input() height: number | string = 200; |   @Input() height: number | string = 200; | ||||||
|   @Input() top: number | string = 20; |   @Input() top: number | string = 20; | ||||||
|   @Input() right: number | string = 10; |   @Input() right: number | string = 10; | ||||||
| @ -50,10 +52,13 @@ export class MempoolGraphComponent implements OnInit, OnChanges { | |||||||
|   inverted: boolean; |   inverted: boolean; | ||||||
|   chartInstance: any = undefined; |   chartInstance: any = undefined; | ||||||
|   weightMode: boolean = false; |   weightMode: boolean = false; | ||||||
|  |   isWidget: boolean = false; | ||||||
|  |   showCount: boolean = true; | ||||||
| 
 | 
 | ||||||
|   constructor( |   constructor( | ||||||
|     private vbytesPipe: VbytesPipe, |     private vbytesPipe: VbytesPipe, | ||||||
|     private wubytesPipe: WuBytesPipe, |     private wubytesPipe: WuBytesPipe, | ||||||
|  |     private amountShortenerPipe: AmountShortenerPipe, | ||||||
|     private stateService: StateService, |     private stateService: StateService, | ||||||
|     private storageService: StorageService, |     private storageService: StorageService, | ||||||
|     @Inject(LOCALE_ID) private locale: string, |     @Inject(LOCALE_ID) private locale: string, | ||||||
| @ -62,12 +67,16 @@ export class MempoolGraphComponent implements OnInit, OnChanges { | |||||||
|   ngOnInit(): void { |   ngOnInit(): void { | ||||||
|     this.isLoading = true; |     this.isLoading = true; | ||||||
|     this.inverted = this.storageService.getValue('inverted-graph') === 'true'; |     this.inverted = this.storageService.getValue('inverted-graph') === 'true'; | ||||||
|  |     this.isWidget = this.template === 'widget'; | ||||||
|  |     this.showCount = !this.isWidget && !this.hideCount; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   ngOnChanges() { |   ngOnChanges(changes) { | ||||||
|     if (!this.data) { |     if (!this.data) { | ||||||
|       return; |       return; | ||||||
|     } |     } | ||||||
|  |     this.isWidget = this.template === 'widget'; | ||||||
|  |     this.showCount = !this.isWidget && !this.hideCount; | ||||||
|     this.windowPreference = this.windowPreferenceOverride ? this.windowPreferenceOverride : this.storageService.getValue('graphWindowPreference'); |     this.windowPreference = this.windowPreferenceOverride ? this.windowPreferenceOverride : this.storageService.getValue('graphWindowPreference'); | ||||||
|     this.mempoolVsizeFeesData = this.handleNewMempoolData(this.data.concat([])); |     this.mempoolVsizeFeesData = this.handleNewMempoolData(this.data.concat([])); | ||||||
|     this.mountFeeChart(); |     this.mountFeeChart(); | ||||||
| @ -96,10 +105,12 @@ export class MempoolGraphComponent implements OnInit, OnChanges { | |||||||
|     mempoolStats.reverse(); |     mempoolStats.reverse(); | ||||||
|     const labels = mempoolStats.map(stats => stats.added); |     const labels = mempoolStats.map(stats => stats.added); | ||||||
|     const finalArrayVByte = this.generateArray(mempoolStats); |     const finalArrayVByte = this.generateArray(mempoolStats); | ||||||
|  |     const finalArrayCount = this.generateCountArray(mempoolStats); | ||||||
| 
 | 
 | ||||||
|     return { |     return { | ||||||
|       labels: labels, |       labels: labels, | ||||||
|       series: finalArrayVByte |       series: finalArrayVByte, | ||||||
|  |       countSeries: finalArrayCount, | ||||||
|     }; |     }; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
| @ -124,9 +135,13 @@ export class MempoolGraphComponent implements OnInit, OnChanges { | |||||||
|     return finalArray; |     return finalArray; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  |   generateCountArray(mempoolStats: OptimizedMempoolStats[]) { | ||||||
|  |     return mempoolStats.filter(stats => stats.count > 0).map(stats => [stats.added * 1000, stats.count]); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|   mountFeeChart() { |   mountFeeChart() { | ||||||
|     this.orderLevels(); |     this.orderLevels(); | ||||||
|     const { series } = this.mempoolVsizeFeesData; |     const { series, countSeries } = this.mempoolVsizeFeesData; | ||||||
| 
 | 
 | ||||||
|     const seriesGraph = []; |     const seriesGraph = []; | ||||||
|     const newColors = []; |     const newColors = []; | ||||||
| @ -178,6 +193,29 @@ export class MempoolGraphComponent implements OnInit, OnChanges { | |||||||
|         }); |         }); | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
|  |     if (this.showCount) { | ||||||
|  |       newColors.push('white'); | ||||||
|  |       seriesGraph.push({ | ||||||
|  |         zlevel: 1, | ||||||
|  |         yAxisIndex: 1, | ||||||
|  |         name: 'count', | ||||||
|  |         type: 'line', | ||||||
|  |         stack: 'count', | ||||||
|  |         smooth: false, | ||||||
|  |         markPoint: false, | ||||||
|  |         lineStyle: { | ||||||
|  |           width: 2, | ||||||
|  |           opacity: 1, | ||||||
|  |         }, | ||||||
|  |         symbol: 'none', | ||||||
|  |         silent: true, | ||||||
|  |         areaStyle: { | ||||||
|  |           color: null, | ||||||
|  |           opacity: 0, | ||||||
|  |         }, | ||||||
|  |         data: countSeries, | ||||||
|  |       }); | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
|     this.mempoolVsizeFeesOptions = { |     this.mempoolVsizeFeesOptions = { | ||||||
|       series: this.inverted ? [...seriesGraph].reverse() : seriesGraph, |       series: this.inverted ? [...seriesGraph].reverse() : seriesGraph, | ||||||
| @ -201,7 +239,11 @@ export class MempoolGraphComponent implements OnInit, OnChanges { | |||||||
|           label: { |           label: { | ||||||
|             formatter: (params: any) => { |             formatter: (params: any) => { | ||||||
|               if (params.axisDimension === 'y') { |               if (params.axisDimension === 'y') { | ||||||
|                 return this.vbytesPipe.transform(params.value, 2, 'vB', 'MvB', true) |                 if (params.axisIndex === 0) { | ||||||
|  |                   return this.vbytesPipe.transform(params.value, 2, 'vB', 'MvB', true); | ||||||
|  |                 } else { | ||||||
|  |                   return this.amountShortenerPipe.transform(params.value, 2, undefined, true); | ||||||
|  |                 } | ||||||
|               } else { |               } else { | ||||||
|                 return formatterXAxis(this.locale, this.windowPreference, params.value); |                 return formatterXAxis(this.locale, this.windowPreference, params.value); | ||||||
|               } |               } | ||||||
| @ -214,7 +256,11 @@ export class MempoolGraphComponent implements OnInit, OnChanges { | |||||||
|           const itemFormatted = []; |           const itemFormatted = []; | ||||||
|           let totalParcial = 0; |           let totalParcial = 0; | ||||||
|           let progressPercentageText = ''; |           let progressPercentageText = ''; | ||||||
|           const items = this.inverted ? [...params].reverse() : params; |           let countItem; | ||||||
|  |           let items = this.inverted ? [...params].reverse() : params; | ||||||
|  |           if (items[items.length - 1].seriesName === 'count') { | ||||||
|  |             countItem = items.pop(); | ||||||
|  |           } | ||||||
|           items.map((item: any, index: number) => { |           items.map((item: any, index: number) => { | ||||||
|             totalParcial += item.value[1]; |             totalParcial += item.value[1]; | ||||||
|             const progressPercentage = (item.value[1] / totalValue) * 100; |             const progressPercentage = (item.value[1] / totalValue) * 100; | ||||||
| @ -276,6 +322,7 @@ export class MempoolGraphComponent implements OnInit, OnChanges { | |||||||
|             </tr>`);
 |             </tr>`);
 | ||||||
|           }); |           }); | ||||||
|           const classActive = (this.template === 'advanced') ? 'fees-wrapper-tooltip-chart-advanced' : ''; |           const classActive = (this.template === 'advanced') ? 'fees-wrapper-tooltip-chart-advanced' : ''; | ||||||
|  |           const titleCount = $localize`Count`; | ||||||
|           const titleRange = $localize`Range`; |           const titleRange = $localize`Range`; | ||||||
|           const titleSize = $localize`:@@7faaaa08f56427999f3be41df1093ce4089bbd75:Size`; |           const titleSize = $localize`:@@7faaaa08f56427999f3be41df1093ce4089bbd75:Size`; | ||||||
|           const titleSum = $localize`Sum`; |           const titleSum = $localize`Sum`; | ||||||
| @ -286,6 +333,25 @@ export class MempoolGraphComponent implements OnInit, OnChanges { | |||||||
|                 ${this.vbytesPipe.transform(totalValue, 2, 'vB', 'MvB', false)} |                 ${this.vbytesPipe.transform(totalValue, 2, 'vB', 'MvB', false)} | ||||||
|               </span> |               </span> | ||||||
|             </div> |             </div> | ||||||
|  |             ` +
 | ||||||
|  |               (this.showCount && countItem ? ` | ||||||
|  |                 <table class="count"> | ||||||
|  |                   <tbody> | ||||||
|  |                     <tr class="item"> | ||||||
|  |                       <td class="indicator-container"> | ||||||
|  |                         <span class="indicator" style="background-color: white"></span> | ||||||
|  |                         <span> | ||||||
|  |                           ${titleCount} | ||||||
|  |                         </span> | ||||||
|  |                       </td> | ||||||
|  |                       <td style="text-align: right;"> | ||||||
|  |                         <span>${this.amountShortenerPipe.transform(countItem.value[1], 2, undefined, true)}</span> | ||||||
|  |                       </td> | ||||||
|  |                     </tr> | ||||||
|  |                   </tbody> | ||||||
|  |                 </table> | ||||||
|  |               ` : '')
 | ||||||
|  |             + ` | ||||||
|             <table> |             <table> | ||||||
|               <thead> |               <thead> | ||||||
|                 <tr> |                 <tr> | ||||||
| @ -305,12 +371,12 @@ export class MempoolGraphComponent implements OnInit, OnChanges { | |||||||
|           </div>`;
 |           </div>`;
 | ||||||
|         } |         } | ||||||
|       }, |       }, | ||||||
|       dataZoom: (this.template === 'widget' && this.isMobile()) ? null : [{ |       dataZoom: (this.isWidget && this.isMobile()) ? null : [{ | ||||||
|         type: 'inside', |         type: 'inside', | ||||||
|         realtime: true, |         realtime: true, | ||||||
|         zoomLock: (this.template === 'widget') ? true : false, |         zoomLock: (this.isWidget) ? true : false, | ||||||
|         zoomOnMouseWheel: (this.template === 'advanced') ? true : false, |         zoomOnMouseWheel: (this.template === 'advanced') ? true : false, | ||||||
|         moveOnMouseMove: (this.template === 'widget') ? true : false, |         moveOnMouseMove: (this.isWidget) ? true : false, | ||||||
|         maxSpan: 100, |         maxSpan: 100, | ||||||
|         minSpan: 10, |         minSpan: 10, | ||||||
|       }, { |       }, { | ||||||
| @ -339,7 +405,7 @@ export class MempoolGraphComponent implements OnInit, OnChanges { | |||||||
|       }, |       }, | ||||||
|       xAxis: [ |       xAxis: [ | ||||||
|         { |         { | ||||||
|           name: this.template === 'widget' ? '' : formatterXAxisLabel(this.locale, this.windowPreference), |           name: this.isWidget ? '' : formatterXAxisLabel(this.locale, this.windowPreference), | ||||||
|           nameLocation: 'middle', |           nameLocation: 'middle', | ||||||
|           nameTextStyle: { |           nameTextStyle: { | ||||||
|             padding: [20, 0, 0, 0], |             padding: [20, 0, 0, 0], | ||||||
| @ -357,7 +423,7 @@ export class MempoolGraphComponent implements OnInit, OnChanges { | |||||||
|           }, |           }, | ||||||
|         } |         } | ||||||
|       ], |       ], | ||||||
|       yAxis: { |       yAxis: [{ | ||||||
|         type: 'value', |         type: 'value', | ||||||
|         axisLine: { onZero: false }, |         axisLine: { onZero: false }, | ||||||
|         axisLabel: { |         axisLabel: { | ||||||
| @ -371,7 +437,17 @@ export class MempoolGraphComponent implements OnInit, OnChanges { | |||||||
|             opacity: 0.25, |             opacity: 0.25, | ||||||
|           } |           } | ||||||
|         } |         } | ||||||
|  |       }, this.showCount ? { | ||||||
|  |         type: 'value', | ||||||
|  |         position: 'right', | ||||||
|  |         axisLine: { onZero: false }, | ||||||
|  |         axisLabel: { | ||||||
|  |           formatter: (value: number) => (`${this.amountShortenerPipe.transform(value, 2, undefined, true)}`), | ||||||
|         }, |         }, | ||||||
|  |         splitLine: { | ||||||
|  |           show: false, | ||||||
|  |         } | ||||||
|  |       } : null], | ||||||
|     }; |     }; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -17,6 +17,6 @@ export class PrivacyPolicyComponent { | |||||||
| 
 | 
 | ||||||
|   ngOnInit(): void { |   ngOnInit(): void { | ||||||
|     this.seoService.setTitle('Privacy Policy'); |     this.seoService.setTitle('Privacy Policy'); | ||||||
|     this.seoService.setDescription('Trusted third parties are security holes, as are trusted first parties...you should only trust your own self-hosted instance of The Mempool Open Source Project™.'); |     this.seoService.setDescription('Trusted third parties are security holes, as are trusted first parties...you should only trust your own self-hosted instance of The Mempool Open Source Project®.'); | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  | |||||||
| @ -1,4 +1,4 @@ | |||||||
| import { Component, ElementRef, HostListener, OnInit, OnDestroy, ViewChild, Input, DoCheck } from '@angular/core'; | import { Component, ElementRef, HostListener, OnInit, OnDestroy, ViewChild, Input, ChangeDetectorRef, ChangeDetectionStrategy, AfterViewChecked } from '@angular/core'; | ||||||
| import { Subscription } from 'rxjs'; | import { Subscription } from 'rxjs'; | ||||||
| import { MarkBlockState, StateService } from '../../services/state.service'; | import { MarkBlockState, StateService } from '../../services/state.service'; | ||||||
| import { specialBlocks } from '../../app.constants'; | import { specialBlocks } from '../../app.constants'; | ||||||
| @ -8,8 +8,9 @@ import { BlockExtended } from '../../interfaces/node-api.interface'; | |||||||
|   selector: 'app-start', |   selector: 'app-start', | ||||||
|   templateUrl: './start.component.html', |   templateUrl: './start.component.html', | ||||||
|   styleUrls: ['./start.component.scss'], |   styleUrls: ['./start.component.scss'], | ||||||
|  |   changeDetection: ChangeDetectionStrategy.OnPush | ||||||
| }) | }) | ||||||
| export class StartComponent implements OnInit, OnDestroy, DoCheck { | export class StartComponent implements OnInit, AfterViewChecked, OnDestroy { | ||||||
|   @Input() showLoadingIndicator = false; |   @Input() showLoadingIndicator = false; | ||||||
| 
 | 
 | ||||||
|   interval = 60; |   interval = 60; | ||||||
| @ -23,7 +24,7 @@ export class StartComponent implements OnInit, OnDestroy, DoCheck { | |||||||
|   timeLtrSubscription: Subscription; |   timeLtrSubscription: Subscription; | ||||||
|   timeLtr: boolean = this.stateService.timeLtr.value; |   timeLtr: boolean = this.stateService.timeLtr.value; | ||||||
|   chainTipSubscription: Subscription; |   chainTipSubscription: Subscription; | ||||||
|   chainTip: number = -1; |   chainTip: number = 100; | ||||||
|   tipIsSet: boolean = false; |   tipIsSet: boolean = false; | ||||||
|   lastMark: MarkBlockState; |   lastMark: MarkBlockState; | ||||||
|   markBlockSubscription: Subscription; |   markBlockSubscription: Subscription; | ||||||
| @ -41,7 +42,8 @@ export class StartComponent implements OnInit, OnDestroy, DoCheck { | |||||||
|   blocksPerPage: number = 1; |   blocksPerPage: number = 1; | ||||||
|   pageWidth: number; |   pageWidth: number; | ||||||
|   firstPageWidth: number; |   firstPageWidth: number; | ||||||
|   minScrollWidth: number; |   minScrollWidth: number = 40 + (155 * (8 + (2 * Math.ceil(window.innerWidth / 155)))); | ||||||
|  |   currentScrollWidth: number = null; | ||||||
|   pageIndex: number = 0; |   pageIndex: number = 0; | ||||||
|   pages: any[] = []; |   pages: any[] = []; | ||||||
|   pendingMark: number | null = null; |   pendingMark: number | null = null; | ||||||
| @ -49,9 +51,10 @@ export class StartComponent implements OnInit, OnDestroy, DoCheck { | |||||||
|   lastUpdate: number = 0; |   lastUpdate: number = 0; | ||||||
|   lastMouseX: number; |   lastMouseX: number; | ||||||
|   velocity: number = 0; |   velocity: number = 0; | ||||||
|   mempoolOffset: number = 0; |   mempoolOffset: number = null; | ||||||
|  |   mempoolWidth: number = 0; | ||||||
|  |   scrollLeft: number = null; | ||||||
| 
 | 
 | ||||||
|   private resizeObserver: ResizeObserver; |  | ||||||
|   chainWidth: number = window.innerWidth; |   chainWidth: number = window.innerWidth; | ||||||
|   menuOpen: boolean = false; |   menuOpen: boolean = false; | ||||||
|   menuSliding: boolean = false; |   menuSliding: boolean = false; | ||||||
| @ -59,24 +62,18 @@ export class StartComponent implements OnInit, OnDestroy, DoCheck { | |||||||
| 
 | 
 | ||||||
|   constructor( |   constructor( | ||||||
|     private stateService: StateService, |     private stateService: StateService, | ||||||
|  |     private cd: ChangeDetectorRef, | ||||||
|   ) { |   ) { | ||||||
|     this.isiOS = ['iPhone','iPod','iPad'].includes((navigator as any)?.userAgentData?.platform || navigator.platform); |     this.isiOS = ['iPhone','iPod','iPad'].includes((navigator as any)?.userAgentData?.platform || navigator.platform); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   ngDoCheck(): void { |  | ||||||
|     if (this.pendingOffset != null) { |  | ||||||
|       const offset = this.pendingOffset; |  | ||||||
|       this.pendingOffset = null; |  | ||||||
|       this.addConvertedScrollOffset(offset); |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   ngOnInit() { |   ngOnInit() { | ||||||
|     this.firstPageWidth = 40 + (this.blockWidth * this.dynamicBlocksAmount); |     this.firstPageWidth = 40 + (this.blockWidth * this.dynamicBlocksAmount); | ||||||
|     this.blockCounterSubscription = this.stateService.blocks$.subscribe((blocks) => { |     this.blockCounterSubscription = this.stateService.blocks$.subscribe((blocks) => { | ||||||
|       this.blockCount = blocks.length; |       this.blockCount = blocks.length; | ||||||
|       this.dynamicBlocksAmount = Math.min(this.blockCount, this.stateService.env.KEEP_BLOCKS_AMOUNT, 8); |       this.dynamicBlocksAmount = Math.min(this.blockCount, this.stateService.env.KEEP_BLOCKS_AMOUNT, 8); | ||||||
|       this.firstPageWidth = 40 + (this.blockWidth * this.dynamicBlocksAmount); |       this.firstPageWidth = 40 + (this.blockWidth * this.dynamicBlocksAmount); | ||||||
|  |       this.minScrollWidth = 40 + (8 * this.blockWidth) + (this.pageWidth * 2); | ||||||
|       if (this.blockCount <= Math.min(8, this.stateService.env.KEEP_BLOCKS_AMOUNT)) { |       if (this.blockCount <= Math.min(8, this.stateService.env.KEEP_BLOCKS_AMOUNT)) { | ||||||
|         this.onResize(); |         this.onResize(); | ||||||
|       } |       } | ||||||
| @ -122,7 +119,7 @@ export class StartComponent implements OnInit, OnDestroy, DoCheck { | |||||||
|             this.scrollToBlock(scrollToHeight); |             this.scrollToBlock(scrollToHeight); | ||||||
|           } |           } | ||||||
|         } |         } | ||||||
|         if (!this.tipIsSet || (blockHeight < 0 && !this.mempoolOffset)) { |         if (!this.tipIsSet || (blockHeight < 0 && this.mempoolOffset == null)) { | ||||||
|           this.pendingMark = blockHeight; |           this.pendingMark = blockHeight; | ||||||
|         } |         } | ||||||
|       } |       } | ||||||
| @ -168,15 +165,47 @@ export class StartComponent implements OnInit, OnDestroy, DoCheck { | |||||||
|     }); |     }); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   onMempoolOffsetChange(offset): void { |   ngAfterViewChecked(): void { | ||||||
|     const delta = offset - this.mempoolOffset; |     if (this.currentScrollWidth !== this.blockchainContainer?.nativeElement?.scrollWidth) { | ||||||
|  |       this.currentScrollWidth = this.blockchainContainer?.nativeElement?.scrollWidth; | ||||||
|  |       if (this.pendingOffset != null) { | ||||||
|  |         const delta = this.pendingOffset - (this.mempoolOffset || 0); | ||||||
|  |         this.mempoolOffset = this.pendingOffset; | ||||||
|  |         this.currentScrollWidth = this.blockchainContainer?.nativeElement?.scrollWidth; | ||||||
|  |         this.pendingOffset = null; | ||||||
|         this.addConvertedScrollOffset(delta); |         this.addConvertedScrollOffset(delta); | ||||||
|     this.mempoolOffset = offset; |  | ||||||
|         this.applyPendingMarkArrow(); |         this.applyPendingMarkArrow(); | ||||||
|  |       } else { | ||||||
|  |         this.applyScrollLeft(); | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   onMempoolOffsetChange(offset): void { | ||||||
|  |     if (offset !== this.mempoolOffset) { | ||||||
|  |       this.pendingOffset = offset; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   applyScrollLeft(): void { | ||||||
|  |     if (this.blockchainContainer?.nativeElement?.scrollWidth) { | ||||||
|  |       let lastScrollLeft = null; | ||||||
|  |       while (this.scrollLeft < 0 && this.shiftPagesForward() && lastScrollLeft !== this.scrollLeft) { | ||||||
|  |         lastScrollLeft = this.scrollLeft; | ||||||
|  |         this.scrollLeft += this.pageWidth; | ||||||
|  |       } | ||||||
|  |       lastScrollLeft = null; | ||||||
|  |       while (this.scrollLeft > this.blockchainContainer.nativeElement.scrollWidth && this.shiftPagesBack() && lastScrollLeft !== this.scrollLeft) { | ||||||
|  |         lastScrollLeft = this.scrollLeft; | ||||||
|  |         this.scrollLeft -= this.pageWidth; | ||||||
|  |       } | ||||||
|  |       this.blockchainContainer.nativeElement.scrollLeft = this.scrollLeft; | ||||||
|  |     } | ||||||
|  |     this.cd.detectChanges(); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   applyPendingMarkArrow(): void { |   applyPendingMarkArrow(): void { | ||||||
|     if (this.pendingMark != null) { |     if (this.pendingMark != null && this.pendingMark <= this.chainTip) { | ||||||
|       if (this.pendingMark < 0) { |       if (this.pendingMark < 0) { | ||||||
|         this.scrollToBlock(this.chainTip - this.pendingMark); |         this.scrollToBlock(this.chainTip - this.pendingMark); | ||||||
|       } else { |       } else { | ||||||
| @ -191,6 +220,7 @@ export class StartComponent implements OnInit, OnDestroy, DoCheck { | |||||||
|     window.clearTimeout(this.menuTimeout); |     window.clearTimeout(this.menuTimeout); | ||||||
|     this.menuTimeout = window.setTimeout(() => { |     this.menuTimeout = window.setTimeout(() => { | ||||||
|       this.menuSliding = false; |       this.menuSliding = false; | ||||||
|  |       this.cd.markForCheck(); | ||||||
|     }, 300); |     }, 300); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
| @ -200,9 +230,8 @@ export class StartComponent implements OnInit, OnDestroy, DoCheck { | |||||||
|     this.isMobile = this.chainWidth <= 767.98; |     this.isMobile = this.chainWidth <= 767.98; | ||||||
|     let firstVisibleBlock; |     let firstVisibleBlock; | ||||||
|     let offset; |     let offset; | ||||||
|     if (this.blockchainContainer?.nativeElement != null) { |  | ||||||
|     this.pages.forEach(page => { |     this.pages.forEach(page => { | ||||||
|         const left = page.offset - this.getConvertedScrollOffset(); |       const left = page.offset - this.getConvertedScrollOffset(this.scrollLeft); | ||||||
|       const right = left + this.pageWidth; |       const right = left + this.pageWidth; | ||||||
|       if (left <= 0 && right > 0) { |       if (left <= 0 && right > 0) { | ||||||
|         const blockIndex = Math.max(0, Math.floor(left / -this.blockWidth)); |         const blockIndex = Math.max(0, Math.floor(left / -this.blockWidth)); | ||||||
| @ -210,24 +239,24 @@ export class StartComponent implements OnInit, OnDestroy, DoCheck { | |||||||
|         offset = left + (blockIndex * this.blockWidth); |         offset = left + (blockIndex * this.blockWidth); | ||||||
|       } |       } | ||||||
|     }); |     }); | ||||||
|     } |  | ||||||
| 
 | 
 | ||||||
|     this.blocksPerPage = Math.ceil(this.chainWidth / this.blockWidth); |     this.blocksPerPage = Math.ceil(this.chainWidth / this.blockWidth); | ||||||
|     this.pageWidth = this.blocksPerPage * this.blockWidth; |     this.pageWidth = this.blocksPerPage * this.blockWidth; | ||||||
|     this.minScrollWidth = this.firstPageWidth + (this.pageWidth * 2); |     this.minScrollWidth = 40 + (8 * this.blockWidth) + (this.pageWidth * 2); | ||||||
| 
 | 
 | ||||||
|     if (firstVisibleBlock != null) { |     if (firstVisibleBlock != null) { | ||||||
|       this.scrollToBlock(firstVisibleBlock, offset + (this.isMobile ? this.blockWidth : 0)); |       this.scrollToBlock(firstVisibleBlock, offset); | ||||||
|     } else { |     } else { | ||||||
|       this.updatePages(); |       this.updatePages(); | ||||||
|     } |     } | ||||||
|  |     this.cd.markForCheck(); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   onMouseDown(event: MouseEvent) { |   onMouseDown(event: MouseEvent) { | ||||||
|     if (!(event.which > 1 || event.button > 0)) { |     if (!(event.which > 1 || event.button > 0)) { | ||||||
|       this.mouseDragStartX = event.clientX; |       this.mouseDragStartX = event.clientX; | ||||||
|       this.resetMomentum(event.clientX); |       this.resetMomentum(event.clientX); | ||||||
|       this.blockchainScrollLeftInit = this.blockchainContainer.nativeElement.scrollLeft; |       this.blockchainScrollLeftInit = this.scrollLeft; | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
|   onPointerDown(event: PointerEvent) { |   onPointerDown(event: PointerEvent) { | ||||||
| @ -253,8 +282,8 @@ export class StartComponent implements OnInit, OnDestroy, DoCheck { | |||||||
|     if (this.mouseDragStartX != null) { |     if (this.mouseDragStartX != null) { | ||||||
|       this.updateVelocity(event.clientX); |       this.updateVelocity(event.clientX); | ||||||
|       this.stateService.setBlockScrollingInProgress(true); |       this.stateService.setBlockScrollingInProgress(true); | ||||||
|       this.blockchainContainer.nativeElement.scrollLeft = |       this.scrollLeft = this.blockchainScrollLeftInit + this.mouseDragStartX - event.clientX; | ||||||
|         this.blockchainScrollLeftInit + this.mouseDragStartX - event.clientX; |       this.applyScrollLeft(); | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
|   @HostListener('document:mouseup', []) |   @HostListener('document:mouseup', []) | ||||||
| @ -310,25 +339,31 @@ export class StartComponent implements OnInit, OnDestroy, DoCheck { | |||||||
|         } else { |         } else { | ||||||
|           this.velocity += dv; |           this.velocity += dv; | ||||||
|         } |         } | ||||||
|         this.blockchainContainer.nativeElement.scrollLeft -= displacement; |         this.scrollLeft -= displacement; | ||||||
|  |         this.applyScrollLeft(); | ||||||
|         this.animateMomentum(); |         this.animateMomentum(); | ||||||
|       } |       } | ||||||
|     }); |     }); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   onScroll(e) { |   onScroll(e) { | ||||||
|  |     if (this.blockchainContainer?.nativeElement?.scrollLeft == null) { | ||||||
|  |       return; | ||||||
|  |     } | ||||||
|  |     this.scrollLeft = this.blockchainContainer?.nativeElement?.scrollLeft; | ||||||
|     const middlePage = this.pageIndex === 0 ? this.pages[0] : this.pages[1]; |     const middlePage = this.pageIndex === 0 ? this.pages[0] : this.pages[1]; | ||||||
|     // compensate for css transform
 |     // compensate for css transform
 | ||||||
|     const translation = (this.isMobile ? this.chainWidth * 0.95 : this.chainWidth * 0.5); |     const translation = (this.isMobile ? this.chainWidth * 0.95 : this.chainWidth * 0.5); | ||||||
|     const backThreshold = middlePage.offset + (this.pageWidth * 0.5) + translation; |     const backThreshold = middlePage.offset + (this.pageWidth * 0.5) + translation; | ||||||
|     const forwardThreshold = middlePage.offset - (this.pageWidth * 0.5) + translation; |     const forwardThreshold = middlePage.offset - (this.pageWidth * 0.5) + translation; | ||||||
|     const scrollLeft = this.getConvertedScrollOffset(); |     this.scrollLeft = this.blockchainContainer.nativeElement.scrollLeft; | ||||||
|     if (scrollLeft > backThreshold) { |     const offsetScroll = this.getConvertedScrollOffset(this.scrollLeft); | ||||||
|  |     if (offsetScroll > backThreshold) { | ||||||
|       if (this.shiftPagesBack()) { |       if (this.shiftPagesBack()) { | ||||||
|         this.addConvertedScrollOffset(-this.pageWidth); |         this.addConvertedScrollOffset(-this.pageWidth); | ||||||
|         this.blockchainScrollLeftInit -= this.pageWidth; |         this.blockchainScrollLeftInit -= this.pageWidth; | ||||||
|       } |       } | ||||||
|     } else if (scrollLeft < forwardThreshold) { |     } else if (offsetScroll < forwardThreshold) { | ||||||
|       if (this.shiftPagesForward()) { |       if (this.shiftPagesForward()) { | ||||||
|         this.addConvertedScrollOffset(this.pageWidth); |         this.addConvertedScrollOffset(this.pageWidth); | ||||||
|         this.blockchainScrollLeftInit += this.pageWidth; |         this.blockchainScrollLeftInit += this.pageWidth; | ||||||
| @ -337,10 +372,6 @@ export class StartComponent implements OnInit, OnDestroy, DoCheck { | |||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   scrollToBlock(height, blockOffset = 0) { |   scrollToBlock(height, blockOffset = 0) { | ||||||
|     if (!this.blockchainContainer?.nativeElement) { |  | ||||||
|       setTimeout(() => { this.scrollToBlock(height, blockOffset); }, 50); |  | ||||||
|       return; |  | ||||||
|     } |  | ||||||
|     if (this.isMobile) { |     if (this.isMobile) { | ||||||
|       blockOffset -= this.blockWidth; |       blockOffset -= this.blockWidth; | ||||||
|     } |     } | ||||||
| @ -348,15 +379,15 @@ export class StartComponent implements OnInit, OnDestroy, DoCheck { | |||||||
|     const pages = []; |     const pages = []; | ||||||
|     this.pageIndex = Math.max(viewingPageIndex - 1, 0); |     this.pageIndex = Math.max(viewingPageIndex - 1, 0); | ||||||
|     let viewingPage = this.getPageAt(viewingPageIndex); |     let viewingPage = this.getPageAt(viewingPageIndex); | ||||||
|     const isLastPage = viewingPage.height < this.blocksPerPage; |     const isLastPage = viewingPage.height <= 0; | ||||||
|     if (isLastPage) { |     if (isLastPage) { | ||||||
|       this.pageIndex = Math.max(viewingPageIndex - 2, 0); |       this.pageIndex = Math.max(viewingPageIndex - 2, 0); | ||||||
|       viewingPage = this.getPageAt(viewingPageIndex); |       viewingPage = this.getPageAt(viewingPageIndex); | ||||||
|     } |     } | ||||||
|     const left = viewingPage.offset - this.getConvertedScrollOffset(); |     const left = viewingPage.offset - this.getConvertedScrollOffset(this.scrollLeft); | ||||||
|     const blockIndex = viewingPage.height - height; |     const blockIndex = viewingPage.height - height; | ||||||
|     const targetOffset = (this.blockWidth * blockIndex) + left; |     const targetOffset = (this.blockWidth * blockIndex) + left; | ||||||
|     let deltaOffset = targetOffset - blockOffset; |     const deltaOffset = targetOffset - blockOffset; | ||||||
| 
 | 
 | ||||||
|     if (isLastPage) { |     if (isLastPage) { | ||||||
|       pages.push(this.getPageAt(viewingPageIndex - 2)); |       pages.push(this.getPageAt(viewingPageIndex - 2)); | ||||||
| @ -386,6 +417,7 @@ export class StartComponent implements OnInit, OnDestroy, DoCheck { | |||||||
|     pages.push(this.getPageAt(this.pageIndex + 1)); |     pages.push(this.getPageAt(this.pageIndex + 1)); | ||||||
|     pages.push(this.getPageAt(this.pageIndex + 2)); |     pages.push(this.getPageAt(this.pageIndex + 2)); | ||||||
|     this.pages = pages; |     this.pages = pages; | ||||||
|  |     this.cd.markForCheck(); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   shiftPagesBack(): boolean { |   shiftPagesBack(): boolean { | ||||||
| @ -439,44 +471,40 @@ export class StartComponent implements OnInit, OnDestroy, DoCheck { | |||||||
|   blockInViewport(height: number): boolean { |   blockInViewport(height: number): boolean { | ||||||
|     const firstHeight = this.pages[0].height; |     const firstHeight = this.pages[0].height; | ||||||
|     const translation = (this.isMobile ? this.chainWidth * 0.95 : this.chainWidth * 0.5); |     const translation = (this.isMobile ? this.chainWidth * 0.95 : this.chainWidth * 0.5); | ||||||
|     const firstX = this.pages[0].offset - this.getConvertedScrollOffset() + translation; |     const firstX = this.pages[0].offset - this.getConvertedScrollOffset(this.scrollLeft) + translation; | ||||||
|     const xPos = firstX + ((firstHeight - height) * 155); |     const xPos = firstX + ((firstHeight - height) * 155); | ||||||
|     return xPos > -55 && xPos < (this.chainWidth - 100); |     return xPos > -55 && xPos < (this.chainWidth - 100); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   getConvertedScrollOffset(): number { |   getConvertedScrollOffset(scrollLeft): number { | ||||||
|     if (this.timeLtr) { |     if (this.timeLtr) { | ||||||
|       return -(this.blockchainContainer?.nativeElement?.scrollLeft || 0) - this.mempoolOffset; |       return -(scrollLeft || 0) - (this.mempoolOffset || 0); | ||||||
|     } else { |     } else { | ||||||
|       return (this.blockchainContainer?.nativeElement?.scrollLeft || 0) - this.mempoolOffset; |       return (scrollLeft || 0) - (this.mempoolOffset || 0); | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   setScrollLeft(offset: number): void { |   setScrollLeft(offset: number): void { | ||||||
|     if (this.timeLtr) { |     if (this.timeLtr) { | ||||||
|       this.blockchainContainer.nativeElement.scrollLeft = offset - this.mempoolOffset; |       this.scrollLeft = offset - (this.mempoolOffset || 0); | ||||||
|     } else { |     } else { | ||||||
|       this.blockchainContainer.nativeElement.scrollLeft = offset + this.mempoolOffset; |       this.scrollLeft = offset + (this.mempoolOffset || 0); | ||||||
|     } |     } | ||||||
|  |     this.applyScrollLeft(); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   addConvertedScrollOffset(offset: number): void { |   addConvertedScrollOffset(offset: number): void { | ||||||
|     if (!this.blockchainContainer?.nativeElement) { |  | ||||||
|       this.pendingOffset = offset; |  | ||||||
|       return; |  | ||||||
|     } |  | ||||||
|     if (this.timeLtr) { |     if (this.timeLtr) { | ||||||
|       this.blockchainContainer.nativeElement.scrollLeft -= offset; |       this.scrollLeft -= offset; | ||||||
|     } else { |     } else { | ||||||
|       this.blockchainContainer.nativeElement.scrollLeft += offset; |       this.scrollLeft += offset; | ||||||
|     } |     } | ||||||
|  |     this.applyScrollLeft(); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   ngOnDestroy() { |   ngOnDestroy() { | ||||||
|     if (this.blockchainContainer?.nativeElement) { |  | ||||||
|     // clean up scroll position to prevent caching wrong scroll in Firefox
 |     // clean up scroll position to prevent caching wrong scroll in Firefox
 | ||||||
|     this.setScrollLeft(0); |     this.setScrollLeft(0); | ||||||
|     } |  | ||||||
|     this.timeLtrSubscription.unsubscribe(); |     this.timeLtrSubscription.unsubscribe(); | ||||||
|     this.chainTipSubscription.unsubscribe(); |     this.chainTipSubscription.unsubscribe(); | ||||||
|     this.markBlockSubscription.unsubscribe(); |     this.markBlockSubscription.unsubscribe(); | ||||||
|  | |||||||
| @ -69,6 +69,12 @@ | |||||||
|                 </button> |                 </button> | ||||||
|                 <div class="dropdown-fees" ngbDropdownMenu aria-labelledby="dropdownFees"> |                 <div class="dropdown-fees" ngbDropdownMenu aria-labelledby="dropdownFees"> | ||||||
|                   <ul> |                   <ul> | ||||||
|  |                     <li (click)="this.showCount = !this.showCount" | ||||||
|  |                       [class]="this.showCount ? '' : 'inactive'"> | ||||||
|  |                       <span class="square" [ngStyle]="{'backgroundColor': 'white'}"></span> | ||||||
|  |                       <span class="fee-text">{{ titleCount }}</span> | ||||||
|  |                     </li> | ||||||
|  |                     <hr style="margin: 4px;"> | ||||||
|                     <ng-template ngFor let-feeData let-i="index" [ngForOf]="feeLevelDropdownData"> |                     <ng-template ngFor let-feeData let-i="index" [ngForOf]="feeLevelDropdownData"> | ||||||
|                       <ng-template [ngIf]="feeData.fee <= (feeLevels[maxFeeIndex])"> |                       <ng-template [ngIf]="feeData.fee <= (feeLevels[maxFeeIndex])"> | ||||||
|                         <li (click)="filterFeeIndex = feeData.fee" |                         <li (click)="filterFeeIndex = feeData.fee" | ||||||
| @ -92,8 +98,8 @@ | |||||||
|         </div> |         </div> | ||||||
|         <div class="card-body"> |         <div class="card-body"> | ||||||
|           <div class="incoming-transactions-graph"> |           <div class="incoming-transactions-graph"> | ||||||
|             <app-mempool-graph #mempoolgraph dir="ltr" [template]="'advanced'" |             <app-mempool-graph #mempoolgraph dir="ltr" [template]="'advanced'" [hideCount]="!showCount" | ||||||
|               [limitFilterFee]="filterFeeIndex" [height]="500" [left]="65" [right]="10" |               [limitFilterFee]="filterFeeIndex" [height]="500" [left]="65" [right]="showCount ? 50 : 10" | ||||||
|               [data]="mempoolStats && mempoolStats.length ? mempoolStats : null"></app-mempool-graph> |               [data]="mempoolStats && mempoolStats.length ? mempoolStats : null"></app-mempool-graph> | ||||||
|           </div> |           </div> | ||||||
|         </div> |         </div> | ||||||
|  | |||||||
| @ -32,6 +32,7 @@ export class StatisticsComponent implements OnInit { | |||||||
|   chartColors = chartColors; |   chartColors = chartColors; | ||||||
|   filterSize = 100000; |   filterSize = 100000; | ||||||
|   filterFeeIndex = 1; |   filterFeeIndex = 1; | ||||||
|  |   showCount = true; | ||||||
|   maxFeeIndex: number; |   maxFeeIndex: number; | ||||||
|   dropDownOpen = false; |   dropDownOpen = false; | ||||||
| 
 | 
 | ||||||
| @ -46,6 +47,7 @@ export class StatisticsComponent implements OnInit { | |||||||
|   inverted: boolean; |   inverted: boolean; | ||||||
|   feeLevelDropdownData = []; |   feeLevelDropdownData = []; | ||||||
|   timespan = ''; |   timespan = ''; | ||||||
|  |   titleCount = $localize`Count`; | ||||||
| 
 | 
 | ||||||
|   constructor( |   constructor( | ||||||
|     @Inject(LOCALE_ID) private locale: string, |     @Inject(LOCALE_ID) private locale: string, | ||||||
|  | |||||||
| @ -17,6 +17,6 @@ export class TrademarkPolicyComponent { | |||||||
| 
 | 
 | ||||||
|   ngOnInit(): void { |   ngOnInit(): void { | ||||||
|     this.seoService.setTitle('Trademark Policy'); |     this.seoService.setTitle('Trademark Policy'); | ||||||
|     this.seoService.setDescription('An overview of the trademarks registered by Mempool Space K.K. and The Mempool Open Source Project™ and what we consider to be lawful usage of those trademarks.'); |     this.seoService.setDescription('An overview of the trademarks registered by Mempool Space K.K. and The Mempool Open Source Project® and what we consider to be lawful usage of those trademarks.'); | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  | |||||||
| @ -2,6 +2,7 @@ import { Block, Transaction } from "./electrs.interface"; | |||||||
| 
 | 
 | ||||||
| export interface OptimizedMempoolStats { | export interface OptimizedMempoolStats { | ||||||
|   added: number; |   added: number; | ||||||
|  |   count: number; | ||||||
|   vbytes_per_second: number; |   vbytes_per_second: number; | ||||||
|   total_fee: number; |   total_fee: number; | ||||||
|   mempool_byte_weight: number; |   mempool_byte_weight: number; | ||||||
|  | |||||||
| @ -114,7 +114,7 @@ export class NodesMap implements OnInit, OnChanges { | |||||||
|           node[3], // Alias
 |           node[3], // Alias
 | ||||||
|           node[2], // Public key
 |           node[2], // Public key
 | ||||||
|           node[5], // Channels
 |           node[5], // Channels
 | ||||||
|           node[6].en, // Country
 |           node[6]?.en, // Country
 | ||||||
|           node[7], // ISO Code
 |           node[7], // ISO Code
 | ||||||
|         ]); |         ]); | ||||||
|       } |       } | ||||||
|  | |||||||
| @ -12,6 +12,8 @@ export class SeoService { | |||||||
|   baseTitle = 'mempool'; |   baseTitle = 'mempool'; | ||||||
|   baseDescription = 'Explore the full Bitcoin ecosystem with The Mempool Open Project™.'; |   baseDescription = 'Explore the full Bitcoin ecosystem with The Mempool Open Project™.'; | ||||||
| 
 | 
 | ||||||
|  |   canonicalLink: HTMLElement = document.getElementById('canonical'); | ||||||
|  | 
 | ||||||
|   constructor( |   constructor( | ||||||
|     private titleService: Title, |     private titleService: Title, | ||||||
|     private metaService: Meta, |     private metaService: Meta, | ||||||
| @ -65,6 +67,16 @@ export class SeoService { | |||||||
|     this.metaService.updateTag({ property: 'og:description', content: this.getDescription()}); |     this.metaService.updateTag({ property: 'og:description', content: this.getDescription()}); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  |   updateCanonical(path) { | ||||||
|  |     let domain = 'mempool.space'; | ||||||
|  |     if (this.stateService.env.BASE_MODULE === 'liquid') { | ||||||
|  |       domain = 'liquid.network'; | ||||||
|  |     } else if (this.stateService.env.BASE_MODULE === 'bisq') { | ||||||
|  |       domain = 'bisq.markets'; | ||||||
|  |     } | ||||||
|  |     this.canonicalLink.setAttribute('href', 'https://' + domain + path); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|   getTitle(): string { |   getTitle(): string { | ||||||
|     if (this.network === 'testnet') |     if (this.network === 'testnet') | ||||||
|       return this.baseTitle + ' - Bitcoin Testnet'; |       return this.baseTitle + ' - Bitcoin Testnet'; | ||||||
|  | |||||||
| @ -117,6 +117,7 @@ export class StateService { | |||||||
|   difficultyAdjustment$ = new ReplaySubject<DifficultyAdjustment>(1); |   difficultyAdjustment$ = new ReplaySubject<DifficultyAdjustment>(1); | ||||||
|   mempoolTransactions$ = new Subject<Transaction>(); |   mempoolTransactions$ = new Subject<Transaction>(); | ||||||
|   mempoolTxPosition$ = new Subject<{ txid: string, position: MempoolPosition, cpfp: CpfpInfo | null}>(); |   mempoolTxPosition$ = new Subject<{ txid: string, position: MempoolPosition, cpfp: CpfpInfo | null}>(); | ||||||
|  |   mempoolRemovedTransactions$ = new Subject<Transaction>(); | ||||||
|   blockTransactions$ = new Subject<Transaction>(); |   blockTransactions$ = new Subject<Transaction>(); | ||||||
|   isLoadingWebSocket$ = new ReplaySubject<boolean>(1); |   isLoadingWebSocket$ = new ReplaySubject<boolean>(1); | ||||||
|   isLoadingMempool$ = new BehaviorSubject<boolean>(true); |   isLoadingMempool$ = new BehaviorSubject<boolean>(true); | ||||||
|  | |||||||
| @ -358,6 +358,12 @@ export class WebsocketService { | |||||||
|       }); |       }); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     if (response['address-removed-transactions']) { | ||||||
|  |       response['address-removed-transactions'].forEach((addressTransaction: Transaction) => { | ||||||
|  |         this.stateService.mempoolRemovedTransactions$.next(addressTransaction); | ||||||
|  |       }); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     if (response['block-transactions']) { |     if (response['block-transactions']) { | ||||||
|       response['block-transactions'].forEach((addressTransaction: Transaction) => { |       response['block-transactions'].forEach((addressTransaction: Transaction) => { | ||||||
|         this.stateService.blockTransactions$.next(addressTransaction); |         this.stateService.blockTransactions$.next(addressTransaction); | ||||||
|  | |||||||
| @ -20,6 +20,11 @@ export class GeolocationComponent implements OnChanges { | |||||||
|   formattedLocation: string = ''; |   formattedLocation: string = ''; | ||||||
| 
 | 
 | ||||||
|   ngOnChanges(): void { |   ngOnChanges(): void { | ||||||
|  |     if (!this.data) { | ||||||
|  |       this.formattedLocation = '-'; | ||||||
|  |       return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     const city = this.data.city ? this.data.city : ''; |     const city = this.data.city ? this.data.city : ''; | ||||||
|     const subdivisionLikeCity = this.data.city === this.data.subdivision; |     const subdivisionLikeCity = this.data.city === this.data.subdivision; | ||||||
|     let subdivision = this.data.subdivision; |     let subdivision = this.data.subdivision; | ||||||
|  | |||||||
| @ -7,17 +7,17 @@ | |||||||
|   <script src="/resources/config.js"></script> |   <script src="/resources/config.js"></script> | ||||||
|   <base href="/"> |   <base href="/"> | ||||||
| 
 | 
 | ||||||
|   <meta name="description" content="Explore the full Bitcoin ecosystem with The Mempool Open Source Project™. See the real-time status of your transactions, get network info, and more." /> |   <meta name="description" content="Explore the full Bitcoin ecosystem with The Mempool Open Source Project®. See the real-time status of your transactions, get network info, and more." /> | ||||||
|   <meta property="og:image" content="https://mempool.space/resources/mempool-space-preview.png" /> |   <meta property="og:image" content="https://mempool.space/resources/mempool-space-preview.png" /> | ||||||
|   <meta property="og:image:type" content="image/png" /> |   <meta property="og:image:type" content="image/png" /> | ||||||
|   <meta property="og:image:width" content="1000" /> |   <meta property="og:image:width" content="1000" /> | ||||||
|   <meta property="og:image:height" content="500" /> |   <meta property="og:image:height" content="500" /> | ||||||
|   <meta property="og:description" content="Explore the full Bitcoin ecosystem with The Mempool Open Source Project™. See the real-time status of your transactions, get network info, and more." /> |   <meta property="og:description" content="Explore the full Bitcoin ecosystem with The Mempool Open Source Project®. See the real-time status of your transactions, get network info, and more." /> | ||||||
|   <meta name="twitter:card" content="summary_large_image"> |   <meta name="twitter:card" content="summary_large_image"> | ||||||
|   <meta name="twitter:site" content="@mempool"> |   <meta name="twitter:site" content="@mempool"> | ||||||
|   <meta name="twitter:creator" content="@mempool"> |   <meta name="twitter:creator" content="@mempool"> | ||||||
|   <meta name="twitter:title" content="The Mempool Open Source Project®"> |   <meta name="twitter:title" content="The Mempool Open Source Project®"> | ||||||
|   <meta name="twitter:description" content="Explore the full Bitcoin ecosystem with The Mempool Open Source Project™. See the real-time status of your transactions, get network info, and more." /> |   <meta name="twitter:description" content="Explore the full Bitcoin ecosystem with The Mempool Open Source Project®. See the real-time status of your transactions, get network info, and more." /> | ||||||
|   <meta name="twitter:image:src" content="https://mempool.space/resources/mempool-space-preview.png" /> |   <meta name="twitter:image:src" content="https://mempool.space/resources/mempool-space-preview.png" /> | ||||||
|   <meta name="twitter:domain" content="mempool.space"> |   <meta name="twitter:domain" content="mempool.space"> | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -25,6 +25,12 @@ | |||||||
|   "ESPLORA": { |   "ESPLORA": { | ||||||
|     "UNIX_SOCKET_PATH": "/elements/socket/esplora-liquid-mainnet", |     "UNIX_SOCKET_PATH": "/elements/socket/esplora-liquid-mainnet", | ||||||
|     "FALLBACK": [ |     "FALLBACK": [ | ||||||
|  |       "http://node201.va1.mempool.space:3001", | ||||||
|  |       "http://node202.va1.mempool.space:3001", | ||||||
|  |       "http://node203.va1.mempool.space:3001", | ||||||
|  |       "http://node204.va1.mempool.space:3001", | ||||||
|  |       "http://node205.va1.mempool.space:3001", | ||||||
|  |       "http://node206.va1.mempool.space:3001", | ||||||
|       "http://node201.fmt.mempool.space:3001", |       "http://node201.fmt.mempool.space:3001", | ||||||
|       "http://node202.fmt.mempool.space:3001", |       "http://node202.fmt.mempool.space:3001", | ||||||
|       "http://node203.fmt.mempool.space:3001", |       "http://node203.fmt.mempool.space:3001", | ||||||
|  | |||||||
| @ -25,6 +25,12 @@ | |||||||
|   "ESPLORA": { |   "ESPLORA": { | ||||||
|     "UNIX_SOCKET_PATH": "/elements/socket/esplora-liquid-testnet", |     "UNIX_SOCKET_PATH": "/elements/socket/esplora-liquid-testnet", | ||||||
|     "FALLBACK": [ |     "FALLBACK": [ | ||||||
|  |       "http://node201.va1.mempool.space:3004", | ||||||
|  |       "http://node202.va1.mempool.space:3004", | ||||||
|  |       "http://node203.va1.mempool.space:3004", | ||||||
|  |       "http://node204.va1.mempool.space:3004", | ||||||
|  |       "http://node205.va1.mempool.space:3004", | ||||||
|  |       "http://node206.va1.mempool.space:3004", | ||||||
|       "http://node201.fmt.mempool.space:3004", |       "http://node201.fmt.mempool.space:3004", | ||||||
|       "http://node202.fmt.mempool.space:3004", |       "http://node202.fmt.mempool.space:3004", | ||||||
|       "http://node203.fmt.mempool.space:3004", |       "http://node203.fmt.mempool.space:3004", | ||||||
|  | |||||||
| @ -16,8 +16,33 @@ | |||||||
|     "PASSWORD": "__BITCOIN_RPC_PASS__" |     "PASSWORD": "__BITCOIN_RPC_PASS__" | ||||||
|   }, |   }, | ||||||
|   "ESPLORA": { |   "ESPLORA": { | ||||||
|     "REST_API_URL": "http://127.0.0.1:5000", |     "UNIX_SOCKET_PATH": "/bitcoin/socket/esplora-bitcoin-mainnet", | ||||||
|     "UNIX_SOCKET_PATH": "/bitcoin/socket/esplora-bitcoin-mainnet" |     "FALLBACK": [ | ||||||
|  |       "http://node201.va1.mempool.space:3000", | ||||||
|  |       "http://node202.va1.mempool.space:3000", | ||||||
|  |       "http://node203.va1.mempool.space:3000", | ||||||
|  |       "http://node204.va1.mempool.space:3000", | ||||||
|  |       "http://node205.va1.mempool.space:3000", | ||||||
|  |       "http://node206.va1.mempool.space:3000", | ||||||
|  |       "http://node201.fmt.mempool.space:3000", | ||||||
|  |       "http://node202.fmt.mempool.space:3000", | ||||||
|  |       "http://node203.fmt.mempool.space:3000", | ||||||
|  |       "http://node204.fmt.mempool.space:3000", | ||||||
|  |       "http://node205.fmt.mempool.space:3000", | ||||||
|  |       "http://node206.fmt.mempool.space:3000", | ||||||
|  |       "http://node201.fra.mempool.space:3000", | ||||||
|  |       "http://node202.fra.mempool.space:3000", | ||||||
|  |       "http://node203.fra.mempool.space:3000", | ||||||
|  |       "http://node204.fra.mempool.space:3000", | ||||||
|  |       "http://node205.fra.mempool.space:3000", | ||||||
|  |       "http://node206.fra.mempool.space:3000", | ||||||
|  |       "http://node201.tk7.mempool.space:3000", | ||||||
|  |       "http://node202.tk7.mempool.space:3000", | ||||||
|  |       "http://node203.tk7.mempool.space:3000", | ||||||
|  |       "http://node204.tk7.mempool.space:3000", | ||||||
|  |       "http://node205.tk7.mempool.space:3000", | ||||||
|  |       "http://node206.tk7.mempool.space:3000" | ||||||
|  |     ] | ||||||
|   }, |   }, | ||||||
|   "LIGHTNING": { |   "LIGHTNING": { | ||||||
|     "ENABLED": true, |     "ENABLED": true, | ||||||
|  | |||||||
| @ -37,6 +37,12 @@ | |||||||
|   "ESPLORA": { |   "ESPLORA": { | ||||||
|     "UNIX_SOCKET_PATH": "/bitcoin/socket/esplora-bitcoin-mainnet", |     "UNIX_SOCKET_PATH": "/bitcoin/socket/esplora-bitcoin-mainnet", | ||||||
|     "FALLBACK": [ |     "FALLBACK": [ | ||||||
|  |       "http://node201.va1.mempool.space:3000", | ||||||
|  |       "http://node202.va1.mempool.space:3000", | ||||||
|  |       "http://node203.va1.mempool.space:3000", | ||||||
|  |       "http://node204.va1.mempool.space:3000", | ||||||
|  |       "http://node205.va1.mempool.space:3000", | ||||||
|  |       "http://node206.va1.mempool.space:3000", | ||||||
|       "http://node201.fmt.mempool.space:3000", |       "http://node201.fmt.mempool.space:3000", | ||||||
|       "http://node202.fmt.mempool.space:3000", |       "http://node202.fmt.mempool.space:3000", | ||||||
|       "http://node203.fmt.mempool.space:3000", |       "http://node203.fmt.mempool.space:3000", | ||||||
| @ -74,6 +80,12 @@ | |||||||
|     "AUDIT": true, |     "AUDIT": true, | ||||||
|     "AUDIT_START_HEIGHT": 774000, |     "AUDIT_START_HEIGHT": 774000, | ||||||
|     "SERVERS": [ |     "SERVERS": [ | ||||||
|  |       "node201.va1.mempool.space", | ||||||
|  |       "node202.va1.mempool.space", | ||||||
|  |       "node203.va1.mempool.space", | ||||||
|  |       "node204.va1.mempool.space", | ||||||
|  |       "node205.va1.mempool.space", | ||||||
|  |       "node206.va1.mempool.space", | ||||||
|       "node201.fmt.mempool.space", |       "node201.fmt.mempool.space", | ||||||
|       "node202.fmt.mempool.space", |       "node202.fmt.mempool.space", | ||||||
|       "node203.fmt.mempool.space", |       "node203.fmt.mempool.space", | ||||||
|  | |||||||
| @ -16,8 +16,33 @@ | |||||||
|     "PASSWORD": "__BITCOIN_RPC_PASS__" |     "PASSWORD": "__BITCOIN_RPC_PASS__" | ||||||
|   }, |   }, | ||||||
|   "ESPLORA": { |   "ESPLORA": { | ||||||
|     "REST_API_URL": "http://127.0.0.1:5003", |     "UNIX_SOCKET_PATH": "/bitcoin/socket/esplora-bitcoin-signet", | ||||||
|     "UNIX_SOCKET_PATH": "/bitcoin/socket/esplora-bitcoin-signet" |     "FALLBACK": [ | ||||||
|  |       "http://node201.va1.mempool.space:3003", | ||||||
|  |       "http://node202.va1.mempool.space:3003", | ||||||
|  |       "http://node203.va1.mempool.space:3003", | ||||||
|  |       "http://node204.va1.mempool.space:3003", | ||||||
|  |       "http://node205.va1.mempool.space:3003", | ||||||
|  |       "http://node206.va1.mempool.space:3003", | ||||||
|  |       "http://node201.fmt.mempool.space:3003", | ||||||
|  |       "http://node202.fmt.mempool.space:3003", | ||||||
|  |       "http://node203.fmt.mempool.space:3003", | ||||||
|  |       "http://node204.fmt.mempool.space:3003", | ||||||
|  |       "http://node205.fmt.mempool.space:3003", | ||||||
|  |       "http://node206.fmt.mempool.space:3003", | ||||||
|  |       "http://node201.fra.mempool.space:3003", | ||||||
|  |       "http://node202.fra.mempool.space:3003", | ||||||
|  |       "http://node203.fra.mempool.space:3003", | ||||||
|  |       "http://node204.fra.mempool.space:3003", | ||||||
|  |       "http://node205.fra.mempool.space:3003", | ||||||
|  |       "http://node206.fra.mempool.space:3003", | ||||||
|  |       "http://node201.tk7.mempool.space:3003", | ||||||
|  |       "http://node202.tk7.mempool.space:3003", | ||||||
|  |       "http://node203.tk7.mempool.space:3003", | ||||||
|  |       "http://node204.tk7.mempool.space:3003", | ||||||
|  |       "http://node205.tk7.mempool.space:3003", | ||||||
|  |       "http://node206.tk7.mempool.space:3003" | ||||||
|  |     ] | ||||||
|   }, |   }, | ||||||
|   "LIGHTNING": { |   "LIGHTNING": { | ||||||
|     "ENABLED": true, |     "ENABLED": true, | ||||||
|  | |||||||
| @ -27,6 +27,12 @@ | |||||||
|   "ESPLORA": { |   "ESPLORA": { | ||||||
|     "UNIX_SOCKET_PATH": "/bitcoin/socket/esplora-bitcoin-signet", |     "UNIX_SOCKET_PATH": "/bitcoin/socket/esplora-bitcoin-signet", | ||||||
|     "FALLBACK": [ |     "FALLBACK": [ | ||||||
|  |       "http://node201.va1.mempool.space:3003", | ||||||
|  |       "http://node202.va1.mempool.space:3003", | ||||||
|  |       "http://node203.va1.mempool.space:3003", | ||||||
|  |       "http://node204.va1.mempool.space:3003", | ||||||
|  |       "http://node205.va1.mempool.space:3003", | ||||||
|  |       "http://node206.va1.mempool.space:3003", | ||||||
|       "http://node201.fmt.mempool.space:3003", |       "http://node201.fmt.mempool.space:3003", | ||||||
|       "http://node202.fmt.mempool.space:3003", |       "http://node202.fmt.mempool.space:3003", | ||||||
|       "http://node203.fmt.mempool.space:3003", |       "http://node203.fmt.mempool.space:3003", | ||||||
|  | |||||||
| @ -16,8 +16,33 @@ | |||||||
|     "PASSWORD": "__BITCOIN_RPC_PASS__" |     "PASSWORD": "__BITCOIN_RPC_PASS__" | ||||||
|   }, |   }, | ||||||
|   "ESPLORA": { |   "ESPLORA": { | ||||||
|     "REST_API_URL": "http://127.0.0.1:5002", |     "UNIX_SOCKET_PATH": "/bitcoin/socket/esplora-bitcoin-testnet", | ||||||
|     "UNIX_SOCKET_PATH": "/bitcoin/socket/esplora-bitcoin-testnet" |     "FALLBACK": [ | ||||||
|  |       "http://node201.va1.mempool.space:3002", | ||||||
|  |       "http://node202.va1.mempool.space:3002", | ||||||
|  |       "http://node203.va1.mempool.space:3002", | ||||||
|  |       "http://node204.va1.mempool.space:3002", | ||||||
|  |       "http://node205.va1.mempool.space:3002", | ||||||
|  |       "http://node206.va1.mempool.space:3002", | ||||||
|  |       "http://node201.fmt.mempool.space:3002", | ||||||
|  |       "http://node202.fmt.mempool.space:3002", | ||||||
|  |       "http://node203.fmt.mempool.space:3002", | ||||||
|  |       "http://node204.fmt.mempool.space:3002", | ||||||
|  |       "http://node205.fmt.mempool.space:3002", | ||||||
|  |       "http://node206.fmt.mempool.space:3002", | ||||||
|  |       "http://node201.fra.mempool.space:3002", | ||||||
|  |       "http://node202.fra.mempool.space:3002", | ||||||
|  |       "http://node203.fra.mempool.space:3002", | ||||||
|  |       "http://node204.fra.mempool.space:3002", | ||||||
|  |       "http://node205.fra.mempool.space:3002", | ||||||
|  |       "http://node206.fra.mempool.space:3002", | ||||||
|  |       "http://node201.tk7.mempool.space:3002", | ||||||
|  |       "http://node202.tk7.mempool.space:3002", | ||||||
|  |       "http://node203.tk7.mempool.space:3002", | ||||||
|  |       "http://node204.tk7.mempool.space:3002", | ||||||
|  |       "http://node205.tk7.mempool.space:3002", | ||||||
|  |       "http://node206.tk7.mempool.space:3002" | ||||||
|  |     ] | ||||||
|   }, |   }, | ||||||
|   "LIGHTNING": { |   "LIGHTNING": { | ||||||
|     "ENABLED": true, |     "ENABLED": true, | ||||||
|  | |||||||
| @ -27,6 +27,12 @@ | |||||||
|   "ESPLORA": { |   "ESPLORA": { | ||||||
|     "UNIX_SOCKET_PATH": "/bitcoin/socket/esplora-bitcoin-testnet", |     "UNIX_SOCKET_PATH": "/bitcoin/socket/esplora-bitcoin-testnet", | ||||||
|     "FALLBACK": [ |     "FALLBACK": [ | ||||||
|  |       "http://node201.va1.mempool.space:3002", | ||||||
|  |       "http://node202.va1.mempool.space:3002", | ||||||
|  |       "http://node203.va1.mempool.space:3002", | ||||||
|  |       "http://node204.va1.mempool.space:3002", | ||||||
|  |       "http://node205.va1.mempool.space:3002", | ||||||
|  |       "http://node206.va1.mempool.space:3002", | ||||||
|       "http://node201.fmt.mempool.space:3002", |       "http://node201.fmt.mempool.space:3002", | ||||||
|       "http://node202.fmt.mempool.space:3002", |       "http://node202.fmt.mempool.space:3002", | ||||||
|       "http://node203.fmt.mempool.space:3002", |       "http://node203.fmt.mempool.space:3002", | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user