Redesign top of address page
This commit is contained in:
parent
746a045c48
commit
9514bb703b
@ -4,7 +4,7 @@
|
|||||||
<a [routerLink]="['/lightning/channel' | relativeUrl, channel.id]">
|
<a [routerLink]="['/lightning/channel' | relativeUrl, channel.id]">
|
||||||
<span
|
<span
|
||||||
*ngIf="label"
|
*ngIf="label"
|
||||||
class="badge badge-pill badge-warning"
|
class="badge badge-pill badge-warning {{ class }}"
|
||||||
>{{ label }}</span>
|
>{{ label }}</span>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
@ -15,6 +15,6 @@
|
|||||||
<ng-template #default>
|
<ng-template #default>
|
||||||
<span
|
<span
|
||||||
*ngIf="label"
|
*ngIf="label"
|
||||||
class="badge badge-pill badge-warning"
|
class="badge badge-pill badge-warning {{ class }}"
|
||||||
>{{ label }}</span>
|
>{{ label }}</span>
|
||||||
</ng-template>
|
</ng-template>
|
@ -15,6 +15,7 @@ export class AddressLabelsComponent implements OnChanges {
|
|||||||
@Input() vin: Vin;
|
@Input() vin: Vin;
|
||||||
@Input() vout: Vout;
|
@Input() vout: Vout;
|
||||||
@Input() channel: any;
|
@Input() channel: any;
|
||||||
|
@Input() class: string = '';
|
||||||
|
|
||||||
label?: string;
|
label?: string;
|
||||||
|
|
||||||
|
@ -14,40 +14,39 @@
|
|||||||
<div class="box">
|
<div class="box">
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md">
|
@if (isMobile) {
|
||||||
<table class="table table-borderless table-striped address-table">
|
<div class="col-sm">
|
||||||
<tbody>
|
<table class="table table-borderless table-striped">
|
||||||
<tr *ngIf="addressInfo && addressInfo.unconfidential">
|
<tbody>
|
||||||
<td i18n="address.unconfidential">Unconfidential</td>
|
<ng-container *ngTemplateOutlet="balanceRow"></ng-container>
|
||||||
<td>
|
<ng-container *ngTemplateOutlet="pendingBalanceRow"></ng-container>
|
||||||
<app-truncate [text]="addressInfo.unconfidential" [lastChars]="8" [link]="['/address/' | relativeUrl, addressInfo.unconfidential]">
|
<ng-container *ngTemplateOutlet="utxoRow"></ng-container>
|
||||||
<app-clipboard [text]="addressInfo.unconfidential"></app-clipboard>
|
<ng-container *ngTemplateOutlet="pendingUtxoRow"></ng-container>
|
||||||
</app-truncate>
|
<ng-container *ngTemplateOutlet="volumeRow"></ng-container>
|
||||||
</td>
|
<ng-container *ngTemplateOutlet="typeRow"></ng-container>
|
||||||
</tr>
|
</tbody>
|
||||||
<ng-template [ngIf]="!address.electrum">
|
</table>
|
||||||
<tr>
|
|
||||||
<td i18n="address.total-received">Total received</td>
|
|
||||||
<td *ngIf="address.chain_stats.funded_txo_sum !== undefined; else confidentialTd"><app-amount [satoshis]="chainStats.funded_txo_sum + mempoolStats.funded_txo_sum" [noFiat]="true"></app-amount></td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td i18n="address.total-sent">Total sent</td>
|
|
||||||
<td *ngIf="address.chain_stats.spent_txo_sum !== undefined; else confidentialTd"><app-amount [satoshis]="chainStats.spent_txo_sum + mempoolStats.spent_txo_sum" [noFiat]="true"></app-amount></td>
|
|
||||||
</tr>
|
|
||||||
</ng-template>
|
|
||||||
<tr>
|
|
||||||
<td i18n="address.balance">Balance</td>
|
|
||||||
<td *ngIf="address.chain_stats.funded_txo_sum !== undefined; else confidentialTd"><app-amount [satoshis]="chainStats.balance + mempoolStats.balance" [noFiat]="true"></app-amount> <span class="fiat"><app-fiat [value]="chainStats.balance + mempoolStats.balance"></app-fiat></span></td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
<div class="w-100 d-block d-md-none"></div>
|
|
||||||
<div class="col-md qrcode-col">
|
|
||||||
<div class="qr-wrapper">
|
|
||||||
<app-qrcode [data]="address.address"></app-qrcode>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
} @else {
|
||||||
|
<div class="col-sm">
|
||||||
|
<table class="table table-borderless table-striped">
|
||||||
|
<tbody>
|
||||||
|
<ng-container *ngTemplateOutlet="balanceRow"></ng-container>
|
||||||
|
<ng-container *ngTemplateOutlet="utxoRow"></ng-container>
|
||||||
|
<ng-container *ngTemplateOutlet="volumeRow"></ng-container>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<div class="col-sm">
|
||||||
|
<table class="table table-borderless table-striped">
|
||||||
|
<tbody>
|
||||||
|
<ng-container *ngTemplateOutlet="pendingBalanceRow"></ng-container>
|
||||||
|
<ng-container *ngTemplateOutlet="pendingUtxoRow"></ng-container>
|
||||||
|
<ng-container *ngTemplateOutlet="typeRow"></ng-container>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -182,3 +181,57 @@
|
|||||||
<span class="skeleton-loader"></span>
|
<span class="skeleton-loader"></span>
|
||||||
</div>
|
</div>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
|
|
||||||
|
|
||||||
|
<ng-template #balanceRow>
|
||||||
|
<tr>
|
||||||
|
<td i18n="address.balance">Balance</td>
|
||||||
|
<td *ngIf="chainStats.funded_txo_sum !== undefined; else confidentialTd"><app-amount [satoshis]="chainStats.balance" [noFiat]="true"></app-amount> <span class="fiat"><app-fiat [value]="chainStats.balance"></app-fiat></span></td>
|
||||||
|
</tr>
|
||||||
|
</ng-template>
|
||||||
|
|
||||||
|
<ng-template #pendingBalanceRow>
|
||||||
|
<tr>
|
||||||
|
<td i18n="address.pending" class="font-italic">pending</td>
|
||||||
|
<td *ngIf="mempoolStats.funded_txo_sum !== undefined; else confidentialTd" class="font-italic"><app-amount [satoshis]="mempoolStats.balance" [noFiat]="true" [addPlus]="true"></app-amount> <span class="fiat"><app-fiat [value]="mempoolStats.balance"></app-fiat></span></td>
|
||||||
|
</tr>
|
||||||
|
</ng-template>
|
||||||
|
|
||||||
|
<ng-template #utxoRow>
|
||||||
|
<tr>
|
||||||
|
<td i18n="address.unspent_txos">Unspent TXOs</td>
|
||||||
|
<td>{{ chainStats.utxos }}</td>
|
||||||
|
</tr>
|
||||||
|
</ng-template>
|
||||||
|
|
||||||
|
<ng-template #pendingUtxoRow>
|
||||||
|
<tr>
|
||||||
|
<td i18n="address.pending" class="font-italic">pending</td>
|
||||||
|
<td class="font-italic">{{ mempoolStats.utxos > 0 ? '+' : ''}}{{ mempoolStats.utxos }}</td>
|
||||||
|
</tr>
|
||||||
|
</ng-template>
|
||||||
|
|
||||||
|
<ng-template #volumeRow>
|
||||||
|
<tr>
|
||||||
|
<td i18n="address.volume">Volume</td>
|
||||||
|
<td><app-amount [satoshis]="chainStats.volume + mempoolStats.volume"></app-amount></td>
|
||||||
|
</tr>
|
||||||
|
</ng-template>
|
||||||
|
|
||||||
|
<ng-template #typeRow>
|
||||||
|
<tr>
|
||||||
|
<td i18n="address.type">Type</td>
|
||||||
|
<td><app-address-type [vout]="exampleVout || exampleVin?.prevout || null"></app-address-type><app-address-labels [channel]="exampleChannel" [vin]="exampleVin" [vout]="exampleVout" class="ml-1"></app-address-labels></td>
|
||||||
|
</tr>
|
||||||
|
</ng-template>
|
||||||
|
|
||||||
|
<ng-template #liquidRow>
|
||||||
|
<tr *ngIf="addressInfo && addressInfo.unconfidential">
|
||||||
|
<td i18n="address.unconfidential">Unconfidential</td>
|
||||||
|
<td>
|
||||||
|
<app-truncate [text]="addressInfo.unconfidential" [lastChars]="8" [link]="['/address/' | relativeUrl, addressInfo.unconfidential]">
|
||||||
|
<app-clipboard [text]="addressInfo.unconfidential"></app-clipboard>
|
||||||
|
</app-truncate>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</ng-template>
|
@ -25,7 +25,7 @@
|
|||||||
tr td {
|
tr td {
|
||||||
&:last-child {
|
&:last-child {
|
||||||
text-align: right;
|
text-align: right;
|
||||||
@media (min-width: 576px) {
|
@media (min-width: 768px) {
|
||||||
text-align: left;
|
text-align: left;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import { Component, OnInit, OnDestroy } from '@angular/core';
|
import { Component, OnInit, OnDestroy, HostListener } from '@angular/core';
|
||||||
import { ActivatedRoute, ParamMap } from '@angular/router';
|
import { ActivatedRoute, ParamMap } from '@angular/router';
|
||||||
import { ElectrsApiService } from '../../services/electrs-api.service';
|
import { ElectrsApiService } from '../../services/electrs-api.service';
|
||||||
import { switchMap, filter, catchError, map, tap } from 'rxjs/operators';
|
import { switchMap, filter, catchError, map, tap } from 'rxjs/operators';
|
||||||
import { Address, ChainStats, Transaction } from '../../interfaces/electrs.interface';
|
import { Address, ChainStats, Transaction, Vin, Vout } from '../../interfaces/electrs.interface';
|
||||||
import { WebsocketService } from '../../services/websocket.service';
|
import { WebsocketService } from '../../services/websocket.service';
|
||||||
import { StateService } from '../../services/state.service';
|
import { StateService } from '../../services/state.service';
|
||||||
import { AudioService } from '../../services/audio.service';
|
import { AudioService } from '../../services/audio.service';
|
||||||
@ -79,6 +79,10 @@ class AddressStats implements ChainStats {
|
|||||||
return this.funded_txo_sum - this.spent_txo_sum;
|
return this.funded_txo_sum - this.spent_txo_sum;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get volume(): number {
|
||||||
|
return this.funded_txo_sum + this.spent_txo_sum;
|
||||||
|
}
|
||||||
|
|
||||||
get utxos(): number {
|
get utxos(): number {
|
||||||
return this.funded_txo_count - this.spent_txo_count;
|
return this.funded_txo_count - this.spent_txo_count;
|
||||||
}
|
}
|
||||||
@ -92,6 +96,8 @@ class AddressStats implements ChainStats {
|
|||||||
export class AddressComponent implements OnInit, OnDestroy {
|
export class AddressComponent implements OnInit, OnDestroy {
|
||||||
network = '';
|
network = '';
|
||||||
|
|
||||||
|
isMobile: boolean;
|
||||||
|
|
||||||
address: Address;
|
address: Address;
|
||||||
addressString: string;
|
addressString: string;
|
||||||
isLoadingAddress = true;
|
isLoadingAddress = true;
|
||||||
@ -110,6 +116,10 @@ export class AddressComponent implements OnInit, OnDestroy {
|
|||||||
chainStats: AddressStats;
|
chainStats: AddressStats;
|
||||||
mempoolStats: AddressStats;
|
mempoolStats: AddressStats;
|
||||||
|
|
||||||
|
exampleChannel?: any;
|
||||||
|
exampleVin?: Vin;
|
||||||
|
exampleVout?: Vout;
|
||||||
|
|
||||||
now = Date.now() / 1000;
|
now = Date.now() / 1000;
|
||||||
balancePeriod: 'all' | '1m' = 'all';
|
balancePeriod: 'all' | '1m' = 'all';
|
||||||
|
|
||||||
@ -147,6 +157,9 @@ export class AddressComponent implements OnInit, OnDestroy {
|
|||||||
this.isLoadingTransactions = true;
|
this.isLoadingTransactions = true;
|
||||||
this.transactions = null;
|
this.transactions = null;
|
||||||
this.addressInfo = null;
|
this.addressInfo = null;
|
||||||
|
this.exampleChannel = null;
|
||||||
|
this.exampleVin = null;
|
||||||
|
this.exampleVout = null;
|
||||||
document.body.scrollTo(0, 0);
|
document.body.scrollTo(0, 0);
|
||||||
this.addressString = params.get('id') || '';
|
this.addressString = params.get('id') || '';
|
||||||
if (/^[A-Z]{2,5}1[AC-HJ-NP-Z02-9]{8,100}|04[a-fA-F0-9]{128}|(02|03)[a-fA-F0-9]{64}$/.test(this.addressString)) {
|
if (/^[A-Z]{2,5}1[AC-HJ-NP-Z02-9]{8,100}|04[a-fA-F0-9]{128}|(02|03)[a-fA-F0-9]{64}$/.test(this.addressString)) {
|
||||||
@ -252,6 +265,18 @@ export class AddressComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
this.isLoadingTransactions = false;
|
this.isLoadingTransactions = false;
|
||||||
|
|
||||||
|
for (const tx of this.transactions) {
|
||||||
|
if (!this.exampleVin) {
|
||||||
|
this.exampleVin = tx.vin.find(v => v.prevout?.scriptpubkey_address === this.address.address);
|
||||||
|
}
|
||||||
|
if (!this.exampleVout) {
|
||||||
|
this.exampleVout = tx.vout.find(v => v.scriptpubkey_address === this.address.address);
|
||||||
|
}
|
||||||
|
if (this.exampleVin && this.exampleVout) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!this.showBalancePeriod()) {
|
if (!this.showBalancePeriod()) {
|
||||||
this.setBalancePeriod('all');
|
this.setBalancePeriod('all');
|
||||||
} else {
|
} else {
|
||||||
@ -370,6 +395,11 @@ export class AddressComponent implements OnInit, OnDestroy {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@HostListener('window:resize', ['$event'])
|
||||||
|
onResize(): void {
|
||||||
|
this.isMobile = window.innerWidth < 768;
|
||||||
|
}
|
||||||
|
|
||||||
ngOnDestroy(): void {
|
ngOnDestroy(): void {
|
||||||
this.mainSubscription.unsubscribe();
|
this.mainSubscription.unsubscribe();
|
||||||
this.mempoolTxSubscription.unsubscribe();
|
this.mempoolTxSubscription.unsubscribe();
|
||||||
|
@ -0,0 +1,29 @@
|
|||||||
|
@switch (vout?.scriptpubkey_type || null) {
|
||||||
|
@case ('fee') {
|
||||||
|
<span i18n="address.fee">fee</span>
|
||||||
|
}
|
||||||
|
@case ('empty') {
|
||||||
|
<span i18n="address.empty">empty</span>
|
||||||
|
}
|
||||||
|
@case ('v0_p2wpkh') {
|
||||||
|
<span>P2WPKH</span>
|
||||||
|
}
|
||||||
|
@case ('v0_p2wsh') {
|
||||||
|
<span>P2WSH</span>
|
||||||
|
}
|
||||||
|
@case ('v1_p2tr') {
|
||||||
|
<span>P2TR</span>
|
||||||
|
}
|
||||||
|
@case ('provably_unspendable') {
|
||||||
|
<span i18n="address.provably-unspendable">provably unspendable</span>
|
||||||
|
}
|
||||||
|
@case ('multisig') {
|
||||||
|
<span i18n="address.bare-multisig">bare multisig</span>
|
||||||
|
}
|
||||||
|
@case (null) {
|
||||||
|
<span>unknown</span>
|
||||||
|
}
|
||||||
|
@default {
|
||||||
|
<span>{{ vout.scriptpubkey_type.toUpperCase() }}</span>
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
import { Component, Input } from '@angular/core';
|
||||||
|
import { Vout } from '../../../interfaces/electrs.interface';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-address-type',
|
||||||
|
templateUrl: './address-type.component.html',
|
||||||
|
styleUrls: []
|
||||||
|
})
|
||||||
|
export class AddressTypeComponent {
|
||||||
|
@Input() vout: Vout;
|
||||||
|
}
|
@ -87,6 +87,7 @@ import { ChangeComponent } from '../components/change/change.component';
|
|||||||
import { SatsComponent } from './components/sats/sats.component';
|
import { SatsComponent } from './components/sats/sats.component';
|
||||||
import { BtcComponent } from './components/btc/btc.component';
|
import { BtcComponent } from './components/btc/btc.component';
|
||||||
import { FeeRateComponent } from './components/fee-rate/fee-rate.component';
|
import { FeeRateComponent } from './components/fee-rate/fee-rate.component';
|
||||||
|
import { AddressTypeComponent } from './components/address-type/address-type.component';
|
||||||
import { TruncateComponent } from './components/truncate/truncate.component';
|
import { TruncateComponent } from './components/truncate/truncate.component';
|
||||||
import { SearchResultsComponent } from '../components/search-form/search-results/search-results.component';
|
import { SearchResultsComponent } from '../components/search-form/search-results/search-results.component';
|
||||||
import { TimestampComponent } from './components/timestamp/timestamp.component';
|
import { TimestampComponent } from './components/timestamp/timestamp.component';
|
||||||
@ -202,6 +203,7 @@ import { OnlyVsizeDirective, OnlyWeightDirective } from './components/weight-dir
|
|||||||
SatsComponent,
|
SatsComponent,
|
||||||
BtcComponent,
|
BtcComponent,
|
||||||
FeeRateComponent,
|
FeeRateComponent,
|
||||||
|
AddressTypeComponent,
|
||||||
TruncateComponent,
|
TruncateComponent,
|
||||||
SearchResultsComponent,
|
SearchResultsComponent,
|
||||||
TimestampComponent,
|
TimestampComponent,
|
||||||
@ -343,6 +345,7 @@ import { OnlyVsizeDirective, OnlyWeightDirective } from './components/weight-dir
|
|||||||
SatsComponent,
|
SatsComponent,
|
||||||
BtcComponent,
|
BtcComponent,
|
||||||
FeeRateComponent,
|
FeeRateComponent,
|
||||||
|
AddressTypeComponent,
|
||||||
TruncateComponent,
|
TruncateComponent,
|
||||||
SearchResultsComponent,
|
SearchResultsComponent,
|
||||||
TimestampComponent,
|
TimestampComponent,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user