From 855b20834efe4495eca4232a36bda16ff9ff201c Mon Sep 17 00:00:00 2001 From: Mononaut Date: Sun, 31 Mar 2024 03:45:48 +0000 Subject: [PATCH] Display first seen time on block visualiation tooltips --- backend/src/api/common.ts | 1 + backend/src/api/mempool-blocks.ts | 4 ++- backend/src/mempool.interfaces.ts | 3 ++- .../block-overview-graph.component.html | 1 + .../block-overview-graph.component.ts | 1 + .../block-overview-graph/tx-view.ts | 2 ++ .../block-overview-tooltip.component.html | 8 ++++++ .../block-overview-tooltip.component.ts | 4 +++ .../block-view/block-view.component.html | 1 + .../app/components/block/block.component.html | 5 ++-- .../eight-blocks/eight-blocks.component.html | 1 + .../src/app/components/time/time.component.ts | 25 ++++++++++++++++++- .../src/app/interfaces/node-api.interface.ts | 1 + .../src/app/interfaces/websocket.interface.ts | 3 ++- frontend/src/app/shared/common.utils.ts | 3 ++- 15 files changed, 56 insertions(+), 7 deletions(-) diff --git a/backend/src/api/common.ts b/backend/src/api/common.ts index d8cf0d73f..5053d4da3 100644 --- a/backend/src/api/common.ts +++ b/backend/src/api/common.ts @@ -552,6 +552,7 @@ export class Common { value: tx.vout.reduce((acc, vout) => acc + (vout.value ? vout.value : 0), 0), acc: tx.acceleration || undefined, rate: tx.effectiveFeePerVsize, + time: tx.firstSeen || undefined, }; } diff --git a/backend/src/api/mempool-blocks.ts b/backend/src/api/mempool-blocks.ts index 687fdbef4..3af2a9967 100644 --- a/backend/src/api/mempool-blocks.ts +++ b/backend/src/api/mempool-blocks.ts @@ -598,7 +598,8 @@ class MempoolBlocks { tx.value, Math.round((tx.rate || (tx.fee / tx.vsize)) * 100) / 100, tx.flags, - 1 + tx.time || 0, + 1, ]; } else { return [ @@ -608,6 +609,7 @@ class MempoolBlocks { tx.value, Math.round((tx.rate || (tx.fee / tx.vsize)) * 100) / 100, tx.flags, + tx.time || 0, ]; } } diff --git a/backend/src/mempool.interfaces.ts b/backend/src/mempool.interfaces.ts index 5b31f13a6..a41e3a1ba 100644 --- a/backend/src/mempool.interfaces.ts +++ b/backend/src/mempool.interfaces.ts @@ -200,6 +200,7 @@ export interface TransactionStripped { value: number; acc?: boolean; rate?: number; // effective fee rate + time?: number; } export interface TransactionClassified extends TransactionStripped { @@ -207,7 +208,7 @@ export interface TransactionClassified extends TransactionStripped { } // [txid, fee, vsize, value, rate, flags, acceleration?] -export type TransactionCompressed = [string, number, number, number, number, number, 1?]; +export type TransactionCompressed = [string, number, number, number, number, number, number, 1?]; // [txid, rate, flags, acceleration?] export type MempoolDeltaChange = [string, number, number, (1|0)]; diff --git a/frontend/src/app/components/block-overview-graph/block-overview-graph.component.html b/frontend/src/app/components/block-overview-graph/block-overview-graph.component.html index 702718742..2ef07d12c 100644 --- a/frontend/src/app/components/block-overview-graph/block-overview-graph.component.html +++ b/frontend/src/app/components/block-overview-graph/block-overview-graph.component.html @@ -14,6 +14,7 @@ [blockConversion]="blockConversion" [filterFlags]="activeFilterFlags" [filterMode]="filterMode" + [relativeTime]="relativeTime" >
diff --git a/frontend/src/app/components/block-overview-graph/block-overview-graph.component.ts b/frontend/src/app/components/block-overview-graph/block-overview-graph.component.ts index f7e9d297f..f2a1f1c7c 100644 --- a/frontend/src/app/components/block-overview-graph/block-overview-graph.component.ts +++ b/frontend/src/app/components/block-overview-graph/block-overview-graph.component.ts @@ -46,6 +46,7 @@ export class BlockOverviewGraphComponent implements AfterViewInit, OnDestroy, On @Input() excludeFilters: string[] = []; @Input() filterFlags: bigint | null = null; @Input() filterMode: FilterMode = 'and'; + @Input() relativeTime: number | null; @Input() blockConversion: Price; @Input() overrideColors: ((tx: TxView) => Color) | null = null; @Output() txClickEvent = new EventEmitter<{ tx: TransactionStripped, keyModifier: boolean}>(); diff --git a/frontend/src/app/components/block-overview-graph/tx-view.ts b/frontend/src/app/components/block-overview-graph/tx-view.ts index f9f6eeeb7..fec0e7090 100644 --- a/frontend/src/app/components/block-overview-graph/tx-view.ts +++ b/frontend/src/app/components/block-overview-graph/tx-view.ts @@ -32,6 +32,7 @@ export default class TxView implements TransactionStripped { rate?: number; flags: number; bigintFlags?: bigint | null = 0b00000100_00000000_00000000_00000000n; + time?: number; status?: 'found' | 'missing' | 'sigop' | 'fresh' | 'freshcpfp' | 'added' | 'censored' | 'selected' | 'rbf' | 'accelerated'; context?: 'projected' | 'actual'; scene?: BlockScene; @@ -53,6 +54,7 @@ export default class TxView implements TransactionStripped { this.scene = scene; this.context = tx.context; this.txid = tx.txid; + this.time = tx.time || 0; this.fee = tx.fee; this.vsize = tx.vsize; this.value = tx.value; diff --git a/frontend/src/app/components/block-overview-tooltip/block-overview-tooltip.component.html b/frontend/src/app/components/block-overview-tooltip/block-overview-tooltip.component.html index 1ef0d1686..ec60803a5 100644 --- a/frontend/src/app/components/block-overview-tooltip/block-overview-tooltip.component.html +++ b/frontend/src/app/components/block-overview-tooltip/block-overview-tooltip.component.html @@ -14,6 +14,14 @@ {{ txid | shortenString : 16}} + + First seen + + + + First seen + + Amount diff --git a/frontend/src/app/components/block-overview-tooltip/block-overview-tooltip.component.ts b/frontend/src/app/components/block-overview-tooltip/block-overview-tooltip.component.ts index f163e74fc..c6f656796 100644 --- a/frontend/src/app/components/block-overview-tooltip/block-overview-tooltip.component.ts +++ b/frontend/src/app/components/block-overview-tooltip/block-overview-tooltip.component.ts @@ -3,6 +3,7 @@ import { Position } from '../../components/block-overview-graph/sprite-types.js' import { Price } from '../../services/price.service'; import { TransactionStripped } from '../../interfaces/node-api.interface.js'; import { Filter, FilterMode, TransactionFlags, toFilters } from '../../shared/filters.utils'; +import { Block } from '../../interfaces/electrs.interface.js'; @Component({ selector: 'app-block-overview-tooltip', @@ -11,6 +12,7 @@ import { Filter, FilterMode, TransactionFlags, toFilters } from '../../shared/fi }) export class BlockOverviewTooltipComponent implements OnChanges { @Input() tx: TransactionStripped | void; + @Input() relativeTime?: number; @Input() cursorPosition: Position; @Input() clickable: boolean; @Input() auditEnabled: boolean = false; @@ -19,6 +21,7 @@ export class BlockOverviewTooltipComponent implements OnChanges { @Input() filterMode: FilterMode = 'and'; txid = ''; + time: number = 0; fee = 0; value = 0; vsize = 1; @@ -56,6 +59,7 @@ export class BlockOverviewTooltipComponent implements OnChanges { if (this.tx && (changes.tx || changes.filterFlags || changes.filterMode)) { this.txid = this.tx.txid || ''; + this.time = this.tx.time || 0; this.fee = this.tx.fee || 0; this.value = this.tx.value || 0; this.vsize = this.tx.vsize || 1; diff --git a/frontend/src/app/components/block-view/block-view.component.html b/frontend/src/app/components/block-view/block-view.component.html index 9a2ddf373..f0dc94e2c 100644 --- a/frontend/src/app/components/block-view/block-view.component.html +++ b/frontend/src/app/components/block-view/block-view.component.html @@ -8,6 +8,7 @@ [orientation]="'top'" [flip]="false" [disableSpinner]="true" + [relativeTime]="block?.timestamp" (txClickEvent)="onTxClick($event)" >
diff --git a/frontend/src/app/components/block/block.component.html b/frontend/src/app/components/block/block.component.html index 0c0655c01..279830ba5 100644 --- a/frontend/src/app/components/block/block.component.html +++ b/frontend/src/app/components/block/block.component.html @@ -117,6 +117,7 @@ [blockConversion]="blockConversion" [showFilters]="true" [excludeFilters]="['replacement']" + [relativeTime]="block?.timestamp" (txClickEvent)="onTxClick($event)" > @@ -232,7 +233,7 @@ + [showFilters]="true" [excludeFilters]="['replacement']" [relativeTime]="block?.timestamp"> @@ -247,7 +248,7 @@ + [showFilters]="true" [excludeFilters]="['replacement']" [relativeTime]="block?.timestamp"> diff --git a/frontend/src/app/components/eight-blocks/eight-blocks.component.html b/frontend/src/app/components/eight-blocks/eight-blocks.component.html index 59390c953..414a693d3 100644 --- a/frontend/src/app/components/eight-blocks/eight-blocks.component.html +++ b/frontend/src/app/components/eight-blocks/eight-blocks.component.html @@ -12,6 +12,7 @@ [animationDuration]="animationDuration" [animationOffset]="animationOffset" [disableSpinner]="true" + [relativeTime]="blockInfo[i]?.timestamp" (txClickEvent)="onTxClick($event)" >
diff --git a/frontend/src/app/components/time/time.component.ts b/frontend/src/app/components/time/time.component.ts index 679604ff5..61cc799dc 100644 --- a/frontend/src/app/components/time/time.component.ts +++ b/frontend/src/app/components/time/time.component.ts @@ -23,7 +23,7 @@ export class TimeComponent implements OnInit, OnChanges, OnDestroy { @Input() time: number; @Input() dateString: number; - @Input() kind: 'plain' | 'since' | 'until' | 'span' = 'plain'; + @Input() kind: 'plain' | 'since' | 'until' | 'span' | 'before' = 'plain'; @Input() fastRender = false; @Input() fixedRender = false; @Input() relative = false; @@ -206,6 +206,29 @@ export class TimeComponent implements OnInit, OnChanges, OnDestroy { } } break; + case 'before': + if (number === 1) { + switch (unit) { // singular (1 day) + case 'year': return $localize`:@@time-span:${dateStrings.i18nYear}:DATE: before`; break; + case 'month': return $localize`:@@time-span:${dateStrings.i18nMonth}:DATE: before`; break; + case 'week': return $localize`:@@time-span:${dateStrings.i18nWeek}:DATE: before`; break; + case 'day': return $localize`:@@time-span:${dateStrings.i18nDay}:DATE: before`; break; + case 'hour': return $localize`:@@time-span:${dateStrings.i18nHour}:DATE: before`; break; + case 'minute': return $localize`:@@time-span:${dateStrings.i18nMinute}:DATE: before`; break; + case 'second': return $localize`:@@time-span:${dateStrings.i18nSecond}:DATE: before`; break; + } + } else { + switch (unit) { // plural (2 days) + case 'year': return $localize`:@@time-span:${dateStrings.i18nYears}:DATE: before`; break; + case 'month': return $localize`:@@time-span:${dateStrings.i18nMonths}:DATE: before`; break; + case 'week': return $localize`:@@time-span:${dateStrings.i18nWeeks}:DATE: before`; break; + case 'day': return $localize`:@@time-span:${dateStrings.i18nDays}:DATE: before`; break; + case 'hour': return $localize`:@@time-span:${dateStrings.i18nHours}:DATE: before`; break; + case 'minute': return $localize`:@@time-span:${dateStrings.i18nMinutes}:DATE: before`; break; + case 'second': return $localize`:@@time-span:${dateStrings.i18nSeconds}:DATE: before`; break; + } + } + break; default: if (number === 1) { switch (unit) { // singular (1 day) diff --git a/frontend/src/app/interfaces/node-api.interface.ts b/frontend/src/app/interfaces/node-api.interface.ts index f8057fda5..9680d2069 100644 --- a/frontend/src/app/interfaces/node-api.interface.ts +++ b/frontend/src/app/interfaces/node-api.interface.ts @@ -230,6 +230,7 @@ export interface TransactionStripped { rate?: number; // effective fee rate acc?: boolean; flags?: number | null; + time?: number; status?: 'found' | 'missing' | 'sigop' | 'fresh' | 'freshcpfp' | 'added' | 'censored' | 'selected' | 'rbf' | 'accelerated'; context?: 'projected' | 'actual'; } diff --git a/frontend/src/app/interfaces/websocket.interface.ts b/frontend/src/app/interfaces/websocket.interface.ts index 9553fef02..a36126051 100644 --- a/frontend/src/app/interfaces/websocket.interface.ts +++ b/frontend/src/app/interfaces/websocket.interface.ts @@ -100,12 +100,13 @@ export interface TransactionStripped { acc?: boolean; // is accelerated? rate?: number; // effective fee rate flags?: number; + time?: number; status?: 'found' | 'missing' | 'sigop' | 'fresh' | 'freshcpfp' | 'added' | 'censored' | 'selected' | 'rbf' | 'accelerated'; context?: 'projected' | 'actual'; } // [txid, fee, vsize, value, rate, flags, acceleration?] -export type TransactionCompressed = [string, number, number, number, number, number, 1?]; +export type TransactionCompressed = [string, number, number, number, number, number, number, 1?]; // [txid, rate, flags, acceleration?] export type MempoolDeltaChange = [string, number, number, (1|0)]; diff --git a/frontend/src/app/shared/common.utils.ts b/frontend/src/app/shared/common.utils.ts index 18a330fab..482d9567f 100644 --- a/frontend/src/app/shared/common.utils.ts +++ b/frontend/src/app/shared/common.utils.ts @@ -164,7 +164,8 @@ export function uncompressTx(tx: TransactionCompressed): TransactionStripped { value: tx[3], rate: tx[4], flags: tx[5], - acc: !!tx[6], + time: tx[6], + acc: !!tx[7], }; }