Merge branch 'master' into simon/show-ln-capacity-on-mobile
This commit is contained in:
		
						commit
						b638719e72
					
				| @ -25,6 +25,8 @@ export class AppComponent implements OnInit { | |||||||
|     if (this.locale.startsWith('ar') || this.locale.startsWith('fa') || this.locale.startsWith('he')) { |     if (this.locale.startsWith('ar') || this.locale.startsWith('fa') || this.locale.startsWith('he')) { | ||||||
|       this.dir = 'rtl'; |       this.dir = 'rtl'; | ||||||
|       this.class = 'rtl-layout'; |       this.class = 'rtl-layout'; | ||||||
|  |     } else { | ||||||
|  |       this.class = 'ltr-layout'; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     tooltipConfig.animation = false; |     tooltipConfig.animation = false; | ||||||
|  | |||||||
| @ -27,7 +27,6 @@ | |||||||
|   left: 0; |   left: 0; | ||||||
|   top: 75px; |   top: 75px; | ||||||
|   transform: translateX(50vw); |   transform: translateX(50vw); | ||||||
|   transition: transform 1s; |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| .position-container.liquid, .position-container.liquidtestnet { | .position-container.liquid, .position-container.liquidtestnet { | ||||||
| @ -84,9 +83,9 @@ | |||||||
| 
 | 
 | ||||||
| .time-toggle { | .time-toggle { | ||||||
|   color: white; |   color: white; | ||||||
|   font-size: 1rem; |   font-size: 0.8rem; | ||||||
|   position: absolute; |   position: absolute; | ||||||
|   bottom: -1.5em; |   bottom: -1.8em; | ||||||
|   left: 1px; |   left: 1px; | ||||||
|   transform: translateX(-50%); |   transform: translateX(-50%); | ||||||
|   background: none; |   background: none; | ||||||
| @ -97,14 +96,31 @@ | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| .blockchain-wrapper.ltr-transition .blocks-wrapper, | .blockchain-wrapper.ltr-transition .blocks-wrapper, | ||||||
|  | .blockchain-wrapper.ltr-transition .position-container, | ||||||
| .blockchain-wrapper.ltr-transition .time-toggle { | .blockchain-wrapper.ltr-transition .time-toggle { | ||||||
|   transition: transform 1s; |   transition: transform 1s; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| .blockchain-wrapper.time-ltr .blocks-wrapper { | .blockchain-wrapper.time-ltr { | ||||||
|  |   .blocks-wrapper { | ||||||
|     transform: scaleX(-1); |     transform: scaleX(-1); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
| .blockchain-wrapper.time-ltr .time-toggle { |   .time-toggle { | ||||||
|     transform: translateX(-50%) scaleX(-1); |     transform: translateX(-50%) scaleX(-1); | ||||||
|   } |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | :host-context(.ltr-layout) { | ||||||
|  |   .blockchain-wrapper.time-ltr .blocks-wrapper, | ||||||
|  |   .blockchain-wrapper .blocks-wrapper { | ||||||
|  |     direction: ltr; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | :host-context(.rtl-layout) { | ||||||
|  |   .blockchain-wrapper.time-ltr .blocks-wrapper, | ||||||
|  |   .blockchain-wrapper .blocks-wrapper { | ||||||
|  |     direction: rtl; | ||||||
|  |   } | ||||||
|  | } | ||||||
| @ -147,3 +147,9 @@ | |||||||
|     transform: scaleX(-1); |     transform: scaleX(-1); | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | :host-context(.rtl-layout) { | ||||||
|  |   #arrow-up { | ||||||
|  |     transform: translateX(70px); | ||||||
|  |   } | ||||||
|  | } | ||||||
| @ -287,11 +287,12 @@ export class MempoolBlocksComponent implements OnInit, OnDestroy { | |||||||
| 
 | 
 | ||||||
|     this.arrowVisible = true; |     this.arrowVisible = true; | ||||||
| 
 | 
 | ||||||
|     for (const block of this.mempoolBlocks) { |     let found = false; | ||||||
|       for (let i = 0; i < block.feeRange.length - 1; i++) { |     for (let txInBlockIndex = 0; txInBlockIndex < this.mempoolBlocks.length && !found; txInBlockIndex++) { | ||||||
|  |       const block = this.mempoolBlocks[txInBlockIndex]; | ||||||
|  |       for (let i = 0; i < block.feeRange.length - 1 && !found; i++) { | ||||||
|         if (this.txFeePerVSize < block.feeRange[i + 1] && this.txFeePerVSize >= block.feeRange[i]) { |         if (this.txFeePerVSize < block.feeRange[i + 1] && this.txFeePerVSize >= block.feeRange[i]) { | ||||||
|           const txInBlockIndex = this.mempoolBlocks.indexOf(block); |           const feeRangeIndex = i; | ||||||
|           const feeRangeIndex = block.feeRange.findIndex((val, index) => this.txFeePerVSize < block.feeRange[index + 1]); |  | ||||||
|           const feeRangeChunkSize = 1 / (block.feeRange.length - 1); |           const feeRangeChunkSize = 1 / (block.feeRange.length - 1); | ||||||
| 
 | 
 | ||||||
|           const txFee = this.txFeePerVSize - block.feeRange[i]; |           const txFee = this.txFeePerVSize - block.feeRange[i]; | ||||||
| @ -306,9 +307,13 @@ export class MempoolBlocksComponent implements OnInit, OnDestroy { | |||||||
|             + ((1 - feePosition) * blockedFilledPercentage * this.blockWidth); |             + ((1 - feePosition) * blockedFilledPercentage * this.blockWidth); | ||||||
| 
 | 
 | ||||||
|           this.rightPosition = arrowRightPosition; |           this.rightPosition = arrowRightPosition; | ||||||
|           break; |           found = true; | ||||||
|         } |         } | ||||||
|       } |       } | ||||||
|  |       if (this.txFeePerVSize >= block.feeRange[block.feeRange.length - 1]) { | ||||||
|  |         this.rightPosition = txInBlockIndex * (this.blockWidth + this.blockPadding); | ||||||
|  |         found = true; | ||||||
|  |       } | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -8,7 +8,7 @@ | |||||||
| 
 | 
 | ||||||
| <div *ngIf="countdown > 0" class="warning-label">{{ eventName }} in {{ countdown | number }} block{{ countdown === 1 ? '' : 's' }}!</div> | <div *ngIf="countdown > 0" class="warning-label">{{ eventName }} in {{ countdown | number }} block{{ countdown === 1 ? '' : 's' }}!</div> | ||||||
| 
 | 
 | ||||||
| <div id="blockchain-container" dir="ltr" #blockchainContainer | <div id="blockchain-container" [dir]="timeLtr ? 'rtl' : 'ltr'" #blockchainContainer | ||||||
|   (mousedown)="onMouseDown($event)" |   (mousedown)="onMouseDown($event)" | ||||||
|   (dragstart)="onDragStart($event)" |   (dragstart)="onDragStart($event)" | ||||||
| > | > | ||||||
|  | |||||||
| @ -1,4 +1,5 @@ | |||||||
| import { Component, ElementRef, HostListener, OnInit, ViewChild } from '@angular/core'; | import { Component, ElementRef, HostListener, OnInit, OnDestroy, ViewChild } from '@angular/core'; | ||||||
|  | import { Subscription } from 'rxjs'; | ||||||
| import { StateService } from '../../services/state.service'; | import { StateService } from '../../services/state.service'; | ||||||
| import { specialBlocks } from '../../app.constants'; | import { specialBlocks } from '../../app.constants'; | ||||||
| 
 | 
 | ||||||
| @ -7,7 +8,7 @@ import { specialBlocks } from '../../app.constants'; | |||||||
|   templateUrl: './start.component.html', |   templateUrl: './start.component.html', | ||||||
|   styleUrls: ['./start.component.scss'], |   styleUrls: ['./start.component.scss'], | ||||||
| }) | }) | ||||||
| export class StartComponent implements OnInit { | export class StartComponent implements OnInit, OnDestroy { | ||||||
|   interval = 60; |   interval = 60; | ||||||
|   colors = ['#5E35B1', '#ffffff']; |   colors = ['#5E35B1', '#ffffff']; | ||||||
| 
 | 
 | ||||||
| @ -16,6 +17,8 @@ export class StartComponent implements OnInit { | |||||||
|   eventName = ''; |   eventName = ''; | ||||||
|   mouseDragStartX: number; |   mouseDragStartX: number; | ||||||
|   blockchainScrollLeftInit: number; |   blockchainScrollLeftInit: number; | ||||||
|  |   timeLtrSubscription: Subscription; | ||||||
|  |   timeLtr: boolean = this.stateService.timeLtr.value; | ||||||
|   @ViewChild('blockchainContainer') blockchainContainer: ElementRef; |   @ViewChild('blockchainContainer') blockchainContainer: ElementRef; | ||||||
| 
 | 
 | ||||||
|   constructor( |   constructor( | ||||||
| @ -23,6 +26,9 @@ export class StartComponent implements OnInit { | |||||||
|   ) { } |   ) { } | ||||||
| 
 | 
 | ||||||
|   ngOnInit() { |   ngOnInit() { | ||||||
|  |     this.timeLtrSubscription = this.stateService.timeLtr.subscribe((ltr) => { | ||||||
|  |       this.timeLtr = !!ltr; | ||||||
|  |     }); | ||||||
|     this.stateService.blocks$ |     this.stateService.blocks$ | ||||||
|       .subscribe((blocks: any) => { |       .subscribe((blocks: any) => { | ||||||
|         if (this.stateService.network !== '') { |         if (this.stateService.network !== '') { | ||||||
| @ -72,4 +78,8 @@ export class StartComponent implements OnInit { | |||||||
|     this.mouseDragStartX = null; |     this.mouseDragStartX = null; | ||||||
|     this.stateService.setBlockScrollingInProgress(false); |     this.stateService.setBlockScrollingInProgress(false); | ||||||
|   } |   } | ||||||
|  | 
 | ||||||
|  |   ngOnDestroy() { | ||||||
|  |     this.timeLtrSubscription.unsubscribe(); | ||||||
|  |   } | ||||||
| } | } | ||||||
|  | |||||||
| @ -195,7 +195,7 @@ | |||||||
|         <h2 id="flow" i18n="transaction.flow|Transaction flow">Flow</h2> |         <h2 id="flow" i18n="transaction.flow|Transaction flow">Flow</h2> | ||||||
|       </div> |       </div> | ||||||
| 
 | 
 | ||||||
|       <button type="button" class="btn btn-outline-info flow-toggle btn-sm float-right" (click)="toggleGraph()" i18n="hide-flow-diagram">Hide flow diagram</button> |       <button type="button" class="btn btn-outline-info flow-toggle btn-sm float-right" (click)="toggleGraph()" i18n="hide-diagram">Hide diagram</button> | ||||||
| 
 | 
 | ||||||
|       <div class="clearfix"></div> |       <div class="clearfix"></div> | ||||||
| 
 | 
 | ||||||
| @ -208,7 +208,11 @@ | |||||||
|             [lineLimit]="inOutLimit" |             [lineLimit]="inOutLimit" | ||||||
|             [maxStrands]="graphExpanded ? maxInOut : 24" |             [maxStrands]="graphExpanded ? maxInOut : 24" | ||||||
|             [network]="network" |             [network]="network" | ||||||
|             [tooltip]="true"> |             [tooltip]="true" | ||||||
|  |             [inputIndex]="inputIndex" [outputIndex]="outputIndex" | ||||||
|  |             (selectInput)="selectInput($event)" | ||||||
|  |             (selectOutput)="selectOutput($event)" | ||||||
|  |           > | ||||||
|           </tx-bowtie-graph> |           </tx-bowtie-graph> | ||||||
|         </div> |         </div> | ||||||
|         <div class="toggle-wrapper" *ngIf="maxInOut > 24"> |         <div class="toggle-wrapper" *ngIf="maxInOut > 24"> | ||||||
| @ -234,13 +238,13 @@ | |||||||
|       </div> |       </div> | ||||||
| 
 | 
 | ||||||
|       <div class="title-buttons"> |       <div class="title-buttons"> | ||||||
|         <button *ngIf="!showFlow" type="button" class="btn btn-outline-info flow-toggle btn-sm" (click)="toggleGraph()" i18n="show">Show flow diagram</button> |         <button *ngIf="!showFlow" type="button" class="btn btn-outline-info flow-toggle btn-sm" (click)="toggleGraph()" i18n="show-diagram">Show diagram</button> | ||||||
|         <button type="button" class="btn btn-outline-info btn-sm" (click)="txList.toggleDetails()" i18n="transaction.details|Transaction Details">Details</button> |         <button type="button" class="btn btn-outline-info btn-sm" (click)="txList.toggleDetails()" i18n="transaction.details|Transaction Details">Details</button> | ||||||
|       </div> |       </div> | ||||||
|     </div> |     </div> | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|     <app-transactions-list #txList [transactions]="[tx]" [errorUnblinded]="errorUnblinded" [outputIndex]="outputIndex" [transactionPage]="true"></app-transactions-list> |     <app-transactions-list #txList [transactions]="[tx]" [errorUnblinded]="errorUnblinded" [inputIndex]="inputIndex" [outputIndex]="outputIndex" [transactionPage]="true"></app-transactions-list> | ||||||
| 
 | 
 | ||||||
|     <div class="title text-left"> |     <div class="title text-left"> | ||||||
|       <h2 i18n="transaction.details">Details</h2> |       <h2 i18n="transaction.details">Details</h2> | ||||||
|  | |||||||
| @ -3,36 +3,36 @@ | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| .container-buttons { | .container-buttons { | ||||||
|   align-self: center; |   align-self: flex-start; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| .title-block { | .title-block { | ||||||
|   flex-wrap: wrap; |   flex-wrap: wrap; | ||||||
|  |   align-items: baseline; | ||||||
|   @media (min-width: 650px) { |   @media (min-width: 650px) { | ||||||
|     flex-direction: row; |     flex-direction: row; | ||||||
|   } |   } | ||||||
|   h1 { |   h1 { | ||||||
|     margin: 0rem; |     margin: 0rem; | ||||||
|  |     margin-right: 15px; | ||||||
|     line-height: 1; |     line-height: 1; | ||||||
|   } |   } | ||||||
| } | } | ||||||
| .tx-link { | .tx-link { | ||||||
|   display: flex; |  | ||||||
| 	flex-grow: 1; |  | ||||||
|   margin-bottom: 0px; |   margin-bottom: 0px; | ||||||
|   margin-top: 8px; |   margin-top: 8px; | ||||||
| 	@media (min-width: 650px) { |   display: inline-block; | ||||||
|     align-self: end; |   width: 100%; | ||||||
|     margin-left: 15px; |   flex-shrink: 0; | ||||||
|     margin-top: 0px; |   @media (min-width: 651px) { | ||||||
|     margin-bottom: -3px; |     display: flex; | ||||||
| 	} |     width: auto; | ||||||
| 	@media (min-width: 768px) { |     flex-grow: 1; | ||||||
|     margin-bottom: 0px; |     margin-bottom: 0px; | ||||||
|     top: 1px; |     top: 1px; | ||||||
|     position: relative; |     position: relative; | ||||||
|   } |   } | ||||||
| 	@media (max-width: 768px) { |   @media (max-width: 650px) { | ||||||
|     order: 3; |     order: 3; | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  | |||||||
| @ -47,6 +47,7 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy { | |||||||
|   now = new Date().getTime(); |   now = new Date().getTime(); | ||||||
|   timeAvg$: Observable<number>; |   timeAvg$: Observable<number>; | ||||||
|   liquidUnblinding = new LiquidUnblinding(); |   liquidUnblinding = new LiquidUnblinding(); | ||||||
|  |   inputIndex: number; | ||||||
|   outputIndex: number; |   outputIndex: number; | ||||||
|   showFlow: boolean = true; |   showFlow: boolean = true; | ||||||
|   graphExpanded: boolean = false; |   graphExpanded: boolean = false; | ||||||
| @ -121,8 +122,15 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy { | |||||||
|       .pipe( |       .pipe( | ||||||
|         switchMap((params: ParamMap) => { |         switchMap((params: ParamMap) => { | ||||||
|           const urlMatch = (params.get('id') || '').split(':'); |           const urlMatch = (params.get('id') || '').split(':'); | ||||||
|  |           if (urlMatch.length === 2 && urlMatch[1].length === 64) { | ||||||
|  |             this.inputIndex = parseInt(urlMatch[0], 10); | ||||||
|  |             this.outputIndex = null; | ||||||
|  |             this.txId = urlMatch[1]; | ||||||
|  |           } else { | ||||||
|             this.txId = urlMatch[0]; |             this.txId = urlMatch[0]; | ||||||
|             this.outputIndex = urlMatch[1] === undefined ? null : parseInt(urlMatch[1], 10); |             this.outputIndex = urlMatch[1] === undefined ? null : parseInt(urlMatch[1], 10); | ||||||
|  |             this.inputIndex = null; | ||||||
|  |           } | ||||||
|           this.seoService.setTitle( |           this.seoService.setTitle( | ||||||
|             $localize`:@@bisq.transaction.browser-title:Transaction: ${this.txId}:INTERPOLATION:` |             $localize`:@@bisq.transaction.browser-title:Transaction: ${this.txId}:INTERPOLATION:` | ||||||
|           ); |           ); | ||||||
| @ -334,6 +342,16 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy { | |||||||
|     this.graphExpanded = false; |     this.graphExpanded = false; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  |   selectInput(input) { | ||||||
|  |     this.inputIndex = input; | ||||||
|  |     this.outputIndex = null; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   selectOutput(output) { | ||||||
|  |     this.outputIndex = output; | ||||||
|  |     this.inputIndex = null; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|   @HostListener('window:resize', ['$event']) |   @HostListener('window:resize', ['$event']) | ||||||
|   setGraphSize(): void { |   setGraphSize(): void { | ||||||
|     if (this.graphContainer) { |     if (this.graphContainer) { | ||||||
|  | |||||||
| @ -20,9 +20,9 @@ | |||||||
|       <div class="col"> |       <div class="col"> | ||||||
|         <table class="table table-borderless smaller-text table-sm table-tx-vin"> |         <table class="table table-borderless smaller-text table-sm table-tx-vin"> | ||||||
|           <tbody> |           <tbody> | ||||||
|             <ng-template ngFor let-vin let-vindex="index" [ngForOf]="tx['@vinLimit'] ? ((tx.vin.length > rowLimit) ? tx.vin.slice(0, rowLimit - 2) : tx.vin.slice(0, rowLimit)) : tx.vin" [ngForTrackBy]="trackByIndexFn"> |             <ng-template ngFor let-vin let-vindex="index" [ngForOf]="tx['@vinLimit'] ? ((tx.vin.length > inputRowLimit) ? tx.vin.slice(0, inputRowLimit - 2) : tx.vin.slice(0, inputRowLimit)) : tx.vin" [ngForTrackBy]="trackByIndexFn"> | ||||||
|               <tr [ngClass]="{ |               <tr [ngClass]="{ | ||||||
|                 'assetBox': assetsMinimal && vin.prevout && assetsMinimal[vin.prevout.asset] && !vin.is_coinbase && vin.prevout.scriptpubkey_address && tx._unblinded, |                 'assetBox': (assetsMinimal && vin.prevout && assetsMinimal[vin.prevout.asset] && !vin.is_coinbase && vin.prevout.scriptpubkey_address && tx._unblinded) || inputIndex === vindex, | ||||||
|                 'highlight': vin.prevout?.scriptpubkey_address === this.address && this.address !== '' |                 'highlight': vin.prevout?.scriptpubkey_address === this.address && this.address !== '' | ||||||
|               }"> |               }"> | ||||||
|                 <td class="arrow-td"> |                 <td class="arrow-td"> | ||||||
| @ -146,7 +146,7 @@ | |||||||
|                 </td> |                 </td> | ||||||
|               </tr> |               </tr> | ||||||
|             </ng-template> |             </ng-template> | ||||||
|             <tr *ngIf="tx.vin.length > rowLimit && tx['@vinLimit']"> |             <tr *ngIf="tx.vin.length > inputRowLimit && tx['@vinLimit']"> | ||||||
|               <td colspan="3" class="text-center"> |               <td colspan="3" class="text-center"> | ||||||
|                 <button class="btn btn-sm btn-primary mt-2" (click)="loadMoreInputs(tx);"><span i18n="show-all">Show all</span> ({{ tx.vin.length }})</button> |                 <button class="btn btn-sm btn-primary mt-2" (click)="loadMoreInputs(tx);"><span i18n="show-all">Show all</span> ({{ tx.vin.length }})</button> | ||||||
|               </td> |               </td> | ||||||
| @ -158,7 +158,7 @@ | |||||||
|       <div class="col mobile-bottomcol"> |       <div class="col mobile-bottomcol"> | ||||||
|         <table class="table table-borderless smaller-text table-sm table-tx-vout"> |         <table class="table table-borderless smaller-text table-sm table-tx-vout"> | ||||||
|           <tbody> |           <tbody> | ||||||
|             <ng-template ngFor let-vout let-vindex="index" [ngForOf]="tx['@voutLimit'] && !outputIndex ? ((tx.vout.length > rowLimit) ? tx.vout.slice(0, rowLimit - 2) : tx.vout.slice(0, rowLimit)) : tx.vout" [ngForTrackBy]="trackByIndexFn"> |             <ng-template ngFor let-vout let-vindex="index" [ngForOf]="tx['@voutLimit'] ? ((tx.vout.length > outputRowLimit) ? tx.vout.slice(0, outputRowLimit - 2) : tx.vout.slice(0, outputRowLimit)) : tx.vout" [ngForTrackBy]="trackByIndexFn"> | ||||||
|               <tr [ngClass]="{ |               <tr [ngClass]="{ | ||||||
|                 'assetBox': assetsMinimal && assetsMinimal[vout.asset] && vout.scriptpubkey_address && tx.vin && !tx.vin[0].is_coinbase && tx._unblinded || outputIndex === vindex, |                 'assetBox': assetsMinimal && assetsMinimal[vout.asset] && vout.scriptpubkey_address && tx.vin && !tx.vin[0].is_coinbase && tx._unblinded || outputIndex === vindex, | ||||||
|                 'highlight': vout.scriptpubkey_address === this.address && this.address !== '' |                 'highlight': vout.scriptpubkey_address === this.address && this.address !== '' | ||||||
| @ -220,7 +220,7 @@ | |||||||
|                       <fa-icon [icon]="['fas', 'arrow-alt-circle-right']" [fixedWidth]="true"></fa-icon> |                       <fa-icon [icon]="['fas', 'arrow-alt-circle-right']" [fixedWidth]="true"></fa-icon> | ||||||
|                     </span> |                     </span> | ||||||
|                     <ng-template #spent> |                     <ng-template #spent> | ||||||
|                       <a *ngIf="tx._outspends[vindex].txid else outputNoTxId" [routerLink]="['/tx/' | relativeUrl, tx._outspends[vindex].txid]" class="red"> |                       <a *ngIf="tx._outspends[vindex].txid else outputNoTxId" [routerLink]="['/tx/' | relativeUrl, tx._outspends[vindex].vin + ':' + tx._outspends[vindex].txid]" class="red"> | ||||||
|                         <fa-icon [icon]="['fas', 'arrow-alt-circle-right']" [fixedWidth]="true"></fa-icon> |                         <fa-icon [icon]="['fas', 'arrow-alt-circle-right']" [fixedWidth]="true"></fa-icon> | ||||||
|                       </a> |                       </a> | ||||||
|                       <ng-template #outputNoTxId> |                       <ng-template #outputNoTxId> | ||||||
| @ -257,7 +257,7 @@ | |||||||
|                 </td> |                 </td> | ||||||
|               </tr> |               </tr> | ||||||
|             </ng-template> |             </ng-template> | ||||||
|             <tr *ngIf="tx.vout.length > rowLimit && tx['@voutLimit'] && !outputIndex"> |             <tr *ngIf="tx.vout.length > outputRowLimit && tx['@voutLimit']"> | ||||||
|               <td colspan="3" class="text-center"> |               <td colspan="3" class="text-center"> | ||||||
|                 <button class="btn btn-sm btn-primary mt-2" (click)="tx['@voutLimit'] = false;"><span i18n="show-all">Show all</span> ({{ tx.vout.length }})</button> |                 <button class="btn btn-sm btn-primary mt-2" (click)="tx['@voutLimit'] = false;"><span i18n="show-all">Show all</span> ({{ tx.vout.length }})</button> | ||||||
|               </td> |               </td> | ||||||
|  | |||||||
| @ -24,6 +24,7 @@ export class TransactionsListComponent implements OnInit, OnChanges { | |||||||
|   @Input() transactionPage = false; |   @Input() transactionPage = false; | ||||||
|   @Input() errorUnblinded = false; |   @Input() errorUnblinded = false; | ||||||
|   @Input() paginated = false; |   @Input() paginated = false; | ||||||
|  |   @Input() inputIndex: number; | ||||||
|   @Input() outputIndex: number; |   @Input() outputIndex: number; | ||||||
|   @Input() address: string = ''; |   @Input() address: string = ''; | ||||||
|   @Input() rowLimit = 12; |   @Input() rowLimit = 12; | ||||||
| @ -37,6 +38,8 @@ export class TransactionsListComponent implements OnInit, OnChanges { | |||||||
|   showDetails$ = new BehaviorSubject<boolean>(false); |   showDetails$ = new BehaviorSubject<boolean>(false); | ||||||
|   assetsMinimal: any; |   assetsMinimal: any; | ||||||
|   transactionsLength: number = 0; |   transactionsLength: number = 0; | ||||||
|  |   inputRowLimit: number = 12; | ||||||
|  |   outputRowLimit: number = 12; | ||||||
| 
 | 
 | ||||||
|   constructor( |   constructor( | ||||||
|     public stateService: StateService, |     public stateService: StateService, | ||||||
| @ -97,20 +100,26 @@ export class TransactionsListComponent implements OnInit, OnChanges { | |||||||
|     ).subscribe(() => this.ref.markForCheck()); |     ).subscribe(() => this.ref.markForCheck()); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   ngOnChanges(): void { |   ngOnChanges(changes): void { | ||||||
|  |     if (changes.inputIndex || changes.outputIndex || changes.rowLimit) { | ||||||
|  |       this.inputRowLimit = Math.max(this.rowLimit, (this.inputIndex || 0) + 3); | ||||||
|  |       this.outputRowLimit = Math.max(this.rowLimit, (this.outputIndex || 0) + 3); | ||||||
|  |       if ((this.inputIndex || this.outputIndex) && !changes.transactions) { | ||||||
|  |         setTimeout(() => { | ||||||
|  |           const assetBoxElements = document.getElementsByClassName('assetBox'); | ||||||
|  |           if (assetBoxElements && assetBoxElements[0]) { | ||||||
|  |             assetBoxElements[0].scrollIntoView({block: "center"}); | ||||||
|  |           } | ||||||
|  |         }, 10); | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     if (changes.transactions || changes.address) { | ||||||
|       if (!this.transactions || !this.transactions.length) { |       if (!this.transactions || !this.transactions.length) { | ||||||
|         return; |         return; | ||||||
|       } |       } | ||||||
| 
 | 
 | ||||||
|       this.transactionsLength = this.transactions.length; |       this.transactionsLength = this.transactions.length; | ||||||
|     if (this.outputIndex) { | 
 | ||||||
|       setTimeout(() => { |  | ||||||
|         const assetBoxElements = document.getElementsByClassName('assetBox'); |  | ||||||
|         if (assetBoxElements && assetBoxElements[0]) { |  | ||||||
|           assetBoxElements[0].scrollIntoView(); |  | ||||||
|         } |  | ||||||
|       }, 10); |  | ||||||
|     } |  | ||||||
| 
 | 
 | ||||||
|       this.transactions.forEach((tx) => { |       this.transactions.forEach((tx) => { | ||||||
|         tx['@voutLimit'] = true; |         tx['@voutLimit'] = true; | ||||||
| @ -144,6 +153,7 @@ export class TransactionsListComponent implements OnInit, OnChanges { | |||||||
|         } |         } | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
|  |   } | ||||||
| 
 | 
 | ||||||
|   onScroll(): void { |   onScroll(): void { | ||||||
|     const scrollHeight = document.body.scrollHeight; |     const scrollHeight = document.body.scrollHeight; | ||||||
|  | |||||||
| @ -44,7 +44,7 @@ | |||||||
|           <span *ngSwitchCase="'output'" i18n="transaction.output">Output</span> |           <span *ngSwitchCase="'output'" i18n="transaction.output">Output</span> | ||||||
|           <span *ngSwitchCase="'fee'" i18n="transaction.fee">Fee</span> |           <span *ngSwitchCase="'fee'" i18n="transaction.fee">Fee</span> | ||||||
|         </ng-container> |         </ng-container> | ||||||
|         <span *ngIf="line.type !== 'fee'"> #{{ line.index }}</span> |         <span *ngIf="line.type !== 'fee'"> #{{ line.index + 1 }}</span> | ||||||
|       </p> |       </p> | ||||||
|       <p *ngIf="line.value == null && line.confidential" i18n="shared.confidential">Confidential</p> |       <p *ngIf="line.value == null && line.confidential" i18n="shared.confidential">Confidential</p> | ||||||
|       <p *ngIf="line.value != null"><app-amount [satoshis]="line.value"></app-amount></p> |       <p *ngIf="line.value != null"><app-amount [satoshis]="line.value"></app-amount></p> | ||||||
|  | |||||||
| @ -41,6 +41,18 @@ | |||||||
|         <stop offset="98%" [attr.stop-color]="gradient[0]" /> |         <stop offset="98%" [attr.stop-color]="gradient[0]" /> | ||||||
|         <stop offset="100%" [attr.stop-color]="gradient[0]" /> |         <stop offset="100%" [attr.stop-color]="gradient[0]" /> | ||||||
|       </linearGradient> |       </linearGradient> | ||||||
|  |       <linearGradient id="input-highlight-gradient" x1="0%" y1="0%" x2="100%" y2="0%"> | ||||||
|  |       <stop offset="0%" [attr.stop-color]="gradient[0]" /> | ||||||
|  |       <stop offset="2%" [attr.stop-color]="gradient[0]" /> | ||||||
|  |         <stop offset="30%" stop-color="#1bd8f4" /> | ||||||
|  |         <stop offset="100%" [attr.stop-color]="gradient[1]" /> | ||||||
|  |       </linearGradient> | ||||||
|  |       <linearGradient id="output-highlight-gradient" x1="0%" y1="0%" x2="100%" y2="0%"> | ||||||
|  |         <stop offset="0%" [attr.stop-color]="gradient[1]" /> | ||||||
|  |         <stop offset="70%" stop-color="#1bd8f4" /> | ||||||
|  |         <stop offset="98%" [attr.stop-color]="gradient[0]" /> | ||||||
|  |         <stop offset="100%" [attr.stop-color]="gradient[0]" /> | ||||||
|  |       </linearGradient> | ||||||
|       <linearGradient id="fee-hover-gradient" x1="0%" y1="0%" x2="100%" y2="0%"> |       <linearGradient id="fee-hover-gradient" x1="0%" y1="0%" x2="100%" y2="0%"> | ||||||
|         <stop offset="0%" [attr.stop-color]="gradient[1]" /> |         <stop offset="0%" [attr.stop-color]="gradient[1]" /> | ||||||
|         <stop offset="100%" stop-color="white" /> |         <stop offset="100%" stop-color="white" /> | ||||||
| @ -56,20 +68,24 @@ | |||||||
|       <path |       <path | ||||||
|         [attr.d]="input.path" |         [attr.d]="input.path" | ||||||
|         class="line {{input.class}}" |         class="line {{input.class}}" | ||||||
|  |         [class.highlight]="inputData[i].index === inputIndex" | ||||||
|         [style]="input.style" |         [style]="input.style" | ||||||
|         attr.marker-start="url(#{{input.class}}-arrow)" |         attr.marker-start="url(#{{input.class}}-arrow)" | ||||||
|         (pointerover)="onHover($event, 'input', i);" |         (pointerover)="onHover($event, 'input', i);" | ||||||
|         (pointerout)="onBlur($event, 'input', i);" |         (pointerout)="onBlur($event, 'input', i);" | ||||||
|  |         (click)="onClick($event, 'input', inputData[i].index);" | ||||||
|       /> |       /> | ||||||
|     </ng-container> |     </ng-container> | ||||||
|     <ng-container *ngFor="let output of outputs; let i = index"> |     <ng-container *ngFor="let output of outputs; let i = index"> | ||||||
|       <path |       <path | ||||||
|         [attr.d]="output.path" |         [attr.d]="output.path" | ||||||
|         class="line {{output.class}}" |         class="line {{output.class}}" | ||||||
|  |         [class.highlight]="outputData[i].index === outputIndex" | ||||||
|         [style]="output.style" |         [style]="output.style" | ||||||
|         attr.marker-start="url(#{{output.class}}-arrow)" |         attr.marker-start="url(#{{output.class}}-arrow)" | ||||||
|         (pointerover)="onHover($event, 'output', i);" |         (pointerover)="onHover($event, 'output', i);" | ||||||
|         (pointerout)="onBlur($event, 'output', i);" |         (pointerout)="onBlur($event, 'output', i);" | ||||||
|  |         (click)="onClick($event, 'output', outputData[i].index);" | ||||||
|       /> |       /> | ||||||
|     </ng-container> |     </ng-container> | ||||||
|   </svg> |   </svg> | ||||||
|  | |||||||
| @ -12,6 +12,17 @@ | |||||||
|       stroke: url(#fee-gradient); |       stroke: url(#fee-gradient); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     &.highlight { | ||||||
|  |       z-index: 8; | ||||||
|  |       cursor: pointer; | ||||||
|  |       &.input { | ||||||
|  |         stroke: url(#input-highlight-gradient); | ||||||
|  |       } | ||||||
|  |       &.output { | ||||||
|  |         stroke: url(#output-highlight-gradient); | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     &:hover { |     &:hover { | ||||||
|       z-index: 10; |       z-index: 10; | ||||||
|       cursor: pointer; |       cursor: pointer; | ||||||
|  | |||||||
| @ -1,5 +1,11 @@ | |||||||
| import { Component, OnInit, Input, OnChanges, HostListener } from '@angular/core'; | import { Component, OnInit, Input, Output, EventEmitter, OnChanges, HostListener } from '@angular/core'; | ||||||
| import { Transaction } from '../../interfaces/electrs.interface'; | import { StateService } from '../../services/state.service'; | ||||||
|  | import { Outspend, Transaction } from '../../interfaces/electrs.interface'; | ||||||
|  | import { Router } from '@angular/router'; | ||||||
|  | import { ReplaySubject, merge, Subscription } from 'rxjs'; | ||||||
|  | import { tap, switchMap } from 'rxjs/operators'; | ||||||
|  | import { ApiService } from '../../services/api.service'; | ||||||
|  | import { RelativeUrlPipe } from '../../shared/pipes/relative-url/relative-url.pipe'; | ||||||
| 
 | 
 | ||||||
| interface SvgLine { | interface SvgLine { | ||||||
|   path: string; |   path: string; | ||||||
| @ -34,6 +40,11 @@ export class TxBowtieGraphComponent implements OnInit, OnChanges { | |||||||
|   @Input() minWeight = 2; //
 |   @Input() minWeight = 2; //
 | ||||||
|   @Input() maxStrands = 24; // number of inputs/outputs to keep fully on-screen.
 |   @Input() maxStrands = 24; // number of inputs/outputs to keep fully on-screen.
 | ||||||
|   @Input() tooltip = false; |   @Input() tooltip = false; | ||||||
|  |   @Input() inputIndex: number; | ||||||
|  |   @Input() outputIndex: number; | ||||||
|  | 
 | ||||||
|  |   @Output() selectInput = new EventEmitter<number>(); | ||||||
|  |   @Output() selectOutput = new EventEmitter<number>(); | ||||||
| 
 | 
 | ||||||
|   inputData: Xput[]; |   inputData: Xput[]; | ||||||
|   outputData: Xput[]; |   outputData: Xput[]; | ||||||
| @ -45,6 +56,10 @@ export class TxBowtieGraphComponent implements OnInit, OnChanges { | |||||||
|   isLiquid: boolean = false; |   isLiquid: boolean = false; | ||||||
|   hoverLine: Xput | void = null; |   hoverLine: Xput | void = null; | ||||||
|   tooltipPosition = { x: 0, y: 0 }; |   tooltipPosition = { x: 0, y: 0 }; | ||||||
|  |   outspends: Outspend[] = []; | ||||||
|  | 
 | ||||||
|  |   outspendsSubscription: Subscription; | ||||||
|  |   refreshOutspends$: ReplaySubject<string> = new ReplaySubject(); | ||||||
| 
 | 
 | ||||||
|   gradientColors = { |   gradientColors = { | ||||||
|     '': ['#9339f4', '#105fb0'], |     '': ['#9339f4', '#105fb0'], | ||||||
| @ -61,12 +76,45 @@ export class TxBowtieGraphComponent implements OnInit, OnChanges { | |||||||
| 
 | 
 | ||||||
|   gradient: string[] = ['#105fb0', '#105fb0']; |   gradient: string[] = ['#105fb0', '#105fb0']; | ||||||
| 
 | 
 | ||||||
|  |   constructor( | ||||||
|  |     private router: Router, | ||||||
|  |     private relativeUrlPipe: RelativeUrlPipe, | ||||||
|  |     private stateService: StateService, | ||||||
|  |     private apiService: ApiService, | ||||||
|  |   ) { } | ||||||
|  | 
 | ||||||
|   ngOnInit(): void { |   ngOnInit(): void { | ||||||
|     this.initGraph(); |     this.initGraph(); | ||||||
|  | 
 | ||||||
|  |     this.outspendsSubscription = merge( | ||||||
|  |       this.refreshOutspends$ | ||||||
|  |         .pipe( | ||||||
|  |           switchMap((txid) => this.apiService.getOutspendsBatched$([txid])), | ||||||
|  |           tap((outspends: Outspend[][]) => { | ||||||
|  |             if (!this.tx || !outspends || !outspends.length) { | ||||||
|  |               return; | ||||||
|  |             } | ||||||
|  |             this.outspends = outspends[0]; | ||||||
|  |           }), | ||||||
|  |         ), | ||||||
|  |       this.stateService.utxoSpent$ | ||||||
|  |         .pipe( | ||||||
|  |           tap((utxoSpent) => { | ||||||
|  |             for (const i in utxoSpent) { | ||||||
|  |               this.outspends[i] = { | ||||||
|  |                 spent: true, | ||||||
|  |                 txid: utxoSpent[i].txid, | ||||||
|  |                 vin: utxoSpent[i].vin, | ||||||
|  |               }; | ||||||
|  |             } | ||||||
|  |           }), | ||||||
|  |         ), | ||||||
|  |     ).subscribe(() => {}); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   ngOnChanges(): void { |   ngOnChanges(): void { | ||||||
|     this.initGraph(); |     this.initGraph(); | ||||||
|  |     this.refreshOutspends$.next(this.tx.txid); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   initGraph(): void { |   initGraph(): void { | ||||||
| @ -76,11 +124,12 @@ export class TxBowtieGraphComponent implements OnInit, OnChanges { | |||||||
|     this.combinedWeight = Math.min(this.maxCombinedWeight, Math.floor((this.width - (2 * this.midWidth)) / 6)); |     this.combinedWeight = Math.min(this.maxCombinedWeight, Math.floor((this.width - (2 * this.midWidth)) / 6)); | ||||||
| 
 | 
 | ||||||
|     const totalValue = this.calcTotalValue(this.tx); |     const totalValue = this.calcTotalValue(this.tx); | ||||||
|     let voutWithFee = this.tx.vout.map(v => { |     let voutWithFee = this.tx.vout.map((v, i) => { | ||||||
|       return { |       return { | ||||||
|         type: v.scriptpubkey_type === 'fee' ? 'fee' : 'output', |         type: v.scriptpubkey_type === 'fee' ? 'fee' : 'output', | ||||||
|         value: v?.value, |         value: v?.value, | ||||||
|         address: v?.scriptpubkey_address || v?.scriptpubkey_type?.toUpperCase(), |         address: v?.scriptpubkey_address || v?.scriptpubkey_type?.toUpperCase(), | ||||||
|  |         index: i, | ||||||
|         pegout: v?.pegout?.scriptpubkey_address, |         pegout: v?.pegout?.scriptpubkey_address, | ||||||
|         confidential: (this.isLiquid && v?.value === undefined), |         confidential: (this.isLiquid && v?.value === undefined), | ||||||
|       } as Xput; |       } as Xput; | ||||||
| @ -91,11 +140,12 @@ export class TxBowtieGraphComponent implements OnInit, OnChanges { | |||||||
|     } |     } | ||||||
|     const outputCount = voutWithFee.length; |     const outputCount = voutWithFee.length; | ||||||
| 
 | 
 | ||||||
|     let truncatedInputs = this.tx.vin.map(v => { |     let truncatedInputs = this.tx.vin.map((v, i) => { | ||||||
|       return { |       return { | ||||||
|         type: 'input', |         type: 'input', | ||||||
|         value: v?.prevout?.value, |         value: v?.prevout?.value, | ||||||
|         address: v?.prevout?.scriptpubkey_address || v?.prevout?.scriptpubkey_type?.toUpperCase(), |         address: v?.prevout?.scriptpubkey_address || v?.prevout?.scriptpubkey_type?.toUpperCase(), | ||||||
|  |         index: i, | ||||||
|         coinbase: v?.is_coinbase, |         coinbase: v?.is_coinbase, | ||||||
|         pegin: v?.is_pegin, |         pegin: v?.is_pegin, | ||||||
|         confidential: (this.isLiquid && v?.prevout?.value === undefined), |         confidential: (this.isLiquid && v?.prevout?.value === undefined), | ||||||
| @ -306,8 +356,7 @@ export class TxBowtieGraphComponent implements OnInit, OnChanges { | |||||||
|       }; |       }; | ||||||
|     } else { |     } else { | ||||||
|       this.hoverLine = { |       this.hoverLine = { | ||||||
|         ...this.outputData[index], |         ...this.outputData[index] | ||||||
|         index |  | ||||||
|       }; |       }; | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| @ -315,4 +364,29 @@ export class TxBowtieGraphComponent implements OnInit, OnChanges { | |||||||
|   onBlur(event, side, index): void { |   onBlur(event, side, index): void { | ||||||
|     this.hoverLine = null; |     this.hoverLine = null; | ||||||
|   } |   } | ||||||
|  | 
 | ||||||
|  |   onClick(event, side, index): void { | ||||||
|  |     if (side === 'input') { | ||||||
|  |       const input = this.tx.vin[index]; | ||||||
|  |       if (input && input.txid && input.vout != null) { | ||||||
|  |         this.router.navigate([this.relativeUrlPipe.transform('/tx'), input.txid + ':' + input.vout], { | ||||||
|  |           queryParamsHandling: 'merge', | ||||||
|  |           fragment: 'flow' | ||||||
|  |         }); | ||||||
|  |       } else { | ||||||
|  |         this.selectInput.emit(index); | ||||||
|  |       } | ||||||
|  |     } else { | ||||||
|  |       const output = this.tx.vout[index]; | ||||||
|  |       const outspend = this.outspends[index]; | ||||||
|  |       if (output && outspend && outspend.spent && outspend.txid) { | ||||||
|  |         this.router.navigate([this.relativeUrlPipe.transform('/tx'), outspend.vin + ':' + outspend.txid], { | ||||||
|  |           queryParamsHandling: 'merge', | ||||||
|  |           fragment: 'flow' | ||||||
|  |         }); | ||||||
|  |       } else { | ||||||
|  |         this.selectOutput.emit(index); | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } | ||||||
| } | } | ||||||
|  | |||||||
| @ -1,9 +1,9 @@ | |||||||
| <div class="widget-toggler"> | <div class="widget-toggler"> | ||||||
|   <a href="javascript:;" (click)="switchMode('avg')" class="toggler-option" |   <a href="" (click)="switchMode('avg')" class="toggler-option" | ||||||
|     [ngClass]="{'inactive': mode !== 'avg'}"><small>avg</small></a> |     [ngClass]="{'inactive': mode === 'avg'}"><small>avg</small></a> | ||||||
|   <span style="color: #ffffff66; font-size: 8px"> | </span> |   <span style="color: #ffffff66; font-size: 8px"> | </span> | ||||||
|   <a href="javascript:;" (click)="switchMode('med')" class="toggler-option" |   <a href="" (click)="switchMode('med')" class="toggler-option" | ||||||
|     [ngClass]="{'inactive': mode !== 'med'}"><small>med</small></a> |     [ngClass]="{'inactive': mode === 'med'}"><small>med</small></a> | ||||||
| </div> | </div> | ||||||
| 
 | 
 | ||||||
| <div class="fee-estimation-wrapper" *ngIf="statistics$ | async as statistics; else loadingReward"> | <div class="fee-estimation-wrapper" *ngIf="statistics$ | async as statistics; else loadingReward"> | ||||||
|  | |||||||
| @ -18,5 +18,6 @@ export class ChannelsStatisticsComponent implements OnInit { | |||||||
| 
 | 
 | ||||||
|   switchMode(mode: 'avg' | 'med') { |   switchMode(mode: 'avg' | 'med') { | ||||||
|     this.mode = mode; |     this.mode = mode; | ||||||
|  |     return false; | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  | |||||||
| @ -1,4 +1,4 @@ | |||||||
| import { Inject, Injectable, PLATFORM_ID } from '@angular/core'; | import { Inject, Injectable, PLATFORM_ID, LOCALE_ID } from '@angular/core'; | ||||||
| import { ReplaySubject, BehaviorSubject, Subject, fromEvent, Observable } from 'rxjs'; | import { ReplaySubject, BehaviorSubject, Subject, fromEvent, Observable } from 'rxjs'; | ||||||
| import { Transaction } from '../interfaces/electrs.interface'; | import { Transaction } from '../interfaces/electrs.interface'; | ||||||
| import { IBackendInfo, MempoolBlock, MempoolBlockWithTransactions, MempoolBlockDelta, MempoolInfo, Recommendedfees, ReplacedTransaction, TransactionStripped } from '../interfaces/websocket.interface'; | import { IBackendInfo, MempoolBlock, MempoolBlockWithTransactions, MempoolBlockDelta, MempoolInfo, Recommendedfees, ReplacedTransaction, TransactionStripped } from '../interfaces/websocket.interface'; | ||||||
| @ -113,6 +113,7 @@ export class StateService { | |||||||
| 
 | 
 | ||||||
|   constructor( |   constructor( | ||||||
|     @Inject(PLATFORM_ID) private platformId: any, |     @Inject(PLATFORM_ID) private platformId: any, | ||||||
|  |     @Inject(LOCALE_ID) private locale: string, | ||||||
|     private router: Router, |     private router: Router, | ||||||
|     private storageService: StorageService, |     private storageService: StorageService, | ||||||
|   ) { |   ) { | ||||||
| @ -151,7 +152,10 @@ export class StateService { | |||||||
| 
 | 
 | ||||||
|     this.blockVSize = this.env.BLOCK_WEIGHT_UNITS / 4; |     this.blockVSize = this.env.BLOCK_WEIGHT_UNITS / 4; | ||||||
| 
 | 
 | ||||||
|     this.timeLtr = new BehaviorSubject<boolean>(this.storageService.getValue('time-preference-ltr') === 'true'); |     const savedTimePreference = this.storageService.getValue('time-preference-ltr'); | ||||||
|  |     const rtlLanguage = (this.locale.startsWith('ar') || this.locale.startsWith('fa') || this.locale.startsWith('he')); | ||||||
|  |     // default time direction is right-to-left, unless locale is a RTL language
 | ||||||
|  |     this.timeLtr = new BehaviorSubject<boolean>(savedTimePreference === 'true' || (savedTimePreference == null && rtlLanguage)); | ||||||
|     this.timeLtr.subscribe((ltr) => { |     this.timeLtr.subscribe((ltr) => { | ||||||
|       this.storageService.setValue('time-preference-ltr', ltr ? 'true' : 'false'); |       this.storageService.setValue('time-preference-ltr', ltr ? 'true' : 'false'); | ||||||
|     }); |     }); | ||||||
|  | |||||||
| @ -112,14 +112,14 @@ class Server { | |||||||
|         const screenshot = await page.screenshot(); |         const screenshot = await page.screenshot(); | ||||||
|         return screenshot; |         return screenshot; | ||||||
|       } else if (success === false) { |       } else if (success === false) { | ||||||
|         logger.warn(`failed to render page preview for ${action} due to client-side error, e.g. requested an invalid txid`); |         logger.warn(`failed to render ${path} for ${action} due to client-side error, e.g. requested an invalid txid`); | ||||||
|         page.repairRequested = true; |         page.repairRequested = true; | ||||||
|       } else { |       } else { | ||||||
|         logger.warn(`failed to render page preview for ${action} due to puppeteer timeout`); |         logger.warn(`failed to render ${path} for ${action} due to puppeteer timeout`); | ||||||
|         page.repairRequested = true; |         page.repairRequested = true; | ||||||
|       } |       } | ||||||
|     } catch (e) { |     } catch (e) { | ||||||
|       logger.err(`failed to render page for ${action}: ` + (e instanceof Error ? e.message : `${e}`)); |       logger.err(`failed to render ${path} for ${action}: ` + (e instanceof Error ? e.message : `${e}`)); | ||||||
|       page.repairRequested = true; |       page.repairRequested = true; | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| @ -154,7 +154,7 @@ class Server { | |||||||
|         res.send(img); |         res.send(img); | ||||||
|       } |       } | ||||||
|     } catch (e) { |     } catch (e) { | ||||||
|       logger.err(e instanceof Error ? e.message : `${e}`); |       logger.err(e instanceof Error ? e.message : `${e} ${req.params[0]}`); | ||||||
|       res.status(500).send(e instanceof Error ? e.message : e); |       res.status(500).send(e instanceof Error ? e.message : e); | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user