diff --git a/frontend/src/app/components/cluster-diagram/cluster-diagram.component.html b/frontend/src/app/components/cluster-diagram/cluster-diagram.component.html index 59dbe36de..1848d1456 100644 --- a/frontend/src/app/components/cluster-diagram/cluster-diagram.component.html +++ b/frontend/src/app/components/cluster-diagram/cluster-diagram.component.html @@ -48,10 +48,12 @@ [class.inactive]="outline.chunkIndex !== effectiveActiveChunk" [attr.d]="outline.path" [ngbTooltip]="chunkLabelTooltip" + [disableTooltip]="isMobile" container="body" placement="top" (mouseenter)="onChunkEnter(outline.chunkIndex)" - (mouseleave)="onChunkLeave()" /> + (mouseleave)="onChunkLeave()" + (click)="onChunkClick(outline.chunkIndex, $event)" /> + (mouseleave)="onChunkLeave()" + (click)="onChunkClick(outline.chunkIndex, $event)"> {{ outline.feerate | feeRounding }} sat/vB @@ -76,7 +80,8 @@ stroke-width="12" (mouseenter)="onEdgeEnter(i, $event)" (mousemove)="onEdgeMove($event)" - (mouseleave)="onEdgeLeave()" /> + (mouseleave)="onEdgeLeave()" + (click)="onEdgeClick(i, $event)" /> + (click)="onNodeClick(node, $event)"> @@ -154,3 +159,39 @@ child + + + + + {{ hoverNode.tx.txid | shortenString }} + this transaction + + + + + Fee + {{ hoverNode.tx.fee | number }} sats + + + Size + + + + Fee rate + {{ hoverNode.feerate | feeRounding }} sat/vB + + + + parents + children + + + + + parent + child + + diff --git a/frontend/src/app/components/cluster-diagram/cluster-diagram.component.scss b/frontend/src/app/components/cluster-diagram/cluster-diagram.component.scss index 603a5f999..18d716f94 100644 --- a/frontend/src/app/components/cluster-diagram/cluster-diagram.component.scss +++ b/frontend/src/app/components/cluster-diagram/cluster-diagram.component.scss @@ -144,17 +144,14 @@ font-weight: 700; } -.cluster-tooltip { - position: absolute; +.cluster-tooltip, +.cluster-mobile-panel { background: color-mix(in srgb, var(--active-bg) 95%, transparent); border-radius: 4px; box-shadow: 1px 1px 10px rgba(0, 0, 0, 0.5); color: var(--tooltip-grey); padding: 8px 12px; text-align: left; - pointer-events: none; - max-width: 360px; - white-space: nowrap; .tx-id-row { display: flex; @@ -225,3 +222,15 @@ &.descendant { background: var(--cluster-descendant-color); } } } + +.cluster-tooltip { + position: absolute; + pointer-events: none; + max-width: 360px; + white-space: nowrap; +} + +.cluster-mobile-panel { + margin-top: 8px; + white-space: normal; +} diff --git a/frontend/src/app/components/cluster-diagram/cluster-diagram.component.ts b/frontend/src/app/components/cluster-diagram/cluster-diagram.component.ts index df14efcba..070d3e46b 100644 --- a/frontend/src/app/components/cluster-diagram/cluster-diagram.component.ts +++ b/frontend/src/app/components/cluster-diagram/cluster-diagram.component.ts @@ -22,6 +22,7 @@ export class ClusterDiagramComponent implements OnChanges, AfterViewInit, OnDest @Input() cluster: { txs: CpfpClusterTx[]; chunks: CpfpClusterChunk[]; chunkIndex: number }; @Input() txid: string; @Input() preview = false; + @Input() isMobile = false; @ViewChild('graphContainer', { static: true }) graphContainer: ElementRef; @ViewChild('tooltip') tooltipElement: ElementRef; @@ -125,8 +126,17 @@ export class ClusterDiagramComponent implements OnChanges, AfterViewInit, OnDest } onNodeEnter(node: RenderedNode, event: MouseEvent): void { - if (this.preview) { return; } + if (this.preview || this.isMobile) { return; } + this.applyNodeHighlight(node); + this.updateTooltipPosition(event); + this.cd.markForCheck(); + } + + private applyNodeHighlight(node: RenderedNode): void { this.hoverNode = node; + this.hoverEdge = null; + this.hoverChunkIndex = null; + this.applyEffectiveChunk(); this.clearHighlights(); node.hovered = true; for (const edge of this.edges) { @@ -140,58 +150,63 @@ export class ClusterDiagramComponent implements OnChanges, AfterViewInit, OnDest edge.highlightKind = 'ancestor'; } } - this.updateTooltipPosition(event); - this.cd.markForCheck(); } onNodeMove(event: MouseEvent): void { - if (this.preview) { return; } + if (this.preview || this.isMobile) { return; } this.updateTooltipPosition(event); this.cd.markForCheck(); } onNodeLeave(): void { - if (this.preview) { return; } + if (this.preview || this.isMobile) { return; } this.hoverNode = null; this.clearHighlights(); this.cd.markForCheck(); } onEdgeEnter(edgeIndex: number, event: MouseEvent): void { - if (this.preview) { return; } + if (this.preview || this.isMobile) { return; } + this.applyEdgeHighlight(edgeIndex); + this.updateTooltipPosition(event); + this.cd.markForCheck(); + } + + private applyEdgeHighlight(edgeIndex: number): void { this.clearHighlights(); + this.hoverNode = null; + this.hoverChunkIndex = null; + this.applyEffectiveChunk(); const edge = this.edges[edgeIndex]; edge.highlighted = true; edge.highlightKind = 'direct'; this.nodes[edge.parentIndex].relation = 'ancestor'; this.nodes[edge.childIndex].relation = 'descendant'; this.hoverEdge = edge; - this.updateTooltipPosition(event); - this.cd.markForCheck(); } onEdgeMove(event: MouseEvent): void { - if (this.preview) { return; } + if (this.preview || this.isMobile) { return; } this.updateTooltipPosition(event); this.cd.markForCheck(); } onEdgeLeave(): void { - if (this.preview) { return; } + if (this.preview || this.isMobile) { return; } this.hoverEdge = null; this.clearHighlights(); this.cd.markForCheck(); } onChunkEnter(chunkIndex: number): void { - if (this.preview) { return; } + if (this.preview || this.isMobile) { return; } this.hoverChunkIndex = chunkIndex; this.applyEffectiveChunk(); this.cd.markForCheck(); } onChunkLeave(): void { - if (this.preview) { return; } + if (this.preview || this.isMobile) { return; } this.hoverChunkIndex = null; this.applyEffectiveChunk(); this.cd.markForCheck(); @@ -212,13 +227,61 @@ export class ClusterDiagramComponent implements OnChanges, AfterViewInit, OnDest } } - onNodeClick(node: RenderedNode): void { + onNodeClick(node: RenderedNode, event: MouseEvent): void { if (this.preview) { return; } + if (this.isMobile) { + event.stopPropagation(); + if (this.hoverNode?.index === node.index) { + this.clearMobileSelection(); + } else { + this.applyNodeHighlight(node); + this.cd.markForCheck(); + } + return; + } const network = this.stateService.network; const prefix = network && network !== 'mainnet' ? `/${network}` : ''; this.router.navigate([prefix + '/tx/', node.tx.txid]); } + onEdgeClick(edgeIndex: number, event: MouseEvent): void { + if (this.preview || !this.isMobile) { return; } + event.stopPropagation(); + const edge = this.edges[edgeIndex]; + if (this.hoverEdge === edge) { + this.clearMobileSelection(); + } else { + this.applyEdgeHighlight(edgeIndex); + this.cd.markForCheck(); + } + } + + onChunkClick(chunkIndex: number, event: MouseEvent): void { + if (this.preview || !this.isMobile) { return; } + event.stopPropagation(); + this.hoverNode = null; + this.hoverEdge = null; + this.clearHighlights(); + this.hoverChunkIndex = chunkIndex; + this.applyEffectiveChunk(); + this.cd.markForCheck(); + } + + @HostListener('click') + onBackgroundClick(): void { + if (this.preview || !this.isMobile) { return; } + this.clearMobileSelection(); + } + + private clearMobileSelection(): void { + this.hoverNode = null; + this.hoverEdge = null; + this.hoverChunkIndex = null; + this.clearHighlights(); + this.applyEffectiveChunk(); + this.cd.markForCheck(); + } + private clearHighlights(): void { for (const node of this.nodes) { node.hovered = false; diff --git a/frontend/src/app/components/transaction/transaction-details/transaction-details.component.html b/frontend/src/app/components/transaction/transaction-details/transaction-details.component.html index f6decfacf..41951982b 100644 --- a/frontend/src/app/components/transaction/transaction-details/transaction-details.component.html +++ b/frontend/src/app/components/transaction/transaction-details/transaction-details.component.html @@ -248,13 +248,14 @@ - + diff --git a/frontend/src/app/components/transaction/transaction-raw.component.html b/frontend/src/app/components/transaction/transaction-raw.component.html index 228aedcb9..ee2242099 100644 --- a/frontend/src/app/components/transaction/transaction-raw.component.html +++ b/frontend/src/app/components/transaction/transaction-raw.component.html @@ -104,7 +104,7 @@ + [cluster]="cpfpInfo.cluster" [txid]="transaction.txid" [isMobile]="isMobile"> @@ -248,4 +248,4 @@ } - \ No newline at end of file + diff --git a/frontend/src/app/components/transaction/transaction.component.html b/frontend/src/app/components/transaction/transaction.component.html index 04da15d9b..800d3b54e 100644 --- a/frontend/src/app/components/transaction/transaction.component.html +++ b/frontend/src/app/components/transaction/transaction.component.html @@ -92,7 +92,7 @@ + [cluster]="cpfpInfo.cluster" [txid]="tx.txid" [isMobile]="isMobile"> @@ -393,4 +393,4 @@ - \ No newline at end of file +