From 1af38456f3baa5a01a5b2413588f7c0228c21dae Mon Sep 17 00:00:00 2001 From: Mononaut Date: Mon, 3 Oct 2022 16:57:15 +0000 Subject: [PATCH 1/9] Fix tx marker boundary condition --- .../mempool-blocks/mempool-blocks.component.ts | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/frontend/src/app/components/mempool-blocks/mempool-blocks.component.ts b/frontend/src/app/components/mempool-blocks/mempool-blocks.component.ts index 17236e2ca..e1a443680 100644 --- a/frontend/src/app/components/mempool-blocks/mempool-blocks.component.ts +++ b/frontend/src/app/components/mempool-blocks/mempool-blocks.component.ts @@ -287,11 +287,12 @@ export class MempoolBlocksComponent implements OnInit, OnDestroy { this.arrowVisible = true; - for (const block of this.mempoolBlocks) { - for (let i = 0; i < block.feeRange.length - 1; i++) { + let found = false; + for (let txInBlockIndex = 0; txInBlockIndex < this.mempoolBlocks.length && !found; txInBlockIndex++) { + const block = this.mempoolBlocks[txInBlockIndex]; + for (let i = 0; i < block.feeRange.length - 1 && !found; i++) { if (this.txFeePerVSize < block.feeRange[i + 1] && this.txFeePerVSize >= block.feeRange[i]) { - const txInBlockIndex = this.mempoolBlocks.indexOf(block); - const feeRangeIndex = block.feeRange.findIndex((val, index) => this.txFeePerVSize < block.feeRange[index + 1]); + const feeRangeIndex = i; const feeRangeChunkSize = 1 / (block.feeRange.length - 1); const txFee = this.txFeePerVSize - block.feeRange[i]; @@ -306,9 +307,13 @@ export class MempoolBlocksComponent implements OnInit, OnDestroy { + ((1 - feePosition) * blockedFilledPercentage * this.blockWidth); this.rightPosition = arrowRightPosition; - break; + found = true; } } + if (this.txFeePerVSize >= block.feeRange[block.feeRange.length - 1]) { + this.rightPosition = txInBlockIndex * (this.blockWidth + this.blockPadding); + found = true; + } } } From be2b72eea76ebee3d954ceaf17ed9ef45d519d9d Mon Sep 17 00:00:00 2001 From: Mononaut Date: Mon, 3 Oct 2022 21:44:55 +0000 Subject: [PATCH 2/9] Enable block scrolling in ltr time mode --- .../src/app/components/app/app.component.ts | 2 ++ .../blockchain/blockchain.component.scss | 26 +++++++++++++++---- .../mempool-blocks.component.scss | 6 +++++ .../app/components/start/start.component.html | 2 +- .../app/components/start/start.component.ts | 14 ++++++++-- 5 files changed, 42 insertions(+), 8 deletions(-) diff --git a/frontend/src/app/components/app/app.component.ts b/frontend/src/app/components/app/app.component.ts index 633e466a4..d9d6f77d6 100644 --- a/frontend/src/app/components/app/app.component.ts +++ b/frontend/src/app/components/app/app.component.ts @@ -25,6 +25,8 @@ export class AppComponent implements OnInit { if (this.locale.startsWith('ar') || this.locale.startsWith('fa') || this.locale.startsWith('he')) { this.dir = 'rtl'; this.class = 'rtl-layout'; + } else { + this.class = 'ltr-layout'; } tooltipConfig.animation = false; diff --git a/frontend/src/app/components/blockchain/blockchain.component.scss b/frontend/src/app/components/blockchain/blockchain.component.scss index 2f13374fe..97d864398 100644 --- a/frontend/src/app/components/blockchain/blockchain.component.scss +++ b/frontend/src/app/components/blockchain/blockchain.component.scss @@ -27,7 +27,6 @@ left: 0; top: 75px; transform: translateX(50vw); - transition: transform 1s; } .position-container.liquid, .position-container.liquidtestnet { @@ -97,14 +96,31 @@ } .blockchain-wrapper.ltr-transition .blocks-wrapper, +.blockchain-wrapper.ltr-transition .position-container, .blockchain-wrapper.ltr-transition .time-toggle { transition: transform 1s; } -.blockchain-wrapper.time-ltr .blocks-wrapper { - transform: scaleX(-1); +.blockchain-wrapper.time-ltr { + .blocks-wrapper { + transform: scaleX(-1); + } + + .time-toggle { + transform: translateX(-50%) scaleX(-1); + } } -.blockchain-wrapper.time-ltr .time-toggle { - transform: translateX(-50%) scaleX(-1); +:host-context(.ltr-layout) { + .blockchain-wrapper.time-ltr .blocks-wrapper, + .blockchain-wrapper .blocks-wrapper { + direction: ltr; + } +} + +:host-context(.rtl-layout) { + .blockchain-wrapper.time-ltr .blocks-wrapper, + .blockchain-wrapper .blocks-wrapper { + direction: rtl; + } } \ No newline at end of file diff --git a/frontend/src/app/components/mempool-blocks/mempool-blocks.component.scss b/frontend/src/app/components/mempool-blocks/mempool-blocks.component.scss index 8032be92f..565d4b302 100644 --- a/frontend/src/app/components/mempool-blocks/mempool-blocks.component.scss +++ b/frontend/src/app/components/mempool-blocks/mempool-blocks.component.scss @@ -146,4 +146,10 @@ .block-body { transform: scaleX(-1); } +} + +:host-context(.rtl-layout) { + #arrow-up { + transform: translateX(70px); + } } \ No newline at end of file diff --git a/frontend/src/app/components/start/start.component.html b/frontend/src/app/components/start/start.component.html index 9d7f39ba2..89b6efdc3 100644 --- a/frontend/src/app/components/start/start.component.html +++ b/frontend/src/app/components/start/start.component.html @@ -8,7 +8,7 @@
{{ eventName }} in {{ countdown | number }} block{{ countdown === 1 ? '' : 's' }}!
-
diff --git a/frontend/src/app/components/start/start.component.ts b/frontend/src/app/components/start/start.component.ts index 78e004985..37c94baa3 100644 --- a/frontend/src/app/components/start/start.component.ts +++ b/frontend/src/app/components/start/start.component.ts @@ -1,4 +1,5 @@ -import { Component, ElementRef, HostListener, OnInit, ViewChild } from '@angular/core'; +import { Component, ElementRef, HostListener, OnInit, OnDestroy, ViewChild } from '@angular/core'; +import { Subscription } from 'rxjs'; import { StateService } from '../../services/state.service'; import { specialBlocks } from '../../app.constants'; @@ -7,7 +8,7 @@ import { specialBlocks } from '../../app.constants'; templateUrl: './start.component.html', styleUrls: ['./start.component.scss'], }) -export class StartComponent implements OnInit { +export class StartComponent implements OnInit, OnDestroy { interval = 60; colors = ['#5E35B1', '#ffffff']; @@ -16,6 +17,8 @@ export class StartComponent implements OnInit { eventName = ''; mouseDragStartX: number; blockchainScrollLeftInit: number; + timeLtrSubscription: Subscription; + timeLtr: boolean = this.stateService.timeLtr.value; @ViewChild('blockchainContainer') blockchainContainer: ElementRef; constructor( @@ -23,6 +26,9 @@ export class StartComponent implements OnInit { ) { } ngOnInit() { + this.timeLtrSubscription = this.stateService.timeLtr.subscribe((ltr) => { + this.timeLtr = !!ltr; + }); this.stateService.blocks$ .subscribe((blocks: any) => { if (this.stateService.network !== '') { @@ -72,4 +78,8 @@ export class StartComponent implements OnInit { this.mouseDragStartX = null; this.stateService.setBlockScrollingInProgress(false); } + + ngOnDestroy() { + this.timeLtrSubscription.unsubscribe(); + } } From 5d3ee50bca8f8abc6d17ac28d06504edd7af5c5f Mon Sep 17 00:00:00 2001 From: Mononaut Date: Mon, 3 Oct 2022 22:07:59 +0000 Subject: [PATCH 3/9] Reverse time by default for RTL languages --- frontend/src/app/services/state.service.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/frontend/src/app/services/state.service.ts b/frontend/src/app/services/state.service.ts index 920f32dd9..9f7cc58a3 100644 --- a/frontend/src/app/services/state.service.ts +++ b/frontend/src/app/services/state.service.ts @@ -1,4 +1,4 @@ -import { Inject, Injectable, PLATFORM_ID } from '@angular/core'; +import { Inject, Injectable, PLATFORM_ID, LOCALE_ID } from '@angular/core'; import { ReplaySubject, BehaviorSubject, Subject, fromEvent, Observable } from 'rxjs'; import { Transaction } from '../interfaces/electrs.interface'; import { IBackendInfo, MempoolBlock, MempoolBlockWithTransactions, MempoolBlockDelta, MempoolInfo, Recommendedfees, ReplacedTransaction, TransactionStripped } from '../interfaces/websocket.interface'; @@ -113,6 +113,7 @@ export class StateService { constructor( @Inject(PLATFORM_ID) private platformId: any, + @Inject(LOCALE_ID) private locale: string, private router: Router, private storageService: StorageService, ) { @@ -151,7 +152,10 @@ export class StateService { this.blockVSize = this.env.BLOCK_WEIGHT_UNITS / 4; - this.timeLtr = new BehaviorSubject(this.storageService.getValue('time-preference-ltr') === 'true'); + const savedTimePreference = this.storageService.getValue('time-preference-ltr'); + const rtlLanguage = (this.locale.startsWith('ar') || this.locale.startsWith('fa') || this.locale.startsWith('he')); + // default time direction is right-to-left, unless locale is a RTL language + this.timeLtr = new BehaviorSubject(savedTimePreference === 'true' || (savedTimePreference == null && rtlLanguage)); this.timeLtr.subscribe((ltr) => { this.storageService.setValue('time-preference-ltr', ltr ? 'true' : 'false'); }); From c10ace8fb5819c3b80692b6ee7076ad2dc2f0fc1 Mon Sep 17 00:00:00 2001 From: Mononaut Date: Tue, 4 Oct 2022 21:00:46 +0000 Subject: [PATCH 4/9] Scroll to input/output when clicked in tx diagram --- .../transaction/transaction.component.html | 7 +- .../transaction/transaction.component.ts | 11 +++ .../transactions-list.component.html | 10 +-- .../transactions-list.component.ts | 84 +++++++++++-------- .../tx-bowtie-graph-tooltip.component.html | 2 +- .../tx-bowtie-graph.component.html | 2 + .../tx-bowtie-graph.component.ts | 22 +++-- 7 files changed, 88 insertions(+), 50 deletions(-) diff --git a/frontend/src/app/components/transaction/transaction.component.html b/frontend/src/app/components/transaction/transaction.component.html index 5b35fc7b1..dcd59a1f8 100644 --- a/frontend/src/app/components/transaction/transaction.component.html +++ b/frontend/src/app/components/transaction/transaction.component.html @@ -208,7 +208,10 @@ [lineLimit]="inOutLimit" [maxStrands]="graphExpanded ? maxInOut : 24" [network]="network" - [tooltip]="true"> + [tooltip]="true" + (selectInput)="selectInput($event)" + (selectOutput)="selectOutput($event)" + >
@@ -240,7 +243,7 @@
- +

Details

diff --git a/frontend/src/app/components/transaction/transaction.component.ts b/frontend/src/app/components/transaction/transaction.component.ts index 1db6e8f09..8aa313c81 100644 --- a/frontend/src/app/components/transaction/transaction.component.ts +++ b/frontend/src/app/components/transaction/transaction.component.ts @@ -47,6 +47,7 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy { now = new Date().getTime(); timeAvg$: Observable; liquidUnblinding = new LiquidUnblinding(); + inputIndex: number; outputIndex: number; showFlow: boolean = true; graphExpanded: boolean = false; @@ -334,6 +335,16 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy { this.graphExpanded = false; } + selectInput(input) { + this.inputIndex = input; + this.outputIndex = null; + } + + selectOutput(output) { + this.outputIndex = output; + this.inputIndex = null; + } + @HostListener('window:resize', ['$event']) setGraphSize(): void { if (this.graphContainer) { 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 81c3dce5c..e26740aa5 100644 --- a/frontend/src/app/components/transactions-list/transactions-list.component.html +++ b/frontend/src/app/components/transactions-list/transactions-list.component.html @@ -20,9 +20,9 @@
- + - + @@ -158,7 +158,7 @@
@@ -146,7 +146,7 @@
- + rowLimit && tx['@voutLimit'] && !outputIndex"> + diff --git a/frontend/src/app/components/transactions-list/transactions-list.component.ts b/frontend/src/app/components/transactions-list/transactions-list.component.ts index 8fd81af51..03c2d59ee 100644 --- a/frontend/src/app/components/transactions-list/transactions-list.component.ts +++ b/frontend/src/app/components/transactions-list/transactions-list.component.ts @@ -24,6 +24,7 @@ export class TransactionsListComponent implements OnInit, OnChanges { @Input() transactionPage = false; @Input() errorUnblinded = false; @Input() paginated = false; + @Input() inputIndex: number; @Input() outputIndex: number; @Input() address: string = ''; @Input() rowLimit = 12; @@ -37,6 +38,8 @@ export class TransactionsListComponent implements OnInit, OnChanges { showDetails$ = new BehaviorSubject(false); assetsMinimal: any; transactionsLength: number = 0; + inputRowLimit: number = 12; + outputRowLimit: number = 12; constructor( public stateService: StateService, @@ -97,50 +100,57 @@ export class TransactionsListComponent implements OnInit, OnChanges { ).subscribe(() => this.ref.markForCheck()); } - ngOnChanges(): void { - if (!this.transactions || !this.transactions.length) { - return; + ngOnChanges(changes): void { + if (changes.inputIndex || changes.outputIndex || changes.rowLimit) { + this.inputRowLimit = Math.max(this.rowLimit, (this.inputIndex || 0) + 3); + this.outputRowLimit = Math.max(this.rowLimit, (this.outputIndex || 0) + 3); + if (this.inputIndex || this.outputIndex) { + setTimeout(() => { + const assetBoxElements = document.getElementsByClassName('assetBox'); + if (assetBoxElements && assetBoxElements[0]) { + assetBoxElements[0].scrollIntoView({block: "center"}); + } + }, 10); + } } - - this.transactionsLength = this.transactions.length; - if (this.outputIndex) { - setTimeout(() => { - const assetBoxElements = document.getElementsByClassName('assetBox'); - if (assetBoxElements && assetBoxElements[0]) { - assetBoxElements[0].scrollIntoView(); - } - }, 10); - } - - this.transactions.forEach((tx) => { - tx['@voutLimit'] = true; - tx['@vinLimit'] = true; - if (tx['addressValue'] !== undefined) { + if (changes.transactions || changes.address) { + if (!this.transactions || !this.transactions.length) { return; } - if (this.address) { - const addressIn = tx.vout - .filter((v: Vout) => v.scriptpubkey_address === this.address) - .map((v: Vout) => v.value || 0) - .reduce((a: number, b: number) => a + b, 0); + this.transactionsLength = this.transactions.length; - const addressOut = tx.vin - .filter((v: Vin) => v.prevout && v.prevout.scriptpubkey_address === this.address) - .map((v: Vin) => v.prevout.value || 0) - .reduce((a: number, b: number) => a + b, 0); - tx['addressValue'] = addressIn - addressOut; - } - }); - const txIds = this.transactions.filter((tx) => !tx._outspends).map((tx) => tx.txid); - if (txIds.length) { - this.refreshOutspends$.next(txIds); - } - if (this.stateService.env.LIGHTNING) { - const txIds = this.transactions.filter((tx) => !tx._channels).map((tx) => tx.txid); + this.transactions.forEach((tx) => { + tx['@voutLimit'] = true; + tx['@vinLimit'] = true; + if (tx['addressValue'] !== undefined) { + return; + } + + if (this.address) { + const addressIn = tx.vout + .filter((v: Vout) => v.scriptpubkey_address === this.address) + .map((v: Vout) => v.value || 0) + .reduce((a: number, b: number) => a + b, 0); + + const addressOut = tx.vin + .filter((v: Vin) => v.prevout && v.prevout.scriptpubkey_address === this.address) + .map((v: Vin) => v.prevout.value || 0) + .reduce((a: number, b: number) => a + b, 0); + + tx['addressValue'] = addressIn - addressOut; + } + }); + const txIds = this.transactions.filter((tx) => !tx._outspends).map((tx) => tx.txid); if (txIds.length) { - this.refreshChannels$.next(txIds); + this.refreshOutspends$.next(txIds); + } + if (this.stateService.env.LIGHTNING) { + const txIds = this.transactions.filter((tx) => !tx._channels).map((tx) => tx.txid); + if (txIds.length) { + this.refreshChannels$.next(txIds); + } } } } diff --git a/frontend/src/app/components/tx-bowtie-graph-tooltip/tx-bowtie-graph-tooltip.component.html b/frontend/src/app/components/tx-bowtie-graph-tooltip/tx-bowtie-graph-tooltip.component.html index 563e6ed00..6872438a0 100644 --- a/frontend/src/app/components/tx-bowtie-graph-tooltip/tx-bowtie-graph-tooltip.component.html +++ b/frontend/src/app/components/tx-bowtie-graph-tooltip/tx-bowtie-graph-tooltip.component.html @@ -44,7 +44,7 @@ Output Fee - #{{ line.index }} + #{{ line.index + 1 }}

Confidential

diff --git a/frontend/src/app/components/tx-bowtie-graph/tx-bowtie-graph.component.html b/frontend/src/app/components/tx-bowtie-graph/tx-bowtie-graph.component.html index 03056cd53..3f0143a67 100644 --- a/frontend/src/app/components/tx-bowtie-graph/tx-bowtie-graph.component.html +++ b/frontend/src/app/components/tx-bowtie-graph/tx-bowtie-graph.component.html @@ -60,6 +60,7 @@ attr.marker-start="url(#{{input.class}}-arrow)" (pointerover)="onHover($event, 'input', i);" (pointerout)="onBlur($event, 'input', i);" + (click)="onClick($event, 'input', inputData[i].index);" /> @@ -70,6 +71,7 @@ attr.marker-start="url(#{{output.class}}-arrow)" (pointerover)="onHover($event, 'output', i);" (pointerout)="onBlur($event, 'output', i);" + (click)="onClick($event, 'output', outputData[i].index);" /> diff --git a/frontend/src/app/components/tx-bowtie-graph/tx-bowtie-graph.component.ts b/frontend/src/app/components/tx-bowtie-graph/tx-bowtie-graph.component.ts index 16e2736f7..592232955 100644 --- a/frontend/src/app/components/tx-bowtie-graph/tx-bowtie-graph.component.ts +++ b/frontend/src/app/components/tx-bowtie-graph/tx-bowtie-graph.component.ts @@ -1,4 +1,4 @@ -import { Component, OnInit, Input, OnChanges, HostListener } from '@angular/core'; +import { Component, OnInit, Input, Output, EventEmitter, OnChanges, HostListener } from '@angular/core'; import { Transaction } from '../../interfaces/electrs.interface'; interface SvgLine { @@ -35,6 +35,9 @@ export class TxBowtieGraphComponent implements OnInit, OnChanges { @Input() maxStrands = 24; // number of inputs/outputs to keep fully on-screen. @Input() tooltip = false; + @Output() selectInput = new EventEmitter(); + @Output() selectOutput = new EventEmitter(); + inputData: Xput[]; outputData: Xput[]; inputs: SvgLine[]; @@ -76,11 +79,12 @@ export class TxBowtieGraphComponent implements OnInit, OnChanges { this.combinedWeight = Math.min(this.maxCombinedWeight, Math.floor((this.width - (2 * this.midWidth)) / 6)); const totalValue = this.calcTotalValue(this.tx); - let voutWithFee = this.tx.vout.map(v => { + let voutWithFee = this.tx.vout.map((v, i) => { return { type: v.scriptpubkey_type === 'fee' ? 'fee' : 'output', value: v?.value, address: v?.scriptpubkey_address || v?.scriptpubkey_type?.toUpperCase(), + index: i, pegout: v?.pegout?.scriptpubkey_address, confidential: (this.isLiquid && v?.value === undefined), } as Xput; @@ -91,11 +95,12 @@ export class TxBowtieGraphComponent implements OnInit, OnChanges { } const outputCount = voutWithFee.length; - let truncatedInputs = this.tx.vin.map(v => { + let truncatedInputs = this.tx.vin.map((v, i) => { return { type: 'input', value: v?.prevout?.value, address: v?.prevout?.scriptpubkey_address || v?.prevout?.scriptpubkey_type?.toUpperCase(), + index: i, coinbase: v?.is_coinbase, pegin: v?.is_pegin, confidential: (this.isLiquid && v?.prevout?.value === undefined), @@ -306,8 +311,7 @@ export class TxBowtieGraphComponent implements OnInit, OnChanges { }; } else { this.hoverLine = { - ...this.outputData[index], - index + ...this.outputData[index] }; } } @@ -315,4 +319,12 @@ export class TxBowtieGraphComponent implements OnInit, OnChanges { onBlur(event, side, index): void { this.hoverLine = null; } + + onClick(event, side, index): void { + if (side === 'input') { + this.selectInput.emit(index); + } else { + this.selectOutput.emit(index); + } + } } From 0df796f8732d4c163cd7be520ac906f06fe40a2c Mon Sep 17 00:00:00 2001 From: Mononaut Date: Tue, 4 Oct 2022 21:08:54 +0000 Subject: [PATCH 5/9] Shorten tx diagram toggle labels --- .../src/app/components/transaction/transaction.component.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/src/app/components/transaction/transaction.component.html b/frontend/src/app/components/transaction/transaction.component.html index 5b35fc7b1..8eb085c27 100644 --- a/frontend/src/app/components/transaction/transaction.component.html +++ b/frontend/src/app/components/transaction/transaction.component.html @@ -195,7 +195,7 @@

Flow

- +
@@ -234,7 +234,7 @@
- +
From d1cedbb98139030b986971bc03c58890aa27b67a Mon Sep 17 00:00:00 2001 From: Mononaut Date: Tue, 4 Oct 2022 22:09:32 +0000 Subject: [PATCH 6/9] Fix tx confirmation badge layout bug --- .../transaction/transaction.component.scss | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/frontend/src/app/components/transaction/transaction.component.scss b/frontend/src/app/components/transaction/transaction.component.scss index e0e7d79fe..df8d37ebc 100644 --- a/frontend/src/app/components/transaction/transaction.component.scss +++ b/frontend/src/app/components/transaction/transaction.component.scss @@ -3,38 +3,38 @@ } .container-buttons { - align-self: center; + align-self: flex-start; } .title-block { - flex-wrap: wrap; + flex-wrap: wrap; + align-items: baseline; @media (min-width: 650px) { flex-direction: row; } h1 { margin: 0rem; + margin-right: 15px; line-height: 1; } } .tx-link { - display: flex; - flex-grow: 1; margin-bottom: 0px; margin-top: 8px; - @media (min-width: 650px) { - align-self: end; - margin-left: 15px; - margin-top: 0px; - margin-bottom: -3px; - } - @media (min-width: 768px) { + display: inline-block; + width: 100%; + flex-shrink: 0; + @media (min-width: 651px) { + display: flex; + width: auto; + flex-grow: 1; margin-bottom: 0px; top: 1px; position: relative; - } - @media (max-width: 768px) { - order: 3; - } + } + @media (max-width: 650px) { + order: 3; + } } .td-width { From 626a1a2977701cd13c767006f11c20782e13cbe5 Mon Sep 17 00:00:00 2001 From: Mononaut Date: Tue, 4 Oct 2022 23:29:04 +0000 Subject: [PATCH 7/9] Navigate to vin/vout page on diagram click --- .../tx-bowtie-graph.component.ts | 66 ++++++++++++++++++- 1 file changed, 63 insertions(+), 3 deletions(-) diff --git a/frontend/src/app/components/tx-bowtie-graph/tx-bowtie-graph.component.ts b/frontend/src/app/components/tx-bowtie-graph/tx-bowtie-graph.component.ts index 592232955..7eaba9ce6 100644 --- a/frontend/src/app/components/tx-bowtie-graph/tx-bowtie-graph.component.ts +++ b/frontend/src/app/components/tx-bowtie-graph/tx-bowtie-graph.component.ts @@ -1,5 +1,11 @@ import { Component, OnInit, Input, Output, EventEmitter, OnChanges, HostListener } from '@angular/core'; -import { Transaction } from '../../interfaces/electrs.interface'; +import { StateService } from '../../services/state.service'; +import { Outspend, Transaction } from '../../interfaces/electrs.interface'; +import { Router } from '@angular/router'; +import { ReplaySubject, merge, Subscription } from 'rxjs'; +import { tap, switchMap } from 'rxjs/operators'; +import { ApiService } from '../../services/api.service'; +import { RelativeUrlPipe } from '../../shared/pipes/relative-url/relative-url.pipe'; interface SvgLine { path: string; @@ -48,6 +54,10 @@ export class TxBowtieGraphComponent implements OnInit, OnChanges { isLiquid: boolean = false; hoverLine: Xput | void = null; tooltipPosition = { x: 0, y: 0 }; + outspends: Outspend[] = []; + + outspendsSubscription: Subscription; + refreshOutspends$: ReplaySubject = new ReplaySubject(); gradientColors = { '': ['#9339f4', '#105fb0'], @@ -64,12 +74,45 @@ export class TxBowtieGraphComponent implements OnInit, OnChanges { gradient: string[] = ['#105fb0', '#105fb0']; + constructor( + private router: Router, + private relativeUrlPipe: RelativeUrlPipe, + private stateService: StateService, + private apiService: ApiService, + ) { } + ngOnInit(): void { this.initGraph(); + + this.outspendsSubscription = merge( + this.refreshOutspends$ + .pipe( + switchMap((txid) => this.apiService.getOutspendsBatched$([txid])), + tap((outspends: Outspend[][]) => { + if (!this.tx || !outspends || !outspends.length) { + return; + } + this.outspends = outspends[0]; + }), + ), + this.stateService.utxoSpent$ + .pipe( + tap((utxoSpent) => { + for (const i in utxoSpent) { + this.outspends[i] = { + spent: true, + txid: utxoSpent[i].txid, + vin: utxoSpent[i].vin, + }; + } + }), + ), + ).subscribe(() => {}); } ngOnChanges(): void { this.initGraph(); + this.refreshOutspends$.next(this.tx.txid); } initGraph(): void { @@ -322,9 +365,26 @@ export class TxBowtieGraphComponent implements OnInit, OnChanges { onClick(event, side, index): void { if (side === 'input') { - this.selectInput.emit(index); + const input = this.tx.vin[index]; + if (input && input.txid && input.vout != null) { + this.router.navigate([this.relativeUrlPipe.transform('/tx'), input.txid + ':' + input.vout], { + queryParamsHandling: 'merge', + fragment: 'flow' + }); + } else { + this.selectInput.emit(index); + } } else { - this.selectOutput.emit(index); + const output = this.tx.vout[index]; + const outspend = this.outspends[index]; + if (output && outspend && outspend.spent && outspend.txid) { + this.router.navigate([this.relativeUrlPipe.transform('/tx'), outspend.txid], { + queryParamsHandling: 'merge', + fragment: 'flow' + }); + } else { + this.selectOutput.emit(index); + } } } } From 75fd036ec2812a0d9fdd4401909cab2b823ef330 Mon Sep 17 00:00:00 2001 From: Mononaut Date: Tue, 4 Oct 2022 23:30:14 +0000 Subject: [PATCH 8/9] Highlight url input/output in tx diagram & list --- .../transaction/transaction.component.html | 1 + .../transaction/transaction.component.ts | 11 +++++++++-- .../transactions-list.component.html | 2 +- .../transactions-list.component.ts | 2 +- .../tx-bowtie-graph/tx-bowtie-graph.component.html | 14 ++++++++++++++ .../tx-bowtie-graph/tx-bowtie-graph.component.scss | 11 +++++++++++ .../tx-bowtie-graph/tx-bowtie-graph.component.ts | 4 +++- 7 files changed, 40 insertions(+), 5 deletions(-) diff --git a/frontend/src/app/components/transaction/transaction.component.html b/frontend/src/app/components/transaction/transaction.component.html index dcd59a1f8..5bd40a557 100644 --- a/frontend/src/app/components/transaction/transaction.component.html +++ b/frontend/src/app/components/transaction/transaction.component.html @@ -209,6 +209,7 @@ [maxStrands]="graphExpanded ? maxInOut : 24" [network]="network" [tooltip]="true" + [inputIndex]="inputIndex" [outputIndex]="outputIndex" (selectInput)="selectInput($event)" (selectOutput)="selectOutput($event)" > diff --git a/frontend/src/app/components/transaction/transaction.component.ts b/frontend/src/app/components/transaction/transaction.component.ts index 8aa313c81..c64c112b1 100644 --- a/frontend/src/app/components/transaction/transaction.component.ts +++ b/frontend/src/app/components/transaction/transaction.component.ts @@ -122,8 +122,15 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy { .pipe( switchMap((params: ParamMap) => { const urlMatch = (params.get('id') || '').split(':'); - this.txId = urlMatch[0]; - this.outputIndex = urlMatch[1] === undefined ? null : parseInt(urlMatch[1], 10); + if (urlMatch.length === 2 && urlMatch[1].length === 64) { + this.inputIndex = parseInt(urlMatch[0], 10); + this.outputIndex = null; + this.txId = urlMatch[1]; + } else { + this.txId = urlMatch[0]; + this.outputIndex = urlMatch[1] === undefined ? null : parseInt(urlMatch[1], 10); + this.inputIndex = null; + } this.seoService.setTitle( $localize`:@@bisq.transaction.browser-title:Transaction: ${this.txId}:INTERPOLATION:` ); 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 e26740aa5..e53c54a7a 100644 --- a/frontend/src/app/components/transactions-list/transactions-list.component.html +++ b/frontend/src/app/components/transactions-list/transactions-list.component.html @@ -220,7 +220,7 @@ - + diff --git a/frontend/src/app/components/transactions-list/transactions-list.component.ts b/frontend/src/app/components/transactions-list/transactions-list.component.ts index 03c2d59ee..4f3f1cec3 100644 --- a/frontend/src/app/components/transactions-list/transactions-list.component.ts +++ b/frontend/src/app/components/transactions-list/transactions-list.component.ts @@ -104,7 +104,7 @@ export class TransactionsListComponent implements OnInit, OnChanges { if (changes.inputIndex || changes.outputIndex || changes.rowLimit) { this.inputRowLimit = Math.max(this.rowLimit, (this.inputIndex || 0) + 3); this.outputRowLimit = Math.max(this.rowLimit, (this.outputIndex || 0) + 3); - if (this.inputIndex || this.outputIndex) { + if ((this.inputIndex || this.outputIndex) && !changes.transactions) { setTimeout(() => { const assetBoxElements = document.getElementsByClassName('assetBox'); if (assetBoxElements && assetBoxElements[0]) { diff --git a/frontend/src/app/components/tx-bowtie-graph/tx-bowtie-graph.component.html b/frontend/src/app/components/tx-bowtie-graph/tx-bowtie-graph.component.html index 3f0143a67..ced3b5f57 100644 --- a/frontend/src/app/components/tx-bowtie-graph/tx-bowtie-graph.component.html +++ b/frontend/src/app/components/tx-bowtie-graph/tx-bowtie-graph.component.html @@ -41,6 +41,18 @@ + + + + + + + + + + + + @@ -56,6 +68,7 @@ (); @Output() selectOutput = new EventEmitter(); @@ -378,7 +380,7 @@ export class TxBowtieGraphComponent implements OnInit, OnChanges { const output = this.tx.vout[index]; const outspend = this.outspends[index]; if (output && outspend && outspend.spent && outspend.txid) { - this.router.navigate([this.relativeUrlPipe.transform('/tx'), outspend.txid], { + this.router.navigate([this.relativeUrlPipe.transform('/tx'), outspend.vin + ':' + outspend.txid], { queryParamsHandling: 'merge', fragment: 'flow' }); From 0c1993e2649e93003c721e951eed3c17f4e4131b Mon Sep 17 00:00:00 2001 From: softsimon Date: Thu, 6 Oct 2022 20:33:54 +0400 Subject: [PATCH 9/9] Updating time toggle size --- .../src/app/components/blockchain/blockchain.component.scss | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/src/app/components/blockchain/blockchain.component.scss b/frontend/src/app/components/blockchain/blockchain.component.scss index 2f13374fe..9b6c12a80 100644 --- a/frontend/src/app/components/blockchain/blockchain.component.scss +++ b/frontend/src/app/components/blockchain/blockchain.component.scss @@ -84,9 +84,9 @@ .time-toggle { color: white; - font-size: 1rem; + font-size: 0.8rem; position: absolute; - bottom: -1.5em; + bottom: -1.8em; left: 1px; transform: translateX(-50%); background: none;