From 1710ae0503032319c82facb4e56cdfcb0914de0e Mon Sep 17 00:00:00 2001 From: natsoni Date: Fri, 5 Jul 2024 16:30:12 +0900 Subject: [PATCH 1/7] Improve step colors in timeline --- .../acceleration-timeline.component.html | 8 ++++---- .../acceleration-timeline.component.scss | 12 ++++++++++++ 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/frontend/src/app/components/acceleration-timeline/acceleration-timeline.component.html b/frontend/src/app/components/acceleration-timeline/acceleration-timeline.component.html index 6d3591fb9..41722aaca 100644 --- a/frontend/src/app/components/acceleration-timeline/acceleration-timeline.component.html +++ b/frontend/src/app/components/acceleration-timeline/acceleration-timeline.component.html @@ -39,10 +39,10 @@
- +
-
Accelerated
+
Accelerated
@@ -52,10 +52,10 @@
- +
-
Mined
+
Mined
@if (tx.status.block_time) { diff --git a/frontend/src/app/components/acceleration-timeline/acceleration-timeline.component.scss b/frontend/src/app/components/acceleration-timeline/acceleration-timeline.component.scss index d0338ec84..73ca2b270 100644 --- a/frontend/src/app/components/acceleration-timeline/acceleration-timeline.component.scss +++ b/frontend/src/app/components/acceleration-timeline/acceleration-timeline.component.scss @@ -145,6 +145,12 @@ transition: background-color 300ms, border 300ms; } + &.waiting { + .shape { + background: var(--grey); + } + } + &.sent-selected { .shape { background: var(--primary); @@ -166,6 +172,12 @@ .status { margin-top: -64px; + + .badge.badge-waiting { + opacity: 0.5; + background-color: var(--grey); + color: white; + } .badge.badge-accelerated { background-color: var(--tertiary); From fb8bd4b194ac7adab873363b1585182a3863da83 Mon Sep 17 00:00:00 2001 From: natsoni Date: Fri, 5 Jul 2024 16:35:00 +0900 Subject: [PATCH 2/7] Add i18n to acceleration timeline --- .../acceleration-timeline.component.html | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/frontend/src/app/components/acceleration-timeline/acceleration-timeline.component.html b/frontend/src/app/components/acceleration-timeline/acceleration-timeline.component.html index 41722aaca..c7efc9b93 100644 --- a/frontend/src/app/components/acceleration-timeline/acceleration-timeline.component.html +++ b/frontend/src/app/components/acceleration-timeline/acceleration-timeline.component.html @@ -28,7 +28,7 @@
-
Sent
+
Sent
@@ -42,7 +42,7 @@
-
Accelerated
+
Accelerated
@@ -55,7 +55,7 @@
-
Mined
+
Mined
@if (tx.status.block_time) { From 7c08a104ce110e3f4abdfc497b450df7e25e4bfb Mon Sep 17 00:00:00 2001 From: natsoni Date: Fri, 5 Jul 2024 16:48:50 +0900 Subject: [PATCH 3/7] remove rtl for now --- .../acceleration-timeline.component.ts | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/frontend/src/app/components/acceleration-timeline/acceleration-timeline.component.ts b/frontend/src/app/components/acceleration-timeline/acceleration-timeline.component.ts index d40215c1d..0bafdbfa7 100644 --- a/frontend/src/app/components/acceleration-timeline/acceleration-timeline.component.ts +++ b/frontend/src/app/components/acceleration-timeline/acceleration-timeline.component.ts @@ -1,4 +1,4 @@ -import { Component, Input, OnInit, OnChanges, Inject, LOCALE_ID } from '@angular/core'; +import { Component, Input, OnInit, OnChanges } from '@angular/core'; import { ETA } from '../../services/eta.service'; import { Transaction } from '../../interfaces/electrs.interface'; @@ -13,15 +13,8 @@ export class AccelerationTimelineComponent implements OnInit, OnChanges { @Input() eta: ETA; acceleratedAt: number; - dir: 'rtl' | 'ltr' = 'ltr'; - constructor( - @Inject(LOCALE_ID) private locale: string, - ) { - if (this.locale.startsWith('ar') || this.locale.startsWith('fa') || this.locale.startsWith('he')) { - this.dir = 'rtl'; - } - } + constructor() {} ngOnInit(): void { this.acceleratedAt = this.tx.acceleratedAt ?? new Date().getTime() / 1000; From a0992f6091db6deb0817bee3d3a794848d418f42 Mon Sep 17 00:00:00 2001 From: natsoni Date: Fri, 5 Jul 2024 18:12:28 +0900 Subject: [PATCH 4/7] More accel timeline polish --- .../acceleration-timeline.component.html | 24 +++++++++---------- .../acceleration-timeline.component.ts | 1 + .../transaction/transaction.component.html | 4 ++-- 3 files changed, 15 insertions(+), 14 deletions(-) diff --git a/frontend/src/app/components/acceleration-timeline/acceleration-timeline.component.html b/frontend/src/app/components/acceleration-timeline/acceleration-timeline.component.html index c7efc9b93..cd9d1f2e2 100644 --- a/frontend/src/app/components/acceleration-timeline/acceleration-timeline.component.html +++ b/frontend/src/app/components/acceleration-timeline/acceleration-timeline.component.html @@ -12,9 +12,9 @@
@if (eta) { - ~ + ~ } @else if (tx.status.block_time) { - + }
@@ -24,8 +24,8 @@
-
- +
+
Sent
@@ -34,25 +34,25 @@
-
+
-
-
- +
+
+
-
Accelerated
+
Accelerated
-
+
-
- +
+
Mined
diff --git a/frontend/src/app/components/acceleration-timeline/acceleration-timeline.component.ts b/frontend/src/app/components/acceleration-timeline/acceleration-timeline.component.ts index 0bafdbfa7..6f775e7a8 100644 --- a/frontend/src/app/components/acceleration-timeline/acceleration-timeline.component.ts +++ b/frontend/src/app/components/acceleration-timeline/acceleration-timeline.component.ts @@ -11,6 +11,7 @@ export class AccelerationTimelineComponent implements OnInit, OnChanges { @Input() transactionTime: number; @Input() tx: Transaction; @Input() eta: ETA; + @Input() isAcceleration: boolean; acceleratedAt: number; diff --git a/frontend/src/app/components/transaction/transaction.component.html b/frontend/src/app/components/transaction/transaction.component.html index 0f9a4d9c4..a0e841303 100644 --- a/frontend/src/app/components/transaction/transaction.component.html +++ b/frontend/src/app/components/transaction/transaction.component.html @@ -152,12 +152,12 @@
- +

Acceleration Timeline

- +
From bf51e3e1c97b90e6720098b2716d3a3271cfe52b Mon Sep 17 00:00:00 2001 From: natsoni Date: Sun, 7 Jul 2024 14:41:44 +0900 Subject: [PATCH 5/7] Show unaccelerated ETA in acceleration timeline --- .../acceleration-timeline.component.html | 264 ++++++++++++++---- .../acceleration-timeline.component.scss | 66 ++++- .../acceleration-timeline.component.ts | 18 +- .../transaction/transaction.component.html | 18 +- .../transaction/transaction.component.ts | 16 ++ frontend/src/app/services/eta.service.ts | 54 ++++ 6 files changed, 357 insertions(+), 79 deletions(-) diff --git a/frontend/src/app/components/acceleration-timeline/acceleration-timeline.component.html b/frontend/src/app/components/acceleration-timeline/acceleration-timeline.component.html index cd9d1f2e2..d5f1c0915 100644 --- a/frontend/src/app/components/acceleration-timeline/acceleration-timeline.component.html +++ b/frontend/src/app/components/acceleration-timeline/acceleration-timeline.component.html @@ -1,3 +1,4 @@ +@if (tx.status.confirmed) {
@@ -11,68 +12,225 @@
- @if (eta) { - ~ - } @else if (tx.status.block_time) { - }
- -
-
-
-
- -
-
-
Sent
-
- +
+
+
+ +
+
+
First seen
+
+ +
-
-
-
-
-
-
-
- -
-
-
Accelerated
-
- +
+
-
-
-
-
-
-
- -
-
-
Mined
-
- @if (tx.status.block_time) { +
+
+
+ +
+
+
Accelerated
+
+ +
+
+
+
+
+
+
+ +
+
+
Mined
+
- } @else if (eta) { - - } +
+
+
+
+
+
+} @else if (acceleratedETA) { +
+
+
+
+
+
+
+ +
+
+
+
+
+ ~ +
+
+
+
+
+
+
+ +
+
+
First seen
+
+ +
+
+
+
+
+
+
+
+ +
+
+
Accelerated
+
+ Now +
+
+
+
+
+
+
+
+ +
+
+
Mined
+
+
+
+
+
+
+
+
+
+
+ ~ ({{ accelerateRatio }}x slower) +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
- - -
-
- - -
-
- -
\ No newline at end of file +
+} @else if (standardETA) { +
+
+
+
+
+
+
+ +
+
+
+
+
+ @if (eta) { + ~ + } +
+
+
+
+
+
+
+ +
+
+
First seen
+
+ +
+
+
+
+
+
+
+
+ +
+
+
Accelerated
+
+ +
+
+
+
+
+
+
+
+ +
+
+
Mined
+
+
+
+
+
+
+
+
+
+
+ ~ ({{ accelerateRatio }}x slower) +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+} \ No newline at end of file diff --git a/frontend/src/app/components/acceleration-timeline/acceleration-timeline.component.scss b/frontend/src/app/components/acceleration-timeline/acceleration-timeline.component.scss index 73ca2b270..54061f54e 100644 --- a/frontend/src/app/components/acceleration-timeline/acceleration-timeline.component.scss +++ b/frontend/src/app/components/acceleration-timeline/acceleration-timeline.component.scss @@ -84,10 +84,6 @@ background: var(--primary); border-radius: 5px; - &.loading { - animation: standardPulse 1s infinite; - } - &.left { right: 50%; } @@ -118,6 +114,26 @@ left: 50%; } } + + .corner-up { + position: absolute; + left: -5px; + left: 48.5%; + height: 86px; + border-left: solid 10px var(--primary); + border-bottom: solid 10px var(--primary); + border-bottom-right-radius: 10px; + // horrible css: + @media (max-width: 1030px) { + left: 48%; + } + @media (max-width: 850px) { + left: 47%; + } + @media (max-width: 700px) { + left: 46%; + } + } } @@ -142,6 +158,9 @@ height: 100%; border-radius: 50%; background: white; + &.accelerating { + animation: acceleratePulse 1s infinite; + } transition: background-color 300ms, border 300ms; } @@ -151,12 +170,6 @@ } } - &.sent-selected { - .shape { - background: var(--primary); - } - } - &.accelerated-selected { .shape { background: var(--tertiary); @@ -190,6 +203,30 @@ font-size: 12px; line-height: 16px; white-space: nowrap; + + &.sm-margin { + @media (max-width: 650px) { + margin-left: 20px; + } + } + } + } + + .connector { + position: relative; + height: 10px; + + .corner-down { + position: absolute; + @media (max-width: 650px) { + width: 223px; + } + width: 290px; + height: 90px; + bottom: 50%; + border-left: solid 10px var(--primary); + border-bottom: solid 10px var(--primary); + border-bottom-left-radius: 10px; } } } @@ -201,9 +238,8 @@ 100% { background-color: var(--tertiary) } } -@keyframes standardPulse { - 0% { background-color: var(--primary) } - 50% { background-color: var(--secondary) } - 100% { background-color: var(--primary) } - +@keyframes textPulse { + 0% { color: var(--tertiary) } + 50% { color: var(--mainnet-alt) } + 100% { color: var(--tertiary) } } \ No newline at end of file diff --git a/frontend/src/app/components/acceleration-timeline/acceleration-timeline.component.ts b/frontend/src/app/components/acceleration-timeline/acceleration-timeline.component.ts index 6f775e7a8..ba687e093 100644 --- a/frontend/src/app/components/acceleration-timeline/acceleration-timeline.component.ts +++ b/frontend/src/app/components/acceleration-timeline/acceleration-timeline.component.ts @@ -11,9 +11,14 @@ export class AccelerationTimelineComponent implements OnInit, OnChanges { @Input() transactionTime: number; @Input() tx: Transaction; @Input() eta: ETA; - @Input() isAcceleration: boolean; + // A mined transaction has standard ETA and accelerated ETA undefined + // A transaction in mempool has either standardETA defined (if accelerated) or acceleratedETA defined (if not accelerated yet) + @Input() standardETA: number; + @Input() acceleratedETA: number; acceleratedAt: number; + now: number; + accelerateRatio: number; constructor() {} @@ -22,6 +27,15 @@ export class AccelerationTimelineComponent implements OnInit, OnChanges { } ngOnChanges(changes): void { + this.now = Math.floor(new Date().getTime() / 1000); + if (changes?.eta?.currentValue || changes?.standardETA?.currentValue || changes?.acceleratedETA?.currentValue) { + if (changes?.eta?.currentValue) { + if (changes?.acceleratedETA?.currentValue) { + this.accelerateRatio = Math.floor((Math.floor(changes.eta.currentValue.time / 1000) - this.now) / (Math.floor(changes.acceleratedETA.currentValue / 1000) - this.now)); + } else if (changes?.standardETA?.currentValue) { + this.accelerateRatio = Math.floor((Math.floor(changes.standardETA.currentValue / 1000) - this.now) / (Math.floor(changes.eta.currentValue.time / 1000) - this.now)); + } + } + } } - } diff --git a/frontend/src/app/components/transaction/transaction.component.html b/frontend/src/app/components/transaction/transaction.component.html index a0e841303..9528d2e66 100644 --- a/frontend/src/app/components/transaction/transaction.component.html +++ b/frontend/src/app/components/transaction/transaction.component.html @@ -152,15 +152,6 @@
- -
-

Acceleration Timeline

-
-
- -
-
-

RBF Timeline

@@ -170,6 +161,15 @@
+ +
+

Acceleration Timeline

+
+
+ +
+
+

Flow

diff --git a/frontend/src/app/components/transaction/transaction.component.ts b/frontend/src/app/components/transaction/transaction.component.ts index d2cc0789d..d1d3fe5d7 100644 --- a/frontend/src/app/components/transaction/transaction.component.ts +++ b/frontend/src/app/components/transaction/transaction.component.ts @@ -112,6 +112,7 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy { txChanged$ = new BehaviorSubject(false); // triggered whenever this.tx changes (long term, we should refactor to make this.tx an observable itself) isAccelerated$ = new BehaviorSubject(false); // refactor this to make isAccelerated an observable itself ETA$: Observable; + standardETA$: Observable; isCached: boolean = false; now = Date.now(); da$: Observable; @@ -809,6 +810,21 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy { this.miningStats = stats; this.isAccelerated$.next(this.isAcceleration); // hack to trigger recalculation of ETA without adding another source observable }); + if (!this.tx.status?.confirmed) { + this.standardETA$ = combineLatest([ + this.stateService.mempoolBlocks$.pipe(startWith(null)), + this.stateService.difficultyAdjustment$.pipe(startWith(null)), + ]).pipe( + map(([mempoolBlocks, da]) => { + return this.etaService.calculateUnacceleratedETA( + this.tx, + mempoolBlocks, + da, + this.cpfpInfo, + ); + }) + ) + } } this.isAccelerated$.next(this.isAcceleration); } diff --git a/frontend/src/app/services/eta.service.ts b/frontend/src/app/services/eta.service.ts index cc1436e4c..f632c9adb 100644 --- a/frontend/src/app/services/eta.service.ts +++ b/frontend/src/app/services/eta.service.ts @@ -225,4 +225,58 @@ export class EtaService { blocks: Math.ceil(eta / da.adjustedTimeAvg), }; } + + calculateUnacceleratedETA( + tx: Transaction, + mempoolBlocks: MempoolBlock[], + da: DifficultyAdjustment, + cpfpInfo: CpfpInfo | null, + ): ETA | null { + if (!tx || !mempoolBlocks) { + return null; + } + const now = Date.now(); + + // use known projected position, or fall back to feerate-based estimate + const mempoolPosition = this.mempoolPositionFromFees(this.getFeeRateFromCpfpInfo(tx, cpfpInfo), mempoolBlocks); + if (!mempoolPosition) { + return null; + } + + // difficulty adjustment estimate is required to know avg block time on non-Liquid networks + if (!da) { + return null; + } + + const blocks = mempoolPosition.block + 1; + const wait = da.adjustedTimeAvg * (mempoolPosition.block + 1); + return { + now, + time: wait + now + da.timeOffset, + wait, + blocks, + }; + } + + + getFeeRateFromCpfpInfo(tx: Transaction, cpfpInfo: CpfpInfo | null): number { + if (!cpfpInfo) { + return tx.fee / (tx.weight / 4); + } + + const relatives = [...(cpfpInfo.ancestors || []), ...(cpfpInfo.descendants || [])]; + if (cpfpInfo.bestDescendant && !cpfpInfo.descendants?.length) { + relatives.push(cpfpInfo.bestDescendant); + } + + if (!!relatives.length) { + const totalWeight = tx.weight + relatives.reduce((prev, val) => prev + val.weight, 0); + const totalFees = tx.fee + relatives.reduce((prev, val) => prev + val.fee, 0); + + return totalFees / (totalWeight / 4); + } + + return tx.fee / (tx.weight / 4); + + } } From cd56128bb6f0868a324969b2165686c4a2d5875c Mon Sep 17 00:00:00 2001 From: natsoni Date: Mon, 8 Jul 2024 22:01:36 +0900 Subject: [PATCH 6/7] Implement feedbacks on acceleration timeline --- .../acceleration-timeline.component.html | 189 +++++------------- .../acceleration-timeline.component.scss | 73 +++---- 2 files changed, 77 insertions(+), 185 deletions(-) diff --git a/frontend/src/app/components/acceleration-timeline/acceleration-timeline.component.html b/frontend/src/app/components/acceleration-timeline/acceleration-timeline.component.html index d5f1c0915..2b826f2a1 100644 --- a/frontend/src/app/components/acceleration-timeline/acceleration-timeline.component.html +++ b/frontend/src/app/components/acceleration-timeline/acceleration-timeline.component.html @@ -20,9 +20,9 @@
- +
-
+
First seen
@@ -34,9 +34,9 @@
- +
-
+
Accelerated
@@ -47,10 +47,9 @@
- +
-
-
Mined
+
@@ -60,94 +59,41 @@
} @else if (acceleratedETA) { -
-
-
-
-
-
-
- -
-
-
-
-
- ~ -
-
-
-
-
-
-
- -
-
-
First seen
-
- -
-
-
-
-
-
-
-
- -
-
-
Accelerated
-
- Now -
-
-
-
-
-
-
-
- -
-
-
Mined
-
-
-
-
-
-
-
-
-
-
- ~ ({{ accelerateRatio }}x slower) -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-} @else if (standardETA) { +} @else if (standardETA) {
+
+
+
+
+
+
+
+ @if (eta) { + ~ ({{ accelerateRatio }}x faster) + } +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Mined
+
+
+
@@ -159,9 +105,7 @@
- @if (eta) { - ~ - } + ~
@@ -169,9 +113,9 @@
- +
-
+
First seen
@@ -182,52 +126,23 @@
-
- -
-
-
Accelerated
-
- -
-
-
-
-
-
-
-
- -
-
-
Mined
-
-
-
-
-
-
-
-
-
-
- ~ ({{ accelerateRatio }}x slower) -
-
-
-
-
-
-
-
-
+
+
+
+
+
+ Accelerated  +
+
+
+
diff --git a/frontend/src/app/components/acceleration-timeline/acceleration-timeline.component.scss b/frontend/src/app/components/acceleration-timeline/acceleration-timeline.component.scss index 54061f54e..8648052f4 100644 --- a/frontend/src/app/components/acceleration-timeline/acceleration-timeline.component.scss +++ b/frontend/src/app/components/acceleration-timeline/acceleration-timeline.component.scss @@ -1,7 +1,7 @@ .acceleration-timeline { position: relative; width: 100%; - padding: 1em 0; + padding: 0.5em 0 1em; &::after, &::before { content: ''; @@ -69,6 +69,15 @@ font-size: 12px; line-height: 16px; white-space: nowrap; + + .compare { + font-style: italic; + color: var(--mainnet-alt); + font-weight: 600; + @media (max-width: 600px) { + display: none; + } + } } } @@ -115,26 +124,27 @@ } } - .corner-up { + .connector { position: absolute; + height: 88px; + width: 10px; left: -5px; - left: 48.5%; - height: 86px; - border-left: solid 10px var(--primary); - border-bottom: solid 10px var(--primary); - border-bottom-right-radius: 10px; - // horrible css: - @media (max-width: 1030px) { - left: 48%; + top: -73px; + transform: translateX(120%); + background: var(--tertiary); + + &.down { + border-top-left-radius: 10px; } - @media (max-width: 850px) { - left: 47%; + + &.up { + border-top-right-radius: 10px; } - @media (max-width: 700px) { - left: 46%; + + &.loading { + animation: acceleratePulse 1s infinite; } } - } .nodes { @@ -150,8 +160,6 @@ transform: translateY(-50%); border-radius: 50%; padding: 2px; - background: transparent; - transition: background-color 300ms, padding 300ms; .shape { width: 100%; @@ -161,7 +169,6 @@ &.accelerating { animation: acceleratePulse 1s infinite; } - transition: background-color 300ms, border 300ms; } &.waiting { @@ -203,30 +210,6 @@ font-size: 12px; line-height: 16px; white-space: nowrap; - - &.sm-margin { - @media (max-width: 650px) { - margin-left: 20px; - } - } - } - } - - .connector { - position: relative; - height: 10px; - - .corner-down { - position: absolute; - @media (max-width: 650px) { - width: 223px; - } - width: 290px; - height: 90px; - bottom: 50%; - border-left: solid 10px var(--primary); - border-bottom: solid 10px var(--primary); - border-bottom-left-radius: 10px; } } } @@ -237,9 +220,3 @@ 50% { background-color: var(--mainnet-alt) } 100% { background-color: var(--tertiary) } } - -@keyframes textPulse { - 0% { color: var(--tertiary) } - 50% { color: var(--mainnet-alt) } - 100% { color: var(--tertiary) } -} \ No newline at end of file From 23ee613414e48e58f13c5d1347fd9db909365eee Mon Sep 17 00:00:00 2001 From: natsoni Date: Mon, 8 Jul 2024 22:49:31 +0900 Subject: [PATCH 7/7] Fix missing 'Mined' tag --- .../acceleration-timeline/acceleration-timeline.component.html | 1 + 1 file changed, 1 insertion(+) diff --git a/frontend/src/app/components/acceleration-timeline/acceleration-timeline.component.html b/frontend/src/app/components/acceleration-timeline/acceleration-timeline.component.html index 2b826f2a1..23462f8f7 100644 --- a/frontend/src/app/components/acceleration-timeline/acceleration-timeline.component.html +++ b/frontend/src/app/components/acceleration-timeline/acceleration-timeline.component.html @@ -50,6 +50,7 @@
+
Mined