diff --git a/frontend/src/app/app.module.ts b/frontend/src/app/app.module.ts
index 60b54361b..6a29d76ef 100644
--- a/frontend/src/app/app.module.ts
+++ b/frontend/src/app/app.module.ts
@@ -42,6 +42,7 @@ import { AudioService } from './services/audio.service';
import { FiatComponent } from './fiat/fiat.component';
import { MempoolBlockComponent } from './components/mempool-block/mempool-block.component';
import { FeeDistributionGraphComponent } from './components/fee-distribution-graph/fee-distribution-graph.component';
+import { TimespanComponent } from './components/timespan/timespan.component';
@NgModule({
declarations: [
@@ -66,6 +67,7 @@ import { FeeDistributionGraphComponent } from './components/fee-distribution-gra
SearchFormComponent,
LatestBlocksComponent,
TimeSinceComponent,
+ TimespanComponent,
AddressLabelsComponent,
MempoolBlocksComponent,
QrcodeComponent,
diff --git a/frontend/src/app/components/block/block.component.html b/frontend/src/app/components/block/block.component.html
index 0b4449bbf..467bd9672 100644
--- a/frontend/src/app/components/block/block.component.html
+++ b/frontend/src/app/components/block/block.component.html
@@ -18,7 +18,7 @@
{{ block.timestamp * 1000 | date:'yyyy-MM-dd HH:mm' }}
|
diff --git a/frontend/src/app/components/block/block.component.scss b/frontend/src/app/components/block/block.component.scss
index 589490939..859b81409 100644
--- a/frontend/src/app/components/block/block.component.scss
+++ b/frontend/src/app/components/block/block.component.scss
@@ -1,15 +1,10 @@
.title-block {
color: #FFF;
- padding-left: 10px;
padding-top: 20px;
padding-bottom: 3px;
border-top: 5px solid #FFF;
}
-.title-block > h1 {
- margin: 0;
-}
-
.mobile-width {
width: 130px;
}
diff --git a/frontend/src/app/components/mempool-block/mempool-block.component.scss b/frontend/src/app/components/mempool-block/mempool-block.component.scss
index 4893d9ccc..e9d02f6fc 100644
--- a/frontend/src/app/components/mempool-block/mempool-block.component.scss
+++ b/frontend/src/app/components/mempool-block/mempool-block.component.scss
@@ -14,12 +14,7 @@
.title-block {
color: #FFF;
- padding-left: 10px;
padding-top: 20px;
padding-bottom: 3px;
border-top: 5px solid #FFF;
-}
-
-.title-block > h1 {
- margin: 0;
}
\ No newline at end of file
diff --git a/frontend/src/app/components/timespan/timespan.component.ts b/frontend/src/app/components/timespan/timespan.component.ts
new file mode 100644
index 000000000..6433aa250
--- /dev/null
+++ b/frontend/src/app/components/timespan/timespan.component.ts
@@ -0,0 +1,44 @@
+import { Component, ChangeDetectionStrategy, Input, OnChanges } from '@angular/core';
+
+@Component({
+ selector: 'app-timespan',
+ template: `{{ text }}`,
+ changeDetection: ChangeDetectionStrategy.OnPush
+})
+export class TimespanComponent implements OnChanges {
+ @Input() time: number;
+ text: string;
+
+ constructor() { }
+
+ ngOnChanges() {
+ const seconds = this.time;
+ if (seconds < 60) {
+ return '< 1 minute';
+ }
+ const intervals = {
+ year: 31536000,
+ month: 2592000,
+ week: 604800,
+ day: 86400,
+ hour: 3600,
+ minute: 60,
+ second: 1
+ };
+ let counter;
+ for (const i in intervals) {
+ if (intervals.hasOwnProperty(i)) {
+ counter = Math.floor(seconds / intervals[i]);
+ if (counter > 0) {
+ if (counter === 1) {
+ this.text = counter + ' ' + i; // singular (1 day ago)
+ break;
+ } else {
+ this.text = counter + ' ' + i + 's'; // plural (2 days ago)
+ break;
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/frontend/src/app/components/transaction/transaction.component.html b/frontend/src/app/components/transaction/transaction.component.html
index 0c0b3912e..687ff3dfc 100644
--- a/frontend/src/app/components/transaction/transaction.component.html
+++ b/frontend/src/app/components/transaction/transaction.component.html
@@ -17,44 +17,58 @@
+
-
-
-
-
-
-
- Included in block |
-
- {{ tx.status.block_height }}
- at {{ tx.status.block_time * 1000 | date:'yyyy-MM-dd HH:mm' }}
-
- |
-
-
-
- Fee per vByte |
-
- {{ tx.fee / (tx.weight / 4) | number : '1.2-2' }} sats/vB
-
- Optimal
- Overpaid {{ overpaidTimes }}x
- Overpaid {{ overpaidTimes }}x
- |
-
-
- Fee |
- {{ tx.fee | number }} sats () |
-
-
-
-
+
+
+
+
+
+
+
+ Fee |
+ {{ tx.fee | number }} sats () |
+
+
+ Fee per vByte |
+
+ {{ tx.fee / (tx.weight / 4) | number : '1.1-1' }} sats/vB
+
+ Optimal
+ Overpaid {{ overpaidTimes }}x
+ Overpaid {{ overpaidTimes }}x
+ |
+
+
+
+
+
+
+
@@ -64,31 +78,51 @@
-
-
-
-
-
- |
- |
-
-
+
+
+
+
+
+
+ |
+ |
+
+
+
+ First seen |
+ ago |
+
+
+
- First seen |
- ago |
+ ETA |
+
+
+
+
+
+ ~{{ 10 * txInBlockIndex + 10 }} minutes ({{ txInBlockIndex + 1 }} block{{ txInBlockIndex > 0 ? 's' : '' }})
+
+ |
-
-
-
- Fees |
- {{ tx.fee | number }} sats () |
-
-
- Fees per vByte |
- {{ tx.fee / (tx.weight / 4) | number : '1.2-2' }} sats/vB |
-
-
-
+
+
+
+
+
+
+
+ Fee |
+ {{ tx.fee | number }} sats () |
+
+
+ Fee per vByte |
+ {{ tx.fee / (tx.weight / 4) | number : '1.1-1' }} sats/vB |
+
+
+
+
+
@@ -119,14 +153,36 @@
diff --git a/frontend/src/app/components/transaction/transaction.component.scss b/frontend/src/app/components/transaction/transaction.component.scss
index 66730e887..33c4353d9 100644
--- a/frontend/src/app/components/transaction/transaction.component.scss
+++ b/frontend/src/app/components/transaction/transaction.component.scss
@@ -4,19 +4,22 @@
.title-block {
color: #FFF;
- padding-left: 10px;
padding-top: 20px;
- padding-bottom: 3px;
border-top: 5px solid #FFF;
}
-.title-block > h1 {
- margin: 0;
-}
-
-
@media (max-width: 767.98px) {
.mobile-bottomcol {
margin-top: 15px;
}
}
+
+.td-width {
+ width: 175px;
+}
+
+@media (max-width: 767.98px) {
+ .td-width {
+ width: 150px;
+ }
+}
diff --git a/frontend/src/app/components/transaction/transaction.component.ts b/frontend/src/app/components/transaction/transaction.component.ts
index 6946b95eb..549d024bd 100644
--- a/frontend/src/app/components/transaction/transaction.component.ts
+++ b/frontend/src/app/components/transaction/transaction.component.ts
@@ -1,13 +1,14 @@
import { Component, OnInit, OnDestroy } from '@angular/core';
import { ElectrsApiService } from '../../services/electrs-api.service';
import { ActivatedRoute, ParamMap } from '@angular/router';
-import { switchMap, filter } from 'rxjs/operators';
+import { switchMap, filter, take } from 'rxjs/operators';
import { Transaction, Block } from '../../interfaces/electrs.interface';
import { of } from 'rxjs';
import { StateService } from '../../services/state.service';
import { WebsocketService } from '../../services/websocket.service';
import { AudioService } from 'src/app/services/audio.service';
import { ApiService } from 'src/app/services/api.service';
+import { MempoolBlock } from 'src/app/interfaces/websocket.interface';
@Component({
selector: 'app-transaction',
@@ -20,13 +21,13 @@ export class TransactionComponent implements OnInit, OnDestroy {
feeRating: number;
overpaidTimes: number;
medianFeeNeeded: number;
+ txInBlockIndex: number;
isLoadingTx = true;
error: any = undefined;
latestBlock: Block;
transactionTime = -1;
rightPosition = 0;
- blockDepth = 0;
constructor(
private route: ActivatedRoute,
@@ -56,6 +57,7 @@ export class TransactionComponent implements OnInit, OnDestroy {
.subscribe((tx: Transaction) => {
this.tx = tx;
this.isLoadingTx = false;
+ this.setMempoolBlocksSubscription();
if (!tx.status.confirmed) {
this.websocketService.startTrackTransaction(tx.txid);
@@ -91,6 +93,25 @@ export class TransactionComponent implements OnInit, OnDestroy {
});
}
+ setMempoolBlocksSubscription() {
+ this.stateService.mempoolBlocks$
+ .subscribe((mempoolBlocks) => {
+ if (!this.tx) {
+ return;
+ }
+
+ const txFeePerVSize = this.tx.fee / (this.tx.weight / 4);
+
+ for (const block of mempoolBlocks) {
+ for (let i = 0; i < block.feeRange.length - 1; i++) {
+ if (txFeePerVSize < block.feeRange[i + 1] && txFeePerVSize >= block.feeRange[i]) {
+ this.txInBlockIndex = mempoolBlocks.indexOf(block);
+ }
+ }
+ }
+ });
+ }
+
getTransactionTime() {
this.apiService.getTransactionTimes$([this.tx.txid])
.subscribe((transactionTimes) => {
@@ -100,7 +121,10 @@ export class TransactionComponent implements OnInit, OnDestroy {
findBlockAndSetFeeRating() {
this.stateService.blocks$
- .pipe(filter((block) => block.height === this.tx.status.block_height))
+ .pipe(
+ filter((block) => block.height === this.tx.status.block_height),
+ take(1)
+ )
.subscribe((block) => {
const feePervByte = this.tx.fee / (this.tx.weight / 4);
this.medianFeeNeeded = block.feeRange[Math.round(block.feeRange.length * 0.5)];
diff --git a/frontend/src/app/components/transactions-list/transactions-list.component.html b/frontend/src/app/components/transactions-list/transactions-list.component.html
index aa45cb4f9..cce721bda 100644
--- a/frontend/src/app/components/transactions-list/transactions-list.component.html
+++ b/frontend/src/app/components/transactions-list/transactions-list.component.html
@@ -7,7 +7,7 @@
{{ tx.status.block_time * 1000 | date:'yyyy-MM-dd HH:mm' }}
- ago
+ ago
diff --git a/frontend/src/styles.scss b/frontend/src/styles.scss
index ee98672b3..bbe983877 100644
--- a/frontend/src/styles.scss
+++ b/frontend/src/styles.scss
@@ -324,6 +324,10 @@ tr {
white-space: nowrap;
}
+h1, h2, h3 {
+ margin-bottom: 15px;
+}
+
@media (min-width: 992px) {
.lg-inline {
display: inline-block;