Merge branch 'master' into mononaut/fix-mobile-bottom-nav

This commit is contained in:
wiz
2023-01-27 03:49:07 +09:00
committed by GitHub
92 changed files with 37829 additions and 16586 deletions

View File

@@ -138,6 +138,10 @@
"translation": "src/locale/messages.hi.xlf",
"baseHref": "/hi/"
},
"ne": {
"translation": "src/locale/messages.ne.xlf",
"baseHref": "/ne/"
},
"lt": {
"translation": "src/locale/messages.lt.xlf",
"baseHref": "/lt/"

View File

@@ -116,6 +116,7 @@ export const languages: Language[] = [
// { code: 'hr', name: 'Hrvatski' }, // Croatian
// { code: 'id', name: 'Bahasa Indonesia' },// Indonesian
{ code: 'hi', name: 'हिन्दी' }, // Hindi
{ code: 'ne', name: 'नेपाली' }, // Nepalese
{ code: 'it', name: 'Italiano' }, // Italian
{ code: 'he', name: 'עברית' }, // Hebrew
{ code: 'ka', name: 'ქართული' }, // Georgian

View File

@@ -1,11 +1,9 @@
<div class="container-xl">
<h1 i18n="shared.address">Address</h1>
<span class="address-link">
<a [routerLink]="['/address/' | relativeUrl, addressString]">
<span class="d-inline d-lg-none">{{ addressString | shortenString : 24 }}</span>
<span class="d-none d-lg-inline">{{ addressString }}</span>
</a>
<app-clipboard [text]="addressString"></app-clipboard>
<app-truncate [text]="addressString" [lastChars]="8" [link]="['/address/' | relativeUrl, addressString]">
<app-clipboard [text]="addressString"></app-clipboard>
</app-truncate>
</span>
<br>

View File

@@ -6,12 +6,12 @@
<h1 i18n="shared.transaction">Transaction</h1>
</div>
<span class="tx-link float-left">
<a [routerLink]="['/tx' | relativeUrl, bisqTx.id]">
<span class="d-inline d-lg-none">{{ bisqTx.id | shortenString : 24 }}</span>
<span class="d-none d-lg-inline">{{ bisqTx.id }}</span>
</a>
<app-clipboard [text]="bisqTx.id"></app-clipboard>
<span class="tx-link">
<span class="txid">
<app-truncate [text]="bisqTx.id" [lastChars]="12" [link]="['/tx/' | relativeUrl, bisqTx.id]">
<app-clipboard [text]="bisqTx.id"></app-clipboard>
</app-truncate>
</span>
</span>
<span class="grow"></span>
<div class="container-buttons">

View File

@@ -6,17 +6,16 @@
<div class="col-md">
<div class="row d-flex justify-content-between">
<div class="title-wrapper">
<h1 class="title truncated"><span class="first">{{addressString.slice(0,-4)}}</span><span class="last-four">{{addressString.slice(-4)}}</span></h1>
<h1 class="title"><app-truncate [text]="addressString"></app-truncate></h1>
</div>
</div>
<table class="table table-borderless table-striped">
<tbody>
<tr *ngIf="addressInfo && addressInfo.unconfidential">
<td i18n="address.unconfidential">Unconfidential</td>
<td><a [routerLink]="['/address/' | relativeUrl, addressInfo.unconfidential]">
<span class="d-inline d-lg-none">{{ addressInfo.unconfidential | shortenString : 14 }}</span>
<span class="d-none d-lg-inline">{{ addressInfo.unconfidential }}</span>
</a> <app-clipboard [text]="addressInfo.unconfidential"></app-clipboard></td>
<td>
<app-truncate [text]="addressInfo.unconfidential" [lastChars]="7" [link]="['/address/' | relativeUrl, addressInfo.unconfidential]"></app-truncate>
</td>
</tr>
<ng-template [ngIf]="!address.electrum">
<tr>

View File

@@ -2,11 +2,9 @@
<div class="title-address">
<h1 i18n="shared.address">Address</h1>
<div class="tx-link">
<a [routerLink]="['/address/' | relativeUrl, addressString]" >
<span class="d-inline d-lg-none">{{ addressString | shortenString : 18 }}</span>
<span class="d-none d-lg-inline">{{ addressString }}</span>
</a>
<app-clipboard [text]="addressString"></app-clipboard>
<app-truncate [text]="addressString" [lastChars]="8" [link]="['/address/' | relativeUrl, addressString]">
<app-clipboard [text]="addressString"></app-clipboard>
</app-truncate>
</div>
</div>
@@ -21,10 +19,11 @@
<tbody>
<tr *ngIf="addressInfo && addressInfo.unconfidential">
<td i18n="address.unconfidential">Unconfidential</td>
<td><a [routerLink]="['/address/' | relativeUrl, addressInfo.unconfidential]">
<span class="d-inline d-lg-none">{{ addressInfo.unconfidential | shortenString : 14 }}</span>
<span class="d-none d-lg-inline">{{ addressInfo.unconfidential }}</span>
</a> <app-clipboard [text]="addressInfo.unconfidential"></app-clipboard></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 [ngIf]="!address.electrum">
<tr>

View File

@@ -2,11 +2,9 @@
<div class="title-asset">
<h1 i18n="asset|Liquid Asset page title">Asset</h1>
<div class="tx-link">
<a [routerLink]="['/assets/asset/' | relativeUrl, assetString]">
<span class="d-inline d-lg-none">{{ assetString | shortenString : 24 }}</span>
<span class="d-none d-lg-inline">{{ assetString }}</span>
</a>
<app-clipboard [text]="assetString"></app-clipboard>
<app-truncate [text]="assetString" [lastChars]="8" [link]="['/assets/asset/' | relativeUrl, assetString]">
<app-clipboard [text]="assetString"></app-clipboard>
</app-truncate>
</div>
</div>

View File

@@ -213,6 +213,7 @@ export class BlockFeeRatesGraphComponent implements OnInit {
},
},
legend: (data.series.length === 0) ? undefined : {
padding: [10, 75],
data: data.legends,
selected: JSON.parse(this.storageService.getValue('fee_rates_legend')) ?? {
'Min': true,

View File

@@ -10,12 +10,13 @@ const defaultHoverColor = hexToColor('1bd8f4');
const feeColors = mempoolFeeColors.map(hexToColor);
const auditFeeColors = feeColors.map((color) => darken(desaturate(color, 0.3), 0.9));
const marginalFeeColors = feeColors.map((color) => darken(desaturate(color, 0.8), 1.1));
const auditColors = {
censored: hexToColor('f344df'),
missing: darken(desaturate(hexToColor('f344df'), 0.3), 0.7),
added: hexToColor('0099ff'),
selected: darken(desaturate(hexToColor('0099ff'), 0.3), 0.7),
}
};
// convert from this class's update format to TxSprite's update format
function toSpriteUpdate(params: ViewUpdateParams): SpriteUpdateParams {
@@ -161,13 +162,13 @@ export default class TxView implements TransactionStripped {
case 'censored':
return auditColors.censored;
case 'missing':
return auditColors.missing;
return marginalFeeColors[feeLevelIndex] || marginalFeeColors[mempoolFeeColors.length - 1];
case 'fresh':
return auditColors.missing;
case 'added':
return auditColors.added;
case 'selected':
return auditColors.selected;
return marginalFeeColors[feeLevelIndex] || marginalFeeColors[mempoolFeeColors.length - 1];
case 'found':
if (this.context === 'projected') {
return auditFeeColors[feeLevelIndex] || auditFeeColors[mempoolFeeColors.length - 1];

View File

@@ -35,12 +35,12 @@
<tr *ngIf="tx && tx.status && tx.status.length">
<td class="td-width" i18n="transaction.audit-status">Audit status</td>
<ng-container [ngSwitch]="tx?.status">
<td *ngSwitchCase="'found'" i18n="transaction.audit.match">match</td>
<td *ngSwitchCase="'censored'" i18n="transaction.audit.removed">removed</td>
<td *ngSwitchCase="'missing'" i18n="transaction.audit.marginal">marginal fee rate</td>
<td *ngSwitchCase="'fresh'" i18n="transaction.audit.recently-broadcast">recently broadcast</td>
<td *ngSwitchCase="'added'" i18n="transaction.audit.added">added</td>
<td *ngSwitchCase="'selected'" i18n="transaction.audit.marginal">marginal fee rate</td>
<td *ngSwitchCase="'found'" i18n="transaction.audit.match">Match</td>
<td *ngSwitchCase="'censored'" i18n="transaction.audit.removed">Removed</td>
<td *ngSwitchCase="'missing'" i18n="transaction.audit.marginal">Marginal fee rate</td>
<td *ngSwitchCase="'fresh'" i18n="transaction.audit.recently-broadcasted">Recently broadcasted</td>
<td *ngSwitchCase="'added'" i18n="transaction.audit.added">Added</td>
<td *ngSwitchCase="'selected'" i18n="transaction.audit.marginal">Marginal fee rate</td>
</ng-container>
</tr>
</tbody>

View File

@@ -4,8 +4,7 @@ import { StateService } from '../../services/state.service';
import { specialBlocks } from '../../app.constants';
import { BlockExtended } from '../../interfaces/node-api.interface';
import { Location } from '@angular/common';
import { config } from 'process';
import { CacheService } from 'src/app/services/cache.service';
import { CacheService } from '../../services/cache.service';
interface BlockchainBlock extends BlockExtended {
placeholder?: boolean;

View File

@@ -64,26 +64,6 @@
white-space: nowrap;
margin: 0;
display: inline-block;
&.truncated {
text-overflow: unset;
display: flex;
flex-direction: row;
align-items: baseline;
.first {
flex-grow: 1;
flex-shrink: 1;
overflow: hidden;
text-overflow: ellipsis;
margin-right: -2px;
}
.last-four {
flex-shrink: 0;
flex-grow: 0;
}
}
}
::ng-deep .title-wrapper {

View File

@@ -13,21 +13,21 @@
</div>
</div>
<div class="item">
<h5 class="card-title" i18n="mining.rewards-per-tx" i18n-ngbTooltip="mining.rewards-per-tx"
ngbTooltip="Reward Per Tx" placement="bottom" #rewardspertx [disableTooltip]="!isEllipsisActive(rewardspertx)">Reward Per Tx</h5>
<div class="card-text" i18n-ngbTooltip="mining.rewards-per-tx-desc" ngbTooltip="Average miners' reward per transaction in the past 144 blocks" placement="bottom">
<h5 class="card-title" i18n="mining.fees-per-block" i18n-ngbTooltip="mining.fees-per-block"
ngbTooltip="Avg Block Fees" placement="bottom" #rewardsperblock [disableTooltip]="!isEllipsisActive(rewardsperblock)">Avg Block Fees</h5>
<div class="card-text" i18n-ngbTooltip="mining.fees-per-block-desc" ngbTooltip="Average fees per block in the past 144 blocks" placement="bottom">
<div class="fee-text">
{{ rewardStats.rewardPerTx | amountShortener: 2 }}
<span i18n="shared.sat-vbyte|sat/vB">sats/tx</span>
{{ (rewardStats.feePerBlock / 100000000) | amountShortener: 4 }}
<span i18n="shared.btc-block|BTC/block">BTC/block</span>
</div>
<span class="fiat">
<app-fiat [value]="rewardStats.rewardPerTx"></app-fiat>
<app-fiat [value]="rewardStats.feePerBlock"></app-fiat>
</span>
</div>
</div>
<div class="item">
<h5 class="card-title" i18n="mining.average-fee" i18n-ngbTooltip="mining.average-fee"
ngbTooltip="Average Fee" placement="bottom" #averagefee [disableTooltip]="!isEllipsisActive(averagefee)">Average Fee</h5>
ngbTooltip="Avg Tx Fee" placement="bottom" #averagefee [disableTooltip]="!isEllipsisActive(averagefee)">Avg Tx Fee</h5>
<div class="card-text" i18n-ngbTooltip="mining.average-fee" ngbTooltip="Fee paid on average for each transaction in the past 144 blocks" placement="bottom">
<div class="fee-text">{{ rewardStats.feePerTx | amountShortener: 2 }}
<span i18n="shared.sat-vbyte|sat/vB">sats/tx</span>

View File

@@ -42,8 +42,8 @@ export class RewardStatsComponent implements OnInit {
map((stats) => {
return {
totalReward: stats.totalReward,
rewardPerTx: stats.totalReward / stats.totalTx,
feePerTx: stats.totalFee / stats.totalTx,
feePerBlock: stats.totalFee / 144,
};
})
);

View File

@@ -4,7 +4,7 @@
</app-preview-title>
<div class="row d-flex justify-content-between full-width-row">
<div class="title-wrapper">
<h1 class="title truncated"><span class="first">{{txId.slice(0,-4)}}</span><span class="last-four">{{txId.slice(-4)}}</span></h1>
<h1 class="title truncated"><app-truncate [text]="txId"></app-truncate></h1>
</div>
<div *ngIf="network !== 'liquid' && network !== 'liquidtestnet'" class="features">
<app-tx-features [tx]="tx"></app-tx-features>

View File

@@ -3,21 +3,25 @@
<div class="title-block">
<div *ngIf="rbfTransaction" class="alert alert-mempool" role="alert">
<span i18n="transaction.rbf.replacement|RBF replacement">This transaction has been replaced by:</span>
<a class="alert-link" [routerLink]="['/tx/' | relativeUrl, rbfTransaction.txid]">
<span class="d-inline d-lg-none">{{ rbfTransaction.txid | shortenString : 24 }}</span>
<span class="d-none d-lg-inline">{{ rbfTransaction.txid }}</span>
</a>
<app-truncate [text]="rbfTransaction.txid" [lastChars]="12" [link]="['/tx/' | relativeUrl, rbfTransaction.txid]"></app-truncate>
</div>
<ng-container *ngIf="!rbfTransaction || rbfTransaction?.size">
<div *ngIf="rbfReplaces?.length" class="alert alert-mempool" role="alert">
<span i18n="transaction.rbf.replaced|RBF replaced">This transaction replaced:</span>
<div class="tx-list">
<app-truncate [text]="replaced" [lastChars]="12" *ngFor="let replaced of rbfReplaces" [link]="['/tx/' | relativeUrl, replaced]"></app-truncate>
</div>
</div>
<ng-container *ngIf="!rbfTransaction || rbfTransaction?.size || tx">
<h1 i18n="shared.transaction">Transaction</h1>
<span class="tx-link float-left">
<a [routerLink]="['/tx/' | relativeUrl, txId]">
<span class="d-inline d-lg-none">{{ txId | shortenString : 24 }}</span>
<span class="d-none d-lg-inline">{{ txId }}</span>
</a>
<app-clipboard [text]="txId"></app-clipboard>
<span class="tx-link">
<span class="txid">
<app-truncate [text]="txId" [lastChars]="12" [link]="['/tx/' | relativeUrl, txId]">
<app-clipboard [text]="txId"></app-clipboard>
</app-truncate>
</span>
</span>
<div class="container-buttons">
@@ -28,7 +32,10 @@
<ng-template #confirmationPlural let-i i18n="shared.confirmation-count.plural|Transaction plural confirmation count">{{ i }} confirmations</ng-template>
</button>
</ng-template>
<ng-template [ngIf]="tx && !tx?.status.confirmed">
<ng-template [ngIf]="tx && !tx?.status?.confirmed && replaced">
<button type="button" class="btn btn-sm btn-danger" i18n="transaction.unconfirmed|Transaction unconfirmed state">Replaced</button>
</ng-template>
<ng-template [ngIf]="tx && !tx?.status?.confirmed && !replaced">
<button type="button" class="btn btn-sm btn-danger" i18n="transaction.unconfirmed|Transaction unconfirmed state">Unconfirmed</button>
</ng-template>
</div>
@@ -91,7 +98,7 @@
<div class="col-sm">
<table class="table table-borderless table-striped">
<tbody>
<ng-template [ngIf]="transactionTime !== 0">
<ng-template [ngIf]="transactionTime !== 0 && !replaced">
<tr *ngIf="transactionTime === -1; else firstSeenTmpl">
<td><span class="skeleton-loader"></span></td>
<td><span class="skeleton-loader"></span></td>
@@ -103,7 +110,7 @@
</tr>
</ng-template>
</ng-template>
<tr>
<tr *ngIf="!replaced">
<td class="td-width" i18n="transaction.eta|Transaction ETA">ETA</td>
<td>
<ng-template [ngIf]="txInBlockIndex === undefined" [ngIfElse]="estimationTmpl">
@@ -144,12 +151,12 @@
<br>
<h2 class="text-left">CPFP <fa-icon [icon]="['fas', 'info-circle']" [fixedWidth]="true" size="xs"></fa-icon></h2>
<div class="box">
<table class="table table-borderless table-striped">
<div class="box cpfp-details">
<table class="table table-fixed table-borderless table-striped">
<thead>
<tr>
<th i18n="transactions-list.vout.scriptpubkey-type">Type</th>
<th i18n="dashboard.latest-transactions.txid">TXID</th>
<th class="txids" i18n="dashboard.latest-transactions.txid">TXID</th>
<th class="d-none d-lg-table-cell" i18n="transaction.vsize|Transaction Virtual Size">Virtual size</th>
<th i18n="transaction.fee-rate|Transaction fee rate">Fee rate</th>
<th class="d-none d-lg-table-cell"></th>
@@ -159,10 +166,8 @@
<ng-template [ngIf]="cpfpInfo?.descendants?.length">
<tr *ngFor="let cpfpTx of cpfpInfo.descendants">
<td><span class="badge badge-primary" i18n="transaction.descendant|Descendant">Descendant</span></td>
<td><a [routerLink]="['/tx' | relativeUrl, cpfpTx.txid]">
<span class="d-inline d-lg-none">{{ cpfpTx.txid | shortenString : 8 }}</span>
<span class="d-none d-lg-inline">{{ cpfpTx.txid }}</span>
</a>
<td>
<app-truncate [text]="cpfpTx.txid" [link]="['/tx' | relativeUrl, cpfpTx.txid]"></app-truncate>
</td>
<td class="d-none d-lg-table-cell" [innerHTML]="cpfpTx.weight / 4 | vbytes: 2"></td>
<td>{{ cpfpTx.fee / (cpfpTx.weight / 4) | feeRounding }} <span class="symbol" i18n="shared.sat-vbyte|sat/vB">sat/vB</span></td>
@@ -172,11 +177,8 @@
<ng-template [ngIf]="cpfpInfo?.bestDescendant">
<tr>
<td><span class="badge badge-success" i18n="transaction.descendant|Descendant">Descendant</span></td>
<td>
<a [routerLink]="['/tx' | relativeUrl, cpfpInfo.bestDescendant.txid]">
<span class="d-inline d-lg-none">{{ cpfpInfo.bestDescendant.txid | shortenString : 8 }}</span>
<span class="d-none d-lg-inline">{{ cpfpInfo.bestDescendant.txid }}</span>
</a>
<td class="txids">
<app-truncate [text]="cpfpInfo.bestDescendant.txid" [link]="['/tx' | relativeUrl, cpfpInfo.bestDescendant.txid]"></app-truncate>
</td>
<td class="d-none d-lg-table-cell" [innerHTML]="cpfpInfo.bestDescendant.weight / 4 | vbytes: 2"></td>
<td>{{ cpfpInfo.bestDescendant.fee / (cpfpInfo.bestDescendant.weight / 4) | feeRounding }} <span class="symbol" i18n="shared.sat-vbyte|sat/vB">sat/vB</span></td>
@@ -186,10 +188,8 @@
<ng-template [ngIf]="cpfpInfo?.ancestors?.length">
<tr *ngFor="let cpfpTx of cpfpInfo.ancestors">
<td><span class="badge badge-primary" i18n="transaction.ancestor|Transaction Ancestor">Ancestor</span></td>
<td><a [routerLink]="['/tx' | relativeUrl, cpfpTx.txid]">
<span class="d-inline d-lg-none">{{ cpfpTx.txid | shortenString : 8 }}</span>
<span class="d-none d-lg-inline">{{ cpfpTx.txid }}</span>
</a>
<td class="txids">
<app-truncate [text]="cpfpTx.txid" [link]="['/tx' | relativeUrl, cpfpTx.txid]"></app-truncate>
</td>
<td class="d-none d-lg-table-cell" [innerHTML]="cpfpTx.weight / 4 | vbytes: 2"></td>
<td>{{ cpfpTx.fee / (cpfpTx.weight / 4) | feeRounding }} <span class="symbol" i18n="shared.sat-vbyte|sat/vB">sat/vB</span></td>

View File

@@ -19,22 +19,32 @@
}
}
.tx-link {
display: flex;
flex-direction: row;
justify-content: flex-start;
align-items: baseline;
width: 0;
max-width: 100%;
margin-right: 0px;
margin-bottom: 0px;
margin-top: 8px;
display: inline-block;
width: 100%;
flex-shrink: 0;
@media (min-width: 651px) {
display: flex;
width: auto;
flex-grow: 1;
margin-bottom: 0px;
margin-right: 1em;
top: 1px;
position: relative;
}
@media (max-width: 650px) {
width: 100%;
order: 3;
}
.txid {
width: 200px;
min-width: 200px;
flex-grow: 1;
}
}
.td-width {
@@ -188,4 +198,16 @@
margin-left: 8px;
}
}
}
.cpfp-details {
.txids {
width: 60%;
}
}
.tx-list {
.alert-link {
display: block;
}
}

View File

@@ -40,15 +40,21 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy {
transactionTime = -1;
subscription: Subscription;
fetchCpfpSubscription: Subscription;
fetchRbfSubscription: Subscription;
fetchCachedTxSubscription: Subscription;
txReplacedSubscription: Subscription;
blocksSubscription: Subscription;
queryParamsSubscription: Subscription;
urlFragmentSubscription: Subscription;
fragmentParams: URLSearchParams;
rbfTransaction: undefined | Transaction;
replaced: boolean = false;
rbfReplaces: string[];
cpfpInfo: CpfpInfo | null;
showCpfpDetails = false;
fetchCpfp$ = new Subject<string>();
fetchRbfHistory$ = new Subject<string>();
fetchCachedTx$ = new Subject<string>();
now = new Date().getTime();
timeAvg$: Observable<number>;
liquidUnblinding = new LiquidUnblinding();
@@ -122,7 +128,11 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy {
}
}),
delay(2000)
)))
)),
catchError(() => {
return of(null);
})
)
),
catchError(() => {
return of(null);
@@ -155,6 +165,49 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy {
this.cpfpInfo = cpfpInfo;
});
this.fetchRbfSubscription = this.fetchRbfHistory$
.pipe(
switchMap((txId) =>
this.apiService
.getRbfHistory$(txId)
),
catchError(() => {
return of([]);
})
).subscribe((replaces) => {
this.rbfReplaces = replaces;
});
this.fetchCachedTxSubscription = this.fetchCachedTx$
.pipe(
switchMap((txId) =>
this.apiService
.getRbfCachedTx$(txId)
),
catchError(() => {
return of(null);
})
).subscribe((tx) => {
if (!tx) {
return;
}
this.tx = tx;
if (tx.fee === undefined) {
this.tx.fee = 0;
}
this.tx.feePerVsize = tx.fee / (tx.weight / 4);
this.isLoadingTx = false;
this.error = undefined;
this.waitingForTransaction = false;
this.graphExpanded = false;
this.setupGraph();
if (!this.tx?.status?.confirmed) {
this.fetchRbfHistory$.next(this.tx.txid);
}
});
this.subscription = this.route.paramMap
.pipe(
switchMap((params: ParamMap) => {
@@ -268,6 +321,7 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy {
} else {
this.fetchCpfp$.next(this.tx.txid);
}
this.fetchRbfHistory$.next(this.tx.txid);
}
setTimeout(() => { this.applyFragment(); }, 0);
},
@@ -299,6 +353,10 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy {
}
this.rbfTransaction = rbfTransaction;
this.cacheService.setTxCache([this.rbfTransaction]);
this.replaced = true;
if (rbfTransaction && !this.tx) {
this.fetchCachedTx$.next(this.txId);
}
});
this.queryParamsSubscription = this.route.queryParams.subscribe((params) => {
@@ -364,8 +422,10 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy {
this.waitingForTransaction = false;
this.isLoadingTx = true;
this.rbfTransaction = undefined;
this.replaced = false;
this.transactionTime = -1;
this.cpfpInfo = null;
this.rbfReplaces = [];
this.showCpfpDetails = false;
document.body.scrollTo(0, 0);
this.leaveTransaction();
@@ -431,6 +491,8 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy {
ngOnDestroy() {
this.subscription.unsubscribe();
this.fetchCpfpSubscription.unsubscribe();
this.fetchRbfSubscription.unsubscribe();
this.fetchCachedTxSubscription.unsubscribe();
this.txReplacedSubscription.unsubscribe();
this.blocksSubscription.unsubscribe();
this.queryParamsSubscription.unsubscribe();

View File

@@ -1,16 +1,14 @@
<ng-container *ngFor="let tx of transactions; let i = index; trackBy: trackByFn">
<div *ngIf="!transactionPage" class="header-bg box tx-page-container">
<a class="float-left" [routerLink]="['/tx/' | relativeUrl, tx.txid]">
<span style="float: left;" class="d-block d-md-none">{{ tx.txid | shortenString : 16 }}</span>
<span style="float: left;" class="d-none d-md-block">{{ tx.txid }}</span>
<a class="tx-link" [routerLink]="['/tx/' | relativeUrl, tx.txid]">
<app-truncate [text]="tx.txid"></app-truncate>
</a>
<div class="float-right">
<div>
<ng-template [ngIf]="tx.status.confirmed">&lrm;{{ tx.status.block_time * 1000 | date:'yyyy-MM-dd HH:mm' }}</ng-template>
<ng-template [ngIf]="!tx.status.confirmed && tx.firstSeen">
<i><app-time-since [time]="tx.firstSeen" [fastRender]="true"></app-time-since></i>
</ng-template>
</div>
<div class="clearfix"></div>
</div>
<div class="header-bg box" infiniteScroll [alwaysCallback]="true" [infiniteScrollDistance]="2" [infiniteScrollUpDistance]="1.5" [infiniteScrollThrottle]="50" (scrolled)="onScroll()" [attr.data-cy]="'tx-' + i">
@@ -18,7 +16,7 @@
<div *ngIf="errorUnblinded" class="error-unblinded">{{ errorUnblinded }}</div>
<div class="row">
<div class="col">
<table class="table table-borderless smaller-text table-sm table-tx-vin">
<table class="table table-fixed table-borderless smaller-text table-sm table-tx-vin">
<tbody>
<ng-template ngFor let-vin let-vindex="index" [ngForOf]="tx.vin.slice(0, getVinLimit(tx))" [ngForTrackBy]="trackByIndexFn">
<tr [ngClass]="{
@@ -49,7 +47,7 @@
</ng-template>
</ng-template>
</td>
<td>
<td class="address-cell">
<div [ngSwitch]="true">
<ng-container *ngSwitchCase="vin.is_coinbase"><span i18n="transactions-list.coinbase">Coinbase</span><ng-template [ngIf]="network !== 'liquid' && network !== 'liquidtestnet'">&nbsp;<span i18n="transactions-list.newly-generated-coins">(Newly Generated Coins)</span></ng-template><br /><a placement="bottom" [ngbTooltip]="vin.scriptsig | hex2ascii"><span class="badge badge-secondary scriptmessage longer">{{ vin.scriptsig | hex2ascii }}</span></a></ng-container>
<ng-container *ngSwitchCase="vin.is_pegin">
@@ -66,12 +64,8 @@
</ng-template>
</ng-template>
<ng-template #defaultAddress>
<a class="shortable-address" *ngIf="vin.prevout.scriptpubkey_address; else vinScriptPubkeyType" [routerLink]="['/address/' | relativeUrl, vin.prevout.scriptpubkey_address]" title="{{ vin.prevout.scriptpubkey_address }}">
<span class="d-block d-lg-none">{{ vin.prevout.scriptpubkey_address | shortenString : 16 }}</span>
<span class="d-none d-lg-inline-flex justify-content-start">
<span class="addr-left flex-grow-1" [style]="vin.prevout.scriptpubkey_address.length > 40 ? 'max-width: 235px' : ''">{{ vin.prevout.scriptpubkey_address }}</span>
<span *ngIf="vin.prevout.scriptpubkey_address.length > 40" class="addr-right">{{ vin.prevout.scriptpubkey_address | capAddress: 40: 10 }}</span>
</span>
<a class="address" *ngIf="vin.prevout.scriptpubkey_address; else vinScriptPubkeyType" [routerLink]="['/address/' | relativeUrl, vin.prevout.scriptpubkey_address]" title="{{ vin.prevout.scriptpubkey_address }}">
<app-truncate [text]="vin.prevout.scriptpubkey_address" [lastChars]="8"></app-truncate>
</a>
<ng-template #vinScriptPubkeyType>
{{ vin.prevout.scriptpubkey_type?.toUpperCase() }}
@@ -100,7 +94,7 @@
</tr>
<tr *ngIf="(showDetails$ | async) === true">
<td colspan="3" class="details-container" >
<table class="table table-striped table-borderless details-table mb-3">
<table class="table table-striped table-fixed table-borderless details-table mb-3">
<tbody>
<ng-template [ngIf]="vin.scriptsig">
<tr>
@@ -112,9 +106,23 @@
<td style="text-align: left;">{{ vin.scriptsig }}</td>
</tr>
</ng-template>
<tr *ngIf="vin.witness">
<tr *ngIf="vin.witness" class="vin-witness">
<td i18n="transactions-list.witness">Witness</td>
<td style="text-align: left;">{{ vin.witness.join(' ') }}</td>
<td style="text-align: left;">
<ng-container *ngFor="let witness of vin.witness; index as i">
<input type="checkbox" [id]="'tx' + vindex + 'witness' + i" style="display: none;">
<p class="witness-item" [class.accordioned]="witness.length > 1000">
{{ witness }}
</p>
<div class="witness-toggle" *ngIf="witness.length > 1000">
<span class="ellipsis">...</span>
<label [for]="'tx' + vindex + 'witness' + i" class="btn btn-sm btn-primary mt-2">
<span class="show-all" i18n="show-all">Show all</span>
<span class="show-less" i18n="show-less">Show less</span>
</label>
</div>
</ng-container>
</td>
</tr>
<tr *ngIf="vin.inner_redeemscript_asm">
<td i18n="transactions-list.p2sh-redeem-script">P2SH redeem script</td>
@@ -153,7 +161,7 @@
<ng-template #showMoreInputsLabel>
<span i18n="show-more">Show more</span>
</ng-template>
({{ tx.vin.length - getVinLimit(tx) }} <span i18n="inputs-remaining">remaining</span>)
(<ng-container *ngTemplateOutlet="xRemaining; context: {$implicit: tx.vin.length - getVinLimit(tx)}"></ng-container>)
</button>
</td>
</tr>
@@ -162,20 +170,16 @@
</div>
<div class="w-100 d-block d-md-none"></div>
<div class="col mobile-bottomcol">
<table class="table table-borderless smaller-text table-sm table-tx-vout">
<table class="table table-fixed table-borderless smaller-text table-sm table-tx-vout">
<tbody>
<ng-template ngFor let-vout let-vindex="index" [ngForOf]="tx.vout.slice(0, getVoutLimit(tx))" [ngForTrackBy]="trackByIndexFn">
<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>
<a class="shortable-address" *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-none d-lg-inline-flex justify-content-start">
<span class="addr-left flex-grow-1" [style]="vout.scriptpubkey_address.length > 40 ? 'max-width: 235px' : ''">{{ vout.scriptpubkey_address }}</span>
<span *ngIf="vout.scriptpubkey_address.length > 40" class="addr-right">{{ vout.scriptpubkey_address | capAddress: 40: 10 }}</span>
</span>
<td class="address-cell">
<a class="address" *ngIf="vout.scriptpubkey_address; else scriptpubkey_type" [routerLink]="['/address/' | relativeUrl, vout.scriptpubkey_address]" title="{{ vout.scriptpubkey_address }}">
<app-truncate [text]="vout.scriptpubkey_address" [lastChars]="8"></app-truncate>
</a>
<div>
<app-address-labels [vout]="vout" [channel]="tx._channels && tx._channels.outputs[vindex] ? tx._channels.outputs[vindex] : null"></app-address-labels>
@@ -185,13 +189,11 @@
<ng-container i18n="transactions-list.peg-out-to">Peg-out to <ng-container *ngTemplateOutlet="pegOutLink"></ng-container></ng-container>
<ng-template #pegOutLink>
<a *ngIf="stateService.env.BASE_MODULE === 'liquid'; else localPegoutLink" [attr.href]="'https://mempool.space/address/' + vout.pegout.scriptpubkey_address" title="{{ vout.pegout.scriptpubkey_address }}">
<span class="d-block d-lg-none">{{ vout.pegout.scriptpubkey_address | shortenString : 16 }}</span>
<span class="d-none d-lg-block">{{ vout.pegout.scriptpubkey_address | shortenString : 35 }}</span>
<app-truncate [text]="vout.pegout.scriptpubkey_address"></app-truncate>
</a>
<ng-template #localPegoutLink>
<a [routerLink]="['/address/', vout.pegout.scriptpubkey_address]" title="{{ vout.pegout.scriptpubkey_address }}">
<span class="d-block d-lg-none">{{ vout.pegout.scriptpubkey_address | shortenString : 16 }}</span>
<span class="d-none d-lg-block">{{ vout.pegout.scriptpubkey_address | shortenString : 35 }}</span>
<app-truncate [text]="vout.pegout.scriptpubkey_address"></app-truncate>
</a>
</ng-template>
</ng-template>
@@ -270,7 +272,7 @@
<ng-template #showMoreOutputsLabel>
<span i18n="show-more">Show more</span>
</ng-template>
({{ tx.vout.length - getVoutLimit(tx) }} <span i18n="outputs-remaining">remaining</span>)
(<ng-container *ngTemplateOutlet="xRemaining; context: {$implicit: tx.vout.length - getVoutLimit(tx)}"></ng-container>)
</button>
</td>
</tr>
@@ -324,3 +326,5 @@
<br />
<a [routerLink]="['/assets/asset/' | relativeUrl, item.asset]">{{ item.asset | shortenString : 13 }}</a>
</ng-template>
<ng-template #xRemaining let-x i18n="x-remaining">{{ x }} remaining</ng-template>

View File

@@ -1,6 +1,6 @@
.arrow-td {
width: 20px;
width: 30px;
padding-top: 0;
padding-bottom: 0;
}
@@ -45,6 +45,10 @@
}
}
td.amount {
width: 32.5%;
}
.extra-info {
display: none;
@media (min-width: 576px) {
@@ -81,6 +85,10 @@
}
.tx-page-container {
display: flex;
flex-direction: row;
align-items: baseline;
white-space: nowrap;
padding: 10px;
margin-bottom: 10px;
margin-top: 10px;
@@ -97,9 +105,7 @@
&:first-child {
color: #ffffff66;
white-space: pre-wrap;
@media (min-width: 476px) {
white-space: nowrap;
}
width: 150px;
}
&:nth-child(2) {
word-break: break-all;
@@ -130,14 +136,7 @@ h2 {
margin-top: 10px;
}
.addr-left {
font-family: monospace;
overflow: hidden;
text-overflow: ellipsis;
margin-right: -7px
}
.addr-right {
.address {
font-family: monospace;
}
@@ -146,3 +145,50 @@ h2 {
font-style: italic;
font-size: 12px;
}
.tx-link {
width: 0;
flex-grow: 1;
margin-inline-end: 2em;
}
.vin-witness {
.witness-item.accordioned {
max-height: 300px;
overflow: hidden;
}
input:checked + .witness-item.accordioned {
max-height: none;
}
.witness-toggle {
display: flex;
flex-direction: row;
align-items: flex-start;
justify-content: space-between;
margin-bottom: 1em;
.show-all {
display: inline;
}
.show-less {
display: none;
}
.ellipsis {
visibility: visible;
}
}
input:checked ~ .witness-toggle {
.show-all {
display: none;
}
.show-less {
display: inline;
}
.ellipsis {
visibility: hidden;
}
}
}

View File

@@ -31,8 +31,7 @@
<p *ngIf="!isConnector">Peg Out</p>
<p *ngIf="line.value != null"><app-amount [satoshis]="line.value"></app-amount></p>
<p class="address">
<span class="first">{{ line.pegout.slice(0, -4) }}</span>
<span class="last-four">{{ line.pegout.slice(-4) }}</span>
<app-truncate [text]="line.pegout"></app-truncate>
</p>
</ng-container>
</ng-template>
@@ -49,8 +48,7 @@
<ng-container *ngIf="isConnector && line.txid">
<p>
<span i18n="transaction">Transaction</span>&nbsp;
<span class="first">{{ line.txid.slice(0, 8) }}</span>...
<span class="last-four">{{ line.txid.slice(-4) }}</span>
<app-truncate [text]="line.txid"></app-truncate>
</p>
<ng-container [ngSwitch]="line.type">
<p *ngSwitchCase="'input'"><span i18n="transaction.output">Output</span>&nbsp; #{{ line.vout + 1 }}</p>
@@ -60,8 +58,7 @@
<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.type !== 'fee' && line.address" class="address">
<span class="first">{{ line.address.slice(0, -4) }}</span>
<span class="last-four">{{ line.address.slice(-4) }}</span>
<app-truncate [text]="line.address"></app-truncate>
</p>
</ng-template>
</div>

View File

@@ -17,22 +17,5 @@
.address {
width: 100%;
max-width: 100%;
display: flex;
flex-direction: row;
align-items: baseline;
justify-content: flex-start;
.first {
flex-grow: 0;
flex-shrink: 1;
overflow: hidden;
text-overflow: ellipsis;
margin-right: -2px;
}
.last-four {
flex-shrink: 0;
flex-grow: 0;
}
}
}

View File

@@ -127,7 +127,11 @@
</thead>
<tbody>
<tr *ngFor="let transaction of transactions$ | async; let i = index;">
<td class="table-cell-txid"><a [routerLink]="['/tx' | relativeUrl, transaction.txid]">{{ transaction.txid | shortenString : 10 }}</a></td>
<td class="table-cell-txid">
<a [routerLink]="['/tx' | relativeUrl, transaction.txid]">
<app-truncate [text]="transaction.txid" [lastChars]="5"></app-truncate>
</a>
</td>
<td class="table-cell-satoshis"><app-amount *ngIf="(network$ | async) !== 'liquid' && (network$ | async) !== 'liquidtestnet'; else liquidAmount" [satoshis]="transaction.value" digitsInfo="1.2-4" [noFiat]="true"></app-amount><ng-template #liquidAmount i18n="shared.confidential">Confidential</ng-template></td>
<td class="table-cell-fiat" *ngIf="(network$ | async) === ''" ><app-fiat [value]="transaction.value" digitsInfo="1.0-0"></app-fiat></td>
<td class="table-cell-fees">{{ transaction.fee / transaction.vsize | feeRounding }} <span class="symbol" i18n="shared.sat-vbyte|sat/vB">sat/vB</span></td>

View File

@@ -12,7 +12,8 @@ if (browserWindowEnv.BASE_MODULE && (browserWindowEnv.BASE_MODULE === 'bisq' ||
routes = [
{
path: '',
redirectTo: 'api/rest'
redirectTo: 'api/rest',
pathMatch: 'full'
},
{
path: 'api/:type',
@@ -20,11 +21,13 @@ if (browserWindowEnv.BASE_MODULE && (browserWindowEnv.BASE_MODULE === 'bisq' ||
},
{
path: 'api',
redirectTo: 'api/rest'
redirectTo: 'api/rest',
pathMatch: 'full'
},
{
path: '**',
redirectTo: 'api/rest'
redirectTo: 'api/rest',
pathMatch: 'full'
}
];
} else {

View File

@@ -1,10 +1,9 @@
<div class="mb-2 box-top">
<div class="box-left text-truncate">
<h3 class="mb-0 text-truncate">{{ channel.alias || '?' }}</h3>
<a [routerLink]="['/lightning/node' | relativeUrl, channel.public_key]" >
{{ channel.public_key | shortenString : 12 }}
</a>
<app-clipboard [text]="channel.public_key"></app-clipboard>
<app-truncate [text]="channel.public_key" [lastChars]="6" [link]="['/lightning/node' | relativeUrl, channel.public_key]">
<app-clipboard [text]="channel.public_key"></app-clipboard>
</app-truncate>
</div>
<div class="box-right">
<div class="second-line"><ng-container *ngTemplateOutlet="xChannels; context: {$implicit: channel.channels }"></ng-container></div>

View File

@@ -2,7 +2,7 @@
display: flex;
flex-direction: row;
@media (max-width: 768px) {
@media (max-width: 767.98px) {
flex-direction: column;
}
}
@@ -10,16 +10,13 @@
.tx-link {
display: flex;
flex-grow: 1;
@media (min-width: 650px) {
@media (min-width: 768px) {
top: 1px;
position: relative;
align-self: end;
margin-left: 15px;
margin-top: 0px;
margin-bottom: -3px;
}
@media (min-width: 768px) {
margin-bottom: 4px;
top: 1px;
position: relative;
}
@media (max-width: 768px) {
order: 2;

View File

@@ -49,10 +49,9 @@
<td class="alias text-left">
<div>{{ node.alias || '?' }}</div>
<div class="second-line">
<a [routerLink]="['/lightning/node' | relativeUrl, node.public_key]">
<span>{{ node.public_key | shortenString : publicKeySize }}</span>
</a>
<app-clipboard [text]="node.public_key" size="small"></app-clipboard>
<app-truncate [text]="node.public_key" [maxWidth]="200" [lastChars]="6" [link]="['/lightning/node' | relativeUrl, node.public_key]">
<app-clipboard [text]="node.public_key" size="small"></app-clipboard>
</app-truncate>
</div>
</td>
<td class="alias text-left d-none d-md-table-cell">

View File

@@ -56,7 +56,7 @@
<!-- Top nodes per capacity -->
<div class="col">
<div class="card" style="height: 409px">
<div class="card">
<div class="card-body">
<a class="title-link" href="" [routerLink]="['/lightning/nodes/rankings/liquidity' | relativeUrl]">
<h5 class="card-title d-inline" i18n="lightning.liquidity-ranking">Liquidity Ranking</h5>
@@ -70,7 +70,7 @@
<!-- Top nodes per channels -->
<div class="col">
<div class="card" style="height: 409px">
<div class="card">
<div class="card-body">
<a class="title-link" href="" [routerLink]="['/lightning/nodes/rankings/connectivity' | relativeUrl]">
<h5 class="card-title d-inline" i18n="lightning.connectivity-ranking">Connectivity Ranking</h5>

View File

@@ -3,11 +3,11 @@
<div class="title-container mb-2" *ngIf="!error">
<h1 class="mb-0 text-truncate">{{ node.alias }}</h1>
<span class="tx-link">
<a [routerLink]="['/lightning/node' | relativeUrl, node.public_key]">
<span class="d-inline d-lg-none">{{ node.public_key | shortenString : 24 }}</span>
<span class="d-none d-lg-inline">{{ node.public_key }}</span>
</a>
<app-clipboard [text]="node.public_key"></app-clipboard>
<span class="node-id">
<app-truncate [text]="node.public_key" [lastChars]="8" [link]="['/lightning/node' | relativeUrl, node.public_key]">
<app-clipboard [text]="node.public_key"></app-clipboard>
</app-truncate>
</span>
</span>
</div>
@@ -215,7 +215,7 @@
</div>
<div *ngIf="hasDetails" class="text-right mt-3">
<button type="button" class="btn btn-outline-info btn-sm btn-details" (click)="toggleShowDetails()" i18n="node.details|Node Details">Details</button>
<button type="button" class="btn btn-outline-info btn-sm btn-details" (click)="toggleShowDetails()" i18n="transaction.details|Transaction Details">Details</button>
</div>
<div *ngIf="!error">

View File

@@ -8,6 +8,11 @@
flex-wrap: wrap;
}
.node-id {
width: 0;
flex-grow: 1;
}
.qr-wrapper {
background-color: #FFF;
padding: 10px;

View File

@@ -21,7 +21,7 @@
<div class="spinner-border text-light"></div>
</div>
<table class="table table-borderless text-center m-auto" style="max-width: 900px">
<table class="table table-borderless table-fixed text-center m-auto" style="max-width: 900px">
<thead>
<tr>
<th class="text-left rank" i18n="mining.rank">Rank</th>

View File

@@ -42,14 +42,14 @@
}
.rank {
width: 20%;
width: 8%;
@media (max-width: 576px) {
display: none
}
}
.name {
width: 20%;
width: 36%;
@media (max-width: 576px) {
width: 80%;
max-width: 150px;
@@ -59,21 +59,21 @@
}
.share {
width: 20%;
width: 15%;
@media (max-width: 576px) {
display: none
}
}
.nodes {
width: 20%;
width: 15%;
@media (max-width: 576px) {
width: 10%;
}
}
.capacity {
width: 20%;
width: 26%;
@media (max-width: 576px) {
width: 10%;
max-width: 100px;
@@ -91,3 +91,8 @@ a:hover .link {
.flag {
font-size: 20px;
}
.text-truncate .link {
overflow: hidden;
text-overflow: ellipsis;
}

View File

@@ -51,7 +51,7 @@
<app-toggle [textLeft]="'Sort by nodes'" [textRight]="'capacity'" [checked]="true" (toggleStatusChanged)="onGroupToggleStatusChanged($event)"></app-toggle>
</div>
<table class="table table-borderless text-center m-auto" style="max-width: 900px" *ngIf="!widget">
<table class="table table-borderless table-fixed text-center m-auto" style="max-width: 900px" *ngIf="!widget">
<thead>
<tr>
<th class="rank text-left pl-0" i18n="mining.rank">Rank</th>

View File

@@ -4,7 +4,7 @@
</h1>
<div [class]="widget ? 'widget' : 'full'">
<table class="table table-borderless">
<table class="table table-borderless table-fixed">
<thead>
<th class="rank"></th>
<th class="alias text-left" i18n="nodes.alias">Alias</th>
@@ -29,10 +29,10 @@
{{ node.channels | number }}
</td>
<td *ngIf="!widget" class="timestamp-first text-left">
<app-timestamp [customFormat]="'yyyy-MM-dd'" [unixTime]="node.firstSeen"></app-timestamp>
<app-timestamp [customFormat]="'yyyy-MM-dd'" [unixTime]="node.firstSeen" [hideTimeSince]="true"></app-timestamp>
</td>
<td *ngIf="!widget" class="timestamp-update text-left">
<app-timestamp [customFormat]="'yyyy-MM-dd'" [unixTime]="node.updatedAt"></app-timestamp>
<app-timestamp [customFormat]="'yyyy-MM-dd'" [unixTime]="node.updatedAt" [hideTimeSince]="true"></app-timestamp>
</td>
<td *ngIf="!widget" class="location text-right text-truncate">
<app-geolocation [data]="node.geolocation" [type]="'list-isp'"></app-geolocation>

View File

@@ -1,7 +1,7 @@
.container-xl {
max-width: 1400px;
padding-bottom: 100px;
@media (min-width: 767.98px) {
@media (min-width: 960px) {
padding-left: 50px;
padding-right: 50px;
}
@@ -15,40 +15,44 @@
width: 5%;
}
.widget .rank {
@media (min-width: 767.98px) {
@media (min-width: 960px) {
width: 13%;
}
@media (max-width: 767.98px) {
@media (max-width: 960px) {
padding-left: 0px;
padding-right: 0px;
}
}
.full .alias {
width: 10%;
width: 20%;
overflow: hidden;
text-overflow: ellipsis;
max-width: 350px;
@media (max-width: 767.98px) {
max-width: 175px;
@media (max-width: 960px) {
width: 40%;
max-width: 500px;
}
}
.widget .alias {
width: 55%;
width: 60%;
overflow: hidden;
text-overflow: ellipsis;
max-width: 350px;
@media (max-width: 767.98px) {
@media (max-width: 960px) {
max-width: 175px;
}
}
.full .capacity {
width: 10%;
@media (max-width: 960px) {
width: 30%;
}
}
.widget .capacity {
width: 32%;
@media (max-width: 767.98px) {
@media (max-width: 960px) {
padding-left: 0px;
padding-right: 0px;
}
@@ -57,28 +61,31 @@
.full .channels {
width: 15%;
padding-right: 50px;
@media (max-width: 767.98px) {
@media (max-width: 960px) {
display: none;
}
}
.full .timestamp-first {
width: 15%;
@media (max-width: 767.98px) {
width: 10%;
@media (max-width: 960px) {
display: none;
}
}
.full .timestamp-update {
width: 15%;
@media (max-width: 767.98px) {
width: 10%;
@media (max-width: 960px) {
display: none;
}
}
.full .location {
width: 10%;
@media (max-width: 767.98px) {
width: 15%;
@media (max-width: 960px) {
width: 30%;
}
@media (max-width: 600px) {
display: none;
}
}

View File

@@ -29,10 +29,10 @@
<app-amount [satoshis]="node.capacity" [digitsInfo]="'1.2-2'" [noFiat]="true"></app-amount>
</td>
<td *ngIf="!widget" class="timestamp-first text-left">
<app-timestamp [customFormat]="'yyyy-MM-dd'" [unixTime]="node.firstSeen"></app-timestamp>
<app-timestamp [customFormat]="'yyyy-MM-dd'" [unixTime]="node.firstSeen" [hideTimeSince]="true"></app-timestamp>
</td>
<td *ngIf="!widget" class="timestamp-update text-left">
<app-timestamp [customFormat]="'yyyy-MM-dd'" [unixTime]="node.updatedAt"></app-timestamp>
<app-timestamp [customFormat]="'yyyy-MM-dd'" [unixTime]="node.updatedAt" [hideTimeSince]="true"></app-timestamp>
</td>
<td *ngIf="!widget" class="location text-right text-truncate">
<app-geolocation [data]="node.geolocation" [type]="'list-isp'"></app-geolocation>

View File

@@ -1,7 +1,7 @@
.container-xl {
max-width: 1400px;
padding-bottom: 100px;
@media (min-width: 767.98px) {
@media (min-width: 960px) {
padding-left: 50px;
padding-right: 50px;
}
@@ -15,70 +15,77 @@
width: 5%;
}
.widget .rank {
@media (min-width: 767.98px) {
@media (min-width: 960px) {
width: 13%;
}
@media (max-width: 767.98px) {
@media (max-width: 960px) {
padding-left: 0px;
padding-right: 0px;
}
}
.full .alias {
width: 10%;
width: 20%;
overflow: hidden;
text-overflow: ellipsis;
max-width: 350px;
@media (max-width: 767.98px) {
max-width: 175px;
@media (max-width: 960px) {
width: 40%;
max-width: 500px;
}
}
.widget .alias {
width: 55%;
width: 60%;
overflow: hidden;
text-overflow: ellipsis;
max-width: 350px;
@media (max-width: 767.98px) {
@media (max-width: 960px) {
max-width: 175px;
}
}
.full .channels {
.full .capacity {
width: 10%;
@media (max-width: 960px) {
width: 30%;
}
}
.widget .channels {
.widget .capacity {
width: 32%;
@media (max-width: 767.98px) {
@media (max-width: 960px) {
padding-left: 0px;
padding-right: 0px;
}
}
.full .capacity {
.full .channels {
width: 15%;
padding-right: 50px;
@media (max-width: 767.98px) {
@media (max-width: 960px) {
display: none;
}
}
.full .timestamp-first {
width: 15%;
@media (max-width: 767.98px) {
width: 10%;
@media (max-width: 960px) {
display: none;
}
}
.full .timestamp-update {
width: 15%;
@media (max-width: 767.98px) {
width: 10%;
@media (max-width: 960px) {
display: none;
}
}
.full .location {
width: 10%;
@media (max-width: 767.98px) {
width: 15%;
@media (max-width: 960px) {
width: 30%;
}
@media (max-width: 600px) {
display: none;
}
}

View File

@@ -5,7 +5,7 @@ import { CpfpInfo, OptimizedMempoolStats, AddressInformation, LiquidPegs, ITrans
import { Observable } from 'rxjs';
import { StateService } from './state.service';
import { WebsocketResponse } from '../interfaces/websocket.interface';
import { Outspend } from '../interfaces/electrs.interface';
import { Outspend, Transaction } from '../interfaces/electrs.interface';
@Injectable({
providedIn: 'root'
@@ -119,6 +119,14 @@ export class ApiService {
return this.httpClient.get<AddressInformation>(this.apiBaseUrl + this.apiBasePath + '/api/v1/validate-address/' + address);
}
getRbfHistory$(txid: string): Observable<string[]> {
return this.httpClient.get<string[]>(this.apiBaseUrl + this.apiBasePath + '/api/v1/tx/' + txid + '/replaces');
}
getRbfCachedTx$(txid: string): Observable<Transaction> {
return this.httpClient.get<Transaction>(this.apiBaseUrl + this.apiBasePath + '/api/v1/tx/' + txid + '/cached');
}
listLiquidPegsMonth$(): Observable<LiquidPegs[]> {
return this.httpClient.get<LiquidPegs[]>(this.apiBaseUrl + this.apiBasePath + '/api/v1/liquid/pegs/month');
}

View File

@@ -1,7 +1,7 @@
<span *ngIf="seconds === undefined">-</span>
<span *ngIf="seconds !== undefined">
&lrm;{{ seconds * 1000 | date: customFormat ?? 'yyyy-MM-dd HH:mm' }}
<div class="lg-inline">
<div class="lg-inline" *ngIf="!hideTimeSince">
<i class="symbol">(<app-time-since [time]="seconds" [fastRender]="true"></app-time-since>)</i>
</div>
</span>

View File

@@ -10,6 +10,7 @@ export class TimestampComponent implements OnChanges {
@Input() unixTime: number;
@Input() dateString: string;
@Input() customFormat: string;
@Input() hideTimeSince: boolean = false;
seconds: number | undefined = undefined;

View File

@@ -0,0 +1,19 @@
<span class="truncate" [style.max-width]="maxWidth ? maxWidth + 'px' : null">
<ng-container *ngIf="link">
<a [routerLink]="link" class="truncate-link">
<ng-container *ngIf="rtl; then rtlTruncated; else ltrTruncated;"></ng-container>
</a>
</ng-container>
<ng-container *ngIf="!link">
<ng-container *ngIf="rtl; then rtlTruncated; else ltrTruncated;"></ng-container>
</ng-container>
<ng-content></ng-content>
</span>
<ng-template #ltrTruncated>
<span class="first">{{text.slice(0,-lastChars)}}</span><span class="last-four">{{text.slice(-lastChars)}}</span>
</ng-template>
<ng-template #rtlTruncated>
<span class="first">{{text.slice(lastChars)}}</span><span class="last-four">{{text.slice(0,lastChars)}}</span>
</ng-template>

View File

@@ -0,0 +1,26 @@
.truncate {
text-overflow: unset;
display: flex;
flex-direction: row;
align-items: baseline;
.truncate-link {
display: flex;
flex-direction: row;
align-items: baseline;
flex-shrink: 1;
overflow: hidden;
}
.first {
flex-grow: 0;
flex-shrink: 1;
overflow: hidden;
text-overflow: ellipsis;
}
.last-four {
flex-shrink: 0;
flex-grow: 0;
}
}

View File

@@ -0,0 +1,23 @@
import { Component, Input, Inject, LOCALE_ID, ChangeDetectionStrategy } from '@angular/core';
@Component({
selector: 'app-truncate',
templateUrl: './truncate.component.html',
styleUrls: ['./truncate.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class TruncateComponent {
@Input() text: string;
@Input() link: any = null;
@Input() lastChars: number = 4;
@Input() maxWidth: number = null;
rtl: boolean;
constructor(
@Inject(LOCALE_ID) private locale: string,
) {
if (this.locale.startsWith('ar') || this.locale.startsWith('fa') || this.locale.startsWith('he')) {
this.rtl = true;
}
}
}

View File

@@ -5,6 +5,6 @@ import { Pipe, PipeTransform } from '@angular/core';
})
export class Decimal2HexPipe implements PipeTransform {
transform(decimal: number): string {
return `0x` + decimal.toString(16);
return `0x` + ( decimal.toString(16) ).padStart(8, '0');
}
}

View File

@@ -77,6 +77,7 @@ import { IndexingProgressComponent } from '../components/indexing-progress/index
import { SvgImagesComponent } from '../components/svg-images/svg-images.component';
import { ChangeComponent } from '../components/change/change.component';
import { SatsComponent } from './components/sats/sats.component';
import { TruncateComponent } from './components/truncate/truncate.component';
import { SearchResultsComponent } from '../components/search-form/search-results/search-results.component';
import { TimestampComponent } from './components/timestamp/timestamp.component';
import { ToggleComponent } from './components/toggle/toggle.component';
@@ -152,6 +153,7 @@ import { GeolocationComponent } from '../shared/components/geolocation/geolocati
SvgImagesComponent,
ChangeComponent,
SatsComponent,
TruncateComponent,
SearchResultsComponent,
TimestampComponent,
ToggleComponent,
@@ -252,6 +254,7 @@ import { GeolocationComponent } from '../shared/components/geolocation/geolocati
SvgImagesComponent,
ChangeComponent,
SatsComponent,
TruncateComponent,
SearchResultsComponent,
TimestampComponent,
ToggleComponent,

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff