diff --git a/app-template/package.json b/app-template/package.json index 64474379a..4cb6f7c94 100644 --- a/app-template/package.json +++ b/app-template/package.json @@ -42,7 +42,7 @@ "url": "https://github.com/bitpay/copay/issues" }, "dependencies": { - "bitcore-wallet-client": "4.2.0", + "bitcore-wallet-client": "4.2.1", "coveralls": "^2.11.9", "express": "^4.11.2", "fs": "0.0.2", diff --git a/public/views/preferencesGlobal.html b/old/preferencesGlobal.html similarity index 100% rename from public/views/preferencesGlobal.html rename to old/preferencesGlobal.html diff --git a/public/views/includes/topbar.html b/old/topbar.html similarity index 100% rename from public/views/includes/topbar.html rename to old/topbar.html diff --git a/package.json b/package.json index f353072c2..94e489f66 100644 --- a/package.json +++ b/package.json @@ -45,7 +45,7 @@ "url": "https://github.com/bitpay/copay/issues" }, "dependencies": { - "bitcore-wallet-client": "4.2.0", + "bitcore-wallet-client": "4.2.1", "coveralls": "^2.11.9", "express": "^4.11.2", "fs": "0.0.2", diff --git a/public/views/addressbook.add.html b/public/views/addressbook.add.html new file mode 100644 index 000000000..c5b3762fe --- /dev/null +++ b/public/views/addressbook.add.html @@ -0,0 +1,63 @@ + + + + + + + Add entry + + + + + +
+ +
+ + + +
+ +
+ +
+ +
+
+
diff --git a/public/views/addressbook.html b/public/views/addressbook.html new file mode 100644 index 000000000..3262e0c19 --- /dev/null +++ b/public/views/addressbook.html @@ -0,0 +1,52 @@ + + + + + + + Addressbook + + + + + + + + +
+ +
+ + + + + {{addrEntry.name}} + + + + + + + + + + +
+
diff --git a/public/views/addressbook.view.html b/public/views/addressbook.view.html new file mode 100644 index 000000000..00b191e7f --- /dev/null +++ b/public/views/addressbook.view.html @@ -0,0 +1,36 @@ + + + + + + + Addressbook + + + + + +
+
+

Name

+ {{addressbookEntry.name}} +
+
+

Email

+ {{addressbookEntry.email}} +
+
+

Address

+ {{addressbookEntry.address}} +
+
+ + + +
+
diff --git a/public/views/modals/addressbook.html b/public/views/modals/addressbook.html deleted file mode 100644 index e6a434a8c..000000000 --- a/public/views/modals/addressbook.html +++ /dev/null @@ -1,96 +0,0 @@ - - - -
- Addressbook - Add entry -
- -
- - - -
- -
- -
- - - -

{{addrEntry.label}}

-

{{addrEntry.address}}

- - - - - -
- - -
- -
- -
- - - - -
- -
- -
- -
-
-
diff --git a/public/views/modals/receive-tips.html b/public/views/modals/receive-tips.html new file mode 100644 index 000000000..b148acd35 --- /dev/null +++ b/public/views/modals/receive-tips.html @@ -0,0 +1,15 @@ + + + + + + + +
+

Receive bitcoin by sharing your address

+

Other bitcoin users can scan this code to send you money

+
+
+
diff --git a/public/views/modals/scan-tips.html b/public/views/modals/scan-tips.html new file mode 100644 index 000000000..512b60327 --- /dev/null +++ b/public/views/modals/scan-tips.html @@ -0,0 +1,15 @@ + + + + + + + +
+

Scan the code to pay with bitcoin

+

QR codes could also contain a bitcoin wallet invitation, or an URL

+
+
+
diff --git a/public/views/onboarding/backupRequest.html b/public/views/onboarding/backupRequest.html index f7099033f..0284f8bdf 100644 --- a/public/views/onboarding/backupRequest.html +++ b/public/views/onboarding/backupRequest.html @@ -16,12 +16,14 @@
-
- -
-
- +
+
+ +
+
+ +
diff --git a/public/views/onboarding/backupWarning.html b/public/views/onboarding/backupWarning.html index d3406fb13..19f21d0ac 100644 --- a/public/views/onboarding/backupWarning.html +++ b/public/views/onboarding/backupWarning.html @@ -8,7 +8,7 @@
-

+

Are you being watched?

@@ -18,14 +18,14 @@

- +

Anyone with your backup phrase can access or spend your bitcoin.

-
+
diff --git a/public/views/onboarding/collectEmail.html b/public/views/onboarding/collectEmail.html index 5629d5c8c..a63f2c652 100644 --- a/public/views/onboarding/collectEmail.html +++ b/public/views/onboarding/collectEmail.html @@ -1,11 +1,4 @@ - - - - -
@@ -14,16 +7,42 @@

Wallet Created

-
-

Where would you like to receive email notifications about payments?

+
+
+

Where would you like to receive email notifications about payments?

+
+
+ +
+
+
+
+

Is this email address correct?

+
+
+

{{email}}

+
+
+
+ +
+
+ +
+
+
+
+
+
-
- -
-
-
diff --git a/public/views/onboarding/tour.html b/public/views/onboarding/tour.html index 8df77f256..d4be4de4a 100644 --- a/public/views/onboarding/tour.html +++ b/public/views/onboarding/tour.html @@ -30,7 +30,7 @@ Just scan the code to pay

-
+
@@ -54,9 +54,9 @@ The exchange rate changes with the market

-
+
@@ -78,7 +78,7 @@ Not even BitPay can access it

-
+
diff --git a/public/views/paperWallet.html b/public/views/paperWallet.html index eca39330a..ed1dede89 100644 --- a/public/views/paperWallet.html +++ b/public/views/paperWallet.html @@ -1,84 +1,100 @@ -
-
+ + + {{'Sweep paper wallet' | translate}} + + + + -
-
-
-

-
- - Backup Needed. - - Before receiving funds, you must backup your wallet. If this device is lost, it is impossible to access your funds without a backup. - + +
+
+

+
+ + Backup Needed. + + Before receiving funds, you must backup your wallet. If this device is lost, it is impossible to access your funds without a backup. + +
+
-
-
-
-

-
- {{error|translate}} -
-
-
-
-
- - -
- -
-
-
-
+ + diff --git a/public/views/preferencesAdvanced.html b/public/views/preferencesAdvanced.html index e63d6eb8c..1fcc94c14 100644 --- a/public/views/preferencesAdvanced.html +++ b/public/views/preferencesAdvanced.html @@ -5,7 +5,7 @@ - +
@@ -13,7 +13,7 @@ Wallet Information - + Sweep paper wallet diff --git a/public/views/preferencesHistory.html b/public/views/preferencesHistory.html index e2d2d8d55..1118a2a99 100644 --- a/public/views/preferencesHistory.html +++ b/public/views/preferencesHistory.html @@ -4,12 +4,12 @@ - +
-
+
Export to file
-
+
Export to file preparing... diff --git a/public/views/tab-send.html b/public/views/tab-send.html index a230be863..4da7003e1 100644 --- a/public/views/tab-send.html +++ b/public/views/tab-send.html @@ -17,15 +17,15 @@
-
+
Contacts & Wallets - +
No Wallet - Contact
- {{item.label}} + {{item.name}}
diff --git a/public/views/tab-settings.html b/public/views/tab-settings.html index 92276b712..43827c659 100644 --- a/public/views/tab-settings.html +++ b/public/views/tab-settings.html @@ -6,9 +6,10 @@
- + Address Book +
Preferences
diff --git a/src/js/controllers/addressbook.js b/src/js/controllers/addressbook.js new file mode 100644 index 000000000..d9e136901 --- /dev/null +++ b/src/js/controllers/addressbook.js @@ -0,0 +1,56 @@ +'use strict'; + +angular.module('copayApp.controllers').controller('addressbookListController', function($scope, $log, $timeout, addressbookService, lodash, popupService) { + + var contacts; + + $scope.initAddressbook = function() { + addressbookService.list(function(err, ab) { + if (err) $log.error(err); + + $scope.isEmptyList = lodash.isEmpty(ab); + + contacts = []; + lodash.each(ab, function(v, k) { + contacts.push({ + name: lodash.isObject(v) ? v.name : v, + address: k, + email: lodash.isObject(v) ? v.email : null + }); + }); + + $scope.addressbook = lodash.clone(contacts); + }); + }; + + $scope.findAddressbook = function(search) { + if (!search || search.length < 2) { + $scope.addressbook = contacts; + $timeout(function() { + $scope.$apply(); + }, 10); + return; + } + + var result = lodash.filter(contacts, function(item) { + var val = item.name; + return lodash.includes(val.toLowerCase(), search.toLowerCase()); + }); + + $scope.addressbook = result; + }; + + $scope.remove = function(addr) { + $timeout(function() { + addressbookService.remove(addr, function(err, ab) { + if (err) { + popupService.showAlert(err); + return; + } + $scope.initAddressbook(); + $scope.$digest(); + }); + }, 100); + }; + +}); diff --git a/src/js/controllers/addressbookAdd.js b/src/js/controllers/addressbookAdd.js new file mode 100644 index 000000000..8d6831076 --- /dev/null +++ b/src/js/controllers/addressbookAdd.js @@ -0,0 +1,36 @@ +'use strict'; + +angular.module('copayApp.controllers').controller('addressbookAddController', function($scope, $state, $timeout, addressbookService, popupService) { + + $scope.addressbookEntry = { + 'address': '', + 'name': '', + 'email': '' + }; + + $scope.onQrCodeScanned = function(data, addressbookForm) { + $timeout(function() { + var form = addressbookForm; + if (data && form) { + data = data.replace('bitcoin:', ''); + form.address.$setViewValue(data); + form.address.$isValid = true; + form.address.$render(); + } + $scope.$digest(); + }, 100); + }; + + $scope.add = function(addressbook) { + $timeout(function() { + addressbookService.add(addressbook, function(err, ab) { + if (err) { + popupService.showAlert(err); + return; + } + $state.go('tabs.addressbook'); + }); + }, 100); + }; + +}); diff --git a/src/js/controllers/addressbookView.js b/src/js/controllers/addressbookView.js new file mode 100644 index 000000000..a0064fad4 --- /dev/null +++ b/src/js/controllers/addressbookView.js @@ -0,0 +1,37 @@ +'use strict'; + +angular.module('copayApp.controllers').controller('addressbookViewController', function($scope, $state, $timeout, $stateParams, lodash, addressbookService, popupService) { + + var address = $stateParams.address; + + if (!address) { + $state.go('tabs.addressbook'); + return; + } + + addressbookService.get(address, function(err, obj) { + if (err) { + popupService.showAlert(err); + return; + } + if (!lodash.isObject(obj)) { + var name = obj; + obj = { + 'name': name, + 'address': address, + 'email': '' + }; + } + $scope.addressbookEntry = obj; + }); + + $scope.sendTo = function() { + $timeout(function() { + $state.transitionTo('send.amount', { + toAddress: $scope.addressbookEntry.address, + toName: $scope.addressbookEntry.name + }); + }, 100); + }; + +}); diff --git a/src/js/controllers/backup.js b/src/js/controllers/backup.js index cec5dd02f..1cf2cc0ed 100644 --- a/src/js/controllers/backup.js +++ b/src/js/controllers/backup.js @@ -1,209 +1,219 @@ 'use strict'; angular.module('copayApp.controllers').controller('backupController', - function($rootScope, $scope, $timeout, $log, $state, $stateParams, $ionicPopup, $ionicModal, $ionicNavBarDelegate, uxLanguage, lodash, fingerprintService, platformInfo, configService, profileService, bwcService, walletService, ongoingProcess, storageService) { - var wallet = profileService.getWallet($stateParams.walletId); - $ionicNavBarDelegate.title(wallet.credentials.walletName); - $scope.n = wallet.n; - var keys; + function($rootScope, $scope, $timeout, $log, $state, $stateParams, $ionicPopup, $ionicNavBarDelegate, uxLanguage, lodash, fingerprintService, platformInfo, configService, profileService, bwcService, walletService, ongoingProcess, storageService, popupService, gettextCatalog, $ionicModal) { + var wallet = profileService.getWallet($stateParams.walletId); + $ionicNavBarDelegate.title(wallet.credentials.walletName); + $scope.n = wallet.n; + var keys; - $scope.credentialsEncrypted = wallet.isPrivKeyEncrypted(); + $scope.credentialsEncrypted = wallet.isPrivKeyEncrypted(); - var isDeletedSeed = function() { - if (!wallet.credentials.mnemonic && !wallet.credentials.mnemonicEncrypted) - return true; + var isDeletedSeed = function() { + if (!wallet.credentials.mnemonic && !wallet.credentials.mnemonicEncrypted) + return true; - return false; - }; + return false; + }; - $scope.init = function() { - $scope.deleted = isDeletedSeed(); - if ($scope.deleted) { - $log.debug('no mnemonics'); - return; - } - - walletService.getKeys(wallet, function(err, k) { - if (err || !k) { - $state.go('wallet.preferences'); + $scope.init = function() { + $scope.deleted = isDeletedSeed(); + if ($scope.deleted) { + $log.debug('no mnemonics'); return; } - $scope.credentialsEncrypted = false; - keys = k; - $scope.initFlow(); - }); - }; - var shuffledWords = function(words) { - var sort = lodash.sortBy(words); - - return lodash.map(sort, function(w) { - return { - word: w, - selected: false - }; - }); - }; - - $scope.initFlow = function() { - if (!keys) return; - $scope.viewTitle = "Backup Phrase"; - var words = keys.mnemonic; - - $scope.mnemonicWords = words.split(/[\u3000\s]+/); - $scope.shuffledMnemonicWords = shuffledWords($scope.mnemonicWords); - $scope.mnemonicHasPassphrase = wallet.mnemonicHasPassphrase(); - $scope.useIdeograms = words.indexOf("\u3000") >= 0; - $scope.passphrase = ''; - $scope.customWords = []; - $scope.step = 1; - $scope.selectComplete = false; - $scope.backupError = false; - - words = lodash.repeat('x', 300); - $timeout(function() { - $scope.$apply(); - }, 10); - }; - - $scope.goBack = function() { - if ($scope.step == 1) { - if ($stateParams.fromOnboarding) $state.go('onboarding.backupRequest'); - else $state.go('wallet.preferences'); - } else { - $scope.goToStep($scope.step - 1); - } - }; - - var backupError = function(err) { - ongoingProcess.set('validatingWords', false); - $log.debug('Failed to verify backup: ', err); - $scope.backupError = true; - - $timeout(function() { - $scope.$apply(); - }, 1); - }; - - $scope.closePopup = function(val) { - if (val) { - $scope.closeModal(); - if ($stateParams.fromOnboarding) $state.go('onboarding.disclaimer'); - else $state.go('tabs.home') - } else { - confirmBackupPopup.close(); - $scope.goToStep(1); - } - }; - - $ionicModal.fromTemplateUrl('views/includes/confirmBackupPopup.html', { - scope: $scope, - animation: 'slide-in-up' - }).then(function(modal) { - $scope.modal = modal; - }); - $scope.openModal = function() { - $scope.modal.show(); - }; - $scope.closeModal = function() { - $scope.modal.hide(); - }; - // Cleanup the modal when we're done with it! - $scope.$on('$destroy', function() { - $scope.modal.remove(); - }); - - var confirm = function(cb) { - $scope.backupError = false; - - var customWordList = lodash.pluck($scope.customWords, 'word'); - - if (!lodash.isEqual($scope.mnemonicWords, customWordList)) { - return cb('Mnemonic string mismatch'); - } - - $timeout(function() { - if ($scope.mnemonicHasPassphrase) { - var walletClient = bwcService.getClient(); - var separator = $scope.useIdeograms ? '\u3000' : ' '; - var customSentence = customWordList.join(separator); - var passphrase = $scope.passphrase || ''; - - try { - walletClient.seedFromMnemonic(customSentence, { - network: wallet.credentials.network, - passphrase: passphrase, - account: wallet.credentials.account - }); - } catch (err) { - walletClient.credentials.xPrivKey = lodash.repeat('x', 64); - return cb(err); + walletService.getKeys(wallet, function(err, k) { + if (err || !k) { + $log.error('Could not get keys: ', err); + $state.go('wallet.preferences'); + return; } - - if (walletClient.credentials.xPrivKey.substr(walletClient.credentials.xPrivKey) != keys.xPrivKey) { - delete walletClient.credentials; - return cb('Private key mismatch'); - } - } - - profileService.setBackupFlag(wallet.credentials.walletId); - return cb(); - }, 1); - }; - - var finalStep = function() { - ongoingProcess.set('validatingWords', true); - confirm(function(err) { - ongoingProcess.set('validatingWords', false); - if (err) { - backupError(err); - } - $timeout(function() { - $scope.openModal(); - return; - }, 1); - }); - }; - - $scope.goToStep = function(n) { - if (n == 1) - $scope.initFlow(); - if (n == 2) { - $scope.step = 2; - $scope.viewTitle = "Let's verify your backup phrase"; - } - if (n == 3) { - if (!$scope.mnemonicHasPassphrase) - finalStep(); - else - $scope.step = 3; - } - if (n == 4) - finalStep(); - }; - - $scope.addButton = function(index, item) { - var newWord = { - word: item.word, - prevIndex: index + $scope.credentialsEncrypted = false; + keys = k; + $scope.initFlow(); + }); }; - $scope.customWords.push(newWord); - $scope.shuffledMnemonicWords[index].selected = true; - $scope.shouldContinue(); - }; - $scope.removeButton = function(index, item) { - if ($scope.loading) return; - $scope.customWords.splice(index, 1); - $scope.shuffledMnemonicWords[item.prevIndex].selected = false; - $scope.shouldContinue(); - }; + var shuffledWords = function(words) { + var sort = lodash.sortBy(words); - $scope.shouldContinue = function() { - if ($scope.customWords.length == $scope.shuffledMnemonicWords.length) - $scope.selectComplete = true; - else + return lodash.map(sort, function(w) { + return { + word: w, + selected: false + }; + }); + }; + + $scope.initFlow = function() { + if (!keys) return; + + var words = keys.mnemonic; + + $scope.mnemonicWords = words.split(/[\u3000\s]+/); + $scope.shuffledMnemonicWords = shuffledWords($scope.mnemonicWords); + $scope.mnemonicHasPassphrase = wallet.mnemonicHasPassphrase(); + $scope.useIdeograms = words.indexOf("\u3000") >= 0; + $scope.passphrase = ''; + $scope.customWords = []; + $scope.step = 1; $scope.selectComplete = false; - }; + $scope.backupError = false; -}); + words = lodash.repeat('x', 300); + $timeout(function() { + $scope.$apply(); + }, 10); + }; + + $scope.goBack = function() { + if ($scope.step == 1) { + if ($stateParams.fromOnboarding) $state.go('onboarding.backupRequest'); + else $state.go('wallet.preferences'); + } else { + $scope.goToStep($scope.step - 1); + } + }; + + var backupError = function(err) { + ongoingProcess.set('validatingWords', false); + $log.debug('Failed to verify backup: ', err); + $scope.backupError = true; + + $timeout(function() { + $scope.$apply(); + }, 1); + }; + + $ionicModal.fromTemplateUrl('views/includes/confirmBackupPopup.html', { + scope: $scope, + animation: 'slide-in-up' + }).then(function(modal) { + $scope.modal = modal; + }); + $scope.openModal = function() { + $scope.modal.show(); + }; + $scope.closeModal = function() { + $scope.modal.hide(); + }; + // Cleanup the modal when we're done with it! + $scope.$on('$destroy', function() { + $scope.modal.remove(); + }); + + var openPopup = function() { + + if ($scope.backupError) { + var title = gettextCatalog.getString('uh oh...'); + var message = gettextCatalog.getString("It's importante that you write your backup phrase down correctly. If something happens to your wallet, you'll need this backup to recover your money Please review your backup and try again"); + popupService.showAlert(title, message, function() { + $scope.goToStep(1); + }) + } + else { + $scope.openModal(); + $scope.closePopup = function() { + $scope.closeModal(); + if ($stateParams.fromOnboarding) $state.go('onboarding.disclaimer'); + else { + $ionicHistory.clearHistory(); + $state.go('tabs.home') + } + }; + } + } + + var confirm = function(cb) { + $scope.backupError = false; + + var customWordList = lodash.pluck($scope.customWords, 'word'); + + if (!lodash.isEqual($scope.mnemonicWords, customWordList)) { + return cb('Mnemonic string mismatch'); + } + + $timeout(function() { + if ($scope.mnemonicHasPassphrase) { + var walletClient = bwcService.getClient(); + var separator = $scope.useIdeograms ? '\u3000' : ' '; + var customSentence = customWordList.join(separator); + var passphrase = $scope.passphrase || ''; + + try { + walletClient.seedFromMnemonic(customSentence, { + network: wallet.credentials.network, + passphrase: passphrase, + account: wallet.credentials.account + }); + } catch (err) { + walletClient.credentials.xPrivKey = lodash.repeat('x', 64); + return cb(err); + } + + if (walletClient.credentials.xPrivKey.substr(walletClient.credentials.xPrivKey) != keys.xPrivKey) { + delete walletClient.credentials; + return cb('Private key mismatch'); + } + } + + profileService.setBackupFlag(wallet.credentials.walletId); + return cb(); + }, 1); + }; + + var finalStep = function() { + ongoingProcess.set('validatingWords', true); + confirm(function(err) { + ongoingProcess.set('validatingWords', false); + if (err) { + backupError(err); + } + $timeout(function() { + openPopup(); + return; + }, 1); + }); + }; + + $scope.goToStep = function(n) { + if (n == 1) + $scope.initFlow(); + if (n == 2) + $scope.step = 2; + if (n == 3) { + if (!$scope.mnemonicHasPassphrase) + finalStep(); + else + $scope.step = 3; + } + if (n == 4) + finalStep(); + }; + + $scope.addButton = function(index, item) { + var newWord = { + word: item.word, + prevIndex: index + }; + $scope.customWords.push(newWord); + $scope.shuffledMnemonicWords[index].selected = true; + $scope.shouldContinue(); + }; + + $scope.removeButton = function(index, item) { + if ($scope.loading) return; + $scope.customWords.splice(index, 1); + $scope.shuffledMnemonicWords[item.prevIndex].selected = false; + $scope.shouldContinue(); + }; + + $scope.shouldContinue = function() { + if ($scope.customWords.length == $scope.shuffledMnemonicWords.length) + $scope.selectComplete = true; + else + $scope.selectComplete = false; + }; + + }); \ No newline at end of file diff --git a/src/js/controllers/copayers.js b/src/js/controllers/copayers.js index b95a22121..2743f800f 100644 --- a/src/js/controllers/copayers.js +++ b/src/js/controllers/copayers.js @@ -21,7 +21,7 @@ angular.module('copayApp.controllers').controller('copayersController', $scope.isCordova = platformInfo.isCordova; $scope.showDeletePopup = function() { - popupService.showConfirm(gettextCatalog.getString('Confirm'), gettextCatalog.getString('Are you sure you want to delete this wallet?'), function(res) { + popupService.showConfirm(gettextCatalog.getString('Confirm'), gettextCatalog.getString('Are you sure you want to delete this wallet?'), null, null, function(res) { if (res) deleteWallet(); }); }; diff --git a/src/js/controllers/modals/addressbook.js b/src/js/controllers/modals/addressbook.js deleted file mode 100644 index ee63c7e75..000000000 --- a/src/js/controllers/modals/addressbook.js +++ /dev/null @@ -1,111 +0,0 @@ -'use strict'; - -angular.module('copayApp.controllers').controller('addressbookModalController', function($scope, $log, $state, $timeout, $ionicPopup, addressbookService, lodash, popupService) { - - var contacts; - - $scope.initAddressbook = function() { - addressbookService.list(function(err, ab) { - if (err) $log.error(err); - - $scope.isEmptyList = lodash.isEmpty(ab); - - contacts = []; - lodash.each(ab, function(v, k) { - contacts.push({ - label: v, - address: k - }); - }); - - $scope.addressbook = lodash.clone(contacts); - }); - }; - - $scope.findAddressbook = function(search) { - if (!search || search.length < 2) { - $scope.addressbook = contacts; - $timeout(function() { - $scope.$apply(); - }, 10); - return; - } - - var result = lodash.filter(contacts, function(item) { - var val = item.label; - return lodash.includes(val.toLowerCase(), search.toLowerCase()); - }); - - $scope.addressbook = result; - }; - - $scope.sendTo = function(item) { - $scope.closeAddressbookModal(); - $timeout(function() { - $state.transitionTo('send.amount', { toAddress: item.address, toName: item.label}) - }, 100); - }; - - $scope.closeAddressbookModal = function() { - $scope.cleanAddressbookEntry(); - $scope.addAddressbookEntry = false; - $scope.addressbookModal.hide(); - }; - - $scope.onQrCodeScanned = function(data, addressbookForm) { - $timeout(function() { - var form = addressbookForm; - if (data && form) { - data = data.replace('bitcoin:', ''); - form.address.$setViewValue(data); - form.address.$isValid = true; - form.address.$render(); - } - $scope.$digest(); - }, 100); - }; - - $scope.cleanAddressbookEntry = function() { - $scope.addressbookEntry = { - 'address': '', - 'label': '' - }; - }; - - $scope.toggleAddAddressbookEntry = function() { - $scope.cleanAddressbookEntry(); - $scope.addAddressbookEntry = !$scope.addAddressbookEntry; - }; - - $scope.add = function(addressbook) { - $timeout(function() { - addressbookService.add(addressbook, function(err, ab) { - if (err) { - popupService.showAlert(err); - return; - } - $scope.initAddressbook(); - $scope.toggleAddAddressbookEntry(); - $scope.$digest(); - }); - }, 100); - }; - - $scope.remove = function(addr) { - $timeout(function() { - addressbookService.remove(addr, function(err, ab) { - if (err) { - popupService.showAlert(err); - return; - } - $scope.initAddressbook(); - $scope.$digest(); - }); - }, 100); - }; - - $scope.$on('$destroy', function() { - $scope.addressbookModal.remove(); - }); - -}); diff --git a/src/js/controllers/modals/receiveTips.js b/src/js/controllers/modals/receiveTips.js new file mode 100644 index 000000000..2297be7a4 --- /dev/null +++ b/src/js/controllers/modals/receiveTips.js @@ -0,0 +1,10 @@ +'use strict'; + +angular.module('copayApp.controllers').controller('receiveTipsController', function($scope, $log, storageService) { + $scope.close = function() { + $log.debug('Receive tips accepted'); + storageService.setReceiveTipsAccepted(true, function(err) { + $scope.receiveTipsModal.hide(); + }); + } +}); diff --git a/src/js/controllers/modals/scanTips.js b/src/js/controllers/modals/scanTips.js new file mode 100644 index 000000000..a82f70554 --- /dev/null +++ b/src/js/controllers/modals/scanTips.js @@ -0,0 +1,11 @@ +'use strict'; + +angular.module('copayApp.controllers').controller('scanTipsController', function($scope, $log, storageService) { + $scope.close = function() { + $log.debug('Scan tips accepted'); + storageService.setScanTipsAccepted(true, function(err) { + $scope.$emit('TipsModalClosed', function() {}); + $scope.scanTipsModal.hide(); + }); + } +}); diff --git a/src/js/controllers/modals/scanner.js b/src/js/controllers/modals/scanner.js index c1bccb708..6616d505a 100644 --- a/src/js/controllers/modals/scanner.js +++ b/src/js/controllers/modals/scanner.js @@ -1,6 +1,6 @@ 'use strict'; -angular.module('copayApp.controllers').controller('scannerController', function($scope, $timeout) { +angular.module('copayApp.controllers').controller('scannerController', function($scope, $timeout, storageService, $ionicModal, platformInfo) { // QR code Scanner var video; @@ -73,6 +73,35 @@ angular.module('copayApp.controllers').controller('scannerController', function( }; $scope.init = function() { + if (platformInfo.isCordova) scannerInit(); + else checkTips(); + }; + + function checkTips() { + //TODO addapt tips to the new QR plugin (mobile) + storageService.getScanTipsAccepted(function(err, accepted) { + if (err) $log.warn(err); + if (accepted) { + scannerInit(); + return; + } + + $timeout(function() { + $ionicModal.fromTemplateUrl('views/modals/scan-tips.html', { + scope: $scope + }).then(function(modal) { + $scope.scanTipsModal = modal; + $scope.scanTipsModal.show(); + }); + }, 1000); + }); + }; + + $scope.$on('TipsModalClosed', function(event) { + scannerInit(); + }); + + function scannerInit() { setScanner(); $timeout(function() { if ($scope.beforeScan) { diff --git a/src/js/controllers/onboarding/backupRequest.js b/src/js/controllers/onboarding/backupRequest.js index 8284baafb..01fcfcfcd 100644 --- a/src/js/controllers/onboarding/backupRequest.js +++ b/src/js/controllers/onboarding/backupRequest.js @@ -1,22 +1,28 @@ 'use strict'; -angular.module('copayApp.controllers').controller('backupRequestController', function($scope, $state, $stateParams, $ionicPopup) { +angular.module('copayApp.controllers').controller('backupRequestController', function($scope, $state, $stateParams, $ionicPopup, popupService, gettextCatalog) { $scope.walletId = $stateParams.walletId; + $scope.openPopup = function() { - var backupLaterPopup = $ionicPopup.show({ - templateUrl: "views/includes/backupLaterPopup.html", - scope: $scope, + + var title = gettextCatalog.getString('Without a backup, you could lose money'); + var message = gettextCatalog.getString('If something happens to this device, this app is deleted, or your password forgotten, neither you nor Bitpay can recover your funds'); + var okText = gettextCatalog.getString('I understand'); + var cancelText = gettextCatalog.getString('Go back'); + popupService.showConfirm(title, message, okText, cancelText, function(val) { + if (val) { + var title = gettextCatalog.getString('Are you sure you want to skip the backup?'); + var message = gettextCatalog.getString('You can create a backup later from your wallet settings'); + var okText = gettextCatalog.getString('Yes, skip backup'); + var cancelText = gettextCatalog.getString('Go back'); + popupService.showConfirm(title, message, okText, cancelText, function(val) { + if (val) { + $state.go('onboarding.disclaimer'); + } + }); + } }); - - $scope.goBack = function() { - backupLaterPopup.close(); - }; - - $scope.continue = function() { - backupLaterPopup.close(); - $state.go('onboarding.disclaimer'); - }; } }); diff --git a/src/js/controllers/onboarding/collectEmail.js b/src/js/controllers/onboarding/collectEmail.js index 8fcda40f5..6479e0ea8 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, $stateParams, profileService, configService, walletService, platformInfo) { +angular.module('copayApp.controllers').controller('collectEmailController', function($scope, $state, $timeout, $stateParams, profileService, configService, walletService, platformInfo) { var isCordova = platformInfo.isCordova; var isWP = platformInfo.isWP; @@ -9,11 +9,7 @@ angular.module('copayApp.controllers').controller('collectEmailController', func var wallet = profileService.getWallet($stateParams.walletId); var walletId = wallet.credentials.walletId; - var config = configService.getSync(); - config.emailFor = config.emailFor || {}; - $scope.email = config.emailFor && config.emailFor[walletId]; - - $scope.save = function(form) { + $scope.save = function() { var opts = { emailFor: {} }; @@ -25,13 +21,32 @@ angular.module('copayApp.controllers').controller('collectEmailController', func if (err) $log.warn(err); configService.set(opts, function(err) { if (err) $log.warn(err); - if (!usePushNotifications) $state.go('onboarding.backupRequest', {walletId: walletId}); - else $state.go('onboarding.notifications', {walletId: walletId}); + if (!usePushNotifications) $state.go('onboarding.backupRequest', { + walletId: walletId + }); + else $state.go('onboarding.notifications', { + walletId: walletId + }); }); }); }; + $scope.confirm = function(emailForm) { + if (emailForm.$invalid) return; + $scope.confirmation = true; + $scope.email = emailForm.email.$modelValue; + }; + + $scope.cancel = function() { + $scope.confirmation = false; + $timeout(function() { + $scope.$digest(); + }, 1); + }; + $scope.onboardingMailSkip = function() { - $state.go('onboarding.backupRequest', {walletId: walletId}); + $state.go('onboarding.backupRequest', { + walletId: walletId + }); }; }); diff --git a/src/js/controllers/paperWallet.js b/src/js/controllers/paperWallet.js index 6393fdc3f..27c784d2f 100644 --- a/src/js/controllers/paperWallet.js +++ b/src/js/controllers/paperWallet.js @@ -1,16 +1,31 @@ angular.module('copayApp.controllers').controller('paperWalletController', - function($scope, $timeout, $log, $ionicModal, $ionicHistory, configService, profileService, $state, addressService, bitcore, ongoingProcess, txFormatService, $stateParams, walletService) { - + function($scope, $timeout, $log, $ionicModal, $ionicHistory, popupService, gettextCatalog, platformInfo, configService, profileService, $state, bitcore, ongoingProcess, txFormatService, $stateParams, walletService) { var wallet = profileService.getWallet($stateParams.walletId); var rawTx; + $scope.init = function() { + $scope.wallet = wallet; + $scope.isCordova = platformInfo.isCordova; + $scope.needsBackup = wallet.needsBackup; + $scope.walletAlias = wallet.name; + $scope.walletName = wallet.credentials.walletName; + $scope.formData = {}; + $scope.formData.inputData = null; + $scope.scannedKey = null; + $scope.balance = null; + $scope.balanceSat = null; + $scope.scanned = false; + $timeout(function() { + $scope.$apply(); + }, 10); + }; + $scope.onQrCodeScanned = function(data) { - $scope.inputData = data; + $scope.formData.inputData = data; $scope.onData(data); }; $scope.onData = function(data) { - $scope.error = null; $scope.scannedKey = data; $scope.isPkEncrypted = (data.substring(0, 2) == '6P'); }; @@ -48,7 +63,6 @@ angular.module('copayApp.controllers').controller('paperWalletController', $scope.scanFunds = function() { $scope.privateKey = ''; $scope.balanceSat = 0; - $scope.error = null; ongoingProcess.set('scanning', true); $timeout(function() { @@ -56,12 +70,13 @@ angular.module('copayApp.controllers').controller('paperWalletController', ongoingProcess.set('scanning', false); if (err) { $log.error(err); - $scope.error = err.message || err.toString(); + popupService.showAlert(gettextCatalog.getString('Error scanning funds:'), err || err.toString()); } else { $scope.privateKey = privateKey; $scope.balanceSat = balance; var config = configService.getSync().wallet.settings; $scope.balance = txFormatService.formatAmount(balance) + ' ' + config.unitName; + $scope.scanned = true; } $scope.$apply(); @@ -70,7 +85,7 @@ angular.module('copayApp.controllers').controller('paperWalletController', }; function _sweepWallet(cb) { - addressService.getAddress(wallet.credentials.walletId, true, function(err, destinationAddress) { + walletService.getAddress(wallet, true, function(err, destinationAddress) { if (err) return cb(err); wallet.buildTxFromPrivateKey($scope.privateKey, destinationAddress, null, function(err, tx) { @@ -90,18 +105,16 @@ angular.module('copayApp.controllers').controller('paperWalletController', $scope.sweepWallet = function() { ongoingProcess.set('sweepingWallet', true); $scope.sending = true; - $scope.error = null; $timeout(function() { _sweepWallet(function(err, destinationAddress, txid) { ongoingProcess.set('sweepingWallet', false); - + $scope.sending = false; if (err) { - $scope.error = err.message || err.toString(); $log.error(err); + popupService.showAlert(gettextCatalog.getString('Error sweeping wallet:'), err || err.toString()); } else { - var type = walletService.getViewStatus(wallet, txp); - $scope.openStatusModal(type, txp, function() { + $scope.openStatusModal('broadcasted', function() { $ionicHistory.clearHistory(); $state.go('tabs.home'); }); @@ -111,19 +124,18 @@ angular.module('copayApp.controllers').controller('paperWalletController', }, 100); }; - $scope.openStatusModal = function(type, txp, cb) { + $scope.openStatusModal = function(type, cb) { + $scope.tx = {}; + $scope.tx.amountStr = $scope.balance; $scope.type = type; - $scope.tx = txFormatService.processTx(txp); $scope.color = wallet.backgroundColor; $scope.cb = cb; $ionicModal.fromTemplateUrl('views/modals/tx-status.html', { - scope: $scope, - animation: 'slide-in-up' + scope: $scope }).then(function(modal) { $scope.txStatusModal = modal; $scope.txStatusModal.show(); }); }; - }); diff --git a/src/js/controllers/preferencesAdvancedController.js b/src/js/controllers/preferencesAdvancedController.js new file mode 100644 index 000000000..0af8cf258 --- /dev/null +++ b/src/js/controllers/preferencesAdvancedController.js @@ -0,0 +1,10 @@ +'use strict'; + +angular.module('copayApp.controllers').controller('preferencesAdvancedController', function($scope, $timeout, $stateParams, profileService) { + var wallet = profileService.getWallet($stateParams.walletId); + $scope.network = wallet.network; + + $timeout(function() { + $scope.$apply(); + }, 1); +}); diff --git a/src/js/controllers/preferencesBitpayCard.js b/src/js/controllers/preferencesBitpayCard.js index 8a297c8aa..9e4c9da8a 100644 --- a/src/js/controllers/preferencesBitpayCard.js +++ b/src/js/controllers/preferencesBitpayCard.js @@ -5,7 +5,7 @@ angular.module('copayApp.controllers').controller('preferencesBitpayCardControll $scope.logout = function() { var title = 'Are you sure you would like to log out of your Bitpay Card account?'; - popupService.showConfirm(title, null, function(res) { + popupService.showConfirm(title, null, null, null, function(res) { if (res) logout(); }); }; diff --git a/src/js/controllers/preferencesDelete.js b/src/js/controllers/preferencesDelete.js index 8e95a31c1..1ec93fd4b 100644 --- a/src/js/controllers/preferencesDelete.js +++ b/src/js/controllers/preferencesDelete.js @@ -10,7 +10,7 @@ angular.module('copayApp.controllers').controller('preferencesDeleteWalletContro $scope.showDeletePopup = function() { var title = gettextCatalog.getString('Warning!'); var message = gettextCatalog.getString('Are you sure you want to delete this wallet?'); - popupService.showConfirm(title, message, function(res) { + popupService.showConfirm(title, message, null, null, function(res) { if (res) deleteWallet(); }); }; diff --git a/src/js/controllers/preferencesGlidera.js b/src/js/controllers/preferencesGlidera.js index 86c3516af..16749e66e 100644 --- a/src/js/controllers/preferencesGlidera.js +++ b/src/js/controllers/preferencesGlidera.js @@ -23,7 +23,9 @@ angular.module('copayApp.controllers').controller('preferencesGlideraController' } $scope.token = glidera.token; $scope.permissions = glidera.permissions; - $scope.update({fullUpdate: true}); + $scope.update({ + fullUpdate: true + }); }); }; @@ -62,7 +64,7 @@ angular.module('copayApp.controllers').controller('preferencesGlideraController' }; $scope.revokeToken = function() { - popupService.showConfirm('Glidera', 'Are you sure you would like to log out of your Glidera account?', function(res) { + popupService.showConfirm('Glidera', 'Are you sure you would like to log out of your Glidera account?', null, null, function(res) { if (res) { glideraService.removeToken(function() { $timeout(function() { diff --git a/src/js/controllers/preferencesHistory.js b/src/js/controllers/preferencesHistory.js index a44f69b36..500a8bd53 100644 --- a/src/js/controllers/preferencesHistory.js +++ b/src/js/controllers/preferencesHistory.js @@ -1,10 +1,11 @@ 'use strict'; angular.module('copayApp.controllers').controller('preferencesHistory', - function($scope, $log, $stateParams, $timeout, $ionicNavBarDelegate, gettextCatalog, storageService, $state, $ionicHistory, profileService, lodash) { + function($scope, $log, $stateParams, $timeout, $state, $ionicHistory, $ionicNavBarDelegate, gettextCatalog, storageService, platformInfo, profileService, lodash) { $ionicNavBarDelegate.title(gettextCatalog.getString('Transaction History')); $scope.wallet = profileService.getWallet($stateParams.walletId); $scope.csvReady = false; + $scope.isCordova = platformInfo.isCordova; $scope.csvHistory = function(cb) { var allTxs = []; diff --git a/src/js/controllers/tab-receive.js b/src/js/controllers/tab-receive.js index 3a03d090a..187d3822e 100644 --- a/src/js/controllers/tab-receive.js +++ b/src/js/controllers/tab-receive.js @@ -1,6 +1,6 @@ 'use strict'; -angular.module('copayApp.controllers').controller('tabReceiveController', function($scope, $timeout, $log, platformInfo, walletService, profileService, configService, lodash, gettextCatalog, popupService) { +angular.module('copayApp.controllers').controller('tabReceiveController', function($scope, $timeout, $log, $ionicModal, storageService, platformInfo, walletService, profileService, configService, lodash, gettextCatalog, popupService) { $scope.isCordova = platformInfo.isCordova; @@ -8,10 +8,27 @@ angular.module('copayApp.controllers').controller('tabReceiveController', functi $scope.wallets = profileService.getWallets({ onlyComplete: true }); - $scope.isCordova = platformInfo.isCordova; $scope.isNW = platformInfo.isNW; + $scope.isCordova = platformInfo.isCordova; + if (!$scope.isCordova) $scope.checkTips(); } + $scope.checkTips = function() { + storageService.getReceiveTipsAccepted(function(err, accepted) { + if (err) $log.warn(err); + if (accepted) return; + + $timeout(function() { + $ionicModal.fromTemplateUrl('views/modals/receive-tips.html', { + scope: $scope + }).then(function(modal) { + $scope.receiveTipsModal = modal; + $scope.receiveTipsModal.show(); + }); + }, 1000); + }); + }; + $scope.$on('Wallet/Changed', function(event, wallet) { if (!wallet) { $log.debug('No wallet provided'); diff --git a/src/js/controllers/tab-send.js b/src/js/controllers/tab-send.js index de70a0a0a..a9756e008 100644 --- a/src/js/controllers/tab-send.js +++ b/src/js/controllers/tab-send.js @@ -1,18 +1,20 @@ 'use strict'; -angular.module('copayApp.controllers').controller('tabSendController', function($scope, $ionicModal, $log, $timeout, addressbookService, profileService, lodash, $state, walletService, incomingData ) { +angular.module('copayApp.controllers').controller('tabSendController', function($scope, $log, $timeout, addressbookService, profileService, lodash, $state, walletService, incomingData ) { var originalList; $scope.init = function() { originalList = []; - var wallets = profileService.getWallets({onlyComplete: true}); + var wallets = profileService.getWallets({ + onlyComplete: true + }); lodash.each(wallets, function(v) { originalList.push({ color: v.color, - label: v.name, + name: v.name, isWallet: true, getAddress: function(cb) { walletService.getAddress(v, false, cb); @@ -26,16 +28,20 @@ angular.module('copayApp.controllers').controller('tabSendController', function( var contacts = []; lodash.each(ab, function(v, k) { contacts.push({ - label: v, + name: lodash.isObject(v) ? v.name : v, address: k, getAddress: function(cb) { - return cb(null,k); + return cb(null, k); }, }); }); originalList = originalList.concat(contacts); $scope.list = lodash.clone(originalList); + + $timeout(function() { + $scope.$apply(); + }, 1); }); }; @@ -54,7 +60,7 @@ angular.module('copayApp.controllers').controller('tabSendController', function( } var result = lodash.filter(originalList, function(item) { - var val = item.label || item.alias || item.name; + var val = item.name; return lodash.includes(val.toLowerCase(), search.toLowerCase()); }); @@ -62,22 +68,16 @@ angular.module('copayApp.controllers').controller('tabSendController', function( }; $scope.goToAmount = function(item) { - item.getAddress(function(err,addr){ - if (err|| !addr) { + item.getAddress(function(err, addr) { + if (err || !addr) { $log.error(err); return; } - $log.debug('Got toAddress:' + addr + ' | ' + item.label) - return $state.transitionTo('send.amount', { toAddress: addr, toName: item.label}) - }); - }; - - $scope.openAddressbookModal = function() { - $ionicModal.fromTemplateUrl('views/modals/addressbook.html', { - scope: $scope - }).then(function(modal) { - $scope.addressbookModal = modal; - $scope.addressbookModal.show(); + $log.debug('Got toAddress:' + addr + ' | ' + item.name); + return $state.transitionTo('send.amount', { + toAddress: addr, + toName: item.name + }) }); }; diff --git a/src/js/controllers/tab-settings.js b/src/js/controllers/tab-settings.js index e4ce0e833..b779d5f87 100644 --- a/src/js/controllers/tab-settings.js +++ b/src/js/controllers/tab-settings.js @@ -1,6 +1,6 @@ 'use strict'; -angular.module('copayApp.controllers').controller('tabSettingsController', function($scope, $rootScope, $log, $ionicModal, $window, lodash, configService, uxLanguage, platformInfo, pushNotificationsService, profileService, feeService) { +angular.module('copayApp.controllers').controller('tabSettingsController', function($scope, $rootScope, $log, $window, lodash, configService, uxLanguage, platformInfo, pushNotificationsService, profileService, feeService) { $scope.init = function() { @@ -38,17 +38,6 @@ angular.module('copayApp.controllers').controller('tabSettingsController', funct $scope.wallets = profileService.getWallets(); }; - $scope.openAddressbookModal = function() { - - $ionicModal.fromTemplateUrl('views/modals/addressbook.html', { - scope: $scope - }).then(function(modal) { - $scope.addressbookModal = modal; - $scope.addressbookModal.show(); - }); - }; - - $scope.openSettings = function() { cordova.plugins.diagnostic.switchToSettings(function() { $log.debug('switched to settings'); diff --git a/src/js/routes.js b/src/js/routes.js index e37d8efa2..0c2158085 100644 --- a/src/js/routes.js +++ b/src/js/routes.js @@ -490,6 +490,41 @@ angular.module('copayApp').config(function(historicLogProvider, $provide, $logPr } }) + /* + * + * Addressbook + * + */ + + + .state('tabs.addressbook', { + url: '/addressbook', + views: { + 'tab-settings': { + templateUrl: 'views/addressbook.html', + controller: 'addressbookListController' + } + } + }) + .state('tabs.addressbook.add', { + url: '/add', + views: { + 'tab-settings@tabs': { + templateUrl: 'views/addressbook.add.html', + controller: 'addressbookAddController' + } + } + }) + .state('tabs.addressbook.view', { + url: '/view/:address', + views: { + 'tab-settings@tabs': { + templateUrl: 'views/addressbook.view.html', + controller: 'addressbookViewController' + } + } + }) + /* * *TO DO @@ -726,7 +761,7 @@ angular.module('copayApp').config(function(historicLogProvider, $provide, $logPr } }); }) - .run(function($rootScope, $state, $location, $log, $timeout, $ionicHistory, $ionicPlatform, lodash, platformInfo, profileService, uxLanguage, gettextCatalog, openURLService) { + .run(function($rootScope, $state, $location, $log, $timeout, $ionicHistory, $ionicPlatform, lodash, platformInfo, profileService, uxLanguage, gettextCatalog, openURLService, storageService) { uxLanguage.init(); openURLService.init(); @@ -739,21 +774,35 @@ angular.module('copayApp').config(function(historicLogProvider, $provide, $logPr cordova.plugins.Keyboard.disableScroll(true); } + window.addEventListener('native.keyboardshow', function() { + document.querySelector('div.tabs').style.display = 'none'; + angular.element(document.querySelector('ion-content.has-tabs')).css('bottom', 0); + }); + + window.addEventListener('native.keyboardhide', function() { + var tabs = document.querySelectorAll('div.tabs'); + angular.element(tabs[0]).css('display', ''); + }); + $ionicPlatform.registerBackButtonAction(function(e) { - var fromDisclaimer = $ionicHistory.currentStateName().match(/disclaimer/) ? 'true' : ''; - var fromTabs = $ionicHistory.currentStateName().match(/tabs/) ? 'true' : ''; + var fromWelcome = $ionicHistory.currentStateName().match(/welcome/) ? true : false; + var matchHome = $ionicHistory.currentStateName().match(/home/) ? true : false; + var matchReceive = $ionicHistory.currentStateName().match(/receive/) ? true : false; + var matchSend = $ionicHistory.currentStateName().match(/send/) ? true : false; + var matchSettings = $ionicHistory.currentStateName().match(/settings/) ? true : false; + var fromTabs = matchHome | matchReceive | matchSend | matchSettings; - if ($rootScope.backButtonPressedOnceToExit || fromDisclaimer) { - ionic.Platform.exitApp(); - } else if ($ionicHistory.backView() && !fromTabs) { + if ($ionicHistory.backView() && !fromTabs) { $ionicHistory.goBack(); + } else if ($rootScope.backButtonPressedOnceToExit || fromWelcome) { + ionic.Platform.exitApp(); } else { $rootScope.backButtonPressedOnceToExit = true; window.plugins.toast.showShortBottom(gettextCatalog.getString('Press again to exit')); - setInterval(function() { + $timeout(function() { $rootScope.backButtonPressedOnceToExit = false; - }, 5000); + }, 3000); } e.preventDefault(); }, 101); @@ -782,10 +831,19 @@ angular.module('copayApp').config(function(historicLogProvider, $provide, $logPr if (err) { if (err.message && err.message.match('NOPROFILE')) { $log.debug('No profile... redirecting'); - $state.transitionTo('onboarding.welcome'); + $state.go('onboarding.welcome'); } else if (err.message && err.message.match('NONAGREEDDISCLAIMER')) { $log.debug('Display disclaimer... redirecting'); - $state.transitionTo('onboarding.disclaimer'); + storageService.getLastState(function(err, state) { + if (err && !state) { + $log.error(err); + $state.go('onboarding.disclaimer'); + } + else { + var state = JSON.parse(state); + $state.go(state.name, state.toParams); + } + }) } else { throw new Error(err); // TODO } @@ -793,7 +851,7 @@ angular.module('copayApp').config(function(historicLogProvider, $provide, $logPr profileService.storeProfileIfDirty(); $log.debug('Profile loaded ... Starting UX.'); - $state.transitionTo('tabs.home'); + $state.go('tabs.home'); } }); }); @@ -816,6 +874,9 @@ angular.module('copayApp').config(function(historicLogProvider, $provide, $logPr $log.debug('Route change from:', fromState.name || '-', ' to:', toState.name); $log.debug(' toParams:' + JSON.stringify(toParams || {})); $log.debug(' fromParams:' + JSON.stringify(fromParams || {})); - + var state = {}; + state.name = toState.name; + state.toParams = toParams; + storageService.setLastState(JSON.stringify(state), function() {}); }); }); diff --git a/src/js/services/addressbookService.js b/src/js/services/addressbookService.js index 74fe7edcc..edd3237d1 100644 --- a/src/js/services/addressbookService.js +++ b/src/js/services/addressbookService.js @@ -3,12 +3,16 @@ angular.module('copayApp.services').factory('addressbookService', function(bitcore, storageService, lodash) { var root = {}; - root.getLabel = function(addr, cb) { + root.get = function(addr, cb) { storageService.getAddressbook('testnet', function(err, ab) { - if (ab && ab[addr]) return cb(ab[addr]); + if (err) return cb(err); + if (ab) ab = JSON.parse(ab); + if (ab && ab[addr]) return cb(null, ab[addr]); - storageService.getAddressbook('livnet', function(err, ab) { - if (ab && ab[addr]) return cb(ab[addr]); + storageService.getAddressbook('livenet', function(err, ab) { + if (err) return cb(err); + if (ab) ab = JSON.parse(ab); + if (ab && ab[addr]) return cb(null, ab[addr]); return cb(); }); }); @@ -38,7 +42,7 @@ angular.module('copayApp.services').factory('addressbookService', function(bitco ab = ab || {}; if (lodash.isArray(ab)) ab = {}; // No array if (ab[entry.address]) return cb('Entry already exist'); - ab[entry.address] = entry.label; + ab[entry.address] = entry; storageService.setAddressbook(network, JSON.stringify(ab), function(err, ab) { if (err) return cb('Error adding new entry'); root.list(function(err, ab) { diff --git a/src/js/services/popupService.js b/src/js/services/popupService.js index c94e6df6a..a510ca153 100644 --- a/src/js/services/popupService.js +++ b/src/js/services/popupService.js @@ -10,14 +10,19 @@ angular.module('copayApp.services').service('popupService', function($log, $ioni if (!cb) cb = function() {}; $ionicPopup.alert({ title: title, - template: message + template: message, + okType: 'button-clear button-positive' }).then(cb); }; - var _ionicConfirm = function(title, message, cb) { + var _ionicConfirm = function(title, message, okText, cancelText, cb) { $ionicPopup.confirm({ title: title, - template: message + template: message, + cancelText: cancelText, + cancelType: 'button-clear button-positive', + okText: okText, + okType: 'button-clear button-positive' }).then(function(res) { return cb(res); }); @@ -42,16 +47,16 @@ angular.module('copayApp.services').service('popupService', function($log, $ioni navigator.notification.alert(message, cb, title); }; - var _cordovaConfirm = function(title, message, cb) { - var onConfirm = function (buttonIndex) { + var _cordovaConfirm = function(title, message, okText, cancelText, cb) { + var onConfirm = function(buttonIndex) { if (buttonIndex == 1) return cb(true); else return cb(false); } - navigator.notification.confirm(message, onConfirm, title); + navigator.notification.confirm(message, onConfirm, title, [okText, cancelText]); }; var _cordovaPrompt = function(title, message, cb) { - var onPrompt = function (results) { + var onPrompt = function(results) { if (results.buttonIndex == 1) return cb(results.input1); else return cb(); } @@ -81,17 +86,19 @@ angular.module('copayApp.services').service('popupService', function($log, $ioni * * @param {String} Title * @param {String} Message + * @param {String} okText + * @param {String} cancelText * @param {Callback} Function * @returns {Callback} OK: true, Cancel: false */ - this.showConfirm = function(title, message, cb) { + this.showConfirm = function(title, message, okText, cancelText, cb) { $log.warn(title + ": " + message); if (isCordova) - _cordovaConfirm(title, message, cb); + _cordovaConfirm(title, message, okText, cancelText, cb); else - _ionicConfirm(title, message, cb); + _ionicConfirm(title, message, okText, cancelText, cb); }; /** diff --git a/src/js/services/storageService.js b/src/js/services/storageService.js index a498dfb89..4df36dab1 100644 --- a/src/js/services/storageService.js +++ b/src/js/services/storageService.js @@ -276,6 +276,13 @@ angular.module('copayApp.services') storage.remove('nextStep-' + service, cb); }; + root.setLastState = function(state, toParams, cb) { + storage.set('lastState', state, toParams, cb); + }; + + root.getLastState = function(cb) { + storage.get('lastState', cb); + }; root.checkQuota = function() { var block = ''; @@ -342,6 +349,22 @@ angular.module('copayApp.services') }); }; + root.setScanTipsAccepted = function(val, cb) { + storage.set('scanTips', val, cb); + }; + + root.getScanTipsAccepted = function(cb) { + storage.get('scanTips', cb); + }; + + root.setReceiveTipsAccepted = function(val, cb) { + storage.set('receiveTips', val, cb); + }; + + root.getReceiveTipsAccepted = function(cb) { + storage.get('receiveTips', cb); + }; + root.setAmazonGiftCards = function(network, gcs, cb) { storage.set('amazonGiftCards-' + network, gcs, cb); }; diff --git a/src/js/services/walletService.js b/src/js/services/walletService.js index 204869c61..d23c8b906 100644 --- a/src/js/services/walletService.js +++ b/src/js/services/walletService.js @@ -1023,7 +1023,7 @@ angular.module('copayApp.services').factory('walletService', function($log, $tim try { keys = wallet.getKeys(password); } catch (e) { - return cb(err); + return cb(e); } return cb(null, keys); diff --git a/src/sass/views/onboarding/onboard-backup-request.scss b/src/sass/views/onboarding/onboard-backup-request.scss index 0ad4d641d..8ed5a36d8 100644 --- a/src/sass/views/onboarding/onboard-backup-request.scss +++ b/src/sass/views/onboarding/onboard-backup-request.scss @@ -8,4 +8,50 @@ #arrow-down{ font-size: 4.2rem; } + .cta-buttons{ + width:100%; + float:none; + position: absolute; + bottom: 0; + } +} + +@media (max-width: 400px){ + #onboarding-backup-request{ + .warning{ + margin: 2rem auto 1rem; + height: 8rem; + } + h3{ + font-size:1.3rem; + } + p{ + font-size:.9rem; + max-width: 80%; + flex: 0 0 80%; + } + .cta-buttons{ + float:none; + bottom:0; + position: absolute; + button{ + max-width: 400px; + } + } + } +} +@media (max-height: 540px){ + #onboarding-backup-request{ + .cta-buttons{ + float:left; + position: relative; + } + } +} +@media (min-height: 980px){ + #onboarding-backup-request{ + #arrow-down{ + margin-top: 15rem; + } + } } \ No newline at end of file diff --git a/src/sass/views/onboarding/onboard-backup-warning.scss b/src/sass/views/onboarding/onboard-backup-warning.scss index 0ee7b5c1b..7eaeacbca 100644 --- a/src/sass/views/onboarding/onboard-backup-warning.scss +++ b/src/sass/views/onboarding/onboard-backup-warning.scss @@ -8,4 +8,46 @@ #arrow-down{ font-size: 4.2rem; } + .cta-buttons{ + float:none; + bottom:46px; + position: absolute; + width:100%; + button{ + max-width: 400px; + } + } +} + +@media (max-width: 400px){ + #onboarding-backup-warning{ + .warning{ + margin: 2rem auto 1rem; + height: 8rem; + } + h3{ + font-size:1.3rem; + } + p{ + font-size:.9rem; + max-width: 80%; + flex: 0 0 80%; + } + .warning-image{ + height: 11rem; + } + .cta-buttons{ + float:none; + bottom:46px; + position: absolute; + } + } +} +@media (max-height: 540px){ + #onboarding-backup-warning{ + .cta-buttons{ + float:left; + position: relative; + } + } } \ No newline at end of file diff --git a/src/sass/views/onboarding/onboard-collect-email.scss b/src/sass/views/onboarding/onboard-collect-email.scss index 125d21a93..ad0be19fb 100644 --- a/src/sass/views/onboarding/onboard-collect-email.scss +++ b/src/sass/views/onboarding/onboard-collect-email.scss @@ -14,25 +14,22 @@ margin-top: 1rem; margin-bottom: 1rem; } - .collect-overlay, .bar-overlay { - animation-name: opacity; - animation-iteration-count: 1; - animation-timing-function: ease-in; - animation-duration: .2s; - animation-delay: .8s; - animation-fill-mode: forwards; - opacity: 0; - } - .collect-overlay{ - top:-1px; - } - .bar-overlay{ - background: rgba(0, 0, 0, 0.23); - .button-clear{ - color:#fff; - min-width: 100%; - } - } + .collect-overlay { + animation-name: opacity; + animation-iteration-count: 1; + animation-timing-function: ease-in; + animation-duration: .2s; + animation-delay: .8s; + animation-fill-mode: forwards; + opacity: 0; + button { + position: absolute; + right: 11px; + } + } + .collect-overlay{ + top:-1px; + } #collect-email { opacity: 1; background: #fff; @@ -52,10 +49,7 @@ label { background: rgba(200, 200, 200, 0.20); height: 3rem; - margin-top: 0; - input { - position: absolute; - } + margin-top:0; i { position: absolute; right: 3%; @@ -64,6 +58,19 @@ } } } +@media (min-width: 1000px){ + #onboarding-collect-email{ + #collect-email{ + p, form{ + max-width: 600px; + @include center-block(); + } + form{ + margin-top:.5rem; + } + } + } +} @keyframes topBottom { 0% { diff --git a/src/sass/views/onboarding/onboard-tour.scss b/src/sass/views/onboarding/onboard-tour.scss index ddb13b75e..b955d6c53 100644 --- a/src/sass/views/onboarding/onboard-tour.scss +++ b/src/sass/views/onboarding/onboard-tour.scss @@ -23,6 +23,10 @@ background-position: top; } } + .cta-button{ + position: absolute; + bottom: 85px; + } } @media (max-width: 400px){ @@ -34,3 +38,22 @@ } } } + +@media (min-width: 1000px){ + #onboard-tour{ + p, h2, h3{ + max-width: 600px; + } + button{ + max-width: 400px; + } + #cta{ + margin: 2rem 0 0; + } + &-control{ + #cta{ + margin-bottom: 2rem; + } + } + } +} diff --git a/src/sass/views/onboarding/onboarding.scss b/src/sass/views/onboarding/onboarding.scss index 0c5522ac3..c84b0e3df 100644 --- a/src/sass/views/onboarding/onboarding.scss +++ b/src/sass/views/onboarding/onboarding.scss @@ -81,6 +81,12 @@ h2{ font-size: 1.2rem; } + p,h2,h3{ + max-width: 600px !important; + } + button{ + max-width: 400px !important; + } } } }