Merge pull request #2649 from mononaut/flow-diagram-spent-connectors
Extend flow diagram to differentiate spent and unspent TXOs
This commit is contained in:
		
						commit
						9345b1609f
					
				@ -126,9 +126,13 @@ export class LiquidUnblinding {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  async checkUnblindedTx(tx: Transaction) {
 | 
					  async checkUnblindedTx(tx: Transaction) {
 | 
				
			||||||
    const windowLocationHash = window.location.hash.substring('#blinded='.length);
 | 
					    if (!window.location.hash?.length) {
 | 
				
			||||||
    if (windowLocationHash.length > 0) {
 | 
					      return tx;
 | 
				
			||||||
      const blinders = this.parseBlinders(windowLocationHash);
 | 
					    }
 | 
				
			||||||
 | 
					    const fragmentParams = new URLSearchParams(window.location.hash.slice(1) || '');
 | 
				
			||||||
 | 
					    const blinderStr = fragmentParams.get('blinded');
 | 
				
			||||||
 | 
					    if (blinderStr && blinderStr.length) {
 | 
				
			||||||
 | 
					      const blinders = this.parseBlinders(blinderStr);
 | 
				
			||||||
      if (blinders) {
 | 
					      if (blinders) {
 | 
				
			||||||
        this.commitments = await this.makeCommitmentMap(blinders);
 | 
					        this.commitments = await this.makeCommitmentMap(blinders);
 | 
				
			||||||
        return this.tryUnblindTx(tx);
 | 
					        return this.tryUnblindTx(tx);
 | 
				
			||||||
 | 
				
			|||||||
@ -29,7 +29,7 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  <div class="row graph-wrapper">
 | 
					  <div class="row graph-wrapper">
 | 
				
			||||||
    <tx-bowtie-graph [tx]="tx" [width]="1112" [height]="346" [network]="network"></tx-bowtie-graph>
 | 
					    <tx-bowtie-graph [tx]="tx" [width]="1132" [height]="346" [network]="network"></tx-bowtie-graph>
 | 
				
			||||||
    <div class="above-bow">
 | 
					    <div class="above-bow">
 | 
				
			||||||
      <p class="field pair">
 | 
					      <p class="field pair">
 | 
				
			||||||
        <span [innerHTML]="'‎' + (tx.size | bytes: 2)"></span>
 | 
					        <span [innerHTML]="'‎' + (tx.size | bytes: 2)"></span>
 | 
				
			||||||
 | 
				
			|||||||
@ -69,7 +69,7 @@
 | 
				
			|||||||
.graph-wrapper {
 | 
					.graph-wrapper {
 | 
				
			||||||
  position: relative;
 | 
					  position: relative;
 | 
				
			||||||
  background: #181b2d;
 | 
					  background: #181b2d;
 | 
				
			||||||
  padding: 10px;
 | 
					  padding: 10px 0;
 | 
				
			||||||
  padding-bottom: 0;
 | 
					  padding-bottom: 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  .above-bow {
 | 
					  .above-bow {
 | 
				
			||||||
 | 
				
			|||||||
@ -209,6 +209,7 @@
 | 
				
			|||||||
            [maxStrands]="graphExpanded ? maxInOut : 24"
 | 
					            [maxStrands]="graphExpanded ? maxInOut : 24"
 | 
				
			||||||
            [network]="network"
 | 
					            [network]="network"
 | 
				
			||||||
            [tooltip]="true"
 | 
					            [tooltip]="true"
 | 
				
			||||||
 | 
					            [connectors]="true"
 | 
				
			||||||
            [inputIndex]="inputIndex" [outputIndex]="outputIndex"
 | 
					            [inputIndex]="inputIndex" [outputIndex]="outputIndex"
 | 
				
			||||||
          >
 | 
					          >
 | 
				
			||||||
          </tx-bowtie-graph>
 | 
					          </tx-bowtie-graph>
 | 
				
			||||||
 | 
				
			|||||||
@ -86,7 +86,7 @@
 | 
				
			|||||||
  position: relative;
 | 
					  position: relative;
 | 
				
			||||||
  width: 100%;
 | 
					  width: 100%;
 | 
				
			||||||
  background: #181b2d;
 | 
					  background: #181b2d;
 | 
				
			||||||
  padding: 10px;
 | 
					  padding: 10px 0;
 | 
				
			||||||
  padding-bottom: 0;
 | 
					  padding-bottom: 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -404,7 +404,7 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy {
 | 
				
			|||||||
  @HostListener('window:resize', ['$event'])
 | 
					  @HostListener('window:resize', ['$event'])
 | 
				
			||||||
  setGraphSize(): void {
 | 
					  setGraphSize(): void {
 | 
				
			||||||
    if (this.graphContainer) {
 | 
					    if (this.graphContainer) {
 | 
				
			||||||
      this.graphWidth = this.graphContainer.nativeElement.clientWidth - 24;
 | 
					      this.graphWidth = this.graphContainer.nativeElement.clientWidth;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -22,13 +22,13 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  <ng-template #pegin>
 | 
					  <ng-template #pegin>
 | 
				
			||||||
    <ng-container *ngIf="line.pegin; else pegout">
 | 
					    <ng-container *ngIf="line.pegin; else pegout">
 | 
				
			||||||
      <p>Peg In</p>
 | 
					      <p *ngIf="!isConnector">Peg In</p>
 | 
				
			||||||
    </ng-container>
 | 
					    </ng-container>
 | 
				
			||||||
  </ng-template>
 | 
					  </ng-template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  <ng-template #pegout>
 | 
					  <ng-template #pegout>
 | 
				
			||||||
    <ng-container *ngIf="line.pegout; else normal">
 | 
					    <ng-container *ngIf="line.pegout; else normal">
 | 
				
			||||||
      <p>Peg Out</p>
 | 
					      <p *ngIf="!isConnector">Peg Out</p>
 | 
				
			||||||
      <p *ngIf="line.value != null"><app-amount [satoshis]="line.value"></app-amount></p>
 | 
					      <p *ngIf="line.value != null"><app-amount [satoshis]="line.value"></app-amount></p>
 | 
				
			||||||
      <p class="address">
 | 
					      <p class="address">
 | 
				
			||||||
        <span class="first">{{ line.pegout.slice(0, -4) }}</span>
 | 
					        <span class="first">{{ line.pegout.slice(0, -4) }}</span>
 | 
				
			||||||
@ -38,7 +38,7 @@
 | 
				
			|||||||
  </ng-template>
 | 
					  </ng-template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  <ng-template #normal>
 | 
					  <ng-template #normal>
 | 
				
			||||||
      <p>
 | 
					      <p *ngIf="!isConnector">
 | 
				
			||||||
        <ng-container [ngSwitch]="line.type">
 | 
					        <ng-container [ngSwitch]="line.type">
 | 
				
			||||||
          <span *ngSwitchCase="'input'" i18n="transaction.input">Input</span>
 | 
					          <span *ngSwitchCase="'input'" i18n="transaction.input">Input</span>
 | 
				
			||||||
          <span *ngSwitchCase="'output'" i18n="transaction.output">Output</span>
 | 
					          <span *ngSwitchCase="'output'" i18n="transaction.output">Output</span>
 | 
				
			||||||
@ -46,6 +46,17 @@
 | 
				
			|||||||
        </ng-container>
 | 
					        </ng-container>
 | 
				
			||||||
        <span *ngIf="line.type !== 'fee'"> #{{ line.index + 1 }}</span>
 | 
					        <span *ngIf="line.type !== 'fee'"> #{{ line.index + 1 }}</span>
 | 
				
			||||||
      </p>
 | 
					      </p>
 | 
				
			||||||
 | 
					      <ng-container *ngIf="isConnector && line.txid">
 | 
				
			||||||
 | 
					        <p>
 | 
				
			||||||
 | 
					          <span i18n="transaction">Transaction</span> 
 | 
				
			||||||
 | 
					          <span class="first">{{ line.txid.slice(0, 8) }}</span>...
 | 
				
			||||||
 | 
					          <span class="last-four">{{ line.txid.slice(-4) }}</span>
 | 
				
			||||||
 | 
					        </p>
 | 
				
			||||||
 | 
					          <ng-container [ngSwitch]="line.type">
 | 
				
			||||||
 | 
					            <p *ngSwitchCase="'input'"><span i18n="transaction.output">Output</span>  #{{ line.vout + 1 }}</p>
 | 
				
			||||||
 | 
					            <p *ngSwitchCase="'output'"><span i18n="transaction.input">Input</span>  #{{ line.vin + 1 }}</p>
 | 
				
			||||||
 | 
					          </ng-container>
 | 
				
			||||||
 | 
					      </ng-container>
 | 
				
			||||||
      <p *ngIf="line.value == null && line.confidential" i18n="shared.confidential">Confidential</p>
 | 
					      <p *ngIf="line.value == null && line.confidential" i18n="shared.confidential">Confidential</p>
 | 
				
			||||||
      <p *ngIf="line.value != null"><app-amount [satoshis]="line.value"></app-amount></p>
 | 
					      <p *ngIf="line.value != null"><app-amount [satoshis]="line.value"></app-amount></p>
 | 
				
			||||||
      <p *ngIf="line.type !== 'fee' && line.address" class="address">
 | 
					      <p *ngIf="line.type !== 'fee' && line.address" class="address">
 | 
				
			||||||
 | 
				
			|||||||
@ -5,6 +5,9 @@ interface Xput {
 | 
				
			|||||||
  type: 'input' | 'output' | 'fee';
 | 
					  type: 'input' | 'output' | 'fee';
 | 
				
			||||||
  value?: number;
 | 
					  value?: number;
 | 
				
			||||||
  index?: number;
 | 
					  index?: number;
 | 
				
			||||||
 | 
					  txid?: string;
 | 
				
			||||||
 | 
					  vin?: number;
 | 
				
			||||||
 | 
					  vout?: number;
 | 
				
			||||||
  address?: string;
 | 
					  address?: string;
 | 
				
			||||||
  rest?: number;
 | 
					  rest?: number;
 | 
				
			||||||
  coinbase?: boolean;
 | 
					  coinbase?: boolean;
 | 
				
			||||||
@ -21,6 +24,7 @@ interface Xput {
 | 
				
			|||||||
export class TxBowtieGraphTooltipComponent implements OnChanges {
 | 
					export class TxBowtieGraphTooltipComponent implements OnChanges {
 | 
				
			||||||
  @Input() line: Xput | void;
 | 
					  @Input() line: Xput | void;
 | 
				
			||||||
  @Input() cursorPosition: { x: number, y: number };
 | 
					  @Input() cursorPosition: { x: number, y: number };
 | 
				
			||||||
 | 
					  @Input() isConnector: boolean = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  tooltipPosition = { x: 0, y: 0 };
 | 
					  tooltipPosition = { x: 0, y: 0 };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -29,6 +29,14 @@
 | 
				
			|||||||
        <stop offset="0%" [attr.stop-color]="gradient[1]" />
 | 
					        <stop offset="0%" [attr.stop-color]="gradient[1]" />
 | 
				
			||||||
        <stop offset="100%" [attr.stop-color]="gradient[0]" />
 | 
					        <stop offset="100%" [attr.stop-color]="gradient[0]" />
 | 
				
			||||||
      </linearGradient>
 | 
					      </linearGradient>
 | 
				
			||||||
 | 
					      <linearGradient id="input-connector-gradient" x1="0%" y1="0%" x2="100%" y2="0%">
 | 
				
			||||||
 | 
					        <stop offset="0%" [attr.stop-color]="gradient[2]" />
 | 
				
			||||||
 | 
					        <stop offset="80%" [attr.stop-color]="gradient[0]" />
 | 
				
			||||||
 | 
					      </linearGradient>
 | 
				
			||||||
 | 
					      <linearGradient id="output-connector-gradient" x1="0%" y1="0%" x2="100%" y2="0%">
 | 
				
			||||||
 | 
					        <stop offset="20%" [attr.stop-color]="gradient[0]" />
 | 
				
			||||||
 | 
					        <stop offset="100%" [attr.stop-color]="gradient[2]" />
 | 
				
			||||||
 | 
					      </linearGradient>
 | 
				
			||||||
      <linearGradient id="input-hover-gradient" x1="0%" y1="0%" x2="100%" y2="0%">
 | 
					      <linearGradient id="input-hover-gradient" x1="0%" y1="0%" x2="100%" y2="0%">
 | 
				
			||||||
      <stop offset="0%" [attr.stop-color]="gradient[0]" />
 | 
					      <stop offset="0%" [attr.stop-color]="gradient[0]" />
 | 
				
			||||||
      <stop offset="2%" [attr.stop-color]="gradient[0]" />
 | 
					      <stop offset="2%" [attr.stop-color]="gradient[0]" />
 | 
				
			||||||
@ -41,6 +49,14 @@
 | 
				
			|||||||
        <stop offset="98%" [attr.stop-color]="gradient[0]" />
 | 
					        <stop offset="98%" [attr.stop-color]="gradient[0]" />
 | 
				
			||||||
        <stop offset="100%" [attr.stop-color]="gradient[0]" />
 | 
					        <stop offset="100%" [attr.stop-color]="gradient[0]" />
 | 
				
			||||||
      </linearGradient>
 | 
					      </linearGradient>
 | 
				
			||||||
 | 
					      <linearGradient id="input-hover-connector-gradient" x1="0%" y1="0%" x2="100%" y2="0%">
 | 
				
			||||||
 | 
					        <stop offset="0%" stop-color="white" />
 | 
				
			||||||
 | 
					        <stop offset="80%" [attr.stop-color]="gradient[0]" />
 | 
				
			||||||
 | 
					      </linearGradient>
 | 
				
			||||||
 | 
					      <linearGradient id="output-hover-connector-gradient" x1="0%" y1="0%" x2="100%" y2="0%">
 | 
				
			||||||
 | 
					        <stop offset="20%" [attr.stop-color]="gradient[0]" />
 | 
				
			||||||
 | 
					        <stop offset="100%" stop-color="white" />
 | 
				
			||||||
 | 
					      </linearGradient>
 | 
				
			||||||
      <linearGradient id="input-highlight-gradient" x1="0%" y1="0%" x2="100%" y2="0%">
 | 
					      <linearGradient id="input-highlight-gradient" x1="0%" y1="0%" x2="100%" y2="0%">
 | 
				
			||||||
      <stop offset="0%" [attr.stop-color]="gradient[0]" />
 | 
					      <stop offset="0%" [attr.stop-color]="gradient[0]" />
 | 
				
			||||||
      <stop offset="2%" [attr.stop-color]="gradient[0]" />
 | 
					      <stop offset="2%" [attr.stop-color]="gradient[0]" />
 | 
				
			||||||
@ -65,6 +81,22 @@
 | 
				
			|||||||
    </defs>
 | 
					    </defs>
 | 
				
			||||||
    <path [attr.d]="middle.path" class="line middle" [style]="middle.style"/>
 | 
					    <path [attr.d]="middle.path" class="line middle" [style]="middle.style"/>
 | 
				
			||||||
    <ng-container *ngFor="let input of inputs; let i = index">
 | 
					    <ng-container *ngFor="let input of inputs; let i = index">
 | 
				
			||||||
 | 
					      <path *ngIf="connectors && !inputData[i].coinbase && !inputData[i].pegin"
 | 
				
			||||||
 | 
					        [attr.d]="input.connectorPath"
 | 
				
			||||||
 | 
					        class="input connector {{input.class}}"
 | 
				
			||||||
 | 
					        [class.highlight]="inputData[i].index === inputIndex"
 | 
				
			||||||
 | 
					        (pointerover)="onHover($event, 'input-connector', i);"
 | 
				
			||||||
 | 
					        (pointerout)="onBlur($event, 'input-connector', i);"
 | 
				
			||||||
 | 
					        (click)="onClick($event, 'input-connector', inputData[i].index);"
 | 
				
			||||||
 | 
					      />
 | 
				
			||||||
 | 
					      <path
 | 
				
			||||||
 | 
					        [attr.d]="input.markerPath"
 | 
				
			||||||
 | 
					        class="input marker-target {{input.class}}"
 | 
				
			||||||
 | 
					        [class.highlight]="inputData[i].index === inputIndex"
 | 
				
			||||||
 | 
					        (pointerover)="onHover($event, 'input', i);"
 | 
				
			||||||
 | 
					        (pointerout)="onBlur($event, 'input', i);"
 | 
				
			||||||
 | 
					        (click)="onClick($event, 'input', inputData[i].index);"
 | 
				
			||||||
 | 
					      />
 | 
				
			||||||
      <path
 | 
					      <path
 | 
				
			||||||
        [attr.d]="input.path"
 | 
					        [attr.d]="input.path"
 | 
				
			||||||
        class="line {{input.class}}"
 | 
					        class="line {{input.class}}"
 | 
				
			||||||
@ -77,6 +109,22 @@
 | 
				
			|||||||
      />
 | 
					      />
 | 
				
			||||||
    </ng-container>
 | 
					    </ng-container>
 | 
				
			||||||
    <ng-container *ngFor="let output of outputs; let i = index">
 | 
					    <ng-container *ngFor="let output of outputs; let i = index">
 | 
				
			||||||
 | 
					      <path *ngIf="connectors && outspends[outputData[i].index]?.spent"
 | 
				
			||||||
 | 
					        [attr.d]="output.connectorPath"
 | 
				
			||||||
 | 
					        class="output connector {{output.class}}"
 | 
				
			||||||
 | 
					        [class.highlight]="outputData[i].index === outputIndex"
 | 
				
			||||||
 | 
					        (pointerover)="onHover($event, 'output-connector', i);"
 | 
				
			||||||
 | 
					        (pointerout)="onBlur($event, 'output-connector', i);"
 | 
				
			||||||
 | 
					        (click)="onClick($event, 'output-connector', outputData[i].index);"
 | 
				
			||||||
 | 
					      />
 | 
				
			||||||
 | 
					      <path
 | 
				
			||||||
 | 
					        [attr.d]="output.markerPath"
 | 
				
			||||||
 | 
					        class="output marker-target {{output.class}}"
 | 
				
			||||||
 | 
					        [class.highlight]="outputData[i].index === outputIndex"
 | 
				
			||||||
 | 
					        (pointerover)="onHover($event, 'output', i);"
 | 
				
			||||||
 | 
					        (pointerout)="onBlur($event, 'output', i);"
 | 
				
			||||||
 | 
					        (click)="onClick($event, 'output', outputData[i].index);"
 | 
				
			||||||
 | 
					      />
 | 
				
			||||||
      <path
 | 
					      <path
 | 
				
			||||||
        [attr.d]="output.path"
 | 
					        [attr.d]="output.path"
 | 
				
			||||||
        class="line {{output.class}}"
 | 
					        class="line {{output.class}}"
 | 
				
			||||||
@ -94,5 +142,6 @@
 | 
				
			|||||||
    *ngIf=[tooltip]
 | 
					    *ngIf=[tooltip]
 | 
				
			||||||
    [line]="hoverLine"
 | 
					    [line]="hoverLine"
 | 
				
			||||||
    [cursorPosition]="tooltipPosition"
 | 
					    [cursorPosition]="tooltipPosition"
 | 
				
			||||||
 | 
					    [isConnector]="hoverConnector"
 | 
				
			||||||
  ></app-tx-bowtie-graph-tooltip>
 | 
					  ></app-tx-bowtie-graph-tooltip>
 | 
				
			||||||
</div>
 | 
					</div>
 | 
				
			||||||
 | 
				
			|||||||
@ -22,19 +22,46 @@
 | 
				
			|||||||
        stroke: url(#output-highlight-gradient);
 | 
					        stroke: url(#output-highlight-gradient);
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    &:hover {
 | 
					  .line:hover, .marker-target:hover + .line {
 | 
				
			||||||
      z-index: 10;
 | 
					    z-index: 10;
 | 
				
			||||||
      cursor: pointer;
 | 
					    cursor: pointer;
 | 
				
			||||||
      &.input {
 | 
					    &.input {
 | 
				
			||||||
        stroke: url(#input-hover-gradient);
 | 
					      stroke: url(#input-hover-gradient);
 | 
				
			||||||
      }
 | 
					    }
 | 
				
			||||||
      &.output {
 | 
					    &.output {
 | 
				
			||||||
        stroke: url(#output-hover-gradient);
 | 
					      stroke: url(#output-hover-gradient);
 | 
				
			||||||
      }
 | 
					    }
 | 
				
			||||||
      &.fee {
 | 
					    &.fee {
 | 
				
			||||||
        stroke: url(#fee-hover-gradient);
 | 
					      stroke: url(#fee-hover-gradient);
 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					
 | 
				
			||||||
 | 
					  .connector {
 | 
				
			||||||
 | 
					    stroke: none;
 | 
				
			||||||
 | 
					    opacity: 0.75;
 | 
				
			||||||
 | 
					    cursor: pointer;
 | 
				
			||||||
 | 
					    &.input {
 | 
				
			||||||
 | 
					      fill: url(#input-connector-gradient);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    &.output {
 | 
				
			||||||
 | 
					      fill: url(#output-connector-gradient);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  .connector:hover {
 | 
				
			||||||
 | 
					    &.input {
 | 
				
			||||||
 | 
					      fill: url(#input-hover-connector-gradient);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    &.output {
 | 
				
			||||||
 | 
					      fill: url(#output-hover-connector-gradient);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  .marker-target {
 | 
				
			||||||
 | 
					    stroke: none;
 | 
				
			||||||
 | 
					    fill: transparent;
 | 
				
			||||||
 | 
					    cursor: pointer;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -11,12 +11,17 @@ interface SvgLine {
 | 
				
			|||||||
  path: string;
 | 
					  path: string;
 | 
				
			||||||
  style: string;
 | 
					  style: string;
 | 
				
			||||||
  class?: string;
 | 
					  class?: string;
 | 
				
			||||||
 | 
					  connectorPath?: string;
 | 
				
			||||||
 | 
					  markerPath?: string;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
interface Xput {
 | 
					interface Xput {
 | 
				
			||||||
  type: 'input' | 'output' | 'fee';
 | 
					  type: 'input' | 'output' | 'fee';
 | 
				
			||||||
  value?: number;
 | 
					  value?: number;
 | 
				
			||||||
  index?: number;
 | 
					  index?: number;
 | 
				
			||||||
 | 
					  txid?: string;
 | 
				
			||||||
 | 
					  vin?: number;
 | 
				
			||||||
 | 
					  vout?: number;
 | 
				
			||||||
  address?: string;
 | 
					  address?: string;
 | 
				
			||||||
  rest?: number;
 | 
					  rest?: number;
 | 
				
			||||||
  coinbase?: boolean;
 | 
					  coinbase?: boolean;
 | 
				
			||||||
@ -40,6 +45,7 @@ export class TxBowtieGraphComponent implements OnInit, OnChanges {
 | 
				
			|||||||
  @Input() minWeight = 2; //
 | 
					  @Input() minWeight = 2; //
 | 
				
			||||||
  @Input() maxStrands = 24; // number of inputs/outputs to keep fully on-screen.
 | 
					  @Input() maxStrands = 24; // number of inputs/outputs to keep fully on-screen.
 | 
				
			||||||
  @Input() tooltip = false;
 | 
					  @Input() tooltip = false;
 | 
				
			||||||
 | 
					  @Input() connectors = false;
 | 
				
			||||||
  @Input() inputIndex: number;
 | 
					  @Input() inputIndex: number;
 | 
				
			||||||
  @Input() outputIndex: number;
 | 
					  @Input() outputIndex: number;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -49,9 +55,12 @@ export class TxBowtieGraphComponent implements OnInit, OnChanges {
 | 
				
			|||||||
  outputs: SvgLine[];
 | 
					  outputs: SvgLine[];
 | 
				
			||||||
  middle: SvgLine;
 | 
					  middle: SvgLine;
 | 
				
			||||||
  midWidth: number;
 | 
					  midWidth: number;
 | 
				
			||||||
 | 
					  txWidth: number;
 | 
				
			||||||
 | 
					  connectorWidth: number;
 | 
				
			||||||
  combinedWeight: number;
 | 
					  combinedWeight: number;
 | 
				
			||||||
  isLiquid: boolean = false;
 | 
					  isLiquid: boolean = false;
 | 
				
			||||||
  hoverLine: Xput | void = null;
 | 
					  hoverLine: Xput | void = null;
 | 
				
			||||||
 | 
					  hoverConnector: boolean = false;
 | 
				
			||||||
  tooltipPosition = { x: 0, y: 0 };
 | 
					  tooltipPosition = { x: 0, y: 0 };
 | 
				
			||||||
  outspends: Outspend[] = [];
 | 
					  outspends: Outspend[] = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -59,16 +68,16 @@ export class TxBowtieGraphComponent implements OnInit, OnChanges {
 | 
				
			|||||||
  refreshOutspends$: ReplaySubject<string> = new ReplaySubject();
 | 
					  refreshOutspends$: ReplaySubject<string> = new ReplaySubject();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  gradientColors = {
 | 
					  gradientColors = {
 | 
				
			||||||
    '': ['#9339f4', '#105fb0'],
 | 
					    '': ['#9339f4', '#105fb0', '#9339f400'],
 | 
				
			||||||
    bisq: ['#9339f4', '#105fb0'],
 | 
					    bisq: ['#9339f4', '#105fb0', '#9339f400'],
 | 
				
			||||||
    // liquid: ['#116761', '#183550'],
 | 
					    // liquid: ['#116761', '#183550'],
 | 
				
			||||||
    liquid: ['#09a197', '#0f62af'],
 | 
					    liquid: ['#09a197', '#0f62af', '#09a19700'],
 | 
				
			||||||
    // 'liquidtestnet': ['#494a4a', '#272e46'],
 | 
					    // 'liquidtestnet': ['#494a4a', '#272e46'],
 | 
				
			||||||
    'liquidtestnet': ['#d2d2d2', '#979797'],
 | 
					    'liquidtestnet': ['#d2d2d2', '#979797', '#d2d2d200'],
 | 
				
			||||||
    // testnet: ['#1d486f', '#183550'],
 | 
					    // testnet: ['#1d486f', '#183550'],
 | 
				
			||||||
    testnet: ['#4edf77', '#10a0af'],
 | 
					    testnet: ['#4edf77', '#10a0af', '#4edf7700'],
 | 
				
			||||||
    // signet: ['#6f1d5d', '#471850'],
 | 
					    // signet: ['#6f1d5d', '#471850'],
 | 
				
			||||||
    signet: ['#d24fc8', '#a84fd2'],
 | 
					    signet: ['#d24fc8', '#a84fd2', '#d24fc800'],
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  gradient: string[] = ['#105fb0', '#105fb0'];
 | 
					  gradient: string[] = ['#105fb0', '#105fb0'];
 | 
				
			||||||
@ -118,7 +127,9 @@ export class TxBowtieGraphComponent implements OnInit, OnChanges {
 | 
				
			|||||||
    this.isLiquid = (this.network === 'liquid' || this.network === 'liquidtestnet');
 | 
					    this.isLiquid = (this.network === 'liquid' || this.network === 'liquidtestnet');
 | 
				
			||||||
    this.gradient = this.gradientColors[this.network];
 | 
					    this.gradient = this.gradientColors[this.network];
 | 
				
			||||||
    this.midWidth = Math.min(10, Math.ceil(this.width / 100));
 | 
					    this.midWidth = Math.min(10, Math.ceil(this.width / 100));
 | 
				
			||||||
    this.combinedWeight = Math.min(this.maxCombinedWeight, Math.floor((this.width - (2 * this.midWidth)) / 6));
 | 
					    this.txWidth = this.connectors ? Math.max(this.width - 200, this.width * 0.8) : this.width - 20;
 | 
				
			||||||
 | 
					    this.combinedWeight = Math.min(this.maxCombinedWeight, Math.floor((this.txWidth - (2 * this.midWidth)) / 6));
 | 
				
			||||||
 | 
					    this.connectorWidth = (this.width - this.txWidth) / 2;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const totalValue = this.calcTotalValue(this.tx);
 | 
					    const totalValue = this.calcTotalValue(this.tx);
 | 
				
			||||||
    let voutWithFee = this.tx.vout.map((v, i) => {
 | 
					    let voutWithFee = this.tx.vout.map((v, i) => {
 | 
				
			||||||
@ -141,6 +152,8 @@ export class TxBowtieGraphComponent implements OnInit, OnChanges {
 | 
				
			|||||||
      return {
 | 
					      return {
 | 
				
			||||||
        type: 'input',
 | 
					        type: 'input',
 | 
				
			||||||
        value: v?.prevout?.value,
 | 
					        value: v?.prevout?.value,
 | 
				
			||||||
 | 
					        txid: v.txid,
 | 
				
			||||||
 | 
					        vout: v.vout,
 | 
				
			||||||
        address: v?.prevout?.scriptpubkey_address || v?.prevout?.scriptpubkey_type?.toUpperCase(),
 | 
					        address: v?.prevout?.scriptpubkey_address || v?.prevout?.scriptpubkey_type?.toUpperCase(),
 | 
				
			||||||
        index: i,
 | 
					        index: i,
 | 
				
			||||||
        coinbase: v?.is_coinbase,
 | 
					        coinbase: v?.is_coinbase,
 | 
				
			||||||
@ -268,7 +281,7 @@ export class TxBowtieGraphComponent implements OnInit, OnChanges {
 | 
				
			|||||||
      // required to prevent this line overlapping its neighbor
 | 
					      // required to prevent this line overlapping its neighbor
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      if (this.tooltip || !xputs[i].rest) {
 | 
					      if (this.tooltip || !xputs[i].rest) {
 | 
				
			||||||
        const w = (this.width - Math.max(lastWeight, line.weight)) / 2; // approximate horizontal width of the curved section of the line
 | 
					        const w = (this.width - Math.max(lastWeight, line.weight) - (2 * this.connectorWidth)) / 2; // approximate horizontal width of the curved section of the line
 | 
				
			||||||
        const y1 = line.outerY;
 | 
					        const y1 = line.outerY;
 | 
				
			||||||
        const y2 = line.innerY;
 | 
					        const y2 = line.innerY;
 | 
				
			||||||
        const t = (lastWeight + line.weight) / 2; // distance between center of this line and center of previous line
 | 
					        const t = (lastWeight + line.weight) / 2; // distance between center of this line and center of previous line
 | 
				
			||||||
@ -308,13 +321,15 @@ export class TxBowtieGraphComponent implements OnInit, OnChanges {
 | 
				
			|||||||
      return {
 | 
					      return {
 | 
				
			||||||
        path: this.makePath(side, line.outerY, line.innerY, line.thickness, line.offset, pad + maxOffset),
 | 
					        path: this.makePath(side, line.outerY, line.innerY, line.thickness, line.offset, pad + maxOffset),
 | 
				
			||||||
        style: this.makeStyle(line.thickness, xputs[i].type),
 | 
					        style: this.makeStyle(line.thickness, xputs[i].type),
 | 
				
			||||||
        class: xputs[i].type
 | 
					        class: xputs[i].type,
 | 
				
			||||||
 | 
					        connectorPath: this.connectors ? this.makeConnectorPath(side, line.outerY, line.innerY, line.thickness): null,
 | 
				
			||||||
 | 
					        markerPath: this.makeMarkerPath(side, line.outerY, line.innerY, line.thickness),
 | 
				
			||||||
      };
 | 
					      };
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  makePath(side: 'in' | 'out', outer: number, inner: number, weight: number, offset: number, pad: number): string {
 | 
					  makePath(side: 'in' | 'out', outer: number, inner: number, weight: number, offset: number, pad: number): string {
 | 
				
			||||||
    const start = (weight * 0.5);
 | 
					    const start = (weight * 0.5) + this.connectorWidth;
 | 
				
			||||||
    const curveStart = Math.max(start + 1, pad - offset);
 | 
					    const curveStart = Math.max(start + 1, pad - offset);
 | 
				
			||||||
    const end =  this.width / 2 - (this.midWidth * 0.9) + 1;
 | 
					    const end =  this.width / 2 - (this.midWidth * 0.9) + 1;
 | 
				
			||||||
    const curveEnd = end - offset - 10;
 | 
					    const curveEnd = end - offset - 10;
 | 
				
			||||||
@ -332,6 +347,40 @@ export class TxBowtieGraphComponent implements OnInit, OnChanges {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  makeConnectorPath(side: 'in' | 'out', y: number, inner, weight: number): string {
 | 
				
			||||||
 | 
					    const halfWidth = weight * 0.5;
 | 
				
			||||||
 | 
					    const offset = 10; //Math.max(2, halfWidth * 0.2);
 | 
				
			||||||
 | 
					    const lineEnd = this.connectorWidth;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // align with for svg horizontal gradient bug correction
 | 
				
			||||||
 | 
					    if (Math.round(y) === Math.round(inner)) {
 | 
				
			||||||
 | 
					      y -= 1;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (side === 'in') {
 | 
				
			||||||
 | 
					      return `M ${lineEnd - offset} ${y - halfWidth} L ${halfWidth + lineEnd - offset} ${y} L ${lineEnd - offset} ${y + halfWidth} L -${10} ${ y + halfWidth} L -${10} ${y - halfWidth}`;
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      return `M ${this.width - halfWidth - lineEnd + offset} ${y - halfWidth} L ${this.width - lineEnd + offset} ${y} L ${this.width - halfWidth - lineEnd + offset} ${y + halfWidth} L ${this.width + 10} ${ y + halfWidth} L ${this.width + 10} ${y - halfWidth}`;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  makeMarkerPath(side: 'in' | 'out', y: number, inner, weight: number): string {
 | 
				
			||||||
 | 
					    const halfWidth = weight * 0.5;
 | 
				
			||||||
 | 
					    const offset = 10; //Math.max(2, halfWidth * 0.2);
 | 
				
			||||||
 | 
					    const lineEnd = this.connectorWidth;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // align with for svg horizontal gradient bug correction
 | 
				
			||||||
 | 
					    if (Math.round(y) === Math.round(inner)) {
 | 
				
			||||||
 | 
					      y -= 1;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (side === 'in') {
 | 
				
			||||||
 | 
					      return `M ${lineEnd - offset} ${y - halfWidth} L ${halfWidth + lineEnd - offset} ${y} L ${lineEnd - offset} ${y + halfWidth} L ${weight + lineEnd} ${ y + halfWidth} L ${weight + lineEnd} ${y - halfWidth}`;
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      return `M ${this.width - halfWidth - lineEnd + offset} ${y - halfWidth} L ${this.width - lineEnd + offset} ${y} L ${this.width - halfWidth - lineEnd + offset} ${y + halfWidth} L ${this.width - halfWidth - lineEnd} ${ y + halfWidth} L ${this.width - halfWidth - lineEnd} ${y - halfWidth}`;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  makeStyle(minWeight, type): string {
 | 
					  makeStyle(minWeight, type): string {
 | 
				
			||||||
    if (type === 'fee') {
 | 
					    if (type === 'fee') {
 | 
				
			||||||
      return `stroke-width: ${minWeight}`;
 | 
					      return `stroke-width: ${minWeight}`;
 | 
				
			||||||
@ -346,26 +395,31 @@ export class TxBowtieGraphComponent implements OnInit, OnChanges {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  onHover(event, side, index): void {
 | 
					  onHover(event, side, index): void {
 | 
				
			||||||
    if (side === 'input') {
 | 
					    if (side.startsWith('input')) {
 | 
				
			||||||
      this.hoverLine = {
 | 
					      this.hoverLine = {
 | 
				
			||||||
        ...this.inputData[index],
 | 
					        ...this.inputData[index],
 | 
				
			||||||
        index
 | 
					        index
 | 
				
			||||||
      };
 | 
					      };
 | 
				
			||||||
 | 
					      this.hoverConnector = (side === 'input-connector');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
      this.hoverLine = {
 | 
					      this.hoverLine = {
 | 
				
			||||||
        ...this.outputData[index]
 | 
					        ...this.outputData[index],
 | 
				
			||||||
 | 
					        ...this.outspends[this.outputData[index].index]
 | 
				
			||||||
      };
 | 
					      };
 | 
				
			||||||
 | 
					      this.hoverConnector = (side === 'output-connector');
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  onBlur(event, side, index): void {
 | 
					  onBlur(event, side, index): void {
 | 
				
			||||||
    this.hoverLine = null;
 | 
					    this.hoverLine = null;
 | 
				
			||||||
 | 
					    this.hoverConnector = false;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  onClick(event, side, index): void {
 | 
					  onClick(event, side, index): void {
 | 
				
			||||||
    if (side === 'input') {
 | 
					    if (side.startsWith('input')) {
 | 
				
			||||||
      const input = this.tx.vin[index];
 | 
					      const input = this.tx.vin[index];
 | 
				
			||||||
      if (input && !input.is_coinbase && !input.is_pegin && input.txid && input.vout != null) {
 | 
					      if (side === 'input-connector' && input && !input.is_coinbase && !input.is_pegin && input.txid && input.vout != null) {
 | 
				
			||||||
        this.router.navigate([this.relativeUrlPipe.transform('/tx'), input.txid], {
 | 
					        this.router.navigate([this.relativeUrlPipe.transform('/tx'), input.txid], {
 | 
				
			||||||
          queryParamsHandling: 'merge',
 | 
					          queryParamsHandling: 'merge',
 | 
				
			||||||
          fragment: (new URLSearchParams({
 | 
					          fragment: (new URLSearchParams({
 | 
				
			||||||
@ -385,7 +439,7 @@ export class TxBowtieGraphComponent implements OnInit, OnChanges {
 | 
				
			|||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
      const output = this.tx.vout[index];
 | 
					      const output = this.tx.vout[index];
 | 
				
			||||||
      const outspend = this.outspends[index];
 | 
					      const outspend = this.outspends[index];
 | 
				
			||||||
      if (output && outspend && outspend.spent && outspend.txid) {
 | 
					      if (side === 'output-connector' && output && outspend && outspend.spent && outspend.txid) {
 | 
				
			||||||
        this.router.navigate([this.relativeUrlPipe.transform('/tx'), outspend.txid], {
 | 
					        this.router.navigate([this.relativeUrlPipe.transform('/tx'), outspend.txid], {
 | 
				
			||||||
          queryParamsHandling: 'merge',
 | 
					          queryParamsHandling: 'merge',
 | 
				
			||||||
          fragment: (new URLSearchParams({
 | 
					          fragment: (new URLSearchParams({
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user