From d497baf6620e330d48cafa078476b3889aa6dbe5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20Baz=C3=A1n?= Date: Fri, 3 Nov 2017 16:33:16 -0300 Subject: [PATCH] send view and addressbook Provider --- src/app/app.module.ts | 3 + src/pages/send/send.html | 30 +++- src/pages/send/send.ts | 170 +++++++++++++++++-- src/providers/address-book/address-book.ts | 133 +++++++++++++++ src/providers/incoming-data/incoming-data.ts | 10 +- 5 files changed, 327 insertions(+), 19 deletions(-) create mode 100644 src/providers/address-book/address-book.ts diff --git a/src/app/app.module.ts b/src/app/app.module.ts index dccce3b71..7f08500a3 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -64,6 +64,8 @@ import { ConfirmPage } from '../pages/send/confirm/confirm'; import { CustomAmountPage } from '../pages/receive/custom-amount/custom-amount'; /* Providers */ + +import { AddressBookProvider } from '../providers/address-book/address-book'; import { AppProvider } from '../providers/app/app'; import { BwcProvider } from '../providers/bwc/bwc'; import { BwcErrorProvider } from '../providers/bwc-error/bwc-error'; @@ -124,6 +126,7 @@ let pages: any = [ ]; let providers: any = [ + AddressBookProvider, AndroidFingerprintAuth, AppProvider, BwcProvider, diff --git a/src/pages/send/send.html b/src/pages/send/send.html index ec9a881c8..d67bfb184 100644 --- a/src/pages/send/send.html +++ b/src/pages/send/send.html @@ -9,13 +9,35 @@ - Recipient - + - - +
+ + Contacts + + {{item.name}} + + +
+
+ + Transfer to Wallet + + {{item.name}} + + +
+ + + To get started, you'll need to create a bitcoin wallet and get some bitcoin. + + + + + + \ No newline at end of file diff --git a/src/pages/send/send.ts b/src/pages/send/send.ts index fe88ec0e0..2ccba5334 100644 --- a/src/pages/send/send.ts +++ b/src/pages/send/send.ts @@ -1,33 +1,181 @@ import { Component } from '@angular/core'; import { NavController } from 'ionic-angular'; -import * as _ from 'lodash'; +import { Logger } from '@nsalaun/ng-logger'; + +//providers +import { ProfileProvider } from '../../providers/profile/profile'; +import { WalletProvider } from '../../providers/wallet/wallet'; +import { AddressBookProvider } from '../../providers/address-book/address-book'; +import { BwcProvider } from '../../providers/bwc/bwc'; +import { IncomingDataProvider } from '../../providers/incoming-data/incoming-data'; +import { PopupProvider } from '../../providers/popup/popup'; + +//pages import { AmountPage } from './amount/amount'; +import { CreateWalletPage } from '../add/create-wallet/create-wallet'; + +import * as _ from 'lodash'; @Component({ selector: 'page-send', templateUrl: 'send.html', }) export class SendPage { - public search: string; + public search: string = ''; + public wallets: any; + public walletList: any; + public contactsList: any; + public hasWallets: boolean; + public hasContacts: boolean; + public contactsShowMore: boolean; + public searchFocus: boolean; + private CONTACTS_SHOW_LIMIT: number = 10; + private currentContactsPage: number = 0; + + constructor( + private navCtrl: NavController, + private profileProvider: ProfileProvider, + private walletProvider: WalletProvider, + private addressBookProvider: AddressBookProvider, + private logger: Logger, + private bwcProvider: BwcProvider, + private incomingDataProvider: IncomingDataProvider, + private popupProvider: PopupProvider + ) { - constructor(public navCtrl: NavController) { } - + ionViewDidLoad() { - this.search = ''; console.log('ionViewDidLoad SendPage'); } - openScanner() { + ionViewDidEnter() { + this.wallets = this.profileProvider.getWallets({ + onlyComplete: true + }); + this.hasWallets = !(_.isEmpty(this.wallets)); + console.log(this.hasWallets); + this.updateWalletsList(); + this.updateContactsList(); + } + + private updateWalletsList(): void { + if (!this.hasWallets) return; + + this.walletList = []; + _.each(this.wallets, (v: any) => { + this.walletList.push({ + color: v.color, + name: v.name, + recipientType: 'wallet', + coin: v.coin, + network: v.network, + getAddress: (): Promise => { + return new Promise((resolve, reject) => { + this.walletProvider.getAddress(v, false).then((addr) => { + return resolve(addr); + }).catch((err) => { + return reject(err); + }); + }); + } + }); + }); + } + + private updateContactsList() { + this.addressBookProvider.list().then((ab: any) => { + + this.hasContacts = _.isEmpty(ab) ? false : true; + if (!this.hasContacts) return; + + this.contactsList = []; + _.each(ab, (v: any, k: string) => { + this.contactsList.push({ + name: _.isObject(v) ? v.name : v, + address: k, + email: _.isObject(v) ? v.email : null, + recipientType: 'contact', + coin: this.getCoin(k), + getAddress: (): Promise => { + return new Promise((resolve, reject) => { + return resolve(k); + }); + } + }); + }); + let contacts = this.contactsList.slice(0, (this.currentContactsPage + 1) * this.CONTACTS_SHOW_LIMIT); + this.contactsShowMore = this.contactsList.length > contacts.length; + return; + }); + } + + private getCoin(address: string): string { + let cashAddress = this.bwcProvider.getBitcoreCash().Address.isValid(address, 'livenet'); + if (cashAddress) { + return 'bch'; + } + return 'btc'; + } + + public openScanner(): void { this.navCtrl.parent.select(2); } - findContact(search: string) { - // TODO: Improve this function - console.log("Send search string", search); - if (search.length === 34) { - this.navCtrl.push(AmountPage, {address: search, sending: true}); + public showMore(): void { + this.currentContactsPage++; + this.updateWalletsList(); + } + + public searchInFocus(): void { + this.searchFocus = true; + } + + public searchBlurred(): void { + if (this.search == null || this.search.length == 0) { + this.searchFocus = false; } } + findContact(search: string): void { + if (this.incomingDataProvider.redir(search)) return; + if (!search || search.length < 2) { + this.updateContactsList(); + return; + } + let result = _.filter(this.contactsList, (item: any) => { + let val = item.name; + return _.includes(val.toLowerCase(), search.toLowerCase()); + }); + this.contactsList = result; + } + + public goToAmount(item: any): void { + item.getAddress().then((addr: string) => { + if (!addr) { + //Error is already formated + this.popupProvider.ionicAlert('Error - no address'); + return; + } + this.logger.debug('Got toAddress:' + addr + ' | ' + item.name); + this.navCtrl.push(AmountPage, { + recipientType: item.recipientType, + toAddress: addr, + toName: item.name, + toEmail: item.email, + toColor: item.color, + coin: item.coin + }); + return; + }).catch((err: any) => { + this.logger.warn(err); + }); + }; + + public createWallet(): void { + this.navCtrl.push(CreateWalletPage, {}); + }; + + // TODO check if wallets have balance + } diff --git a/src/providers/address-book/address-book.ts b/src/providers/address-book/address-book.ts new file mode 100644 index 000000000..7c5553396 --- /dev/null +++ b/src/providers/address-book/address-book.ts @@ -0,0 +1,133 @@ +import { Injectable } from '@angular/core'; +import { BwcProvider } from '../bwc/bwc'; +import { Logger } from '@nsalaun/ng-logger'; +import { PersistenceProvider } from '../../providers/persistence/persistence'; + +import * as _ from 'lodash'; + +@Injectable() +export class AddressBookProvider { + + constructor( + private bwcProvider: BwcProvider, + private logger: Logger, + private persistenceProvider: PersistenceProvider, + ) { + console.log('Hello AddressBookProvider Provider'); + } + + private getNetwork(address: string): string { + let network; + try { + network = (this.bwcProvider.getBitcore().Address(address)).network.name; + } catch (e) { + this.logger.warn('No valid bitcoin address. Trying bitcoin cash...'); + network = (this.bwcProvider.getBitcoreCash().Address(address)).network.name; + } + return network; + }; + + private get(addr: string): Promise { + return new Promise((resolve, reject) => { + this.persistenceProvider.getAddressbook('testnet').then((ab: any) => { + if (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 && ab[addr]) return resolve(ab[addr]); + return resolve(); + }).catch((err: any) => { + return reject(); + }); + }).catch((err: any) => { + return reject(); + }); + }) + }; + + public list(): Promise { + return new Promise((resolve, reject) => { + this.persistenceProvider.getAddressbook('testnet').then((ab: any) => { + if (ab) ab = JSON.parse(ab); + + ab = ab || {}; + this.persistenceProvider.getAddressbook('livenet').then((ab2: any) => { + if (ab2) ab2 = JSON.parse(ab2); + + ab2 = ab2 || {}; + return resolve(_.defaults(ab2, ab)); + }).catch((err: any) => { + return reject(err); + }); + }).catch((err: any) => { + return reject('Could not get the Addressbook'); + }); + }); + }; + + public add(entry: any): Promise { + return new Promise((resolve, reject) => { + 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); + ab = ab || {}; + if (_.isArray(ab)) ab = {}; // No array + if (ab[entry.address]) return reject('Entry already exist'); + ab[entry.address] = entry; + this.persistenceProvider.setAddressbook(network, JSON.stringify(ab)).then((ab: any) => { + this.list().then((ab: any) => { + return resolve(ab); + }).catch((err: any) => { + return reject(err); + }); + }).catch((err: any) => { + return reject('Error adding new entry'); + }); + }).catch((err: any) => { + return reject(err); + }); + }); + }; + + public remove(addr: any): Promise { + return new Promise((resolve, reject) => { + 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); + ab = ab || {}; + if (_.isEmpty(ab)) return reject('Addressbook is empty'); + if (!ab[addr]) return reject('Entry does not exist'); + delete ab[addr]; + this.persistenceProvider.setAddressbook(network, JSON.stringify(ab)).then(() => { + this.list().then((ab: any) => { + return resolve(ab); + }).catch((err: any) => { + return reject(err); + }); + }).catch(() => { + return reject('Error deleting entry'); + }); + }).catch((err: any) => { + return reject(err); + }); + }); + }; + + public removeAll(): Promise { + return new Promise((resolve, reject) => { + this.persistenceProvider.removeAddressbook('livenet').then(() => { + this.persistenceProvider.removeAddressbook('testnet').then(() => { + return resolve(); + }); + }).catch(() => { + return reject('Error deleting addressbook'); + }).catch(() => { + return reject('Error deleting addressbook'); + }); + }); + }; + +} diff --git a/src/providers/incoming-data/incoming-data.ts b/src/providers/incoming-data/incoming-data.ts index 45b9ea8cd..ca382ee54 100644 --- a/src/providers/incoming-data/incoming-data.ts +++ b/src/providers/incoming-data/incoming-data.ts @@ -1,5 +1,5 @@ -import { Injectable } from '@angular/core'; -import { Events, NavController } from 'ionic-angular'; +import { Injectable, Injector, ViewChild } from '@angular/core'; +import { Events, NavController, App } from 'ionic-angular'; import { Logger } from '@nsalaun/ng-logger'; //providers @@ -18,10 +18,10 @@ import { ImportWalletPage } from '../../pages/add/import-wallet/import-wallet'; @Injectable() export class IncomingDataProvider { - + private navCtrl: NavController; constructor( + private app: App, private events: Events, - private navCtrl: NavController, private bwcProvider: BwcProvider, private payproProvider: PayproProvider, private scanProvider: ScanProvider, @@ -29,6 +29,8 @@ export class IncomingDataProvider { private logger: Logger, private appProvider: AppProvider ) { + //TODO Injecting NavController in constructor of service fails with no provider error + this.navCtrl = app.getActiveNav(); console.log('Hello IncomingDataProvider Provider'); }