Merge pull request #787 from SonicWizard/ionic

Add Address and Transaction pages; Refactor TransactionsComponent to TransactionListComponent
This commit is contained in:
Jason Dreyzehner 2017-08-10 15:05:11 -04:00 committed by GitHub
commit f35d7dd960
40 changed files with 1155 additions and 307 deletions

View File

@ -28,6 +28,7 @@
"@ionic-native/splash-screen": "3.10.2",
"@ionic-native/status-bar": "3.10.2",
"@ionic/storage": "2.0.1",
"angular2-qrcode": "^2.0.1",
"ionic-angular": "3.4.2",
"ionicons": "3.0.0",
"rxjs": "5.4.0",
@ -37,12 +38,12 @@
"devDependencies": {
"@angular/cli": "1.1.2",
"@ionic/app-scripts": "1.3.7",
"@ionic/cli-plugin-cordova": "1.5.0",
"@ionic/cli-plugin-ionic-angular": "1.4.0",
"@ionic/cli-plugin-cordova": "1.6.2",
"@ionic/cli-plugin-ionic-angular": "1.4.1",
"@types/jasmine": "2.5.41",
"@types/node": "7.0.4",
"codecov": "2.2.0",
"ionic": "3.6.0",
"ionic": "3.7.0",
"jasmine-core": "2.5.2",
"jasmine-spec-reporter": "3.2.0",
"karma": "1.4.1",

View File

@ -7,6 +7,7 @@ import { SplashScreen } from '@ionic-native/splash-screen';
import { InsightApp } from './app.component';
import { PagesModule, BlocksPage, BroadcastTxPage, NodeStatusPage, VerifyMessagePage } from '../pages';
import { BlocksService, StorageService } from '../services';
import { ApiProvider } from '../providers/api/api';
@NgModule({
declarations: [
@ -31,7 +32,8 @@ import { BlocksService, StorageService } from '../services';
SplashScreen,
StorageService,
BlocksService,
{provide: ErrorHandler, useClass: IonicErrorHandler}
{provide: ErrorHandler, useClass: IonicErrorHandler},
ApiProvider
]
})

View File

@ -7,7 +7,14 @@
// styles are for the entire app and not just one component.
// Additionally, this file can be also used as an entry point
// to import other Sass files to be included in the output CSS.
//
.ellipsis {
display: block;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
// Shared Sass variables, which can be used to adjust Ionic's
// default Sass variables, belong in "theme/variables.scss".
//

View File

@ -0,0 +1,12 @@
<ion-navbar color="primary">
<button ion-button menuToggle>
<ion-icon name="menu"></ion-icon>
</button>
<ion-title>{{title}}</ion-title>
<ion-buttons end>
<button ion-button icon-only>
<ion-icon name="search"></ion-icon>
</button>
</ion-buttons>
</ion-navbar>
<ion-searchbar (ionInput)="search($event)" placeholder="{{ 'Search for block, transaction or address' }}" [(ngModel)]="q" [debounce]="1000"></ion-searchbar>

View File

@ -0,0 +1,16 @@
import { NgModule } from '@angular/core';
import { IonicModule } from 'ionic-angular';
import { HeadNavComponent } from './head-nav';
@NgModule({
declarations: [
HeadNavComponent,
],
imports: [
IonicModule,
],
exports: [
HeadNavComponent
]
})
export class HeadNavComponentModule {}

View File

@ -0,0 +1,3 @@
head-nav {
}

View File

@ -0,0 +1,101 @@
import { Component } from '@angular/core';
import { Input } from '@angular/core';
import { NavController } from 'ionic-angular';
import { Http } from '@angular/http';
import { ApiProvider } from '../../providers/api/api';
/**
* Generated class for the HeadNavComponent component.
*
* See https://angular.io/docs/ts/latest/api/core/index/ComponentMetadata-class.html
* for more info on Angular Components.
*/
@Component({
selector: 'head-nav',
templateUrl: 'head-nav.html'
})
export class HeadNavComponent {
public loading: boolean;
@Input() public title: string;
public q: string;
public badQuery: boolean = false;
constructor(private navCtrl: NavController, private http: Http, private api: ApiProvider) {
}
private resetSearch(): void {
this.q = '';
this.loading = false;
}
public search(): void {
let apiPrefix: string = this.api.apiPrefix;
this.http.get(apiPrefix + 'block/' + this.q).subscribe(
function (data: any) {
this.resetSearch();
console.log('block', data);
let parsedData: any = JSON.parse(data._body);
this.navCtrl.push('block-detail', {
'blockHash': parsedData.hash
});
}.bind(this),
() => {
this.http.get(apiPrefix + 'tx/' + this.q).subscribe(
function (data: any) {
this.resetSearch();
console.log('tx', data);
let parsedData: any = JSON.parse(data._body);
this.navCtrl.push('transaction', {
'txId': parsedData.txid
});
}.bind(this),
() => {
this.http.get(apiPrefix + 'addr/' + this.q).subscribe(
function (data: any) {
this.resetSearch();
console.log('addr', data);
let parsedData: any = JSON.parse(data._body);
this.navCtrl.push('address', {
'addrStr': parsedData.addrStr
});
}.bind(this),
() => {
this.http.get(apiPrefix + 'block-index/' + this.q).subscribe(
function (data: any): void {
this.resetSearch();
let parsedData: any = JSON.parse(data._body);
this.navCtrl.push('block-detail', {
'blockHash': parsedData.blockHash
});
}.bind(this),
function (): void {
this.loading = false;
this.reportBadQuery();
}.bind(this)
);
}
);
}
);
}
);
}
/* tslint:disable:no-unused-variable */
private reportBadQuery(): void {
this.badQuery = true;
console.log('badQuery', this.badQuery);
setTimeout(
function (): void {
this.badQuery = false;
console.log('badQuery', this.badQuery);
}.bind(this),
2000
);
};
/* tslint:enable:no-unused-variable */
}

View File

@ -0,0 +1,7 @@
<ion-grid>
<ion-row *ngFor="let tx of transactions.txs">
<ion-col col-12>
<transaction [tx]="tx"></transaction>
</ion-col>
</ion-row>
</ion-grid>

View File

@ -0,0 +1,18 @@
import { NgModule } from '@angular/core';
import { IonicModule } from 'ionic-angular';
import { TransactionListComponent } from './transaction-list';
import { TransactionComponentModule } from '../transaction/transaction.module';
@NgModule({
declarations: [
TransactionListComponent
],
imports: [
IonicModule,
TransactionComponentModule
],
exports: [
TransactionListComponent
]
})
export class TransactionListComponentModule {}

View File

@ -0,0 +1,3 @@
transaction-list {
}

View File

@ -0,0 +1,40 @@
import { Component } from '@angular/core';
import { Input } from '@angular/core';
import { Http } from '@angular/http';
import { ApiProvider } from '../../providers/api/api';
/**
* Generated class for the TransactionListComponent component.
*
* See https://angular.io/docs/ts/latest/api/core/index/ComponentMetadata-class.html
* for more info on Angular Components.
*/
@Component({
selector: 'transaction-list',
templateUrl: 'transaction-list.html'
})
export class TransactionListComponent {
public loading: boolean = true;
@Input() public queryType: string;
@Input() public queryValue: string;
public transactions: any = [];
constructor(private http: Http, private api: ApiProvider) {
}
private ngOnInit(): void {
let url: string = this.api.apiPrefix + 'txs?' + this.queryType + '=' + this.queryValue;
this.http.get(url).subscribe(
(data) => {
this.transactions = JSON.parse(data['_body']);
this.loading = false;
},
(err) => {
console.log('err is', err);
this.loading = false;
}
);
}
}

View File

@ -0,0 +1,83 @@
<ion-grid>
<ion-row>
<ion-col col-8>
<div class="ellipsis">
<ion-icon name="add-circle" [hidden]="expanded" (click)="toggleExpanded()"></ion-icon><ion-icon name="remove-circle" [hidden]="!expanded" (click)="toggleExpanded()"></ion-icon> <span><a (click)="goToTx(tx.txid)">{{ tx.txid }}</a></span>
</div>
</ion-col>
<ion-col col-4 text-right>
<div [hidden]="!tx.firstSeenTs">
<span translate>first seen at</span>
<time>{{tx.firstSeenTs * 1000 | date:'medium'}}</time>
</div>
<div [hidden]="!tx.blocktime && tx.firstSeenTs">
<span translate>mined</span>
<time>{{tx.time * 1000 | date:'medium'}}</time>
</div>
</ion-col>
</ion-row>
<ion-row>
<ion-col col-5>
<ion-list [hidden]="!tx.isCoinBase">
<ion-item>
No Inputs (Newly Generated Coins)
</ion-item>
</ion-list>
<ion-list [hidden]="tx.isCoinBase">
<ion-item *ngFor="let vin of tx.vin">
<div>
<p><a (click)="goToAddress(vin.addr)">{{ vin.addr }}</a> <span item-end>{{ vin.value + ' BTC' }}</span></p>
<div [hidden]="!expanded">
<p><b>Confirmations</b> {{vin.confirmations}}</p>
<p><b>scriptSig</b></p>
<div *ngIf="vin.scriptSig">
<div *ngFor="let item of vin.scriptSig.asm | split:' '" class="ellipsis">
<p>{{item}}</p>
</div>
</div>
</div>
</div>
</ion-item>
</ion-list>
</ion-col>
<ion-col col-2 text-center>
<ion-icon name="arrow-forward"></ion-icon>
</ion-col>
<ion-col col-5>
<ion-list>
<ion-item *ngFor="let vout of tx.vout">
<div>
<p><a (click)="goToAddress(getAddress(vout))">{{ getAddress(vout) }}</a></p>
<div [hidden]="!expanded">
<p><b>Type</b> {{vout.scriptPubKey.type}}</p>
<p><b>scriptPubKey</b></p>
<div class="ellipsis">
<span>{{vout.scriptPubKey.asm}}</span>
</div>
</div>
</div>
<div item-end>
{{ vout.value + ' BTC' }}
<span [hidden]="!vout.spentTxId">(S)</span>
<span [hidden]="vout.spentTxId">(U)</span>
</div>
</ion-item>
</ion-list>
</ion-col>
</ion-row>
<ion-row>
<ion-col col-6>
<span [hidden]="tx.isCoinBase">Fee {{ tx.fees + ' BTC' }}</span>
</ion-col>
<ion-col col-6 text-right>
{{ tx.confirmations }} Confirmations
<span class="">{{ tx.valueOut + ' BTC' }}</span>
</ion-col>
</ion-row>
</ion-grid>

View File

@ -0,0 +1,18 @@
import { NgModule } from '@angular/core';
import { IonicModule } from 'ionic-angular';
import { TransactionComponent } from './transaction';
import { SplitPipe } from '../../pipes/split/split';
@NgModule({
declarations: [
TransactionComponent,
SplitPipe
],
imports: [
IonicModule
],
exports: [
TransactionComponent
]
})
export class TransactionComponentModule {}

View File

@ -0,0 +1,11 @@
transaction {
ion-grid {
background-color: #F4F4F4;
border: 1px solid #eee;
border-radius: 2px;
ion-row {
border: 1px solid #eee;
}
}
}

View File

@ -0,0 +1,46 @@
import { Component } from '@angular/core';
import { Input } from '@angular/core';
import { NavController } from 'ionic-angular';
/**
* Generated class for the TransactionComponent component.
*
* See https://angular.io/docs/ts/latest/api/core/index/ComponentMetadata-class.html
* for more info on Angular Components.
*/
@Component({
selector: 'transaction',
templateUrl: 'transaction.html'
})
export class TransactionComponent {
public expanded: boolean = false;
@Input() public tx: any = {};
constructor(private navCtrl: NavController) {
}
public getAddress(vout: any): string {
if (vout.scriptPubKey && vout.scriptPubKey.addresses) {
return vout.scriptPubKey.addresses[0];
} else {
return 'Unparsed address';
}
}
public goToTx(txId: string): void {
this.navCtrl.push('transaction', {
'txId': txId
});
}
public goToAddress(addrStr: string): void {
this.navCtrl.push('address', {
'addrStr': addrStr
});
}
public toggleExpanded(): void {
this.expanded = !this.expanded;
}
}

View File

@ -1,48 +0,0 @@
<div>
<div *ngFor="let tx of transactions.txs" style="border: 1px solid purple">
<p>{{ tx.txid }}</p>
<div [hidden]="!tx.firstSeenTs">
<span translate>first seen at</span>
<time>{{tx.firstSeenTs * 1000 | date:'medium'}}</time>
</div>
<div [hidden]="!tx.blocktime && tx.firstSeenTs">
<span translate>mined</span>
<time>{{tx.time * 1000 | date:'medium'}}</time>
</div>
<div [hidden]="!tx.isCoinBase">
<div *ngFor="let vin of tx.vin">
<div>
<span translate>No Inputs (Newly Generated Coins)</span>
</div>
</div>
<div>
&gt;
</div>
<div *ngFor="let vout of tx.vout">
<div>
<p>{{ getAddress(vout) }} {{ vout.value + ' BTC' }} <span [hidden]="!vout.spentTxId">(S)</span><span [hidden]="vout.spentTxId">(U)</span></p>
</div>
</div>
</div>
<div [hidden]="tx.isCoinBase">
<div *ngFor="let vin of tx.vin">
<div>{{ vin.addr }}</div>
<p>{{ vin.value + ' BTC' }}</p>
</div>
<div>
&gt;
</div>
<div *ngFor="let vout of tx.vout">
<div>
<p>{{ getAddress(vout) }} {{ vout.value + ' BTC' }} <span [hidden]="!vout.spentTxId">(S)</span><span [hidden]="vout.spentTxId">(U)</span></p>
</div>
</div>
</div>
</div>
</div>

View File

@ -1,16 +0,0 @@
import { NgModule } from '@angular/core';
import { IonicModule } from 'ionic-angular';
import { TransactionsComponent } from './transactions';
@NgModule({
declarations: [
TransactionsComponent
],
imports: [
IonicModule
],
exports: [
TransactionsComponent
]
})
export class TransactionsComponentModule {}

View File

@ -1,3 +0,0 @@
transactions {
}

View File

@ -1,50 +0,0 @@
import { Component } from '@angular/core';
import { Input } from '@angular/core';
import { Http } from '@angular/http';
/**
* Generated class for the TransactionsComponent component.
*
* See https://angular.io/docs/ts/latest/api/core/index/ComponentMetadata-class.html
* for more info on Angular Components.
*/
@Component({
selector: 'transactions',
templateUrl: 'transactions.html'
})
export class TransactionsComponent {
public loading: boolean = true;
@Input() public blockHash: string;
public transactions: any = [];
constructor(private http: Http) {
}
private ngOnInit(): void {
let apiPrefix: string = 'http://localhost:3001/insight-api/';
this.http.get(apiPrefix + 'txs?block=' + this.blockHash).subscribe(
(data) => {
this.transactions = JSON.parse(data['_body']);
this.loading = false;
this.transactions.txs.forEach((tx) => {
console.log('tx is', tx);
});
},
(err) => {
console.log('err is', err);
this.loading = false;
}
);
}
public getAddress(vout: any): string {
if (vout.scriptPubKey && vout.scriptPubKey.addresses) {
return vout.scriptPubKey.addresses[0];
} else {
return 'Unparsed address';
}
}
}

View File

@ -0,0 +1,47 @@
<ion-header>
<head-nav [title]="'Address'"></head-nav>
</ion-header>
<ion-content padding>
<h1>Address</h1>
<p class="ellipsis"><b>Address</b> {{ address.addrStr }}</p>
<p>{{ address.balance }} BTC</p>
<h2>Summary</h2>
<ion-list>
<ion-item>
Total Received
<span item-end>
{{ address.totalReceived }} BTC
</span>
</ion-item>
<ion-item>
Total Sent
<span item-end>
{{ address.totalSent }} BTC
</span>
</ion-item>
<ion-item>
Final Balance
<span item-end>
{{ address.balance }} BTC
</span>
</ion-item>
<ion-item>
No. Transactions
<span item-end>
{{ address.txApperances }}
</span>
</ion-item>
</ion-list>
<p text-center>
<qr-code [value]="address.addrStr" [size]="160"></qr-code>
<p>
<h2>Transactions</h2>
<transaction-list [queryType]="'address'" [queryValue]="addrStr"></transaction-list>
</ion-content>

View File

@ -0,0 +1,22 @@
import { NgModule } from '@angular/core';
import { IonicPageModule } from 'ionic-angular';
import { AddressPage } from './address';
import { TransactionListComponentModule } from '../../components/transaction-list/transaction-list.module';
import { HeadNavComponentModule } from '../../components/head-nav/head-nav.module';
import { QRCodeModule } from 'angular2-qrcode';
@NgModule({
declarations: [
AddressPage
],
imports: [
IonicPageModule.forChild(AddressPage),
TransactionListComponentModule,
HeadNavComponentModule,
QRCodeModule
],
exports: [
AddressPage
]
})
export class AddressPageModule {}

View File

@ -0,0 +1,3 @@
page-address {
}

View File

@ -0,0 +1,43 @@
import { Component } from '@angular/core';
import { IonicPage, NavController, NavParams } from 'ionic-angular';
import { Http } from '@angular/http';
import { ApiProvider } from '../../providers/api/api';
/**
* Generated class for the AddressPage page.
*
* See http://ionicframework.com/docs/components/#navigation for more info
* on Ionic pages and navigation.
*/
@IonicPage({
name: 'address',
segment: 'address/:addrStr'
})
@Component({
selector: 'page-address',
templateUrl: 'address.html'
})
export class AddressPage {
public loading: boolean = true;
private addrStr: string;
public address: any = {};
constructor(public navCtrl: NavController, public navParams: NavParams, private http: Http, private api: ApiProvider) {
this.addrStr = navParams.get('addrStr');
}
public ionViewDidLoad(): void {
this.http.get(this.api.apiPrefix + 'addr/' + this.addrStr + '/?noTxList=1').subscribe(
(data) => {
this.address = JSON.parse(data['_body']);
this.loading = false;
},
(err) => {
console.log('err is', err);
this.loading = false;
}
);
}
}

View File

@ -1,41 +1,98 @@
<!--
Generated template for the BlockDetailPage page.
See http://ionicframework.com/docs/components/#navigation for more info on
Ionic pages and navigation.
-->
<ion-header>
<ion-navbar>
<ion-title>Block Detail</ion-title>
</ion-navbar>
<head-nav [title]="'Block'"></head-nav>
</ion-header>
<ion-content padding>
<h1>Block # {{ block.height }}</h1>
<p>BlockHash {{ block.hash }}</p>
<h2>Summary</h2>
<div *ngIf="!loading">
<p>Number of Transactions {{ block.tx.length }}</p>
<p>Height {{ block.height }} <span [hidden]="!block.isMainChain">(Mainchain)</span></p>
<p>Block Reward {{ block.reward + ' BTC' }}</p>
<p>Timestamp {{ block.time * 1000 | date:'medium' }}</p>
<p>Mined by <a href="{{ block.poolInfo.url }}">{{ block.poolInfo.poolName }}</a></p>
<p>Merkle Root {{ block.merkleroot }}</p>
<p>Previous Block <a (click)="goToPreviousBlock()">{{ block.height - 1 }}</a></p>
<p>Difficulty {{ block.difficulty }}</p>
<p>Bits {{ block.bits }}</p>
<p>Size (bytes) {{ block.size }}</p>
<p>Version {{ block.version }}</p>
<p>Nonce {{ block.nonce }}</p>
<p>Next Block <a (click)="goToNextBlock()">{{ block.height + 1 }}</a></p>
<h1>Block #{{ block.height }}</h1>
<p class="ellipsis"><b>BlockHash</b> {{ block.hash }}</p>
<h3>Transactions</h3>
<transactions [blockHash]="block.hash"></transactions>
</div>
<div *ngIf="loading">
<p>Loading...</p>
<h2>Summary</h2>
<ion-list *ngIf="!loading">
<ion-item>
Number of Transactions
<span item-end>
{{ block.tx.length }}
</span>
</ion-item>
<ion-item>
Height
<span item-end>
{{ block.height }} <span [hidden]="!block.isMainChain">(Mainchain)</span>
</span>
</ion-item>
<ion-item>
Block Reward
<span item-end>
{{ block.reward + ' BTC' }}
</span>
</ion-item>
<ion-item>
Timestamp
<span item-end>
{{ block.time * 1000 | date:'medium' }}
</span>
</ion-item>
<ion-item>
Mined by
<span item-end>
<a href="{{ block.poolInfo.url }}">{{ block.poolInfo.poolName }}</a>
</span>
</ion-item>
<ion-item>
Merkle Root
<span item-end class="ellipsis">
{{ block.merkleroot }}
</span>
</ion-item>
<ion-item>
Previous Block
<span item-end>
<a (click)="goToPreviousBlock()">{{ block.height - 1 }}</a>
</span>
</ion-item>
<ion-item>
Difficulty
<span item-end>
{{ block.difficulty }}
</span>
</ion-item>
<ion-item>
Bits
<span item-end>
{{ block.bits }}
</span>
</ion-item>
<ion-item>
Size (bytes)
<span item-end>
{{ block.size }}
</span>
</ion-item>
<ion-item>
Version
<span item-end>
{{ block.version }}
</span>
</ion-item>
<ion-item>
Nonce
<span item-end>
{{ block.nonce }}
</span>
</ion-item>
<ion-item>
Next Block
<span item-end>
<a (click)="goToNextBlock()">{{ block.height + 1 }}</a>
</span>
</ion-item>
</ion-list>
<div *ngIf="!loading">
<h2>Transactions</h2>
<transaction-list [queryType]="'block'" [queryValue]="block.hash"></transaction-list>
</div>
</ion-content>

View File

@ -1,15 +1,17 @@
import { NgModule } from '@angular/core';
import { IonicPageModule } from 'ionic-angular';
import { BlockDetailPage } from './block-detail';
import { TransactionsComponent } from '../../components/transactions/transactions';
import { TransactionListComponentModule } from '../../components/transaction-list/transaction-list.module';
import { HeadNavComponentModule } from '../../components/head-nav/head-nav.module';
@NgModule({
declarations: [
BlockDetailPage,
TransactionsComponent
BlockDetailPage
],
imports: [
IonicPageModule.forChild(BlockDetailPage)
IonicPageModule.forChild(BlockDetailPage),
TransactionListComponentModule,
HeadNavComponentModule
],
exports: [
BlockDetailPage

View File

@ -1,6 +1,7 @@
import { Component } from '@angular/core';
import { IonicPage, NavController, NavParams } from 'ionic-angular';
import { Http } from '@angular/http';
import { ApiProvider } from '../../providers/api/api';
/**
* Generated class for the BlockDetailPage page.
@ -24,12 +25,10 @@ export class BlockDetailPage {
tx: []
};
constructor(public navCtrl: NavController, private http: Http, public navParams: NavParams) {
constructor(public navCtrl: NavController, private http: Http, public navParams: NavParams, private api: ApiProvider) {
this.blockHash = navParams.get('blockHash');
let apiPrefix: string = 'http://localhost:3001/insight-api/';
this.http.get(apiPrefix + 'block/' + this.blockHash).subscribe(
this.http.get(this.api.apiPrefix + 'block/' + this.blockHash).subscribe(
(data) => {
this.block = JSON.parse(data['_body']);
this.loading = false;

View File

@ -1,41 +1,32 @@
<ion-header>
<ion-navbar color="primary">
<button ion-button menuToggle>
<ion-icon name="menu"></ion-icon>
</button>
<ion-title>{{title}}</ion-title>
<ion-buttons end>
<button ion-button icon-only>
<ion-icon name="search"></ion-icon>
</button>
</ion-buttons>
</ion-navbar>
<head-nav [title]="'Blocks'"></head-nav>
</ion-header>
<ion-content padding>
<ion-list>
<ion-grid>
<ion-row>
<ion-searchbar (ionInput)="search($event)" placeholder="{{ 'Search for block, transaction or address' }}" [(ngModel)]="q"></ion-searchbar>
<ion-col><b>Height</b></ion-col>
<ion-col><b>Timestamp</b></ion-col>
<ion-col text-right><b>Transactions</b></ion-col>
<ion-col><b>Mined By</b></ion-col>
<ion-col text-right><b>Size</b></ion-col>
</ion-row>
<ion-item>
<ion-row>
<ion-col>Height</ion-col>
<ion-col>Transactions</ion-col>
<ion-col>Mined By</ion-col>
</ion-row>
</ion-item>
<ion-item *ngFor="let block of blocks | async">
<ion-row>
<ion-col>
#{{block.height}}
</ion-col>
<ion-col>
{{block.transactionCount}} transactions
</ion-col>
<ion-col>
<a *ngIf="block.poolName" href="{{block.url}}">{{block.poolName}}</a>
</ion-col>
</ion-row>
</ion-item>
</ion-list>
<ion-row *ngFor="let block of blocks | async">
<ion-col>
<a (click)="goToBlock(block.hash)">{{block.height}}</a>
</ion-col>
<ion-col>
{{ block.timestamp * 1000 | date:'medium' }}
</ion-col>
<ion-col text-right>
{{block.transactionCount}}
</ion-col>
<ion-col>
<a *ngIf="block.poolName" href="{{block.url}}">{{block.poolName}}</a>
</ion-col>
<ion-col text-right>
{{ block.size }}
</ion-col>
</ion-row>
</ion-grid>
</ion-content>

View File

@ -21,9 +21,11 @@ describe('Blocks', () => {
expect(instance).toBeTruthy();
});
/*
it('has a search method', () => {
spyOn(instance, 'search');
instance.search();
expect(instance.search).toHaveBeenCalled();
});
*/
});

View File

@ -3,7 +3,6 @@ import { NavController } from 'ionic-angular';
import { Observable } from 'rxjs';
import { Block } from '../../models';
import { BlocksService } from '../../services';
import { Http } from '@angular/http';
@Component({
templateUrl: './blocksPage.html'
@ -11,81 +10,22 @@ import { Http } from '@angular/http';
export class BlocksPage {
public loading: boolean;
public title: string;
public blocks: Observable<Block[]>;
public q: string;
public badQuery: boolean = false;
constructor(private navCtrl: NavController, private http: Http, private blocksService: BlocksService) {
constructor(private navCtrl: NavController, private blocksService: BlocksService) {
this.title = 'Blocks';
this.blocks = blocksService.latestBlocks;
// this.blocks.subscribe((blocks) => {
// console.log(blocks);
// });
this.blocks.subscribe((blocks) => {
console.log('blocks', blocks);
});
blocksService.getLatestBlocks();
}
public search(): void {
console.log('q is', this.q);
let apiPrefix: string = 'http://localhost:3001/insight-api/';
this.http.get(apiPrefix + 'block/' + this.q).subscribe(
(data) => {
this.resetSearch();
console.log('block', data);
},
() => {
this.http.get(apiPrefix + 'tx/' + this.q).subscribe(
(data: any) => {
this.resetSearch();
console.log('tx', data);
},
() => {
this.http.get(apiPrefix + 'addr/' + this.q).subscribe(
(data: any) => {
this.resetSearch();
console.log('addr', data);
},
() => {
this.http.get(apiPrefix + 'block-index/' + this.q).subscribe(
function (data: any): void {
this.resetSearch();
let parsedData: any = JSON.parse(data._body);
this.navCtrl.push('block-detail', {
'blockHash': parsedData.blockHash
});
}.bind(this),
function (): void {
this.loading = false;
this.reportBadQuery();
}.bind(this)
);
}
);
}
);
}
);
public goToBlock(blockHash: string): void {
this.navCtrl.push('block-detail', {
'blockHash': blockHash
});
}
private resetSearch(): void {
this.q = '';
this.loading = false;
}
/* tslint:disable:no-unused-variable */
private reportBadQuery(): void {
this.badQuery = true;
console.log('badQuery', this.badQuery);
setTimeout(
function (): void {
this.badQuery = false;
console.log('badQuery', this.badQuery);
}.bind(this),
2000
);
};
/* tslint:enable:no-unused-variable */
}

View File

@ -1,6 +1,7 @@
import { NgModule } from '@angular/core';
import { IonicModule } from 'ionic-angular';
import { ComponentsModule } from '../components';
import { HeadNavComponentModule } from '../components/head-nav/head-nav.module';
import {
BlocksPage,
BroadcastTxPage,
@ -15,7 +16,7 @@ import {
NodeStatusPage,
VerifyMessagePage
],
imports: [ IonicModule, ComponentsModule ],
imports: [ IonicModule, ComponentsModule, HeadNavComponentModule ],
exports: [
// CustomComponent,
],

View File

@ -0,0 +1,42 @@
<ion-header>
<head-nav [title]="'Transaction'"></head-nav>
</ion-header>
<ion-content padding>
<h1>Transaction</h1>
<p class="ellipsis"><b>Transaction</b> {{ tx.txid }}</p>
<h2>Summary</h2>
<ion-list>
<ion-item>
Size
<span item-end>
{{ tx.size }} (bytes)
</span>
</ion-item>
<ion-item>
Received Time
<span item-end>
{{ tx.time * 1000 | date:'medium' }}
</span>
</ion-item>
<ion-item>
Mined Time
<span item-end>
{{ tx.blocktime * 1000 | date:'medium' }}
</span>
</ion-item>
<ion-item>
Included in Block
<span item-end>
<a (click)="goToBlock(tx.blockhash)">{{ tx.blockhash }}</a>
</span>
</ion-item>
</ion-list>
<h2>Details</h2>
<transaction [tx]="tx"></transaction>
</ion-content>

View File

@ -0,0 +1,20 @@
import { NgModule } from '@angular/core';
import { IonicPageModule } from 'ionic-angular';
import { TransactionPage } from './transaction';
import { TransactionComponentModule } from '../../components/transaction/transaction.module';
import { HeadNavComponentModule } from '../../components/head-nav/head-nav.module';
@NgModule({
declarations: [
TransactionPage
],
imports: [
IonicPageModule.forChild(TransactionPage),
TransactionComponentModule,
HeadNavComponentModule
],
exports: [
TransactionPage
]
})
export class TransactionPageModule {}

View File

@ -0,0 +1,3 @@
page-transaction {
}

View File

@ -0,0 +1,48 @@
import { Component } from '@angular/core';
import { IonicPage, NavController, NavParams } from 'ionic-angular';
import { Http } from '@angular/http';
import { ApiProvider } from '../../providers/api/api';
/**
* Generated class for the TransactionPage page.
*
* See http://ionicframework.com/docs/components/#navigation for more info
* on Ionic pages and navigation.
*/
@IonicPage({
name: 'transaction',
segment: 'tx/:txId'
})
@Component({
selector: 'page-transaction',
templateUrl: 'transaction.html'
})
export class TransactionPage {
public loading: boolean = true;
private txId: string;
public tx: any = {};
constructor(public navCtrl: NavController, public navParams: NavParams, private http: Http, private api: ApiProvider) {
this.txId = navParams.get('txId');
}
public ionViewDidLoad(): void {
this.http.get(this.api.apiPrefix + 'tx/' + this.txId).subscribe(
(data) => {
this.tx = JSON.parse(data['_body']);
this.loading = false;
},
(err) => {
console.log('err is', err);
this.loading = false;
}
);
}
public goToBlock(blockHash: string): void {
this.navCtrl.push('block-detail', {
'blockHash': blockHash
});
}
}

View File

@ -0,0 +1,21 @@
import { Pipe, PipeTransform } from '@angular/core';
/**
* Generated class for the SplitPipe pipe.
*
* See https://angular.io/docs/ts/latest/guide/pipes.html for more info on
* Angular Pipes.
*/
@Pipe({
name: 'split'
})
export class SplitPipe implements PipeTransform {
/**
* Takes a value and makes it lowercase.
*/
public transform(value: string, delimiter: string): Array<string> {
let array: Array<string> = value.split(delimiter);
console.log('split is', array);
return array;
}
}

View File

@ -0,0 +1,19 @@
import { Injectable } from '@angular/core';
import { Http } from '@angular/http';
import 'rxjs/add/operator/map';
/*
Generated class for the ApiProvider provider.
See https://angular.io/docs/ts/latest/guide/dependency-injection.html
for more info on providers and Angular DI.
*/
@Injectable()
export class ApiProvider {
public apiPrefix: string = 'http://localhost:3001/insight-api/';
constructor(public http: Http) {
}
}

View File

@ -0,0 +1,78 @@
/* tslint:disable:no-unused-variable */
import { TestBed, ComponentFixture, inject } from '@angular/core/testing';
import { HttpModule } from '@angular/http';
import { CurrencyProvider } from './currency';
describe('CurrencyProvider', () => {
let currency;
beforeEach(() => {
TestBed.configureTestingModule({
imports: [
HttpModule
],
providers: [
CurrencyProvider
]
});
});
beforeEach(inject([CurrencyProvider], _cp => {
currency = _cp;
}));
it('initialises', () => {
expect(currency).not.toBeNull();
expect(currency.defaultCurrency).toBe('BTC');
expect(currency.currencySymbol).toBe('BTC');
expect(currency.factor).toBe(1);
});
it('sets currency by updating the symbol and changing the multiplication factor', () => {
currency.setCurrency('USD');
expect(currency.currencySymbol).toBe('USD');
expect(currency.factor).toEqual(1);
currency.setCurrency('BTC');
expect(currency.currencySymbol).toBe('BTC');
expect(currency.factor).toEqual(1);
currency.setCurrency('mBTC');
expect(currency.currencySymbol).toBe('mBTC');
expect(currency.factor).toEqual(1000);
currency.setCurrency('bits');
expect(currency.currencySymbol).toBe('bits');
expect(currency.factor).toEqual(1000000);
});
it('rounds float using specified number of decimal places', () => {
let aFloat = 4.32943;
expect(currency.roundFloat(aFloat, 2)).toBe(4.33);
expect(currency.roundFloat(aFloat, 3)).toBe(4.329);
expect(currency.roundFloat(aFloat, 4)).toBe(4.3294);
expect(currency.roundFloat(aFloat, 5)).toBe(4.32943);
aFloat = 1234567890.09876543;
expect(currency.roundFloat(aFloat, 2)).toBe(1234567890.10);
expect(currency.roundFloat(aFloat, 3)).toBe(1234567890.099);
expect(currency.roundFloat(aFloat, 4)).toBe(1234567890.0988);
expect(currency.roundFloat(aFloat, 5)).toBe(1234567890.09877);
expect(currency.roundFloat(aFloat, 6)).toBe(1234567890.098765);
expect(currency.roundFloat(aFloat, 7)).toBe(1234567890.0987654);
expect(currency.roundFloat(aFloat, 8)).toBe(1234567890.09876543);
});
it('gets proper conversion after changing currency', () => {
let aFloat = 12345.09876543;
expect(currency.getConversion(aFloat)).toBe('12345.09876543 BTC');
currency.setCurrency('mBTC');
expect(currency.getConversion(aFloat)).toBe('12345098.76543 mBTC');
currency.setCurrency('bits');
expect(currency.getConversion(aFloat)).toBe('12345098765.43 bits');
});
});

View File

@ -0,0 +1,68 @@
import { Injectable } from '@angular/core';
import { Http } from '@angular/http';
import 'rxjs/add/operator/map';
/*
Generated class for the CurrencyProvider provider.
See https://angular.io/docs/ts/latest/guide/dependency-injection.html
for more info on providers and Angular DI.
*/
@Injectable()
export class CurrencyProvider {
private defaultCurrency: string;
private currencySymbol: string;
private factor: number = 1;
constructor(public http: Http) {
this.defaultCurrency = 'BTC';
this.currencySymbol = this.defaultCurrency;
}
private roundFloat(aFloat: number, decimalPlaces: number): number {
return Math.round(aFloat * Math.pow(10, decimalPlaces)) / Math.pow(10, decimalPlaces);
}
public getConversion(value: number): string {
if (value === 0.00000000) return '0 ' + this.currencySymbol; // fix value to show
let response: number;
if (this.currencySymbol === 'USD') {
response = this.roundFloat((value * this.factor), 2);
} else if (this.currencySymbol === 'mBTC') {
this.factor = 1000;
response = this.roundFloat((value * this.factor), 5);
} else if (this.currencySymbol === 'bits') {
this.factor = 1000000;
response = this.roundFloat((value * this.factor), 2);
} else { // assumes currencySymbol is BTC
this.factor = 1;
response = this.roundFloat((value * this.factor), 8);
}
return response + ' ' + this.currencySymbol;
}
public setCurrency(currency: string): void {
this.currencySymbol = currency;
localStorage.setItem('insight-currency', currency);
if (currency === 'USD') {
// TODO Replace this with call
/*
Currency.get({}, function(res) {
$rootScope.currency.factor = $rootScope.currency.bitstamp = res.data.bitstamp;
});
*/
} else if (currency === 'mBTC') {
this.factor = 1000;
} else if (currency === 'bits') {
this.factor = 1000000;
} else {
this.factor = 1;
}
}
}

View File

@ -16,6 +16,8 @@ import { App, Config, Form, IonicModule, Keyboard, DomController, MenuController
import { ConfigMock, PlatformMock } from './mocks';
import { BlocksServiceMock } from './services/mocks';
import { BlocksService } from './services';
import { ApiProvider } from './providers/api/api';
import { HeadNavComponentModule } from './components/head-nav/head-nav.module';
// Unfortunately there's no typing for the `__karma__` variable. Just declare it as any.
declare var __karma__: any;
@ -60,13 +62,15 @@ export class TestUtils {
App, Form, Keyboard, DomController, MenuController, NavController, GestureController,
{provide: Platform, useClass: PlatformMock},
{provide: Config, useClass: ConfigMock},
{provide: BlocksService, useClass: BlocksServiceMock}
{provide: BlocksService, useClass: BlocksServiceMock},
ApiProvider
],
imports: [
FormsModule,
IonicModule,
ReactiveFormsModule,
HttpModule
HttpModule,
HeadNavComponentModule
]
});
}

View File

@ -159,42 +159,46 @@
ws "1.1.1"
xml2js "^0.4.17"
"@ionic/cli-plugin-cordova@1.4.0":
version "1.4.0"
resolved "https://registry.yarnpkg.com/@ionic/cli-plugin-cordova/-/cli-plugin-cordova-1.4.0.tgz#2d3dd7bbd7321e0f1994b6f1a217176f55145296"
"@ionic/cli-plugin-cordova@1.6.2":
version "1.6.2"
resolved "https://registry.yarnpkg.com/@ionic/cli-plugin-cordova/-/cli-plugin-cordova-1.6.2.tgz#f71d10641df8b522f54f8274e0d5bc4dbb8b5208"
dependencies:
"@ionic/cli-utils" "1.4.0"
chalk "^1.1.3"
xml2js "^0.4.17"
"@ionic/cli-utils" "1.7.0"
chalk "^2.0.0"
elementtree "^0.1.7"
tslib "^1.7.1"
"@ionic/cli-plugin-ionic-angular@1.3.1":
version "1.3.1"
resolved "https://registry.yarnpkg.com/@ionic/cli-plugin-ionic-angular/-/cli-plugin-ionic-angular-1.3.1.tgz#8985f5a520db97ed6206615464e173d086ecd1ea"
"@ionic/cli-plugin-ionic-angular@1.4.1":
version "1.4.1"
resolved "https://registry.yarnpkg.com/@ionic/cli-plugin-ionic-angular/-/cli-plugin-ionic-angular-1.4.1.tgz#bb94b7ad61c2f79757b2dff041c0b7e2151777ec"
dependencies:
"@ionic/cli-utils" "1.4.0"
chalk "^1.1.3"
"@ionic/cli-utils" "1.7.0"
chalk "^2.0.0"
tslib "^1.7.1"
"@ionic/cli-utils@1.4.0":
version "1.4.0"
resolved "https://registry.yarnpkg.com/@ionic/cli-utils/-/cli-utils-1.4.0.tgz#78c70b69c3b98a463aa01ea507d7d01da3dfa675"
"@ionic/cli-utils@1.7.0":
version "1.7.0"
resolved "https://registry.yarnpkg.com/@ionic/cli-utils/-/cli-utils-1.7.0.tgz#05bdbf3e7533eeeee27d58f3adbfe26bf2bf0d1d"
dependencies:
archiver "^1.3.0"
chalk "^1.1.3"
cross-spawn "^4.0.2"
archiver "^2.0.0"
chalk "^2.0.0"
ci-info "^1.0.0"
cross-spawn "^5.1.0"
dargs "^5.1.0"
inquirer "^3.0.6"
dev-null "^0.1.1"
inquirer "^3.2.1"
leek "0.0.24"
lodash "^4.17.4"
minimist "^1.2.0"
ncp "^2.0.0"
os-name "^2.0.1"
rimraf "^2.5.4"
semver "^5.3.0"
slice-ansi "0.0.4"
string-width "^2.0.0"
semver "^5.4.1"
slice-ansi "^1.0.0"
string-width "^2.1.1"
strip-ansi "^4.0.0"
superagent "^3.5.2"
tslib "^1.7.1"
uuid "^3.0.1"
wrap-ansi "^2.1.0"
wrap-ansi "^3.0.1"
"@ionic/storage@2.0.1":
version "2.0.1"
@ -331,6 +335,12 @@ amdefine@>=0.0.4:
version "1.0.1"
resolved "https://registry.yarnpkg.com/amdefine/-/amdefine-1.0.1.tgz#4a5282ac164729e93619bcfd3ad151f817ce91f5"
angular2-qrcode@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/angular2-qrcode/-/angular2-qrcode-2.0.1.tgz#1b4e65c302694b5078ca06f71138f7e4367754dc"
dependencies:
qrious "^2.2.0"
ansi-escapes@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-2.0.0.tgz#5bae52be424878dd9783e8910e3fc2922e83c81b"
@ -385,9 +395,9 @@ archiver-utils@^1.3.0:
normalize-path "^2.0.0"
readable-stream "^2.0.0"
archiver@^1.3.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/archiver/-/archiver-1.3.0.tgz#4f2194d6d8f99df3f531e6881f14f15d55faaf22"
archiver@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/archiver/-/archiver-2.0.0.tgz#ffb73ecccd8dd65b0019e1180f78092a053d43c4"
dependencies:
archiver-utils "^1.3.0"
async "^2.0.0"
@ -397,7 +407,7 @@ archiver@^1.3.0:
readable-stream "^2.0.0"
tar-stream "^1.5.0"
walkdir "^0.0.11"
zip-stream "^1.1.0"
zip-stream "^1.2.0"
are-we-there-yet@~1.1.2:
version "1.1.4"
@ -1182,6 +1192,14 @@ caniuse-db@^1.0.30000529, caniuse-db@^1.0.30000618, caniuse-db@^1.0.30000634, ca
version "1.0.30000697"
resolved "https://registry.yarnpkg.com/caniuse-db/-/caniuse-db-1.0.30000697.tgz#20ce6a9ceeef4ef4a15dc8e80f2e8fb9049e8d77"
canvas@^1.6.5:
version "1.6.6"
resolved "https://registry.yarnpkg.com/canvas/-/canvas-1.6.6.tgz#1c7af9e07d003dd082698b4a4e027773281029ca"
dependencies:
nan "^2.4.0"
parse-css-font "^2.0.2"
units-css "^0.4.0"
caseless@~0.11.0:
version "0.11.0"
resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.11.0.tgz#715b96ea9841593cc33067923f5ec60ebda4f7d7"
@ -1207,6 +1225,14 @@ chalk@1.1.3, chalk@^1.0.0, chalk@^1.1.0, chalk@^1.1.1, chalk@^1.1.3:
strip-ansi "^3.0.0"
supports-color "^2.0.0"
chalk@^2.0.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.1.0.tgz#ac5becf14fa21b99c6c92ca7a7d7cfd5b17e743e"
dependencies:
ansi-styles "^3.1.0"
escape-string-regexp "^1.0.5"
supports-color "^4.0.0"
chalk@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.0.1.tgz#dbec49436d2ae15f536114e76d14656cdbc0f44d"
@ -1245,6 +1271,10 @@ chokidar@^1.4.1, chokidar@^1.4.3, chokidar@^1.6.0, chokidar@^1.6.1:
optionalDependencies:
fsevents "^1.0.0"
ci-info@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-1.0.0.tgz#dc5285f2b4e251821683681c381c3388f46ec534"
cipher-base@^1.0.0, cipher-base@^1.0.1, cipher-base@^1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/cipher-base/-/cipher-base-1.0.3.tgz#eeabf194419ce900da3018c207d212f2a6df0a07"
@ -1591,11 +1621,12 @@ cross-spawn@^3.0.0:
lru-cache "^4.0.1"
which "^1.2.9"
cross-spawn@^4.0.2:
version "4.0.2"
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-4.0.2.tgz#7b9247621c23adfdd3856004a823cbe397424d41"
cross-spawn@^5.1.0:
version "5.1.0"
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-5.1.0.tgz#e8bd0efee58fcff6f8f94510a0a554bbfa235449"
dependencies:
lru-cache "^4.0.1"
shebang-command "^1.2.0"
which "^1.2.9"
cryptiles@2.x.x:
@ -1623,6 +1654,32 @@ css-color-names@0.0.4:
version "0.0.4"
resolved "https://registry.yarnpkg.com/css-color-names/-/css-color-names-0.0.4.tgz#808adc2e79cf84738069b646cb20ec27beb629e0"
css-font-size-keywords@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/css-font-size-keywords/-/css-font-size-keywords-1.0.0.tgz#854875ace9aca6a8d2ee0d345a44aae9bb6db6cb"
css-font-stretch-keywords@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/css-font-stretch-keywords/-/css-font-stretch-keywords-1.0.1.tgz#50cee9b9ba031fb5c952d4723139f1e107b54b10"
css-font-style-keywords@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/css-font-style-keywords/-/css-font-style-keywords-1.0.1.tgz#5c3532813f63b4a1de954d13cea86ab4333409e4"
css-font-weight-keywords@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/css-font-weight-keywords/-/css-font-weight-keywords-1.0.0.tgz#9bc04671ac85bc724b574ef5d3ac96b0d604fd97"
css-global-keywords@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/css-global-keywords/-/css-global-keywords-1.0.1.tgz#72a9aea72796d019b1d2a3252de4e5aaa37e4a69"
css-list-helpers@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/css-list-helpers/-/css-list-helpers-1.0.1.tgz#fff57192202db83240c41686f919e449a7024f7d"
dependencies:
tcomb "^2.5.0"
css-loader@^0.28.1:
version "0.28.4"
resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-0.28.4.tgz#6cf3579192ce355e8b38d5f42dd7a1f2ec898d0f"
@ -1663,6 +1720,10 @@ css-selector-tokenizer@^0.7.0:
fastparse "^1.1.1"
regexpu-core "^1.0.0"
css-system-font-keywords@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/css-system-font-keywords/-/css-system-font-keywords-1.0.0.tgz#85c6f086aba4eb32c571a3086affc434b84823ed"
css-what@2.1:
version "2.1.0"
resolved "https://registry.yarnpkg.com/css-what/-/css-what-2.1.0.tgz#9467d032c38cfaefb9f2d79501253062f87fa1bd"
@ -1840,6 +1901,10 @@ detect-node@^2.0.3:
version "2.0.3"
resolved "https://registry.yarnpkg.com/detect-node/-/detect-node-2.0.3.tgz#a2033c09cc8e158d37748fbde7507832bd6ce127"
dev-null@^0.1.1:
version "0.1.1"
resolved "https://registry.yarnpkg.com/dev-null/-/dev-null-0.1.1.tgz#5a205ce3c2b2ef77b6238d6ba179eb74c6a0e818"
di@^0.0.1:
version "0.0.1"
resolved "https://registry.yarnpkg.com/di/-/di-0.0.1.tgz#806649326ceaa7caa3306d75d985ea2748ba913c"
@ -1959,6 +2024,12 @@ electron-to-chromium@^1.2.7:
version "1.3.15"
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.15.tgz#08397934891cbcfaebbd18b82a95b5a481138369"
elementtree@^0.1.7:
version "0.1.7"
resolved "https://registry.yarnpkg.com/elementtree/-/elementtree-0.1.7.tgz#9ac91be6e52fb6e6244c4e54a4ac3ed8ae8e29c0"
dependencies:
sax "1.1.4"
elliptic@^6.0.0:
version "6.4.0"
resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.4.0.tgz#cac9af8762c85836187003c8dfe193e5e2eae5df"
@ -2966,7 +3037,7 @@ inline-process-browser@^1.0.0:
falafel "^1.0.1"
through2 "^0.6.5"
inquirer@^3.0.0, inquirer@^3.0.6:
inquirer@^3.0.0:
version "3.1.1"
resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-3.1.1.tgz#87621c4fba4072f48a8dd71c9f9df6f100b2d534"
dependencies:
@ -2985,6 +3056,25 @@ inquirer@^3.0.0, inquirer@^3.0.6:
strip-ansi "^3.0.0"
through "^2.3.6"
inquirer@^3.2.1:
version "3.2.1"
resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-3.2.1.tgz#06ceb0f540f45ca548c17d6840959878265fa175"
dependencies:
ansi-escapes "^2.0.0"
chalk "^2.0.0"
cli-cursor "^2.1.0"
cli-width "^2.0.0"
external-editor "^2.0.4"
figures "^2.0.0"
lodash "^4.3.0"
mute-stream "0.0.7"
run-async "^2.2.0"
rx-lite "^4.0.8"
rx-lite-aggregates "^4.0.8"
string-width "^2.1.0"
strip-ansi "^4.0.0"
through "^2.3.6"
interpret@^1.0.0:
version "1.0.3"
resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.0.3.tgz#cbc35c62eeee73f19ab7b10a801511401afc0f90"
@ -3003,6 +3093,22 @@ ionic-angular@3.4.2:
version "3.4.2"
resolved "https://registry.yarnpkg.com/ionic-angular/-/ionic-angular-3.4.2.tgz#762631f1af78a5ae1c0aa0f4d23b31435142abe1"
ionic@3.7.0:
version "3.7.0"
resolved "https://registry.yarnpkg.com/ionic/-/ionic-3.7.0.tgz#a3c32174c6d5455c3bc74b6b9152e0f97dce23c8"
dependencies:
"@ionic/cli-utils" "1.7.0"
chalk "^2.0.0"
diff "^3.2.0"
minimist "^1.2.0"
opn "^5.1.0"
os-name "^2.0.1"
rimraf "^2.6.1"
semver "^5.3.0"
ssh-config "^1.0.1"
tar "^2.2.1"
tslib "^1.7.1"
ionicons@3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/ionicons/-/ionicons-3.0.0.tgz#40b8daf4fd7a31150bd002160f66496e22a98c3c"
@ -3182,6 +3288,10 @@ is-utf8@^0.2.0:
version "0.2.1"
resolved "https://registry.yarnpkg.com/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72"
is-wsl@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-1.1.0.tgz#1f16e4aa22b04d1336b66188a66af3c600c3a66d"
is@~0.2.6:
version "0.2.7"
resolved "https://registry.yarnpkg.com/is/-/is-0.2.7.tgz#3b34a2c48f359972f35042849193ae7264b63562"
@ -3206,6 +3316,10 @@ isexe@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10"
isnumeric@^0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/isnumeric/-/isnumeric-0.2.0.tgz#a2347ba360de19e33d0ffd590fddf7755cbf2e64"
isobject@^2.0.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/isobject/-/isobject-2.1.0.tgz#f065561096a3f1da2ef46272f815c840d87e0c89"
@ -4019,7 +4133,7 @@ mute-stream@0.0.7:
version "0.0.7"
resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.7.tgz#3075ce93bc21b8fab43e1bc4da7e8115ed1e7bab"
nan@^2.3.0, nan@^2.3.2:
nan@^2.3.0, nan@^2.3.2, nan@^2.4.0:
version "2.6.2"
resolved "https://registry.yarnpkg.com/nan/-/nan-2.6.2.tgz#e4ff34e6c95fdfb5aecc08de6596f43605a7db45"
@ -4307,6 +4421,12 @@ opn@4.0.2, opn@^4.0.0:
object-assign "^4.0.1"
pinkie-promise "^2.0.0"
opn@^5.1.0:
version "5.1.0"
resolved "https://registry.yarnpkg.com/opn/-/opn-5.1.0.tgz#72ce2306a17dbea58ff1041853352b4a8fc77519"
dependencies:
is-wsl "^1.1.0"
optimist@^0.6.1, optimist@~0.6.0:
version "0.6.1"
resolved "https://registry.yarnpkg.com/optimist/-/optimist-0.6.1.tgz#da3ea74686fa21a19a111c326e90eb15a0196686"
@ -4390,6 +4510,20 @@ parse-asn1@^5.0.0:
evp_bytestokey "^1.0.0"
pbkdf2 "^3.0.3"
parse-css-font@^2.0.2:
version "2.0.2"
resolved "https://registry.yarnpkg.com/parse-css-font/-/parse-css-font-2.0.2.tgz#7b60b060705a25a9b90b7f0ed493e5823248a652"
dependencies:
css-font-size-keywords "^1.0.0"
css-font-stretch-keywords "^1.0.1"
css-font-style-keywords "^1.0.1"
css-font-weight-keywords "^1.0.0"
css-global-keywords "^1.0.1"
css-list-helpers "^1.0.1"
css-system-font-keywords "^1.0.0"
tcomb "^2.5.0"
unquote "^1.1.0"
parse-glob@^3.0.4:
version "3.0.4"
resolved "https://registry.yarnpkg.com/parse-glob/-/parse-glob-3.0.4.tgz#b2c376cfb11f35513badd173ef0bb6e3a388391c"
@ -4912,6 +5046,12 @@ qjobs@^1.1.4:
version "1.1.5"
resolved "https://registry.yarnpkg.com/qjobs/-/qjobs-1.1.5.tgz#659de9f2cf8dcc27a1481276f205377272382e73"
qrious@^2.2.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/qrious/-/qrious-2.3.0.tgz#ca7ce68a82099a67a90e4979f4b1cdf51ef32475"
optionalDependencies:
canvas "^1.6.5"
qs@6.2.0:
version "6.2.0"
resolved "https://registry.yarnpkg.com/qs/-/qs-6.2.0.tgz#3b7848c03c2dece69a9522b0fae8c4126d745f3b"
@ -5399,6 +5539,10 @@ sax@0.6.x:
version "0.6.1"
resolved "https://registry.yarnpkg.com/sax/-/sax-0.6.1.tgz#563b19c7c1de892e09bfc4f2fc30e3c27f0952b9"
sax@1.1.4:
version "1.1.4"
resolved "https://registry.yarnpkg.com/sax/-/sax-1.1.4.tgz#74b6d33c9ae1e001510f179a91168588f1aedaa9"
sax@>=0.6.0, sax@~1.2.1:
version "1.2.4"
resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9"
@ -5449,6 +5593,10 @@ selenium-webdriver@^2.53.2:
version "5.3.0"
resolved "https://registry.yarnpkg.com/semver/-/semver-5.3.0.tgz#9b2ce5d3de02d17c6012ad326aa6b4d0cf54f94f"
semver@^5.4.1:
version "5.4.1"
resolved "https://registry.yarnpkg.com/semver/-/semver-5.4.1.tgz#e059c09d8571f0540823733433505d3a2f00b18e"
semver@~2.3.1:
version "2.3.2"
resolved "https://registry.yarnpkg.com/semver/-/semver-2.3.2.tgz#b9848f25d6cf36333073ec9ef8856d42f1233e52"
@ -5590,9 +5738,11 @@ slash@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/slash/-/slash-1.0.0.tgz#c41f2f6c39fc16d1cd17ad4b5d896114ae470d55"
slice-ansi@0.0.4:
version "0.0.4"
resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-0.0.4.tgz#edbf8903f66f7ce2f8eafd6ceed65e264c831b35"
slice-ansi@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-1.0.0.tgz#044f1a49d8842ff307aad6b505ed178bd950134d"
dependencies:
is-fullwidth-code-point "^2.0.0"
sntp@1.x.x:
version "1.0.9"
@ -5761,6 +5911,10 @@ sprintf-js@~1.0.2:
version "1.0.3"
resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c"
ssh-config@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/ssh-config/-/ssh-config-1.0.1.tgz#38742116dced7d45f5362ca86af77130ccbebc8b"
sshpk@^1.7.0:
version "1.13.1"
resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.13.1.tgz#512df6da6287144316dc4c18fe1cf1d940739be3"
@ -5829,6 +5983,13 @@ string-width@^2.0.0:
is-fullwidth-code-point "^2.0.0"
strip-ansi "^4.0.0"
string-width@^2.1.0, string-width@^2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e"
dependencies:
is-fullwidth-code-point "^2.0.0"
strip-ansi "^4.0.0"
string_decoder@0.10, string_decoder@^0.10.25, string_decoder@~0.10.x:
version "0.10.31"
resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94"
@ -5988,6 +6149,10 @@ tar@^2.0.0, tar@^2.2.1:
fstream "^1.0.2"
inherits "2"
tcomb@^2.5.0:
version "2.7.0"
resolved "https://registry.yarnpkg.com/tcomb/-/tcomb-2.7.0.tgz#10d62958041669a5d53567b9a4ee8cde22b1c2b0"
temp-write@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/temp-write/-/temp-write-2.1.0.tgz#59890918e0ef09d548aaa342f4bd3409d8404e96"
@ -6283,10 +6448,21 @@ uniqs@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/uniqs/-/uniqs-2.0.0.tgz#ffede4b36b25290696e6e165d4a59edb998e6b02"
units-css@^0.4.0:
version "0.4.0"
resolved "https://registry.yarnpkg.com/units-css/-/units-css-0.4.0.tgz#d6228653a51983d7c16ff28f8b9dc3b1ffed3a07"
dependencies:
isnumeric "^0.2.0"
viewport-dimensions "^0.2.0"
unpipe@1.0.0, unpipe@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec"
unquote@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/unquote/-/unquote-1.1.0.tgz#98e1fc608b6b854c75afb1b95afc099ba69d942f"
unreachable-branch-transform@^0.3.0:
version "0.3.0"
resolved "https://registry.yarnpkg.com/unreachable-branch-transform/-/unreachable-branch-transform-0.3.0.tgz#d99cc4c6e746d264928845b611db54b0f3474caa"
@ -6399,6 +6575,10 @@ verror@1.3.6:
dependencies:
extsprintf "1.0.2"
viewport-dimensions@^0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/viewport-dimensions/-/viewport-dimensions-0.2.0.tgz#de740747db5387fd1725f5175e91bac76afdf36c"
vlq@^0.2.1:
version "0.2.2"
resolved "https://registry.yarnpkg.com/vlq/-/vlq-0.2.2.tgz#e316d5257b40b86bb43cb8d5fea5d7f54d6b0ca1"
@ -6622,13 +6802,20 @@ wordwrap@~0.0.2:
version "0.0.3"
resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.3.tgz#a3d5da6cd5c0bc0008d37234bbaf1bed63059107"
wrap-ansi@^2.0.0, wrap-ansi@^2.1.0:
wrap-ansi@^2.0.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-2.1.0.tgz#d8fc3d284dd05794fe84973caecdd1cf824fdd85"
dependencies:
string-width "^1.0.1"
strip-ansi "^3.0.1"
wrap-ansi@^3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-3.0.1.tgz#288a04d87eda5c286e060dfe8f135ce8d007f8ba"
dependencies:
string-width "^2.1.1"
strip-ansi "^4.0.0"
wrappy@1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
@ -6787,7 +6974,7 @@ yn@^1.2.0:
dependencies:
object-assign "^4.1.1"
zip-stream@^1.1.0:
zip-stream@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/zip-stream/-/zip-stream-1.2.0.tgz#a8bc45f4c1b49699c6b90198baacaacdbcd4ba04"
dependencies: