Merge pull request #4035 from mempool/mononaut/lightning-balance-bars
Lightning channel balance progress bars
This commit is contained in:
		
						commit
						94d1eae6da
					
				@ -1,19 +1,43 @@
 | 
			
		||||
<div class="box">
 | 
			
		||||
  <table class="table table-borderless table-striped">
 | 
			
		||||
    <tbody>
 | 
			
		||||
      <tr></tr>
 | 
			
		||||
      <tr>
 | 
			
		||||
        <td i18n="lightning.starting-balance|Channel starting balance">Starting balance</td>
 | 
			
		||||
        <td *ngIf="showStartingBalance && minStartingBalance === maxStartingBalance"><app-sats [satoshis]="minStartingBalance"></app-sats></td>
 | 
			
		||||
        <td *ngIf="showStartingBalance && minStartingBalance !== maxStartingBalance">{{ minStartingBalance | number : '1.0-0' }} - {{ maxStartingBalance | number : '1.0-0' }}<app-sats [valueOverride]=" "></app-sats></td>
 | 
			
		||||
        <td *ngIf="!showStartingBalance">?</td>
 | 
			
		||||
      </tr>
 | 
			
		||||
      <tr *ngIf="channel.status === 2">
 | 
			
		||||
        <td i18n="lightning.closing-balance|Channel closing balance">Closing balance</td>
 | 
			
		||||
        <td *ngIf="showClosingBalance && minClosingBalance === maxClosingBalance"><app-sats [satoshis]="minClosingBalance"></app-sats></td>
 | 
			
		||||
        <td *ngIf="showClosingBalance && minClosingBalance !== maxClosingBalance">{{ minClosingBalance | number : '1.0-0' }} - {{ maxClosingBalance | number : '1.0-0' }}<app-sats [valueOverride]=" "></app-sats></td>
 | 
			
		||||
        <td *ngIf="!showClosingBalance">?</td>
 | 
			
		||||
      </tr>
 | 
			
		||||
    </tbody>
 | 
			
		||||
  </table>
 | 
			
		||||
  <div class="starting-balance" *ngIf="showStartingBalance">
 | 
			
		||||
    <h5 i18n="lightning.starting-balance|Channel starting balance">Starting balance</h5>
 | 
			
		||||
    <div class="nodes">
 | 
			
		||||
      <h5 class="alias">{{ left.alias }}</h5>
 | 
			
		||||
      <h5 class="alias">{{ right.alias }}</h5>
 | 
			
		||||
    </div>
 | 
			
		||||
    <div class="balances">
 | 
			
		||||
      <div class="balance left">
 | 
			
		||||
        <span class="value" *ngIf="minStartingBalance !== maxStartingBalance">{{ minStartingBalance | number : '1.0-0' }} - {{ maxStartingBalance | number : '1.0-0' }}<app-sats [valueOverride]=" "></app-sats></span>
 | 
			
		||||
        <span class="value" *ngIf="minStartingBalance === maxStartingBalance">{{ minStartingBalance | number : '1.0-0' }}<app-sats [valueOverride]=" "></app-sats></span>
 | 
			
		||||
      </div>
 | 
			
		||||
      <div class="balance right">
 | 
			
		||||
        <span class="value" *ngIf="minStartingBalance !== maxStartingBalance">{{ channel.capacity - maxStartingBalance | number : '1.0-0' }} - {{ channel.capacity - minStartingBalance | number : '1.0-0' }}<app-sats [valueOverride]=" "></app-sats></span>
 | 
			
		||||
        <span class="value" *ngIf="minStartingBalance === maxStartingBalance">{{ channel.capacity - maxStartingBalance | number : '1.0-0' }}<app-sats [valueOverride]=" "></app-sats></span>
 | 
			
		||||
      </div>
 | 
			
		||||
    </div>
 | 
			
		||||
    <div class="balance-bar">
 | 
			
		||||
      <div class="bar left" [class.hide-value]="hideStartingLeft" [style]="startingBalanceStyle.left"></div>
 | 
			
		||||
      <div class="bar center" [style]="startingBalanceStyle.center"></div>
 | 
			
		||||
      <div class="bar right"  [class.hide-value]="hideStartingRight" [style]="startingBalanceStyle.right"></div>
 | 
			
		||||
    </div>
 | 
			
		||||
  </div>
 | 
			
		||||
  <br>
 | 
			
		||||
  <div class="closing-balance" *ngIf="showClosingBalance">
 | 
			
		||||
    <h5 i18n="lightning.closing-balance|Channel closing balance">Closing balance</h5>
 | 
			
		||||
    <div class="balances">
 | 
			
		||||
      <div class="balance left">
 | 
			
		||||
        <span class="value" *ngIf="minClosingBalance !== maxClosingBalance">{{ minClosingBalance | number : '1.0-0' }} - {{ maxClosingBalance | number : '1.0-0' }}<app-sats [valueOverride]=" "></app-sats></span>
 | 
			
		||||
        <span class="value" *ngIf="minClosingBalance === maxClosingBalance">{{ minClosingBalance | number : '1.0-0' }}<app-sats [valueOverride]=" "></app-sats></span>
 | 
			
		||||
      </div>
 | 
			
		||||
      <div class="balance right">
 | 
			
		||||
        <span class="value" *ngIf="minClosingBalance !== maxClosingBalance">{{ channel.capacity - maxClosingBalance | number : '1.0-0' }} - {{ channel.capacity - minClosingBalance | number : '1.0-0' }}<app-sats [valueOverride]=" "></app-sats></span>
 | 
			
		||||
        <span class="value" *ngIf="minClosingBalance === maxClosingBalance">{{ channel.capacity - maxClosingBalance | number : '1.0-0' }}<app-sats [valueOverride]=" "></app-sats></span>
 | 
			
		||||
      </div>
 | 
			
		||||
    </div>
 | 
			
		||||
    <div class="balance-bar">
 | 
			
		||||
      <div class="bar left" [class.hide-value]="hideClosingLeft" [style]="closingBalanceStyle.left"></div>
 | 
			
		||||
      <div class="bar center" [style]="closingBalanceStyle.center"></div>
 | 
			
		||||
      <div class="bar right" [class.hide-value]="hideClosingRight" [style]="closingBalanceStyle.right"></div>
 | 
			
		||||
    </div>
 | 
			
		||||
  </div>
 | 
			
		||||
</div>
 | 
			
		||||
@ -6,4 +6,98 @@
 | 
			
		||||
  .box {
 | 
			
		||||
    margin-bottom: 20px;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.starting-balance, .closing-balance {
 | 
			
		||||
  width: 100%;
 | 
			
		||||
 | 
			
		||||
  h5 {
 | 
			
		||||
    text-align: center;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.nodes {
 | 
			
		||||
  display: none;
 | 
			
		||||
  flex-direction: row;
 | 
			
		||||
  align-items: baseline;
 | 
			
		||||
  justify-content: space-between;
 | 
			
		||||
 | 
			
		||||
  @media (max-width: 768px) {
 | 
			
		||||
    display: flex;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.balances {
 | 
			
		||||
  display: flex;
 | 
			
		||||
  flex-direction: row;
 | 
			
		||||
  align-items: baseline;
 | 
			
		||||
  justify-content: space-between;
 | 
			
		||||
  margin-bottom: 8px;
 | 
			
		||||
 | 
			
		||||
  .balance {
 | 
			
		||||
    &.left {
 | 
			
		||||
      text-align: start;
 | 
			
		||||
    }
 | 
			
		||||
    &.right {
 | 
			
		||||
      text-align: end;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.balance-bar {
 | 
			
		||||
  width: 100%;
 | 
			
		||||
  height: 2em;
 | 
			
		||||
  position: relative;
 | 
			
		||||
 | 
			
		||||
  .bar {
 | 
			
		||||
    position: absolute;
 | 
			
		||||
    top: 0;
 | 
			
		||||
    bottom: 0;
 | 
			
		||||
    height: 100%;
 | 
			
		||||
    display: flex;
 | 
			
		||||
    flex-direction: row;
 | 
			
		||||
    align-items: center;
 | 
			
		||||
    justify-content: center;
 | 
			
		||||
 | 
			
		||||
    &.left {
 | 
			
		||||
      background: #105fb0;
 | 
			
		||||
    }
 | 
			
		||||
    &.center {
 | 
			
		||||
      background: repeating-linear-gradient(
 | 
			
		||||
        60deg,
 | 
			
		||||
        #105fb0 0,
 | 
			
		||||
        #105fb0 12px,
 | 
			
		||||
        #1a9436 12px,
 | 
			
		||||
        #1a9436 24px
 | 
			
		||||
      );
 | 
			
		||||
    }
 | 
			
		||||
    &.right {
 | 
			
		||||
      background: #1a9436;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .value {
 | 
			
		||||
      flex: 0;
 | 
			
		||||
      white-space: nowrap;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    &.hide-value {
 | 
			
		||||
      .value {
 | 
			
		||||
        display: none;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @media (max-width: 768px) {
 | 
			
		||||
    height: 1em;
 | 
			
		||||
 | 
			
		||||
    .bar.center {
 | 
			
		||||
      background: repeating-linear-gradient(
 | 
			
		||||
        60deg,
 | 
			
		||||
        #105fb0 0,
 | 
			
		||||
        #105fb0 8px,
 | 
			
		||||
        #1a9436 8px,
 | 
			
		||||
        #1a9436 16px
 | 
			
		||||
      )
 | 
			
		||||
    }
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@ -8,8 +8,8 @@ import { ChangeDetectionStrategy, Component, Input, OnChanges, SimpleChanges } f
 | 
			
		||||
})
 | 
			
		||||
export class ChannelCloseBoxComponent implements OnChanges {
 | 
			
		||||
  @Input() channel: any;
 | 
			
		||||
  @Input() local: any;
 | 
			
		||||
  @Input() remote: any;
 | 
			
		||||
  @Input() left: any;
 | 
			
		||||
  @Input() right: any;
 | 
			
		||||
 | 
			
		||||
  showStartingBalance: boolean = false;
 | 
			
		||||
  showClosingBalance: boolean = false;
 | 
			
		||||
@ -18,29 +18,55 @@ export class ChannelCloseBoxComponent implements OnChanges {
 | 
			
		||||
  minClosingBalance: number;
 | 
			
		||||
  maxClosingBalance: number;
 | 
			
		||||
 | 
			
		||||
  startingBalanceStyle: {
 | 
			
		||||
    left: string,
 | 
			
		||||
    center: string,
 | 
			
		||||
    right: string,
 | 
			
		||||
  } = {
 | 
			
		||||
    left: '',
 | 
			
		||||
    center: '',
 | 
			
		||||
    right: '',
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  closingBalanceStyle: {
 | 
			
		||||
    left: string,
 | 
			
		||||
    center: string,
 | 
			
		||||
    right: string,
 | 
			
		||||
  } = {
 | 
			
		||||
    left: '',
 | 
			
		||||
    center: '',
 | 
			
		||||
    right: '',
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  hideStartingLeft: boolean = false;
 | 
			
		||||
  hideStartingRight: boolean = false;
 | 
			
		||||
  hideClosingLeft: boolean = false;
 | 
			
		||||
  hideClosingRight: boolean = false;
 | 
			
		||||
 | 
			
		||||
  constructor() { }
 | 
			
		||||
 | 
			
		||||
  ngOnChanges(changes: SimpleChanges): void {
 | 
			
		||||
    if (this.channel && this.local && this.remote) {
 | 
			
		||||
      this.showStartingBalance = (this.local.funding_balance || this.remote.funding_balance) && this.channel.funding_ratio;
 | 
			
		||||
      this.showClosingBalance = this.local.closing_balance || this.remote.closing_balance;
 | 
			
		||||
    let closingCapacity;
 | 
			
		||||
    if (this.channel && this.left && this.right) {
 | 
			
		||||
      this.showStartingBalance = (this.left.funding_balance || this.right.funding_balance) && this.channel.funding_ratio;
 | 
			
		||||
      this.showClosingBalance = this.left.closing_balance || this.right.closing_balance;
 | 
			
		||||
 | 
			
		||||
      if (this.channel.single_funded) {
 | 
			
		||||
        if (this.local.funding_balance) {
 | 
			
		||||
        if (this.left.funding_balance) {
 | 
			
		||||
          this.minStartingBalance = this.channel.capacity;
 | 
			
		||||
          this.maxStartingBalance = this.channel.capacity;
 | 
			
		||||
        } else if (this.remote.funding_balance) {
 | 
			
		||||
        } else if (this.right.funding_balance) {
 | 
			
		||||
          this.minStartingBalance = 0;
 | 
			
		||||
          this.maxStartingBalance = 0;
 | 
			
		||||
        }
 | 
			
		||||
      } else {
 | 
			
		||||
        this.minStartingBalance = clampRound(0, this.channel.capacity, this.local.funding_balance * this.channel.funding_ratio);
 | 
			
		||||
        this.maxStartingBalance = clampRound(0, this.channel.capacity, this.channel.capacity - (this.remote.funding_balance * this.channel.funding_ratio));
 | 
			
		||||
        this.minStartingBalance = clampRound(0, this.channel.capacity, this.left.funding_balance * this.channel.funding_ratio);
 | 
			
		||||
        this.maxStartingBalance = clampRound(0, this.channel.capacity, this.channel.capacity - (this.right.funding_balance * this.channel.funding_ratio));
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      const closingCapacity = this.channel.capacity - this.channel.closing_fee;
 | 
			
		||||
      this.minClosingBalance = clampRound(0, closingCapacity, this.local.closing_balance);
 | 
			
		||||
      this.maxClosingBalance = clampRound(0, closingCapacity, closingCapacity - this.remote.closing_balance);
 | 
			
		||||
      closingCapacity = this.channel.capacity - this.channel.closing_fee;
 | 
			
		||||
      this.minClosingBalance = clampRound(0, closingCapacity, this.left.closing_balance);
 | 
			
		||||
      this.maxClosingBalance = clampRound(0, closingCapacity, closingCapacity - this.right.closing_balance);
 | 
			
		||||
 | 
			
		||||
      // margin of error to account for 2 x 330 sat anchor outputs
 | 
			
		||||
      if (Math.abs(this.minClosingBalance - this.maxClosingBalance) <= 660) {
 | 
			
		||||
@ -50,6 +76,26 @@ export class ChannelCloseBoxComponent implements OnChanges {
 | 
			
		||||
      this.showStartingBalance = false;
 | 
			
		||||
      this.showClosingBalance = false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const startingMinPc = (this.minStartingBalance / this.channel.capacity) * 100;
 | 
			
		||||
    const startingMaxPc = (this.maxStartingBalance / this.channel.capacity) * 100;
 | 
			
		||||
    this.startingBalanceStyle = {
 | 
			
		||||
      left: `left: 0%; right: ${100 - startingMinPc}%;`,
 | 
			
		||||
      center: `left: ${startingMinPc}%; right: ${100 -startingMaxPc}%;`,
 | 
			
		||||
      right: `left: ${startingMaxPc}%; right: 0%;`,
 | 
			
		||||
    };
 | 
			
		||||
    this.hideStartingLeft = startingMinPc < 15;
 | 
			
		||||
    this.hideStartingRight = startingMaxPc > 85;
 | 
			
		||||
 | 
			
		||||
    const closingMinPc = (this.minClosingBalance / closingCapacity) * 100;
 | 
			
		||||
    const closingMaxPc = (this.maxClosingBalance / closingCapacity) * 100;
 | 
			
		||||
    this.closingBalanceStyle = {
 | 
			
		||||
      left: `left: 0%; right: ${100 - closingMinPc}%;`,
 | 
			
		||||
      center: `left: ${closingMinPc}%; right: ${100 - closingMaxPc}%;`,
 | 
			
		||||
      right: `left: ${closingMaxPc}%; right: 0%;`,
 | 
			
		||||
    };
 | 
			
		||||
    this.hideClosingLeft = closingMinPc < 15;
 | 
			
		||||
    this.hideClosingRight = closingMaxPc > 85;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -75,14 +75,14 @@
 | 
			
		||||
  <div class="row row-cols-1 row-cols-md-2" *ngIf="!error">
 | 
			
		||||
    <div class="col">
 | 
			
		||||
      <app-channel-box [channel]="channel.node_left"></app-channel-box>
 | 
			
		||||
      <app-channel-close-box *ngIf="showCloseBoxes(channel)" [channel]="channel" [local]="channel.node_left" [remote]="channel.node_right"></app-channel-close-box>
 | 
			
		||||
    </div>
 | 
			
		||||
    <div class="col">
 | 
			
		||||
      <app-channel-box [channel]="channel.node_right"></app-channel-box>
 | 
			
		||||
      <app-channel-close-box *ngIf="showCloseBoxes(channel)" [channel]="channel" [local]="channel.node_right" [remote]="channel.node_left"></app-channel-close-box>
 | 
			
		||||
    </div>
 | 
			
		||||
  </div>
 | 
			
		||||
 | 
			
		||||
  <app-channel-close-box *ngIf="showCloseBoxes(channel)" [channel]="channel" [left]="channel.node_left" [right]="channel.node_right"></app-channel-close-box>
 | 
			
		||||
 | 
			
		||||
  <br>
 | 
			
		||||
 | 
			
		||||
  <ng-container *ngIf="transactions$ | async as transactions">
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user