Merge pull request #1304 from mempool/simon/highlight-address

Address page highlight and transfer value
This commit is contained in:
wiz 2022-03-14 15:31:23 +00:00 committed by GitHub
commit 74e8c18b9d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 53 additions and 16 deletions

View File

@ -62,7 +62,7 @@
</h2> </h2>
</div> </div>
<app-transactions-list [transactions]="transactions" [showConfirmations]="true" (loadMore)="loadMore()"></app-transactions-list> <app-transactions-list [transactions]="transactions" [showConfirmations]="true" [address]="address.address" (loadMore)="loadMore()"></app-transactions-list>
<div class="text-center"> <div class="text-center">
<ng-template [ngIf]="isLoadingTransactions"> <ng-template [ngIf]="isLoadingTransactions">

View File

@ -1,12 +1,12 @@
<ng-container *ngIf="!noFiat && (viewFiat$ | async) && (conversions$ | async) as conversions; else viewFiatVin"> <ng-container *ngIf="!noFiat && (viewFiat$ | async) && (conversions$ | async) as conversions; else viewFiatVin">
<span class="fiat">{{ conversions.USD * (satoshis / 100000000) | currency:'USD':'symbol':'1.2-2' }}</span> <span class="fiat">{{ addPlus && satoshis >= 0 ? '+' : '' }}{{ conversions.USD * (satoshis / 100000000) | currency:'USD':'symbol':'1.2-2' }}</span>
</ng-container> </ng-container>
<ng-template #viewFiatVin> <ng-template #viewFiatVin>
<ng-template [ngIf]="(network === 'liquid' || network === 'liquidtestnet') && (satoshis === undefined || satoshis === null)" [ngIfElse]="default"> <ng-template [ngIf]="(network === 'liquid' || network === 'liquidtestnet') && (satoshis === undefined || satoshis === null)" [ngIfElse]="default">
<span i18n="shared.confidential">Confidential</span> <span i18n="shared.confidential">Confidential</span>
</ng-template> </ng-template>
<ng-template #default> <ng-template #default>
&lrm;{{ satoshis / 100000000 | number : digitsInfo }} &lrm;{{ addPlus && satoshis >= 0 ? '+' : '' }}{{ satoshis / 100000000 | number : digitsInfo }}
<span class="symbol"><ng-template [ngIf]="network === 'liquid'">L-</ng-template> <span class="symbol"><ng-template [ngIf]="network === 'liquid'">L-</ng-template>
<ng-template [ngIf]="network === 'liquidtestnet'">tL-</ng-template> <ng-template [ngIf]="network === 'liquidtestnet'">tL-</ng-template>
<ng-template [ngIf]="network === 'testnet'">t</ng-template> <ng-template [ngIf]="network === 'testnet'">t</ng-template>

View File

@ -18,6 +18,7 @@ export class AmountComponent implements OnInit, OnDestroy {
@Input() satoshis: number; @Input() satoshis: number;
@Input() digitsInfo = '1.8-8'; @Input() digitsInfo = '1.8-8';
@Input() noFiat = false; @Input() noFiat = false;
@Input() addPlus = false;
constructor( constructor(
private stateService: StateService, private stateService: StateService,

View File

@ -21,7 +21,10 @@
<table class="table table-borderless smaller-text table-sm" id="table-tx-vin"> <table class="table table-borderless smaller-text table-sm" id="table-tx-vin">
<tbody> <tbody>
<ng-template ngFor let-vin [ngForOf]="tx['@vinLimit'] ? ((tx.vin.length>12)?tx.vin.slice(0, 10): tx.vin.slice(0, 12)) : tx.vin" [ngForTrackBy]="trackByIndexFn"> <ng-template ngFor let-vin [ngForOf]="tx['@vinLimit'] ? ((tx.vin.length>12)?tx.vin.slice(0, 10): tx.vin.slice(0, 12)) : tx.vin" [ngForTrackBy]="trackByIndexFn">
<tr [ngClass]="assetsMinimal && vin.prevout && assetsMinimal[vin.prevout.asset] && !vin.is_coinbase && vin.prevout.scriptpubkey_address && tx._unblinded ? 'assetBox' : ''"> <tr [ngClass]="{
'assetBox': assetsMinimal && vin.prevout && assetsMinimal[vin.prevout.asset] && !vin.is_coinbase && vin.prevout.scriptpubkey_address && tx._unblinded,
'highlight': vin.prevout?.scriptpubkey_address === this.address && this.address !== ''
}">
<td class="arrow-td"> <td class="arrow-td">
<ng-template [ngIf]="vin.prevout === null && !vin.is_pegin" [ngIfElse]="hasPrevout"> <ng-template [ngIf]="vin.prevout === null && !vin.is_pegin" [ngIfElse]="hasPrevout">
<span class="grey"> <span class="grey">
@ -143,7 +146,10 @@
<table class="table table-borderless smaller-text table-sm" id="table-tx-vout"> <table class="table table-borderless smaller-text table-sm" id="table-tx-vout">
<tbody> <tbody>
<ng-template ngFor let-vout let-vindex="index" [ngForOf]="tx['@voutLimit'] && !outputIndex ? ((tx.vout.length > 12) ? tx.vout.slice(0, 10) : tx.vout.slice(0, 12)) : tx.vout" [ngForTrackBy]="trackByIndexFn"> <ng-template ngFor let-vout let-vindex="index" [ngForOf]="tx['@voutLimit'] && !outputIndex ? ((tx.vout.length > 12) ? tx.vout.slice(0, 10) : tx.vout.slice(0, 12)) : tx.vout" [ngForTrackBy]="trackByIndexFn">
<tr [ngClass]="assetsMinimal && assetsMinimal[vout.asset] && vout.scriptpubkey_address && tx.vin && !tx.vin[0].is_coinbase && tx._unblinded || outputIndex === vindex ? 'assetBox' : ''"> <tr [ngClass]="{
'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 !== ''
}">
<td> <td>
<a *ngIf="vout.scriptpubkey_address; else scriptpubkey_type" [routerLink]="['/address/' | relativeUrl, vout.scriptpubkey_address]" title="{{ vout.scriptpubkey_address }}"> <a *ngIf="vout.scriptpubkey_address; else scriptpubkey_type" [routerLink]="['/address/' | relativeUrl, vout.scriptpubkey_address]" title="{{ vout.scriptpubkey_address }}">
<span class="d-block d-lg-none">{{ vout.scriptpubkey_address | shortenString : 16 }}</span> <span class="d-block d-lg-none">{{ vout.scriptpubkey_address | shortenString : 16 }}</span>
@ -242,13 +248,13 @@
</div> </div>
</div> </div>
<div> <div class="summary">
<div class="float-left mt-2-5" *ngIf="!transactionPage && !tx.vin[0].is_coinbase"> <div class="float-left mt-2-5" *ngIf="!transactionPage && !tx.vin[0].is_coinbase">
{{ tx.fee / (tx.weight / 4) | feeRounding }} <span class="symbol" i18n="shared.sat-vbyte|sat/vB">sat/vB</span> <span class="d-none d-sm-inline-block">&nbsp;&ndash; {{ tx.fee | number }} <span class="symbol" i18n="shared.sat|sat">sat</span> <span class="fiat"><app-fiat [value]="tx.fee"></app-fiat></span></span> {{ tx.fee / (tx.weight / 4) | feeRounding }} <span class="symbol" i18n="shared.sat-vbyte|sat/vB">sat/vB</span> <span class="d-none d-sm-inline-block">&nbsp;&ndash; {{ tx.fee | number }} <span class="symbol" i18n="shared.sat|sat">sat</span> <span class="fiat"><app-fiat [value]="tx.fee"></app-fiat></span></span>
</div> </div>
<div class="float-right"> <div class="float-right">
<span *ngIf="showConfirmations && latestBlock$ | async as latestBlock"> <ng-container *ngIf="showConfirmations && latestBlock$ | async as latestBlock">
<button *ngIf="tx.status.confirmed; else unconfirmedButton" type="button" class="btn btn-sm btn-success mt-2"> <button *ngIf="tx.status.confirmed; else unconfirmedButton" type="button" class="btn btn-sm btn-success mt-2">
<ng-container *ngTemplateOutlet="latestBlock.height - tx.status.block_height + 1 == 1 ? confirmationSingular : confirmationPlural; context: {$implicit: latestBlock.height - tx.status.block_height + 1}"></ng-container> <ng-container *ngTemplateOutlet="latestBlock.height - tx.status.block_height + 1 == 1 ? confirmationSingular : confirmationPlural; context: {$implicit: latestBlock.height - tx.status.block_height + 1}"></ng-container>
<ng-template #confirmationSingular let-i i18n="shared.confirmation-count.singular|Transaction singular confirmation count">{{ i }} confirmation</ng-template> <ng-template #confirmationSingular let-i i18n="shared.confirmation-count.singular|Transaction singular confirmation count">{{ i }} confirmation</ng-template>
@ -257,14 +263,18 @@
<ng-template #unconfirmedButton> <ng-template #unconfirmedButton>
<button type="button" class="btn btn-sm btn-danger mt-2" i18n="transaction.unconfirmed|Transaction unconfirmed state">Unconfirmed</button> <button type="button" class="btn btn-sm btn-danger mt-2" i18n="transaction.unconfirmed|Transaction unconfirmed state">Unconfirmed</button>
</ng-template> </ng-template>
&nbsp; </ng-container>
</span> <button *ngIf="address === ''; else viewingAddress" type="button" class="btn btn-sm btn-primary mt-2 ml-2" (click)="switchCurrency()">
<button type="button" class="btn btn-sm btn-primary mt-2" (click)="switchCurrency()">
<ng-template [ngIf]="(network === 'liquid' || network === 'liquidtestnet') && haveBlindedOutputValues(tx)" [ngIfElse]="defaultAmount" i18n="shared.confidential">Confidential</ng-template> <ng-template [ngIf]="(network === 'liquid' || network === 'liquidtestnet') && haveBlindedOutputValues(tx)" [ngIfElse]="defaultAmount" i18n="shared.confidential">Confidential</ng-template>
<ng-template #defaultAmount> <ng-template #defaultAmount>
<app-amount [satoshis]="getTotalTxOutput(tx)"></app-amount> <app-amount [satoshis]="getTotalTxOutput(tx)"></app-amount>
</ng-template> </ng-template>
</button> </button>
<ng-template #viewingAddress>
<button type="button" class="btn btn-sm mt-2 ml-2" (click)="switchCurrency()" [ngClass]="{'btn-success': tx['addressValue'] >= 0, 'btn-danger': tx['addressValue'] < 0}">
<app-amount [satoshis]="tx['addressValue']" [addPlus]="true"></app-amount>
</button>
</ng-template>
</div> </div>
<div class="clearfix"></div> <div class="clearfix"></div>
</div> </div>

View File

@ -1,10 +1,12 @@
.arrow-td { .arrow-td {
width: 20px; width: 20px;
padding-top: 0;
padding-bottom: 0;
} }
.green, .grey, .red { .green, .grey, .red {
font-size: 16px; font-size: 16px;
top: -2px; top: 1px;
position: relative; position: relative;
@media( min-width: 576px){ @media( min-width: 576px){
font-size: 19px; font-size: 19px;
@ -119,3 +121,11 @@
h2 { h2 {
line-height: 1; line-height: 1;
} }
.highlight {
background-color: #181b2d;
}
.summary {
margin-top: 10px;
}

View File

@ -1,11 +1,11 @@
import { Component, OnInit, Input, ChangeDetectionStrategy, OnChanges, Output, EventEmitter, ChangeDetectorRef } from '@angular/core'; import { Component, OnInit, Input, ChangeDetectionStrategy, OnChanges, Output, EventEmitter, ChangeDetectorRef } from '@angular/core';
import { StateService } from '../../services/state.service'; import { StateService } from '../../services/state.service';
import { Observable, forkJoin, ReplaySubject, BehaviorSubject, merge, of, Subject, Subscription } from 'rxjs'; import { Observable, forkJoin, ReplaySubject, BehaviorSubject, merge, Subscription } from 'rxjs';
import { Outspend, Transaction } from '../../interfaces/electrs.interface'; import { Outspend, Transaction, Vin, Vout } from '../../interfaces/electrs.interface';
import { ElectrsApiService } from '../../services/electrs-api.service'; import { ElectrsApiService } from '../../services/electrs-api.service';
import { environment } from 'src/environments/environment'; import { environment } from 'src/environments/environment';
import { AssetsService } from 'src/app/services/assets.service'; import { AssetsService } from 'src/app/services/assets.service';
import { map, share, switchMap, tap } from 'rxjs/operators'; import { map, switchMap } from 'rxjs/operators';
import { BlockExtended } from 'src/app/interfaces/node-api.interface'; import { BlockExtended } from 'src/app/interfaces/node-api.interface';
@Component({ @Component({
@ -23,6 +23,7 @@ export class TransactionsListComponent implements OnInit, OnChanges {
@Input() transactionPage = false; @Input() transactionPage = false;
@Input() errorUnblinded = false; @Input() errorUnblinded = false;
@Input() outputIndex: number; @Input() outputIndex: number;
@Input() address: string = '';
@Output() loadMore = new EventEmitter(); @Output() loadMore = new EventEmitter();
@ -98,6 +99,21 @@ export class TransactionsListComponent implements OnInit, OnChanges {
if (this.outspends[i]) { if (this.outspends[i]) {
return; return;
} }
if (this.address) {
const addressIn = tx.vout
.filter((v: Vout) => v.scriptpubkey_address === this.address)
.map((v: Vout) => v.value || 0)
.reduce((a: number, b: number) => a + b, 0);
const addressOut = tx.vin
.filter((v: Vin) => v.prevout && v.prevout.scriptpubkey_address === this.address)
.map((v: Vin) => v.prevout.value || 0)
.reduce((a: number, b: number) => a + b, 0);
tx['addressValue'] = addressIn || -addressOut;
}
observableObject[i] = this.electrsApiService.getOutspends$(tx.txid); observableObject[i] = this.electrsApiService.getOutspends$(tx.txid);
}); });
this.refreshOutspends$.next(observableObject); this.refreshOutspends$.next(observableObject);
@ -119,7 +135,7 @@ export class TransactionsListComponent implements OnInit, OnChanges {
} }
getTotalTxOutput(tx: Transaction) { getTotalTxOutput(tx: Transaction) {
return tx.vout.map((v: any) => v.value || 0).reduce((a: number, b: number) => a + b); return tx.vout.map((v: Vout) => v.value || 0).reduce((a: number, b: number) => a + b);
} }
switchCurrency() { switchCurrency() {

View File

@ -72,7 +72,7 @@ export interface Vout {
scriptpubkey: string; scriptpubkey: string;
scriptpubkey_asm: string; scriptpubkey_asm: string;
scriptpubkey_type: string; scriptpubkey_type: string;
scriptpubkey_address: string; scriptpubkey_address?: string;
value: number; value: number;
// Elements // Elements
valuecommitment?: number; valuecommitment?: number;