Add, remove, search address book. Adds bitcoin address validator

This commit is contained in:
Gustavo Maximiliano Cortez 2017-11-09 00:08:27 -03:00
parent 9348049123
commit 707521569e
No known key found for this signature in database
GPG Key ID: 15EDAD8D9F2EB1AF
12 changed files with 316 additions and 8 deletions

View File

@ -131,7 +131,7 @@
"codecov": "2.2.0",
"fs-extra": "^4.0.2",
"html-loader": "0.4.5",
"ionic": "3.16.0",
"ionic": "3.17.0",
"jasmine-core": "2.6.4",
"jasmine-spec-reporter": "4.1.1",
"karma": "1.7.0",

View File

@ -39,6 +39,8 @@ import { TourPage } from '../pages/onboarding/tour/tour';
import { BackupWarningPage } from '../pages/backup/backup-warning/backup-warning';
import { BackupGamePage } from '../pages/backup/backup-game/backup-game';
import { AddressbookPage } from '../pages/addressbook/addressbook';
import { AddressbookAddPage } from '../pages/addressbook/add/add';
import { AddressbookViewPage } from '../pages/addressbook/view/view';
/* Tabs */
import { HomePage } from '../pages/home/home';
@ -106,6 +108,8 @@ let pages: any = [
BackupWarningPage,
BackupGamePage,
AddressbookPage,
AddressbookAddPage,
AddressbookViewPage,
AboutPage,
AdvancedPage,
AltCurrencyPage,

View File

@ -0,0 +1,35 @@
<ion-header>
<ion-navbar>
<ion-title>{{ 'Add Contact' | translate }}</ion-title>
</ion-navbar>
</ion-header>
<ion-content padding>
<form [formGroup]="addressBookAdd">
<ion-list>
<ion-item>
<ion-label stacked>{{ 'Name' | translate }}</ion-label>
<ion-input formControlName="name" type="text"></ion-input>
</ion-item>
<ion-item>
<ion-label stacked>{{ 'Email' | translate }}</ion-label>
<ion-input formControlName="email" type="email"></ion-input>
</ion-item>
<ion-item>
<ion-label stacked>{{ 'Bitcoin Address' | translate }}</ion-label>
<ion-input formControlName="address" type="text" address-validator></ion-input>
</ion-item>
</ion-list>
<button ion-button block (click)="save()" [disabled]="addressBookAdd.invalid || submitAttempt">{{ 'Save' | translate }}</button>
</form>
</ion-content>

View File

@ -0,0 +1,3 @@
page-addressbook-add {
}

View File

@ -0,0 +1,75 @@
import { Component } from '@angular/core';
import { FormBuilder, FormGroup, Validators, AbstractControl, ValidationErrors } from '@angular/forms';
import { NavController, NavParams, AlertController } from 'ionic-angular';
import { BwcProvider } from '../../../providers/bwc/bwc';
import { AddressBookProvider } from '../../../providers/address-book/address-book';
import { AddressValidator } from '../../../validators/address';
@Component({
selector: 'page-addressbook-add',
templateUrl: 'add.html',
})
export class AddressbookAddPage {
private addressBookAdd: FormGroup;
private submitAttempt: boolean = false;
constructor(
public navCtrl: NavController,
public navParams: NavParams,
public alertCtrl: AlertController,
public bwc: BwcProvider,
public ab: AddressBookProvider,
public formBuilder: FormBuilder
) {
this.addressBookAdd = this.formBuilder.group({
name: ['', Validators.compose([Validators.required, Validators.pattern('[a-zA-Z0-9 ]*')])],
email: ['', this.emailOrEmpty],
address: ['', Validators.compose([Validators.required, new AddressValidator(bwc).isValid])]
});
}
ionViewDidLoad() {
console.log('ionViewDidLoad AddressbookAddPage');
}
private emailOrEmpty(control: AbstractControl): ValidationErrors | null {
return control.value === '' ? null : Validators.email(control);
}
public save() {
this.submitAttempt = true;
if(this.addressBookAdd.valid){
this.ab.add(this.addressBookAdd.value).then((ab) => {
this.navCtrl.pop();
}).catch((err) => {
let opts = {
title: err,
buttons: [{
text: 'OK',
handler: () => {
this.navCtrl.pop();
}
}],
}
this.alertCtrl.create(opts).present();
});
}
else {
let opts = {
title: 'Error',
message: 'Could not save the contact',
buttons: [{
text: 'OK',
handler: () => {
this.navCtrl.pop();
}
}],
}
this.alertCtrl.create(opts).present();
}
}
}

View File

@ -27,6 +27,12 @@
</div>
<div *ngIf="!isEmptyList">
<ion-searchbar (ionInput)="getItems($event)"></ion-searchbar>
<ion-list>
<ion-item *ngFor="let entry of addressbook" (click)="viewEntry(entry)">
{{ entry.name }}
</ion-item>
</ion-list>
</div>
</ion-content>

View File

@ -1,5 +1,7 @@
import { Component } from '@angular/core';
import { NavController, NavParams, AlertController } from 'ionic-angular';
import { AddressbookAddPage } from './add/add';
import { AddressbookViewPage } from './view/view';
import { AddressBookProvider } from '../../providers/address-book/address-book';
import { Logger } from '@nsalaun/ng-logger';
import * as _ from 'lodash';
@ -10,9 +12,13 @@ import * as _ from 'lodash';
})
export class AddressbookPage {
private cache: boolean = false;
private isEmptyList: boolean;
private addressbook: Array<object> = [];
private searchQuery: string = '';
constructor(
public navCtrl: NavController,
public navParams: NavParams,
@ -23,6 +29,11 @@ export class AddressbookPage {
this.initAddressbook();
}
ionViewDidEnter() {
if (this.cache) this.initAddressbook();
this.cache = true;
}
private initAddressbook() {
this.addressbookProvider.list().then((ab) => {
this.isEmptyList = _.isEmpty(ab);
@ -54,4 +65,28 @@ export class AddressbookPage {
});
};
private addEntry() {
this.navCtrl.push(AddressbookAddPage);
};
private viewEntry(ab: any) {
this.navCtrl.push(AddressbookViewPage, { address: ab.address });
}
private getItems(ev: any) {
// Reset items back to all of the items
this.initAddressbook();
// set val to the value of the searchbar
let val = ev.target.value;
// if the value is an empty string don't filter the items
if (val && val.trim() != '') {
this.addressbook = this.addressbook.filter((item) => {
let name = item['name'];
return _.includes(name.toLowerCase(), val.toLowerCase());
});
}
}
}

View File

@ -0,0 +1,23 @@
<ion-header>
<ion-navbar>
<ion-title>{{ contact.name }}</ion-title>
</ion-navbar>
</ion-header>
<ion-content padding>
<ion-card>
<ion-card-header>
{{ contact.name }}
</ion-card-header>
<ion-card-content text-wrap>
{{ contact.address }}
</ion-card-content>
</ion-card>
<button ion-button clear color="danger" (click)="remove()">Remove</button>
</ion-content>

View File

@ -0,0 +1,3 @@
page-addressbook-view {
}

View File

@ -0,0 +1,62 @@
import { Component } from '@angular/core';
import { NavController, NavParams, AlertController } from 'ionic-angular';
import { AddressBookProvider } from '../../../providers/address-book/address-book';
@Component({
selector: 'page-addressbook-view',
templateUrl: 'view.html',
})
export class AddressbookViewPage {
private address: string;
private contact: Object = {
name: '',
address: '',
email: ''
};
constructor(
public navCtrl: NavController,
public navParams: NavParams,
public alertCtrl: AlertController,
public ab: AddressBookProvider
) {
this.address = this.navParams.get('address');
}
ionViewDidLoad() {
this.ab.get(this.address).then((entry) => {
this.contact = entry;
}).catch((err) => {
let alertError = this.alertCtrl.create({
title: err,
buttons: [
{
text: 'Go back',
handler: () => {
this.navCtrl.pop();
}
}
]
});
alertError.present();
});
}
private remove() {
this.ab.remove(this.address).then(() => {
this.navCtrl.pop();
}).catch((err) => {
let alertError = this.alertCtrl.create({
title: err,
buttons: [
{
text: 'OK'
}
]
});
alertError.present();
});
}
}

View File

@ -27,14 +27,14 @@ export class AddressBookProvider {
return network;
};
private get(addr: string): Promise<any> {
public get(addr: string): Promise<any> {
return new Promise((resolve, reject) => {
this.persistenceProvider.getAddressbook('testnet').then((ab: any) => {
if (ab) ab = JSON.parse(ab);
if (ab && _.isString(ab)) ab = JSON.parse(ab);
if (ab && ab[addr]) return resolve(ab[addr]);
this.persistenceProvider.getAddressbook('livenet').then((ab: any) => {
if (ab) ab = JSON.parse(ab);
if (ab && _.isString(ab)) ab = JSON.parse(ab);
if (ab && ab[addr]) return resolve(ab[addr]);
return resolve();
}).catch((err: any) => {
@ -49,11 +49,11 @@ export class AddressBookProvider {
public list(): Promise<any> {
return new Promise((resolve, reject) => {
this.persistenceProvider.getAddressbook('testnet').then((ab: any) => {
if (ab) ab = JSON.parse(ab);
if (ab && _.isString(ab)) ab = JSON.parse(ab);
ab = ab || {};
this.persistenceProvider.getAddressbook('livenet').then((ab2: any) => {
if (ab2) ab2 = JSON.parse(ab2);
if (ab2 && _.isString(ab)) ab2 = JSON.parse(ab2);
ab2 = ab2 || {};
return resolve(_.defaults(ab2, ab));
@ -71,7 +71,7 @@ export class AddressBookProvider {
var network = this.getNetwork(entry.address);
if (_.isEmpty(network)) return reject('Not valid bitcoin address');
this.persistenceProvider.getAddressbook(network).then((ab: any) => {
if (ab) ab = JSON.parse(ab);
if (ab && _.isString(ab)) ab = JSON.parse(ab);
ab = ab || {};
if (_.isArray(ab)) ab = {}; // No array
if (ab[entry.address]) return reject('Entry already exist');
@ -96,7 +96,7 @@ export class AddressBookProvider {
var network = this.getNetwork(addr);
if (_.isEmpty(network)) return reject('Not valid bitcoin address');
this.persistenceProvider.getAddressbook(network).then((ab: any) => {
if (ab) ab = JSON.parse(ab);
if (ab && _.isString(ab)) ab = JSON.parse(ab);
ab = ab || {};
if (_.isEmpty(ab)) return reject('Addressbook is empty');
if (!ab[addr]) return reject('Entry does not exist');

62
src/validators/address.ts Normal file
View File

@ -0,0 +1,62 @@
import { FormControl } from '@angular/forms';
import { BwcProvider } from '../providers/bwc/bwc';
export class AddressValidator {
static bitcore: BwcProvider;
constructor(bwc: BwcProvider) {
AddressValidator.bitcore = bwc;
}
isValid(control: FormControl): any {
let b = AddressValidator.bitcore.getBitcore();
let c = AddressValidator.bitcore.getBitcoreCash();
let URI = b.URI;
let Address = b.Address;
let URICash = c.URI;
let AddressCash = c.Address;
// Regular url
if (/^https?:\/\//.test(control.value)) {
return null;
}
// Bip21 uri
let uri, isAddressValidLivenet, isAddressValidTestnet;
if (/^bitcoin:/.test(control.value)) {
let isUriValid = URI.isValid(control.value);
if (isUriValid) {
uri = new URI(control.value);
isAddressValidLivenet = Address.isValid(uri.address.toString(), 'livenet')
isAddressValidTestnet = Address.isValid(uri.address.toString(), 'testnet')
}
if (isUriValid && (isAddressValidLivenet || isAddressValidTestnet)) {
return null;
}
} else if (/^bitcoincash:/.test(control.value)) {
let isUriValid = URICash.isValid(control.value);
if (isUriValid) {
uri = new URICash(control.value);
isAddressValidLivenet = AddressCash.isValid(uri.address.toString(), 'livenet')
}
if (isUriValid && isAddressValidLivenet) {
return null;
}
}
// Regular Address: try Bitcoin and Bitcoin Cash
let regularAddressLivenet = Address.isValid(control.value, 'livenet');
let regularAddressTestnet = Address.isValid(control.value, 'testnet');
let regularAddressCashLivenet = AddressCash.isValid(control.value, 'livenet');
if (regularAddressLivenet || regularAddressTestnet || regularAddressCashLivenet) {
return null;
}
return {
"Invalid Address": true
};
}
}