932 lines
28 KiB
JavaScript
932 lines
28 KiB
JavaScript
'use strict';
|
|
angular.module('copayApp.services')
|
|
.factory('profileService', function profileServiceFactory($rootScope, $timeout, $filter, $log, sjcl, lodash, storageService, bwcService, configService, pushNotificationsService, gettext, gettextCatalog, bwcError, uxLanguage, platformInfo, $ionicHistory, txFormatService, $state) {
|
|
|
|
|
|
var isChromeApp = platformInfo.isChromeApp;
|
|
var isCordova = platformInfo.isCordova;
|
|
var isWP = platformInfo.isWP;
|
|
var isIOS = platformInfo.isIOS;
|
|
|
|
var root = {};
|
|
var errors = bwcService.getErrors();
|
|
var usePushNotifications = isCordova && !isWP;
|
|
|
|
var UPDATE_PERIOD = 15;
|
|
|
|
root.profile = null;
|
|
|
|
Object.defineProperty(root, "focusedClient", {
|
|
get: function() {
|
|
throw "focusedClient is not used any more"
|
|
},
|
|
set: function() {
|
|
throw "focusedClient is not used any more"
|
|
}
|
|
});
|
|
|
|
|
|
root.wallet = {}; // decorated version of client
|
|
|
|
root.updateWalletSettings = function(wallet) {
|
|
var defaults = configService.getDefaults();
|
|
configService.whenAvailable(function(config) {
|
|
wallet.usingCustomBWS = config.bwsFor && config.bwsFor[wallet.id] && (config.bwsFor[wallet.id] != defaults.bws.url);
|
|
wallet.name = (config.aliasFor && config.aliasFor[wallet.id]) || wallet.credentials.walletName;
|
|
wallet.color = (config.colorFor && config.colorFor[wallet.id]) || '#4A90E2';
|
|
wallet.email = config.emailFor && config.emailFor[wallet.id];
|
|
});
|
|
}
|
|
|
|
root.setBackupFlag = function(walletId) {
|
|
storageService.setBackupFlag(walletId, function(err) {
|
|
if (err) $log.error(err);
|
|
$log.debug('Backup flag stored');
|
|
root.wallet[walletId].needsBackup = false;
|
|
});
|
|
};
|
|
|
|
function _requiresBackup(wallet) {
|
|
if (wallet.isPrivKeyExternal()) return false;
|
|
if (!wallet.credentials.mnemonic) return false;
|
|
if (wallet.credentials.network == 'testnet') return false;
|
|
|
|
return true;
|
|
};
|
|
|
|
function _needsBackup(wallet, cb) {
|
|
if (!_requiresBackup(wallet))
|
|
return cb(false);
|
|
|
|
storageService.getBackupFlag(wallet.credentials.walletId, function(err, val) {
|
|
if (err) $log.error(err);
|
|
if (val) return cb(false);
|
|
return cb(true);
|
|
});
|
|
};
|
|
|
|
function _balanceIsHidden(wallet, cb) {
|
|
storageService.getHideBalanceFlag(wallet.credentials.walletId, function(err, shouldHideBalance) {
|
|
if (err) $log.error(err);
|
|
var hideBalance = (shouldHideBalance == 'true') ? true : false;
|
|
return cb(hideBalance);
|
|
});
|
|
};
|
|
// Adds a wallet client to profileService
|
|
root.bindWalletClient = function(wallet, opts) {
|
|
var opts = opts || {};
|
|
var walletId = wallet.credentials.walletId;
|
|
|
|
if ((root.wallet[walletId] && root.wallet[walletId].started) && !opts.force) {
|
|
return false;
|
|
}
|
|
|
|
// INIT WALLET VIEWMODEL
|
|
wallet.id = walletId;
|
|
wallet.started = true;
|
|
wallet.doNotVerifyPayPro = isChromeApp;
|
|
wallet.network = wallet.credentials.network;
|
|
wallet.copayerId = wallet.credentials.copayerId;
|
|
wallet.m = wallet.credentials.m;
|
|
wallet.n = wallet.credentials.n;
|
|
|
|
root.updateWalletSettings(wallet);
|
|
root.wallet[walletId] = wallet;
|
|
|
|
_needsBackup(wallet, function(val) {
|
|
wallet.needsBackup = val;
|
|
});
|
|
|
|
_balanceIsHidden(wallet, function(val) {
|
|
wallet.balanceHidden = val;
|
|
});
|
|
|
|
wallet.removeAllListeners();
|
|
|
|
wallet.on('report', function(n) {
|
|
$log.info('BWC Report:' + n);
|
|
});
|
|
|
|
wallet.on('notification', function(n) {
|
|
$log.debug('BWC Notification:', n);
|
|
|
|
// notification?
|
|
|
|
// TODO (put this in wallet ViewModel)
|
|
if (wallet.cachedStatus)
|
|
wallet.cachedStatus.isValid = false;
|
|
|
|
if (wallet.completeHistory)
|
|
wallet.completeHistory.isValid = false;
|
|
|
|
if (wallet.cachedActivity)
|
|
wallet.cachedActivity.isValid = false;
|
|
|
|
if (wallet.cachedTxps)
|
|
wallet.cachedTxps.isValid = false;
|
|
|
|
|
|
|
|
$rootScope.$emit('bwsEvent', wallet.id, n.type, n);
|
|
});
|
|
|
|
wallet.on('walletCompleted', function() {
|
|
$log.debug('Wallet completed');
|
|
|
|
root.updateCredentials(JSON.parse(wallet.export()), function() {
|
|
$rootScope.$emit('Local/WalletCompleted', walletId);
|
|
});
|
|
});
|
|
|
|
wallet.initialize({
|
|
notificationIncludeOwn: true,
|
|
}, function(err) {
|
|
if (err) {
|
|
$log.error('Could not init notifications err:', err);
|
|
return;
|
|
}
|
|
wallet.setNotificationsInterval(UPDATE_PERIOD);
|
|
wallet.openWallet(function(err) {
|
|
if (wallet.status !== true)
|
|
$log.log('Wallet + ' + walletId + ' status:' + wallet.status)
|
|
});
|
|
});
|
|
|
|
$rootScope.$on('Local/SettingsUpdated', function(e, walletId) {
|
|
if (!walletId || walletId == wallet.id) {
|
|
$log.debug('Updating settings for wallet:' + wallet.id);
|
|
root.updateWalletSettings(wallet);
|
|
}
|
|
});
|
|
|
|
return true;
|
|
};
|
|
|
|
var validationLock = false;
|
|
|
|
root.runValidation = function(client, delay, retryDelay) {
|
|
|
|
delay = delay || 500;
|
|
retryDelay = retryDelay || 50;
|
|
|
|
if (validationLock) {
|
|
return $timeout(function() {
|
|
$log.debug('ValidatingWallet Locked: Retrying in: ' + retryDelay);
|
|
return root.runValidation(client, delay, retryDelay);
|
|
}, retryDelay);
|
|
}
|
|
validationLock = true;
|
|
|
|
// IOS devices are already checked
|
|
var skipDeviceValidation = isIOS || root.profile.isDeviceChecked(platformInfo.ua);
|
|
var walletId = client.credentials.walletId;
|
|
|
|
$log.debug('ValidatingWallet: ' + walletId + ' skip Device:' + skipDeviceValidation);
|
|
$timeout(function() {
|
|
client.validateKeyDerivation({
|
|
skipDeviceValidation: skipDeviceValidation,
|
|
}, function(err, isOK) {
|
|
validationLock = false;
|
|
|
|
$log.debug('ValidatingWallet End: ' + walletId + ' isOK:' + isOK);
|
|
if (isOK) {
|
|
root.profile.setChecked(platformInfo.ua, walletId);
|
|
} else {
|
|
$log.warn('Key Derivation failed for wallet:' + walletId);
|
|
storageService.clearLastAddress(walletId, function() {});
|
|
}
|
|
|
|
root.storeProfileIfDirty();
|
|
});
|
|
}, delay);
|
|
};
|
|
|
|
// Used when reading wallets from the profile
|
|
root.bindWallet = function(credentials, cb) {
|
|
if (!credentials.walletId || !credentials.m)
|
|
return cb('bindWallet should receive credentials JSON');
|
|
|
|
// Create the client
|
|
var getBWSURL = function(walletId) {
|
|
var config = configService.getSync();
|
|
var defaults = configService.getDefaults();
|
|
return ((config.bwsFor && config.bwsFor[walletId]) || defaults.bws.url);
|
|
};
|
|
|
|
|
|
var client = bwcService.getClient(JSON.stringify(credentials), {
|
|
bwsurl: getBWSURL(credentials.walletId),
|
|
});
|
|
|
|
var skipKeyValidation = root.profile.isChecked(platformInfo.ua, credentials.walletId);
|
|
if (!skipKeyValidation)
|
|
root.runValidation(client, 500);
|
|
|
|
$log.info('Binding wallet:' + credentials.walletId + ' Validating?:' + !skipKeyValidation);
|
|
return cb(null, root.bindWalletClient(client));
|
|
};
|
|
|
|
root.bindProfile = function(profile, cb) {
|
|
root.profile = profile;
|
|
|
|
configService.get(function(err) {
|
|
$log.debug('Preferences read');
|
|
if (err) return cb(err);
|
|
|
|
function bindWallets(cb) {
|
|
var l = root.profile.credentials.length;
|
|
var i = 0,
|
|
totalBound = 0;
|
|
|
|
if (!l) return cb();
|
|
|
|
lodash.each(root.profile.credentials, function(credentials) {
|
|
root.bindWallet(credentials, function(err, bound) {
|
|
i++;
|
|
totalBound += bound;
|
|
if (i == l) {
|
|
$log.info('Bound ' + totalBound + ' out of ' + l + ' wallets');
|
|
return cb();
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
bindWallets(function() {
|
|
root.isBound = true;
|
|
|
|
lodash.each(root._queue, function(x) {
|
|
$timeout(function() {
|
|
return x();
|
|
}, 1);
|
|
});
|
|
root._queue = [];
|
|
|
|
|
|
|
|
root.isDisclaimerAccepted(function(val) {
|
|
if (!val) {
|
|
return cb(new Error('NONAGREEDDISCLAIMER: Non agreed disclaimer'));
|
|
}
|
|
if (usePushNotifications)
|
|
root.pushNotificationsInit();
|
|
$rootScope.$emit('disclaimerAccepted');
|
|
return cb();
|
|
});
|
|
});
|
|
});
|
|
};
|
|
|
|
root._queue = [];
|
|
root.whenAvailable = function(cb) {
|
|
if (!root.isBound) {
|
|
root._queue.push(cb);
|
|
return;
|
|
}
|
|
return cb();
|
|
};
|
|
|
|
root.pushNotificationsInit = function() {
|
|
var defaults = configService.getDefaults();
|
|
var push = pushNotificationsService.init(root.wallet);
|
|
|
|
push.on('notification', function(data) {
|
|
if (!data.additionalData.foreground) {
|
|
$log.debug('Push notification event: ', data.message);
|
|
|
|
$timeout(function() {
|
|
var wallets = root.getWallets();
|
|
var walletToFind = data.additionalData.walletId;
|
|
|
|
var walletFound = lodash.find(wallets, function(w) {
|
|
return (lodash.isEqual(walletToFind, sjcl.codec.hex.fromBits(sjcl.hash.sha256.hash(w.id))));
|
|
});
|
|
|
|
if (!walletFound) return $log.debug('Wallet not found');
|
|
}, 100);
|
|
}
|
|
});
|
|
};
|
|
|
|
root.loadAndBindProfile = function(cb) {
|
|
storageService.getProfile(function(err, profile) {
|
|
if (err) {
|
|
$rootScope.$emit('Local/DeviceError', err);
|
|
return cb(err);
|
|
}
|
|
if (!profile) {
|
|
// Migration??
|
|
storageService.tryToMigrate(function(err, migratedProfile) {
|
|
if (err) return cb(err);
|
|
if (!migratedProfile)
|
|
return cb(new Error('NOPROFILE: No profile'));
|
|
|
|
profile = migratedProfile;
|
|
return root.bindProfile(profile, cb);
|
|
})
|
|
} else {
|
|
$log.debug('Profile read');
|
|
return root.bindProfile(profile, cb);
|
|
}
|
|
});
|
|
};
|
|
|
|
var seedWallet = function(opts, cb) {
|
|
opts = opts || {};
|
|
var walletClient = bwcService.getClient(null, opts);
|
|
var network = opts.networkName || 'livenet';
|
|
|
|
if (opts.mnemonic) {
|
|
try {
|
|
opts.mnemonic = root._normalizeMnemonic(opts.mnemonic);
|
|
walletClient.seedFromMnemonic(opts.mnemonic, {
|
|
network: network,
|
|
passphrase: opts.passphrase,
|
|
account: opts.account || 0,
|
|
derivationStrategy: opts.derivationStrategy || 'BIP44',
|
|
});
|
|
|
|
} catch (ex) {
|
|
$log.info(ex);
|
|
return cb(gettext('Could not create: Invalid wallet recovery phrase'));
|
|
}
|
|
} else if (opts.extendedPrivateKey) {
|
|
try {
|
|
walletClient.seedFromExtendedPrivateKey(opts.extendedPrivateKey);
|
|
} catch (ex) {
|
|
$log.warn(ex);
|
|
return cb(gettext('Could not create using the specified extended private key'));
|
|
}
|
|
} else if (opts.extendedPublicKey) {
|
|
try {
|
|
walletClient.seedFromExtendedPublicKey(opts.extendedPublicKey, opts.externalSource, opts.entropySource, {
|
|
account: opts.account || 0,
|
|
derivationStrategy: opts.derivationStrategy || 'BIP44',
|
|
});
|
|
} catch (ex) {
|
|
$log.warn("Creating wallet from Extended Public Key Arg:", ex, opts);
|
|
return cb(gettext('Could not create using the specified extended public key'));
|
|
}
|
|
} else {
|
|
var lang = uxLanguage.getCurrentLanguage();
|
|
try {
|
|
walletClient.seedFromRandomWithMnemonic({
|
|
network: network,
|
|
passphrase: opts.passphrase,
|
|
language: lang,
|
|
account: 0,
|
|
});
|
|
} catch (e) {
|
|
$log.info('Error creating recovery phrase: ' + e.message);
|
|
if (e.message.indexOf('language') > 0) {
|
|
$log.info('Using default language for recovery phrase');
|
|
walletClient.seedFromRandomWithMnemonic({
|
|
network: network,
|
|
passphrase: opts.passphrase,
|
|
account: 0,
|
|
});
|
|
} else {
|
|
return cb(e);
|
|
}
|
|
}
|
|
}
|
|
return cb(null, walletClient);
|
|
};
|
|
|
|
// Creates a wallet on BWC/BWS
|
|
var doCreateWallet = function(opts, cb) {
|
|
$log.debug('Creating Wallet:', opts);
|
|
$timeout(function() {
|
|
seedWallet(opts, function(err, walletClient) {
|
|
if (err) return cb(err);
|
|
|
|
var name = opts.name || gettextCatalog.getString('Personal Wallet');
|
|
var myName = opts.myName || gettextCatalog.getString('me');
|
|
|
|
walletClient.createWallet(name, myName, opts.m, opts.n, {
|
|
network: opts.networkName,
|
|
singleAddress: opts.singleAddress,
|
|
walletPrivKey: opts.walletPrivKey,
|
|
}, function(err, secret) {
|
|
if (err) return bwcError.cb(err, gettext('Error creating wallet'), cb);
|
|
return cb(null, walletClient, secret);
|
|
});
|
|
});
|
|
}, 50);
|
|
};
|
|
|
|
// create and store a wallet
|
|
root.createWallet = function(opts, cb) {
|
|
doCreateWallet(opts, function(err, walletClient, secret) {
|
|
if (err) return cb(err);
|
|
|
|
addAndBindWalletClient(walletClient, {
|
|
bwsurl: opts.bwsurl
|
|
}, cb);
|
|
});
|
|
};
|
|
|
|
// joins and stores a wallet
|
|
root.joinWallet = function(opts, cb) {
|
|
var walletClient = bwcService.getClient();
|
|
$log.debug('Joining Wallet:', opts);
|
|
|
|
try {
|
|
var walletData = bwcService.parseSecret(opts.secret);
|
|
|
|
// check if exist
|
|
if (lodash.find(root.profile.credentials, {
|
|
'walletId': walletData.walletId
|
|
})) {
|
|
return cb(gettext('Cannot join the same wallet more that once'));
|
|
}
|
|
} catch (ex) {
|
|
$log.debug(ex);
|
|
return cb(gettext('Bad wallet invitation'));
|
|
}
|
|
opts.networkName = walletData.network;
|
|
$log.debug('Joining Wallet:', opts);
|
|
|
|
seedWallet(opts, function(err, walletClient) {
|
|
if (err) return cb(err);
|
|
|
|
walletClient.joinWallet(opts.secret, opts.myName || 'me', {}, function(err) {
|
|
if (err) return bwcError.cb(err, gettext('Could not join wallet'), cb);
|
|
addAndBindWalletClient(walletClient, {
|
|
bwsurl: opts.bwsurl
|
|
}, cb);
|
|
});
|
|
});
|
|
};
|
|
|
|
root.getWallet = function(walletId) {
|
|
return root.wallet[walletId];
|
|
};
|
|
|
|
|
|
root.deleteWalletClient = function(client, cb) {
|
|
var walletId = client.credentials.walletId;
|
|
|
|
pushNotificationsService.unsubscribe(root.getWallet(walletId), function(err) {
|
|
if (err) $log.warn('Unsubscription error: ' + err.message);
|
|
else $log.debug('Unsubscribed from push notifications service');
|
|
});
|
|
|
|
$log.debug('Deleting Wallet:', client.credentials.walletName);
|
|
client.removeAllListeners();
|
|
|
|
root.profile.deleteWallet(walletId);
|
|
|
|
delete root.wallet[walletId];
|
|
|
|
storageService.removeAllWalletData(walletId, function(err) {
|
|
if (err) $log.warn(err);
|
|
});
|
|
|
|
storageService.storeProfile(root.profile, function(err) {
|
|
if (err) return cb(err);
|
|
return cb();
|
|
});
|
|
};
|
|
|
|
root.setMetaData = function(walletClient, addressBook, cb) {
|
|
storageService.getAddressbook(walletClient.credentials.network, function(err, localAddressBook) {
|
|
var localAddressBook1 = {};
|
|
try {
|
|
localAddressBook1 = JSON.parse(localAddressBook);
|
|
} catch (ex) {
|
|
$log.warn(ex);
|
|
}
|
|
var mergeAddressBook = lodash.merge(addressBook, localAddressBook1);
|
|
storageService.setAddressbook(walletClient.credentials.network, JSON.stringify(addressBook), function(err) {
|
|
if (err) return cb(err);
|
|
return cb(null);
|
|
});
|
|
});
|
|
}
|
|
|
|
// Adds and bind a new client to the profile
|
|
var addAndBindWalletClient = function(client, opts, cb) {
|
|
if (!client || !client.credentials)
|
|
return cb(gettext('Could not access wallet'));
|
|
|
|
var walletId = client.credentials.walletId
|
|
|
|
if (!root.profile.addWallet(JSON.parse(client.export())))
|
|
return cb(gettext('Wallet already in Copay'));
|
|
|
|
|
|
var skipKeyValidation = root.profile.isChecked(platformInfo.ua, walletId);
|
|
if (!skipKeyValidation)
|
|
root.runValidation(client);
|
|
|
|
root.bindWalletClient(client);
|
|
|
|
var saveBwsUrl = function(cb) {
|
|
var defaults = configService.getDefaults();
|
|
var bwsFor = {};
|
|
bwsFor[walletId] = opts.bwsurl || defaults.bws.url;
|
|
|
|
// Dont save the default
|
|
if (bwsFor[walletId] == defaults.bws.url)
|
|
return cb();
|
|
|
|
configService.set({
|
|
bwsFor: bwsFor,
|
|
}, function(err) {
|
|
if (err) $log.warn(err);
|
|
return cb();
|
|
});
|
|
};
|
|
|
|
saveBwsUrl(function() {
|
|
storageService.storeProfile(root.profile, function(err) {
|
|
var config = configService.getSync();
|
|
if (config.pushNotifications.enabled)
|
|
pushNotificationsService.enableNotifications(root.wallet);
|
|
return cb(err, client);
|
|
});
|
|
});
|
|
};
|
|
|
|
root.storeProfileIfDirty = function(cb) {
|
|
if (root.profile.dirty) {
|
|
storageService.storeProfile(root.profile, function(err) {
|
|
$log.debug('Saved modified Profile');
|
|
if (cb) return cb(err);
|
|
});
|
|
} else {
|
|
if (cb) return cb();
|
|
};
|
|
};
|
|
|
|
root.importWallet = function(str, opts, cb) {
|
|
|
|
var walletClient = bwcService.getClient(null, opts);
|
|
|
|
$log.debug('Importing Wallet:', opts);
|
|
try {
|
|
walletClient.import(str, {
|
|
compressed: opts.compressed,
|
|
password: opts.password
|
|
});
|
|
} catch (err) {
|
|
return cb(gettext('Could not import. Check input file and spending password'));
|
|
}
|
|
|
|
str = JSON.parse(str);
|
|
|
|
var addressBook = str.addressBook || {};
|
|
|
|
addAndBindWalletClient(walletClient, {
|
|
bwsurl: opts.bwsurl
|
|
}, function(err, walletId) {
|
|
if (err) return cb(err);
|
|
root.setMetaData(walletClient, addressBook, function(error) {
|
|
if (error) $log.warn(error);
|
|
return cb(err, walletClient);
|
|
});
|
|
});
|
|
};
|
|
|
|
root.importExtendedPrivateKey = function(xPrivKey, opts, cb) {
|
|
var walletClient = bwcService.getClient(null, opts);
|
|
$log.debug('Importing Wallet xPrivKey');
|
|
|
|
walletClient.importFromExtendedPrivateKey(xPrivKey, opts, function(err) {
|
|
if (err) {
|
|
if (err instanceof errors.NOT_AUTHORIZED)
|
|
return cb(err);
|
|
|
|
return bwcError.cb(err, gettext('Could not import'), cb);
|
|
}
|
|
|
|
addAndBindWalletClient(walletClient, {
|
|
bwsurl: opts.bwsurl
|
|
}, cb);
|
|
});
|
|
};
|
|
|
|
root._normalizeMnemonic = function(words) {
|
|
var isJA = words.indexOf('\u3000') > -1;
|
|
var wordList = words.split(/[\u3000\s]+/);
|
|
|
|
return wordList.join(isJA ? '\u3000' : ' ');
|
|
};
|
|
|
|
root.importMnemonic = function(words, opts, cb) {
|
|
var walletClient = bwcService.getClient(null, opts);
|
|
|
|
$log.debug('Importing Wallet Mnemonic');
|
|
|
|
words = root._normalizeMnemonic(words);
|
|
walletClient.importFromMnemonic(words, {
|
|
network: opts.networkName,
|
|
passphrase: opts.passphrase,
|
|
account: opts.account || 0,
|
|
}, function(err) {
|
|
if (err) {
|
|
if (err instanceof errors.NOT_AUTHORIZED)
|
|
return cb(err);
|
|
|
|
return bwcError.cb(err, gettext('Could not import'), cb);
|
|
}
|
|
|
|
addAndBindWalletClient(walletClient, {
|
|
bwsurl: opts.bwsurl
|
|
}, cb);
|
|
});
|
|
};
|
|
|
|
root.importExtendedPublicKey = function(opts, cb) {
|
|
var walletClient = bwcService.getClient(null, opts);
|
|
$log.debug('Importing Wallet XPubKey');
|
|
|
|
walletClient.importFromExtendedPublicKey(opts.extendedPublicKey, opts.externalSource, opts.entropySource, {
|
|
account: opts.account || 0,
|
|
derivationStrategy: opts.derivationStrategy || 'BIP44',
|
|
}, function(err) {
|
|
if (err) {
|
|
|
|
// in HW wallets, req key is always the same. They can't addAccess.
|
|
if (err instanceof errors.NOT_AUTHORIZED)
|
|
err.name = 'WALLET_DOES_NOT_EXIST';
|
|
|
|
return bwcError.cb(err, gettext('Could not import'), cb);
|
|
}
|
|
|
|
addAndBindWalletClient(walletClient, {
|
|
bwsurl: opts.bwsurl
|
|
}, cb);
|
|
});
|
|
};
|
|
|
|
root.createProfile = function(cb) {
|
|
$log.info('Creating profile');
|
|
var defaults = configService.getDefaults();
|
|
|
|
configService.get(function(err) {
|
|
if (err) $log.debug(err);
|
|
|
|
var p = Profile.create();
|
|
storageService.storeNewProfile(p, function(err) {
|
|
if (err) return cb(err);
|
|
root.bindProfile(p, function(err) {
|
|
// ignore NONAGREEDDISCLAIMER
|
|
if (err && err.toString().match('NONAGREEDDISCLAIMER')) return cb();
|
|
return cb(err);
|
|
});
|
|
});
|
|
});
|
|
};
|
|
|
|
root.createDefaultWallet = function(cb) {
|
|
var opts = {};
|
|
opts.m = 1;
|
|
opts.n = 1;
|
|
opts.network = 'livenet';
|
|
root.createWallet(opts, cb);
|
|
};
|
|
|
|
root.setDisclaimerAccepted = function(cb) {
|
|
root.profile.disclaimerAccepted = true;
|
|
storageService.storeProfile(root.profile, function(err) {
|
|
return cb(err);
|
|
});
|
|
};
|
|
|
|
root.isDisclaimerAccepted = function(cb) {
|
|
var disclaimerAccepted = root.profile && root.profile.disclaimerAccepted;
|
|
if (disclaimerAccepted)
|
|
return cb(true);
|
|
|
|
// OLD flag
|
|
storageService.getCopayDisclaimerFlag(function(err, val) {
|
|
if (val) {
|
|
root.profile.disclaimerAccepted = true;
|
|
return cb(true);
|
|
} else {
|
|
return cb();
|
|
}
|
|
});
|
|
};
|
|
|
|
root.updateCredentials = function(credentials, cb) {
|
|
root.profile.updateWallet(credentials);
|
|
storageService.storeProfile(root.profile, cb);
|
|
};
|
|
|
|
root.getWallets = function(opts) {
|
|
|
|
if (opts && !lodash.isObject(opts))
|
|
throw "bad argument";
|
|
|
|
opts = opts || {};
|
|
|
|
var ret = lodash.values(root.wallet);
|
|
|
|
if (opts.network) {
|
|
ret = lodash.filter(ret, function(x) {
|
|
return (x.credentials.network == opts.network);
|
|
});
|
|
}
|
|
|
|
if (opts.n) {
|
|
ret = lodash.filter(ret, function(w) {
|
|
return (w.credentials.n == opts.n);
|
|
});
|
|
}
|
|
|
|
if (opts.onlyComplete) {
|
|
ret = lodash.filter(ret, function(w) {
|
|
return w.isComplete();
|
|
});
|
|
} else {}
|
|
|
|
return lodash.sortBy(ret, [
|
|
|
|
function(x) {
|
|
return x.isComplete();
|
|
}, 'createdOn'
|
|
]);
|
|
};
|
|
|
|
root.toggleHideBalanceFlag = function(walletId, cb) {
|
|
root.wallet[walletId].balanceHidden = !root.wallet[walletId].balanceHidden;
|
|
storageService.setHideBalanceFlag(walletId, root.wallet[walletId].balanceHidden.toString(), cb);
|
|
};
|
|
|
|
root.getNotifications = function(opts, cb) {
|
|
opts = opts || {};
|
|
|
|
var TIME_STAMP = 60 * 60 * 24 * 7;
|
|
var MAX = 100;
|
|
|
|
var typeFilter1 = {
|
|
'NewBlock': 1,
|
|
'BalanceUpdated': 1,
|
|
'NewOutgoingTxByThirdParty': 1,
|
|
'NewAddress': 1,
|
|
'TxProposalFinallyAccepted': 1,
|
|
'TxProposalFinallyRejected': 1,
|
|
};
|
|
|
|
var typeFilter2 = {
|
|
'TxProposalAcceptedBy': 1,
|
|
'TxProposalRejectedBy': 1,
|
|
'NewTxProposal': 1,
|
|
}
|
|
|
|
var w = root.getWallets();
|
|
if (lodash.isEmpty(w)) return cb();
|
|
|
|
var l = w.length,
|
|
j = 0,
|
|
notifications = [];
|
|
|
|
|
|
function isActivityCached(wallet) {
|
|
return wallet.cachedActivity && wallet.cachedActivity.isValid;
|
|
};
|
|
|
|
|
|
function updateNotifications(wallet, cb2) {
|
|
if (isActivityCached(wallet) && !opts.force) return cb2();
|
|
|
|
wallet.getNotifications({
|
|
timeSpan: TIME_STAMP,
|
|
includeOwn: true,
|
|
}, function(err, n) {
|
|
if (err) return cb2(err);
|
|
|
|
wallet.cachedActivity = {
|
|
n: n.slice(-MAX),
|
|
isValid: true,
|
|
};
|
|
|
|
return cb2();
|
|
});
|
|
};
|
|
|
|
function process(notifications) {
|
|
if (!notifications) return [];
|
|
|
|
var shown = lodash.sortBy(notifications, 'createdOn').reverse();
|
|
|
|
shown = shown.splice(0, opts.limit || MAX);
|
|
|
|
lodash.each(shown, function(x) {
|
|
x.txpId = x.data ? x.data.txProposalId : null;
|
|
x.txid = x.data ? x.data.txid : null;
|
|
x.types = [x.type];
|
|
|
|
if (x.data && x.data.amount)
|
|
x.amountStr = txFormatService.formatAmountStr(x.data.amount);
|
|
|
|
x.action = function() {
|
|
// TODO?
|
|
$state.go('tabs.details', {
|
|
walletId: x.walletId,
|
|
txpId: x.txpId,
|
|
txid: x.txid,
|
|
});
|
|
};
|
|
});
|
|
|
|
var finale = shown; // GROUPING DISABLED!
|
|
|
|
// var finale = [],
|
|
// prev;
|
|
//
|
|
//
|
|
// // Item grouping... DISABLED.
|
|
//
|
|
// // REMOVE (if we want 1-to-1 notification) ????
|
|
// lodash.each(shown, function(x) {
|
|
// if (prev && prev.walletId === x.walletId && prev.txpId && prev.txpId === x.txpId && prev.creatorId && prev.creatorId === x.creatorId) {
|
|
// prev.types.push(x.type);
|
|
// prev.data = lodash.assign(prev.data, x.data);
|
|
// prev.txid = prev.txid || x.txid;
|
|
// prev.amountStr = prev.amountStr || x.amountStr;
|
|
// prev.creatorName = prev.creatorName || x.creatorName;
|
|
// } else {
|
|
// finale.push(x);
|
|
// prev = x;
|
|
// }
|
|
// });
|
|
//
|
|
|
|
var u = bwcService.getUtils();
|
|
lodash.each(finale, function(x) {
|
|
if (x.data && x.data.message && x.wallet && x.wallet.credentials.sharedEncryptingKey) {
|
|
// TODO TODO TODO => BWC
|
|
x.message = u.decryptMessage(x.data.message, x.wallet.credentials.sharedEncryptingKey);
|
|
}
|
|
});
|
|
|
|
return finale;
|
|
};
|
|
|
|
lodash.each(w, function(wallet) {
|
|
updateNotifications(wallet, function(err) {
|
|
j++;
|
|
if (err) {
|
|
$log.warn('Error updating notifications:' + err);
|
|
} else {
|
|
var n = lodash.filter(wallet.cachedActivity.n, function(x) {
|
|
return !typeFilter1[x.type];
|
|
});
|
|
|
|
if (wallet.n == 1) {
|
|
var n = lodash.filter(n, function(x) {
|
|
return !typeFilter2[x.type];
|
|
});
|
|
}
|
|
|
|
var idToName = {};
|
|
if (wallet.cachedStatus) {
|
|
lodash.each(wallet.cachedStatus.wallet.copayers, function(c) {
|
|
idToName[c.id] = c.name;
|
|
});
|
|
}
|
|
|
|
lodash.each(n, function(x) {
|
|
x.wallet = wallet;
|
|
if (x.creatorId && wallet.cachedStatus) {
|
|
x.creatorName = idToName[x.creatorId];
|
|
};
|
|
});
|
|
|
|
notifications.push(n);
|
|
}
|
|
if (j == l) {
|
|
notifications = lodash.sortBy(notifications, 'createdOn');
|
|
notifications = lodash.compact(lodash.flatten(notifications)).slice(0, MAX);
|
|
return cb(null, process(notifications));
|
|
};
|
|
});
|
|
});
|
|
};
|
|
|
|
|
|
root.getTxps = function(opts, cb) {
|
|
var MAX = 100;
|
|
opts = opts || {};
|
|
|
|
var w = root.getWallets();
|
|
if (lodash.isEmpty(w)) return cb();
|
|
|
|
var txps = [];
|
|
|
|
lodash.each(w, function(x) {
|
|
if (x.pendingTxps)
|
|
txps = txps.concat(x.pendingTxps);
|
|
});
|
|
txps = lodash.sortBy(txps, 'pendingForUs', 'createdOn');
|
|
txps = lodash.compact(lodash.flatten(txps)).slice(0, MAX);
|
|
var n = txps.length;
|
|
return cb(null, txps, n);
|
|
};
|
|
|
|
return root;
|
|
});
|