Merge branch 'master' into simon/angular-universal

* master:
  Update list of supported locales
  Add one more fix to nginx.conf for i18n
  Remove unused i18n tags in frontend/src/index.html
  Update translations from Transifex
  Enable 'fr' locale for French
  Corrected some missing spaces on transactions page and a blank render bug when confirmation time is below 1 minute.
  Implement i18n support in frontend using Angular + Transifex + NGINX

# Conflicts:
#	frontend/src/app/app.constants.ts
#	frontend/sync-assets.js
This commit is contained in:
softsimon
2020-12-02 20:05:49 +07:00
60 changed files with 41992 additions and 451 deletions

View File

@@ -10,16 +10,16 @@
<br>
<h2>About the project</h2>
<h2 i18n="about.about-the-project">About the project</h2>
<div class="row row-cols-1">
<div class="col col-md-6 offset-md-3">
<p>The mempool open-source project aims to implement a high quality explorer and visualization website for the entire Bitcoin ecosystem, without distractions like altcoins, advertising, or third-party trackers.</p>
<p i18n>The mempool open-source project aims to implement a high quality explorer and visualization website for the entire Bitcoin ecosystem, without distractions like altcoins, advertising, or third-party trackers.</p>
</div>
</div>
<br>
<h2>Maintainers</h2>
<h2 i18n="about.maintainers">Maintainers</h2>
<div class="container text-center">
<div class="row row-cols-2">
@@ -29,7 +29,7 @@
@softsimon_
</a>
<br>
Development
<span i18n="about.development">Development</span>
</div>
<div class="col col-md-2">
<a href="https://twitter.com/wiz">
@@ -37,14 +37,14 @@
@wiz
</a>
<br>
Operations
<span i18n="about.operations">Operations</span>
</div>
</div>
</div>
<br><br>
<h2>Sponsors ❤️</h2>
<h2 i18n="about.sponsors.withHeart">Sponsors ❤️</h2>
<div *ngIf="sponsors === null">
<br>
@@ -60,9 +60,9 @@
</ng-template>
<br><br>
<button type="button" class="btn btn-primary" (click)="donationStatus = 2" [hidden]="donationStatus !== 1">Become a sponsor ❤️</button>
<button type="button" class="btn btn-primary" (click)="donationStatus = 2" [hidden]="donationStatus !== 1" i18n="about.become-a-sponsor">Become a sponsor ❤️</button>
<p *ngIf="donationStatus === 2 && !sponsorsEnabled">
Navigate to <a href="https://mempool.space/about" target="_blank">https://mempool.space/about</a> to sponsor
<span i18n="about.navigate-to">Navigate to</span> <a href="https://mempool.space/about" target="_blank">https://mempool.space/about</a> <span i18n="about.to-sponsor">to sponsor</span>
</p>
<div style="max-width: 300px;" class="mx-auto" [hidden]="donationStatus !== 2 || !sponsorsEnabled">
@@ -79,17 +79,17 @@
</div>
<input formControlName="handle" class="form-control" type="text" placeholder="Twitter handle (Optional)">
</div>
<div class="required" *ngIf="donationForm.get('amount').hasError('required')">Amount required</div>
<div class="required" *ngIf="donationForm.get('amount').hasError('min')">Minimum amount is 0.001 BTC</div>
<div class="required" *ngIf="donationForm.get('amount').hasError('required')" i18n="about.sponsor.amount-required">Amount required</div>
<div class="required" *ngIf="donationForm.get('amount').hasError('min')" i18n="about.sponsor.minimum-amount">Minimum amount is 0.001 BTC</div>
<div class="input-group mt-4">
<button class="btn btn-primary mx-auto" type="submit" [disabled]="donationForm.invalid">Request invoice</button>
<button class="btn btn-primary mx-auto" type="submit" [disabled]="donationForm.invalid" i18n="about.sponsor.request-invoice">Request invoice</button>
</div>
</form>
</div>
<ng-template #lowAmount>
<div class="input-group mb-4 text-small">
If you donate 0.01 BTC or more, your profile photo will be added to the list of sponsors above :)
<span i18n="about.sponsor.description">If you donate 0.01 BTC or more, your profile photo will be added to the list of sponsors above :)</span>
</div>
</ng-template>
@@ -167,13 +167,13 @@
<p style="font-size: 12px;">{{ donationObj.amount }} BTC</p>
</ng-template>
<p>Waiting for transaction... </p>
<p i18n="about.sponsor.waiting-for-transaction">Waiting for transaction... </p>
<div class="spinner-border text-light"></div>
</div>
<div *ngIf="donationStatus === 4" class="text-center">
<h2>Donation confirmed!<br>Thank you!</h2>
<p>If you specified a Twitter handle, the profile photo should now be visible on this page when you reload.</p>
<h2><span i18n="about.sponsor.donation-confirmed">Donation confirmed!</span><br><span i18n="about.sponsor.thank-you">Thank you!</span></h2>
<p i18n="about.sponsor.sponsor-completed">If you specified a Twitter handle, the profile photo should now be visible on this page when you reload.</p>
</div>
<br><br><br><br>
@@ -203,7 +203,7 @@
<br><br>
<div class="text-center">
<a [routerLink]="['/terms-of-service']">Terms of Service</a>
<a [routerLink]="['/terms-of-service']" i18n="shared.terms-of-service|Terms of Service">Terms of Service</a>
</div>
</div>

View File

@@ -1,2 +1,2 @@
<span *ngIf="multisig" class="badge badge-pill badge-warning">multisig {{ multisigM }} of {{ multisigN }}</span>
<span *ngIf="secondLayerClose" class="badge badge-pill badge-warning">Layer{{ network === 'liquid' ? '3' : '2' }} Peg-out</span>
<span *ngIf="multisig" class="badge badge-pill badge-warning" i18n="address-labels.multisig">multisig {{ multisigM }} of {{ multisigN }}</span>
<span *ngIf="secondLayerClose" class="badge badge-pill badge-warning" i18n="address-labels.upper-layer-peg-out">Layer{{ network === 'liquid' ? '3' : '2' }} Peg-out</span>

View File

@@ -1,5 +1,5 @@
<div class="container-xl">
<h1 style="float: left;">Address</h1>
<h1 style="float: left;" i18n="shared.address">Address</h1>
<a [routerLink]="['/address/' | relativeUrl, addressString]" style="line-height: 56px; margin-left: 10px;">
<span class="d-inline d-lg-none">{{ addressString | shortenString : 24 }}</span>
<span class="d-none d-lg-inline">{{ addressString }}</span>
@@ -17,15 +17,15 @@
<table class="table table-borderless table-striped">
<tbody>
<tr>
<td>Total received</td>
<td i18n="address.total-received">Total received</td>
<td *ngIf="address.chain_stats.funded_txo_sum !== undefined; else confidentialTd"><app-amount [satoshis]="receieved" [noFiat]="true"></app-amount></td>
</tr>
<tr>
<td>Total sent</td>
<td i18n="address.total-sent">Total sent</td>
<td *ngIf="address.chain_stats.spent_txo_sum !== undefined; else confidentialTd"><app-amount [satoshis]="sent" [noFiat]="true"></app-amount></td>
</tr>
<tr>
<td>Balance</td>
<td i18n="address.balance">Balance</td>
<td *ngIf="address.chain_stats.funded_txo_sum !== undefined; else confidentialTd"><app-amount [satoshis]="receieved - sent" [noFiat]="true"></app-amount> (<app-fiat [value]="receieved - sent"></app-fiat>)</td>
</tr>
</tbody>
@@ -43,7 +43,7 @@
<br>
<h2><ng-template [ngIf]="transactions?.length">{{ (transactions?.length | number) || '?' }} of </ng-template>{{ txCount | number }} transactions</h2>
<h2><ng-template [ngIf]="transactions?.length">{{ (transactions?.length | number) || '?' }} <span i18n="shared.of">of</span>&nbsp;</ng-template>{{ txCount | number }} <span i18n="shared.transactions">transactions</span></h2>
<app-transactions-list [transactions]="transactions" [showConfirmations]="true" (loadMore)="loadMore()"></app-transactions-list>
@@ -98,7 +98,7 @@
<ng-template [ngIf]="error">
<div class="text-center">
Error loading address data.
<span i18n="address.error.loading-address-data">Error loading address data.</span>
<br>
<i>{{ error.error }}</i>
</div>
@@ -109,5 +109,5 @@
<br>
<ng-template #confidentialTd>
<td>Confidential</td>
<td i18n="shared.confidential">Confidential</td>
</ng-template>

View File

@@ -3,7 +3,7 @@
</ng-container>
<ng-template #viewFiatVin>
<ng-template [ngIf]="network === 'liquid' && (satoshis === undefined || satoshis === null)" [ngIfElse]="default">
Confidential
<span i18n="shared.confidential">Confidential</span>
</ng-template>
<ng-template #default>
{{ satoshis / 100000000 | number : digitsInfo }}

View File

@@ -6,17 +6,17 @@
<ul ngbNav #nav="ngbNav" [(activeId)]="active" class="nav-tabs">
<li *ngIf="network.val !== 'bisq'" [ngbNavItem]="0">
<a ngbNavLink>Websocket</a>
<a ngbNavLink i18n="api-docs.tab.websocket|API Docs tab for Websocket">Websocket</a>
<ng-template ngbNavContent>
<table class="table">
<tr>
<th style="border-top: 0;">Endpoint</th>
<th style="border-top: 0;">Description</th>
<th style="border-top: 0;" i18n="api-docs.shared.endpoint|API Docs Endpoint">Endpoint</th>
<th style="border-top: 0;" i18n="api-docs.shared.description|API Docs Description">Description</th>
</tr>
<tr>
<td class="nowrap">wss://{{ hostname }}{{ network.val === '' ? '' : '/' + network.val }}/api/v1/ws</td>
<td>Default push: <code>{{ '{' }} action: 'want', data: ['blocks', ...] {{ '}' }}</code> to express what you want pushed. Available: <code>blocks</code>, <code>mempool-block</code>, <code>live-2h-chart</code>, and <code>stats</code>.<br><br>Push transactions related to address: <code>{{ '{' }} 'track-address': '3PbJ...bF9B' {{ '}' }}</code> to receive all new transactions containing that address as input or output. Returns an array of transactions. <code>address-transactions</code> for new mempool transactions, and <code>block-transactions</code> for new block confirmed transactions.</td>
<td i18n="api-docs.websocket.websocket">Default push: <code>{{ '{' }} action: 'want', data: ['blocks', ...] {{ '}' }}</code> to express what you want pushed. Available: <code>blocks</code>, <code>mempool-block</code>, <code>live-2h-chart</code>, and <code>stats</code>.<br><br>Push transactions related to address: <code>{{ '{' }} 'track-address': '3PbJ...bF9B' {{ '}' }}</code> to receive all new transactions containing that address as input or output. Returns an array of transactions. <code>address-transactions</code> for new mempool transactions, and <code>block-transactions</code> for new block confirmed transactions.</td>
</tr>
</table>
@@ -24,21 +24,21 @@
</li>
<li *ngIf="network.val !== 'bisq'" [ngbNavItem]="1">
<a ngbNavLink>Fee Estimates</a>
<a ngbNavLink i18n="api-docs.tab.fees|API Docs tab for Fees">Fees</a>
<ng-template ngbNavContent>
<table class="table">
<tr>
<th style="border-top: 0;">Endpoint</th>
<th style="border-top: 0;">Description</th>
<th style="border-top: 0;" i18n="api-docs.shared.endpoint|API Docs Endpoint">Endpoint</th>
<th style="border-top: 0;" i18n="api-docs.shared.description|API Docs Description">Description</th>
</tr>
<tr>
<td class="nowrap"><a href="{{ network.val === '' ? '' : '/' + network.val }}/api/v1/fees/recommended" target="_blank">GET {{ network.val === '' ? '' : '/' + network.val }}/api/v1/fees/recommended</a></td>
<td>Returns our currently suggested fees for new transactions.</td>
<td i18n="api-docs.fees.recommended|API Docs for /api/v1/fees/recommended">Returns our currently suggested fees for new transactions.</td>
</tr>
<tr>
<td class="nowrap"><a href="{{ network.val === '' ? '' : '/' + network.val }}/api/v1/fees/mempool-blocks" target="_blank">GET {{ network.val === '' ? '' : '/' + network.val }}/api/v1/fees/mempool-blocks</a></td>
<td>Returns current mempool as projected blocks.</td>
<td i18n="api-docs.fees.mempool-blocks|API Docs for /api/v1/fees/mempool-blocks">Returns current mempool as projected blocks.</td>
</tr>
</table>
@@ -46,25 +46,25 @@
</li>
<li *ngIf="network.val !== 'bisq'" [ngbNavItem]="2">
<a ngbNavLink>Mempool</a>
<a ngbNavLink i18n="api-docs.tab.mempool|API Docs tab for Mempool">Mempool</a>
<ng-template ngbNavContent>
<table class="table">
<tr>
<th style="border-top: 0;">Endpoint</th>
<th style="border-top: 0;">Description</th>
<th style="border-top: 0;" i18n="api-docs.shared.endpoint|API Docs Endpoint">Endpoint</th>
<th style="border-top: 0;" i18n="api-docs.shared.description|API Docs Description">Description</th>
</tr>
<tr>
<td class="nowrap"><a href="{{ network.val === '' ? '' : '/' + network.val }}/api/mempool" target="_blank">GET {{ network.val === '' ? '' : '/' + network.val }}/api/mempool</a></td>
<td>Returns current mempool backlog statistics.</td>
<td i18n="api-docs.mempool.mempool|API Docs for /api/mempool">Returns current mempool backlog statistics.</td>
</tr>
<tr>
<td class="nowrap"><a href="{{ network.val === '' ? '' : '/' + network.val }}/api/mempool/txids" target="_blank">GET {{ network.val === '' ? '' : '/' + network.val }}/api/mempool/txids</a></td>
<td>Get the full list of txids in the mempool as an array. The order of the txids is arbitrary and does not match bitcoind.</td>
<td i18n="api-docs.mempool.txids|API Docs for /api/mempool/txids">Get the full list of txids in the mempool as an array. The order of the txids is arbitrary and does not match bitcoind.</td>
</tr>
<tr>
<td class="nowrap"><a href="{{ network.val === '' ? '' : '/' + network.val }}/api/mempool/recent" target="_blank">GET {{ network.val === '' ? '' : '/' + network.val }}/api/mempool/recent</a></td>
<td>Get a list of the last 10 transactions to enter the mempool. Each transaction object contains simplified overview data, with the following fields: <code>txid</code>, <code>fee</code>, <code>vsize</code>, and <code>value</code>.</td>
<td i18n="api-docs.mempool.recent|API Docs for /api/mempool/recent">Get a list of the last 10 transactions to enter the mempool. Each transaction object contains simplified overview data, with the following fields: <code>txid</code>, <code>fee</code>, <code>vsize</code>, and <code>value</code>.</td>
</tr>
</table>
@@ -72,53 +72,53 @@
</li>
<li *ngIf="network.val !== 'bisq'" [ngbNavItem]="3">
<a ngbNavLink>Blocks</a>
<a ngbNavLink i18n="api-docs.tab.blocks|API Docs tab for Blocks">Blocks</a>
<ng-template ngbNavContent>
<table class="table">
<tr>
<th style="border-top: 0;">Endpoint</th>
<th style="border-top: 0;">Description</th>
<th style="border-top: 0;" i18n="api-docs.shared.endpoint|API Docs Endpoint">Endpoint</th>
<th style="border-top: 0;" i18n="api-docs.shared.description|API Docs Description">Description</th>
</tr>
<tr>
<td class="nowrap"><a href="{{ network.val === '' ? '' : '/' + network.val }}/api/block/000000000000000015dc777b3ff2611091336355d3f0ee9766a2cf3be8e4b1ce" target="_blank">GET {{ network.val === '' ? '' : '/' + network.val }}/api/block/:hash</a></td>
<td>Returns details about a block. Available fields: <code>id</code>, <code>height</code>, <code>version</code>, <code>timestamp</code>, <code>bits</code>, <code>nonce</code>, <code>merkle_root</code>, <code>tx_count</code>, <code>size</code>, <code>weight</code>,<ng-container *ngIf="network.val === 'liquid'"> <code>proof</code>,</ng-container> and <code>previousblockhash</code>.</td>
<td i18n>Returns details about a block. Available fields: <code>id</code>, <code>height</code>, <code>version</code>, <code>timestamp</code>, <code>bits</code>, <code>nonce</code>, <code>merkle_root</code>, <code>tx_count</code>, <code>size</code>, <code>weight</code>,<ng-container *ngIf="network.val === 'liquid'"> <code>proof</code>,</ng-container> and <code>previousblockhash</code>.</td>
</tr>
<tr>
<td class="nowrap"><a href="{{ network.val === '' ? '' : '/' + network.val }}/api/block/000000000000000015dc777b3ff2611091336355d3f0ee9766a2cf3be8e4b1ce/status" target="_blank">GET {{ network.val === '' ? '' : '/' + network.val }}/api/block/:hash/status</a></td>
<td>Returns the confirmation status of a block. Available fields: <code>in_best_chain</code> (boolean, false for orphaned blocks), <code>next_best</code> (the hash of the next block, only available for blocks in the best chain).</td>
<td i18n>Returns the confirmation status of a block. Available fields: <code>in_best_chain</code> (boolean, false for orphaned blocks), <code>next_best</code> (the hash of the next block, only available for blocks in the best chain).</td>
</tr>
<tr>
<td class="nowrap"><a href="{{ network.val === '' ? '' : '/' + network.val }}/api/block/000000000000000015dc777b3ff2611091336355d3f0ee9766a2cf3be8e4b1ce/txs" target="_blank">GET {{ network.val === '' ? '' : '/' + network.val }}/api/block/:hash/txs[/:start_index]</a></td>
<td>Returns a list of transactions in the block (up to 25 transactions beginning at <code>start_index</code>). Transactions returned here do not have the <code>status</code> field, since all the transactions share the same block and confirmation status.</td>
<td i18n>Returns a list of transactions in the block (up to 25 transactions beginning at <code>start_index</code>). Transactions returned here do not have the <code>status</code> field, since all the transactions share the same block and confirmation status.</td>
</tr>
<tr>
<td class="nowrap"><a href="{{ network.val === '' ? '' : '/' + network.val }}/api/block/000000000000000015dc777b3ff2611091336355d3f0ee9766a2cf3be8e4b1ce/txids" target="_blank">GET {{ network.val === '' ? '' : '/' + network.val }}/api/block/:hash/txids</a></td>
<td>Returns a list of all txids in the block.</td>
<td i18n>Returns a list of all txids in the block.</td>
</tr>
<tr>
<td class="nowrap"><a href="{{ network.val === '' ? '' : '/' + network.val }}/api/block/000000000000000015dc777b3ff2611091336355d3f0ee9766a2cf3be8e4b1ce/txid/218" target="_blank">GET {{ network.val === '' ? '' : '/' + network.val }}/api/block/:hash/txid/:index</a></td>
<td>Returns the transaction at index <code>:index</code> within the specified block.</td>
<td i18n>Returns the transaction at index <code>:index</code> within the specified block.</td>
</tr>
<tr>
<td class="nowrap"><a href="{{ network.val === '' ? '' : '/' + network.val }}/api/block/000000000000000015dc777b3ff2611091336355d3f0ee9766a2cf3be8e4b1ce/raw" target="_blank">GET {{ network.val === '' ? '' : '/' + network.val }}/api/block/:hash/txid/raw</a></td>
<td>Returns the raw block representation in binary.</td>
<td i18n>Returns the raw block representation in binary.</td>
</tr>
<tr>
<td class="nowrap"><a href="{{ network.val === '' ? '' : '/' + network.val }}/api/block-height/0" target="_blank">GET {{ network.val === '' ? '' : '/' + network.val }}/api/block-height/:height</a></td>
<td>Returns the hash of the block currently at <code>:height</code>.</td>
<td i18n>Returns the hash of the block currently at <code>:height</code>.</td>
</tr>
<tr>
<td class="nowrap"><a href="{{ network.val === '' ? '' : '/' + network.val }}/api/blocks" target="_blank">GET {{ network.val === '' ? '' : '/' + network.val }}/api/blocks[/:start_height]</a></td>
<td>Returns the 10 newest blocks starting at the tip or at <code>:start_height</code> if specified.</td>
<td i18n>Returns the 10 newest blocks starting at the tip or at <code>:start_height</code> if specified.</td>
</tr>
<tr>
<td class="nowrap"><a href="{{ network.val === '' ? '' : '/' + network.val }}/api/blocks/tip/height" target="_blank">GET {{ network.val === '' ? '' : '/' + network.val }}/api/blocks/tip/height</a></td>
<td>Returns the height of the last block.</td>
<td i18n>Returns the height of the last block.</td>
</tr>
<tr>
<td class="nowrap"><a href="{{ network.val === '' ? '' : '/' + network.val }}/api/blocks/tip/hash" target="_blank">GET {{ network.val === '' ? '' : '/' + network.val }}/api/blocks/tip/hash</a></td>
<td>Returns the hash of the last block.</td>
<td i18n>Returns the hash of the last block.</td>
</tr>
</table>
@@ -126,49 +126,49 @@
</li>
<li *ngIf="network.val !== 'bisq'" [ngbNavItem]="4">
<a ngbNavLink>Transactions</a>
<a ngbNavLink i18n="api-docs.tab.transactions|API Docs tab for Transactions">Transactions</a>
<ng-template ngbNavContent>
<table class="table">
<tr>
<th style="border-top: 0;">Endpoint</th>
<th style="border-top: 0;">Description</th>
<th style="border-top: 0;" i18n="api-docs.shared.endpoint|API Docs Endpoint">Endpoint</th>
<th style="border-top: 0;" i18n="api-docs.shared.description|API Docs Description">Description</th>
</tr>
<tr>
<td class="nowrap"><a href="{{ network.val === '' ? '' : '/' + network.val }}/api/tx/15e10745f15593a899cef391191bdd3d7c12412cc4696b7bcb669d0feadc8521" target="_blank">GET {{ network.val === '' ? '' : '/' + network.val }}/api/tx/:txid</a></td>
<td>Returns details about a transaction. Available fields: <code>txid</code>, <code>version</code>, <code>locktime</code>, <code>size</code>, <code>weight</code>, <code>fee</code>, <code>vin</code>, <code>vout</code>, and <code>status</code>.</td>
<td i18n>Returns details about a transaction. Available fields: <code>txid</code>, <code>version</code>, <code>locktime</code>, <code>size</code>, <code>weight</code>, <code>fee</code>, <code>vin</code>, <code>vout</code>, and <code>status</code>.</td>
</tr>
<tr>
<td class="nowrap"><a href="{{ network.val === '' ? '' : '/' + network.val }}/api/tx/15e10745f15593a899cef391191bdd3d7c12412cc4696b7bcb669d0feadc8521/status" target="_blank">GET {{ network.val === '' ? '' : '/' + network.val }}/api/tx/:txid/status</a></td>
<td>Returns the confirmation status of a transaction. Available fields: <code>confirmed</code> (boolean), <code>block_height</code> (optional), and <code>block_hash</code> (optional).</td>
<td i18n>Returns the confirmation status of a transaction. Available fields: <code>confirmed</code> (boolean), <code>block_height</code> (optional), and <code>block_hash</code> (optional).</td>
</tr>
<tr>
<td class="nowrap"><a href="{{ network.val === '' ? '' : '/' + network.val }}/api/tx/15e10745f15593a899cef391191bdd3d7c12412cc4696b7bcb669d0feadc8521/hex" target="_blank">GET {{ network.val === '' ? '' : '/' + network.val }}/api/tx/:txid/hex</a></td>
<td>Returns a transaction serialized as hex.</td>
<td i18n>Returns a transaction serialized as hex.</td>
</tr>
<tr>
<td class="nowrap"><a href="{{ network.val === '' ? '' : '/' + network.val }}/api/tx/15e10745f15593a899cef391191bdd3d7c12412cc4696b7bcb669d0feadc8521/raw" target="_blank">GET {{ network.val === '' ? '' : '/' + network.val }}/api/tx/:txid/raw</a></td>
<td>Returns a transaction as binary data.</td>
<td i18n>Returns a transaction as binary data.</td>
</tr>
<tr *ngIf="network.val !== 'liquid'">
<td class="nowrap"><a href="{{ network.val === '' ? '' : '/' + network.val }}/api/tx/15e10745f15593a899cef391191bdd3d7c12412cc4696b7bcb669d0feadc8521/merkleblock-proof" target="_blank">GET {{ network.val === '' ? '' : '/' + network.val }}/api/tx/:txid/merkleblock-proof</a></td>
<td>Returns a merkle inclusion proof for the transaction using <a href="https://bitcoin.org/en/glossary/merkle-block">bitcoind's merkleblock</a> format.</td>
<td i18n>Returns a merkle inclusion proof for the transaction using <a href="https://bitcoin.org/en/glossary/merkle-block">bitcoind's merkleblock</a> format.</td>
</tr>
<tr>
<td class="nowrap"><a href="{{ network.val === '' ? '' : '/' + network.val }}/api/tx/15e10745f15593a899cef391191bdd3d7c12412cc4696b7bcb669d0feadc8521/merkle-proof" target="_blank">GET {{ network.val === '' ? '' : '/' + network.val }}/api/tx/:txid/merkle-proof</a></td>
<td>Returns a merkle inclusion proof for the transaction using <a href="https://electrumx.readthedocs.io/en/latest/protocol-methods.html#blockchain-transaction-get-merkle">Electrum's blockchain.transaction.get_merkle format.</a></td>
<td i18n>Returns a merkle inclusion proof for the transaction using <a href="https://electrumx.readthedocs.io/en/latest/protocol-methods.html#blockchain-transaction-get-merkle">Electrum's blockchain.transaction.get_merkle format.</a></td>
</tr>
<tr>
<td class="nowrap"><a href="{{ network.val === '' ? '' : '/' + network.val }}/api/tx/15e10745f15593a899cef391191bdd3d7c12412cc4696b7bcb669d0feadc8521/outspend/3" target="_blank">GET {{ network.val === '' ? '' : '/' + network.val }}/api/tx/:txid/outspend/:vout</a></td>
<td>Returns the spending status of a transaction output. Available fields: <code>spent</code> (boolean), <code>txid</code> (optional), <code>vin</code> (optional), and <code>status</code> (optional, the status of the spending tx).</td>
<td i18n>Returns the spending status of a transaction output. Available fields: <code>spent</code> (boolean), <code>txid</code> (optional), <code>vin</code> (optional), and <code>status</code> (optional, the status of the spending tx).</td>
</tr>
<tr>
<td class="nowrap"><a href="{{ network.val === '' ? '' : '/' + network.val }}/api/tx/15e10745f15593a899cef391191bdd3d7c12412cc4696b7bcb669d0feadc8521/outspends" target="_blank">GET {{ network.val === '' ? '' : '/' + network.val }}/api/tx/:txid/outspends</a></td>
<td>Returns the spending status of all transaction outputs.</td>
<td i18n>Returns the spending status of all transaction outputs.</td>
</tr>
<tr>
<td class="nowrap">POST {{ network.val === '' ? '' : '/' + network.val }}/api/tx</td>
<td>Broadcast a raw transaction to the network. The transaction should be provided as hex in the request body. The <code>txid</code> will be returned on success.</td>
<td i18n>Broadcast a raw transaction to the network. The transaction should be provided as hex in the request body. The <code>txid</code> will be returned on success.</td>
</tr>
</table>
@@ -176,33 +176,33 @@
</li>
<li *ngIf="network.val !== 'bisq'" [ngbNavItem]="5">
<a ngbNavLink>Addresses</a>
<a ngbNavLink i18n="api-docs.tab.addresses|API Docs tab for Addresses">Addresses</a>
<ng-template ngbNavContent>
<table class="table">
<tr>
<th style="border-top: 0;">Endpoint</th>
<th style="border-top: 0;">Description</th>
<th style="border-top: 0;" i18n="api-docs.shared.endpoint|API Docs Endpoint">Endpoint</th>
<th style="border-top: 0;" i18n="api-docs.shared.description|API Docs Description">Description</th>
</tr>
<tr>
<td class="nowrap"><a href="{{ network.val === '' ? '' : '/' + network.val }}/api/address/1wizSAYSbuyXbt9d8JV8ytm5acqq2TorC" target="_blank">GET {{ network.val === '' ? '' : '/' + network.val }}/api/address/:address</a></td>
<td>Returns details about an address. Available fields: <code>address</code>, <code>chain_stats</code>, and <code>mempool_stats</code>. {{ '{' }}chain,mempool{{ '}' }}_stats each contain an object with <code>tx_count</code>, <code>funded_txo_count</code>, <code>funded_txo_sum</code>, <code>spent_txo_count</code>, and <code>spent_txo_sum</code>.</td>
<td i18n>Returns details about an address. Available fields: <code>address</code>, <code>chain_stats</code>, and <code>mempool_stats</code>. {{ '{' }}chain,mempool{{ '}' }}_stats each contain an object with <code>tx_count</code>, <code>funded_txo_count</code>, <code>funded_txo_sum</code>, <code>spent_txo_count</code>, and <code>spent_txo_sum</code>.</td>
</tr>
<tr>
<td class="nowrap"><a href="{{ network.val === '' ? '' : '/' + network.val }}/api/address/1wizSAYSbuyXbt9d8JV8ytm5acqq2TorC/txs" target="_blank">GET {{ network.val === '' ? '' : '/' + network.val }}/api/address/:address/txs</a></td>
<td>Get transaction history for the specified address/scripthash, sorted with newest first. Returns up to 50 mempool transactions plus the first 25 confirmed transactions. You can request more confirmed transactions using <code>:last_seen_txid</code> (see below).
<td i18n>Get transaction history for the specified address/scripthash, sorted with newest first. Returns up to 50 mempool transactions plus the first 25 confirmed transactions. You can request more confirmed transactions using <code>:last_seen_txid</code> (see below).
</tr>
<tr>
<td class="nowrap"><a href="{{ network.val === '' ? '' : '/' + network.val }}/api/address/1wizSAYSbuyXbt9d8JV8ytm5acqq2TorC/txs/chain" target="_blank">GET {{ network.val === '' ? '' : '/' + network.val }}/api/address/:address/txs/chain</a></td>
<td>Get confirmed transaction history for the specified address/scripthash, sorted with newest first. Returns 25 transactions per page. More can be requested by specifying the last txid seen by the previous query.</td>
<td i18n>Get confirmed transaction history for the specified address/scripthash, sorted with newest first. Returns 25 transactions per page. More can be requested by specifying the last txid seen by the previous query.</td>
</tr>
<tr>
<td class="nowrap"><a href="{{ network.val === '' ? '' : '/' + network.val }}/api/address/1wizSAYSbuyXbt9d8JV8ytm5acqq2TorC/txs/mempool" target="_blank">GET {{ network.val === '' ? '' : '/' + network.val }}/api/address/:address/txs/mempool</a></td>
<td>Get unconfirmed transaction history for the specified address/scripthash. Returns up to 50 transactions (no paging).</td>
<td i18n>Get unconfirmed transaction history for the specified address/scripthash. Returns up to 50 transactions (no paging).</td>
</tr>
<tr>
<td class="nowrap"><a href="{{ network.val === '' ? '' : '/' + network.val }}/api/address/1wizSAYSbuyXbt9d8JV8ytm5acqq2TorC/utxo" target="_blank">GET {{ network.val === '' ? '' : '/' + network.val }}/api/address/:address/utxo</a></td>
<td>Get the list of unspent transaction outputs associated with the address/scripthash. Available fields: <code>txid</code>, <code>vout</code>, <code>value</code>, and <code>status</code> (with the status of the funding tx).<ng-container *ngIf="network.val === 'liquid'">There is also a <code>valuecommitment</code> field that may appear in place of <code>value</code>, plus the following additional fields: <code>asset</code>/<code>assetcommitment</code>, <code>nonce</code>/<code>noncecommitment</code>, <code>surjection_proof</code>, and <code>range_proof</code>.</ng-container></td>
<td i18n>Get the list of unspent transaction outputs associated with the address/scripthash. Available fields: <code>txid</code>, <code>vout</code>, <code>value</code>, and <code>status</code> (with the status of the funding tx).<ng-container *ngIf="network.val === 'liquid'">There is also a <code>valuecommitment</code> field that may appear in place of <code>value</code>, plus the following additional fields: <code>asset</code>/<code>assetcommitment</code>, <code>nonce</code>/<code>noncecommitment</code>, <code>surjection_proof</code>, and <code>range_proof</code>.</ng-container></td>
</tr>
</table>
@@ -210,66 +210,66 @@
</li>
<li *ngIf="network.val === 'liquid'" [ngbNavItem]="6">
<a ngbNavLink>Assets</a>
<a ngbNavLink i18n="api-docs.tab.assets|API Docs tab for Assets">Assets</a>
<ng-template ngbNavContent>
<table class="table">
<tr>
<th style="border-top: 0;">Endpoint</th>
<th style="border-top: 0;">Description</th>
<th style="border-top: 0;" i18n="api-docs.shared.endpoint|API Docs Endpoint">Endpoint</th>
<th style="border-top: 0;" i18n="api-docs.shared.description|API Docs Description">Description</th>
</tr>
<tr>
<td class="nowrap"><a href="/liquid/api/asset/6f0279e9ed041c3d710a9f57d0c02928416460c4b722ae3457a11eec381c526d" target="_blank">GET /liquid/api/asset/:asset_id</a></td>
<td>Returns information about a Liquid asset.</td>
<td i18n>Returns information about a Liquid asset.</td>
</tr>
<tr>
<td class="nowrap"><a href="/liquid/api/asset/4b5417ec5ab6112bedf539c3b4f5a806ed539542d8b717e1c4470aa3180edce5/txs" target="_blank">GET /liquid/api/asset/:asset_id/txs[/mempool|/chain]</a></td>
<td>Returns transactions associated with the specified Liquid asset. For the network's native asset, returns a list of peg in, peg out, and burn transactions. For user-issued assets, returns a list of issuance, reissuance, and burn transactions. Does not include regular transactions transferring this asset.</td>
<td i18n>Returns transactions associated with the specified Liquid asset. For the network's native asset, returns a list of peg in, peg out, and burn transactions. For user-issued assets, returns a list of issuance, reissuance, and burn transactions. Does not include regular transactions transferring this asset.</td>
</tr>
<tr>
<td class="nowrap"><a href="/liquid/api/asset/4b5417ec5ab6112bedf539c3b4f5a806ed539542d8b717e1c4470aa3180edce5/supply" target="_blank">GET /liquid/api/asset/:asset_id/supply[/decimal]</a></td>
<td>Get the current total supply of the specified asset. For the native asset (L-BTC), this is calculated as [chain,mempool]_stats.peg_in_amount - [chain,mempool]_stats.peg_out_amount - [chain,mempool]_stats.burned_amount. For issued assets, this is calculated as [chain,mempool]_stats.issued_amount - [chain,mempool]_stats.burned_amount. Not available for assets with blinded issuances. If /decimal is specified, returns the supply as a decimal according to the asset's divisibility. Otherwise, returned in base units.</td>
<td i18n>Get the current total supply of the specified asset. For the native asset (L-BTC), this is calculated as [chain,mempool]_stats.peg_in_amount - [chain,mempool]_stats.peg_out_amount - [chain,mempool]_stats.burned_amount. For issued assets, this is calculated as [chain,mempool]_stats.issued_amount - [chain,mempool]_stats.burned_amount. Not available for assets with blinded issuances. If /decimal is specified, returns the supply as a decimal according to the asset's divisibility. Otherwise, returned in base units.</td>
</tr>
</table>
</ng-template>
</li>
<li *ngIf="network.val === 'bisq'" [ngbNavItem]="1">
<a ngbNavLink>BSQ</a>
<a ngbNavLink i18n="api-docs.tab.bsq|API Docs tab for BSQ">BSQ</a>
<ng-template ngbNavContent>
<table class="table">
<tr>
<th style="border-top: 0;">Endpoint</th>
<th style="border-top: 0;">Description</th>
<th style="border-top: 0;" i18n="api-docs.shared.endpoint|API Docs Endpoint">Endpoint</th>
<th style="border-top: 0;" i18n="api-docs.shared.description|API Docs Description">Description</th>
</tr>
<tr>
<td class="nowrap"><a href="/bisq/api/stats" target="_blank">GET /bisq/api/stats</a></td>
<td>Returns statistics about all Bisq transactions.</td>
<td i18n>Returns statistics about all Bisq transactions.</td>
</tr>
<tr>
<td class="nowrap"><a href="/bisq/api/tx/4b5417ec5ab6112bedf539c3b4f5a806ed539542d8b717e1c4470aa3180edce5" target="_blank">GET /bisq/api/tx/:txid</a></td>
<td>Returns details about a Bisq transaction.</td>
<td i18n>Returns details about a Bisq transaction.</td>
</tr>
<tr>
<td class="nowrap"><a href="/bisq/api/txs/0/25" target="_blank">GET /bisq/api/txs/:index/:length</a></td>
<td>Returns :length of latest Bisq transactions, starting from :index.</td>
<td i18n>Returns :length of latest Bisq transactions, starting from :index.</td>
</tr>
<tr>
<td class="nowrap"><a href="/bisq/api/block/000000000000000000079aa6bfa46eb8fc20474e8673d6e8a123b211236bf82d" target="_blank">GET /bisq/api/block/:hash</a></td>
<td>Returns all Bisq transactions that exist in a Bitcoin block.</td>
<td i18n>Returns all Bisq transactions that exist in a Bitcoin block.</td>
</tr>
<tr>
<td class="nowrap"><a href="/bisq/api/blocks/0/25" target="_blank">GET /bisq/api/blocks/:index/:length</a></td>
<td>Returns :length Bitcoin blocks that contain Bisq transactions, starting from :index.</td>
<td i18n>Returns :length Bitcoin blocks that contain Bisq transactions, starting from :index.</td>
</tr>
<tr>
<td class="nowrap"><a href="/bisq/api/blocks/tip/height" target="_blank">GET /bisq/api/blocks/tip/height</a></td>
<td>Returns the most recently processed Bitcoin block height processed by Bisq.</td>
<td i18n>Returns the most recently processed Bitcoin block height processed by Bisq.</td>
</tr>
<tr>
<td class="nowrap"><a href="/bisq/api/address/B1DgwRN92rdQ9xpEVCdXRfgeqGw9X4YtrZz" target="_blank">GET /bisq/api/address/:address</a></td>
<td>Returns all Bisq transactions belonging to a Bitcoin address, with 'B' prefixed in front of the address.</td>
<td i18n>Returns all Bisq transactions belonging to a Bitcoin address, with 'B' prefixed in front of the address.</td>
</tr>
</table>
</ng-template>
@@ -281,7 +281,7 @@
<br>
<div class="text-center">
<a [routerLink]="['/terms-of-service']">Terms of Service</a>
<a [routerLink]="['/terms-of-service']" i18n="shared.terms-of-service|Terms of Service">Terms of Service</a>
</div>
</div>

View File

@@ -17,19 +17,19 @@
<table class="table table-borderless table-striped">
<tbody>
<tr>
<td>Name</td>
<td i18n="asset.name|Liquid Asset name">Name</td>
<td>{{ assetContract[2] }} ({{ assetContract[1] }})</td>
</tr>
<tr>
<td>Precision</td>
<td i18n="asset.precision|Liquid Asset precision">Precision</td>
<td>{{ assetContract[3] }}</td>
</tr>
<tr *ngIf="!isNativeAsset && assetContract[0]">
<td>Issuer</td>
<td i18n="asset.issuer|Liquid Asset issuer">Issuer</td>
<td><a target="_blank" href="{{ 'http://' + assetContract[0] }}">{{ assetContract[0] }}</a></td>
</tr>
<tr *ngIf="!isNativeAsset">
<td>Issuance tx</td>
<td i18n="asset.issuance-tx|Liquid Asset issuance TX">Issuance TX</td>
<td><a [routerLink]="['/tx/' | relativeUrl, asset.issuance_txin.txid]">{{ asset.issuance_txin.txid | shortenString : 13 }}</a> <app-clipboard class="d-none d-sm-inline-block" [text]="asset.issuance_txin.txid"></app-clipboard></td>
</tr>
</tbody>
@@ -40,27 +40,27 @@
<table class="table table-borderless table-striped">
<tbody>
<tr *ngIf="isNativeAsset">
<td>Pegged in</td>
<td i18n="asset.pegged-in|Liquid Asset pegged-in amount">Pegged in</td>
<td>{{ formatAmount(asset.chain_stats.peg_in_amount, assetContract[3]) | number: '1.0-' + assetContract[3] }} {{ assetContract[1] }}</td>
</tr>
<tr *ngIf="isNativeAsset">
<td>Pegged out</td>
<td i18n="asset.pegged-out|Liquid Asset pegged-out amount">Pegged out</td>
<td>{{ formatAmount(asset.chain_stats.peg_out_amount, assetContract[3]) | number: '1.0-' + assetContract[3] }} {{ assetContract[1] }}</td>
</tr>
<tr *ngIf="!isNativeAsset">
<td>Issued amount</td>
<td i18n="asset.issued-amount|Liquid Asset issued amount">Issued amount</td>
<td *ngIf="!blindedIssuance; else confidentialTd">{{ formatAmount(asset.chain_stats.issued_amount, assetContract[3]) | number: '1.0-' + assetContract[3] }} {{ assetContract[1] }}</td>
</tr>
<tr>
<td>Burned amount</td>
<td i18n="asset.burned-amount|Liquid Asset burned amount">Burned amount</td>
<td *ngIf="!blindedIssuance; else confidentialTd">{{ formatAmount(asset.chain_stats.burned_amount, assetContract[3]) | number: '1.0-' + assetContract[3] }} {{ assetContract[1] }}</td>
</tr>
<tr *ngIf="!isNativeAsset">
<td>Circulating amount</td>
<td i18n="asset.circulating-amount|Liquid Asset circulating amount">Circulating amount</td>
<td *ngIf="!blindedIssuance; else confidentialTd">{{ formatAmount(asset.chain_stats.issued_amount - asset.chain_stats.burned_amount, assetContract[3]) | number: '1.0-' + assetContract[3] }} {{ assetContract[1] }}</td>
</tr>
<tr *ngIf="isNativeAsset">
<td>Circulating amount</td>
<td i18n="asset.circulating-amount|Liquid Asset circulating amount">Circulating amount</td>
<td>{{ formatAmount(asset.chain_stats.peg_in_amount - asset.chain_stats.burned_amount - asset.chain_stats.peg_out_amount, assetContract[3]) | number: '1.0-' + assetContract[3] }} {{ assetContract[1] }}</td>
</tr>
</tbody>
@@ -129,7 +129,7 @@
<ng-template [ngIf]="error">
<div class="text-center">
Error loading asset data.
<span i18n="asset.error.loading-asset-data">Error loading asset data.</span>
<br>
<i>{{ error.error }}</i>
</div>
@@ -140,5 +140,5 @@
<br>
<ng-template #confidentialTd>
<td>Confidential</td>
<td i18n="shared.confidential">Confidential</td>
</ng-template>

View File

@@ -1,7 +1,7 @@
<div class="container-xl">
<div class="title-block">
<h1 class="float-left"><ng-template [ngIf]="blockHeight === 0">Genesis </ng-template>Block <ng-template [ngIf]="blockHeight"><a [routerLink]="['/block/' | relativeUrl, blockHash]">{{ blockHeight }}</a></ng-template></h1>
<h1 class="float-left"><ng-template [ngIf]="blockHeight === 0" i18n="block.genesis">Genesis </ng-template><ng-template [ngIf]="blockHeight" i18n="block.block">Block <a [routerLink]="['/block/' | relativeUrl, blockHash]">{{ blockHeight }}</a></ng-template></h1>
<button [routerLink]="['/' | relativeUrl]" class="btn btn-sm float-right mr-2 mt-2">&#10005;</button>
</div>
@@ -15,24 +15,24 @@
<table class="table table-borderless table-striped">
<tbody>
<tr>
<td class="td-width">Hash</td>
<td class="td-width" i18n="block.hash">Hash</td>
<td><a [routerLink]="['/block/' | relativeUrl, block.id]" title="{{ block.id }}">{{ block.id | shortenString : 13 }}</a> <app-clipboard class="d-none d-sm-inline-block" [text]="block.id"></app-clipboard></td>
</tr>
<tr>
<td>Timestamp</td>
<td i18n="block.timestamp">Timestamp</td>
<td>
{{ block.timestamp * 1000 | date:'yyyy-MM-dd HH:mm' }}
<div class="lg-inline">
<i>(<app-time-since [time]="block.timestamp" [fastRender]="true"></app-time-since> ago)</i>
<i>(<app-time-since [time]="block.timestamp" [fastRender]="true"></app-time-since>)</i>
</div>
</td>
</tr>
<tr>
<td>Size</td>
<td i18n="block.size">Size</td>
<td>{{ block.size | bytes: 2 }}</td>
</tr>
<tr>
<td>Weight</td>
<td i18n="block.weight">Weight</td>
<td>{{ block.weight | wuBytes: 2 }}</td>
</tr>
</tbody>
@@ -42,19 +42,19 @@
<table class="table table-borderless table-striped">
<tbody>
<tr *ngIf="block.medianFee !== undefined">
<td class="td-width">Median fee</td>
<td>~{{ block.medianFee | number:'1.0-0' }} sat/vB (<app-fiat [value]="block.medianFee * 140" digitsInfo="1.2-2" ngbTooltip="Based on average native segwit transaction of 140 vBytes" placement="bottom"></app-fiat>)</td>
<td class="td-width" i18n="block.median-fee">Median fee</td>
<td>~{{ block.medianFee | number:'1.0-0' }} <span i18n="shared.sat-vbyte|sat/vB">sat/vB</span> (<app-fiat [value]="block.medianFee * 140" digitsInfo="1.2-2" ngbTooltip="Based on average native segwit transaction of 140 vBytes" placement="bottom"></app-fiat>)</td>
</tr>
<ng-template [ngIf]="fees !== undefined" [ngIfElse]="loadingFees">
<tr>
<td>Total fees</td>
<td i18n="block.total-fees|Total fees in a block">Total fees</td>
<td *ngIf="network !== 'liquid'; else liquidTotalFees"><app-amount [satoshis]="fees * 100000000" digitsInfo="1.2-2" [noFiat]="true"></app-amount> (<app-fiat [value]="fees * 100000000" digitsInfo="1.0-0"></app-fiat>)</td>
<ng-template #liquidTotalFees>
<td>{{ fees * 100000000 | number }} L-sat (<app-fiat [value]="fees * 100000000" digitsInfo="1.2-2"></app-fiat>)</td>
</ng-template>
</tr>
<tr *ngIf="network !== 'liquid'">
<td>Subsidy + fees:</td>
<td i18n="block.subsidy-and-fees|Total subsidy and fees in a block">Subsidy + fees:</td>
<td>
<app-amount [satoshis]="(blockSubsidy + fees) * 100000000" digitsInfo="1.2-2" [noFiat]="true"></app-amount> (<app-fiat [value]="(blockSubsidy + fees) * 100000000" digitsInfo="1.0-0"></app-fiat>)
</td>
@@ -62,16 +62,16 @@
</ng-template>
<ng-template #loadingFees>
<tr>
<td>Total fees</td>
<td i18n="block.total-fees|Total fees in a block">Total fees</td>
<td style="width: 75%;"><span class="skeleton-loader"></span></td>
</tr>
<tr *ngIf="network !== 'liquid'">
<td>Subsidy + fees:</td>
<td i18n="block.subsidy-and-fees|Total subsidy and fees in a block">Subsidy + fees:</td>
<td><span class="skeleton-loader"></span></td>
</tr>
</ng-template>
<tr>
<td>Miner</td>
<td i18n="block.miner">Miner</td>
<td><app-miner [coinbaseTransaction]="coinbaseTx"></app-miner></td>
</tr>
</tbody>
@@ -82,7 +82,7 @@
<br>
<h2 class="float-left">{{ block.tx_count | number }} transaction<ng-template [ngIf]="block.tx_count !== 1">s</ng-template></h2>
<h2 class="float-left">{{ block.tx_count | number }} <ng-template [ngIf]="block.tx_count === 1" i18n="shared.transaction-count.singular">transaction</ng-template><ng-template [ngIf]="block.tx_count !== 1" i18n="shared.transaction-count.plural">transactions</ng-template></h2>
<ngb-pagination class="float-right" [collectionSize]="block.tx_count" [rotate]="true" [pageSize]="itemsPerPage" [(page)]="page" (pageChange)="pageChange(page)" [maxSize]="paginationMaxSize" [boundaryLinks]="true"></ngb-pagination>
@@ -162,7 +162,7 @@
<ng-template [ngIf]="error">
<div class="text-center">
Error loading block data.
<span i18n="block.error.loading-block-data">Error loading block data.</span>
<br><br>
<i>{{ error.error }}</i>
</div>

View File

@@ -7,14 +7,14 @@
</div>
<div class="block-body">
<div class="fees">
~{{ block.medianFee | number:'1.0-0' }} sat/vB
~{{ block.medianFee | number:'1.0-0' }} <span i18n="shared.sat-vbyte|sat/vB">sat/vB</span>
</div>
<div class="fee-span">
{{ block.feeRange[1] | number:'1.0-0' }} - {{ block.feeRange[block.feeRange.length - 1] | number:'1.0-0' }} sat/vB
{{ block.feeRange[1] | number:'1.0-0' }} - {{ block.feeRange[block.feeRange.length - 1] | number:'1.0-0' }} <span i18n="shared.sat-vbyte|sat/vB">sat/vB</span>
</div>
<div class="block-size">{{ block.size | bytes: 2 }}</div>
<div class="transaction-count">{{ block.tx_count | number }} transaction<ng-template [ngIf]="block.tx_count !== 1">s</ng-template></div>
<div class="time-difference"><app-time-since [time]="block.timestamp" [fastRender]="true"></app-time-since> ago</div>
<div class="transaction-count">{{ block.tx_count | number }} <ng-template [ngIf]="block.tx_count === 1" i18n="shared.transaction">transaction</ng-template><ng-template [ngIf]="block.tx_count !== 1" i18n="shared.transactions">transactions</ng-template></div>
<div class="time-difference"><app-time-since [time]="block.timestamp" [fastRender]="true"></app-time-since></div>
</div>
</div>
</div>

View File

@@ -8,7 +8,7 @@
</div>
<div *ngIf="(isLoading$ | async) === true" class="position-container loading">
<div class="loading-block">
<h3>Waiting for blocks...</h3>
<h3 i18n="blockchain.waiting-for-blocks|Loading text">Waiting for blocks...</h3>
<div class="spinner-border text-light mt-2"></div>
</div>
</div>

View File

@@ -2,4 +2,4 @@
<button #btn class="btn btn-sm btn-link pt-0" style="line-height: 0.9;" [attr.data-clipboard-text]="text">
<img src="./resources/clippy.svg" width="13">
</button>
</span>
</span>

View File

@@ -1,21 +1,21 @@
<table style="width: 100%;">
<tr *ngIf="(isLoadingWebSocket$ | async) === false && (feeEstimations$ | async) as feeEstimations; else loadingFees">
<td class="d-none d-md-block">
<h5 class="card-title">Low priority</h5>
<h5 class="card-title" i18n="fees-box.low-priority">Low priority</h5>
<p class="card-text">
{{ feeEstimations.hourFee }} sat/vB (<app-fiat [value]="feeEstimations.hourFee * 140" ngbTooltip="Based on average native segwit transaction of 140 vBytes" placement="bottom"></app-fiat>)
{{ feeEstimations.hourFee }} <span i18n="shared.sat-vbyte|sat/vB">sat/vB</span> (<app-fiat [value]="feeEstimations.hourFee * 140" ngbTooltip="Based on average native segwit transaction of 140 vBytes" placement="bottom"></app-fiat>)
</p>
</td>
<td>
<h5 class="card-title">Medium priority</h5>
<h5 class="card-title" i18n="fees-box.medium-priority">Medium priority</h5>
<p class="card-text">
{{ feeEstimations.halfHourFee }} sat/vB (<app-fiat [value]="feeEstimations.halfHourFee * 140" ngbTooltip="Based on average native segwit transaction of 140 vBytes" placement="bottom"></app-fiat>)
{{ feeEstimations.halfHourFee }} <span i18n="shared.sat-vbyte|sat/vB">sat/vB</span> (<app-fiat [value]="feeEstimations.halfHourFee * 140" ngbTooltip="Based on average native segwit transaction of 140 vBytes" placement="bottom"></app-fiat>)
</p>
</td>
<td>
<h5 class="card-title">High priority</h5>
<h5 class="card-title" i18n="fees-box.high-priority">High priority</h5>
<p class="card-text">
{{ feeEstimations.fastestFee }} sat/vB (<app-fiat [value]="feeEstimations.fastestFee * 140" ngbTooltip="Based on average native segwit transaction of 140 vBytes" placement="bottom"></app-fiat>)
{{ feeEstimations.fastestFee }} <span i18n="shared.sat-vbyte|sat/vB">sat/vB</span> (<app-fiat [value]="feeEstimations.fastestFee * 140" ngbTooltip="Based on average native segwit transaction of 140 vBytes" placement="bottom"></app-fiat>)
</p>
</td>
</tr>
@@ -24,15 +24,15 @@
<ng-template #loadingFees>
<tr>
<td class="d-none d-md-block">
<h5 class="card-title">Low priority</h5>
<h5 class="card-title" i18n="fees-box.low-priority">Low priority</h5>
<p class="card-text"><span class="skeleton-loader"></span></p>
</td>
<td>
<h5 class="card-title">Medium priority</h5>
<h5 class="card-title" i18n="fees-box.medium-priority">Medium priority</h5>
<p class="card-text"><span class="skeleton-loader" style="width: 80%;"></span></p>
</td>
<td>
<h5 class="card-title">High priority</h5>
<h5 class="card-title" i18n="fees-box.high-priority">High priority</h5>
<p class="card-text"><span class="skeleton-loader"></span></p>
</td>
</tr>

View File

@@ -2,22 +2,22 @@
<div class="container-xl">
<div class="row text-center" *ngIf="mempoolInfoData$ | async as mempoolInfoData">
<div class="col d-none d-sm-block">
<span class="txPerSecond">Tx vBytes per second:</span>
<span class="txPerSecond" i18n="footer.tx-vbytes-per-second">Tx vBytes per second:</span>
<span *ngIf="mempoolInfoData.vBytesPerSecond === 0; else inSync">
&nbsp;<span class="badge badge-pill badge-warning">Backend is synchronizing</span>
&nbsp;<span class="badge badge-pill badge-warning" i18n="footer.backend-is-synchronizing">Backend is synchronizing</span>
</span>
<ng-template #inSync>
<div class="progress sub-text">
<div class="progress-bar {{ mempoolInfoData.progressClass }}" role="progressbar" [ngStyle]="{'width': mempoolInfoData.progressWidth}">{{ mempoolInfoData.vBytesPerSecond | ceil | number }} vBytes/s</div>
<div class="progress-bar {{ mempoolInfoData.progressClass }}" role="progressbar" [ngStyle]="{'width': mempoolInfoData.progressWidth}">{{ mempoolInfoData.vBytesPerSecond | ceil | number }} <span i18n="shared.vbytes-per-second">vBytes/s</span></div>
</div>
</ng-template>
</div>
<div class="col">
<span class="unconfirmedTx">Unconfirmed<span class="extra-text"> transactions</span>:</span>
<span class="unconfirmedTx"><span i18n="shared.unconfirmed">Unconfirmed</span> <span class="extra-text" i18n="shared.transactions">transactions</span>:</span>
<div class="sub-text">{{ mempoolInfoData.memPoolInfo.size | number }}</div>
</div>
<div class="col">
<span class="mempoolSize">Mempool size:</span>
<span class="mempoolSize" i18n="footer.mempool-size">Mempool size:</span>
<div class="sub-text" *ngIf="(mempoolBlocksData$ | async) as mempoolBlocksData">{{ mempoolBlocksData.size | bytes }} ({{ mempoolBlocksData.blocks }} block<span [hidden]="mempoolBlocksData.blocks <= 1">s</span>)</div>
</div>
</div>

View File

@@ -6,17 +6,17 @@
<table class="table table-borderless" [alwaysCallback]="true" [fromRoot]="true" [infiniteScrollContainer]="'body'" infiniteScroll [infiniteScrollDistance]="1.5" [infiniteScrollUpDistance]="1.5" [infiniteScrollThrottle]="50" (scrolled)="loadMore()">
<thead>
<th style="width: 15%;">Height</th>
<th class="d-none d-md-block" style="width: 20%;">Timestamp</th>
<th style="width: 20%;">Mined</th>
<th class="d-none d-lg-block" style="width: 15%;">Transactions</th>
<th style="width: 20%;">Filled</th>
<th style="width: 15%;" i18n="latest-blocks.height">Height</th>
<th class="d-none d-md-block" style="width: 20%;" i18n="latest-blocks.timestamp">Timestamp</th>
<th style="width: 20%;" i18n="latest-blocks.mined">Mined</th>
<th class="d-none d-lg-block" style="width: 15%;" i18n="latest-blocks.transactions">Transactions</th>
<th style="width: 20%;" i18n="latest-blocks.filled">Filled</th>
</thead>
<tbody>
<tr *ngFor="let block of blocks; let i= index; trackBy: trackByBlock">
<td><a [routerLink]="['/block' | relativeUrl, block.id]" [state]="{ data: { block: block } }">{{ block.height }}</a></td>
<td class="d-none d-md-block">{{ block.timestamp * 1000 | date:'yyyy-MM-dd HH:mm' }}</td>
<td><app-time-since [time]="block.timestamp" [fastRender]="true"></app-time-since> ago</td>
<td><app-time-since [time]="block.timestamp" [fastRender]="true"></app-time-since></td>
<td class="d-none d-lg-block">{{ block.tx_count | number }}</td>
<td>
<div class="progress position-relative">

View File

@@ -4,8 +4,8 @@
<a class="navbar-brand" [routerLink]="['/' | relativeUrl]" style="position: relative;">
<ng-container *ngIf="{ val: connectionState$ | async } as connectionState">
<img src="./resources/mempool-logo.png" height="35" width="140" class="logo" [ngStyle]="{'opacity': connectionState.val === 2 ? 1 : 0.5 }">
<div class="badge badge-warning connection-badge" *ngIf="connectionState.val === 0">Offline</div>
<div class="badge badge-warning connection-badge" style="left: 0px;" *ngIf="connectionState.val === 1">Reconnecting...</div>
<div class="badge badge-warning connection-badge" *ngIf="connectionState.val === 0" i18n="master-page.offline">Offline</div>
<div class="badge badge-warning connection-badge" style="left: 0px;" *ngIf="connectionState.val === 1" i18n="master-page.reconnecting">Reconnecting...</div>
</ng-container>
</a>
@@ -16,7 +16,7 @@
<div ngbDropdownMenu>
<button ngbDropdownItem class="mainnet" routerLink="/"><img src="./resources/bitcoin-logo.png" style="width: 30px;" class="mr-1"> Mainnet</button>
<button ngbDropdownItem *ngIf="env.TESTNET_ENABLED" class="testnet" [class.active]="network.val === 'testnet'" routerLink="/testnet"><img src="./resources/testnet-logo.png" style="width: 30px;" class="mr-1"> Testnet</button>
<h6 *ngIf="env.LIQUID_ENABLED || env.BISQ_ENABLED" class="dropdown-header">Layer 2 Networks</h6>
<h6 *ngIf="env.LIQUID_ENABLED || env.BISQ_ENABLED" class="dropdown-header" i18n="master-page.layer2-networks-header">Layer 2 Networks</h6>
<button ngbDropdownItem *ngIf="env.BISQ_ENABLED" class="mainnet" [class.active]="network.val === 'bisq'" routerLink="/bisq"><img src="./resources/bisq-logo.png" style="width: 30px;" class="mr-1"> Bisq</button>
<button ngbDropdownItem *ngIf="env.LIQUID_ENABLED" class="liquid" [class.active]="network.val === 'liquid'" routerLink="/liquid"><img src="./resources/liquid-logo.png" style="width: 30px;" class="mr-1"> Liquid</button>
</div>

View File

@@ -13,23 +13,23 @@
<table class="table table-borderless table-striped">
<tbody>
<tr>
<td>Median fee</td>
<td i18n="mempool-block.median-fee">Median fee</td>
<td>~{{ mempoolBlock.medianFee | number:'1.0-0' }} sat/vB (<app-fiat [value]="mempoolBlock.medianFee * 140" digitsInfo="1.2-2" ngbTooltip="Based on average native segwit transaction of 140 vBytes" placement="bottom"></app-fiat>)</td>
</tr>
<tr>
<td>Fee span</td>
<td i18n="mempool-block.fee-span">Fee span</td>
<td><span class="yellow-color">{{ mempoolBlock.feeRange[0] | number:'1.0-0' }} - {{ mempoolBlock.feeRange[mempoolBlock.feeRange.length - 1] | number:'1.0-0' }} sat/vB</span></td>
</tr>
<tr>
<td>Total fees</td>
<td i18n="mempool-block.total-fees">Total fees</td>
<td><app-amount [satoshis]="mempoolBlock.totalFees" [digitsInfo]="'1.2-2'" [noFiat]="true"></app-amount> (<app-fiat [value]="mempoolBlock.totalFees" digitsInfo="1.0-0"></app-fiat>)</td>
</tr>
<tr>
<td>Transactions</td>
<td i18n="mempool-block.transactions">Transactions</td>
<td>{{ mempoolBlock.nTx }}</td>
</tr>
<tr>
<td>Filled</td>
<td i18n="mempool-block.filled">Filled</td>
<td>
<div class="progress position-relative">
<div class="progress-bar progress-mempool {{ (network$ | async) }}" role="progressbar" [ngStyle]="{'width': (mempoolBlock.blockVSize / 1000000) * 100 + '%' }"></div>
@@ -48,4 +48,4 @@
<br>
</div>
</div>

View File

@@ -5,23 +5,23 @@
<a [routerLink]="['/mempool-block/' | relativeUrl, i]" class="blockLink">&nbsp;</a>
<div class="block-body">
<div class="fees">
~{{ projectedBlock.medianFee | number:'1.0-0' }} sat/vB
~{{ projectedBlock.medianFee | number:'1.0-0' }} <span i18n="shared.sat-vbyte|sat/vB">sat/vB</span>
</div>
<div class="fee-span">
{{ projectedBlock.feeRange[0] | number:'1.0-0' }} - {{ projectedBlock.feeRange[projectedBlock.feeRange.length - 1] | number:'1.0-0' }} sat/vB
{{ projectedBlock.feeRange[0] | number:'1.0-0' }} - {{ projectedBlock.feeRange[projectedBlock.feeRange.length - 1] | number:'1.0-0' }} <span i18n="shared.sat-vbyte|sat/vB">sat/vB</span>
</div>
<div class="block-size">{{ projectedBlock.blockSize | bytes: 2 }}</div>
<div class="transaction-count">{{ projectedBlock.nTx | number }} transaction<ng-template [ngIf]="projectedBlock.nTx !== 1">s</ng-template></div>
<div class="transaction-count">{{ projectedBlock.nTx | number }} <ng-template [ngIf]="projectedBlock.nTx === 1" i18n="shared.transaction-count.singular">transaction</ng-template><ng-template [ngIf]="projectedBlock.nTx !== 1" i18n="shared.transaction-count.plural">transactions</ng-template></div>
<div class="time-difference" *ngIf="projectedBlock.blockVSize <= 1000000; else mergedBlock">
<ng-template [ngIf]="network === 'liquid'" [ngIfElse]="timeDiffMainnet">
In &lt; {{ 1 * i + 1 }} minute
<span i18n="mempool-blocks.eta-of-next-block|Block Frequency">In</span> &lt; {{ 1 * i + 1 }} <span i18n="shared.minute">minute</span>
</ng-template>
<ng-template #timeDiffMainnet>
In ~{{ 10 * i + 10 }} minutes
<span i18n="mempool-blocks.eta-of-next-block|Block Frequency">In</span> ~{{ 10 * i + 10 }} <span i18n="shared.minutes">minutes</span>
</ng-template>
</div>
<ng-template #mergedBlock>
<div class="time-difference"><b>({{ projectedBlock.blockVSize / 1000000 | ceil }} blocks)</b></div>
<div class="time-difference"><b>({{ projectedBlock.blockVSize / 1000000 | ceil }} <span i18n="shared.blocks">blocks</span>)</b></div>
</ng-template>
</div>
<span class="animated-border"></span>

View File

@@ -7,6 +7,6 @@
<a placement="bottom" [ngbTooltip]="title" [href]="url" target="_blank" class="badge badge-primary">{{ miner }}</a>
</ng-template>
<ng-template #unknownMiner>
<span class="badge badge-secondary">Unknown</span>
<span class="badge badge-secondary" i18n="miner.tag.unknown-miner">Unknown</span>
</ng-template>
</ng-template>

View File

@@ -1,10 +1,10 @@
<form [formGroup]="searchForm" (submit)="searchForm.valid && search()" novalidate>
<div class="d-flex">
<div class="search-box-container mr-2">
<input #instance="ngbTypeahead" [ngbTypeahead]="typeaheadSearch" (selectItem)="itemSelected()" (focus)="focus$.next($any($event).target.value)" (click)="click$.next($any($event).target.value)" formControlName="searchText" type="text" class="form-control" placeholder="TXID, block height, hash or address">
<input #instance="ngbTypeahead" [ngbTypeahead]="typeaheadSearch" (selectItem)="itemSelected()" (focus)="focus$.next($any($event).target.value)" (click)="click$.next($any($event).target.value)" formControlName="searchText" type="text" class="form-control" i18n-placeholder="search-form.searchbar-placeholder" placeholder="TXID, block height, hash or address">
</div>
<div>
<button [disabled]="isSearching" type="submit" class="btn btn-block btn-primary"><fa-icon [icon]="['fas', 'search']" [fixedWidth]="true" title="Search"></fa-icon></button>
</div>
</div>
</form>
</form>

View File

@@ -3,7 +3,7 @@
<div class="row">
<div class="col-lg-12" *ngIf="loading">
<div class="text-center">
<h3>Loading graphs...</h3>
<h3 i18n="statistics.loading-graphs">Loading graphs...</h3>
<br>
<div class="spinner-border text-light"></div>
</div>
@@ -13,7 +13,7 @@
<div class="card mb-3" *ngIf="mempoolStats.length">
<div class="card-header">
<i class="fa fa-area-chart"></i> Mempool by vBytes (sat/vByte)
<i class="fa fa-area-chart"></i> <span i18n="statistics.memory-by-vBytes">Mempool by vBytes (sat/vByte)</span>
<form [formGroup]="radioGroupForm" style="float: right;">
<div class="spinner-border text-light bootstrap-spinner" *ngIf="spinnerLoading"></div>
@@ -54,7 +54,8 @@
<div class="col-lg-12">
<div class="card mb-3" *ngIf="mempoolTransactionsWeightPerSecondData">
<div class="card-header">
<i class="fa fa-area-chart"></i> Transaction vBytes per second (vB/s)</div>
<i class="fa fa-area-chart"></i> <span i18n="statistics.transaction-vbytes-per-second">Transaction vBytes per second (vB/s)</span>
</div>
<div class="card-body">
<div style="height: 600px;">
<app-chartist

View File

@@ -18,4 +18,4 @@
</div>
</div>
</div>

View File

@@ -18,17 +18,6 @@ export class TimeSinceComponent implements OnInit, OnChanges, OnDestroy {
private ref: ChangeDetectorRef,
private stateService: StateService,
) {
if (document.body.clientWidth < 768) {
this.intervals = {
year: 31536000,
month: 2592000,
week: 604800,
day: 86400,
hour: 3600,
min: 60,
sec: 1
};
} else {
this.intervals = {
year: 31536000,
month: 2592000,
@@ -38,7 +27,6 @@ export class TimeSinceComponent implements OnInit, OnChanges, OnDestroy {
minute: 60,
second: 1
};
}
}
ngOnInit() {
@@ -65,7 +53,7 @@ export class TimeSinceComponent implements OnInit, OnChanges, OnDestroy {
calculate() {
const seconds = Math.floor((+new Date() - +new Date(this.time * 1000)) / 1000);
if (seconds < 60) {
return '< 1 minute';
return $localize`:@@time-since.just-now:Just now`;
}
let counter;
for (const i in this.intervals) {
@@ -73,9 +61,41 @@ export class TimeSinceComponent implements OnInit, OnChanges, OnDestroy {
counter = Math.floor(seconds / this.intervals[i]);
if (counter > 0) {
if (counter === 1) {
return counter + ' ' + i; // singular (1 day ago)
switch (i) { // singular (1 day ago)
case 'year': return $localize`:@@time-since.year.ago:${counter}:INTERPOLATION: year ago`; break;
case 'month': return $localize`:@@time-since.month.ago:${counter}:INTERPOLATION: month ago`; break;
case 'week': return $localize`:@@time-since.week.ago:${counter}:INTERPOLATION: week ago`; break;
case 'day': return $localize`:@@time-since.day.ago:${counter}:INTERPOLATION: day ago`; break;
case 'hour': return $localize`:@@time-since.hour.ago:${counter}:INTERPOLATION: hour ago`; break;
case 'minute':
if (document.body.clientWidth < 768) {
return $localize`:@@time-since.min.ago:${counter}:INTERPOLATION: min ago`;
}
return $localize`:@@time-since.minute.ago:${counter}:INTERPOLATION: minute ago`;
case 'second':
if (document.body.clientWidth < 768) {
return $localize`:@@time-since.sec.ago:${counter}:INTERPOLATION: sec ago`;
}
return $localize`:@@time-since.second.ago:${counter}:INTERPOLATION: second ago`;
}
} else {
return counter + ' ' + i + 's'; // plural (2 days ago)
switch (i) { // plural (2 days ago)
case 'year': return $localize`:@@time-since.years.ago:${counter}:INTERPOLATION: years ago`; break;
case 'month': return $localize`:@@time-since.months.ago:${counter}:INTERPOLATION: months ago`; break;
case 'week': return $localize`:@@time-since.weeks.ago:${counter}:INTERPOLATION: weeks ago`; break;
case 'day': return $localize`:@@time-since.days.ago:${counter}:INTERPOLATION: days ago`; break;
case 'hour': return $localize`:@@time-since.hours.ago:${counter}:INTERPOLATION: hours ago`; break;
case 'minute':
if (document.body.clientWidth < 768) {
return $localize`:@@time-since.mins.ago:${counter}:INTERPOLATION: mins ago`;
}
return $localize`:@@time-since.minutes.ago:${counter}:INTERPOLATION: minutes ago`;
case 'second':
if (document.body.clientWidth < 768) {
return $localize`:@@time-since.secs.ago:${counter}:INTERPOLATION: secs ago`;
}
return $localize`:@@time-since.seconds.ago:${counter}:INTERPOLATION: seconds ago`;
}
}
}
}

View File

@@ -14,7 +14,8 @@ export class TimespanComponent implements OnChanges {
ngOnChanges() {
const seconds = this.time;
if (seconds < 60) {
return '< 1 minute';
this.text = '< 1 minute';
return;
}
const intervals = {
year: 31536000,

View File

@@ -2,20 +2,20 @@
<div class="title-block">
<div *ngIf="rbfTransaction" class="alert alert-mempool" role="alert">
This transaction has been replaced by:
<span i18n="transaction.rbf.replacement|RBF replacement">This transaction has been replaced by:</span>
<a class="alert-link" [routerLink]="['/tx/' | relativeUrl, rbfTransaction.txid]" [state]="{ data: rbfTransaction }">
<span class="d-inline d-lg-none">{{ rbfTransaction.txid | shortenString : 24 }}</span>
<span class="d-none d-lg-inline">{{ rbfTransaction.txid }}</span>
</a>
</div>
<h1 class="float-left mr-3 mb-md-3">Transaction</h1>
<h1 class="float-left mr-3 mb-md-3" i18n="shared.transaction">Transaction</h1>
<ng-template [ngIf]="tx?.status?.confirmed">
<button *ngIf="latestBlock" type="button" class="btn btn-sm btn-success float-right mr-2 mt-1 mt-md-3">{{ latestBlock.height - tx.status.block_height + 1 }} confirmation<ng-container *ngIf="latestBlock.height - tx.status.block_height + 1 > 1">s</ng-container></button>
<button *ngIf="latestBlock" type="button" class="btn btn-sm btn-success float-right mr-2 mt-1 mt-md-3">{{ latestBlock.height - tx.status.block_height + 1 }} <ng-container *ngIf="latestBlock.height - tx.status.block_height + 1 == 1" i18n="shared.confirmation-count.singular|Transaction singular confirmation count">confirmation</ng-container><ng-container *ngIf="latestBlock.height - tx.status.block_height + 1 > 1" i18n="shared.confirmation-count.plural|Transaction plural confirmation count">confirmations</ng-container></button>
</ng-template>
<ng-template [ngIf]="tx && !tx?.status.confirmed">
<button type="button" class="btn btn-sm btn-danger float-right mr-2 mt-1 mt-md-3">Unconfirmed</button>
<button type="button" class="btn btn-sm btn-danger float-right mr-2 mt-1 mt-md-3" i18n="transaction.unconfirmed|Transaction unconfirmed state">Unconfirmed</button>
</ng-template>
<div>
@@ -39,28 +39,28 @@
<table class="table table-borderless table-striped">
<tbody>
<tr>
<td>Timestamp</td>
<td i18n="transaction.timestamp|Transaction Timestamp">Timestamp</td>
<td>
{{ tx.status.block_time * 1000 | date:'yyyy-MM-dd HH:mm' }}
<div class="lg-inline">
<i>(<app-time-since [time]="tx.status.block_time" [fastRender]="true"></app-time-since> ago)</i>
<i>(<app-time-since [time]="tx.status.block_time" [fastRender]="true"></app-time-since>)</i>
</div>
</td>
</tr>
<tr *ngIf="latestBlock && tx.status.block_height <= latestBlock.height - 8">
<td class="td-width">Included in block</td>
<td class="td-width" i18n="transaction.included-in-block|Transaction included in block">Included in block</td>
<td>
<a [routerLink]="['/block/' | relativeUrl, tx.status.block_hash]" [state]="{ data: { blockHeight: tx.status.block_height } }">{{ tx.status.block_height }}</a>
</td>
</tr>
<ng-template [ngIf]="transactionTime > 0">
<tr>
<td>Confirmed</td>
<td>After <app-timespan [time]="tx.status.block_time - transactionTime"></app-timespan></td>
<td i18n="transaction.confirmed|Transaction Confirmed state">Confirmed</td>
<td><span i18n="transaction.confirmed.after|Transaction confirmed after">After</span>&nbsp;<app-timespan [time]="tx.status.block_time - transactionTime"></app-timespan></td>
</tr>
</ng-template>
<tr *ngIf="network !== 'liquid'">
<td class="td-width">Features</td>
<td class="td-width" i18n="transaction.features|Transaction features">Features</td>
<td>
<app-tx-features [tx]="tx"></app-tx-features>
</td>
@@ -72,13 +72,13 @@
<table class="table table-borderless table-striped">
<tbody>
<tr>
<td class="td-width">Fee</td>
<td class="td-width" i18n="transaction.fee|Transaction fee">Fee</td>
<td>{{ tx.fee | number }} sat (<app-fiat [value]="tx.fee"></app-fiat>)</td>
</tr>
<tr>
<td>Fee per vByte</td>
<td i18n="transaction.fee-per-vbyte|Transaction fee">Fee per vByte</td>
<td>
{{ tx.fee / (tx.weight / 4) | number : '1.1-1' }} sat/vB
{{ tx.fee / (tx.weight / 4) | number : '1.1-1' }} <span i18n="shared.sat-vbyte|sat/vB">sat/vB</span>
&nbsp;
<app-tx-fee-rating *ngIf="tx.fee" [tx]="tx"></app-tx-fee-rating>
</td>
@@ -106,35 +106,34 @@
</tr>
<ng-template #firstSeenTmpl>
<tr>
<td>First seen</td>
<td><i><app-time-since [time]="transactionTime" [fastRender]="true"></app-time-since> ago</i></td>
<td i18n="transaction.first-seen|Transaction first seen">First seen</td>
<td><i><app-time-since [time]="transactionTime" [fastRender]="true"></app-time-since></i></td>
</tr>
</ng-template>
</ng-template>
<tr>
<td class="td-width">ETA</td>
<td class="td-width" i18n="transaction.eta|Transaction ETA">ETA</td>
<td>
<ng-template [ngIf]="txInBlockIndex === undefined" [ngIfElse]="estimationTmpl">
<span class="skeleton-loader"></span>
</ng-template>
<ng-template #estimationTmpl>
<ng-template [ngIf]="txInBlockIndex >= 7" [ngIfElse]="belowBlockLimit">
In several hours (or more)
<span i18n="transaction.eta.in-several-hours|Transaction ETA in several hours or more">In several hours (or more)</span>
</ng-template>
<ng-template #belowBlockLimit>
<ng-template [ngIf]="network === 'liquid'" [ngIfElse]="timeEstimateDefault">
&lt; {{ 1 * txInBlockIndex + 1 }} minutes <i>({{ txInBlockIndex + 1 }} block{{ txInBlockIndex > 0 ? 's' : '' }})</i>
&lt; {{ 1 * txInBlockIndex + 1 }}&nbsp;<span i18n="transaction.minutes|Transaction Minutes">minutes</span>&nbsp;<i>({{ txInBlockIndex + 1 }} <span i18n="transaction.eta.block|Transaction ETA (X blocks)">block</span>{{ txInBlockIndex > 0 ? 's' : '' }})</i>
</ng-template>
<ng-template #timeEstimateDefault>
~{{ 10 * txInBlockIndex + 10 }} minutes <i>({{ txInBlockIndex + 1 }} block{{ txInBlockIndex > 0 ? 's' : '' }})</i>
~{{ 10 * txInBlockIndex + 10 }}&nbsp;<span i18n="transaction.minutes|Transaction Minutes">minutes</span>&nbsp;<i>({{ txInBlockIndex + 1 }} <span i18n="transaction.eta.block|Transaction ETA (X blocks)">block</span>{{ txInBlockIndex > 0 ? 's' : '' }})</i>
</ng-template>
</ng-template>
</ng-template>
</td>
</tr>
<tr *ngIf="network !== 'liquid'">
<td class="td-width">Features</td>
<td class="td-width" i18n="transaction.features|Transaction Features">Features</td>
<td>
<app-tx-features [tx]="tx"></app-tx-features>
</td>
@@ -146,12 +145,12 @@
<table class="table table-borderless table-striped">
<tbody>
<tr>
<td class="td-width">Fee</td>
<td>{{ tx.fee | number }} sat (<app-fiat [value]="tx.fee"></app-fiat>)</td>
<td class="td-width" i18n="transaction.fee|Transaction Fee">Fee</td>
<td>{{ tx.fee | number }} <span i18n="transaction.fee.sat|Transaction Fee sat">sat</span> (<app-fiat [value]="tx.fee"></app-fiat>)</td>
</tr>
<tr>
<td>Fee per vByte</td>
<td>{{ tx.fee / (tx.weight / 4) | number : '1.1-1' }} sat/vB</td>
<td i18n="transaction.fee-per-vbyte|Transaction fee">Fee per vByte</td>
<td>{{ tx.fee / (tx.weight / 4) | number : '1.1-1' }} <span i18n="shared.sat-vbyte|sat/vB">sat/vB</span></td>
</tr>
</tbody>
</table>
@@ -162,24 +161,24 @@
<br>
<h2 class="float-left">Inputs & Outputs</h2>
<h2 class="float-left" i18n="transaction.inputs-and-outputs|Transaction inputs and outputs">Inputs & Outputs</h2>
<button type="button" class="btn btn-outline-info btn-sm float-right mr-1 mt-0 mt-md-2" (click)="txList.toggleDetails()">Details</button>
<button type="button" class="btn btn-outline-info btn-sm float-right mr-1 mt-0 mt-md-2" (click)="txList.toggleDetails()" i18n="transaction.details|Transaction Details">Details</button>
<div class="clearfix"></div>
<app-transactions-list #txList [transactions]="[tx]" [transactionPage]="true"></app-transactions-list>
<h2>Details</h2>
<h2 i18n="transaction.details">Details</h2>
<div class="box">
<table class="table table-borderless table-striped">
<tbody>
<tr>
<td>Size</td>
<td i18n="transaction.size|Transaction Size">Size</td>
<td>{{ tx.size | bytes: 2 }}</td>
</tr>
<tr>
<td>Weight</td>
<td i18n="transaction.weight|Transaction Weight">Weight</td>
<td>{{ tx.weight | wuBytes: 2 }}</td>
</tr>
</tbody>
@@ -263,8 +262,8 @@
<ng-template [ngIf]="error">
<div class="text-center" *ngIf="waitingForTransaction; else errorTemplate">
<h3>Transaction not found.</h3>
<h5>Waiting for it to appear in the mempool...</h5>
<h3 i18n="transaction.error.transaction-not-found">Transaction not found.</h3>
<h5 i18n="transaction.error.waiting-for-it-to-appear">Waiting for it to appear in the mempool...</h5>
<div class="spinner-border text-light mt-2"></div>
</div>

View File

@@ -7,7 +7,7 @@
<div class="float-right">
<ng-template [ngIf]="tx.status.confirmed">{{ 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> ago</i>
<i><app-time-since [time]="tx.firstSeen" [fastRender]="true"></app-time-since></i>
</ng-template>
</div>
<div class="clearfix"></div>
@@ -36,9 +36,9 @@
</td>
<td>
<div [ngSwitch]="true">
<ng-container *ngSwitchCase="vin.is_coinbase"><a placement="bottom" [ngbTooltip]="vin.scriptsig | hex2ascii">Coinbase<ng-template [ngIf]="network !== 'liquid'"> (Newly Generated Coins)</ng-template></a><br><span class="badge badge-secondary scriptmessage longer">{{ vin.scriptsig | hex2ascii }}</span></ng-container>
<ng-container *ngSwitchCase="vin.is_coinbase"><a placement="bottom" [ngbTooltip]="vin.scriptsig | hex2ascii"><span i18n="transactions-list.coinbase">Coinbase</span><ng-template [ngIf]="network !== 'liquid'">&nbsp;<span i18n="transactions-list.newly-generated-coins">(Newly Generated Coins)</span></ng-template></a><br><span class="badge badge-secondary scriptmessage longer">{{ vin.scriptsig | hex2ascii }}</span></ng-container>
<ng-container *ngSwitchCase="vin.is_pegin">
Peg-in
<span i18n="transactions-list.peg-in">Peg-in</span>
</ng-container>
<ng-container *ngSwitchDefault>
<a [routerLink]="['/address/' | relativeUrl, vin.prevout.scriptpubkey_address]" title="{{ vin.prevout.scriptpubkey_address }}">
@@ -68,32 +68,32 @@
<tbody>
<ng-template [ngIf]="vin.scriptsig">
<tr>
<td>ScriptSig (ASM)</td>
<td i18n="transactions-list.scriptsig.asm|ScriptSig (ASM)">ScriptSig (ASM)</td>
<td [innerHTML]="vin.scriptsig_asm | asmStyler"></td>
</tr>
<tr>
<td>ScriptSig (HEX)</td>
<td i18n="transactions-list.scriptsig.hex|ScriptSig (HEX)">ScriptSig (HEX)</td>
<td>{{ vin.scriptsig }}</td>
</tr>
</ng-template>
<tr *ngIf="vin.witness">
<td>Witness</td>
<td i18n="transactions-list.witness">Witness</td>
<td>{{ vin.witness.join(' ') }}</td>
</tr>
<tr *ngIf="vin.inner_redeemscript_asm">
<td>P2SH redeem script</td>
<td i18n="transactions-list.p2sh-redeem-script">P2SH redeem script</td>
<td [innerHTML]="vin.inner_redeemscript_asm | asmStyler"></td>
</tr>
<tr *ngIf="vin.inner_witnessscript_asm">
<td>P2WSH witness script</td>
<td i18n="transactions-list.p2wsh-witness-script">P2WSH witness script</td>
<td [innerHTML]="vin.inner_witnessscript_asm | asmStyler"></td>
</tr>
<tr>
<td>nSequence</td>
<td i18n="transactions-list.nsequence">nSequence</td>
<td>{{ formatHex(vin.sequence) }}</td>
</tr>
<tr *ngIf="vin.prevout">
<td>Previous output script</td>
<td i18n="transactions-list.previous-output-script">Previous output script</td>
<td [innerHTML]="vin.prevout.scriptpubkey_asm | asmStyler">{{ vin.prevout.scriptpubkey_type ? ('(' + vin.prevout.scriptpubkey_type + ')') : '' }}"</td>
</tr>
</tbody>
@@ -103,7 +103,7 @@
</ng-template>
<tr *ngIf="tx.vin.length > 10 && tx['@vinLimit']">
<td colspan="3" class="text-center">
<button class="btn btn-sm btn-primary mt-2" (click)="tx['@vinLimit'] = false;">Load all ({{ tx.vin.length - 10 }})</button>
<button class="btn btn-sm btn-primary mt-2" (click)="tx['@vinLimit'] = false;"><span i18n="transactions-list.load-all">Load all</span> ({{ tx.vin.length - 10 }})</button>
</td>
</tr>
</tbody>
@@ -122,7 +122,8 @@
</a>
<ng-template #scriptpubkey_type>
<ng-template [ngIf]="vout.pegout" [ngIfElse]="defaultscriptpubkey_type">
Peg-out to <a [routerLink]="['/address/', vout.pegout.scriptpubkey_address]" title="{{ vout.pegout.scriptpubkey_address }}">
<span i18n="transactions-list.peg-out-to">Peg-out to</span>
<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>
</a>
@@ -160,19 +161,19 @@
<table class="table table-striped table-borderless details-table mb-3">
<tbody>
<tr *ngIf="vout.scriptpubkey_type">
<td>Type</td>
<td i18n="transactions-list.vout.scriptpubkey-type">Type</td>
<td>{{ vout.scriptpubkey_type.toUpperCase() }}</td>
</tr>
<tr>
<td>scriptPubKey (ASM)</td>
<td i18n="transactions-list.scriptpubkey.asm|ScriptPubKey (ASM)">ScriptPubKey (ASM)</td>
<td [innerHTML]="vout.scriptpubkey_asm | asmStyler"></td>
</tr>
<tr>
<td>scriptPubKey (HEX)</td>
<td i18n="transactions-list.scriptpubkey.hex|ScriptPubKey (HEX)">ScriptPubKey (HEX)</td>
<td>{{ vout.scriptpubkey }}</td>
</tr>
<tr *ngIf="vout.scriptpubkey_type == 'op_return'">
<td>OP_RETURN data</td>
<td>OP_RETURN <span i18n="transactions-list.vout.scriptpubkey-type.data">data</span></td>
<td>{{ vout.scriptpubkey_asm | hex2ascii }}</td>
</tr>
</tbody>
@@ -182,7 +183,7 @@
</ng-template>
<tr *ngIf="tx.vout.length > 10 && tx['@voutLimit']">
<td colspan="3" class="text-center">
<button class="btn btn-sm btn-primary mt-2" (click)="tx['@voutLimit'] = false;">Load all ({{ tx.vout.length - 10 }})</button>
<button class="btn btn-sm btn-primary mt-2" (click)="tx['@voutLimit'] = false;"><span i18n="transactions-list.load-all">Load all</span> ({{ tx.vout.length - 10 }})</button>
</td>
</tr>
</tbody>
@@ -192,19 +193,19 @@
<div>
<div class="float-left mt-2-5" *ngIf="!transactionPage && tx.fee">
{{ tx.fee / (tx.weight / 4) | number : '1.1-1' }} sat/vB <span class="d-none d-sm-inline-block"> &ndash; {{ tx.fee | number }} sat (<app-fiat [value]="tx.fee"></app-fiat>)</span>
{{ tx.fee / (tx.weight / 4) | number : '1.1-1' }} <span i18n="shared.sat-vbyte|sat/vB">sat/vB</span> <span class="d-none d-sm-inline-block">&nbsp;&ndash; {{ tx.fee | number }} <span i18n="shared.sat|sat">sat</span> (<app-fiat [value]="tx.fee"></app-fiat>)</span>
</div>
<div class="float-right">
<span *ngIf="showConfirmations && latestBlock$ | async as latestBlock">
<button *ngIf="tx.status.confirmed; else unconfirmedButton" type="button" class="btn btn-sm btn-success mt-2">{{ latestBlock.height - tx.status.block_height + 1 }} confirmation<ng-container *ngIf="latestBlock.height - tx.status.block_height + 1 > 1">s</ng-container></button>
<button *ngIf="tx.status.confirmed; else unconfirmedButton" type="button" class="btn btn-sm btn-success mt-2">{{ latestBlock.height - tx.status.block_height + 1 }} <ng-container *ngIf="latestBlock.height - tx.status.block_height + 1 == 1" i18n="shared.confirmation-count.singular">confirmation</ng-container><ng-container *ngIf="latestBlock.height - tx.status.block_height + 1 > 1" i18n="shared.confirmation-count.plural">confirmations</ng-container></button>
<ng-template #unconfirmedButton>
<button type="button" class="btn btn-sm btn-danger mt-2">Unconfirmed</button>
<button type="button" class="btn btn-sm btn-danger mt-2" i18n="transactions-list.unconfirmed">Unconfirmed</button>
</ng-template>
&nbsp;
</span>
<button type="button" class="btn btn-sm btn-primary mt-2" (click)="switchCurrency()">
<ng-template [ngIf]="network === 'liquid'" [ngIfElse]="defaultAmount">Confidential</ng-template>
<ng-template [ngIf]="network === 'liquid'" [ngIfElse]="defaultAmount" i18n="shared.confidential">Confidential</ng-template>
<ng-template #defaultAmount>
<app-amount [satoshis]="getTotalTxOutput(tx)"></app-amount>
</ng-template>

View File

@@ -0,0 +1,22 @@
<!-- this component is only used for the angular string extractor -->{{counter}}
<div>
<span i18n="@@time-since.just-now">Just now</span>
<span i18n="@@time-since.sec.ago">{{counter}} sec ago</span>
<span i18n="@@time-since.secs.ago">{{counter}} secs ago</span>
<span i18n="@@time-since.second.ago">{{counter}} second ago</span>
<span i18n="@@time-since.seconds.ago">{{counter}} seconds ago</span>
<span i18n="@@time-since.min.ago">{{counter}} min ago</span>
<span i18n="@@time-since.mins.ago">{{counter}} mins ago</span>
<span i18n="@@time-since.minute.ago">{{counter}} minute ago</span>
<span i18n="@@time-since.minutes.ago">{{counter}} minutes ago</span>
<span i18n="@@time-since.hour.ago">{{counter}} hour ago</span>
<span i18n="@@time-since.hours.ago">{{counter}} hours ago</span>
<span i18n="@@time-since.day.ago">{{counter}} day ago</span>
<span i18n="@@time-since.days.ago">{{counter}} days ago</span>
<span i18n="@@time-since.week.ago">{{counter}} week ago</span>
<span i18n="@@time-since.weeks.ago">{{counter}} weeks ago</span>
<span i18n="@@time-since.month.ago">{{counter}} month ago</span>
<span i18n="@@time-since.months.ago">{{counter}} months ago</span>
<span i18n="@@time-since.year.ago">{{counter}} year ago</span>
<span i18n="@@time-since.years.ago">{{counter}} years ago</span>
</div>

View File

@@ -0,0 +1,10 @@
import { Component } from '@angular/core';
@Component({
selector: 'app-translation-strings',
templateUrl: './translation-strings.component.html'
})
export class TranslationStringsComponent {
counter: string;
constructor() { }
}

View File

@@ -1,8 +1,8 @@
<span *ngIf="segwitGains.realizedGains && !segwitGains.potentialBech32Gains; else segwitTwo" class="badge badge-success mr-1" ngbTooltip="This transaction saved {{ segwitGains.realizedGains * 100 | number: '1.0-0' }}% on fees by using native SegWit-Bech32" placement="bottom">SegWit</span>
<span *ngIf="segwitGains.realizedGains && !segwitGains.potentialBech32Gains; else segwitTwo" class="badge badge-success mr-1" ngbTooltip="This transaction saved {{ segwitGains.realizedGains * 100 | number: '1.0-0' }}% on fees by using native SegWit-Bech32" placement="bottom" i18n="tx-features.tag.segwit|SegWit">SegWit</span>
<ng-template #segwitTwo>
<span *ngIf="segwitGains.realizedGains && segwitGains.potentialBech32Gains else potentialP2shGains" class="badge badge-warning mr-1" ngbTooltip="This transaction saved {{ segwitGains.realizedGains * 100 | number: '1.0-0' }}% on fees by using SegWit and could save {{ segwitGains.potentialBech32Gains * 100 | number : '1.0-0' }}% more by fully upgrading to native SegWit-Bech32" placement="bottom">SegWit</span>
<span *ngIf="segwitGains.realizedGains && segwitGains.potentialBech32Gains else potentialP2shGains" class="badge badge-warning mr-1" ngbTooltip="This transaction saved {{ segwitGains.realizedGains * 100 | number: '1.0-0' }}% on fees by using SegWit and could save {{ segwitGains.potentialBech32Gains * 100 | number : '1.0-0' }}% more by fully upgrading to native SegWit-Bech32" placement="bottom" i18n="tx-features.tag.segwit|SegWit">SegWit</span>
<ng-template #potentialP2shGains>
<span *ngIf="segwitGains.potentialP2shGains" class="badge badge-danger mr-1" ngbTooltip="This transaction could save {{ segwitGains.potentialBech32Gains * 100 | number : '1.0-0' }}% on fees by upgrading to native SegWit-Bech32 or {{ segwitGains.potentialP2shGains * 100 | number: '1.0-0' }}% by upgrading to SegWit-P2SH" placement="bottom"><del>SegWit</del></span>
<span *ngIf="segwitGains.potentialP2shGains" class="badge badge-danger mr-1" ngbTooltip="This transaction could save {{ segwitGains.potentialBech32Gains * 100 | number : '1.0-0' }}% on fees by upgrading to native SegWit-Bech32 or {{ segwitGains.potentialP2shGains * 100 | number: '1.0-0' }}% by upgrading to SegWit-P2SH" placement="bottom"><del i18n="tx-features.tag.segwit|SegWit">SegWit</del></span>
</ng-template>
</ng-template>
<span *ngIf="isRbfTransaction" class="badge badge-success" ngbTooltip="This transaction support Replace-By-Fee (RBF) allowing fee bumping" placement="bottom">RBF</span>
<span *ngIf="isRbfTransaction" class="badge badge-success" ngbTooltip="This transaction support Replace-By-Fee (RBF) allowing fee bumping" placement="bottom" i18n="tx-features.tag.rbf|RBF">RBF</span>

View File

@@ -1,3 +1,3 @@
<span *ngIf="feeRating === 1" class="badge badge-success">Optimal</span>
<span *ngIf="feeRating === 2" class="badge badge-warning" title="Only ~{{ medianFeeNeeded }} sat/vB was needed to get into this block">Overpaid {{ overpaidTimes }}x</span>
<span *ngIf="feeRating === 3" class="badge badge-danger" title="Only ~{{ medianFeeNeeded }} sat/vB was needed to get into this block">Overpaid {{ overpaidTimes }}x</span>
<span *ngIf="feeRating === 1" class="badge badge-success" i18n="tx-fee-rating.optimal|TX Fee Rating is Optimal">Optimal</span>
<span *ngIf="feeRating === 2" class="badge badge-warning" title="Only ~{{ medianFeeNeeded }} sat/vB was needed to get into this block" i18n="tx-fee-rating.overpaid.warning|TX Fee Rating is Warning">Overpaid {{ overpaidTimes }}x</span>
<span *ngIf="feeRating === 3" class="badge badge-danger" title="Only ~{{ medianFeeNeeded }} sat/vB was needed to get into this block" i18n="tx-fee-rating.overpaid.danger|TX Fee Rating is Danger">Overpaid {{ overpaidTimes }}x</span>