diff --git a/src/providers/persistence/persistence.spec.ts b/src/providers/persistence/persistence.spec.ts index 6baabc2fb..389244500 100644 --- a/src/providers/persistence/persistence.spec.ts +++ b/src/providers/persistence/persistence.spec.ts @@ -2,6 +2,7 @@ import { TestBed, inject } from '@angular/core/testing'; import { PersistenceProvider } from './persistence'; import { IStorage, ISTORAGE, KeyAlreadyExistsError } from './storage/istorage'; import { RamStorage } from './storage/ram-storage'; +import { Logger } from '@nsalaun/ng-logger'; describe('Storage Service', () => { beforeEach(() => { @@ -9,6 +10,7 @@ describe('Storage Service', () => { providers: [ PersistenceProvider, { provide: ISTORAGE, useClass: RamStorage }, + { provide: Logger }, ] }); }); @@ -19,7 +21,7 @@ describe('Storage Service', () => { service = pp; })); it('should correctly perform a profile roundtrip', () => { - var p = { name: 'My profile' }; + let p = { name: 'My profile' }; service.storeNewProfile(p).then(() => { service.getProfile().then((profile) => { expect(typeof profile).toEqual('object'); @@ -28,7 +30,7 @@ describe('Storage Service', () => { }); }); it('should fail to create a profile when one already exists', () => { - var p = { name: 'My profile' }; + let p = { name: 'My profile' }; service.storeNewProfile(p).then(() => { service.storeNewProfile(p).catch((err) => { expect(err.message).toEqual('Key already exists'); diff --git a/src/providers/persistence/persistence.ts b/src/providers/persistence/persistence.ts index 7e3bc86eb..12fd460af 100644 --- a/src/providers/persistence/persistence.ts +++ b/src/providers/persistence/persistence.ts @@ -1,34 +1,441 @@ import { Injectable } from '@angular/core'; import { InjectionToken, Inject } from '@angular/core'; import { IStorage, ISTORAGE } from './storage/istorage'; +import { Logger } from '@nsalaun/ng-logger'; +import * as _ from 'lodash'; + +const Keys = { + ADDRESS_BOOK: network => 'addressbook-' + network, + AGREE_DISCLAIMER: 'agreeDisclaimer', + AMAZON_GIFT_CARDS: network => 'amazonGiftCards-' + network, + APP_IDENTITY: network => 'appIdentity-' + network, + BACKUP: walletId => 'backup-' + walletId, + BALANCE_CACHE: cardId => 'balanceCache-' + cardId, + BITPAY_ACCOUNTS_V2: network => 'bitpayAccounts-v2-' + network, + CLEAN_AND_SCAN_ADDRESSES: 'CleanAndScanAddresses', + COINBASE_REFRESH_TOKEN: network => 'coinbaseRefreshToken-' + network, + COINBASE_TOKEN: network => 'coinbaseToken-' + network, + COINBASE_TXS: network => 'coinbaseTxs-' + network, + CONFIG: 'config', + FEEDBACK: 'feedback', + FOCUSED_WALLET_ID: 'focusedWalletId', + GLIDERA_PERMISSIONS: network => 'glideraPermissions-' + network, + GLIDERA_STATUS: network => 'glideraStatus-' + network, + GLIDERA_TOKEN: network => 'glideraToken-' + network, + GLIDERA_TXS: network => 'glideraTxs-' + network, + HIDE_BALANCE: walletId => 'hideBalance-' + walletId, + HOME_TIP: 'homeTip', + LAST_ADDRESS: walletId => 'lastAddress-' + walletId, + LAST_CURRENCY_USED: 'lastCurrencyUsed', + PROFILE: 'profile', + REMOTE_PREF_STORED: 'remotePrefStored', + TX_CONFIRM_NOTIF: txid => 'txConfirmNotif-' + txid, + TX_HISTORY: walletId => 'txsHistory-' + walletId, +}; @Injectable() export class PersistenceProvider { - constructor( @Inject(ISTORAGE) public storage: IStorage) { + constructor( @Inject(ISTORAGE) public storage: IStorage, private log: Logger) { } storeNewProfile(profile): Promise { - return this.storage.create('profile', profile); + return this.storage.create(Keys.PROFILE, profile); }; storeProfile(profile): Promise { - return this.storage.set('profile', profile); + return this.storage.set(Keys.PROFILE, profile); }; getProfile(): Promise { - return this.storage.get('profile').then((profile) => { - return profile; + return this.storage.get(Keys.PROFILE); + }; + + deleteProfile(): Promise { + return this.storage.remove(Keys.PROFILE); + }; + + setFeedbackInfo(feedbackValues: any): Promise { + return this.storage.set(Keys.FEEDBACK, feedbackValues); + }; + + getFeedbackInfo(): Promise { + return this.storage.get(Keys.FEEDBACK); + }; + + storeFocusedWalletId(walletId: string): Promise { + return this.storage.set(Keys.FOCUSED_WALLET_ID, walletId || ''); + }; + + getFocusedWalletId(): Promise { + return this.storage.get(Keys.FOCUSED_WALLET_ID); + }; + + getLastAddress(walletId: string): Promise { + return this.storage.get(Keys.LAST_ADDRESS(walletId)); + }; + + storeLastAddress(walletId: string, address: any): Promise { + return this.storage.set(Keys.LAST_ADDRESS(walletId), address); + }; + + clearLastAddress(walletId: string): Promise { + return this.storage.remove(Keys.LAST_ADDRESS(walletId)); + }; + + setBackupFlag(walletId: string): Promise { + return this.storage.set(Keys.BACKUP(walletId), Date.now()); + }; + + getBackupFlag(walletId: string): Promise { + return this.storage.get(Keys.BACKUP(walletId)); + }; + + clearBackupFlag(walletId: string): Promise { + return this.storage.remove(Keys.BACKUP(walletId)); + }; + + setCleanAndScanAddresses(walletId: string): Promise { + return this.storage.set(Keys.CLEAN_AND_SCAN_ADDRESSES, walletId); + }; + + getCleanAndScanAddresses(): Promise { + return this.storage.get(Keys.CLEAN_AND_SCAN_ADDRESSES); + }; + + removeCleanAndScanAddresses(): Promise { + return this.storage.remove(Keys.CLEAN_AND_SCAN_ADDRESSES); + }; + + getConfig(): Promise { + return this.storage.get(Keys.CONFIG); + }; + + storeConfig(config: any): Promise { + return this.storage.set(Keys.CONFIG, config); + }; + + clearConfig(): Promise { + return this.storage.remove(Keys.CONFIG); + }; + + getHomeTipAccepted(): Promise { + return this.storage.get(Keys.HOME_TIP); + }; + + setHomeTipAccepted(homeTip: any): Promise { + return this.storage.set(Keys.HOME_TIP, homeTip); + }; + + setHideBalanceFlag(walletId: string, val: any): Promise { + return this.storage.set(Keys.HIDE_BALANCE(walletId), val); + }; + + getHideBalanceFlag(walletId: string): Promise { + return this.storage.get(Keys.HIDE_BALANCE(walletId)); + }; + + //for compatibility + getCopayDisclaimerFlag(): Promise { + return this.storage.get(Keys.AGREE_DISCLAIMER); + }; + + setRemotePrefsStoredFlag(): Promise { + return this.storage.set(Keys.REMOTE_PREF_STORED, true); + }; + + getRemotePrefsStoredFlag(): Promise { + return this.storage.get(Keys.REMOTE_PREF_STORED); + }; + + setGlideraToken(network: string, token: string): Promise { + return this.storage.set(Keys.GLIDERA_TOKEN(network), token); + }; + + getGlideraToken(network: string): Promise { + return this.storage.get(Keys.GLIDERA_TOKEN(network)); + }; + + removeGlideraToken(network: string): Promise { + return this.storage.remove(Keys.GLIDERA_TOKEN(network)); + }; + + setGlideraPermissions(network: string, permissions: any): Promise { + return this.storage.set(Keys.GLIDERA_PERMISSIONS(network), permissions); + }; + + getGlideraPermissions(network: string): Promise { + return this.storage.get(Keys.GLIDERA_PERMISSIONS(network)); + }; + + removeGlideraPermissions(network: string): Promise { + return this.storage.remove(Keys.GLIDERA_PERMISSIONS(network)); + }; + + setGlideraStatus(network: string, status: any): Promise { + return this.storage.set(Keys.GLIDERA_STATUS(network), status); + }; + + getGlideraStatus(network: string): Promise { + return this.storage.get(Keys.GLIDERA_STATUS(network)); + }; + + removeGlideraStatus(network: string): Promise { + return this.storage.remove(Keys.GLIDERA_STATUS(network)); + }; + + setGlideraTxs(network: string, txs: any): Promise { + return this.storage.set(Keys.GLIDERA_TXS(network), txs); + }; + + getGlideraTxs(network: string): Promise { + return this.storage.get(Keys.GLIDERA_TXS(network)); + }; + + removeGlideraTxs(network: string): Promise { + return this.storage.remove(Keys.GLIDERA_TXS(network)); + }; + + setCoinbaseToken(network: string, token: string): Promise { + return this.storage.set(Keys.COINBASE_TOKEN(network), token); + }; + + getCoinbaseToken(network: string): Promise { + return this.storage.get(Keys.COINBASE_TOKEN(network)); + }; + + removeCoinbaseToken(network: string): Promise { + return this.storage.remove(Keys.COINBASE_TOKEN(network)); + }; + + setCoinbaseRefreshToken(network: string, token: string): Promise { + return this.storage.set(Keys.COINBASE_REFRESH_TOKEN(network), token); + }; + + getCoinbaseRefreshToken(network: string): Promise { + return this.storage.get(Keys.COINBASE_REFRESH_TOKEN(network)); + }; + + removeCoinbaseRefreshToken(network: string): Promise { + return this.storage.remove(Keys.COINBASE_REFRESH_TOKEN(network)); + }; + + setCoinbaseTxs(network: string, ctx: any): Promise { + return this.storage.set(Keys.COINBASE_TXS(network), ctx); + }; + + getCoinbaseTxs(network: string): Promise { + return this.storage.get(Keys.COINBASE_TXS(network)); + }; + + removeCoinbaseTxs(network: string): Promise { + return this.storage.remove(Keys.COINBASE_TXS(network)); + }; + + setAddressbook(network: string, addressbook: any): Promise { + return this.storage.set(Keys.ADDRESS_BOOK(network), addressbook); + }; + + getAddressbook(network: string): Promise { + return this.storage.get(Keys.ADDRESS_BOOK(network)); + }; + + removeAddressbook(network: string): Promise { + return this.storage.remove(Keys.ADDRESS_BOOK(network)); + }; + + setLastCurrencyUsed(lastCurrencyUsed: any): Promise { + return this.storage.set(Keys.LAST_CURRENCY_USED, lastCurrencyUsed); + }; + + getLastCurrencyUsed(): Promise { + return this.storage.get(Keys.LAST_CURRENCY_USED); + }; + + checkQuota(): void { + let block = ''; + // 50MB + for (let i = 0; i < 1024 * 1024; ++i) { + block += '12345678901234567890123456789012345678901234567890'; + } + this.storage.set('test', block).catch(err => { + this.log.error('CheckQuota Return:' + err); + }); + }; + + setTxHistory(walletId: string, txs: any): Promise { + return this.storage.set(Keys.TX_HISTORY(walletId), txs).catch(err => { + this.log.error('Error saving tx History. Size:' + txs.length); + this.log.error(err); + }); + } + + getTxHistory(walletId: string): Promise { + return this.storage.get(Keys.TX_HISTORY(walletId)); + } + + removeTxHistory(walletId: string): Promise { + return this.storage.remove(Keys.TX_HISTORY(walletId)); + } + + setBalanceCache(cardId: string, data: any): Promise { + return this.storage.set(Keys.BALANCE_CACHE(cardId), data); + }; + + getBalanceCache(cardId: string): Promise { + return this.storage.get(Keys.BALANCE_CACHE(cardId)); + }; + + removeBalanceCache(cardId: string): Promise { + return this.storage.remove(Keys.BALANCE_CACHE(cardId)); + }; + + setAppIdentity(network: string, data: any): Promise { + return this.storage.set(Keys.APP_IDENTITY(network), data); + }; + + getAppIdentity(network: string): Promise { + return this.storage.get(Keys.APP_IDENTITY(network)).then(data => { + return JSON.parse(data || '{}'); + }); + }; + + removeAppIdentity(network: string): Promise { + return this.storage.remove(Keys.APP_IDENTITY(network)); + }; + + removeAllWalletData(walletId: string): Promise { + return this.clearLastAddress(walletId) + .then(() => { + return this.removeTxHistory(walletId); + }).then(() => { + return this.clearBackupFlag(walletId); + }); + }; + + setAmazonGiftCards(network: string, gcs: any): Promise { + return this.storage.set(Keys.AMAZON_GIFT_CARDS(network), gcs); + }; + + getAmazonGiftCards(network: string): Promise { + return this.storage.get(Keys.AMAZON_GIFT_CARDS(network)); + }; + + removeAmazonGiftCards(network: string): Promise { + return this.storage.remove(Keys.AMAZON_GIFT_CARDS(network)); + }; + + setTxConfirmNotification(txid: string, val: any): Promise { + return this.storage.set(Keys.TX_CONFIRM_NOTIF(txid), val); + }; + + getTxConfirmNotification(txid: string): Promise { + return this.storage.get(Keys.TX_CONFIRM_NOTIF(txid)); + }; + + removeTxConfirmNotification(txid: string): Promise { + return this.storage.remove(Keys.TX_CONFIRM_NOTIF(txid)); + }; + + + // cb(err, accounts) + // accounts: { + // email_1: { + // token: account token + // cards: { + // + // } + // } + // ... + // email_n: { + // token: account token + // cards: { + // + // } + // } + // } + // + getBitpayAccounts(network: string): Promise { + return this.storage.get(Keys.BITPAY_ACCOUNTS_V2(network)); + }; + + setBitpayAccount(network: string, data: { + email: string, + token: string, + familyName?: string, // last name + givenName?: string, // firstName + }): Promise { + return this.getBitpayAccounts(network) + .then(allAccounts => { + allAccounts = allAccounts || {}; + let account = allAccounts[data.email] || {}; + account.token = data.token; + account.familyName = data.familyName; + account.givenName = data.givenName; + allAccounts[data.email] = account; + + this.log.info('Storing BitPay accounts with new account:' + data.email); + return this.storage.set(Keys.BITPAY_ACCOUNTS_V2(network), allAccounts); + }); + }; + + removeBitpayAccount(network: string, email: string): Promise { + return this.getBitpayAccounts(network) + .then(allAccounts => { + allAccounts = allAccounts || {}; + delete allAccounts[email]; + return this.storage.set(Keys.BITPAY_ACCOUNTS_V2(network), allAccounts); + }); + }; + + // cards: [ + // eid: card id + // id: card id + // lastFourDigits: card number + // token: card token + // ] + setBitpayDebitCards(network: string, email: string, cards: any): Promise { + return this.getBitpayAccounts(network).then(allAccounts => { + allAccounts = allAccounts || {}; + if (!allAccounts[email]) throw new Error('Cannot set cards for unknown account ' + email); + allAccounts[email].cards = cards; + return this.storage.set(Keys.BITPAY_ACCOUNTS_V2(network), allAccounts); + }); + }; + + // cards: [ + // eid: card id + // id: card id + // lastFourDigits: card number + // token: card token + // email: account email + // ] + getBitpayDebitCards(network: string): Promise { + return this.getBitpayAccounts(network).then(allAccounts => { + let allCards = []; + _.each(allAccounts, (account, email) => { + if (account.cards) { + // Add account's email to each card + var cards = _.clone(account.cards); + _.each(cards, function (x) { + x.email = email; + }); + + allCards = allCards.concat(cards); + } + }); + return allCards; + }); + }; + + removeBitpayDebitCard(network: string, cardEid: string): Promise { + return this.getBitpayAccounts(network).then(allAccounts => { + _.each(allAccounts, function (account) { + account.cards = _.reject(account.cards, { + eid: cardEid + }); + }); + + return this.storage.set(Keys.BITPAY_ACCOUNTS_V2(network), allAccounts); }); - // decryptOnMobile(str, function (err, str) { - // if (err) return cb(err); - // var p, err; - // try { - // p = Profile.fromString(str); - // } catch (e) { - // $log.debug('Could not read profile:', e); - // err = new Error('Could not read profile:' + p); - // } - // return cb(err, p); - // }); }; } diff --git a/src/providers/persistence/storage/local-storage.ts b/src/providers/persistence/storage/local-storage.ts index e51db7b4e..0be6ec308 100644 --- a/src/providers/persistence/storage/local-storage.ts +++ b/src/providers/persistence/storage/local-storage.ts @@ -28,7 +28,7 @@ export class LocalStorage implements IStorage { } set(k: string, v: any): Promise { - return new Promise(() => { + return new Promise((resolve) => { if (_.isObject(v)) { v = JSON.stringify(v); } @@ -37,11 +37,12 @@ export class LocalStorage implements IStorage { } this.ls.setItem(k, v); + resolve(); }); } remove(k: string): Promise { - return new Promise(() => { + return Promise.resolve().then(() => { this.ls.removeItem(k); }); } diff --git a/src/providers/persistence/storage/ram-storage.ts b/src/providers/persistence/storage/ram-storage.ts index 99b92a224..5b7c57aca 100644 --- a/src/providers/persistence/storage/ram-storage.ts +++ b/src/providers/persistence/storage/ram-storage.ts @@ -4,21 +4,13 @@ export class RamStorage implements IStorage { hash = {}; get(k: string): Promise { - return new Promise((resolve) => { - resolve(this.hash[k]); - }); + return Promise.resolve(this.hash[k]); }; set(k: string, v: any): Promise { - return new Promise((resolve) => { - this.hash[k] = v; - resolve(); - }); + return Promise.resolve().then(() => this.hash[k] = v); }; remove(k: string): Promise { - return new Promise((resolve) => { - delete this.hash[k]; - resolve(); - }); + return Promise.resolve().then(() => { delete this.hash[k]; }); }; create(k: string, v: any): Promise { return this.get(k).then((data) => {