Refactor transaction page component
This commit is contained in:
		
							parent
							
								
									a22d07ae60
								
							
						
					
					
						commit
						23475c7a1b
					
				| @ -0,0 +1,324 @@ | |||||||
|  | <div class="box"> | ||||||
|  |   <div class="row"> | ||||||
|  |     @if (isMobile) { | ||||||
|  |       <div class="col-sm"> | ||||||
|  |         <table class="table table-borderless table-striped"> | ||||||
|  |           <tbody> | ||||||
|  |               <ng-container *ngTemplateOutlet="detailsLeft"></ng-container> | ||||||
|  |               <ng-container *ngTemplateOutlet="detailsRight"></ng-container> | ||||||
|  |           </tbody> | ||||||
|  |         </table> | ||||||
|  |       </div> | ||||||
|  |     } @else { | ||||||
|  |       <div class="col-sm"> | ||||||
|  |         <table class="table table-borderless table-striped"> | ||||||
|  |           <tbody> | ||||||
|  |               <ng-container *ngTemplateOutlet="detailsLeft"></ng-container> | ||||||
|  |           </tbody> | ||||||
|  |         </table> | ||||||
|  |       </div> | ||||||
|  |       <div class="col-sm"> | ||||||
|  |         <table class="table table-borderless table-striped"> | ||||||
|  |           <tbody> | ||||||
|  |               <ng-container *ngTemplateOutlet="detailsRight"></ng-container> | ||||||
|  |           </tbody> | ||||||
|  |         </table> | ||||||
|  |       </div> | ||||||
|  |     } | ||||||
|  |   </div> | ||||||
|  | </div> | ||||||
|  | 
 | ||||||
|  | <ng-template #detailsLeft> | ||||||
|  |   @if (tx?.status?.confirmed) { | ||||||
|  |     <ng-container *ngTemplateOutlet="timestampRow"></ng-container> | ||||||
|  |     <ng-container *ngTemplateOutlet="confirmedAfterRow"></ng-container> | ||||||
|  |   } @else { | ||||||
|  |     <ng-container *ngTemplateOutlet="firstSeenRow"></ng-container> | ||||||
|  |     <ng-container *ngTemplateOutlet="etaRow"></ng-container> | ||||||
|  |   } | ||||||
|  |   <ng-container *ngTemplateOutlet="featuresRow"></ng-container> | ||||||
|  |   @if (tx?.status?.confirmed) { | ||||||
|  |     <ng-container *ngTemplateOutlet="auditRow"></ng-container> | ||||||
|  |   } | ||||||
|  |   <ng-container *ngTemplateOutlet="gogglesRow"></ng-container> | ||||||
|  | </ng-template> | ||||||
|  | 
 | ||||||
|  | <ng-template #detailsRight> | ||||||
|  |   <ng-container *ngTemplateOutlet="feeRow"></ng-container> | ||||||
|  |   <ng-container *ngTemplateOutlet="feeRateRow"></ng-container> | ||||||
|  |   @if (!isLoadingTx && !tx?.status?.confirmed && isAcceleration && ((cpfpInfo && hasEffectiveFeeRate) || accelerationInfo)) { | ||||||
|  |     <ng-container *ngTemplateOutlet="acceleratingRow"></ng-container> | ||||||
|  |   } @else { | ||||||
|  |     <ng-container *ngTemplateOutlet="effectiveRateRow"></ng-container> | ||||||
|  |   } | ||||||
|  |   @if (tx?.status?.confirmed) { | ||||||
|  |     <ng-container *ngTemplateOutlet="minerRow"></ng-container> | ||||||
|  |   } | ||||||
|  | </ng-template> | ||||||
|  | 
 | ||||||
|  | <ng-template #timestampRow> | ||||||
|  |   @if (!isLoadingTx) { | ||||||
|  |     <tr> | ||||||
|  |       <td i18n="block.timestamp">Timestamp</td> | ||||||
|  |       <td> | ||||||
|  |         ‎{{ tx.status.block_time * 1000 | date:'yyyy-MM-dd HH:mm:ss' }} | ||||||
|  |         <div class="lg-inline"> | ||||||
|  |           <i class="symbol">(<app-time kind="since" [time]="tx.status.block_time" [fastRender]="true"></app-time>)</i> | ||||||
|  |         </div> | ||||||
|  |       </td> | ||||||
|  |     </tr> | ||||||
|  |   } @else { | ||||||
|  |     <ng-container *ngTemplateOutlet="skeletonDetailsRow"></ng-container> | ||||||
|  |   } | ||||||
|  | </ng-template> | ||||||
|  | 
 | ||||||
|  | <ng-template #confirmedAfterRow> | ||||||
|  |   @if (!isLoadingTx) { | ||||||
|  |     @if (transactionTime > 0) { | ||||||
|  |       <tr> | ||||||
|  |         <td i18n="transaction.confirmed|Transaction Confirmed state">Confirmed</td> | ||||||
|  |         <td><app-time kind="span" [time]="tx.status.block_time - transactionTime" [fastRender]="true" [showTooltip]="true"></app-time></td> | ||||||
|  |       </tr> | ||||||
|  |     } | ||||||
|  |   } @else { | ||||||
|  |     <ng-container *ngTemplateOutlet="skeletonDetailsRow"></ng-container> | ||||||
|  |   } | ||||||
|  | </ng-template> | ||||||
|  | 
 | ||||||
|  | <ng-template #firstSeenRow> | ||||||
|  |   @if (isLoadingTx) { | ||||||
|  |     <ng-container *ngTemplateOutlet="skeletonDetailsRow"></ng-container> | ||||||
|  |   } @else if (transactionTime > 0) { | ||||||
|  |     <tr> | ||||||
|  |       <td i18n="transaction.first-seen|Transaction first seen">First seen</td> | ||||||
|  |       <td><i><app-time kind="since" [time]="transactionTime" [fastRender]="true" [showTooltip]="true"></app-time></i></td> | ||||||
|  |     </tr> | ||||||
|  |   } @else if (isLoadingFirstSeen) { | ||||||
|  |     <tr> | ||||||
|  |       <td i18n="transaction.first-seen|Transaction first seen">First seen</td> | ||||||
|  |       <td><span class="skeleton-loader"></span></td> | ||||||
|  |     </tr> | ||||||
|  |   } | ||||||
|  | </ng-template> | ||||||
|  | 
 | ||||||
|  | <ng-template #featuresRow> | ||||||
|  |   @if (network !== 'liquid' && network !== 'liquidtestnet') { | ||||||
|  |     @if (!isLoadingTx) { | ||||||
|  |       @if (featuresEnabled) { | ||||||
|  |         <tr> | ||||||
|  |           <td class="td-width" i18n="transaction.features|Transaction features" id="acceleratePreviewAnchor">Features</td> | ||||||
|  |           <td> | ||||||
|  |             <app-tx-features [tx]="tx"></app-tx-features> | ||||||
|  |           </td> | ||||||
|  |         </tr> | ||||||
|  |       } | ||||||
|  |     } @else { | ||||||
|  |       <ng-container *ngTemplateOutlet="skeletonDetailsRow"></ng-container> | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | </ng-template> | ||||||
|  | 
 | ||||||
|  | <ng-template #auditRow> | ||||||
|  |   @if (network === '') { | ||||||
|  |     @if (!isLoadingTx) { | ||||||
|  |       @if (auditStatus) { | ||||||
|  |         <tr> | ||||||
|  |           <td class="td-width" i18n="block.toggle-audit|Toggle Audit">Audit</td> | ||||||
|  |           <td class="wrap-cell"> | ||||||
|  |             <ng-container> | ||||||
|  |               @if (auditStatus.coinbase) { | ||||||
|  |                 <span class="badge badge-primary mr-1" i18n="transactions-list.coinbase">Coinbase</span> | ||||||
|  |               } @else if (auditStatus.expected) { | ||||||
|  |                 <span class="badge badge-success mr-1" i18n-ngbTooltip="Expected in block tooltip" ngbTooltip="This transaction was projected to be included in the block" placement="bottom" i18n="tx-features.tag.expected|Expected in Block">Expected in Block</span> | ||||||
|  |               } @else if (auditStatus.seen) { | ||||||
|  |                 <span class="badge badge-success mr-1" i18n-ngbTooltip="Seen in mempool tooltip" ngbTooltip="This transaction was seen in the mempool prior to mining" placement="bottom" i18n="tx-features.tag.seen|Seen in Mempool">Seen in Mempool</span> | ||||||
|  |               } @else if (!auditStatus.conflict) { | ||||||
|  |                 <span class="badge badge-warning mr-1" i18n-ngbTooltip="Not seen in mempool tooltip" ngbTooltip="This transaction was missing from our mempool prior to mining" placement="bottom" i18n="tx-features.tag.not-seen|Not seen in Mempool">Not seen in Mempool</span> | ||||||
|  |               } | ||||||
|  |               @if (auditStatus.added) { | ||||||
|  |                 <span class="badge badge-warning mr-1" i18n-ngbTooltip="Added transaction tooltip" ngbTooltip="This transaction may have been added out-of-band" placement="bottom" i18n="tx-features.tag.added|Added">Added</span> | ||||||
|  |               } | ||||||
|  |               @if (auditStatus.prioritized) { | ||||||
|  |                 <span class="badge badge-warning mr-1" i18n-ngbTooltip="Prioritized transaction tooltip" ngbTooltip="This transaction may have been prioritized out-of-band" placement="bottom" i18n="tx-features.tag.prioritized|Prioritized">Prioritized</span> | ||||||
|  |               } | ||||||
|  |               @if (auditStatus.conflict) { | ||||||
|  |                 <span class="badge badge-warning mr-1" i18n-ngbTooltip="Conflict in mempool tooltip" ngbTooltip="This transaction conflicted with another version in our mempool" placement="bottom" i18n="tx-features.tag.conflict|Conflict">Conflict</span> | ||||||
|  |               } | ||||||
|  |             </ng-container> | ||||||
|  |           </td> | ||||||
|  |         </tr> | ||||||
|  |       } | ||||||
|  |     } @else { | ||||||
|  |       <ng-container *ngTemplateOutlet="skeletonDetailsRow"></ng-container> | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | </ng-template> | ||||||
|  | 
 | ||||||
|  | <ng-template #etaRow> | ||||||
|  |   @if (!isLoadingTx) { | ||||||
|  |     @if (!replaced && !isCached) { | ||||||
|  |       <tr> | ||||||
|  |         <td class="td-width align-items-center align-middle" i18n="transaction.eta|Transaction ETA">ETA</td> | ||||||
|  |         <td> | ||||||
|  |           <ng-container *ngIf="(ETA$ | async) as eta; else etaSkeleton"> | ||||||
|  |             @if (network === 'liquid' || network === 'liquidtestnet') { | ||||||
|  |               <app-time kind="until" [time]="eta.time" [fastRender]="false" [fixedRender]="true"></app-time> | ||||||
|  |             } @else { | ||||||
|  |               <span [class]="(!tx?.acceleration && acceleratorAvailable && accelerateCtaType === 'button' && !showAccelerationSummary && notAcceleratedOnLoad) ? 'etaDeepMempool d-flex justify-content-between' : ''"> | ||||||
|  |                 @if (eta.blocks >= 7) { | ||||||
|  |                   <span i18n="transaction.eta.not-any-time-soon|Transaction ETA mot any time soon">Not any time soon</span> | ||||||
|  |                 } @else { | ||||||
|  |                   <app-time kind="until" [time]="eta.time" [fastRender]="false" [fixedRender]="true"></app-time> | ||||||
|  |                 } | ||||||
|  |                 @if (!tx?.acceleration && acceleratorAvailable && accelerateCtaType === 'button' && !showAccelerationSummary && notAcceleratedOnLoad) { | ||||||
|  |                   <div class="d-flex accelerate"> | ||||||
|  |                     <a class="btn btn-sm accelerateDeepMempool btn-small-height" [class.disabled]="!eligibleForAcceleration" i18n="transaction.accelerate|Accelerate button label" (click)="onAccelerateClicked()">Accelerate</a> | ||||||
|  |                     <a *ngIf="!eligibleForAcceleration" href="https://mempool.space/accelerator#why-cant-accelerate" target="_blank" class="info-badges ml-1" i18n-ngbTooltip="Mempool Accelerator™ tooltip" ngbTooltip="This transaction cannot be accelerated"> | ||||||
|  |                         <fa-icon [icon]="['fas', 'info-circle']" [fixedWidth]="true"></fa-icon> | ||||||
|  |                     </a> | ||||||
|  |                   </div> | ||||||
|  |                 } | ||||||
|  |               </span> | ||||||
|  |             } | ||||||
|  |           </ng-container> | ||||||
|  |           <ng-template #etaSkeleton> | ||||||
|  |             <span class="skeleton-loader"></span> | ||||||
|  |           </ng-template> | ||||||
|  |         </td> | ||||||
|  |       </tr> | ||||||
|  |     } | ||||||
|  |   } @else { | ||||||
|  |     <ng-container *ngTemplateOutlet="skeletonDetailsRow"></ng-container> | ||||||
|  |   } | ||||||
|  | </ng-template> | ||||||
|  | 
 | ||||||
|  | <ng-template #gogglesRow> | ||||||
|  |   @if (!isLoadingTx) { | ||||||
|  |     @if (isAcceleration || filters.length) { | ||||||
|  |       <tr> | ||||||
|  |         <td class="td-width"> | ||||||
|  |           <span class="goggles-icon"><app-svg-images name="goggles" width="100%" height="100%"></app-svg-images></span> | ||||||
|  |         </td> | ||||||
|  |         <td class="wrap-cell"> | ||||||
|  |           @if (isAcceleration) { | ||||||
|  |             <span class="badge badge-accelerated mr-1" i18n="transaction.audit.accelerated">Accelerated</span> | ||||||
|  |           } | ||||||
|  |           <ng-container *ngFor="let filter of filters;"> | ||||||
|  |             <span class="badge badge-primary filter-tag mr-1">{{ filter.label }}</span> | ||||||
|  |           </ng-container> | ||||||
|  |         </td> | ||||||
|  |       </tr> | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | </ng-template> | ||||||
|  | 
 | ||||||
|  | <ng-template #feeRow> | ||||||
|  |   @if (!isLoadingTx) { | ||||||
|  |     <tr> | ||||||
|  |       <td class="td-width" i18n="transaction.fee|Transaction fee">Fee</td> | ||||||
|  |       <td class="text-wrap">{{ tx.fee | number }} <span class="symbol" i18n="shared.sats">sats</span> | ||||||
|  |         @if (accelerationInfo?.bidBoost ?? tx.feeDelta > 0) { | ||||||
|  |           <span class="oobFees" i18n-ngbTooltip="Acceleration Fees" ngbTooltip="Acceleration fees paid out-of-band"> +{{ accelerationInfo?.bidBoost ?? tx.feeDelta | number }} </span><span class="symbol" i18n="shared.sats">sats</span> | ||||||
|  |         } | ||||||
|  |         <span class="fiat"><app-fiat [blockConversion]="tx.price" [value]="tx.fee + ((accelerationInfo?.bidBoost ?? tx.feeDelta) || 0)"></app-fiat></span> | ||||||
|  |       </td> | ||||||
|  |     </tr> | ||||||
|  |   } @else { | ||||||
|  |     <ng-container *ngTemplateOutlet="skeletonDetailsRow"></ng-container> | ||||||
|  |   } | ||||||
|  | </ng-template> | ||||||
|  | 
 | ||||||
|  | <ng-template #feeRateRow> | ||||||
|  |   @if (!isLoadingTx) { | ||||||
|  |     <tr> | ||||||
|  |       <td i18n="transaction.fee-rate|Transaction fee rate">Fee rate</td> | ||||||
|  |       <td> | ||||||
|  |         <app-fee-rate [fee]="tx.feePerVsize"></app-fee-rate> | ||||||
|  |         @if (tx?.status?.confirmed && tx.fee && !hasEffectiveFeeRate && !accelerationInfo) { | ||||||
|  |             | ||||||
|  |           <app-tx-fee-rating [tx]="tx"></app-tx-fee-rating> | ||||||
|  |         } | ||||||
|  |       </td> | ||||||
|  |     </tr> | ||||||
|  |   } @else { | ||||||
|  |     <ng-container *ngTemplateOutlet="skeletonDetailsRow"></ng-container> | ||||||
|  |   } | ||||||
|  | </ng-template> | ||||||
|  | 
 | ||||||
|  | <ng-template #effectiveRateRow> | ||||||
|  |   @if (!isLoadingTx) { | ||||||
|  |     @if ((cpfpInfo && hasEffectiveFeeRate) || accelerationInfo) { | ||||||
|  |       <tr> | ||||||
|  |         @if (isAcceleration) { | ||||||
|  |           <td i18n="transaction.accelerated-fee-rate|Accelerated transaction fee rate">Accelerated fee rate</td> | ||||||
|  |         } @else { | ||||||
|  |           <td i18n="transaction.effective-fee-rate|Effective transaction fee rate">Effective fee rate</td> | ||||||
|  |         } | ||||||
|  |         <td> | ||||||
|  |           <div class="effective-fee-container"> | ||||||
|  |             @if (accelerationInfo?.acceleratedFeeRate && (!tx.effectiveFeePerVsize || accelerationInfo.acceleratedFeeRate >= tx.effectiveFeePerVsize || tx.acceleration)) { | ||||||
|  |               <app-fee-rate [class.oobFees]="isAcceleration" [fee]="accelerationInfo.acceleratedFeeRate"></app-fee-rate> | ||||||
|  |             } @else { | ||||||
|  |               <app-fee-rate [class.oobFees]="isAcceleration" [fee]="tx.effectiveFeePerVsize"></app-fee-rate> | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             @if (tx?.status?.confirmed && !tx.acceleration && !accelerationInfo && tx.fee && tx.effectiveFeePerVsize) { | ||||||
|  |               <app-tx-fee-rating class="ml-2 mr-2 effective-fee-rating" [tx]="tx"></app-tx-fee-rating> | ||||||
|  |             } | ||||||
|  |           </div> | ||||||
|  |           @if (hasCpfp) { | ||||||
|  |             <button type="button" class="btn btn-outline-info btn-sm btn-small-height float-right" (click)="showCpfpDetails = !showCpfpDetails">CPFP <fa-icon [icon]="['fas', 'info-circle']" [fixedWidth]="true"></fa-icon></button> | ||||||
|  |           } | ||||||
|  |         </td> | ||||||
|  |       </tr> | ||||||
|  |     } | ||||||
|  |   } @else { | ||||||
|  |     <ng-container *ngTemplateOutlet="skeletonDetailsRow"></ng-container> | ||||||
|  |   } | ||||||
|  | </ng-template> | ||||||
|  | 
 | ||||||
|  | <ng-template #acceleratingRow> | ||||||
|  |   <tr> | ||||||
|  |     <td rowspan="2" colspan="2" style="padding: 0;"> | ||||||
|  |       <app-active-acceleration-box [acceleratedBy]="tx.acceleratedBy" [effectiveFeeRate]="tx.effectiveFeePerVsize" [accelerationInfo]="accelerationInfo" [miningStats]="miningStats" [hasCpfp]="hasCpfp" (toggleCpfp)="showCpfpDetails = !showCpfpDetails" [chartPositionLeft]="isMobile"></app-active-acceleration-box> | ||||||
|  |     </td> | ||||||
|  |   </tr> | ||||||
|  |   <tr></tr> | ||||||
|  | </ng-template> | ||||||
|  | 
 | ||||||
|  | <ng-template #minerRow> | ||||||
|  |   @if (network === '') { | ||||||
|  |     @if (!isLoadingTx) { | ||||||
|  |       <tr> | ||||||
|  |         <td class="td-width" i18n="block.miner">Miner</td> | ||||||
|  |         @if (pool) { | ||||||
|  |           <td class="wrap-cell"> | ||||||
|  |             <a placement="bottom" [routerLink]="['/mining/pool' | relativeUrl, pool.slug]" class="badge" style="color: #FFF;padding:0;"> | ||||||
|  |               <span class="miner-name" *ngIf="pool.minerNames?.length > 1 && pool.minerNames[1] != ''"> | ||||||
|  |                 @if (pool.minerNames[1].length > 16) { | ||||||
|  |                   {{ pool.minerNames[1].slice(0, 15) }}… | ||||||
|  |                 } @else { | ||||||
|  |                   {{ pool.minerNames[1] }} | ||||||
|  |                 } | ||||||
|  |               </span> | ||||||
|  |               <img class="pool-logo" [src]="'/resources/mining-pools/' + pool.slug + '.svg'" onError="this.src = '/resources/mining-pools/default.svg'" [alt]="'Logo of ' + pool.name + ' mining pool'"> | ||||||
|  |               {{ pool.name }} | ||||||
|  |             </a> | ||||||
|  |           </td> | ||||||
|  |         } @else { | ||||||
|  |           <td> | ||||||
|  |             <span class="skeleton-loader"></span> | ||||||
|  |           </td> | ||||||
|  |         } | ||||||
|  |       </tr> | ||||||
|  |     } @else { | ||||||
|  |       <ng-container *ngTemplateOutlet="skeletonDetailsRow"></ng-container> | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | </ng-template> | ||||||
|  | 
 | ||||||
|  | <ng-template #skeletonDetailsRow> | ||||||
|  |   <tr> | ||||||
|  |     <td><span class="skeleton-loader"></span></td> | ||||||
|  |   </tr> | ||||||
|  | </ng-template> | ||||||
| @ -0,0 +1,183 @@ | |||||||
|  | .title-block { | ||||||
|  |   flex-wrap: wrap; | ||||||
|  |   align-items: baseline; | ||||||
|  |   @media (min-width: 650px) { | ||||||
|  |     flex-direction: row; | ||||||
|  |   } | ||||||
|  |   h1 { | ||||||
|  |     margin: 0rem; | ||||||
|  |     margin-right: 15px; | ||||||
|  |     line-height: 1; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .td-width { | ||||||
|  | 	width: 150px; | ||||||
|  | 
 | ||||||
|  | 	@media (max-width: 768px) { | ||||||
|  | 	  width: 175px; | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .badge { | ||||||
|  | 	position: relative; | ||||||
|  | 	top: -1px; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .miner-name { | ||||||
|  |   margin-right: 4px; | ||||||
|  |   vertical-align: top; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .pool-logo { | ||||||
|  |   width: 25px; | ||||||
|  |   height: 25px; | ||||||
|  |   position: relative; | ||||||
|  |   top: -1px; | ||||||
|  |   margin-right: 2px; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .badge.badge-accelerated { | ||||||
|  |   background-color: var(--tertiary); | ||||||
|  |   color: white; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .btn-small-height { | ||||||
|  | 	line-height: 1; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .row{ | ||||||
|  | 	flex-direction: column; | ||||||
|  | 	@media (min-width: 850px) { | ||||||
|  | 		flex-direction: row; | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .box.hidden { | ||||||
|  |   visibility: hidden; | ||||||
|  |   height: 0px; | ||||||
|  |   padding-top: 0px; | ||||||
|  |   padding-bottom: 0px; | ||||||
|  |   margin-top: 0px; | ||||||
|  |   margin-bottom: 0px; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | @media (max-width: 767.98px) { | ||||||
|  | 	.mobile-bottomcol { | ||||||
|  | 		margin-top: 15px; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	.details-table td:first-child { | ||||||
|  | 		white-space: pre-wrap; | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .fiat { | ||||||
|  | 	display: block; | ||||||
|  | 	@media (min-width: 768px){ | ||||||
|  | 		display: inline-block; | ||||||
|  | 		margin-left: 15px; | ||||||
|  | 		text-align: left; | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .table { | ||||||
|  |   tr td { | ||||||
|  | 		padding: 0.75rem 0.5rem; | ||||||
|  | 		@media (min-width: 576px) { | ||||||
|  | 			padding: 0.75rem 0.75rem; | ||||||
|  | 		} | ||||||
|  | 		&:last-child { | ||||||
|  | 			text-align: right; | ||||||
|  | 			@media (min-width: 850px) { | ||||||
|  |         text-align: left; | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 		.btn { | ||||||
|  | 			display: block; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  |     &.wrap-cell { | ||||||
|  |       white-space: normal; | ||||||
|  |     } | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .effective-fee-container { | ||||||
|  | 	display: block; | ||||||
|  | 	@media (min-width: 768px){ | ||||||
|  | 		display: inline-block; | ||||||
|  | 	} | ||||||
|  |   @media (max-width: 425px){ | ||||||
|  | 		display: flex; | ||||||
|  |     flex-direction: column; | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | @media (max-width: 767px){ | ||||||
|  |   .hide-on-mobile { | ||||||
|  |     display: none; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .effective-fee-rating { | ||||||
|  |   @media (max-width: 767px){ | ||||||
|  |     margin-right: 0px !important; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .btn-outline-info { | ||||||
|  | 	margin-top: 5px; | ||||||
|  | 	@media (min-width: 768px){ | ||||||
|  | 		margin-top: 0px; | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .eta { | ||||||
|  |   display: flex; | ||||||
|  |   flex-wrap: wrap; | ||||||
|  |   align-content: center; | ||||||
|  |   @media (min-width: 850px) { | ||||||
|  |     justify-content: left !important;     | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .accelerate { | ||||||
|  |   @media (min-width: 850px) { | ||||||
|  |     margin-left: auto; | ||||||
|  |   }   | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .etaDeepMempool { | ||||||
|  |   flex-wrap: wrap; | ||||||
|  |   @media (max-width: 849px) { | ||||||
|  |     justify-content: right !important; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .accelerateDeepMempool { | ||||||
|  |   background-color: var(--tertiary); | ||||||
|  |   margin-left: 5px; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .goggles-icon { | ||||||
|  |   display: block; | ||||||
|  |   width: 2.7em; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .pool-logo { | ||||||
|  |   width: 15px; | ||||||
|  |   height: 15px; | ||||||
|  |   position: relative; | ||||||
|  |   top: -1px; | ||||||
|  |   margin-right: 2px; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .oobFees { | ||||||
|  |   color: #905cf4; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .disabled { | ||||||
|  |   opacity: 0.5; | ||||||
|  |   pointer-events: none; | ||||||
|  | } | ||||||
| @ -0,0 +1,52 @@ | |||||||
|  | import { Component, OnInit, Input, ChangeDetectionStrategy, Output, EventEmitter } from '@angular/core'; | ||||||
|  | import { Transaction } from '@interfaces/electrs.interface'; | ||||||
|  | import { Acceleration, CpfpInfo } from '@interfaces/node-api.interface'; | ||||||
|  | import { Pool, TxAuditStatus } from '@components/transaction/transaction.component'; | ||||||
|  | import { Observable } from 'rxjs'; | ||||||
|  | import { ETA } from '@app/services/eta.service'; | ||||||
|  | import { MiningStats } from '@app/services/mining.service'; | ||||||
|  | import { Filter } from '@app/shared/filters.utils'; | ||||||
|  | 
 | ||||||
|  | @Component({ | ||||||
|  |   selector: 'app-transaction-details', | ||||||
|  |   templateUrl: './transaction-details.component.html', | ||||||
|  |   styleUrls: ['./transaction-details.component.scss'], | ||||||
|  |   changeDetection: ChangeDetectionStrategy.OnPush | ||||||
|  | }) | ||||||
|  | export class TransactionDetailsComponent implements OnInit { | ||||||
|  |   @Input() network: string; | ||||||
|  |   @Input() tx: Transaction; | ||||||
|  |   @Input() isLoadingTx: boolean; | ||||||
|  |   @Input() isMobile: boolean; | ||||||
|  |   @Input() transactionTime: number; | ||||||
|  |   @Input() isLoadingFirstSeen: boolean; | ||||||
|  |   @Input() featuresEnabled: boolean; | ||||||
|  |   @Input() auditStatus: TxAuditStatus; | ||||||
|  |   @Input() filters: Filter[]; | ||||||
|  |   @Input() miningStats: MiningStats; | ||||||
|  |   @Input() pool: Pool | null; | ||||||
|  |   @Input() isAcceleration: boolean; | ||||||
|  |   @Input() hasEffectiveFeeRate: boolean; | ||||||
|  |   @Input() cpfpInfo: CpfpInfo; | ||||||
|  |   @Input() hasCpfp: boolean; | ||||||
|  |   @Input() showCpfpDetails: boolean; | ||||||
|  |   @Input() accelerationInfo: Acceleration; | ||||||
|  |   @Input() acceleratorAvailable: boolean; | ||||||
|  |   @Input() accelerateCtaType: string; | ||||||
|  |   @Input() notAcceleratedOnLoad: boolean; | ||||||
|  |   @Input() showAccelerationSummary: boolean; | ||||||
|  |   @Input() eligibleForAcceleration: boolean; | ||||||
|  |   @Input() replaced: boolean; | ||||||
|  |   @Input() isCached: boolean; | ||||||
|  |   @Input() ETA$: Observable<ETA>; | ||||||
|  | 
 | ||||||
|  |   @Output() accelerateClicked = new EventEmitter<boolean>(); | ||||||
|  | 
 | ||||||
|  |   constructor() {} | ||||||
|  | 
 | ||||||
|  |   ngOnInit(): void {} | ||||||
|  | 
 | ||||||
|  |   onAccelerateClicked(): void { | ||||||
|  |     this.accelerateClicked.emit(true); | ||||||
|  |   } | ||||||
|  | } | ||||||
| @ -0,0 +1,11 @@ | |||||||
|  | import { NgModule } from '@angular/core'; | ||||||
|  | 
 | ||||||
|  | @NgModule({ | ||||||
|  |   declarations: [ | ||||||
|  |   ], | ||||||
|  |   imports: [ | ||||||
|  |   ], | ||||||
|  |   exports: [ | ||||||
|  |   ] | ||||||
|  | }) | ||||||
|  | export class TransactionExtrasModule { } | ||||||
| @ -31,35 +31,35 @@ | |||||||
|   <div class="clearfix"></div> |   <div class="clearfix"></div> | ||||||
| 
 | 
 | ||||||
|   @if (!error) { |   @if (!error) { | ||||||
|     <div class="box"> |     <app-transaction-details | ||||||
|       <div class="row"> |       [network]="network" | ||||||
|         @if (isMobile) { |       [tx]="tx" | ||||||
|           <div class="col-sm"> |       [isLoadingTx]="isLoadingTx" | ||||||
|             <table class="table table-borderless table-striped"> |       [isMobile]="isMobile" | ||||||
|               <tbody> |       [transactionTime]="transactionTime" | ||||||
|                   <ng-container *ngTemplateOutlet="detailsLeft"></ng-container> |       [isLoadingFirstSeen]="isLoadingFirstSeen" | ||||||
|                   <ng-container *ngTemplateOutlet="detailsRight"></ng-container> |       [featuresEnabled]="featuresEnabled" | ||||||
|               </tbody> |       [auditStatus]="auditStatus" | ||||||
|             </table> |       [filters]="filters" | ||||||
|           </div> |       [miningStats]="miningStats" | ||||||
|         } @else { |       [pool]="pool" | ||||||
|           <div class="col-sm"> |       [isAcceleration]="isAcceleration" | ||||||
|             <table class="table table-borderless table-striped"> |       [acceleratorAvailable]="acceleratorAvailable" | ||||||
|               <tbody> |       [accelerateCtaType]="accelerateCtaType" | ||||||
|                   <ng-container *ngTemplateOutlet="detailsLeft"></ng-container> |       [notAcceleratedOnLoad]="notAcceleratedOnLoad" | ||||||
|               </tbody> |       [showAccelerationSummary]="showAccelerationSummary" | ||||||
|             </table> |       [eligibleForAcceleration]="eligibleForAcceleration" | ||||||
|           </div> |       [hasEffectiveFeeRate]="hasEffectiveFeeRate" | ||||||
|           <div class="col-sm"> |       [cpfpInfo]="cpfpInfo" | ||||||
|             <table class="table table-borderless table-striped"> |       [hasCpfp]="hasCpfp" | ||||||
|               <tbody> |       [showCpfpDetails]="showCpfpDetails" | ||||||
|                   <ng-container *ngTemplateOutlet="detailsRight"></ng-container> |       [accelerationInfo]="accelerationInfo" | ||||||
|               </tbody> |       [replaced]="replaced" | ||||||
|             </table> |       [isCached]="isCached" | ||||||
|           </div> |       [ETA$]="ETA$" | ||||||
|         } |       (accelerateClicked)="onAccelerateClicked()" | ||||||
|       </div> |       (toggleCpfp$)="this.showCpfpDetails = !this.showCpfpDetails" | ||||||
|     </div> |     ></app-transaction-details> | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   <span id="accelerate"></span> |   <span id="accelerate"></span> | ||||||
| @ -416,299 +416,4 @@ | |||||||
|     </ng-template> |     </ng-template> | ||||||
|   </ng-template> |   </ng-template> | ||||||
| 
 | 
 | ||||||
| </div> | </div> | ||||||
| 
 |  | ||||||
| <ng-template #detailsLeft> |  | ||||||
|   @if (tx?.status?.confirmed) { |  | ||||||
|     <ng-container *ngTemplateOutlet="timestampRow"></ng-container> |  | ||||||
|     <ng-container *ngTemplateOutlet="confirmedAfterRow"></ng-container> |  | ||||||
|   } @else { |  | ||||||
|     <ng-container *ngTemplateOutlet="firstSeenRow"></ng-container> |  | ||||||
|     <ng-container *ngTemplateOutlet="etaRow"></ng-container> |  | ||||||
|   } |  | ||||||
|   <ng-container *ngTemplateOutlet="featuresRow"></ng-container> |  | ||||||
|   @if (tx?.status?.confirmed) { |  | ||||||
|     <ng-container *ngTemplateOutlet="auditRow"></ng-container> |  | ||||||
|   } |  | ||||||
|   <ng-container *ngTemplateOutlet="gogglesRow"></ng-container> |  | ||||||
| </ng-template> |  | ||||||
| 
 |  | ||||||
| <ng-template #detailsRight> |  | ||||||
|   <ng-container *ngTemplateOutlet="feeRow"></ng-container> |  | ||||||
|   <ng-container *ngTemplateOutlet="feeRateRow"></ng-container> |  | ||||||
|   @if (!isLoadingTx && !tx?.status?.confirmed && isAcceleration && ((cpfpInfo && hasEffectiveFeeRate) || accelerationInfo)) { |  | ||||||
|     <ng-container *ngTemplateOutlet="acceleratingRow"></ng-container> |  | ||||||
|   } @else { |  | ||||||
|     <ng-container *ngTemplateOutlet="effectiveRateRow"></ng-container> |  | ||||||
|   } |  | ||||||
|   @if (tx?.status?.confirmed) { |  | ||||||
|     <ng-container *ngTemplateOutlet="minerRow"></ng-container> |  | ||||||
|   } |  | ||||||
| </ng-template> |  | ||||||
| 
 |  | ||||||
| <ng-template #timestampRow> |  | ||||||
|   @if (!isLoadingTx) { |  | ||||||
|     <tr> |  | ||||||
|       <td i18n="block.timestamp">Timestamp</td> |  | ||||||
|       <td> |  | ||||||
|         ‎{{ tx.status.block_time * 1000 | date:'yyyy-MM-dd HH:mm:ss' }} |  | ||||||
|         <div class="lg-inline"> |  | ||||||
|           <i class="symbol">(<app-time kind="since" [time]="tx.status.block_time" [fastRender]="true"></app-time>)</i> |  | ||||||
|         </div> |  | ||||||
|       </td> |  | ||||||
|     </tr> |  | ||||||
|   } @else { |  | ||||||
|     <ng-container *ngTemplateOutlet="skeletonDetailsRow"></ng-container> |  | ||||||
|   } |  | ||||||
| </ng-template> |  | ||||||
| 
 |  | ||||||
| <ng-template #confirmedAfterRow> |  | ||||||
|   @if (!isLoadingTx) { |  | ||||||
|     @if (transactionTime > 0) { |  | ||||||
|       <tr> |  | ||||||
|         <td i18n="transaction.confirmed|Transaction Confirmed state">Confirmed</td> |  | ||||||
|         <td><app-time kind="span" [time]="tx.status.block_time - transactionTime" [fastRender]="true" [showTooltip]="true"></app-time></td> |  | ||||||
|       </tr> |  | ||||||
|     } |  | ||||||
|   } @else { |  | ||||||
|     <ng-container *ngTemplateOutlet="skeletonDetailsRow"></ng-container> |  | ||||||
|   } |  | ||||||
| </ng-template> |  | ||||||
| 
 |  | ||||||
| <ng-template #firstSeenRow> |  | ||||||
|   @if (isLoadingTx) { |  | ||||||
|     <ng-container *ngTemplateOutlet="skeletonDetailsRow"></ng-container> |  | ||||||
|   } @else if (transactionTime > 0) { |  | ||||||
|     <tr> |  | ||||||
|       <td i18n="transaction.first-seen|Transaction first seen">First seen</td> |  | ||||||
|       <td><i><app-time kind="since" [time]="transactionTime" [fastRender]="true" [showTooltip]="true"></app-time></i></td> |  | ||||||
|     </tr> |  | ||||||
|   } @else if (isLoadingFirstSeen) { |  | ||||||
|     <tr> |  | ||||||
|       <td i18n="transaction.first-seen|Transaction first seen">First seen</td> |  | ||||||
|       <td><span class="skeleton-loader"></span></td> |  | ||||||
|     </tr> |  | ||||||
|   } |  | ||||||
| </ng-template> |  | ||||||
| 
 |  | ||||||
| <ng-template #featuresRow> |  | ||||||
|   @if (network !== 'liquid' && network !== 'liquidtestnet') { |  | ||||||
|     @if (!isLoadingTx) { |  | ||||||
|       @if (featuresEnabled) { |  | ||||||
|         <tr> |  | ||||||
|           <td class="td-width" i18n="transaction.features|Transaction features" id="acceleratePreviewAnchor">Features</td> |  | ||||||
|           <td> |  | ||||||
|             <app-tx-features [tx]="tx"></app-tx-features> |  | ||||||
|           </td> |  | ||||||
|         </tr> |  | ||||||
|       } |  | ||||||
|     } @else { |  | ||||||
|       <ng-container *ngTemplateOutlet="skeletonDetailsRow"></ng-container> |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
| </ng-template> |  | ||||||
| 
 |  | ||||||
| <ng-template #auditRow> |  | ||||||
|   @if (network === '') { |  | ||||||
|     @if (!isLoadingTx) { |  | ||||||
|       @if (auditStatus) { |  | ||||||
|         <tr> |  | ||||||
|           <td class="td-width" i18n="block.toggle-audit|Toggle Audit">Audit</td> |  | ||||||
|           <td class="wrap-cell"> |  | ||||||
|             <ng-container> |  | ||||||
|               @if (auditStatus.coinbase) { |  | ||||||
|                 <span class="badge badge-primary mr-1" i18n="transactions-list.coinbase">Coinbase</span> |  | ||||||
|               } @else if (auditStatus.expected) { |  | ||||||
|                 <span class="badge badge-success mr-1" i18n-ngbTooltip="Expected in block tooltip" ngbTooltip="This transaction was projected to be included in the block" placement="bottom" i18n="tx-features.tag.expected|Expected in Block">Expected in Block</span> |  | ||||||
|               } @else if (auditStatus.seen) { |  | ||||||
|                 <span class="badge badge-success mr-1" i18n-ngbTooltip="Seen in mempool tooltip" ngbTooltip="This transaction was seen in the mempool prior to mining" placement="bottom" i18n="tx-features.tag.seen|Seen in Mempool">Seen in Mempool</span> |  | ||||||
|               } @else if (!auditStatus.conflict) { |  | ||||||
|                 <span class="badge badge-warning mr-1" i18n-ngbTooltip="Not seen in mempool tooltip" ngbTooltip="This transaction was missing from our mempool prior to mining" placement="bottom" i18n="tx-features.tag.not-seen|Not seen in Mempool">Not seen in Mempool</span> |  | ||||||
|               } |  | ||||||
|               @if (auditStatus.added) { |  | ||||||
|                 <span class="badge badge-warning mr-1" i18n-ngbTooltip="Added transaction tooltip" ngbTooltip="This transaction may have been added out-of-band" placement="bottom" i18n="tx-features.tag.added|Added">Added</span> |  | ||||||
|               } |  | ||||||
|               @if (auditStatus.prioritized) { |  | ||||||
|                 <span class="badge badge-warning mr-1" i18n-ngbTooltip="Prioritized transaction tooltip" ngbTooltip="This transaction may have been prioritized out-of-band" placement="bottom" i18n="tx-features.tag.prioritized|Prioritized">Prioritized</span> |  | ||||||
|               } |  | ||||||
|               @if (auditStatus.conflict) { |  | ||||||
|                 <span class="badge badge-warning mr-1" i18n-ngbTooltip="Conflict in mempool tooltip" ngbTooltip="This transaction conflicted with another version in our mempool" placement="bottom" i18n="tx-features.tag.conflict|Conflict">Conflict</span> |  | ||||||
|               } |  | ||||||
|             </ng-container> |  | ||||||
|           </td> |  | ||||||
|         </tr> |  | ||||||
|       } |  | ||||||
|     } @else { |  | ||||||
|       <ng-container *ngTemplateOutlet="skeletonDetailsRow"></ng-container> |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
| </ng-template> |  | ||||||
| 
 |  | ||||||
| <ng-template #etaRow> |  | ||||||
|   @if (!isLoadingTx) { |  | ||||||
|     @if (!replaced && !isCached) { |  | ||||||
|       <tr> |  | ||||||
|         <td class="td-width align-items-center align-middle" i18n="transaction.eta|Transaction ETA">ETA</td> |  | ||||||
|         <td> |  | ||||||
|           <ng-container *ngIf="(ETA$ | async) as eta; else etaSkeleton"> |  | ||||||
|             @if (network === 'liquid' || network === 'liquidtestnet') { |  | ||||||
|               <app-time kind="until" [time]="eta.time" [fastRender]="false" [fixedRender]="true"></app-time> |  | ||||||
|             } @else { |  | ||||||
|               <span [class]="(!tx?.acceleration && acceleratorAvailable && accelerateCtaType === 'button' && !showAccelerationSummary && notAcceleratedOnLoad) ? 'etaDeepMempool d-flex justify-content-between' : ''"> |  | ||||||
|                 @if (eta.blocks >= 7) { |  | ||||||
|                   <span i18n="transaction.eta.not-any-time-soon|Transaction ETA mot any time soon">Not any time soon</span> |  | ||||||
|                 } @else { |  | ||||||
|                   <app-time kind="until" [time]="eta.time" [fastRender]="false" [fixedRender]="true"></app-time> |  | ||||||
|                 } |  | ||||||
|                 @if (!tx?.acceleration && acceleratorAvailable && accelerateCtaType === 'button' && !showAccelerationSummary && notAcceleratedOnLoad) { |  | ||||||
|                   <div class="d-flex accelerate"> |  | ||||||
|                     <a class="btn btn-sm accelerateDeepMempool btn-small-height" [class.disabled]="!eligibleForAcceleration" i18n="transaction.accelerate|Accelerate button label" (click)="onAccelerateClicked()">Accelerate</a> |  | ||||||
|                     <a *ngIf="!eligibleForAcceleration" href="https://mempool.space/accelerator#why-cant-accelerate" target="_blank" class="info-badges ml-1" i18n-ngbTooltip="Mempool Accelerator™ tooltip" ngbTooltip="This transaction cannot be accelerated"> |  | ||||||
|                         <fa-icon [icon]="['fas', 'info-circle']" [fixedWidth]="true"></fa-icon> |  | ||||||
|                     </a> |  | ||||||
|                   </div> |  | ||||||
|                 } |  | ||||||
|               </span> |  | ||||||
|             } |  | ||||||
|           </ng-container> |  | ||||||
|           <ng-template #etaSkeleton> |  | ||||||
|             <span class="skeleton-loader"></span> |  | ||||||
|           </ng-template> |  | ||||||
|         </td> |  | ||||||
|       </tr> |  | ||||||
|     } |  | ||||||
|   } @else { |  | ||||||
|     <ng-container *ngTemplateOutlet="skeletonDetailsRow"></ng-container> |  | ||||||
|   } |  | ||||||
| </ng-template> |  | ||||||
| 
 |  | ||||||
| <ng-template #gogglesRow> |  | ||||||
|   @if (!isLoadingTx) { |  | ||||||
|     @if (isAcceleration || filters.length) { |  | ||||||
|       <tr> |  | ||||||
|         <td class="td-width"> |  | ||||||
|           <span class="goggles-icon"><app-svg-images name="goggles" width="100%" height="100%"></app-svg-images></span> |  | ||||||
|         </td> |  | ||||||
|         <td class="wrap-cell"> |  | ||||||
|           @if (isAcceleration) { |  | ||||||
|             <span class="badge badge-accelerated mr-1" i18n="transaction.audit.accelerated">Accelerated</span> |  | ||||||
|           } |  | ||||||
|           <ng-container *ngFor="let filter of filters;"> |  | ||||||
|             <span class="badge badge-primary filter-tag mr-1">{{ filter.label }}</span> |  | ||||||
|           </ng-container> |  | ||||||
|         </td> |  | ||||||
|       </tr> |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
| </ng-template> |  | ||||||
| 
 |  | ||||||
| <ng-template #feeRow> |  | ||||||
|   @if (!isLoadingTx) { |  | ||||||
|     <tr> |  | ||||||
|       <td class="td-width" i18n="transaction.fee|Transaction fee">Fee</td> |  | ||||||
|       <td class="text-wrap">{{ tx.fee | number }} <span class="symbol" i18n="shared.sats">sats</span> |  | ||||||
|         @if (accelerationInfo?.bidBoost ?? tx.feeDelta > 0) { |  | ||||||
|           <span class="oobFees" i18n-ngbTooltip="Acceleration Fees" ngbTooltip="Acceleration fees paid out-of-band"> +{{ accelerationInfo?.bidBoost ?? tx.feeDelta | number }} </span><span class="symbol" i18n="shared.sats">sats</span> |  | ||||||
|         } |  | ||||||
|         <span class="fiat"><app-fiat [blockConversion]="tx.price" [value]="tx.fee + ((accelerationInfo?.bidBoost ?? tx.feeDelta) || 0)"></app-fiat></span> |  | ||||||
|       </td> |  | ||||||
|     </tr> |  | ||||||
|   } @else { |  | ||||||
|     <ng-container *ngTemplateOutlet="skeletonDetailsRow"></ng-container> |  | ||||||
|   } |  | ||||||
| </ng-template> |  | ||||||
| 
 |  | ||||||
| <ng-template #feeRateRow> |  | ||||||
|   @if (!isLoadingTx) { |  | ||||||
|     <tr> |  | ||||||
|       <td i18n="transaction.fee-rate|Transaction fee rate">Fee rate</td> |  | ||||||
|       <td> |  | ||||||
|         <app-fee-rate [fee]="tx.feePerVsize"></app-fee-rate> |  | ||||||
|         @if (tx?.status?.confirmed && tx.fee && !hasEffectiveFeeRate && !accelerationInfo) { |  | ||||||
|             |  | ||||||
|           <app-tx-fee-rating [tx]="tx"></app-tx-fee-rating> |  | ||||||
|         } |  | ||||||
|       </td> |  | ||||||
|     </tr> |  | ||||||
|   } @else { |  | ||||||
|     <ng-container *ngTemplateOutlet="skeletonDetailsRow"></ng-container> |  | ||||||
|   } |  | ||||||
| </ng-template> |  | ||||||
| 
 |  | ||||||
| <ng-template #effectiveRateRow> |  | ||||||
|   @if (!isLoadingTx) { |  | ||||||
|     @if ((cpfpInfo && hasEffectiveFeeRate) || accelerationInfo) { |  | ||||||
|       <tr> |  | ||||||
|         @if (isAcceleration) { |  | ||||||
|           <td i18n="transaction.accelerated-fee-rate|Accelerated transaction fee rate">Accelerated fee rate</td> |  | ||||||
|         } @else { |  | ||||||
|           <td i18n="transaction.effective-fee-rate|Effective transaction fee rate">Effective fee rate</td> |  | ||||||
|         } |  | ||||||
|         <td> |  | ||||||
|           <div class="effective-fee-container"> |  | ||||||
|             @if (accelerationInfo?.acceleratedFeeRate && (!tx.effectiveFeePerVsize || accelerationInfo.acceleratedFeeRate >= tx.effectiveFeePerVsize || tx.acceleration)) { |  | ||||||
|               <app-fee-rate [class.oobFees]="isAcceleration" [fee]="accelerationInfo.acceleratedFeeRate"></app-fee-rate> |  | ||||||
|             } @else { |  | ||||||
|               <app-fee-rate [class.oobFees]="isAcceleration" [fee]="tx.effectiveFeePerVsize"></app-fee-rate> |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             @if (tx?.status?.confirmed && !tx.acceleration && !accelerationInfo && tx.fee && tx.effectiveFeePerVsize) { |  | ||||||
|               <app-tx-fee-rating class="ml-2 mr-2 effective-fee-rating" [tx]="tx"></app-tx-fee-rating> |  | ||||||
|             } |  | ||||||
|           </div> |  | ||||||
|           @if (hasCpfp) { |  | ||||||
|             <button type="button" class="btn btn-outline-info btn-sm btn-small-height float-right" (click)="showCpfpDetails = !showCpfpDetails">CPFP <fa-icon [icon]="['fas', 'info-circle']" [fixedWidth]="true"></fa-icon></button> |  | ||||||
|           } |  | ||||||
|         </td> |  | ||||||
|       </tr> |  | ||||||
|     } |  | ||||||
|   } @else { |  | ||||||
|     <ng-container *ngTemplateOutlet="skeletonDetailsRow"></ng-container> |  | ||||||
|   } |  | ||||||
| </ng-template> |  | ||||||
| 
 |  | ||||||
| <ng-template #acceleratingRow> |  | ||||||
|   <tr> |  | ||||||
|     <td rowspan="2" colspan="2" style="padding: 0;"> |  | ||||||
|       <app-active-acceleration-box [acceleratedBy]="tx.acceleratedBy" [effectiveFeeRate]="tx.effectiveFeePerVsize" [accelerationInfo]="accelerationInfo" [miningStats]="miningStats" [hasCpfp]="hasCpfp" (toggleCpfp)="showCpfpDetails = !showCpfpDetails" [chartPositionLeft]="isMobile"></app-active-acceleration-box> |  | ||||||
|     </td> |  | ||||||
|   </tr> |  | ||||||
|   <tr></tr> |  | ||||||
| </ng-template> |  | ||||||
| 
 |  | ||||||
| <ng-template #minerRow> |  | ||||||
|   @if (network === '') { |  | ||||||
|     @if (!isLoadingTx) { |  | ||||||
|       <tr> |  | ||||||
|         <td class="td-width" i18n="block.miner">Miner</td> |  | ||||||
|         @if (pool) { |  | ||||||
|           <td class="wrap-cell"> |  | ||||||
|             <a placement="bottom" [routerLink]="['/mining/pool' | relativeUrl, pool.slug]" class="badge" style="color: #FFF;padding:0;"> |  | ||||||
|               <span class="miner-name" *ngIf="pool.minerNames?.length > 1 && pool.minerNames[1] != ''"> |  | ||||||
|                 @if (pool.minerNames[1].length > 16) { |  | ||||||
|                   {{ pool.minerNames[1].slice(0, 15) }}… |  | ||||||
|                 } @else { |  | ||||||
|                   {{ pool.minerNames[1] }} |  | ||||||
|                 } |  | ||||||
|               </span> |  | ||||||
|               <img class="pool-logo" [src]="'/resources/mining-pools/' + pool.slug + '.svg'" onError="this.src = '/resources/mining-pools/default.svg'" [alt]="'Logo of ' + pool.name + ' mining pool'"> |  | ||||||
|               {{ pool.name }} |  | ||||||
|             </a> |  | ||||||
|           </td> |  | ||||||
|         } @else { |  | ||||||
|           <td> |  | ||||||
|             <span class="skeleton-loader"></span> |  | ||||||
|           </td> |  | ||||||
|         } |  | ||||||
|       </tr> |  | ||||||
|     } @else { |  | ||||||
|       <ng-container *ngTemplateOutlet="skeletonDetailsRow"></ng-container> |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
| </ng-template> |  | ||||||
| 
 |  | ||||||
| <ng-template #skeletonDetailsRow> |  | ||||||
|   <tr> |  | ||||||
|     <td><span class="skeleton-loader"></span></td> |  | ||||||
|   </tr> |  | ||||||
| </ng-template> |  | ||||||
| @ -18,6 +18,7 @@ | |||||||
|     line-height: 1; |     line-height: 1; | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  | 
 | ||||||
| .tx-link { | .tx-link { | ||||||
|   display: flex; |   display: flex; | ||||||
|   flex-direction: row; |   flex-direction: row; | ||||||
| @ -60,19 +61,6 @@ | |||||||
| 	top: -1px; | 	top: -1px; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| .miner-name { |  | ||||||
|   margin-right: 4px; |  | ||||||
|   vertical-align: top; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| .pool-logo { |  | ||||||
|   width: 25px; |  | ||||||
|   height: 25px; |  | ||||||
|   position: relative; |  | ||||||
|   top: -1px; |  | ||||||
|   margin-right: 2px; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| .badge.badge-accelerated { | .badge.badge-accelerated { | ||||||
|   background-color: var(--tertiary); |   background-color: var(--tertiary); | ||||||
|   color: white; |   color: white; | ||||||
| @ -94,7 +82,7 @@ | |||||||
| 	margin-bottom: 40px; | 	margin-bottom: 40px; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| .row{ | .row { | ||||||
| 	flex-direction: column; | 	flex-direction: column; | ||||||
| 	@media (min-width: 850px) { | 	@media (min-width: 850px) { | ||||||
| 		flex-direction: row; | 		flex-direction: row; | ||||||
| @ -337,4 +325,4 @@ | |||||||
| .disabled { | .disabled { | ||||||
|   opacity: 0.5; |   opacity: 0.5; | ||||||
|   pointer-events: none; |   pointer-events: none; | ||||||
| } | } | ||||||
|  | |||||||
| @ -38,7 +38,7 @@ import { ZONE_SERVICE } from '@app/injection-tokens'; | |||||||
| import { MiningService, MiningStats } from '@app/services/mining.service'; | import { MiningService, MiningStats } from '@app/services/mining.service'; | ||||||
| import { ETA, EtaService } from '@app/services/eta.service'; | import { ETA, EtaService } from '@app/services/eta.service'; | ||||||
| 
 | 
 | ||||||
| interface Pool { | export interface Pool { | ||||||
|   id: number; |   id: number; | ||||||
|   name: string; |   name: string; | ||||||
|   slug: string; |   slug: string; | ||||||
|  | |||||||
| @ -2,8 +2,10 @@ import { NgModule } from '@angular/core'; | |||||||
| import { CommonModule } from '@angular/common'; | import { CommonModule } from '@angular/common'; | ||||||
| import { Routes, RouterModule } from '@angular/router'; | import { Routes, RouterModule } from '@angular/router'; | ||||||
| import { TransactionComponent } from '@components/transaction/transaction.component'; | import { TransactionComponent } from '@components/transaction/transaction.component'; | ||||||
|  | import { TransactionDetailsComponent } from '@components/transaction/transaction-details/transaction-details.component'; | ||||||
| import { SharedModule } from '@app/shared/shared.module'; | import { SharedModule } from '@app/shared/shared.module'; | ||||||
| import { TxBowtieModule } from '@components/tx-bowtie-graph/tx-bowtie.module'; | import { TxBowtieModule } from '@components/tx-bowtie-graph/tx-bowtie.module'; | ||||||
|  | import { TransactionExtrasModule } from '@components/transaction/transaction-extras.module'; | ||||||
| import { GraphsModule } from '@app/graphs/graphs.module'; | import { GraphsModule } from '@app/graphs/graphs.module'; | ||||||
| import { AccelerateCheckout } from '@components/accelerate-checkout/accelerate-checkout.component'; | import { AccelerateCheckout } from '@components/accelerate-checkout/accelerate-checkout.component'; | ||||||
| import { AccelerateFeeGraphComponent } from '@components/accelerate-checkout/accelerate-fee-graph.component'; | import { AccelerateFeeGraphComponent } from '@components/accelerate-checkout/accelerate-fee-graph.component'; | ||||||
| @ -40,14 +42,17 @@ export class TransactionRoutingModule { } | |||||||
|     SharedModule, |     SharedModule, | ||||||
|     GraphsModule, |     GraphsModule, | ||||||
|     TxBowtieModule, |     TxBowtieModule, | ||||||
|  |     TransactionExtrasModule, | ||||||
|   ], |   ], | ||||||
|   declarations: [ |   declarations: [ | ||||||
|     TransactionComponent, |     TransactionComponent, | ||||||
|  |     TransactionDetailsComponent, | ||||||
|     AccelerateCheckout, |     AccelerateCheckout, | ||||||
|     AccelerateFeeGraphComponent, |     AccelerateFeeGraphComponent, | ||||||
|   ], |   ], | ||||||
|   exports: [ |   exports: [ | ||||||
|     TransactionComponent, |     TransactionComponent, | ||||||
|  |     TransactionDetailsComponent, | ||||||
|     AccelerateCheckout, |     AccelerateCheckout, | ||||||
|     AccelerateFeeGraphComponent, |     AccelerateFeeGraphComponent, | ||||||
|   ] |   ] | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user