From 4e8bd0634c68adb8a57ca479ed1f20e817b458a3 Mon Sep 17 00:00:00 2001 From: Gustavo Maximiliano Cortez Date: Fri, 3 Feb 2017 18:03:29 -0300 Subject: [PATCH] New push notifications --- .gitignore | 4 + app-template/bitpay/appConfig.json | 1 + app-template/config-template.xml | 5 +- app-template/copay/appConfig.json | 1 + src/js/controllers/advancedSettings.js | 2 +- src/js/controllers/onboarding/collectEmail.js | 4 +- .../controllers/onboarding/notifications.js | 4 +- src/js/controllers/preferencesDelete.js | 3 +- .../controllers/preferencesNotifications.js | 14 +- src/js/controllers/tab-home.js | 7 +- src/js/services/configService.js | 17 +- src/js/services/profileService.js | 42 +--- src/js/services/pushNotificationsService.js | 186 +++++++++++------- 13 files changed, 138 insertions(+), 152 deletions(-) diff --git a/.gitignore b/.gitignore index d6ef6fa6c..21dea94db 100644 --- a/.gitignore +++ b/.gitignore @@ -118,3 +118,7 @@ cordova/ios/splash/ cordova/ios/icons/ cordova/project/ +## Firebase +GoogleService-Info.plist +google-services.json + diff --git a/app-template/bitpay/appConfig.json b/app-template/bitpay/appConfig.json index 03a14ea5c..5c3b9d67d 100644 --- a/app-template/bitpay/appConfig.json +++ b/app-template/bitpay/appConfig.json @@ -1,6 +1,7 @@ { "packageName": "bitpay", "packageDescription": "Secure Bitcoin Wallet", + "packageNameId": "com.bitpay.wallet", "userVisibleName": "BitPay", "purposeLine": "Secure Bitcoin Wallet", "bundleName": "wallet", diff --git a/app-template/config-template.xml b/app-template/config-template.xml index 1f179776b..e984f2e8d 100644 --- a/app-template/config-template.xml +++ b/app-template/config-template.xml @@ -61,12 +61,11 @@ - - - + + diff --git a/app-template/copay/appConfig.json b/app-template/copay/appConfig.json index 037713a3f..dacdf9c65 100644 --- a/app-template/copay/appConfig.json +++ b/app-template/copay/appConfig.json @@ -1,6 +1,7 @@ { "packageName": "copay", "packageDescription": "Copay Bitcoin Wallet", + "packageNameId": "com.bitpay.copay", "userVisibleName": "Copay", "purposeLine": "Copay Bitcoin Wallet", "bundleName": "copay", diff --git a/src/js/controllers/advancedSettings.js b/src/js/controllers/advancedSettings.js index 0664a0804..c5cd65103 100644 --- a/src/js/controllers/advancedSettings.js +++ b/src/js/controllers/advancedSettings.js @@ -1,6 +1,6 @@ 'use strict'; -angular.module('copayApp.controllers').controller('advancedSettingsController', function($scope, $rootScope, $log, $window, lodash, configService, uxLanguage, platformInfo, pushNotificationsService, profileService, feeService, storageService, $ionicHistory, $timeout, $ionicScrollDelegate) { +angular.module('copayApp.controllers').controller('advancedSettingsController', function($scope, $rootScope, $log, $window, lodash, configService, uxLanguage, platformInfo, profileService, feeService, storageService, $ionicHistory, $timeout, $ionicScrollDelegate) { var updateConfig = function() { var config = configService.getSync(); diff --git a/src/js/controllers/onboarding/collectEmail.js b/src/js/controllers/onboarding/collectEmail.js index be1cafd24..7a23da915 100644 --- a/src/js/controllers/onboarding/collectEmail.js +++ b/src/js/controllers/onboarding/collectEmail.js @@ -1,6 +1,6 @@ 'use strict'; -angular.module('copayApp.controllers').controller('collectEmailController', function($scope, $state, $timeout, $stateParams, $ionicConfig, profileService, configService, walletService, platformInfo) { +angular.module('copayApp.controllers').controller('collectEmailController', function($scope, $state, $timeout, $stateParams, $ionicConfig, profileService, configService, walletService, platformInfo, pushNotificationsService) { $scope.$on("$ionicView.beforeLeave", function() { $ionicConfig.views.swipeBackEnabled(true); @@ -46,7 +46,7 @@ angular.module('copayApp.controllers').controller('collectEmailController', func walletId: walletId }); } else { - profileService.pushNotificationsInit(); + pushNotificationsService.init(); $state.go('onboarding.backupRequest', { walletId: walletId }); diff --git a/src/js/controllers/onboarding/notifications.js b/src/js/controllers/onboarding/notifications.js index e42d37ac1..c4554c84b 100644 --- a/src/js/controllers/onboarding/notifications.js +++ b/src/js/controllers/onboarding/notifications.js @@ -1,6 +1,6 @@ 'use strict'; -angular.module('copayApp.controllers').controller('notificationsController', function($scope, $state, $timeout, $stateParams, $ionicConfig, profileService, configService, $interval) { +angular.module('copayApp.controllers').controller('notificationsController', function($scope, $state, $timeout, $stateParams, $ionicConfig, profileService, configService, $interval, pushNotificationsService) { $scope.$on("$ionicView.enter", function() { $ionicConfig.views.swipeBackEnabled(false); @@ -15,7 +15,7 @@ angular.module('copayApp.controllers').controller('notificationsController', fun $scope.allowNotif = function() { $scope.notificationDialogOpen = true; $timeout(function() { - profileService.pushNotificationsInit(); + pushNotificationsService.init(); }); $scope.notificationPromise = $interval(function() { PushNotification.hasPermission(function(data) { diff --git a/src/js/controllers/preferencesDelete.js b/src/js/controllers/preferencesDelete.js index 442cb7b0d..21c1d1c64 100644 --- a/src/js/controllers/preferencesDelete.js +++ b/src/js/controllers/preferencesDelete.js @@ -1,7 +1,7 @@ 'use strict'; angular.module('copayApp.controllers').controller('preferencesDeleteWalletController', - function($scope, $ionicHistory, gettextCatalog, lodash, profileService, $state, ongoingProcess, popupService) { + function($scope, $ionicHistory, gettextCatalog, lodash, profileService, $state, ongoingProcess, popupService, pushNotificationsService) { $scope.$on("$ionicView.beforeEnter", function(event, data) { if (!data.stateParams || !data.stateParams.walletId) { @@ -36,6 +36,7 @@ angular.module('copayApp.controllers').controller('preferencesDeleteWalletContro if (err) { popupService.showAlert(gettextCatalog.getString('Error'), err.message || err); } else { + pushNotificationsService.unsubscribe($scope.wallet); $ionicHistory.nextViewOptions({ disableAnimate: true, historyRoot: true diff --git a/src/js/controllers/preferencesNotifications.js b/src/js/controllers/preferencesNotifications.js index 219ad82cd..c2726cbae 100644 --- a/src/js/controllers/preferencesNotifications.js +++ b/src/js/controllers/preferencesNotifications.js @@ -9,7 +9,7 @@ angular.module('copayApp.controllers').controller('preferencesNotificationsContr $scope.isIOSApp = platformInfo.isIOS && platformInfo.isCordova; $scope.pushNotifications = { - value: config.pushNotifications.enabled + value: config.pushNotificationsEnabled }; $scope.latestEmail = { @@ -31,16 +31,14 @@ angular.module('copayApp.controllers').controller('preferencesNotificationsContr $scope.pushNotificationsChange = function() { if (!$scope.pushNotifications) return; var opts = { - pushNotifications: { - enabled: $scope.pushNotifications.value - } + pushNotificationsEnabled: $scope.pushNotifications.value }; configService.set(opts, function(err) { - if (opts.pushNotifications.enabled) - profileService.pushNotificationsInit(); - else - pushNotificationsService.disableNotifications(profileService.getWallets()); if (err) $log.debug(err); + if (opts.pushNotificationsEnabled) + pushNotificationsService.init(); + else + pushNotificationsService.disable(); }); }; diff --git a/src/js/controllers/tab-home.js b/src/js/controllers/tab-home.js index d3d6e9610..7a3fd83f8 100644 --- a/src/js/controllers/tab-home.js +++ b/src/js/controllers/tab-home.js @@ -1,7 +1,7 @@ 'use strict'; angular.module('copayApp.controllers').controller('tabHomeController', - function($rootScope, $timeout, $scope, $state, $stateParams, $ionicModal, $ionicScrollDelegate, $window, gettextCatalog, lodash, popupService, ongoingProcess, externalLinkService, latestReleaseService, profileService, walletService, configService, $log, platformInfo, storageService, txpModalService, appConfigService, startupService, addressbookService, feedbackService, bwcError, nextStepsService, buyAndSellService, homeIntegrationsService, bitpayCardService) { + function($rootScope, $timeout, $scope, $state, $stateParams, $ionicModal, $ionicScrollDelegate, $window, gettextCatalog, lodash, popupService, ongoingProcess, externalLinkService, latestReleaseService, profileService, walletService, configService, $log, platformInfo, storageService, txpModalService, appConfigService, startupService, addressbookService, feedbackService, bwcError, nextStepsService, buyAndSellService, homeIntegrationsService, bitpayCardService, pushNotificationsService) { var wallet; var listeners = []; var notifications = []; @@ -105,8 +105,7 @@ angular.module('copayApp.controllers').controller('tabHomeController', $scope.bitpayCardItems = cards; }); - configService.whenAvailable(function() { - var config = configService.getSync(); + configService.whenAvailable(function(config) { $scope.recentTransactionsEnabled = config.recentTransactions.enabled; if ($scope.recentTransactionsEnabled) getNotifications(); @@ -116,6 +115,8 @@ angular.module('copayApp.controllers').controller('tabHomeController', $scope.nextStepsItems = nextStepsService.get(); } + pushNotificationsService.init(); + $timeout(function() { $ionicScrollDelegate.resize(); $scope.$apply(); diff --git a/src/js/services/configService.js b/src/js/services/configService.js index 9e447f7b9..cfc6ad4ac 100644 --- a/src/js/services/configService.js +++ b/src/js/services/configService.js @@ -59,22 +59,7 @@ angular.module('copayApp.services').factory('configService', function(storageSer url: 'https://api.github.com/repos/bitpay/copay/releases/latest' }, - pushNotifications: { - enabled: true, - config: { - android: { - senderID: '1036948132229', - icon: 'push', - iconColor: '#2F4053' - }, - ios: { - alert: 'true', - badge: 'true', - sound: 'true', - }, - windows: {}, - } - }, + pushNotificationsEnabled: true, emailNotifications: { enabled: false, diff --git a/src/js/services/profileService.js b/src/js/services/profileService.js index d6ddc45c3..ae2d970b2 100644 --- a/src/js/services/profileService.js +++ b/src/js/services/profileService.js @@ -1,6 +1,6 @@ 'use strict'; angular.module('copayApp.services') - .factory('profileService', function profileServiceFactory($rootScope, $timeout, $filter, $log, sjcl, lodash, storageService, bwcService, configService, pushNotificationsService, gettextCatalog, bwcError, uxLanguage, platformInfo, txFormatService, $state) { + .factory('profileService', function profileServiceFactory($rootScope, $timeout, $filter, $log, sjcl, lodash, storageService, bwcService, configService, gettextCatalog, bwcError, uxLanguage, platformInfo, txFormatService, $state) { var isChromeApp = platformInfo.isChromeApp; @@ -274,9 +274,6 @@ angular.module('copayApp.services') if (!val) { return cb(new Error('NONAGREEDDISCLAIMER: Non agreed disclaimer')); } - var config = configService.getSync(); - if (config.pushNotifications.enabled && usePushNotifications) - root.pushNotificationsInit(); return cb(); }); }); @@ -292,35 +289,6 @@ angular.module('copayApp.services') return cb(); }; - root.pushNotificationsInit = function() { - var defaults = configService.getDefaults(); - var push = pushNotificationsService.init(root.wallet); - - if (!push) return; - - 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); - } - }); - - push.on('error', function(e) { - $log.warn('Error with push notifications:' + e.message); - }); - - }; - root.loadAndBindProfile = function(cb) { storageService.getProfile(function(err, profile) { if (err) { @@ -481,11 +449,6 @@ angular.module('copayApp.services') var walletId = client.credentials.walletId; var config = configService.getSync(); - if (config.pushNotifications.enabled) - 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(); @@ -556,9 +519,6 @@ angular.module('copayApp.services') saveBwsUrl(function() { storageService.storeProfile(root.profile, function(err) { - var config = configService.getSync(); - if (config.pushNotifications.enabled) - pushNotificationsService.enableNotifications(root.wallet); return cb(err, client); }); }); diff --git a/src/js/services/pushNotificationsService.js b/src/js/services/pushNotificationsService.js index 904ae4246..63090faae 100644 --- a/src/js/services/pushNotificationsService.js +++ b/src/js/services/pushNotificationsService.js @@ -1,87 +1,123 @@ 'use strict'; -angular.module('copayApp.services') - .factory('pushNotificationsService', function($log, platformInfo, storageService, configService, lodash) { - var root = {}; - var isCordova = platformInfo.isCordova; - var isWP = platformInfo.isWP; - var isIOS = platformInfo.isIOS; - var isAndroid = platformInfo.isAndroid; +angular.module('copayApp.services').factory('pushNotificationsService', function pushNotificationsService($log, $state, $ionicHistory, platformInfo, lodash, appConfigService, profileService, configService) { + var root = {}; + var isIOS = platformInfo.isIOS; + var isAndroid = platformInfo.isAndroid; + var usePushNotifications = platformInfo.isCordova && !platformInfo.isWP; - var usePushNotifications = isCordova && !isWP; + var _token = null; - root.init = function(walletsClients) { - var defaults = configService.getDefaults(); - try { - var push = PushNotification.init(defaults.pushNotifications.config); - } catch(e) { - $log.error(e); - return; - }; + root.init = function() { + if (!usePushNotifications || _token) return; + configService.whenAvailable(function(config) { + if (!config.pushNotificationsEnabled) return; + + $log.debug('Starting push notification registration...'); - push.on('registration', function(data) { - $log.debug('Starting push notification registration'); - root.token = data.registrationId; - var config = configService.getSync(); - if (config.pushNotifications.enabled) root.enableNotifications(walletsClients); - }); + //Keep in mind the function will return null if the token has not been established yet. + FCMPlugin.getToken(function(token) { + $log.debug('Get token for push notifications...'); + _token = token; + root.enable(); + }); + }); + }; - return push; + root.enable = function() { + if (!_token) { + $log.warn('No token available for this device. Cannot set push notifications. Needs registration.'); + return; } - root.enableNotifications = function(walletsClients) { - if (!usePushNotifications) return; + var wallets = profileService.getWallets(); + lodash.forEach(wallets, function(walletClient) { + _subscribe(walletClient); + }); + }; - var config = configService.getSync(); - if (!config.pushNotifications.enabled) return; + root.disable = function() { + if (!_token) { + $log.warn('No token available for this device. Cannot disable push notifications.'); + return; + } - if (!root.token) { - $log.warn('No token available for this device. Cannot set push notifications. Needs registration.'); - return; + var wallets = profileService.getWallets(); + lodash.forEach(wallets, function(walletClient) { + _unsubscribe(walletClient); + }); + _token = null; + }; + + root.unsubscribe = function(walletClient) { + if (!_token) return; + _unsubscribe(walletClient); + }; + + var _subscribe = function(walletClient) { + var opts = { + token : _token, + platform: isIOS ? 'ios' : isAndroid ? 'android' : null, + packageName : appConfigService.packageNameId + }; + walletClient.pushNotificationsSubscribe(opts, function(err) { + if (err) $log.error(walletClient.name + ': Subscription Push Notifications error. ', JSON.stringify(err)); + else $log.debug(walletClient.name + ': Subscription Push Notifications success.'); + }); + }; + + var _unsubscribe = function(walletClient, cb) { + walletClient.pushNotificationsUnsubscribe(_token, function(err) { + if (err) $log.error(walletClient.name + ': Unsubscription Push Notifications error. ', JSON.stringify(err)); + else $log.debug(walletClient.name + ': Unsubscription Push Notifications Success.'); + }); + }; + + var _openWallet = function(walletId) { + var wallet = profileService.getWallet(walletId); + if (!wallet) return; + if (!wallet.isComplete()) { + return $state.go('tabs.copayers', { + walletId: walletId + }); + } + + $state.go('tabs.wallet', { + walletId: walletId + }); + }; + + if (usePushNotifications) { + + FCMPlugin.onTokenRefresh(function(token) { + if (!_token) return; + $log.debug('Refresh and update token for push notifications...'); + _token = token; + root.enable(); + }); + + FCMPlugin.onNotification(function(data) { + if (!_token) return; + $log.debug('New Event Push onNotification: ' + JSON.stringify(data)); + var walletId, copayerId; + if(data.wasTapped) { + // Notification was received on device tray and tapped by the user. + var walletId = data ? data.walletId : null; + if (!walletId) return; + $ionicHistory.nextViewOptions({ + disableAnimate: true, + historyRoot: true + }); + $ionicHistory.clearHistory(); + $state.go('tabs.home').then(function() { + _openWallet(walletId); + }); + } else { + // TODO + // Notification was received in foreground. Maybe the user needs to be notified. } + }); + } - lodash.forEach(walletsClients, function(walletClient) { - var opts = {}; - opts.type = isIOS ? "ios" : isAndroid ? "android" : null; - opts.token = root.token; - root.subscribe(opts, walletClient, function(err, response) { - if (err) $log.warn('Subscription error: ' + err.message + ': ' + JSON.stringify(opts)); - else $log.debug('Subscribed to push notifications service: ' + JSON.stringify(response)); - }); - }); - } + return root; - root.disableNotifications = function(walletsClients) { - if (!usePushNotifications) return; - - lodash.forEach(walletsClients, function(walletClient) { - root.unsubscribe(walletClient, function(err) { - if (err) $log.warn('Unsubscription error: ' + err.message); - else $log.debug('Unsubscribed from push notifications service'); - }); - }); - } - - root.subscribe = function(opts, walletClient, cb) { - if (!usePushNotifications) return cb(); - - var config = configService.getSync(); - if (!config.pushNotifications.enabled) return; - - walletClient.pushNotificationsSubscribe(opts, function(err, resp) { - if (err) return cb(err); - return cb(null, resp); - }); - } - - root.unsubscribe = function(walletClient, cb) { - if (!usePushNotifications) return cb(); - - walletClient.pushNotificationsUnsubscribe(function(err) { - if (err) return cb(err); - return cb(null); - }); - } - - return root; - - }); +});