Skip to content
This repository was archived by the owner on May 24, 2021. It is now read-only.

Add coin base unit [draft] #210

Draft
wants to merge 17 commits into
base: master
Choose a base branch
from
3 changes: 3 additions & 0 deletions StratisCore.UI/src/app/shared/BaseUnit.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export class BaseUnit {
constructor(public name: string, public multiple: number, public defaultFormat: string) { }
}
Empty file.
25 changes: 25 additions & 0 deletions StratisCore.UI/src/app/shared/coins/coins.component.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';

import { CoinsComponent } from './coins.component';

describe('CoinsComponent', () => {
let component: CoinsComponent;
let fixture: ComponentFixture<CoinsComponent>;

beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ CoinsComponent ]
})
.compileComponents();
}));

beforeEach(() => {
fixture = TestBed.createComponent(CoinsComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});

it('should create', () => {
expect(component).toBeTruthy();
});
});
43 changes: 43 additions & 0 deletions StratisCore.UI/src/app/shared/coins/coins.component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { Component, OnInit, Input } from '@angular/core';
import { GlobalService } from '../services/global.service';
import { BaseUnit } from "../BaseUnit";
import { Subject } from 'rxjs';

@Component({
selector: 'coins',
template: `
<span>{{ (amount ? amount : 0) | coinNotation:baseUnit.multiple | number:format }}</span><span *ngIf="showUnit"> {{ this.coinUnit | prefixCoinUnit:baseUnit.name }}</span>
`
})
export class CoinsComponent implements OnInit {

@Input()
amount: number = 0;

@Input()
showUnit: boolean = true;

@Input()
format: string = undefined;

baseUnit: BaseUnit;
coinUnit: string;
baseUnitSubscription: any;

constructor(private globalService: GlobalService) {
this.baseUnitSubscription = this.globalService.baseUnit.subscribe(b => {
this.baseUnit = b;
this.format = this.format != undefined ? this.format : b.defaultFormat;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To be safe, I would go with !!this.format ? this.format : b.defaultFormat instead of this.format != undefined ? this.format : b.defaultFormat

});

this.coinUnit = this.globalService.getCoinUnit();
}

ngOnInit() {
}

ngOnDestroy() {
this.baseUnitSubscription.unsubscribe();
}

}
6 changes: 3 additions & 3 deletions StratisCore.UI/src/app/shared/pipes/coin-notation.pipe.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@ export class CoinNotationPipe implements PipeTransform {

private decimalLimit = 8;

transform(value: number): number {
let temp;
transform(value: number, multiple: number = 100000000): number {
let temp;
if (typeof value === 'number') {
temp = value / 100000000;
temp = value / multiple;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You might need to check that multiple != 0 to avoid division by 0

return temp.toFixed(this.decimalLimit);
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { PrefixCoinUnitPipe } from './prefix-coin-unit.pipe';

describe('PrefixCoinUnitPipe', () => {
it('create an instance', () => {
const pipe = new PrefixCoinUnitPipe();
expect(pipe).toBeTruthy();
});
});
15 changes: 15 additions & 0 deletions StratisCore.UI/src/app/shared/pipes/prefix-coin-unit.pipe.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { Pipe, PipeTransform } from '@angular/core';

@Pipe({
name: 'prefixCoinUnit'
})
export class PrefixCoinUnitPipe implements PipeTransform {

transform(value: string, baseUnit: string): any {
if (baseUnit === 'sats') {
return baseUnit;
}

return baseUnit + value;
}
}
38 changes: 38 additions & 0 deletions StratisCore.UI/src/app/shared/services/global.service.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import {Injectable} from "@angular/core";
import { ElectronService } from 'ngx-electron';
import { BehaviorSubject, Observable } from "rxjs";
import { take, map } from "rxjs/operators";
import { BaseUnit } from "../BaseUnit";

@Injectable({
providedIn: 'root'
Expand All @@ -10,6 +13,15 @@ export class GlobalService {
this.setSidechainEnabled();
this.setTestnetEnabled();
this.setApiPort();

// Store by name so we can match the object and populate the settings list correctly.
let storedBaseUnitName = localStorage.getItem('baseUnit');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

storedBaseUnitName doesn't change, use const instead of let


if (storedBaseUnitName) {
let baseUnit = this.baseUnits.find(b => b.name === storedBaseUnitName);
if (baseUnit)
this.baseUnit.next(baseUnit);
}
}

private applicationVersion: string = "1.0.0";
Expand All @@ -25,6 +37,23 @@ export class GlobalService {
private coinUnit: string;
private network: string;

// Base units relative to sats
private baseUnits = [
new BaseUnit('', 100000000, '1.8-8'), // BTC = 100,000,000 sats
new BaseUnit('m', 100000, '1.5-8'), // mBTC = 100,000 sats
new BaseUnit('μ', 100, '1.2-8'), // μBTC = 100 sats
new BaseUnit('sats', 1, '1.0-0') // Defaults are in sats, no decimal places
];

public baseUnit: BehaviorSubject<BaseUnit> = new BehaviorSubject<BaseUnit>(this.baseUnits[0]);

public formattedBaseUnit: Observable<string> = this.baseUnit.pipe(map(baseUnit => {
if (baseUnit.name === 'sats') {
return baseUnit.name;
}

return baseUnit.name + this.coinUnit;
}));

getApplicationVersion() {
return this.applicationVersion;
Expand Down Expand Up @@ -105,4 +134,13 @@ export class GlobalService {
setCoinUnit(coinUnit: string) {
this.coinUnit = coinUnit;
}

setBaseUnit(baseUnit: BaseUnit) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we should probably use property get and set to simplify this:

set baseUnit(value: BaseUnit) {
    localStorage.setItem('baseUnit', baseUnit.name);
    this.baseUnit.next(baseUnit);
}

get baseUnits() {
    return this.baseUnits;
}

localStorage.setItem('baseUnit', baseUnit.name);
this.baseUnit.next(baseUnit);
}

getBaseUnits() {
return this.baseUnits;
}
}
6 changes: 4 additions & 2 deletions StratisCore.UI/src/app/shared/shared.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,13 @@ import { NgxPaginationModule } from 'ngx-pagination';
import { ClipboardModule } from 'ngx-clipboard';
import { ReactiveFormsModule, FormsModule } from '@angular/forms';
import { GenericModalComponent } from './components/generic-modal/generic-modal.component';
import { CoinsComponent } from './coins/coins.component';
import { PrefixCoinUnitPipe } from './pipes/prefix-coin-unit.pipe';

@NgModule({
imports: [ CommonModule ],
declarations: [ CoinNotationPipe, AutoFocusDirective, PasswordValidationDirective, GenericModalComponent ],
exports: [ CommonModule, ReactiveFormsModule, FormsModule, NgbModule, NgxElectronModule, NgxQRCodeModule, NgxPaginationModule, ClipboardModule, GenericModalComponent, CoinNotationPipe, AutoFocusDirective, PasswordValidationDirective ],
declarations: [ CoinNotationPipe, AutoFocusDirective, PasswordValidationDirective, GenericModalComponent, CoinsComponent, PrefixCoinUnitPipe ],
exports: [ CommonModule, ReactiveFormsModule, FormsModule, NgbModule, NgxElectronModule, NgxQRCodeModule, NgxPaginationModule, ClipboardModule, GenericModalComponent, CoinNotationPipe, AutoFocusDirective, PasswordValidationDirective, CoinsComponent, PrefixCoinUnitPipe ],
entryComponents: [ GenericModalComponent ]
})

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@
<li class="nav-item">
<a class="nav-link" [routerLink]="['resync']" routerLinkActive="active">Rescan Wallet</a>
</li>
<li class="nav-item">
<a class="nav-link" [routerLink]="['base-unit']" routerLinkActive="active">Change Base Unit</a>
</li>
</ul>
<!-- /ul -->
</div>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<div class="card p-4">
<div class="card-header">Change Base Unit</div>
<div class="card-body" style="min-height:200px;">
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we move all inline styles into a base-unit.component.scss

<div class="row">
<div class='col-4 blockLabel'>
Base Unit:
</div>
<div class='col blockText'>
<select class="custom-select col-12" [(ngModel)]="selectedBaseUnit" [ngModelOptions]="{standalone: true}" (change)="onBaseUnitChanged()">
<option *ngFor="let baseUnit of baseUnits" [ngValue]="baseUnit">{{ coinUnit | prefixCoinUnit:baseUnit.name }}</option>
</select>
<span style="padding-left: 15px;">1 Strat = <coins [amount]="100000000"></coins></span>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we move all inline styles into a base-unit.component.scss

</div>
</div>
</div>
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';

import { BaseUnitComponent } from './base-unit.component';

describe('BaseUnitComponent', () => {
let component: BaseUnitComponent;
let fixture: ComponentFixture<BaseUnitComponent>;

beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ BaseUnitComponent ]
})
.compileComponents();
}));

beforeEach(() => {
fixture = TestBed.createComponent(BaseUnitComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});

it('should create', () => {
expect(component).toBeTruthy();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { Component, OnInit } from '@angular/core';
import { GlobalService } from '../../../../shared/services/global.service';
import { BaseUnit } from '../../../../shared/BaseUnit';
import { take } from 'rxjs/operators';

@Component({
selector: 'app-base-unit',
templateUrl: './base-unit.component.html',
styleUrls: ['./base-unit.component.css']
})
export class BaseUnitComponent implements OnInit {
coinUnit: string;
baseUnits: BaseUnit[];

constructor(private globalService: GlobalService) {
// Set the initial value
this.globalService.baseUnit.pipe(take(1)).subscribe(b => {
this.selectedBaseUnit = b;
});

this.coinUnit = this.globalService.getCoinUnit();
this.baseUnits = this.globalService.getBaseUnits();
}

selectedBaseUnit: BaseUnit;

ngOnInit() {
}

onBaseUnitChanged() {
this.globalService.setBaseUnit(this.selectedBaseUnit);
}

}
18 changes: 9 additions & 9 deletions StratisCore.UI/src/app/wallet/dashboard/dashboard.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,12 @@
<div class="col-9 text-left">
<h5>Spendable balance</h5>
<p class="lead">
<strong>{{ (confirmedBalance | coinNotation) || (0 | coinNotation) }}</strong>
<small class="text-uppercase"> {{ coinUnit }}</small>
<strong><coins [amount]="confirmedBalance" [showUnit]="false"></coins></strong>
<small> {{ coinUnit }}</small>
</p>
<ul class="list-unstyled">
<!-- <li><strong>{{ (awaitingMaturity | coinNotation) || (0 | coinNotation) }}</strong><em> (staking)</em></li> -->
<li><strong>{{ (unconfirmedBalance | coinNotation) || (0 | coinNotation) }}</strong><em> (unconfirmed)</em></li>
<li><strong><coins [amount]="unconfirmedBalance"></coins></strong><em> (unconfirmed)</em></li>
<!-- <li><strong>805.65900153</strong> <em>(staking)</em></li> -->
</ul>
</div>
Expand Down Expand Up @@ -66,9 +66,9 @@ <h5>Spendable balance</h5>
<th class="text-center" *ngIf="!transaction.transactionConfirmedInBlock"><span class="bubble bg-warning"></span></th>
<th class="text-center" *ngIf="transaction.transactionConfirmedInBlock"><span class="bubble bg-success"></span></th>
<td class="text-uppercase text-left">{{ transaction.transactionType }}</td>
<td class="text-left" *ngIf="transaction.transactionType == 'sent'"><strong>- {{ transaction.transactionAmount + transaction.transactionFee | coinNotation }} {{ coinUnit }}</strong></td>
<td class="text-left" *ngIf="transaction.transactionType == 'received'"><strong>+ {{ transaction.transactionAmount + transaction.transactionFee | coinNotation }} {{ coinUnit }}</strong></td>
<td class="text-left" *ngIf="transaction.transactionType == 'staked'"><strong>+ {{ transaction.transactionAmount + transaction.transactionFee | coinNotation }} {{ coinUnit }}</strong></td>
<td class="text-left" *ngIf="transaction.transactionType == 'sent'"><strong>- <coins [amount]="transaction.transactionAmount + transaction.transactionFee"></coins></strong></td>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should probably move transaction.transactionAmount + transaction.transactionFee into a variable or a property to .ts file, it is used in few places

<td class="text-left" *ngIf="transaction.transactionType == 'received'"><strong>+ <coins [amount]="transaction.transactionAmount + transaction.transactionFee"></coins></strong></td>
<td class="text-left" *ngIf="transaction.transactionType == 'staked'"><strong>+ <coins [amount]="transaction.transactionAmount + transaction.transactionFee"></coins></strong></td>
<td class="text-left">{{ transaction.transactionTimestamp * 1000 | date:'medium' }}</td>
<td class="text-center"><a class="link" (click)="openTransactionDetailDialog(transaction)">details</a></td>
</tr>
Expand Down Expand Up @@ -145,9 +145,9 @@ <h5>Spendable balance</h5>
<ul class="list-unstyled mb-3">
<li *ngIf="isStarting">Waiting for staking to start...</li>
<div *ngIf="!isStarting">
<li>Staking weight: <strong>{{ stakingWeight | coinNotation | number: '1.0-0' }} {{ coinUnit }}</strong></li>
<li>Coins awaiting maturity: <strong>{{ awaitingMaturity | coinNotation | number: '1.0-0' }} {{ coinUnit }}</strong></li>
<li>Network weight: <strong>{{ netStakingWeight | coinNotation | number: '1.0-0' }} {{ coinUnit }}</strong></li>
<li>Staking weight: <strong><coins [amount]="stakingWeight" [format]="'1.0-0'"></coins></strong></li>
<li>Coins awaiting maturity: <strong><coins [amount]="awaitingMaturity" [format]="'1.0-0'"></coins></strong></li>
<li>Network weight: <strong><coins [amount]="netStakingWeight" [format]="'1.0-0'"></coins></strong></li>
<li class="mt-2">Expected reward time is:</li>
<li><strong>{{ dateTime }}</strong></li>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,11 @@ export class DashboardComponent implements OnInit, OnDestroy {
ngOnInit() {
this.sidechainEnabled = this.globalService.getSidechainEnabled();
this.walletName = this.globalService.getWalletName();
this.coinUnit = this.globalService.getCoinUnit();

this.globalService.formattedBaseUnit
.pipe(takeUntil(this.destroyed$))
.subscribe(baseUnit => this.coinUnit = baseUnit);

this.refreshData();
};

Expand Down
6 changes: 3 additions & 3 deletions StratisCore.UI/src/app/wallet/history/history.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@
<th class="text-center" *ngIf="!transaction.transactionConfirmedInBlock"><span class="bubble bg-warning"></span></th>
<th class="text-center" *ngIf="transaction.transactionConfirmedInBlock"><span class="bubble bg-success"></span></th>
<td class="text-uppercase text-left">{{ transaction.transactionType }}</td>
<td class="text-left" *ngIf="transaction.transactionType == 'sent'"><strong>- {{ transaction.transactionAmount + transaction.transactionFee | coinNotation }} {{ coinUnit }}</strong></td>
<td class="text-left" *ngIf="transaction.transactionType == 'received'"><strong>+ {{ transaction.transactionAmount + transaction.transactionFee | coinNotation }} {{ coinUnit }}</strong></td>
<td class="text-left" *ngIf="transaction.transactionType == 'staked'"><strong>+ {{ transaction.transactionAmount + transaction.transactionFee | coinNotation }} {{ coinUnit }}</strong></td>
<td class="text-left" *ngIf="transaction.transactionType == 'sent'"><strong>- <coins [amount]="transaction.transactionAmount + transaction.transactionFee"></coins></strong></td>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should probably move transaction.transactionAmount + transaction.transactionFee into a variable or a property to .ts file, it is used in few places

<td class="text-left" *ngIf="transaction.transactionType == 'received'"><strong>+ <coins [amount]="transaction.transactionAmount + transaction.transactionFee"></coins></strong></td>
<td class="text-left" *ngIf="transaction.transactionType == 'staked'"><strong>+ <coins [amount]="transaction.transactionAmount + transaction.transactionFee"></coins></strong></td>
<td class="text-left">{{ transaction.transactionTimestamp * 1000 | date:'medium' }}</td>
<td class="text-center"><a class="link" (click)="openTransactionDetailDialog(transaction)">details</a></td>
</tr>
Expand Down
10 changes: 5 additions & 5 deletions StratisCore.UI/src/app/wallet/send/send.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ <h5 class="modal-title">Send to</h5>
<!--<div class="form-group has-danger clearfix">-->
<div class="form-group clearfix">
<label class="float-left" for="amount">Amount</label>
<label class="float-right" for="totalAmount">Spendable: {{ spendableBalance | coinNotation }} {{ coinUnit }}</label>
<label class="float-right" for="totalAmount">Spendable: <coins [amount]="spendableBalance"></coins></label>
<!-- <span class="float-right btn-link" (click)="getMaxBalance()">max</span> -->
<input type="text" class="form-control" [class.is-invalid]="sendFormErrors.amount" [class.is-valid]="!sendFormErrors.amount && sendForm.get('amount').valid" formControlName="amount" placeholder="0.00 {{ coinUnit }}">
<div *ngIf="sendFormErrors.amount" class="invalid-feedback">{{ sendFormErrors.amount }}</div>
Expand All @@ -33,7 +33,7 @@ <h5 class="modal-title">Send to</h5>
<label>Transaction Fee</label>
<div>
<small *ngIf="!estimatedFee" class="blockText text-danger">Please enter a valid amount and destination address to calculate the fee.</small>
<label *ngIf="estimatedFee" class="blockText text-danger">{{ estimatedFee | coinNotation }} {{ coinUnit }}</label>
<label *ngIf="estimatedFee" class="blockText text-danger"><coins [amount]="estimatedFee"></coins></label>
</div>
<!-- fee buttons -->
<!-- <div class="col row">
Expand Down Expand Up @@ -86,7 +86,7 @@ <h5 class="modal-title">Send to</h5>
<!--<div class="form-group has-danger clearfix">-->
<div class="form-group clearfix">
<label class="float-left" for="amount">Amount</label>
<label class="float-right" for="totalAmount">Available: {{ totalBalance | coinNotation }} {{ coinUnit }}</label>
<label class="float-right" for="totalAmount">Available: <coins [amount]="totalBalance"></coins></label>
<!-- <span class="float-right btn-link" (click)="getMaxBalance()">max</span> -->
<input type="text" class="form-control" [class.is-invalid]="sendToSidechainFormErrors.amount" [class.is-valid]="!sendToSidechainFormErrors.amount && sendToSidechainForm.get('amount').valid" formControlName="amount" placeholder="0.00 {{ coinUnit }}">
<div *ngIf="sendToSidechainFormErrors.amount" class="invalid-feedback">{{ sendToSidechainFormErrors.amount }}</div>
Expand All @@ -107,7 +107,7 @@ <h5 class="modal-title">Send to</h5>
<label>Transaction Fee</label>
<div>
<small *ngIf="!estimatedSidechainFee" class="blockText text-danger">Please enter a valid amount, federation and destination address to calculate the fee.</small>
<label *ngIf="estimatedSidechainFee" class="blockText text-danger">{{ estimatedSidechainFee | coinNotation }} {{ coinUnit }}</label>
<label *ngIf="estimatedSidechainFee" class="blockText text-danger"><coins [amount]="estimatedSidechainFee"></coins></label>
</div>
<!-- fee buttons -->
<!-- <div class="col row">
Expand All @@ -132,7 +132,7 @@ <h5 class="modal-title">Send to</h5>
</div>
<div class="form-group clearfix">
<div>
<label class="blockText text-danger">An additional amount of {{ opReturnAmount | coinNotation }} {{ coinUnit }} will be used to send this transaction.</label>
<label class="blockText text-danger">An additional amount of <coins [amount]="opReturnAmount"></coins> will be used to send this transaction.</label>
</div>
</div>
<div class="form-group clearfix">
Expand Down
Loading