Merge branch 'ref/design' of https://github.com/bitpay/bitpay-wallet into feature/recieve_view_polish

This commit is contained in:
Jamal Jackson 2016-10-17 09:11:15 -04:00
commit aad986c266
117 changed files with 7722 additions and 5291 deletions

2
.gitignore vendored
View File

@ -50,7 +50,7 @@ build/Release
node_modules
bower_components
angular-bitcore-wallet-client/angular-bitcore-wallet-client.js
angular-pbkdf2/angular-pbkdf2.js
angular-bitauth/angular-bitauth.js
# Users Environment Variables
.lock-wscript

View File

@ -128,7 +128,7 @@ module.exports = function(grunt) {
'bower_components/angular-md5/angular-md5.js',
'bower_components/angular-mocks/angular-mocks.js',
'bower_components/ngtouch/src/ngTouch.js',
'angular-pbkdf2/angular-pbkdf2.js',
'angular-bitauth/angular-bitauth.js',
'angular-bitcore-wallet-client/angular-bitcore-wallet-client.js'
],
dest: 'www/lib/angular.js'
@ -216,15 +216,6 @@ module.exports = function(grunt) {
}],
}
},
karma: {
unit: {
configFile: 'test/karma.conf.js'
},
prod: {
configFile: 'test/karma.conf.js',
singleRun: true
}
},
nwjs: {
options: {
appName: 'Copay',
@ -251,7 +242,7 @@ module.exports = function(grunt) {
dist: {
files: {
'angular-bitcore-wallet-client/angular-bitcore-wallet-client.js': ['angular-bitcore-wallet-client/index.js'],
'angular-pbkdf2/angular-pbkdf2.js': ['angular-pbkdf2/index.js']
'angular-bitauth/angular-bitauth.js': ['angular-bitauth/index.js']
},
}
}
@ -260,8 +251,6 @@ module.exports = function(grunt) {
grunt.registerTask('default', ['nggettext_compile', 'exec:appConfig', 'exec:externalServices', 'browserify', 'sass', 'concat', 'copy:ionic_fonts', 'copy:ionic_js']);
grunt.registerTask('prod', ['default', 'uglify']);
grunt.registerTask('translate', ['nggettext_extract']);
grunt.registerTask('test', ['karma:unit']);
grunt.registerTask('test-coveralls', ['browserify', 'karma:prod', 'exec:coveralls']);
grunt.registerTask('desktop', ['prod', 'nwjs', 'copy:linux', 'compress:linux']);
grunt.registerTask('osx', ['prod', 'nwjs', 'exec:osx']);
grunt.registerTask('chrome', ['exec:chrome']);

18
angular-bitauth/index.js vendored Normal file
View File

@ -0,0 +1,18 @@
var bitauthModule = angular.module('bitauthModule', []);
var bitauth = require('../node_modules/bitauth');
bitauthModule.constant('MODULE_VERSION', '1.0.0');
bitauthModule.provider("bitauthService", function() {
var provider = {};
provider.$get = function() {
var service = {};
service = bitauth;
return service;
};
return provider;
});

6380
angular-pbkdf2/angular-pbkdf2.js vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,18 +0,0 @@
var pbkdf2Module = angular.module('pbkdf2Module', []);
var pbkdf2Sync = require('../node_modules/pbkdf2').pbkdf2Sync;
pbkdf2Module.constant('MODULE_VERSION', '1.0.0');
pbkdf2Module.provider("pbkdf2Service", function() {
var provider = {};
provider.$get = function() {
var service = {};
service.pbkdf2Sync = pbkdf2Sync;
return service;
};
return provider;
});

View File

@ -5,6 +5,7 @@
//
var templates = {
'package.json': '/',
'Makefile': 'cordova/',
'ProjectMakefile': 'cordova/',
'config-template.xml': '/',

View File

@ -5,12 +5,13 @@
"purposeLine": "Secure Bitcoin Wallet",
"bundleName": "wallet",
"appUri": "bitpay",
"name": "bitpay",
"nameNoSpace": "bitpay",
"nameCase": "BitPay",
"nameCaseNoSpace": "BitPay",
"gitHubRepoName": "bitpay-wallet",
"gitHubRepoUrl": "git://github.com/bitpay/bitpay-wallet.git",
"gitHubRepoBugs": "https://github.com/bitpay/bitpay-wallet/issues",
"disclaimerUrl": "",
"url": "https://bitpay.com",
"appDescription": "Secure Bitcoin Wallet",

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.9 KiB

View File

@ -16,7 +16,8 @@
<preference name="iosPersistentFileLocation" value="Library" />
<preference name="DisallowOverscroll" value="true"/>
<preference name="HideKeyboardFormAccessoryBar" value="true"/>
<preference name="KeyboardDisplayRequiresUserAction" value="false" />
<!-- #355 -->
<!-- <preference name="KeyboardDisplayRequiresUserAction" value="false" /> -->
<preference name="StatusBarBackgroundColor" value="#1e3186" />
<preference name="BackupWebStorage" value="none"/>
<preference name="windows-target-version" value="8.1"/>
@ -56,8 +57,9 @@
<plugin name="cordova-plugin-whitelist" spec="~1.3.0" />
<plugin name="cordova-plugin-wkwebview-engine" spec="https://github.com/driftyco/cordova-plugin-wkwebview-engine.git" />
<plugin name="cordova-plugin-qrscanner" spec="~2.4.0" />
<plugin name="cordova-plugin-customurlscheme" spec="~4.2.0">
<variable name="URL_SCHEME" value="UNUSED" />
<plugin name="cordova-plugin-customurlscheme" spec="https://github.com/cmgustavo/Custom-URL-scheme.git">
<variable name="URL_SCHEME" value="bitcoin" />
<variable name="SECOND_URL_SCHEME" value="*APPURI*" />
</plugin>
<plugin name="phonegap-plugin-push" spec="~1.8.2">
<variable name="SENDER_ID" value="*PUSHSENDERID*"/>

View File

@ -3,14 +3,15 @@
"packageDescription": "Copay Bitcoin Wallet",
"userVisibleName": "Copay",
"purposeLine": "Copay Bitcoin Wallet",
"bundleName": "copay",
"appUri": "copay",
"name": "copay",
"nameNoSpace": "copay",
"nameCase": "Copay",
"nameCaseNoSpace": "Copay",
"bundleName": "copay",
"gitHubRepoName": "copay",
"gitHubRepoUrl": "git://github.com/bitpay/copay.git",
"gitHubRepoBugs": "https://github.com/bitpay/copay/issues",
"disclaimerUrl": "https://copay.io/disclaimer",
"url": "https://copay.io",
"appDescription": "Copay Bitcoin Wallet",

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

View File

@ -14,14 +14,14 @@
"main": "www/index.html",
"window": {
"title": "*USERVISIBLENAME* - *PURPOSELINE*",
"icon": "./www/img/icons/icon-256.png",
"icon": "www/img/icon-128.png",
"toolbar": false,
"show": true,
"visible": true,
"resizable": true,
"frame": true,
"width": 400,
"height": 600,
"height": 650,
"position": "center",
"fullscreen": false
},
@ -32,25 +32,27 @@
},
"dom_storage_quota": 200,
"id": "jid1-x7bV5evAaI1P9Q",
"homepage": "https://github.com/bitpay/copay",
"homepage": "*URL*",
"license": "MIT",
"repository": {
"url": "git://github.com/bitpay/copay.git",
"url": "*GITHUBREPOURL*",
"type": "git"
},
"bugs": {
"url": "https://github.com/bitpay/copay/issues"
"url": "*GITHUBREPOBUGS*"
},
"dependencies": {
"adm-zip": "^0.4.7",
"angular": "1.4.6",
"angular-mocks": "1.4.10",
"bezier-easing": "^2.0.3",
"bhttp": "^1.2.1",
"bitcore-wallet-client": "4.2.1",
"bitauth": "^0.3.2",
"bitcore-wallet-client": "4.3.2",
"bower": "^1.7.9",
"chai": "^3.5.0",
"cordova": "5.4.1",
"cordova-android": "5.1.1",
"cordova-custom-config": "^3.0.5",
"cordova-plugin-qrscanner": "^2.3.1",
"coveralls": "^2.11.9",
"express": "^4.11.2",
@ -65,39 +67,44 @@
"grunt-contrib-uglify": "^2.0.0",
"grunt-contrib-watch": "^1.0.0",
"grunt-exec": "^1.0.0",
"grunt-karma": "^2.0.0",
"grunt-karma-coveralls": "^2.5.4",
"grunt-nw-builder": "^2.0.3",
"grunt-sass": "^1.2.0",
"karma": "^0.13.22",
"karma-chai": "^0.1.0",
"karma-chrome-launcher": "^1.0.1",
"karma-cli": "^1.0.0",
"karma-coverage": "^1.0.0",
"karma-mocha": "^1.0.1",
"karma-mocha-reporter": "^2.0.3",
"karma-phantomjs-launcher": "^1.0.0",
"karma-sinon": "^1.0.5",
"load-grunt-tasks": "^3.5.0",
"mocha": "^2.4.5",
"pbkdf2": "^3.0.4",
"phantomjs-prebuilt": "^2.1.7",
"shelljs": "^0.3.0",
"xcode": "^0.8.2"
"shelljs": "^0.3.0"
},
"scripts": {
"preinstall": "bower install && npm i fs-extra",
"build": "grunt",
"apply:copay": "cd app-template && node apply.js",
"apply:bitpay-wallet": "cd app-template && node apply.js bitpay-wallet",
"start": "npm run build && node app.js",
"watch": "grunt watch",
"test": "./node_modules/.bin/grunt test-coveralls",
"clean": "git clean -dfx",
"start:ios": "npm run build && cd cordova && trash project-ios && make ios && open project-ios/platforms/ios/BitPay\\ Wallet.xcodeproj",
"start:android": "npm run build && cd cordova && trash project-android && make android && open -a /Applications/Android\\ Studio.app project-android/platforms/android"
"preinstall": "bower install && npm i fs-extra",
"postinstall": "npm run apply:bitpay && cordova prepare",
"start": "npm run build:www && ionic serve --nolivereload --nogulp -s",
"start:ios": "npm run build:www && npm run build:ios && npm run open:ios",
"start:android": "npm run build:www && npm run build:android && npm run run:android",
"watch": "grunt watch",
"build:www": "grunt",
"build:www-release": "grunt prod",
"build:ios": "cordova prepare ios && cordova build ios --debug",
"build:android": "cordova prepare android && cordova build android --debug",
"build:ios-release": "cordova prepare ios && cordova build ios --release",
"build:android-release": "cordova prepare android && cordova build android --release",
"open:ios": "open platforms/ios/*.xcodeproj",
"open:android": "open -a open -a /Applications/Android\\ Studio.app platforms/android",
"final:www": "npm run clean-all && npm run build:www-release",
"final:ios": "npm run final:www && npm run build:ios-release && npm run open:ios",
"final:android": "npm run final:www && npm run build:android-release && npm run run:android",
"run:android": "cordova run android --device",
"log:android": "adb logcat | grep chromium",
"apply:copay": "cd app-template && node apply.js copay",
"apply:bitpay": "cd app-template && node apply.js bitpay",
"test": "./node_modules/.bin/grunt test-coveralls",
"clean": "trash platforms && trash plugins && npm run postinstall",
"clean-all": "git clean -dfx && npm install"
},
"devDependencies": {
"trash-cli": "^1.4.0"
"cordova": "^6.3.1",
"grunt": "^1.0.1",
"ionic": "^2.1.0",
"trash-cli": "^1.4.0",
"lodash": "^4.3.0"
}
}

View File

@ -8,7 +8,7 @@
#define MyAppExeName "*PACKAGENAME.exe"
[Setup]
AppId={{804636ee-b017-4cad-8719-e58ac97ffa5c}
AppId={804636ee-b017-4cad-8719-e58ac97ffa5c}
AppName={#MyAppName}
AppVersion={#MyAppVersion}
;AppVerName={#MyAppName} {#MyAppVersion}

View File

@ -1,4 +1,9 @@
{
{
"//":"PLEASE! Do not edit this file directly",
"//":" Modify it at app-template/",
"name": "bitpay",
"description": "Secure Bitcoin Wallet",
"author": "BitPay",
"version": "0.14.0",
"keywords": [
@ -9,14 +14,35 @@
"multisignature",
"bitcore"
],
"homepage": "https://github.com/bitpay/copay",
"main": "www/index.html",
"window": {
"title": "BitPay - Secure Bitcoin Wallet",
"icon": "www/img/icon-128.png",
"toolbar": false,
"show": true,
"visible": true,
"resizable": true,
"frame": true,
"width": 400,
"height": 650,
"position": "center",
"fullscreen": false
},
"webkit": {
"page-cache": false,
"java": false,
"plugin": false
},
"dom_storage_quota": 200,
"id": "jid1-x7bV5evAaI1P9Q",
"homepage": "https://bitpay.com",
"license": "MIT",
"repository": {
"url": "git://github.com/bitpay/copay.git",
"url": "git://github.com/bitpay/bitpay-wallet.git",
"type": "git"
},
"bugs": {
"url": "https://github.com/bitpay/copay/issues"
"url": "https://github.com/bitpay/bitpay-wallet/issues"
},
"dependencies": {
"adm-zip": "^0.4.7",
@ -24,7 +50,8 @@
"angular-mocks": "1.4.10",
"bezier-easing": "^2.0.3",
"bhttp": "^1.2.1",
"bitcore-wallet-client": "4.3.1",
"bitauth": "^0.3.2",
"bitcore-wallet-client": "4.3.2",
"bower": "^1.7.9",
"chai": "^3.5.0",
"cordova-android": "5.1.1",
@ -43,22 +70,10 @@
"grunt-contrib-uglify": "^2.0.0",
"grunt-contrib-watch": "^1.0.0",
"grunt-exec": "^1.0.0",
"grunt-karma": "^2.0.0",
"grunt-karma-coveralls": "^2.5.4",
"grunt-nw-builder": "^2.0.3",
"grunt-sass": "^1.2.0",
"karma": "^0.13.22",
"karma-chai": "^0.1.0",
"karma-chrome-launcher": "^1.0.1",
"karma-cli": "^1.0.0",
"karma-coverage": "^1.0.0",
"karma-mocha": "^1.0.1",
"karma-mocha-reporter": "^2.0.3",
"karma-phantomjs-launcher": "^1.0.0",
"karma-sinon": "^1.0.5",
"load-grunt-tasks": "^3.5.0",
"mocha": "^2.4.5",
"pbkdf2": "^3.0.4",
"phantomjs-prebuilt": "^2.1.7",
"shelljs": "^0.3.0"
},

View File

@ -0,0 +1,45 @@
<ion-view id="bitpayCard-intro">
<ion-nav-bar class="bar-stable">
<ion-nav-back-button>
</ion-nav-back-button>
<ion-nav-title></ion-nav-title>
<ion-nav-buttons side="secondary">
<button class="button back-button button-clear" ng-click="orderBitPayCard()">
<i class="icon ion-ios-information-outline"></i>
</button>
</ion-nav-buttons>
</ion-nav-bar>
<ion-content scroll="false">
<div class="text-center padding">
<img src="img/bitpay-card-visa.svg" width="100%">
</div>
<ion-slide-box>
<ion-slide>
<p translate>
Turn bitcoin into dollars, swipe anywhere Visa&reg; is accepted.
</p>
</ion-slide>
<ion-slide>
<p translate>
<span translate>Get local cash anywhere you go, from any Visa&reg;-compatible ATM.</span>
<div translate class="size-10 m20t text-center">
*ATM bank fees may apply
</div>
</p>
</ion-slide>
<ion-slide>
<p translate>
Pay 0% fees to turn bitcoin into dollars.
</p>
</ion-slide>
</ion-slide-box>
<div class="cta-button">
<button class="button button-block button-primary" ng-click="orderBitPayCard()" translate>
Order the BitPay Card
</button>
<button class="button button-block button-transparent text-white m10t" ng-click="connectBitPayCard()" translate>
Connect my BitPay Card
</button>
</div>
</ion-content>
</ion-view>

View File

@ -12,7 +12,7 @@ var modules = [
'ngCsv',
'angular-md5',
'bwcModule',
'pbkdf2Module',
'bitauthModule',
'copayApp.filters',
'copayApp.services',
'copayApp.controllers',

View File

@ -2,33 +2,10 @@
angular.module('copayApp.controllers').controller('addressbookViewController', function($scope, $state, $timeout, $stateParams, lodash, addressbookService, popupService, $ionicHistory) {
$scope.$on("$ionicView.beforeEnter", function(event, data){
var address = data.stateParams.address;
if (!address) {
$ionicHistory.back();
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;
$timeout(function() {
$scope.$apply();
});
});
});
$scope.addressbookEntry = {};
$scope.addressbookEntry.name = $stateParams.name;
$scope.addressbookEntry.email = $stateParams.email;
$scope.addressbookEntry.address = $stateParams.address;
$scope.sendTo = function() {
$ionicHistory.removeBackView();

View File

@ -5,8 +5,8 @@ angular.module('copayApp.controllers').controller('amazonController',
$scope.network = amazonService.getEnvironment();
$scope.openExternalLink = function(url, target) {
externalLinkService.open(url, target);
$scope.openExternalLink = function(url, optIn, title, message, okText, cancelText) {
externalLinkService.open(url, optIn, title, message, okText, cancelText);
};
var initAmazon = function() {
@ -83,7 +83,7 @@ angular.module('copayApp.controllers').controller('amazonController',
});
};
$scope.$on("$ionicView.beforeEnter", function(event, data){
$scope.$on("$ionicView.beforeEnter", function(event, data) {
initAmazon();
});
});

View File

@ -17,13 +17,14 @@ angular.module('copayApp.controllers').controller('amountController', function($
$scope.$on("$ionicView.beforeEnter", function(event, data) {
$scope.isWallet = data.stateParams.isWallet;
$scope.isCard = data.stateParams.isCard;
$scope.cardId = data.stateParams.cardId;
$scope.toAddress = data.stateParams.toAddress;
$scope.toName = data.stateParams.toName;
$scope.toEmail = data.stateParams.toEmail;
$scope.showAlternativeAmount = !!$scope.isCard;
$scope.showAlternativeAmount = !!$scope.cardId;
$scope.toColor = data.stateParams.toColor;
if (!$scope.isCard && !$stateParams.toAddress) {
if (!$scope.cardId && !$stateParams.toAddress) {
$log.error('Bad params at amount')
throw ('bad params');
}
@ -93,7 +94,7 @@ angular.module('copayApp.controllers').controller('amountController', function($
$scope.pushDigit = function(digit) {
if ($scope.amount && $scope.amount.length >= LENGTH_EXPRESSION_LIMIT) return;
if ($scope.amount.indexOf('.') > -1 && digit == '.') return;
if($scope.showAlternativeAmount && $scope.amount.indexOf('.') > -1 && $scope.amount[$scope.amount.indexOf('.') + 2]) return;
if ($scope.showAlternativeAmount && $scope.amount.indexOf('.') > -1 && $scope.amount[$scope.amount.indexOf('.') + 2]) return;
$scope.amount = ($scope.amount + digit).replace('..', '.');
checkFontSize();
@ -189,7 +190,7 @@ angular.module('copayApp.controllers').controller('amountController', function($
$scope.finish = function() {
var _amount = evaluate(format($scope.amount));
if ($scope.isCard) {
if ($scope.cardId) {
var amountUSD = $scope.showAlternativeAmount ? _amount : $filter('formatFiatAmount')(toFiat(_amount));
var dataSrc = {
@ -199,7 +200,7 @@ angular.module('copayApp.controllers').controller('amountController', function($
ongoingProcess.set('Processing Transaction...', true);
$timeout(function() {
bitpayCardService.topUp(dataSrc, function(err, invoiceId) {
bitpayCardService.topUp($scope.cardId, dataSrc, function(err, invoiceId) {
if (err) {
ongoingProcess.set('Processing Transaction...', false);
popupService.showAlert(gettextCatalog.getString('Error'), bwcError.msg(err));
@ -215,7 +216,7 @@ angular.module('copayApp.controllers').controller('amountController', function($
var payProUrl = data.paymentUrls.BIP73;
$state.transitionTo('tabs.bitpayCard.confirm', {
isCard: $scope.isCard,
cardId: $scope.cardId,
toName: $scope.toName,
paypro: payProUrl
});

View File

@ -1,39 +1,19 @@
'use strict';
angular.module('copayApp.controllers').controller('bitpayCardController', function($scope, $timeout, $log, lodash, bitpayCardService, configService, profileService, walletService, ongoingProcess, pbkdf2Service, moment, popupService, gettextCatalog, bwcError) {
angular.module('copayApp.controllers').controller('bitpayCardController', function($scope, $timeout, $log, $state, lodash, bitpayCardService, moment, popupService, gettextCatalog, $ionicHistory) {
var self = this;
$scope.dateRange = 'last30Days';
$scope.dateRange = { value: 'last30Days'};
$scope.network = bitpayCardService.getEnvironment();
bitpayCardService.getCacheData(function(err, data) {
if (err || lodash.isEmpty(data)) return;
$scope.bitpayCardCached = true;
self.bitpayCardTransactionHistory = data.transactions;
self.bitpayCardCurrentBalance = data.balance;
});
var processTransactions = function(invoices, history) {
for (var i = 0; i < invoices.length; i++) {
var matched = false;
for (var j = 0; j < history.length; j++) {
if (history[j].description[0].indexOf(invoices[i].id) > -1) {
matched = true;
}
}
if (!matched && ['paid', 'confirmed', 'complete'].indexOf(invoices[i].status) > -1) {
history.unshift({
timestamp: invoices[i].invoiceTime,
description: invoices[i].itemDesc,
amount: invoices[i].price,
type: '00611 = Client Funded Deposit',
pending: true,
status: invoices[i].status
});
}
}
return history;
var getFromCache = function(cb) {
bitpayCardService.getBitpayDebitCardsHistory($scope.cardId, function(err, data) {
if (err || lodash.isEmpty(data)) return cb();
$scope.historyCached = true;
self.bitpayCardTransactionHistory = data.transactions;
self.bitpayCardCurrentBalance = data.balance;
return cb();
});
};
var setDateRange = function(preset) {
@ -62,41 +42,35 @@ angular.module('copayApp.controllers').controller('bitpayCardController', functi
};
this.update = function() {
var dateRange = setDateRange($scope.dateRange);
self.loadingSession = true;
bitpayCardService.isAuthenticated(function(err, bpSession) {
self.loadingSession = false;
var dateRange = setDateRange($scope.dateRange.value);
$scope.loadingHistory = true;
bitpayCardService.getHistory($scope.cardId, dateRange, function(err, history) {
$scope.loadingHistory = false;
if (err) {
$log.error(err);
$scope.error = gettextCatalog.getString('Could not get transactions');
return;
}
self.bitpayCardAuthenticated = bpSession.isAuthenticated;
self.bitpayCardTwoFactorPending = bpSession.twoFactorPending ? true : false;
var txs = lodash.clone(history.txs);
for (var i = 0; i < txs.length; i++) {
txs[i] = _getMerchantInfo(txs[i]);
txs[i].icon = _getIconName(txs[i]);
txs[i].desc = _processDescription(txs[i]);
}
self.bitpayCardTransactionHistory = txs;
self.bitpayCardCurrentBalance = history.currentCardBalance;
if (self.bitpayCardTwoFactorPending) return;
if (self.bitpayCardAuthenticated) {
$scope.loadingHistory = true;
bitpayCardService.invoiceHistory(function(err, invoices) {
if ($scope.dateRange.value == 'last30Days') {
$log.debug('BitPay Card: store cache history');
var cacheHistory = {
balance: history.currentCardBalance,
transactions: history.txs
};
bitpayCardService.setBitpayDebitCardsHistory($scope.cardId, cacheHistory, {}, function(err) {
if (err) $log.error(err);
bitpayCardService.transactionHistory(dateRange, function(err, history) {
$scope.loadingHistory = false;
if (err) {
popupService.showAlert(gettextCatalog.getString('Error'), gettextCatalog.getString('Could not get transactions'));
return;
}
self.bitpayCardTransactionHistory = processTransactions(invoices, history.transactionList);
self.bitpayCardCurrentBalance = history.currentCardBalance;
var cacheData = {
balance: self.bitpayCardCurrentBalance,
transactions: self.bitpayCardTransactionHistory
};
bitpayCardService.setCacheData(cacheData, function(err) {
if (err) $log.error(err);
});
});
$scope.historyCached = true;
});
}
$timeout(function() {
@ -105,76 +79,43 @@ angular.module('copayApp.controllers').controller('bitpayCardController', functi
});
};
this.authenticate = function(email, password) {
var data = {
emailAddress : email,
hashedPassword : pbkdf2Service.pbkdf2Sync(password, '..............', 200, 64).toString('hex')
};
// POST /authenticate
// emailAddress:
// hashedPassword:
self.authenticating = true;
bitpayCardService.authenticate(data, function(err, auth) {
self.authenticating = false;
if (err && err.data && err.data.error && !err.data.error.twoFactorPending) {
popupService.showAlert(gettextCatalog.getString('Error'), err.statusText || err.data.error || 'Authentiation error');
return;
}
self.update();
$timeout(function() {
$scope.$apply();
}, 100);
});
};
this.authenticate2FA = function(twoFactorCode) {
var data = {
twoFactorCode : twoFactorCode
};
self.authenticating = true;
bitpayCardService.authenticate2FA(data, function(err, auth) {
self.authenticating = false;
if (err) {
popupService.showAlert(gettextCatalog.getString('Error'), gettextCatalog.getString('Authentiation error'));
return;
}
self.update();
$timeout(function() {
$scope.$apply();
}, 100);
});
};
this.getMerchantInfo = function(tx) {
var _getMerchantInfo = function(tx) {
var bpTranCodes = bitpayCardService.bpTranCodes;
lodash.keys(bpTranCodes).forEach(function(code) {
if (tx.type.indexOf(code) === 0) {
lodash.assign(tx, bpTranCodes[code]);
}
});
return tx;
};
this.getIconName = function(tx) {
var _getIconName = function(tx) {
var icon = tx.mcc || tx.category || null;
if (!icon) return 'default';
return bitpayCardService.iconMap[icon];
};
this.processDescription = function(tx) {
var _processDescription = function(tx) {
if (lodash.isArray(tx.description)) {
return tx.description[0];
}
return tx.description;
};
$scope.$on("$ionicView.beforeEnter", function(event, data){
self.update();
$scope.$on("$ionicView.beforeEnter", function(event, data) {
$scope.cardId = data.stateParams.id;
if (!$scope.cardId) {
var msg = gettextCatalog.getString('Bad param');
$ionicHistory.nextViewOptions({
disableAnimate: true
});
$state.go('tabs.home');
popupService.showAlert(null, msg);
} else {
getFromCache(function() {
self.update();
});
}
});
});

View File

@ -0,0 +1,72 @@
'use strict';
angular.module('copayApp.controllers').controller('bitpayCardIntroController', function($scope, $log, $state, $ionicHistory, storageService, externalLinkService, bitpayCardService, gettextCatalog, popupService) {
var checkOtp = function(obj, cb) {
if (obj.otp) {
var msg = gettextCatalog.getString('Enter Two Factor for BitPay Cards');
popupService.showPrompt(null, msg, null, function(res) {
cb(res);
});
} else {
cb();
}
};
$scope.$on("$ionicView.beforeEnter", function(event, data) {
if (data.stateParams && data.stateParams.secret) {
var obj = {
secret: data.stateParams.secret,
email: data.stateParams.email,
otp: data.stateParams.otp
};
checkOtp(obj, function(otp) {
obj.otp = otp;
bitpayCardService.bitAuthPair(obj, function(err, data) {
if (err) {
popupService.showAlert(null, err);
return;
}
var title = gettextCatalog.getString('Add BitPay Cards?');
var msg = gettextCatalog.getString('Would you like to add this account ({{email}}) to your wallet?', {email: obj.email});
var ok = gettextCatalog.getString('Add cards');
var cancel = gettextCatalog.getString('Go back');
popupService.showConfirm(title, msg, ok, cancel, function(res) {
if (res) {
// Set flag for nextStep
storageService.setNextStep('BitpayCard', true, function(err) {});
// Save data
bitpayCardService.setBitpayDebitCards(data, function(err) {
if (err) return;
$ionicHistory.nextViewOptions({
disableAnimate: true
});
$state.go('tabs.home').then(function() {
if (data.cards[0]) {
$state.transitionTo('tabs.bitpayCard', {id: data.cards[0].id});
}
});
});
}
});
});
});
} else {
bitpayCardService.getCredentials(function(err, credentials) {
if (err) popupService.showAlert(null, err);
else $log.info('BitPay Debit Card Credentials: Ok.');
});
}
});
$scope.orderBitPayCard = function() {
var url = 'https://bitpay.com/visa/';
externalLinkService.open(url);
};
$scope.connectBitPayCard = function() {
var url = 'https://bitpay.com/visa/login';
externalLinkService.open(url);
};
});

View File

@ -16,12 +16,14 @@ angular.module('copayApp.controllers').controller('buyAmazonController',
$log.debug('Wallet changed: ' + w.name);
});
$scope.openExternalLink = function(url, target) {
externalLinkService.open(url, target);
$scope.openExternalLink = function(url, optIn, title, message, okText, cancelText) {
externalLinkService.open(url, optIn, title, message, okText, cancelText);
};
this.confirm = function() {
var message = gettextCatalog.getString('Amazon.com Gift Card purchase for ${{amount}} USD', {amount: $scope.formData.fiat});
var message = gettextCatalog.getString('Amazon.com Gift Card purchase for ${{amount}} USD', {
amount: $scope.formData.fiat
});
var ok = gettextCatalog.getString('Buy');
popupService.showConfirm(null, message, ok, null, function(res) {
if (res) self.createTx();
@ -209,8 +211,10 @@ angular.module('copayApp.controllers').controller('buyAmazonController',
});
};
$scope.$on("$ionicView.enter", function(event, data){
$scope.formData = { fiat: null };
$scope.$on("$ionicView.enter", function(event, data) {
$scope.formData = {
fiat: null
};
$scope.wallets = profileService.getWallets({
network: network,
onlyComplete: true

View File

@ -8,7 +8,7 @@ angular.module('copayApp.controllers').controller('confirmController', function(
$scope.$on("$ionicView.beforeEnter", function(event, data) {
$scope.isWallet = data.stateParams.isWallet;
$scope.isCard = data.stateParams.isCard;
$scope.cardId = data.stateParams.cardId;
$scope.toAmount = data.stateParams.toAmount;
$scope.toAddress = data.stateParams.toAddress;
$scope.toName = data.stateParams.toName;
@ -36,7 +36,7 @@ angular.module('copayApp.controllers').controller('confirmController', function(
$scope.data = {};
var config = configService.getSync().wallet;
$scope.feeLevel = config.settings ? config.settings.feeLevel : '';
$scope.feeLevel = config.settings && config.settings.feeLevel ? config.settings.feeLevel : 'normal';
$scope.toAmount = parseInt($scope.toAmount);
$scope.amountStr = txFormatService.formatAmountStr($scope.toAmount);
@ -274,7 +274,7 @@ angular.module('copayApp.controllers').controller('confirmController', function(
txp.message = description;
txp.payProUrl = paypro;
txp.excludeUnconfirmedUtxos = config.spendUnconfirmed ? false : true;
txp.feeLevel = config.settings.feeLevel || 'normal';
txp.feeLevel = config.settings && config.settings.feeLevel ? config.settings.feeLevel : 'normal';
txp.dryRun = dryRun;
walletService.createTx(wallet, txp, function(err, ctxp) {
@ -374,11 +374,22 @@ angular.module('copayApp.controllers').controller('confirmController', function(
};
$scope.onSuccessConfirm = function() {
var previousView = $ionicHistory.viewHistory().backView && $ionicHistory.viewHistory().backView.stateName;
var fromBitPayCard = previousView.match(/tabs.bitpayCard/) ? true : false;
$ionicHistory.nextViewOptions({
disableAnimate: true
});
$ionicHistory.removeBackView();
$scope.sendStatus = '';
$state.go('tabs.send');
if (fromBitPayCard) {
$timeout(function() {
$state.transitionTo('tabs.bitpayCard', {id: $stateParams.cardId});
}, 100);
} else {
$state.go('tabs.send');
}
};
function publishAndSign(wallet, txp, onSendStatusChange) {

View File

@ -15,6 +15,45 @@ angular.module('copayApp.controllers').controller('exportController',
});
};
function getPassword(cb) {
if ($scope.password) return cb(null, $scope.password);
walletService.prepare(wallet, function(err, password) {
if (err) return cb(err);
$scope.password = password;
return cb(null, password);
});
};
$scope.generateQrCode = function() {
if ($scope.formData.exportWalletInfo || !walletService.isEncrypted(wallet)) {
$scope.file.value = false;
}
getPassword(function(err, password) {
if (err) {
popupService.showAlert(gettextCatalog.getString('Error'), err);
return;
}
walletService.getEncodedWalletInfo(wallet, password, function(err, code) {
if (err) return cb(err);
if (!code)
$scope.formData.supported = false;
else {
$scope.formData.supported = true;
$scope.formData.exportWalletInfo = code;
}
$scope.file.value = false;
$timeout(function() {
$scope.$apply();
});
});
});
};
var init = function() {
$scope.formData = {};
$scope.isEncrypted = wallet.isPrivKeyEncrypted();
@ -24,24 +63,6 @@ angular.module('copayApp.controllers').controller('exportController',
$scope.showAdvanced = false;
$scope.wallet = wallet;
$scope.canSign = wallet.canSign();
walletService.getEncodedWalletInfo(wallet, function(err, code) {
if (err || !code) {
$log.warn(err);
return $ionicHistory.goBack();
}
if (!code)
$scope.formData.supported = false;
else {
$scope.formData.supported = true;
$scope.formData.exportWalletInfo = code;
}
$timeout(function() {
$scope.$apply();
}, 1);
});
};
/*
@ -67,23 +88,31 @@ angular.module('copayApp.controllers').controller('exportController',
};
$scope.downloadWalletBackup = function() {
$scope.getAddressbook(function(err, localAddressBook) {
getPassword(function(err, password) {
if (err) {
popupService.showAlert(gettextCatalog.getString('Error'), gettextCatalog.getString('Failed to export'));
popupService.showAlert(gettextCatalog.getString('Error'), err);
return;
}
var opts = {
noSign: $scope.formData.noSignEnabled,
addressBook: localAddressBook
};
backupService.walletDownload($scope.formData.password, opts, function(err) {
$scope.getAddressbook(function(err, localAddressBook) {
if (err) {
popupService.showAlert(gettextCatalog.getString('Error'), gettextCatalog.getString('Failed to export'));
return;
}
$ionicHistory.removeBackView();
$state.go('tabs.home');
var opts = {
noSign: $scope.formData.noSignEnabled,
addressBook: localAddressBook,
password: password
};
backupService.walletDownload($scope.formData.password, opts, function(err) {
if (err) {
popupService.showAlert(gettextCatalog.getString('Error'), gettextCatalog.getString('Failed to export'));
return;
}
$ionicHistory.removeBackView();
$state.go('tabs.home');
});
});
});
};
@ -104,21 +133,29 @@ angular.module('copayApp.controllers').controller('exportController',
};
$scope.getBackup = function(cb) {
$scope.getAddressbook(function(err, localAddressBook) {
getPassword(function(err, password) {
if (err) {
popupService.showAlert(gettextCatalog.getString('Error'), gettextCatalog.getString('Failed to export'));
return cb(null);
popupService.showAlert(gettextCatalog.getString('Error'), err);
return;
}
var opts = {
noSign: $scope.formData.noSignEnabled,
addressBook: localAddressBook
};
var ew = backupService.walletExport($scope.formData.password, opts);
if (!ew) {
popupService.showAlert(gettextCatalog.getString('Error'), gettextCatalog.getString('Failed to export'));
}
return cb(ew);
$scope.getAddressbook(function(err, localAddressBook) {
if (err) {
popupService.showAlert(gettextCatalog.getString('Error'), gettextCatalog.getString('Failed to export'));
return cb(null);
}
var opts = {
noSign: $scope.formData.noSignEnabled,
addressBook: localAddressBook,
password: password
};
var ew = backupService.walletExport($scope.formData.password, opts);
if (!ew) {
popupService.showAlert(gettextCatalog.getString('Error'), gettextCatalog.getString('Failed to export'));
}
return cb(ew);
});
});
};
@ -171,6 +208,11 @@ angular.module('copayApp.controllers').controller('exportController',
$scope.$on("$ionicView.beforeEnter", function(event, data) {
init();
$scope.file = {
value: true
};
$scope.formData.exportWalletInfo = null;
$scope.password = null;
});
});

View File

@ -5,8 +5,8 @@ angular.module('copayApp.controllers').controller('glideraController',
$scope.network = glideraService.getEnvironment();
$scope.openExternalLink = function(url, target) {
externalLinkService.open(url, target);
$scope.openExternalLink = function(url, optIn, title, message, okText, cancelText) {
externalLinkService.open(url, optIn, title, message, okText, cancelText);
};
var initGlidera = function(accessToken) {
@ -27,7 +27,9 @@ angular.module('copayApp.controllers').controller('glideraController',
}
$scope.token = glidera.token;
$scope.permissions = glidera.permissions;
$scope.update({fullUpdate: true});
$scope.update({
fullUpdate: true
});
});
};
@ -113,7 +115,7 @@ angular.module('copayApp.controllers').controller('glideraController',
});
};
$scope.$on("$ionicView.beforeEnter", function(event, data){
$scope.$on("$ionicView.beforeEnter", function(event, data) {
initGlidera();
});

View File

@ -1,6 +1,6 @@
'use strict';
angular.module('copayApp.controllers').controller('amazonCardDetailsController', function($scope, $log, $timeout, bwcError, amazonService, lodash, ongoingProcess, popupService, gettextCatalog) {
angular.module('copayApp.controllers').controller('amazonCardDetailsController', function($scope, $log, $timeout, bwcError, amazonService, lodash, ongoingProcess, popupService, gettextCatalog, externalLinkService) {
$scope.cancelGiftCard = function() {
ongoingProcess.set('Canceling gift card...', true);
@ -62,4 +62,8 @@ angular.module('copayApp.controllers').controller('amazonCardDetailsController',
$scope.amazonCardDetailsModal.hide();
};
$scope.openExternalLink = function(url, optIn, title, message, okText, cancelText) {
externalLinkService.open(url, optIn, title, message, okText, cancelText);
};
});

View File

@ -139,8 +139,8 @@ angular.module('copayApp.controllers').controller('txDetailsController', functio
});
};
$scope.openExternalLink = function(url, target) {
externalLinkService.open(url, target);
$scope.openExternalLink = function(url, optIn, title, message, okText, cancelText) {
externalLinkService.open(url, optIn, title, message, okText, cancelText);
};
$scope.getShortNetworkName = function() {

View File

@ -15,8 +15,8 @@ angular.module('copayApp.controllers').controller('termsController', function($s
});
};
$scope.openExternalLink = function(url, target) {
externalLinkService.open(url, target);
$scope.openExternalLink = function(url, optIn, title, message, okText, cancelText) {
externalLinkService.open(url, optIn, title, message, okText, cancelText);
};
});

View File

@ -8,7 +8,7 @@ angular.module('copayApp.controllers').controller('preferencesAbout',
$scope.commitHash = $window.commitHash;
$scope.name = $window.appConfig.gitHubRepoName;
$scope.openExternalLink = function(url, target) {
externalLinkService.open(url, target);
$scope.openExternalLink = function(url, optIn, title, message, okText, cancelText) {
externalLinkService.open(url, optIn, title, message, okText, cancelText);
};
});

View File

@ -1,17 +1,17 @@
'use strict';
angular.module('copayApp.controllers').controller('preferencesBitpayCardController',
function($scope, $state, $timeout, $ionicHistory, bitpayCardService, popupService) {
function($scope, $state, $timeout, $ionicHistory, bitpayCardService, popupService, gettextCatalog) {
$scope.logout = function() {
var title = 'Are you sure you would like to log out of your Bitpay Card account?';
popupService.showConfirm(title, null, null, null, function(res) {
if (res) logout();
$scope.remove = function() {
var msg = gettextCatalog.getString('Are you sure you would like to remove your BitPay Card account from this device?');
popupService.showConfirm(null, msg, null, null, function(res) {
if (res) remove();
});
};
var logout = function() {
bitpayCardService.logout(function() {
var remove = function() {
bitpayCardService.remove(function() {
$ionicHistory.removeBackView();
$timeout(function() {
$state.go('tabs.home');
@ -19,4 +19,11 @@ angular.module('copayApp.controllers').controller('preferencesBitpayCardControll
});
};
$scope.$on("$ionicView.beforeEnter", function(event, data) {
bitpayCardService.getBitpayDebitCards(function(err, data) {
if (err) return;
$scope.bitpayCards = data.cards;
});
});
});

View File

@ -21,7 +21,8 @@ angular.module('copayApp.controllers').controller('preferencesDeleteWalletContro
if (err) {
popupService.showAlert(gettextCatalog.getString('Error'), err.message || err);
} else {
$ionicHistory.removeBackView();
$ionicHistory.clearHistory();
$ionicHistory.clearCache();
$state.go('tabs.home');
}
});

View File

@ -2,23 +2,25 @@
angular.module('copayApp.controllers').controller('preferencesEmailController', function($scope, $ionicHistory, $stateParams, gettextCatalog, profileService, walletService, configService) {
var wallet = profileService.getWallet($stateParams.walletId);
var walletId = wallet.credentials.walletId;
$scope.wallet = profileService.getWallet($stateParams.walletId);
var walletId = $scope.wallet.credentials.walletId;
var config = configService.getSync();
config.emailFor = config.emailFor || {};
$scope.emailForExist = config.emailFor && config.emailFor[walletId];
$scope.email = {
value: config.emailFor && config.emailFor[walletId]
};
$scope.save = function() {
$scope.save = function(val) {
var opts = {
emailFor: {}
};
opts.emailFor[walletId] = $scope.email.value;
opts.emailFor[walletId] = val;
walletService.updateRemotePreferences(wallet, {
email: $scope.email.value,
walletService.updateRemotePreferences($scope.wallet, {
email: val,
}, function(err) {
if (err) $log.warn(err);
configService.set(opts, function(err) {

View File

@ -6,12 +6,12 @@ angular.module('copayApp.controllers').controller('tabHomeController',
var listeners = [];
var notifications = [];
$scope.externalServices = {};
$scope.bitpayCardEnabled = true; // TODO
$scope.openTxpModal = txpModalService.open;
$scope.version = $window.version;
$scope.name = $window.appConfig.nameCase;
$scope.homeTip = $stateParams.fromOnboarding;
$scope.isCordova = platformInfo.isCordova;
$scope.isAndroid = platformInfo.isAndroid;
$scope.$on("$ionicView.afterEnter", function() {
startupService.ready();
@ -19,7 +19,7 @@ angular.module('copayApp.controllers').controller('tabHomeController',
if (!$scope.homeTip) {
storageService.getHomeTipAccepted(function(error, value) {
$scope.homeTip = (value == 'false') ? false : true;
$scope.homeTip = (value == 'accepted') ? false : true;
});
}
@ -175,7 +175,7 @@ angular.module('copayApp.controllers').controller('tabHomeController',
};
$scope.hideHomeTip = function() {
storageService.setHomeTipAccepted(false, function(error, value) {
storageService.setHomeTipAccepted('accepted', function() {
$scope.homeTip = false;
$timeout(function() {
$scope.$apply();
@ -203,19 +203,32 @@ angular.module('copayApp.controllers').controller('tabHomeController',
};
var bitpayCardCache = function() {
bitpayCardService.getCacheData(function(err, data) {
if (err ||  lodash.isEmpty(data)) return;
$scope.bitpayCard = data;
bitpayCardService.getBitpayDebitCards(function(err, data) {
if (err) return;
if (lodash.isEmpty(data)) {
$scope.bitpayCards = null;
return;
}
$scope.bitpayCards = data.cards;
});
bitpayCardService.getBitpayDebitCardsHistory(null, function(err, data) {
if (err) return;
if (lodash.isEmpty(data)) {
$scope.cardsHistory = null;
return;
}
$scope.cardsHistory = data;
});
};
$scope.onRefresh = function() {
$scope.$broadcast('scroll.refreshComplete');
$timeout(function() {
$scope.$broadcast('scroll.refreshComplete');
}, 300);
updateAllWallets();
};
$scope.$on("$ionicView.enter", function(event, data) {
$scope.bitpayCard = null;
nextStep();
updateAllWallets();

View File

@ -23,7 +23,10 @@ angular.module('copayApp.controllers').controller('tabReceiveController', functi
if (err) popupService.showAlert(gettextCatalog.getString('Error'), err);
$scope.addr = addr;
if ($scope.wallet.showBackupNeededModal) $scope.openBackupNeededModal();
$scope.$apply();
$timeout(function(){
$scope.$apply();
},10);
});
};

View File

@ -1,6 +1,6 @@
'use strict';
angular.module('copayApp.controllers').controller('tabSendController', function($scope, $log, $timeout, $ionicScrollDelegate, addressbookService, profileService, lodash, $state, walletService, incomingData, popupService) {
angular.module('copayApp.controllers').controller('tabSendController', function($scope, $log, $timeout, $ionicScrollDelegate, addressbookService, profileService, lodash, $state, walletService, incomingData, popupService, $rootScope) {
var originalList;
var CONTACTS_SHOW_LIMIT;
@ -59,6 +59,10 @@ angular.module('copayApp.controllers').controller('tabSendController', function(
});
};
$scope.openScanner = function() {
$state.go('tabs.scan');
}
$scope.showMore = function() {
currentContactsPage++;
updateList();
@ -98,23 +102,28 @@ angular.module('copayApp.controllers').controller('tabSendController', function(
isWallet: item.isWallet,
toAddress: addr,
toName: item.name,
toEmail: item.email
toEmail: item.email,
toColor: item.color
})
});
});
};
var updateHasFunds = function() {
$scope.hasFunds = null;
if ($rootScope.everHasFunds) {
$scope.hasFunds = true;
return;
}
$scope.hasFunds = false;
var wallets = profileService.getWallets({
onlyComplete: true,
});
if (!wallets || !wallets.length) {
$scope.hasFunds = false;
$timeout(function() {
return $timeout(function() {
$scope.$apply();
});
}
@ -128,11 +137,12 @@ angular.module('copayApp.controllers').controller('tabSendController', function(
return;
}
if (status.availableBalanceSat) {
if (status.availableBalanceSat > 0) {
$scope.hasFunds = true;
$rootScope.everHasFunds = true;
}
if (index == wallets.length) {
$scope.hasFunds = $scope.hasFunds || false;
$timeout(function() {
$scope.$apply();
});

View File

@ -24,8 +24,8 @@ angular.module('copayApp.controllers').controller('tabSettingsController', funct
$scope.wallets = profileService.getWallets();
};
$scope.openExternalLink = function(url, target) {
externalLinkService.open(url, target);
$scope.openExternalLink = function(url, optIn, title, message, okText, cancelText) {
externalLinkService.open(url, optIn, title, message, okText, cancelText);
};
$scope.$on("$ionicView.beforeEnter", function(event, data) {

View File

@ -6,7 +6,7 @@ angular.module('copayApp.controllers').controller('tabsController', function($ro
if (!incomingData.redir(data)) {
popupService.showAlert(null, gettextCatalog.getString('Invalid data'));
}
}
};
$scope.setScanFn = function(scanFn) {
$scope.scan = function() {
@ -22,32 +22,4 @@ angular.module('copayApp.controllers').controller('tabsController', function($ro
}, 1);
};
var hideTabsViews = [
'tabs.send.amount',
'tabs.send.confirm',
'tabs.send.addressbook',
'tabs.addressbook',
'tabs.addressbook.add',
'tabs.addressbook.view',
'tabs.preferences.backupWarning',
'tabs.preferences.backup',
'tabs.receive.backupWarning',
'tabs.receive.backup',
'tabs.bitpayCard.amount',
'tabs.bitpayCard.confirm',
];
$rootScope.$on('$ionicView.beforeEnter', function() {
$rootScope.hideTabs = false;
var currentState = $state.current.name;
lodash.each(hideTabsViews, function(view) {
if (currentState === view) {
$rootScope.hideTabs = true;
}
});
});
});

View File

@ -9,6 +9,7 @@ angular.module('copayApp.controllers').controller('walletDetailsController', fun
$scope.completeTxHistory = [];
$scope.openTxpModal = txpModalService.open;
$scope.isCordova = platformInfo.isCordova;
$scope.isAndroid = platformInfo.isAndroid;
$scope.openExternalLink = function(url, target) {
externalLinkService.open(url, target);
@ -161,7 +162,9 @@ angular.module('copayApp.controllers').controller('walletDetailsController', fun
};
$scope.onRefresh = function() {
$scope.$broadcast('scroll.refreshComplete');
$timeout(function() {
$scope.$broadcast('scroll.refreshComplete');
}, 300);
$scope.updateAll(true);
};

View File

@ -0,0 +1,13 @@
'use strict';
angular.module('copayApp.directives')
.directive('hideTabs', function($rootScope) {
return {
restrict: 'A',
link: function($scope, $el) {
$rootScope.hideTabs = 'tabs-item-hide';
$scope.$on('$destroy', function() {
$rootScope.hideTabs = '';
});
}
};
});

View File

@ -251,7 +251,7 @@ angular.module('copayApp').config(function(historicLogProvider, $provide, $logPr
*/
.state('tabs.send.amount', {
url: '/amount/:isWallet/:toAddress/:toName/:toEmail',
url: '/amount/:isWallet/:toAddress/:toName/:toEmail/:toColor',
views: {
'tab-send@tabs': {
controller: 'amountController',
@ -571,7 +571,7 @@ angular.module('copayApp').config(function(historicLogProvider, $provide, $logPr
}
})
.state('tabs.addressbook.view', {
url: '/view/:address',
url: '/view/:address/:email/:name',
views: {
'tab-settings@tabs': {
templateUrl: 'views/addressbook.view.html',
@ -846,8 +846,17 @@ angular.module('copayApp').config(function(historicLogProvider, $provide, $logPr
*
*/
.state('tabs.bitpayCard', {
url: '/bitpay-card',
.state('tabs.bitpayCardIntro', {
url: '/bitpay-card-intro/:secret/:email/:otp',
views: {
'tab-home@tabs': {
controller: 'bitpayCardIntroController',
templateUrl: 'views/bitpayCardIntro.html'
}
}
})
.state('tabs.bitpayCard', {
url: '/bitpay-card/:id',
views: {
'tab-home@tabs': {
controller: 'bitpayCardController',
@ -857,7 +866,7 @@ angular.module('copayApp').config(function(historicLogProvider, $provide, $logPr
}
})
.state('tabs.bitpayCard.amount', {
url: '/amount/:isCard/:toName',
url: '/amount/:cardId/:toName',
views: {
'tab-home@tabs': {
controller: 'amountController',
@ -866,7 +875,7 @@ angular.module('copayApp').config(function(historicLogProvider, $provide, $logPr
}
})
.state('tabs.bitpayCard.confirm', {
url: '/confirm/:isCard/:toAddress/:toName/:toAmount/:toEmail/:description/:paypro',
url: '/confirm/:cardId/:toAddress/:toName/:toAmount/:toEmail/:description/:paypro',
views: {
'tab-home@tabs': {
controller: 'confirmController',
@ -878,6 +887,7 @@ angular.module('copayApp').config(function(historicLogProvider, $provide, $logPr
url: '/preferences',
views: {
'tab-home@tabs': {
controller: 'preferencesBitpayCardController',
templateUrl: 'views/preferencesBitpayCard.html'
}
}
@ -970,8 +980,7 @@ angular.module('copayApp').config(function(historicLogProvider, $provide, $logPr
if (lodash.isEmpty(profileService.getWallets())) {
$log.debug('No wallets and no disclaimer... redirecting');
$state.go('onboarding.welcome');
}
else {
} else {
$log.debug('Display disclaimer... redirecting');
$state.go('onboarding.disclaimer', {
resume: true
@ -980,8 +989,7 @@ angular.module('copayApp').config(function(historicLogProvider, $provide, $logPr
} else {
throw new Error(err); // TODO
}
}
else {
} else {
profileService.storeProfileIfDirty();
$log.debug('Profile loaded ... Starting UX.');
scannerService.gentleInitialize();

View File

@ -1,195 +1,239 @@
'use strict';
angular.module('copayApp.services').factory('bitpayCardService', function($http, $log, lodash, storageService) {
angular.module('copayApp.services').factory('bitpayCardService', function($http, $log, lodash, storageService, bitauthService, platformInfo, moment) {
var root = {};
var credentials = {};
var bpSession = {};
var BITPAY_CARD_NETWORK = 'livenet';
var BITPAY_CARD_API_URL = BITPAY_CARD_NETWORK == 'livenet' ? 'https://bitpay.com' : 'https://test.bitpay.com';
var _setCredentials = function() {
/*
* Development: 'testnet'
* Production: 'livenet'
*/
credentials.NETWORK = 'livenet';
if (credentials.NETWORK == 'testnet') {
credentials.BITPAY_API_URL = 'https://test.bitpay.com';
}
else {
credentials.BITPAY_API_URL = 'https://bitpay.com';
};
var _getCredentials = function(cb) {
var pubkey, sin, isNew;
storageService.getBitpayCardCredentials(BITPAY_CARD_NETWORK, function(err, data) {
if (err) return cb(err);
if (lodash.isString(data)) {
data = JSON.parse(data);
}
var credentials = data || {};
if (lodash.isEmpty(credentials) || (credentials && !credentials.priv)) {
isNew = true;
credentials = bitauthService.generateSin();
}
try {
pubkey = bitauthService.getPublicKeyFromPrivateKey(credentials.priv);
sin = bitauthService.getSinFromPublicKey(pubkey);
if (isNew)
storageService.setBitpayCardCredentials(BITPAY_CARD_NETWORK, JSON.stringify(credentials), function(err) {});
}
catch (e) {
$log.error(e);
return cb(e);
};
return cb(null, credentials);
});
};
var _setError = function(msg, e) {
$log.error(msg);
return e;
var error = e.data ? e.data.error : msg;
return error;
};
var _getUser = function(cb) {
_setCredentials();
storageService.getBitpayCard(credentials.NETWORK, function(err, user) {
if (err) return cb(err);
if (lodash.isString(user)) {
user = JSON.parse(user);
}
return cb(null, user);
});
};
var _setUser = function(user, cb) {
_setCredentials();
user = JSON.stringify(user);
storageService.setBitpayCard(credentials.NETWORK, user, function(err) {
return cb(err);
});
// Show pending task from the UI
storageService.setNextStep('BitpayCard', true, function(err) {});
};
var _getSession = function(cb) {
_setCredentials();
$http({
var _get = function(endpoint) {
return {
method: 'GET',
url: credentials.BITPAY_API_URL + '/visa-api/session',
url: BITPAY_CARD_API_URL + endpoint,
headers: {
'content-type': 'application/json'
}
}).then(function(data) {
$log.info('BitPay Get Session: SUCCESS');
bpSession = data.data.data;
return cb(null, bpSession);
};
};
var _post = function(endpoint, json, credentials) {
var dataToSign = BITPAY_CARD_API_URL + endpoint + JSON.stringify(json);
var signedData = bitauthService.sign(dataToSign, credentials.priv);
return {
method: 'POST',
url: BITPAY_CARD_API_URL + endpoint,
headers: {
'content-type': 'application/json',
'x-identity': credentials.pub,
'x-signature': signedData
},
data: json
};
};
var _postAuth = function(endpoint, json, credentials) {
json['params'].signature = bitauthService.sign(JSON.stringify(json.params), credentials.priv);
json['params'].pubkey = credentials.pub;
json['params'] = JSON.stringify(json.params);
var ret = {
method: 'POST',
url: BITPAY_CARD_API_URL + endpoint,
headers: {
'content-type': 'application/json'
},
data: json
};
$log.debug('post auth:' + JSON.stringify(ret));
return ret;
};
var _afterBitAuthSuccess = function(token, obj, credentials, cb) {
var json = {
method: 'getDebitCards'
};
// Get Debit Cards
$http(_post('/api/v2/' + token, json, credentials)).then(function(data) {
if (data && data.data.error) return cb(data.data.error);
$log.info('BitPay Get Debit Cards: SUCCESS');
return cb(data.data.error, {token: token, cards: data.data.data, email: obj.email});
}, function(data) {
return cb(_setError('BitPay Card Error: Get Session', data));
return cb(_setError('BitPay Card Error: Get Debit Cards', data));
});
};
var _getBitPay = function(endpoint) {
_setCredentials();
return {
method: 'GET',
url: credentials.BITPAY_API_URL + endpoint,
headers: {
'content-type': 'application/json',
'x-csrf-token': bpSession.csrfToken
var _processTransactions = function(invoices, history) {
invoices = invoices || [];
for (var i = 0; i < invoices.length; i++) {
var matched = false;
for (var j = 0; j < history.length; j++) {
if (history[j].description[0].indexOf(invoices[i].id) > -1) {
matched = true;
}
}
};
};
var isInvoiceLessThanOneDayOld = moment() < moment(new Date(invoices[i].invoiceTime)).add(1, 'day');
if (!matched && isInvoiceLessThanOneDayOld) {
var isInvoiceUnderpaid = invoices[i].exceptionStatus === 'paidPartial';
var _postBitPay = function(endpoint, data) {
_setCredentials();
return {
method: 'POST',
url: credentials.BITPAY_API_URL + endpoint,
headers: {
'Content-Type': 'application/json',
'x-csrf-token': bpSession.csrfToken
},
data: data
};
if(['paid', 'confirmed', 'complete'].indexOf(invoices[i].status) >= 0
|| (invoices[i].status === 'invalid' || isInvoiceUnderpaid)) {
history.unshift({
timestamp: new Date(invoices[i].invoiceTime),
description: invoices[i].itemDesc,
amount: invoices[i].price,
type: '00611 = Client Funded Deposit',
pending: true,
status: invoices[i].status
});
}
}
}
return history;
};
root.getEnvironment = function() {
_setCredentials();
return credentials.NETWORK;
return BITPAY_CARD_NETWORK;
};
root.topUp = function(data, cb) {
var dataSrc = {
amount: data.amount,
currency: data.currency
};
$http(_postBitPay('/visa-api/topUp', dataSrc)).then(function(data) {
$log.info('BitPay TopUp: SUCCESS');
return cb(null, data.data.data.invoice);
}, function(data) {
return cb(_setError('BitPay Card Error: TopUp', data));
root.getCredentials = function(cb) {
_getCredentials(function(err, credentials) {
return cb(err, credentials);
});
};
root.transactionHistory = function(dateRange, cb) {
var params;
if (!dateRange.startDate) {
params = '';
} else {
params = '/?startDate=' + dateRange.startDate + '&endDate=' + dateRange.endDate;
root.bitAuthPair = function(obj, cb) {
var deviceName = 'Unknow device';
if (platformInfo.isNW) {
deviceName = require('os').platform();
} else if (platformInfo.isCordova) {
deviceName = device.model;
}
$http(_getBitPay('/visa-api/transactionHistory' + params)).then(function(data) {
$log.info('BitPay Get Transaction History: SUCCESS');
return cb(null, data.data.data);
}, function(data) {
return cb(_setError('BitPay Card Error: Get Transaction History', data));
var json = {
method: 'createToken',
params: {
secret: obj.secret,
version: 2,
deviceName: deviceName,
code: obj.otp
}
};
_getCredentials(function(err, credentials) {
if (err) return cb(err);
$http(_postAuth('/api/v2/', json, credentials)).then(function(data) {
if (data && data.data.error) return cb(data.data.error);
$log.info('BitPay Card BitAuth Create Token: SUCCESS');
_afterBitAuthSuccess(data.data.data, obj, credentials, cb);
}, function(data) {
return cb(_setError('BitPay Card Error Create Token: BitAuth', data));
});
});
};
root.invoiceHistory = function(cb) {
$http(_getBitPay('/visa-api/invoiceHistory')).then(function(data) {
$log.info('BitPay Get Invoice History: SUCCESS');
return cb(null, data.data.data);
}, function(data) {
return cb(_setError('BitPay Card Error: Get Invoice History', data));
root.getHistory = function(cardId, params, cb) {
var invoices, transactions;
params = params || {};
var json = {
method: 'getInvoiceHistory',
params: JSON.stringify(params)
};
_getCredentials(function(err, credentials) {
if (err) return cb(err);
root.getBitpayDebitCards(function(err, data) {
if (err) return cb(err);
var card = lodash.find(data.cards, {id : cardId});
if (!card) return cb(_setError('Not card found'));
// Get invoices
$http(_post('/api/v2/' + card.token, json, credentials)).then(function(data) {
$log.info('BitPay Get Invoices: SUCCESS');
invoices = data.data.data || [];
if (lodash.isEmpty(invoices)) $log.info('No invoices');
json = {
method: 'getTransactionHistory',
params: JSON.stringify(params)
};
// Get transactions list
$http(_post('/api/v2/' + card.token, json, credentials)).then(function(data) {
$log.info('BitPay Get Transactions: SUCCESS');
transactions = data.data.data || {};
transactions['txs'] = _processTransactions(invoices, transactions.transactionList);
return cb(data.data.error, transactions);
}, function(data) {
return cb(_setError('BitPay Card Error: Get Transactions', data));
});
}, function(data) {
return cb(_setError('BitPay Card Error: Get Invoices', data));
});
});
});
};
root.topUp = function(cardId, params, cb) {
params = params || {};
var json = {
method: 'generateTopUpInvoice',
params: JSON.stringify(params)
};
_getCredentials(function(err, credentials) {
if (err) return cb(err);
root.getBitpayDebitCards(function(err, data) {
if (err) return cb(err);
var card = lodash.find(data.cards, {id : cardId});
if (!card) return cb(_setError('Not card found'));
$http(_post('/api/v2/' + card.token, json, credentials)).then(function(data) {
$log.info('BitPay TopUp: SUCCESS');
return cb(data.data.error, data.data.data.invoice);
}, function(data) {
return cb(_setError('BitPay Card Error: TopUp', data));
});
});
});
};
root.getInvoice = function(id, cb) {
$http(_getBitPay('/invoices/' + id)).then(function(data) {
$http(_get('/invoices/' + id)).then(function(data) {
$log.info('BitPay Get Invoice: SUCCESS');
return cb(null, data.data.data);
return cb(data.data.error, data.data.data);
}, function(data) {
return cb(_setError('BitPay Card Error: Get Invoice', data));
});
};
root.authenticate = function(userData, cb) {
_setUser(userData, function(err) {
$http(_postBitPay('/visa-api/authenticate', userData)).then(function(data) {
$log.info('BitPay Authenticate: SUCCESS');
_getSession(function(err, session) {
if (err) return cb(err);
return cb(null, session);
});
}, function(data) {
if (data && data.data && data.data.error.twoFactorPending) {
$log.error('BitPay Card needs 2FA Authentication');
_getSession(function(err, session) {
if (err) return cb(err);
return cb(null, session);
});
} else {
return cb(data);
}
});
});
};
root.authenticate2FA = function(userData, cb) {
$http(_postBitPay('/visa-api/verify-two-factor', userData)).then(function(data) {
$log.info('BitPay 2FA: SUCCESS');
return cb(null, data);
}, function(data) {
return cb(_setError('BitPay Card Error: 2FA', data));
});
};
root.isAuthenticated = function(cb) {
_getSession(function(err, session) {
if (err) return cb(err);
if (!session.isAuthenticated) {
_getUser(function(err, user) {
if (err) return cb(err);
if (lodash.isEmpty(user)) return cb(null, session);
root.authenticate(user, function(err, session) {
if (err) return cb(err);
return cb(null, session);
});
});
} else {
return cb(null, session);
}
});
};
root.getCacheData = function(cb) {
_setCredentials();
storageService.getBitpayCardCache(credentials.NETWORK, function(err, data) {
root.getBitpayDebitCards = function(cb) {
storageService.getBitpayDebitCards(BITPAY_CARD_NETWORK, function(err, data) {
if (err) return cb(err);
if (lodash.isString(data)) {
data = JSON.parse(data);
@ -199,32 +243,54 @@ angular.module('copayApp.services').factory('bitpayCardService', function($http,
});
};
root.setCacheData = function(data, cb) {
_setCredentials();
root.setBitpayDebitCards = function(data, cb) {
data = JSON.stringify(data);
storageService.setBitpayCardCache(credentials.NETWORK, data, function(err) {
storageService.setBitpayDebitCards(BITPAY_CARD_NETWORK, data, function(err) {
if (err) return cb(err);
return cb();
});
};
root.removeCacheData = function(cb) {
_setCredentials();
storageService.removeBitpayCardCache(credentials.NETWORK, function(err) {
root.getBitpayDebitCardsHistory = function(cardId, cb) {
storageService.getBitpayDebitCardsHistory(BITPAY_CARD_NETWORK, function(err, data) {
if (err) return cb(err);
return cb();
if (lodash.isString(data)) {
data = JSON.parse(data);
}
data = data || {};
if (cardId) data = data[cardId];
return cb(null, data);
});
};
root.logout = function(cb) {
_setCredentials();
root.removeCacheData(function() {});
storageService.removeBitpayCard(credentials.NETWORK, function(err) {
$http(_getBitPay('/visa-api/logout')).then(function(data) {
$log.info('BitPay Logout: SUCCESS');
return cb(data);
}, function(data) {
return cb(_setError('BitPay Card Error: Logout ', data));
root.setBitpayDebitCardsHistory = function(cardId, data, opts, cb) {
storageService.getBitpayDebitCardsHistory(BITPAY_CARD_NETWORK, function(err, oldData) {
if (lodash.isString(oldData)) {
oldData = JSON.parse(oldData);
}
if (lodash.isString(data)) {
data = JSON.parse(data);
}
var inv = oldData || {};
inv[cardId] = data;
if (opts && opts.remove) {
delete(inv[cardId]);
}
inv = JSON.stringify(inv);
storageService.setBitpayDebitCardsHistory(BITPAY_CARD_NETWORK, inv, function(err) {
return cb(err);
});
});
};
root.remove = function(cb) {
storageService.removeBitpayCardCredentials(BITPAY_CARD_NETWORK, function(err) {
storageService.removeBitpayDebitCards(BITPAY_CARD_NETWORK, function(err) {
storageService.removeBitpayDebitCardsHistory(BITPAY_CARD_NETWORK, function(err) {
$log.info('BitPay Debit Cards Removed: SUCCESS');
return cb();
});
});
});
};

View File

@ -1,8 +1,8 @@
'use strict';
angular.module('copayApp.services').service('externalLinkService', function($window, $timeout, $log, platformInfo, nodeWebkitService) {
angular.module('copayApp.services').service('externalLinkService', function(platformInfo, nodeWebkitService, popupService, gettextCatalog, $window, $log, $timeout) {
this.open = function(url, target) {
this.open = function(url, optIn, title, message, okText, cancelText) {
var old = $window.handleOpenURL;
$window.handleOpenURL = function(url) {
@ -17,8 +17,16 @@ angular.module('copayApp.services').service('externalLinkService', function($win
if (platformInfo.isNW) {
nodeWebkitService.openExternalLink(url);
} else {
target = target || '_blank';
var ref = window.open(url, target, 'location=no');
if (optIn) {
var message = gettextCatalog.getString(message),
title = gettextCatalog.getString(title),
okText = gettextCatalog.getString(okText),
cancelText = gettextCatalog.getString(cancelText),
openBrowser = function(res) {
if (res) window.open(url, '_system');
};
popupService.showConfirm(title, message, okText, cancelText, openBrowser);
} else window.open(url, '_system');
}
};

View File

@ -1,6 +1,6 @@
'use strict';
angular.module('copayApp.services').factory('incomingData', function($log, $ionicModal, $state, $window, $timeout, bitcore) {
angular.module('copayApp.services').factory('incomingData', function($log, $state, $window, bitcore, lodash) {
var root = {};
@ -23,17 +23,25 @@ angular.module('copayApp.services').factory('incomingData', function($log, $ioni
return newUri;
};
function getParameterByName(name, url) {
if (!url) return;
name = name.replace(/[\[\]]/g, "\\$&");
var regex = new RegExp("[?&]" + name + "(=([^&#]*)|&|#|$)"),
results = regex.exec(url);
if (!results) return null;
if (!results[2]) return '';
return decodeURIComponent(results[2].replace(/\+/g, " "));
}
// data extensions for Payment Protocol with non-backwards-compatible request
if ((/^bitcoin:\?r=[\w+]/).exec(data)) {
data = decodeURIComponent(data.replace('bitcoin:?r=', ''));
$state.go('tabs.send');
$timeout(function() {
$state.go('tabs.send').then(function() {
$state.transitionTo('tabs.send.confirm', {paypro: data});
}, 100);
});
return true;
}
data = sanitizeUri(data);
// BIP21
@ -45,8 +53,7 @@ angular.module('copayApp.services').factory('incomingData', function($log, $ioni
var amount = parsed.amount ? parsed.amount : '';
$state.go('tabs.send');
$timeout(function() {
$state.go('tabs.send').then(function() {
if (parsed.r) {
$state.transitionTo('tabs.send.confirm', {paypro: parsed.r});
} else {
@ -56,29 +63,26 @@ angular.module('copayApp.services').factory('incomingData', function($log, $ioni
$state.transitionTo('tabs.send.amount', {toAddress: addr});
}
}
}, 100);
});
return true;
// Plain URL
} else if (/^https?:\/\//.test(data)) {
$state.go('tabs.send');
$timeout(function() {
$state.go('tabs.send').then(function() {
$state.transitionTo('tabs.send.confirm', {paypro: data});
}, 100);
});
return true;
// Plain Address
} else if (bitcore.Address.isValid(data, 'livenet')) {
$state.go('tabs.send');
$timeout(function() {
$state.go('tabs.send').then(function() {
$state.transitionTo('tabs.send.amount', {toAddress: data});
}, 100);
});
return true;
} else if (bitcore.Address.isValid(data, 'testnet')) {
$state.go('tabs.send');
$timeout(function() {
$state.go('tabs.send').then(function() {
$state.transitionTo('tabs.send.amount', {toAddress: data});
}, 100);
});
return true;
// Protocol
@ -87,20 +91,32 @@ angular.module('copayApp.services').factory('incomingData', function($log, $ioni
} else if (data && data.indexOf($window.appConfig.name + '://coinbase')==0) {
return $state.go('uricoinbase', {url: data});
// BitPayCard Authentication
} else if (data && data.indexOf($window.appConfig.name + '://')==0) {
var secret = getParameterByName('secret', data);
var email = getParameterByName('email', data);
var otp = getParameterByName('otp', data);
$state.go('tabs.home').then(function() {
$state.transitionTo('tabs.bitpayCardIntro', {
secret: secret,
email: email,
otp: otp
});
});
return true;
// Join
} else if (data && data.match(/^copay:[0-9A-HJ-NP-Za-km-z]{70,80}$/)) {
$state.go('tabs.home');
$timeout(function() {
$state.go('tabs.home').then(function() {
$state.transitionTo('tabs.add.join', {url: data});
}, 100);
});
return true;
// Old join
} else if (data && data.match(/^[0-9A-HJ-NP-Za-km-z]{70,80}$/)) {
$state.go('tabs.home');
$timeout(function() {
$state.go('tabs.home').then(function() {
$state.transitionTo('tabs.add.join', {url: data});
}, 100);
});
return true;
}

View File

@ -41,7 +41,7 @@ angular.module('copayApp.services').factory('platformInfo', function($window) {
ret.hasClick = false;
if($window.sessionStorage.getItem('hasClick')) {
if ($window.sessionStorage.getItem('hasClick')) {
ret.hasClick = true;
}

View File

@ -134,8 +134,7 @@ angular.module('copayApp.services')
if (n.type == "NewBlock" && n.data.network == "testnet") {
throttledBwsEvent(n, wallet);
}
else newBwsEvent(n, wallet);
} else newBwsEvent(n, wallet);
});
wallet.on('walletCompleted', function() {
@ -600,6 +599,7 @@ angular.module('copayApp.services')
var walletClient = bwcService.getClient(null, opts);
$log.debug('Importing Wallet:', opts);
try {
walletClient.import(str, {
compressed: opts.compressed,
@ -611,6 +611,12 @@ angular.module('copayApp.services')
str = JSON.parse(str);
if (str.xPrivKey && str.xPrivKeyEncrypted) {
$log.warn('Found both encrypted and decrypted key. Deleting the encrypted version');
delete str.xPrivKeyEncrypted;
delete str.mnemonicEncrypted;
}
var addressBook = str.addressBook || {};
addAndBindWalletClient(walletClient, {

View File

@ -325,28 +325,40 @@ angular.module('copayApp.services')
storage.remove('coinbaseTxs-' + network, cb);
};
root.setBitpayCard = function(network, data, cb) {
storage.set('bitpayCard-' + network, data, cb);
root.setBitpayDebitCardsHistory = function(network, data, cb) {
storage.set('bitpayDebitCardsHistory-' + network, data, cb);
};
root.getBitpayCard = function(network, cb) {
storage.get('bitpayCard-' + network, cb);
root.getBitpayDebitCardsHistory = function(network, cb) {
storage.get('bitpayDebitCardsHistory-' + network, cb);
};
root.removeBitpayCard = function(network, cb) {
storage.remove('bitpayCard-' + network, cb);
root.removeBitpayDebitCardsHistory = function(network, cb) {
storage.remove('bitpayDebitCardsHistory-' + network, cb);
};
root.setBitpayCardCache = function(network, data, cb) {
storage.set('bitpayCardCache-' + network, data, cb);
root.setBitpayDebitCards = function(network, data, cb) {
storage.set('bitpayDebitCards-' + network, data, cb);
};
root.getBitpayCardCache = function(network, cb) {
storage.get('bitpayCardCache-' + network, cb);
root.getBitpayDebitCards = function(network, cb) {
storage.get('bitpayDebitCards-' + network, cb);
};
root.removeBitpayCardCache = function(network, cb) {
storage.remove('bitpayCardCache-' + network, cb);
root.removeBitpayDebitCards = function(network, cb) {
storage.remove('bitpayDebitCards-' + network, cb);
};
root.setBitpayCardCredentials = function(network, data, cb) {
storage.set('bitpayCardCredentials-' + network, data, cb);
};
root.getBitpayCardCredentials = function(network, cb) {
storage.get('bitpayCardCredentials-' + network, cb);
};
root.removeBitpayCardCredentials = function(network, cb) {
storage.remove('bitpayCardCredentials-' + network, cb);
};
root.removeAllWalletData = function(walletId, cb) {

View File

@ -869,9 +869,8 @@ angular.module('copayApp.services').factory('walletService', function($log, $tim
if (!root.isEncrypted(wallet)) return cb();
askPassword(wallet.name, gettext('Enter Spending Password'), function(password) {
if (!password) return cb('no password');
if (!wallet.checkPassword(password)) return cb('wrong password');
if (!password) return cb('No password');
if (!wallet.checkPassword(password)) return cb('Wrong password');
return cb(null, password);
});
@ -990,8 +989,7 @@ angular.module('copayApp.services').factory('walletService', function($log, $tim
});
};
root.getEncodedWalletInfo = function(wallet, cb) {
root.getEncodedWalletInfo = function(wallet, password, cb) {
var derivationPath = wallet.credentials.getBaseAddressDerivationPath();
var encodingType = {
mnemonic: 1,
@ -1002,25 +1000,23 @@ angular.module('copayApp.services').factory('walletService', function($log, $tim
// not supported yet
if (wallet.credentials.derivationStrategy != 'BIP44' || !wallet.canSign())
return null;
return cb(gettextCatalog.getString('Exporting via QR not supported for this wallet'));
root.getKeys(wallet, function(err, keys) {
if (err || !keys) return cb(err);
var keys = root.getKeysWithPassword(wallet, password);
if (keys.mnemonic) {
info = {
type: encodingType.mnemonic,
data: keys.mnemonic,
}
} else {
info = {
type: encodingType.xpriv,
data: keys.xPrivKey
}
if (keys.mnemonic) {
info = {
type: encodingType.mnemonic,
data: keys.mnemonic,
}
return cb(null, info.type + '|' + info.data + '|' + wallet.credentials.network.toLowerCase() + '|' + derivationPath + '|' + (wallet.credentials.mnemonicHasPassphrase));
} else {
info = {
type: encodingType.xpriv,
data: keys.xPrivKey
}
}
});
return cb(null, info.type + '|' + info.data + '|' + wallet.credentials.network.toLowerCase() + '|' + derivationPath + '|' + (wallet.credentials.mnemonicHasPassphrase));
};
root.setTouchId = function(wallet, enabled, cb) {
@ -1055,6 +1051,12 @@ angular.module('copayApp.services').factory('walletService', function($log, $tim
});
};
root.getKeysWithPassword = function(wallet, password) {
try {
return wallet.getKeys(password);
} catch (e) {}
}
root.getViewStatus = function(wallet, txp) {
var status = txp.status;
var type;

View File

@ -408,6 +408,14 @@ input[type=file] {
line-height: 0px;
}
.w100p {
width: 100%;
}
.pointer {
cursor: pointer;
}
.text-right {
text-align: right;
}

View File

@ -25,7 +25,7 @@ $font-size-small: 12px;
$font-family-sans-serif: $roboto;
$font-family-light-sans-serif: $roboto-light;
$button-border-radius: $visible-radius;
$button-border-radius: $subtle-radius;
$button-height: 52px;
$button-padding: 16px;

View File

@ -11,6 +11,14 @@
left: 8px;
font-size: 24px;
}
.big-icon-svg {
left:5px;
& > .bg{
width:30px;
height:30px;
box-shadow: none;
}
}
font-size: 11px;
padding-left: 48px;
}

View File

@ -1,14 +1,27 @@
#bitpayCard {
.bar-header {
border: 0;
background: #1e3186;
.title, .button {
color: #fff;
}
.button {
background-color: transparent;
}
}
.amount {
width: 100%;
text-align: center;
padding: 2rem 1rem 1.5rem 1rem;
min-height: 140px;
height: 140px;
border-color: #172565;
background-color: #1e3186;
background-image: linear-gradient(0deg, #172565, #172565 0%, transparent 0%);
color: #fff;
}
.wallet-details-wallet-info {
bottom: 5px;
}
strong {
line-height: 100%;
}

View File

@ -0,0 +1,70 @@
#bitpayCard-intro {
.slider-pager .slider-pager-page {
color: #fff;
}
.cta-button{
text-align: center;
position: absolute;
bottom: 55px;
padding: 0 1.5rem;
width: 100%;
}
background: rgba(30, 49, 134, 1);
background: -moz-linear-gradient(top, rgba(30, 49, 134, 1) 0%, rgba(17, 27, 73, 1) 100%);
background: -webkit-gradient(left top, left bottom, color-stop(0%, rgba(30, 49, 134, 1)), color-stop(100%, rgba(17, 27, 73, 1)));
background: -webkit-linear-gradient(top, rgba(30, 49, 134, 1) 0%, rgba(17, 27, 73, 1) 100%);
background: -o-linear-gradient(top, rgba(30, 49, 134, 1) 0%, rgba(17, 27, 73, 1) 100%);
background: -ms-linear-gradient(top, rgba(30, 49, 134, 1) 0%, rgba(17, 27, 73, 1) 100%);
background: linear-gradient(to bottom, rgba(30, 49, 134, 1) 0%, rgba(17, 27, 73, 1) 100%);
color: #fff;
height: 100%;
.bar.bar-header {
background: rgb(30, 49, 134);
color: #fff;
button {
color: #fff;
}
.secondary-buttons {
button {
color: rgba(255, 255, 255, .5);
}
}
}
.bar.bar-stable{
border-color: transparent;
border:none;
}
.button-transparent{
background: none !important;
}
.button-translucent{
background: rgba(215, 215, 215, 0.1)
}
.button-primary{
background: rgb(100, 124, 232) !important;
color:#fff;
}
.light-blue{
color:rgb(100, 124, 232);
}
.text-white{
color: #ffffff;
}
ion-content {
background: url(../img/onboarding-welcome-bg.png);
background-position: top center;
background-size: contain;
background-repeat: repeat-x;
height: 100%;
.scroll{
height: 100%;
}
color: #fff;
p {
text-align: center;
margin: 40px 20px;
font-size: 1.2rem;
color: rgba(255, 255, 255, .5);
}
}
}

View File

@ -0,0 +1,15 @@
#export {
.list {
background-color: #fff;
}
.top-tabs.row {
padding: 0;
}
.top-tabs .col {
font-size: 14px;
cursor: pointer;
padding: 10px 5px;
border-bottom-width: 2px;
border-bottom-color: #172565;
}
}

12
src/sass/views/import.scss vendored Normal file
View File

@ -0,0 +1,12 @@
#import {
.top-tabs.row {
padding: 0;
}
.top-tabs .col {
font-size: 14px;
cursor: pointer;
padding: 10px 5px;
border-bottom-width: 2px;
border-bottom-color: #172565;
}
}

View File

@ -107,12 +107,8 @@
i {
color: grey;
position: inherit;
left: 25px;
vertical-align: super;
padding-right: 10px;
border-right: 1px solid;
border-color: grey;
font-size: 20px;
padding: 0 10px;
float: right;
}
contact {
margin-left: 15px;

View File

@ -0,0 +1,14 @@
#glidera {
.glidera-lead {
margin: 1rem;
color: $mid-gray;
font-size: 18px;
text-align: center;
}
.disclosure {
color: $mid-gray;
font-size: 12px;
text-align: left;
margin: 1rem;
}
}

View File

@ -36,7 +36,7 @@
}
.incomplete {
padding: 50px;
height: 350px;
height: 352px;
.title {
padding: 20px;
font-size: 25px;

View File

@ -17,6 +17,10 @@
color: $mid-gray;
margin: 1rem 0;
}
&-description-disabled {
color: cadetblue;
text-decoration: none;
}
.setting-title, .setting-value {
display: block;
overflow: hidden;

View File

@ -12,11 +12,14 @@
@import "walletDetails";
@import "advancedSettings";
@import "bitpayCard";
@import "bitpayCardIntro";
@import "address-book";
@import "wallet-backup-phrase";
@import "zero-state";
@import "onboarding/onboarding";
@import "includes/actionSheet";
@import "export";
@import "import";
@import "includes/walletActivity";
@import "includes/wallets";
@import "includes/modals/modals";
@ -27,4 +30,5 @@
@import "includes/txp-details";
@import "includes/tx-status";
@import "includes/walletSelector";
@import "integrations/coinbase.scss";
@import "integrations/coinbase";
@import "integrations/glidera";

View File

@ -1,49 +0,0 @@
describe('Backup Controller', function() {
var walletService;
describe('Incomplete wallet', function() {
beforeEach(function(done) {
mocks.init(FIXTURES, 'backupController', {
loadProfile: PROFILE.incomplete2of2,
}, done);
});
afterEach(function(done) {
mocks.clear({}, done);
});
it('should be defined', function() {
should.exist(ctrl);
});
it('should set the mnemonic incomplete wallets', function(done) {
scope.initFlow();
should.exist(scope.mnemonicWords);
scope.mnemonicWords.should.deep.equal('dizzy cycle skirt decrease exotic fork sure mixture hair vapor copper hero'.split(' '));
done();
});
});
describe('Complete 1-1 wallet', function() {
beforeEach(function(done) {
mocks.init(FIXTURES, 'backupController', {
loadProfile: PROFILE.testnet1of1,
}, done);
});
afterEach(function(done) {
mocks.clear({}, done);
});
it('should not set the mnemonic for complete wallets', function() {
scope.initFlow();
scope.mnemonicWords.should.deep.equal('cheese where alarm job conduct donkey license pave congress pepper fence current'.split(' '));
});
it('should set main wallet info', function(done) {
scope.walletName.should.equal('kk');
done();
});
});
});

View File

@ -1,18 +0,0 @@
describe('copayers', function() {
var walletService;
var fixtures = {};
beforeEach(function(done){
mocks.init(fixtures, 'copayersController', {}, done);
})
afterEach(function(done){
mocks.clear({}, done);
})
it('should be defined', function() {
should.exist(ctrl);
});
});

View File

@ -1,185 +0,0 @@
describe('createController', function() {
var fixtures = {
// Store prefs
'1eda3e702196b8d5d82fae129249bc79f0d5be2f5309a4e39855e7eb4ad31428': {},
'31f5deeef4cf7fd8fc67297179232e8e4590532960454ad958009132fef3daae': {},
// createWallet 1-1
//
'b665ad8991c67f8f7e8ffb7e86c3b930fd3ff56c68eb6fd441bf374559cfe59c': {
"walletId": "63d910e8-3e1b-4aac-97e9-aa0299a74c2c"
},
'd5cc6adebc752c154998f1c96af2b24e21e52dbd7c07008c333af03b905ffb85': {
"copayerId": "a9dcee10fe9c611300e6c7926ece20780f89b9a98baaa342928038b5503ed929",
"wallet": {
"version": "1.0.0",
"createdOn": 1465385318,
"id": "63d910e8-3e1b-4aac-97e9-aa0299a74c2c",
"name": "{\"iv\":\"BZQVWAP6d1e4G8Fq1rQKbA==\",\"v\":1,\"iter\":1,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"ct\":\"70OA+k4+xTPxim+QSdDtA5/Cf055\"}",
"m": 1,
"n": 1,
"singleAddress": false,
"status": "complete",
"publicKeyRing": [{
"xPubKey": "xpub6DRjAgkh3vGTWDcEmDp4TPwy48Nu8yrp6swCEdCCLL615CgnZon7r3vXYr8LYibMLJh5DriGSito1FRBwVoBkjD1ZWG4dmgiC935wLj3nQC",
"requestPubKey": "02befcc7499abcecf9608bb05e665f374434a89ca0c4e9baeab7dd28c027143458"
}],
"copayers": [{
"version": 2,
"createdOn": 1465385318,
"xPubKey": "xpub6DRjAgkh3vGTWDcEmDp4TPwy48Nu8yrp6swCEdCCLL615CgnZon7r3vXYr8LYibMLJh5DriGSito1FRBwVoBkjD1ZWG4dmgiC935wLj3nQC",
"id": "a9dcee10fe9c611300e6c7926ece20780f89b9a98baaa342928038b5503ed929",
"name": "{\"iv\":\"BZQVWAP6d1e4G8Fq1rQKbA==\",\"v\":1,\"iter\":1,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"ct\":\"wwZd+2LQgYR6cA==\"}",
"requestPubKey": "02befcc7499abcecf9608bb05e665f374434a89ca0c4e9baeab7dd28c027143458",
"signature": "3044022042e069126a42f1b9b498c315a825ef4fc9f4214156442651e4fef5c7678245e702205936045d7b22baa36ba36ef827cc3e5d542d57d9a1afb3a54080d12f0b95c67e",
"requestPubKeys": [{
"key": "02befcc7499abcecf9608bb05e665f374434a89ca0c4e9baeab7dd28c027143458",
"signature": "3044022042e069126a42f1b9b498c315a825ef4fc9f4214156442651e4fef5c7678245e702205936045d7b22baa36ba36ef827cc3e5d542d57d9a1afb3a54080d12f0b95c67e"
}],
"customData": "{\"iv\":\"BZQVWAP6d1e4G8Fq1rQKbA==\",\"v\":1,\"iter\":1,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"ct\":\"1Wjf2KvFkd5k0ypiiSNkSVXk7zdBOiTeCrwzPBI7fMQ/VqXUzrSB6gMGs9jISr+MvCaL1GJIXjaMnlQZNMR0lx/Pd1c6R/nKGBdHjKh0mlI=\"}"
}],
"pubKey": "026d95bb5cc2a30c19e22379ae78b4757aaa2dd0ccbd15a1db054fb50cb98ed361",
"network": "livenet",
"derivationStrategy": "BIP44",
"addressType": "P2PKH",
"addressManager": {
"version": 2,
"derivationStrategy": "BIP44",
"receiveAddressIndex": 0,
"changeAddressIndex": 0,
"copayerIndex": 2147483647
},
"scanStatus": null
}
},
//createWallet 2-2
'5a1d11ebc2a011f018b049de6b5c6b990cdc8e280644103f95a995321dbf0248': {
"walletId": "2f50f598-7550-4e54-8032-15aa892309fb"
},
// join
'58f2f3a6f11cd7dee9a75e026e3ba570c09b952bfea05f596fdb48e6ea323f21': {
"copayerId": "3d4eb9b439eee1b2b73cf792eda52e420f4665109c7234a50cf3cdbf296ea8fb",
"wallet": {
"version": "1.0.0",
"createdOn": 1465347188,
"id": "2f50f598-7550-4e54-8032-15aa892309fb",
"name": "{\"iv\":\"BZQVWAP6d1e4G8Fq1rQKbA==\",\"v\":1,\"iter\":1,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"ct\":\"70OA+k4+xTPxim+QSdDtA5/Cf055\"}",
"m": 2,
"n": 2,
"singleAddress": false,
"status": "pending",
"publicKeyRing": [],
"copayers": [{
"version": 2,
"createdOn": 1465347188,
"xPubKey": "xpub6CkPnrzSUp9qzBVM3hpo4oS2JKC6GJq6brE1yW59QrnhDpvkFLakpxUGRGXH62fiXb5S2VbnD4h2DLoCMfSkwfonbNgNYTJw9Ko5SqWEqCR",
"id": "3d4eb9b439eee1b2b73cf792eda52e420f4665109c7234a50cf3cdbf296ea8fb",
"name": "{\"iv\":\"BZQVWAP6d1e4G8Fq1rQKbA==\",\"v\":1,\"iter\":1,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"ct\":\"wwZd+2LQgYR6cA==\"}",
"requestPubKey": "022941a5ecb8c7224f812ad6b03bd1c9bb77861080b21703eabe18ef9a72b48e72",
"signature": "30440220521623cf346f667658c00f1dea113407f23cecf02932c7dcb4b8bf35f1836b7a02202c77b8e4260942f4e13a58faae1f92e1130bae1157492056347e66741150eb2c",
"requestPubKeys": [{
"key": "022941a5ecb8c7224f812ad6b03bd1c9bb77861080b21703eabe18ef9a72b48e72",
"signature": "30440220521623cf346f667658c00f1dea113407f23cecf02932c7dcb4b8bf35f1836b7a02202c77b8e4260942f4e13a58faae1f92e1130bae1157492056347e66741150eb2c"
}],
"customData": "{\"iv\":\"BZQVWAP6d1e4G8Fq1rQKbA==\",\"v\":1,\"iter\":1,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"ct\":\"YJqN/LtkCY0cOB235RtbGEAY7wKGT0cUUpAvUeLkAUKz3/1axsYZtnG+PU0jHtwQvgmKNLkNcXNR60K+tyRpU0TG1z8pyx4gKwwD3Dt7KzA=\"}"
}],
"pubKey": "026d95bb5cc2a30c19e22379ae78b4757aaa2dd0ccbd15a1db054fb50cb98ed361",
"network": "livenet",
"derivationStrategy": "BIP44",
"addressType": "P2SH",
"addressManager": {
"version": 2,
"derivationStrategy": "BIP44",
"receiveAddressIndex": 0,
"changeAddressIndex": 0,
"copayerIndex": 2147483647
},
"scanStatus": null
}
},
}; // TODO: Read from file
beforeEach(function(done) {
mocks.init(fixtures, 'createController', {}, done);
})
afterEach(function(done) {
mocks.clear({}, done);
});
it('should be defined', function() {
should.exist(ctrl);
});
it('should create a 1-1 wallet from mnemonic', function(done) {
var fakeForm = {};
// FROM DATA
scope.seedSource = {
id: 'set'
};
scope.requiredCopayers = 1;
scope.totalCopayers = 1
scope.walletName = 'A test wallet';
scope.isTestnet = false;
scope.bwsurl = null;
scope.isSingleAddress = false;
scope.privateKey = 'legal winner thank year wave sausage worth useful legal winner thank yellow';
scope._walletPrivKey = 'Kz4CFSTgLzoYfMkt97BTBotUbZYXjMts6Ej9HbVfCf5oLmun1BXy';
ctrl.setSeedSource();
ctrl.create(fakeForm);
should.not.exist(ctrl.error);
mocks.go.walletHome.calledOnce.should.equal(true);
//
// check resulting profile
storageService.getProfile(function(err, profile) {
should.not.exist(err);
var c = profile.credentials[0];
c.network.should.equal('livenet');
// from test vectors from https://dcpos.github.io/bip39/
c.xPrivKey.should.equal('xprv9s21ZrQH143K2x4gnzRB1eZDq92Uuvy9CXbvgQGdvykXZ9mkkot6LBjzDpgaAfvzkuxJe9JKJXQ38VoPutxvACA5MsyoBs5UyQ4HZKGshGs');
done();
});
});
it('should create an incomplete 2-2 wallet from mnemonic', function(done) {
var fakeForm = {};
// FROM DATA
scope.seedSource = {
id: 'set'
};
scope.requiredCopayers = 2;
scope.totalCopayers = 2;
scope.walletName = 'A test wallet';
scope.isTestnet = false;
scope.bwsurl = null;
scope.privateKey = 'dizzy cycle skirt decrease exotic fork sure mixture hair vapor copper hero';
scope._walletPrivKey = 'Kz4CFSTgLzoYfMkt97BTBotUbZYXjMts6Ej9HbVfCf5oLmun1BXy';
ctrl.setSeedSource();
ctrl.create(fakeForm);
should.not.exist(ctrl.error);
mocks.go.walletHome.calledOnce.should.equal(true, 'Go Wallet Home Called');
// check resulting profile
storageService.getProfile(function(err, profile) {
should.not.exist(err);
var c = profile.credentials[0];
c.network.should.equal('livenet');
// from test vectors from https://dcpos.github.io/bip39/
c.xPrivKey.should.equal('xprv9s21ZrQH143K27bhzfejhNcitEAJgLKCfdLxwhr1FLu43FLqLwscAxXgmkucpF4k8eGmepSctkiQDbcR98Qd1bzSeDuR9jeyQAQEanPT2A4');
// m/44'/0'/0'
c.xPubKey.should.equal('xpub6CkPnrzSUp9qzBVM3hpo4oS2JKC6GJq6brE1yW59QrnhDpvkFLakpxUGRGXH62fiXb5S2VbnD4h2DLoCMfSkwfonbNgNYTJw9Ko5SqWEqCR');
done();
});
});
});

View File

@ -1,82 +0,0 @@
describe('disclaimerController', function() {
var walletService;
var storeProfile;
var fixtures = {
'e4d8ae25e03e5fef2e553615b088cfce222083828c13fdb37b8b6cf87bf76236': {
"walletId": "215f125d-57e7-414a-9723-448256113440",
},
'3f3b354d45c3eae3e4fe8830fcb728e5e570515af86e1a35deff0048a7a5e6b5': {
"copayerId": "1a91ead1b6d13da882a25377a20e460df557e77008ea4f60eecbf984f786cf03",
"wallet": {
"version": "1.0.0",
"createdOn": 1465347281,
"id": "215f125d-57e7-414a-9723-448256113440",
"name": "{\"iv\":\"BZQVWAP6d1e4G8Fq1rQKbA==\",\"v\":1,\"iter\":1,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"ct\":\"/gaG7FIkhCiwsWKZUR0sL/cxH+zHMK0=\"}",
"m": 1,
"n": 1,
"singleAddress": false,
"status": "complete",
"publicKeyRing": [{
"xPubKey": "xpub6Cb7MYAX7mJR28MfFueCsoDVVHhoWkQxRC4viAeHanYwRNgDo5xMF42xmAeExzfyPXX3GaALNA8hWFMekVYvDF2BALommUhMgZ52szh88fd",
"requestPubKey": "029a167eebe3ccd9987d41743477f8b75e1f3c30463187e1b106e0cc1155efa4dd"
}],
"copayers": [{
"version": 2,
"createdOn": 1465347281,
"xPubKey": "xpub6Cb7MYAX7mJR28MfFueCsoDVVHhoWkQxRC4viAeHanYwRNgDo5xMF42xmAeExzfyPXX3GaALNA8hWFMekVYvDF2BALommUhMgZ52szh88fd",
"id": "1a91ead1b6d13da882a25377a20e460df557e77008ea4f60eecbf984f786cf03",
"name": "{\"iv\":\"BZQVWAP6d1e4G8Fq1rQKbA==\",\"v\":1,\"iter\":1,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"ct\":\"wwZd+2LQgYR6cA==\"}",
"requestPubKey": "029a167eebe3ccd9987d41743477f8b75e1f3c30463187e1b106e0cc1155efa4dd",
"signature": "3045022100ac3f31ef145eabde6a125958aa9d63c2bd4aa27717d7f6905c3e3ff1e733ee8e02206a43200b775ee5c8f7a85c4d3309d155240d5de46a7d9c5e60045bf49779f40b",
"requestPubKeys": [{
"key": "029a167eebe3ccd9987d41743477f8b75e1f3c30463187e1b106e0cc1155efa4dd",
"signature": "3045022100ac3f31ef145eabde6a125958aa9d63c2bd4aa27717d7f6905c3e3ff1e733ee8e02206a43200b775ee5c8f7a85c4d3309d155240d5de46a7d9c5e60045bf49779f40b"
}],
"customData": "{\"iv\":\"BZQVWAP6d1e4G8Fq1rQKbA==\",\"v\":1,\"iter\":1,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"ct\":\"9l63hoVnA71LshCC5xbOTHA+ivBzux7u8SAci56p4aaVIF4qzXQhQKFX+sAFGfBjULm/E1st6awdXnxbAgjbF7D0zsbBFLFOSCw+ko5Xc6o=\"}"
}],
"pubKey": "026d95bb5cc2a30c19e22379ae78b4757aaa2dd0ccbd15a1db054fb50cb98ed361",
"network": "livenet",
"derivationStrategy": "BIP44",
"addressType": "P2PKH",
"addressManager": {
"version": 2,
"derivationStrategy": "BIP44",
"receiveAddressIndex": 0,
"changeAddressIndex": 0,
"copayerIndex": 2147483647
},
"scanStatus": null
}
},
}; // TODO: Read from file
beforeEach(function(done) {
mocks.init(fixtures, 'disclaimerController', {
initController: true,
noProfile: true,
}, done);
});
afterEach(function(done) {
mocks.clear({}, done);
});
it('should be defined', function() {
should.exist(ctrl);
});
it('should create the initial profile', function(done) {
localStorage.clear();
ctrl.init({
walletPrivKey: 'Kz4CFSTgLzoYfMkt97BTBotUbZYXjMts6Ej9HbVfCf5oLmun1BXy',
mnemonic: 'tunnel fork scare industry noble snow tank bullet over gesture nuclear next',
});
setTimeout(function() {
mocks.ongoingProcess.set.getCall(1).args[0].should.equal('creatingWallet');
mocks.ongoingProcess.set.getCall(1).args[1].should.equal(false);
done();
}, 100);
});
});

View File

@ -1,112 +0,0 @@
describe('importController', function() {
var walletService;
var storeProfile;
var fixtures = {
'31f5deeef4cf7fd8fc67297179232e8e4590532960454ad958009132fef3daae': {},
'4599136eff6deb4c9c78043fa84113617a16d75c45920d662305f6227ae8f0a0': {
"wallet": {
"version": "1.0.0",
"createdOn": 1463488747,
"id": "267bfa75-5575-4af7-8aa3-f5186bc99262",
"name": "{\"iv\":\"BZQVWAP6d1e4G8Fq1rQKbA==\",\"v\":1,\"iter\":1,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"ct\":\"70OA+k4+xTPxim+QSdDtA5/Cf055\"}",
"m": 1,
"n": 1,
"status": "complete",
"publicKeyRing": [{
"xPubKey": "xpub6DRjAgkh3vGTWDcEmDp4TPwy48Nu8yrp6swCEdCCLL615CgnZon7r3vXYr8LYibMLJh5DriGSito1FRBwVoBkjD1ZWG4dmgiC935wLj3nQC",
"requestPubKey": "02befcc7499abcecf9608bb05e665f374434a89ca0c4e9baeab7dd28c027143458"
}],
"copayers": [{
"version": 2,
"createdOn": 1463490295,
"id": "a9dcee10fe9c611300e6c7926ece20780f89b9a98baaa342928038b5503ed929",
"name": "{\"iv\":\"BZQVWAP6d1e4G8Fq1rQKbA==\",\"v\":1,\"iter\":1,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"ct\":\"wwZd+2LQgYR6cA==\"}",
"xPubKey": "xpub6DRjAgkh3vGTWDcEmDp4TPwy48Nu8yrp6swCEdCCLL615CgnZon7r3vXYr8LYibMLJh5DriGSito1FRBwVoBkjD1ZWG4dmgiC935wLj3nQC",
"requestPubKey": "02befcc7499abcecf9608bb05e665f374434a89ca0c4e9baeab7dd28c027143458",
"signature": "3044022042e069126a42f1b9b498c315a825ef4fc9f4214156442651e4fef5c7678245e702205936045d7b22baa36ba36ef827cc3e5d542d57d9a1afb3a54080d12f0b95c67e",
"requestPubKeys": [{
"key": "02befcc7499abcecf9608bb05e665f374434a89ca0c4e9baeab7dd28c027143458",
"signature": "3044022042e069126a42f1b9b498c315a825ef4fc9f4214156442651e4fef5c7678245e702205936045d7b22baa36ba36ef827cc3e5d542d57d9a1afb3a54080d12f0b95c67e"
}],
"customData": "{\"iv\":\"BZQVWAP6d1e4G8Fq1rQKbA==\",\"v\":1,\"iter\":1,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"ct\":\"1Wjf2KvFkd5k0ypiiSNkSVXk7zdBOiTeCrwzPBI7fMQ/VqXUzrSB6gMGs9jISr+MvCaL1GJIXjaMnlQZNMR0lx/Pd1c6R/nKGBdHjKh0mlI=\"}"
}],
"pubKey": "026d95bb5cc2a30c19e22379ae78b4757aaa2dd0ccbd15a1db054fb50cb98ed361",
"network": "livenet",
"derivationStrategy": "BIP44",
"addressType": "P2PKH",
"addressManager": {
"version": 2,
"derivationStrategy": "BIP44",
"receiveAddressIndex": 0,
"changeAddressIndex": 0,
"copayerIndex": 2147483647
},
"scanStatus": null
},
"preferences": {},
"pendingTxps": [],
"balance": {
"totalAmount": 0,
"lockedAmount": 0,
"totalConfirmedAmount": 0,
"lockedConfirmedAmount": 0,
"availableAmount": 0,
"availableConfirmedAmount": 0,
"byAddress": [],
"totalBytesToSendMax": 0,
"totalBytesToSendConfirmedMax": 0
}
}
}; // TODO: Read from file
beforeEach(function(done){
mocks.init(fixtures, 'importController', {}, done);
})
afterEach(function(done){
mocks.clear({}, done);
});
it('should be defined', function() {
should.exist(ctrl);
});
it('should import a 1-1 wallet from mnemonic', function(done) {
var fakeForm = {
words: {
$modelValue: 'legal winner thank year wave sausage worth useful legal winner thank yellow'
},
passphrase: {}
};
// FROM DATA
scope.seedSource = {
id: 'set'
};
scope.bwsurl = null;
scope._walletPrivKey = 'Kz4CFSTgLzoYfMkt97BTBotUbZYXjMts6Ej9HbVfCf5oLmun1BXy';
scope.setSeedSource();
scope.importMnemonic(fakeForm);
should.not.exist(scope.error);
mocks.notification.success.calledOnce.should.equal(true);
// mocks.go.walletHome.calledOnce.should.equal(true);
// check resulting profile
storageService.getProfile(function(err, profile) {
should.not.exist(err);
var c = profile.credentials[0];
c.network.should.equal('livenet');
// from test vectors from https://dcpos.github.io/bip39/
c.xPrivKey.should.equal('xprv9s21ZrQH143K2x4gnzRB1eZDq92Uuvy9CXbvgQGdvykXZ9mkkot6LBjzDpgaAfvzkuxJe9JKJXQ38VoPutxvACA5MsyoBs5UyQ4HZKGshGs');
done();
});
});
});

View File

@ -1,71 +0,0 @@
describe('index', function() {
var walletService;
describe('Incomplete wallet', function() {
beforeEach(function(done) {
mocks.init(FIXTURES, 'indexController', {
loadProfile: PROFILE.incomplete2of2,
initController: true,
}, done);
});
afterEach(function(done) {
mocks.clear({}, done);
});
it('should be defined', function() {
should.exist(ctrl);
});
it('should set the invitation code for incomplete wallets', function(done) {
should.exist(ctrl);
ctrl.walletSecret.should.equal('GJ1A8mopdW7wPNWGVksqwQKz4CFSTgLzoYfMkt97BTBotUbZYXjMts6Ej9HbVfCf5oLmun1BXyL');
// should redirect to copayers
mocks.go.path.getCall(0).args[0].should.equal('copayers');
done();
});
});
describe('Complete 1-1 wallet', function() {
beforeEach(function(done) {
mocks.init(FIXTURES, 'indexController', {
loadProfile: PROFILE.testnet1of1,
initController: true,
}, done);
});
afterEach(function(done) {
mocks.clear({}, done);
});
it('should not set the invitation code for complete wallets', function() {
// should redirect to copayers
mocks.go.path.callCount.should.equal(0);
should.not.exist(ctrl.walletSecret);
});
it('should set main wallet info', function(done) {
ctrl.walletName.should.equal('kk');
ctrl.totalBalanceSat.should.equal(1847686);
done();
});
it('should set information for receive tab', function(done) {
ctrl.tab.should.equal('walletHome');
ctrl.setTab('receive');
ctrl.tab.should.equal('receive');
done();
});
it.skip('should updates remote preferences', function(done) {
ctrl.updateRemotePreferences({}, function() {
done();
});
});
});
});

View File

@ -1,150 +0,0 @@
describe('joinController', function() {
var walletService;
var fixtures = {
// join
'668623e51aaae25c637fb9c57bb30a169a0ff67fa1e67e6e61643c7e5e580a66': {
"copayerId": "962fb5dd31d9f715efdbb33d41533d272bb6c2ecd28bbb8181358f86b08253dd",
"wallet": {
"version": "1.0.0",
"createdOn": 1466006460,
"id": "10387ed3-51cf-43b4-91fe-ad85ca2ae368",
"name": "{\"iv\":\"4Agx234j4p+TQS0QXj7bow==\",\"v\":1,\"iter\":1,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"ct\":\"IEohefHXl/tr4rA=\"}",
"m": 2,
"n": 2,
"singleAddress": false,
"status": "complete",
"publicKeyRing": [{
"xPubKey": "xpub6C6dynsH56i7VhzHzo2ZcJguHsjYuUuoPcAdku8h6c7ZaJSYb4WQjKcGdggbpWEuaQspY3LHmFUoCQhk1ErmdegXnsJeSxoKqiPD1CUxVvT",
"requestPubKey": "0200fbedb7d04af9edbd1602103c1ff68454fd009fd8b1acd957441e776c69ff59"
}, {
"xPubKey": "xpub6BsR71KDdSPMePtuipRiWKMC2Q9XEXfk6WM1trbJzPEhcwVBKyN9UhWtpnGv2pu4mtZyKFRgwL98hDH6TBdeEFNVp8Jf81kPBKPeWpn4sWr",
"requestPubKey": "02ad777ba00bf085a2d167c0600df290037d40e5e0d33b5f8e345b0b80a8861bd4"
}],
"copayers": [{
"version": 2,
"createdOn": 1466006460,
"id": "4f72d7bc290a0343a5096cf28999d5d329a9be42651b061fb9489130d0cf9af9",
"name": "{\"iv\":\"RZr7/0eA7F70T/wBCJo7kw==\",\"v\":1,\"iter\":1,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"ct\":\"nL4c40ADLWELtoE=\"}",
"xPubKey": "xpub6C6dynsH56i7VhzHzo2ZcJguHsjYuUuoPcAdku8h6c7ZaJSYb4WQjKcGdggbpWEuaQspY3LHmFUoCQhk1ErmdegXnsJeSxoKqiPD1CUxVvT",
"requestPubKey": "0200fbedb7d04af9edbd1602103c1ff68454fd009fd8b1acd957441e776c69ff59",
"signature": "304402200af094bbb7c432c9a1323534db125431c87bdec9678f40e89a42f209115a222202207a87a27b5f14bf931e1a15d71aa8407118398e5540a8fcbaf7caffef534b6a49",
"requestPubKeys": [{
"key": "0200fbedb7d04af9edbd1602103c1ff68454fd009fd8b1acd957441e776c69ff59",
"signature": "304402200af094bbb7c432c9a1323534db125431c87bdec9678f40e89a42f209115a222202207a87a27b5f14bf931e1a15d71aa8407118398e5540a8fcbaf7caffef534b6a49"
}],
"customData": "{\"iv\":\"kSIFrEhNScxUNG5BMnV34A==\",\"v\":1,\"iter\":1,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"ct\":\"strUeMXiPhTPmsatrStRfaF9+ZD1LU+m+I6Xeu4m0s43DIqc/DYJwm+37fatohNKQ6J7FQKTCJUgMiidAe30K6Dw7J7GA6mFhedMsGLJNbOmBEhRN0AAbwXW6B0=\"}"
}, {
"version": 2,
"createdOn": 1466006511,
"xPubKey": "xpub6BsR71KDdSPMePtuipRiWKMC2Q9XEXfk6WM1trbJzPEhcwVBKyN9UhWtpnGv2pu4mtZyKFRgwL98hDH6TBdeEFNVp8Jf81kPBKPeWpn4sWr",
"id": "962fb5dd31d9f715efdbb33d41533d272bb6c2ecd28bbb8181358f86b08253dd",
"name": "{\"iv\":\"BZQVWAP6d1e4G8Fq1rQKbA==\",\"v\":1,\"iter\":1,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"ct\":\"UKYkKqeia8gWrLqaJ+TuzA/LVlrG\"}",
"requestPubKey": "02ad777ba00bf085a2d167c0600df290037d40e5e0d33b5f8e345b0b80a8861bd4",
"signature": "3045022100f7c7bbc49ce679e67420db8614bf59dfbd798e8ad95a0427305ae5008e0aa41b02203997647b80cc6e5a365048dc5b7b1822809b3c9209a053aaeef7e9f3920d7cef",
"requestPubKeys": [{
"key": "02ad777ba00bf085a2d167c0600df290037d40e5e0d33b5f8e345b0b80a8861bd4",
"signature": "3045022100f7c7bbc49ce679e67420db8614bf59dfbd798e8ad95a0427305ae5008e0aa41b02203997647b80cc6e5a365048dc5b7b1822809b3c9209a053aaeef7e9f3920d7cef"
}],
"customData": "{\"iv\":\"BZQVWAP6d1e4G8Fq1rQKbA==\",\"v\":1,\"iter\":1,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"ct\":\"HTlgRDT46ysMT3+XzhxeXgrOfJ1Fq+kiTWG/q7RqISdWWE+cmP5LcI6+PSysEpo66AjOlI9ofyMVxKtptabWYSNgydrhnqZ5EKY0TnFRq8Ov7a8+btXf9n9BDsM=\"}"
}],
"pubKey": "03bdebf86549b272addd61076e026d2f6a225db514f08b8fad08536a8c4a6792c1",
"network": "livenet",
"derivationStrategy": "BIP44",
"addressType": "P2SH",
"addressManager": {
"version": 2,
"derivationStrategy": "BIP44",
"receiveAddressIndex": 0,
"changeAddressIndex": 0,
"copayerIndex": 2147483647
},
"scanStatus": null
}
},
'197031879d401f75c308e3d5014ac2e9560ec805e1fdd58c778e0ae0bfe7ec0a': {},
}
beforeEach(function(done) {
mocks.init(fixtures, 'joinController', {}, done);
})
afterEach(function(done) {
mocks.clear({}, done);
});
it('should be defined', function() {
should.exist(ctrl);
});
// // Get html template from cache
// beforeEach(inject(function($templateCache) {
// viewHtml = $templateCache.get("some/valid/templateUrl");
// }));
// // beforeEach(inject(function(_$compile_, _$rootScope_){
// $compile = _$compile_;
// $rootScope = _$rootScope_;
//
// $scope = $rootScope.$new();
// $scope.user = {};
// $scope.logout = sinon.stub();
// dropdownElement = angular.element(viewHtml);
// }));
it('should join a wallet once the form is submitted', function(done) {
// View' s joinForm is not available
//join.onQrCodeScanned('aQRCode');
//
scope.seedSource = {
id: 'set'
};
ctrl.setSeedSource();
// FROM DATA
scope._walletPrivKey = 'Kz4CFSTgLzoYfMkt97BTBotUbZYXjMts6Ej9HbVfCf5oLmun1BXy';
var fakeForm = {
secret: {
$modelValue: '31B6DG8f12vGhG7hWhQy2PKwngiNnQ4ijPcePSwanQ2gD6N4mWs3eVPtdwZqRQbHnLQyxhoJksL'
},
myName: {
$modelValue: 'myCopayerName'
},
bwsurl: 'null',
createPassphrase: {
$modelValue: null
},
privateKey: {
$modelValue: 'useful poet rely letter cause fat student tumble animal toddler proof husband',
},
passphrase: {
$modelValue: null,
},
};
ctrl.join(fakeForm);
should.not.exist(ctrl.error);
mocks.go.walletHome.calledOnce.should.equal(true, 'Go Wallet Home Called');
// check resulting profile
storageService.getProfile(function(err, profile) {
should.not.exist(err);
var c = profile.credentials[0];
c.network.should.equal('livenet');
// from test vectors from https://dcpos.github.io/bip39/
c.xPrivKey.should.equal('xprv9s21ZrQH143K3ettHXncETrbUjzrTB7yBfhzjnYjbFgExeNMecTGPvJgje2WQeSFS17Sd8ssz8FQuCbm4rK62ojAwPCX8GHtjHNHsmJsbUa');
// m/44'/0'/0'
c.xPubKey.should.equal('xpub6BsR71KDdSPMePtuipRiWKMC2Q9XEXfk6WM1trbJzPEhcwVBKyN9UhWtpnGv2pu4mtZyKFRgwL98hDH6TBdeEFNVp8Jf81kPBKPeWpn4sWr');
c.walletName.should.equal('2-2');
done();
});
});
});

View File

@ -1,37 +0,0 @@
describe('Preferences History Controller', function() {
var walletService;
var txHistory = '[{"txid":"bf31ecaa8e10ce57f9a889fc4c893b40ff57b016dd763957d942e21ed55fc62c","action":"received","amount":120000,"fees":4862,"time":1464969291,"confirmations":8,"outputs":[{"amount":120000,"address":"2N4HgtF9cJSzxhVkj5gbKxwJSKWBmnb9FNJ","message":null}],"note":{"body":"just a comment","editedBy":"31a8c3c0be9ffbb9f257c95f3fd2f73a59cf81e40199ba5918417270db8c4cdb","editedByName":"2-2","editedOn":1464969101},"message":null,"creatorName":"","hasUnconfirmedInputs":false,"amountStr":"1,200 bits","alternativeAmountStr":"0.68 USD","feeStr":"49 bits","safeConfirmed":"6+"}]';
describe('Complete 1-1 wallet', function() {
beforeEach(function(done) {
mocks.init(FIXTURES, 'preferencesHistory', {
loadProfile: PROFILE.testnet1of1,
loadStorage: {
'txsHistory-66d3afc9-7d76-4b25-850e-aa62fcc53a7d': txHistory,
},
}, done);
});
afterEach(function(done) {
mocks.clear({}, done);
});
it('should be defined', function() {
should.exist(ctrl);
});
it('should export csv', function(done) {
scope.csvHistory(function(err) {
should.not.exist(err);
should.exist(scope.csvReady);
scope.csvReady.should.equal(true);
should.exist(scope.csvContent);
JSON.stringify(scope.csvContent).should.equal('[{"Date":"2016-06-03T15:54:51.000Z","Destination":"","Description":"","Amount":"0.00120000","Currency":"BTC","Txid":"bf31ecaa8e10ce57f9a889fc4c893b40ff57b016dd763957d942e21ed55fc62c","Creator":"","Copayers":"","Comment":"just a comment"}]');
done();
});
});
});
});

View File

@ -1,19 +0,0 @@
describe('walletHome', function() {
var walletService;
var fixtures = {};
beforeEach(function(done){
mocks.init(fixtures, 'walletHomeController', {}, done);
})
afterEach(function(done){
mocks.clear({}, done);
});
it('should be defined', function() {
should.exist(ctrl);
});
});

File diff suppressed because it is too large Load Diff

View File

@ -1,262 +0,0 @@
var mocks = {};
// UI-Router mock from
// https://gist.github.com/bmwant/4c8e5fee7a539dba69ace42b617d79c3
mocks.$state = function($q) {
this.expectedTransitions = [];
this.transitionTo = function(stateName) {
if (this.expectedTransitions.length > 0) {
var expectedState = this.expectedTransitions.shift();
if (expectedState !== stateName) {
throw Error("Expected transition to state: " + expectedState + " but transitioned to " + stateName);
}
} else {
throw Error("No more transitions were expected! Tried to transition to " + stateName);
}
console.log("Mock transition to: " + stateName);
this.current = stateName;
var deferred = $q.defer();
var promise = deferred.promise;
deferred.resolve();
return promise;
};
this.is = function(name) {
console.log('[helpers.js.24:name:]', name); //TODO
return this.current == name;
};
this.go = this.transitionTo;
this.expectTransitionTo = function(stateName) {
this.expectedTransitions.push(stateName);
};
this.ensureAllTransitionsHappened = function() {
if (this.expectedTransitions.length > 0) {
throw Error("Not all transitions happened!");
}
};
};
mocks.$timeout = function(cb) {
return cb();
};
mocks.modal = function() {};
mocks.ongoingProcess = {
set: sinon.stub(),
clear: sinon.stub(),
};
mocks.setProfile = function(profile) {};
/*
* opts
*/
var getElements = sinon.stub();
getElements.returns([]);
var getElement = sinon.stub();
getElement.returns({
getElementsByTagName: getElement,
});
mocks.$document = {
getElementById: getElement,
};
mocks.init = function(fixtures, controllerName, opts, done) {
console.log(' * Mock init()');
opts = opts || {};
should.exist(controllerName, 'Provide the name of the Controller to mocks.init()');
mocks.go = {};
mocks.go.walletHome = sinon.stub();
mocks.go.path = sinon.stub();
mocks.go.is = function(name) {
return mocks.go.current == name
};
mocks.notification = {
success: sinon.stub(),
};
angular.module('stateMock', []);
angular.module('stateMock').service("$state", mocks.$state.bind());
module('ionic');
module('ngLodash');
module('angularMoment');
module('gettext');
module('stateMock');
module('bwcModule', function($provide) {
console.log(' * bwcService decorator');
$provide.decorator('bwcService', function($delegate, lodash) {
var getClient = $delegate.getClient;
// Fix Encryption IVs
var utils = $delegate.getUtils();
utils.SJCL.iv = 'BZQVWAP6d1e4G8Fq1rQKbA==';
$delegate.getClient = function(walletData, opts) {
var bwc = new $delegate.Client();
if (walletData)
bwc.import(walletData, {
baseUrl: opts.bwsurl || 'https://bws.bitpay.com/bws/api',
verbose: opts.verbose,
transports: ['polling'],
});
function createHash(method, url, args) {
var headers = JSON.stringify(bwc._getHeaders(method, url, args));
// Fixes BWC version... TODO
headers = headers.replace(/bwc-\d+\.\d+\.\d+/, 'bwc-2.4.0')
var x = method + url + JSON.stringify(args) + headers;
var sjcl = $delegate.getSJCL();
return sjcl.codec.hex.fromBits(sjcl.hash.sha256.hash(x));
};
bwc._originalRequest = bwc._doRequest;
bwc._doGetRequest = function(url, cb) {
url += url.indexOf('?') > 0 ? '&' : '?';
url += 'r=' + 69321;
return this._doRequest('get', url, {}, cb);
};
// Use fixtures
bwc._doRequest = function(method, url, args, cb2) {
// find fixed response:
var hash = createHash(method, url, args);
if (lodash.isUndefined(fixtures[hash])) {
console.log('##### UNDEFINED FIXTURED ####:', hash); //TODO
console.log('##### method:', method); //TODO
console.log('##### url :', url); //TODO
console.log('##### args :', JSON.stringify(args)); //TODO
console.log('##### header:', JSON.stringify(bwc._getHeaders(method, url, args)));
var oldURL = bwc.baseURL;
bwc.baseURL = 'http://localhost:3232/bws/api';
console.log('##### running local: to http://localhost:3232/bws/api');
bwc._originalRequest(method, url, args, function(err, response) {
console.log("### RESPONSE: " + hash + "\n", JSON.stringify(response)); //TODO
bwc.baseURL = oldURL;
return cb2(null, response);
});
} else {
console.log('Using fixture: ' + hash.substr(0, 6) + ' for: ' + url);
return cb2(null, fixtures[hash]);
}
};
return bwc;
};
return $delegate;
});
});
module('copayApp.services', {
$modal: mocks.modal,
$timeout: mocks.$timeout,
$state: mocks.$state,
});
module('copayApp.controllers');
inject(function($rootScope, $controller, $injector, lodash, _configService_, _profileService_, _storageService_) {
scope = $rootScope.$new();
storageService = _storageService_;
// Set up the mock http service responses
$httpBackend = $injector.get('$httpBackend');
// backend definition common for all tests
$httpBackend.when('GET', 'https://bitpay.com/api/rates')
.respond({
code: "BTC",
name: "Bitcoin",
rate: 1
}, {
code: "USD",
name: "US Dollar",
rate: 452.92
});
$httpBackend.whenGET(/views.*/).respond(200, '');
_configService_.get(function() {
function startController() {
console.log(' * starting Controller:', controllerName);
ctrl = $controller(controllerName, {
$scope: scope,
$modal: mocks.modal,
ongoingProcess: mocks.ongoingProcess,
notification: mocks.notification,
configService: _configService_,
profileService: _profileService_,
go: mocks.go,
$document: mocks.$document,
});
};
if (opts.initController)
startController();
if (opts.loadStorage) {
lodash.each(opts.loadStorage, function(v, k) {
localStorage.setItem(k, v);
});
}
if (opts.loadProfile) {
localStorage.setItem('profile', JSON.stringify(opts.loadProfile));
_profileService_.loadAndBindProfile(function(err) {
should.not.exist(err, err);
if (!opts.initController)
startController();
done();
});
} else {
if (opts.noProfile){
return done();
}
_profileService_.create({
noWallet: true
}, function(err) {
should.not.exist(err, err);
if (opts.noDisclaimer){
return done();
}
_profileService_.setDisclaimerAccepted(function() {
if (!opts.initController)
startController();
done();
});
});
}
});
});
};
mocks.clear = function(opts, done) {
opts = opts || {};
if (!opts.keepStorage) {
// Adds walletService's module dependencies
console.log(' * deleting localstorage');
localStorage.clear();
}
done();
};

View File

@ -1,115 +0,0 @@
// Karma configuration
// Generated on Thu Mar 12 2015 18:13:33 GMT-0300 (ART)
module.exports = function(config) {
config.set({
// base path that will be used to resolve all patterns (eg. files, exclude)
basePath: '..',
// frameworks to use
// available frameworks: https://npmjs.org/browse/keyword/karma-adapter
frameworks: ['mocha', 'chai', 'sinon'],
// list of files / patterns to load in the browser
files: [
'bower_components/qrcode-generator/js/qrcode.js',
'bower_components/qrcode-decoder-js/lib/qrcode-decoder.js',
'bower_components/moment/min/moment-with-locales.js',
'bower_components/ionic/release/js/ionic.bundle.js',
'bower_components/angular-moment/angular-moment.js',
'bower_components/ng-lodash/build/ng-lodash.js',
'bower_components/angular-qrcode/angular-qrcode.js',
'bower_components/angular-gettext/dist/angular-gettext.js',
'bower_components/ng-csv/build/ng-csv.js',
'bower_components/ionic-toast/dist/ionic-toast.bundle.min.js',
'bower_components/angular-clipboard/angular-clipboard.js',
'bower_components/angular-mocks/angular-mocks.js',
'angular-pbkdf2/angular-pbkdf2.js',
'angular-bitcore-wallet-client/angular-bitcore-wallet-client.js',
'src/js/**/*.js',
'test/helpers.js',
'test/**/*.js'
],
// list of files to exclude
exclude: [
'src/js/translations.js',
// 'src/js/version.js',
'test/karma.conf.js',
'test/old/*'
],
client: {
mocha: {
// reporter: 'html', // change Karma's debug.html to the mocha web reporter
ui: 'tdd'
}
},
// preprocess matching files before serving them to the browser
// available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
preprocessors: {
'src/js/**/*.js': ['coverage']
},
// test results reporter to use
// possible values: 'dots', 'progress'
// available reporters: https://npmjs.org/browse/keyword/karma-reporter
reporters: ['mocha', 'coverage'],
// optionally, configure the reporter
coverageReporter: {
dir: 'coverage/',
reporters: [{
type: 'html',
subdir: 'report-html'
}, {
type: 'lcov',
subdir: 'report-lcov'
}, {
type: 'text-summary'
}]
},
// web server port
port: 9876,
// enable / disable colors in the output (reporters and logs)
colors: true,
// level of logging
// possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
logLevel: config.LOG_INFO,
// enable / disable watching file and executing tests whenever any file changes
autoWatch: true,
// start these browsers
// available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
browsers: ['PhantomJS'],
plugins: [
'karma-mocha-reporter',
'karma-coverage',
'karma-mocha',
'karma-chai',
'karma-sinon',
'karma-phantomjs-launcher',
'karma-chrome-launcher',
],
// Continuous Integration mode
// if true, Karma captures browsers, runs the tests and exits
singleRun: false
});
};

File diff suppressed because it is too large Load Diff

View File

@ -1,250 +0,0 @@
'use strict';
//
// test/unit/directives/directivesSpec.js
//
describe("Unit: Testing Directives", function() {
var $scope, form;
beforeEach(module('copayApp.directives'));
beforeEach(inject(function($rootScope) {
var w = {};
w.isComplete = sinon.stub().returns(true);
w.privateKey = {};
w.settings = {
unitToSatoshi: 100,
unitDecimals: 2,
alternativeName: 'US Dollar',
alternativeIsoCode: 'USD',
};
w.addressBook = {
'juan': '1',
};
w.totalCopayers = 2;
w.getMyCopayerNickname = sinon.stub().returns('nickname');
w.getMyCopayerId = sinon.stub().returns('id');
w.privateKey.toObj = sinon.stub().returns({
wallet: 'mock'
});
w.getSecret = sinon.stub().returns('secret');
w.getName = sinon.stub().returns('fakeWallet');
w.exportEncrypted = sinon.stub().returns('1234567');
w.getTransactionHistory = sinon.stub().yields({});
w.getNetworkName = sinon.stub().returns('testnet');
w.createTx = sinon.stub().yields(null);
w.sendTx = sinon.stub().yields(null);
w.requiresMultipleSignatures = sinon.stub().returns(true);
w.getTxProposals = sinon.stub().returns([1,2,3]);
$rootScope.wallet = w;
}));
describe('Validate Address', function() {
beforeEach(inject(function($compile, $rootScope) {
$scope = $rootScope;
var element = angular.element(
'<form name="form">' +
'<input type="text" id="address" name="address" placeholder="Send to" ng-model="address" valid-address required>' +
'</form>'
);
$scope.model = {
address: null
};
$compile(element)($scope);
$scope.$digest();
form = $scope.form;
}));
it('should validate with network', inject(function($rootScope) {
$rootScope.wallet.getNetworkName = sinon.stub().returns('testnet');
form.address.$setViewValue('mkfTyEk7tfgV611Z4ESwDDSZwhsZdbMpVy');
expect(form.address.$invalid).to.equal(false);
}));
it('should not validate with other network', inject(function($rootScope) {
$rootScope.wallet.getNetworkName = sinon.stub().returns('livenet');
form.address.$setViewValue('mkfTyEk7tfgV611Z4ESwDDSZwhsZdbMpVy');
expect(form.address.$invalid).to.equal(true);
}));
it('should not validate random', function() {
form.address.$setViewValue('thisisaninvalidaddress');
expect(form.address.$invalid).to.equal(true);
});
});
describe('Validate Amount', function() {
describe('Unit: bits', function() {
beforeEach(inject(function($compile, $rootScope) {
$scope = $rootScope;
var element = angular.element(
'<form name="form">' +
'<input type="number" id="amount" name="amount" placeholder="Amount" ng-model="amount" min="0.00000001" max="10000000000" valid-amount required>' +
'</form>'
);
$scope.model = {
amount: null
};
$compile(element)($scope);
$scope.$digest();
form = $scope.form;
}));
it('should validate', function() {
form.amount.$setViewValue(100);
expect(form.amount.$invalid).to.equal(false);
form.amount.$setViewValue(800);
expect(form.amount.$invalid).to.equal(false);
form.amount.$setViewValue(900);
});
it('should not validate', function() {
form.amount.$setViewValue(0);
expect(form.amount.$invalid).to.equal(true);
form.amount.$setViewValue(999999999999);
expect(form.amount.$invalid).to.equal(true);
});
});
describe('Unit: BTC', function() {
beforeEach(inject(function($compile, $rootScope) {
$scope = $rootScope;
var w = $rootScope.wallet;
w.settings.unitToSatoshi = 100000000;
w.settings.unitName = 'BTC';
w.settings.unitDecimals = 8;
$rootScope.availableBalance = 0.04;
var element = angular.element(
'<form name="form">' +
'<input type="number" id="amount" name="amount" placeholder="Amount" ng-model="amount" min="0.00000001" max="10000000000" valid-amount required>' +
'</form>'
);
$scope.model = {
amount: null
};
$compile(element)($scope);
$scope.$digest();
form = $scope.form;
}));
it('should validate', function() {
form.amount.$setViewValue(0.01);
expect(form.amount.$invalid).to.equal(false);
form.amount.$setViewValue(0.039);
expect(form.amount.$invalid).to.equal(false);
form.amount.$setViewValue(100292.039);
expect(form.amount.$invalid).to.equal(false);
});
it('should not validate', function() {
form.amount.$setViewValue(0.039998888888888);
expect(form.amount.$invalid).to.equal(true);
form.amount.$setViewValue(0);
expect(form.amount.$invalid).to.equal(true);
form.amount.$setViewValue(0.0);
expect(form.amount.$invalid).to.equal(true);
});
});
});
describe('Contact directive', function() {
var element1, element2;
beforeEach(inject(function($compile, $rootScope) {
$rootScope.wallet = {
addressBook: {
'2MtBXKLtZuXGDshUcyH6yq7aZ33Snbb49pT': {
label: ':)'
}
}
}
element1 = angular.element(
'<contact address="2MtBXKLtZuXGDshUcyH6yq7aZ33Snbb49pT" />'
);
element2 = angular.element(
'<contact address="2MvCKdnwEMiaexi247gi738U6pwUFZxbhXn" />'
);
$compile(element1)($rootScope);
$compile(element2)($rootScope);
$rootScope.$digest();
}));
it('should replace the content', function() {
expect(element1.html()).to.equal(':)');
expect(element2.html()).to.equal('2MvCKdnwEMiaexi247gi738U6pwUFZxbhXn');
});
});
describe('Password strength', function() {
beforeEach(inject(function($compile, $rootScope) {
$scope = $rootScope;
var element = angular.element(
'<input type="password" name="password" ng-model="password" check-strength="passwordStrength" value="asd" required>'
);
$compile(element)($scope);
$scope.$digest();
}));
it('should check very weak password', function() {
$scope.password = 'asd';
$scope.$digest();
expect($scope.passwordStrength.strength).to.equal(1);
});
it('should check weak password', function() {
$scope.password = 'asdasdASDASD';
$scope.$digest();
expect($scope.passwordStrength.message).to.equal('Weak, add numerals');
});
it('should check medium password', function() {
$scope.password = 'asdasdA1';
$scope.$digest();
expect($scope.passwordStrength.message).to.equal('Medium, add punctuation');
});
it('should check strong password', function() {
$scope.password = 'asdasdASDASD1{';
$scope.$digest();
expect($scope.passwordStrength.message).to.equal('Strong, add punctuation');
});
});
describe('Match Password Inputs', function() {
beforeEach(inject(function($compile, $rootScope) {
$scope = $rootScope;
$rootScope.availableBalance = 1000;
var element = angular.element(
'<form name="form">' +
'<input type="password" ng-model="walletPassword" name="walletPassword" required>' +
'<input type="password" ng-model="walletPasswordConfirm" name="walletPasswordConfirm" match="walletPassword" required>' +
'</form>'
);
$scope.model = {
walletPassword: null,
walletPasswordConfirm: null
};
$compile(element)($scope);
$scope.$digest();
form = $scope.form;
}));
it('should not validate', function() {
form.walletPassword.$setViewValue('mysecretpassword');
form.walletPasswordConfirm.$setViewValue('mySecretPassword');
$scope.$digest();
expect(form.walletPasswordConfirm.$invalid).to.equal(true);
});
it('should validate', function() {
form.walletPassword.$setViewValue('mysecretpassword123');
form.walletPasswordConfirm.$setViewValue('mysecretpassword123');
$scope.$digest();
expect(form.walletPasswordConfirm.$invalid).to.equal(false);
});
});
});

View File

@ -1,168 +0,0 @@
'use strict';
//
// test/unit/filters/filtersSpec.js
//
describe('Angular Filters', function() {
beforeEach(angular.mock.module('copayApp'));
beforeEach(module('copayApp.filters'));
beforeEach(inject(function($rootScope) {
var w = {};
w.isComplete = sinon.stub().returns(true);
w.privateKey = {};
w.settings = {
unitToSatoshi: 100,
unitDecimals: 2,
alternativeName: 'US Dollar',
alternativeIsoCode: 'USD',
};
w.addressBook = {
'juan': '1',
};
w.balanceByAddr = [{
'address1': 1
}];
w.totalCopayers = 2;
w.getMyCopayerNickname = sinon.stub().returns('nickname');
w.getMyCopayerId = sinon.stub().returns('id');
w.privateKey.toObj = sinon.stub().returns({
wallet: 'mock'
});
w.getSecret = sinon.stub().returns('secret');
w.getName = sinon.stub().returns('fakeWallet');
w.getId = sinon.stub().returns('id');
w.exportEncrypted = sinon.stub().returns('1234567');
w.getTransactionHistory = sinon.stub().yields({});
w.getNetworkName = sinon.stub().returns('testnet');
w.getAddressesInfo = sinon.stub().returns({});
w.createTx = sinon.stub().yields(null);
w.sendTx = sinon.stub().yields(null);
w.requiresMultipleSignatures = sinon.stub().returns(true);
w.getTxProposals = sinon.stub().returns([1, 2, 3]);
$rootScope.wallet = w;
}));
var walletConfig = {
requiredCopayers: 3,
totalCopayers: 5,
spendUnconfirmed: 1,
reconnectDelay: 100,
networkName: 'testnet',
alternativeName: 'lol currency',
alternativeIsoCode: 'LOL'
};
describe('removeEmpty addresses', function() {
it('should work with empty lists', inject(function($filter) {
var removeEmpty = $filter('removeEmpty');
expect(removeEmpty([]).length).to.equal(0);
}));
it('should work with undefined', inject(function($filter) {
var removeEmpty = $filter('removeEmpty');
expect(removeEmpty(undefined).length).to.equal(0);
}));
it('should filter empty change addresses from other copayers', inject(function($filter) {
var removeEmpty = $filter('removeEmpty');
var addresses = [{
owned: true,
isChange: false,
balance: 0
}, {
owned: false,
isChange: false,
balance: 0
}, {
owned: true,
isChange: true,
balance: 0
}, {
owned: false,
isChange: true,
balance: 0
}];
expect(removeEmpty(addresses).length).to.equal(2);
}));
});
describe('noFractionNumber', function() {
describe('noFractionNumber bits', function() {
beforeEach(inject(function($rootScope) {
var w = $rootScope.wallet;
w.settings.unitToSatoshi = 100;
w.settings.unitName = 'bits';
}));
it('should format number to display correctly', inject(function($filter) {
var noFraction = $filter('noFractionNumber');
expect(noFraction(3100)).to.equal('3,100');
expect(noFraction(3100200)).to.equal('3,100,200');
expect(noFraction(3)).to.equal('3');
expect(noFraction(0.3)).to.equal(0.3);
expect(noFraction(0.30000000)).to.equal(0.3);
expect(noFraction(3200.01)).to.equal('3,200.01');
expect(noFraction(3200890.010000)).to.equal('3,200,890.01');
}));
});
describe('noFractionNumber BTC', function() {
beforeEach(inject(function($rootScope) {
var w = $rootScope.wallet;
w.settings.unitToSatoshi = 100000000;
w.settings.unitName = 'BTC';
}));
it('should format number to display correctly', inject(function($filter) {
var noFraction = $filter('noFractionNumber');
expect(noFraction(0.30000000)).to.equal(0.3);
expect(noFraction(0.00302000)).to.equal(0.00302);
expect(noFraction(1.00000001)).to.equal(1.00000001);
expect(noFraction(3.10000012)).to.equal(3.10000012);
expect(noFraction(0.00100000)).to.equal(0.001);
expect(noFraction(0.00100009)).to.equal(0.00100009);
expect(noFraction(2000.00312011)).to.equal('2,000.00312011');
expect(noFraction(2000998.00312011)).to.equal('2,000,998.00312011');
}));
});
describe('noFractionNumber mBTC', function() {
beforeEach(inject(function($rootScope) {
var w = $rootScope.wallet;
w.settings.unitToSatoshi = 100000;
w.settings.unitName = 'mBTC';
}));
it('should format number to display correctly', inject(function($filter) {
var noFraction = $filter('noFractionNumber');
expect(noFraction(0.30000)).to.equal(0.3);
expect(noFraction(0.00302)).to.equal(0.00302);
expect(noFraction(1.00001)).to.equal(1.00001);
expect(noFraction(3.10002)).to.equal(3.10002);
expect(noFraction(0.00100000)).to.equal(0.001);
expect(noFraction(0.00100009)).to.equal(0.001);
expect(noFraction(2000.00312)).to.equal('2,000.00312');
expect(noFraction(2000998.00312)).to.equal('2,000,998.00312');
}));
});
describe('noFractionNumber:custom fractionSize', function() {
it('should format number to display correctly', inject(function($filter) {
var noFraction = $filter('noFractionNumber');
expect(noFraction(0.30000, 0)).to.equal('0');
expect(noFraction(1.00001, 0)).to.equal('1');
expect(noFraction(3.10002, 0)).to.equal('3');
expect(noFraction(2000.00312, 0)).to.equal('2,000');
expect(noFraction(2000998.00312, 0)).to.equal('2,000,998');
}));
});
});
});

View File

@ -1,215 +0,0 @@
//
// test/unit/services/servicesSpec.js
//
//
//
var sinon = require('sinon');
var preconditions = require('preconditions').singleton();
describe("Angular services", function() {
beforeEach(angular.mock.module('copayApp'));
beforeEach(angular.mock.module('copayApp.services'));
beforeEach(module(function($provide) {
$provide.value('request', {
'get': function(_, cb) {
cb(null, null, [{
name: 'USD Dollars',
code: 'USD',
rate: 2
}]);
}
});
}));
beforeEach(inject(function($rootScope) {
var w = {};
w.isComplete = sinon.stub().returns(true);
w.privateKey = {};
w.settings = {
unitToSatoshi: 100,
unitDecimals: 2,
alternativeName: 'US Dollar',
alternativeIsoCode: 'USD',
};
w.addressBook = {
'juan': '1',
};
w.balanceByAddr = [{
'address1': 1
}];
w.totalCopayers = 2;
w.getMyCopayerNickname = sinon.stub().returns('nickname');
w.getMyCopayerId = sinon.stub().returns('id');
w.privateKey.toObj = sinon.stub().returns({
wallet: 'mock'
});
w.getSecret = sinon.stub().returns('secret');
w.getName = sinon.stub().returns('fakeWallet');
w.getId = sinon.stub().returns('id');
w.exportEncrypted = sinon.stub().returns('1234567');
w.getTransactionHistory = sinon.stub().yields({});
w.getNetworkName = sinon.stub().returns('testnet');
w.getAddressesInfo = sinon.stub().returns({});
w.createTx = sinon.stub().yields(null);
w.sendTx = sinon.stub().yields(null);
w.requiresMultipleSignatures = sinon.stub().returns(true);
w.getTxProposals = sinon.stub().returns([1, 2, 3]);
$rootScope.wallet = w;
}));
describe("Unit: balanceService", function() {
it('should updateBalance in bits', inject(function(balanceService, $rootScope) {
var w = $rootScope.wallet;
expect(balanceService.update).not.to.equal(null);
var Waddr = Object.keys($rootScope.wallet.balanceByAddr)[0];
var a = {};
a[Waddr] = 200;
w.getBalance = sinon.stub().yields(null, 100000001, a, 90000002, 5);
//retuns values in DEFAULT UNIT(bits)
balanceService.update(w, function() {
var b = w.balanceInfo;
expect(b.totalBalanceBTC).to.be.equal(1.00000001);
expect(b.availableBalanceBTC).to.be.equal(0.90000002);
expect(b.lockedBalanceBTC).to.be.equal(0.09999999);
expect(b.totalBalance).to.be.equal('1,000,000.01');
expect(b.availableBalance).to.be.equal('900,000.02');
expect(b.lockedBalance).to.be.equal('99,999.99');
expect(b.balanceByAddr[Waddr]).to.equal(2);
expect(b.safeUnspentCount).to.equal(5);
expect(b.topAmount).to.equal(899800.02);
}, false);
}));
});
describe("Unit: Notification Service", function() {
it('should contain a notification service', inject(function(notification) {
expect(notification).not.to.equal(null);
}));
});
describe("Unit: identityService Service", function() {
it('should contain a identityService service', inject(function(identityService) {
expect(identityService).not.to.equal(null);
}));
});
describe("Unit: pinService", function() {
it('should contain a pinService service', inject(function(pinService) {
expect(pinService).not.to.equal(null);
}));
it('should be able to check -> save -> get -> clear -> check', function(done) {
inject(function(pinService) {
pinService.save('123', 'user', 'pass', function(err) {
pinService.check(function(err, value) {
should.not.exist(err);
value.should.equal(true);
pinService.get('123', function(err, data) {
should.not.exist(err);
data.email.should.be.equal('user');
data.password.should.be.equal('pass');
pinService.clear(function(err) {
should.not.exist(err);
pinService.check(function(err, value) {
should.not.exist(err);
value.should.equal(false);
done();
});
});
});
});
})
})
});
});
describe("Unit: localstorageService", function() {
it('should contain a localstorageService service', inject(function(localstorageService) {
expect(localstorageService).not.to.equal(null);
}));
});
describe("Unit: Backup Service", function() {
it('should contain a backup service', inject(function(backupService) {
expect(backupService).not.to.equal(null);
}));
it('should backup in file', inject(function(backupService) {
var mock = sinon.mock(window);
var expectation = mock.expects('saveAs');
backupService._download({}, 'test');
expectation.once();
}));
});
describe("Unit: isMobile Service", function() {
it('should contain a isMobile service', inject(function(isMobile) {
expect(isMobile).not.to.equal(null);
}));
it('should not detect mobile by default', inject(function(isMobile) {
isMobile.any().should.equal(false);
}));
it('should detect mobile if user agent is Android', inject(function(isMobile) {
navigator.__defineGetter__('userAgent', function() {
return 'Android 2.2.3';
});
isMobile.any().should.equal(true);
}));
});
describe("Unit: uriHandler service", function() {
it('should contain a uriHandler service', inject(function(uriHandler) {
should.exist(uriHandler);
}));
it('should register', inject(function(uriHandler) {
(function() {
uriHandler.register();
}).should.not.throw();
}));
});
describe('Unit: Rate Service', function() {
it('should be injected correctly', inject(function(rateService) {
should.exist(rateService);
}));
it('should be possible to ask if it is available',
inject(function(rateService) {
should.exist(rateService.isAvailable);
})
);
it('should be possible to ask for conversion from fiat',
function(done) {
inject(function(rateService) {
rateService.whenAvailable(function() {
(1e8).should.equal(rateService.fromFiat(2, 'USD'));
done();
});
})
}
);
it('should be possible to ask for conversion to fiat',
function(done) {
inject(function(rateService) {
rateService.whenAvailable(function() {
(2).should.equal(rateService.toFiat(1e8, 'USD'));
done();
});
})
}
);
});
});

View File

@ -1,11 +0,0 @@
// 'use strict';
//
// describe('sidebarController', function(){
// var scope, controller;
//
// beforeEach(angular.mock.module('copayApp.controllers'));
//
// it('dummy test', function(){
// should.exist(true);
// });
// });

View File

@ -1,65 +0,0 @@
var incomplete2of2Wallet = {
"network": "livenet",
"xPrivKey": "xprv9s21ZrQH143K27bhzfejhNcitEAJgLKCfdLxwhr1FLu43FLqLwscAxXgmkucpF4k8eGmepSctkiQDbcR98Qd1bzSeDuR9jeyQAQEanPT2A4",
"xPubKey": "xpub6CkPnrzSUp9qzBVM3hpo4oS2JKC6GJq6brE1yW59QrnhDpvkFLakpxUGRGXH62fiXb5S2VbnD4h2DLoCMfSkwfonbNgNYTJw9Ko5SqWEqCR",
"requestPrivKey": "0cb89231b31dfaae9034ba794b9c48597eb573429f7b4b1f95e1945b22166bd5",
"requestPubKey": "022941a5ecb8c7224f812ad6b03bd1c9bb77861080b21703eabe18ef9a72b48e72",
"copayerId": "3d4eb9b439eee1b2b73cf792eda52e420f4665109c7234a50cf3cdbf296ea8fb",
"publicKeyRing": [{
"xPubKey": "xpub6CkPnrzSUp9qzBVM3hpo4oS2JKC6GJq6brE1yW59QrnhDpvkFLakpxUGRGXH62fiXb5S2VbnD4h2DLoCMfSkwfonbNgNYTJw9Ko5SqWEqCR",
"requestPubKey": "022941a5ecb8c7224f812ad6b03bd1c9bb77861080b21703eabe18ef9a72b48e72"
}],
"walletId": "7bd8d22f-d132-43e1-b259-d5b430752553",
"walletName": "A test wallet",
"m": 2,
"n": 2,
"walletPrivKey": "Kz4CFSTgLzoYfMkt97BTBotUbZYXjMts6Ej9HbVfCf5oLmun1BXy",
"personalEncryptingKey": "1fgFP/uoLhVxJiMXOWQznA==",
"sharedEncryptingKey": "FZIY4+p4TfBAKRclKtrROw==",
"copayerName": "me",
"mnemonic": "dizzy cycle skirt decrease exotic fork sure mixture hair vapor copper hero",
"entropySource": "79e60ad83e04ee40967147fd6ac58f986c7dcf6c82b125fb4e8c30ff9f9584ee",
"mnemonicHasPassphrase": false,
"derivationStrategy": "BIP44",
"account": 0,
"addressType": "P2SH"
};
var testnet1of1Wallet = {
"network": "testnet",
"xPrivKey": "tprv8ZgxMBicQKsPdK35ubrjCCpPCaBZA7QyKtxNNDWvYyjDAhtxV1HVNLzqwntAJ5QH1RTksRSfbuHLUYvMdGFmy9vHCb4yDRAR2zKqmX8mVa8",
"xPubKey": "tpubDDN7B6QnxsbomkZfPFRj6CVtC7LVh6ufoTpvzHfutjiHbu4hmiEGYDzxo5mgfqkQkBuwZPFkTYLNmQeLg7eFvdb4SFH1LW35sQD6xfymmRP",
"requestPrivKey": "aa39d4d780ad7ec36e26cbd0c0250bce85dfdd8aa7f2222ec7c86d6d62f242d7",
"requestPubKey": "038bb7cc1238280e893dd6949bfce770a319892b3c9045112ec7810191d4157ced",
"copayerId": "5c474b568bde8cd39efe069cd6aff2a80ab1cb18d3b9ae81f8225286f94856bc",
"publicKeyRing": [{
"xPubKey": "tpubDDN7B6QnxsbomkZfPFRj6CVtC7LVh6ufoTpvzHfutjiHbu4hmiEGYDzxo5mgfqkQkBuwZPFkTYLNmQeLg7eFvdb4SFH1LW35sQD6xfymmRP",
"requestPubKey": "038bb7cc1238280e893dd6949bfce770a319892b3c9045112ec7810191d4157ced"
}],
"walletId": "66d3afc9-7d76-4b25-850e-aa62fcc53a7d",
"walletName": "kk",
"m": 1,
"n": 1,
"walletPrivKey": "1d6eb8e5a9f8944e97c2f13423c137ce912fac00f7eb5b3ffe6e3c161ea98bf7",
"personalEncryptingKey": "A2dQiAwpFY2xwIhE26ClFQ==",
"sharedEncryptingKey": "z0BtAIFclGQMH6eHqK9e3w==",
"copayerName": "me",
"mnemonic": "cheese where alarm job conduct donkey license pave congress pepper fence current",
"entropySource": "5c84e65837c0fbd11db935953dbacb60f5c33f40ecfe95e0feded1f62a5ee15d",
"mnemonicHasPassphrase": false,
"derivationStrategy": "BIP44",
"account": 0,
"addressType": "P2PKH"
};
var PROFILE = {
incomplete2of2: {
credentials: [incomplete2of2Wallet],
createdOn: 1463519749,
disclaimerAccepted: true,
},
testnet1of1: {
credentials: [testnet1of1Wallet],
createdOn: 1463519749,
disclaimerAccepted: true,
},
};

View File

@ -1,24 +0,0 @@
describe('walletService', function() {
var walletService;
// Adds walletService's module dependencies
beforeEach(function() {
module('ngLodash');
module('gettext');
module('angularMoment');
module('bwcModule');
module('copayApp.services');
});
beforeEach(inject(function(_walletService_) {
walletService = _walletService_;
}));
it('should be defined', function() {
should.exist(walletService);
});
});

View File

@ -8,7 +8,7 @@
#define MyAppExeName "*PACKAGENAME.exe"
[Setup]
AppId={{804636ee-b017-4cad-8719-e58ac97ffa5c}
AppId={804636ee-b017-4cad-8719-e58ac97ffa5c}
AppName={#MyAppName}
AppVersion={#MyAppVersion}
;AppVerName={#MyAppName} {#MyAppVersion}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.3 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.5 KiB

After

Width:  |  Height:  |  Size: 8.0 KiB

BIN
www/img/icon-128.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.9 KiB

View File

@ -1,4 +1,4 @@
<ion-view id="add-address">
<ion-view id="add-address" hide-tabs>
<ion-nav-bar class="bar-royal">
<ion-nav-title>
<span translate>Add Contact</span>

View File

@ -1,4 +1,4 @@
<ion-view id="view-address-book">
<ion-view id="view-address-book" hide-tabs>
<ion-nav-bar class="bar-royal">
<ion-nav-back-button>
</ion-nav-back-button>
@ -24,7 +24,7 @@
</div>
</ion-content>
<ion-content class="ng-hide" ng-show="!isEmptyList">
<div class="bar bar-header item-input-inset" ng-show="!isEmptyList">
<div class="bar bar-header item-input-inset" ng-show="!isEmptyList && addressbook.length >= 5">
<label class="item-input-wrapper">
<i class="icon ion-ios-search placeholder-icon"></i>
<input type="search"
@ -36,7 +36,7 @@
<ion-list>
<ion-item ng-repeat="addrEntry in addressbook"
class="item-icon-right item-avatar"
ui-sref="tabs.addressbook.view({address:addrEntry.address})">
ui-sref="tabs.addressbook.view({address:addrEntry.address, email: addrEntry.email, name: addrEntry.name})">
<gravatar name="{{addrEntry.name}}" width="50" email="{{addrEntry.email}}"></gravatar>
<h2>{{addrEntry.name}}</h2>
<p>{{addrEntry.address}}</p>

View File

@ -1,4 +1,4 @@
<ion-view id="address-book-view">
<ion-view id="address-book-view" hide-tabs>
<ion-nav-bar class="bar-royal">
<ion-nav-back-button>
</ion-nav-back-button>

View File

@ -1,4 +1,4 @@
<ion-view id="view-amount">
<ion-view id="view-amount" hide-tabs>
<ion-nav-bar class="bar-royal">
<ion-nav-title>
{{'Enter Amount'|translate}}
@ -12,15 +12,17 @@
<div>
<div class="item item-no-bottom-border" translate>Recipient</div>
<div class="item item-text-wrap item-icon-left bitcoin-address" ng-class="{'item-big-icon-left':isCard}">
<i ng-if="isWallet" class="icon ion-briefcase size-21"></i>
<div class="item item-text-wrap item-icon-left bitcoin-address" ng-class="{'item-big-icon-left':cardId}">
<i class="icon big-icon-svg" ng-if="isWallet">
<img src="img/icon-wallet.svg" ng-style="{'background-color': toColor}" class="bg"/>
</i>
<span ng-if="!isWallet">
<gravatar ng-if="!isCard" class="send-gravatar" name="{{toName}}" width="30" email="{{toEmail}}"></gravatar>
<i ng-if="isCard" class="icon big-icon-svg">
<gravatar ng-if="!cardId" class="send-gravatar" name="{{toName}}" width="30" email="{{toEmail}}"></gravatar>
<i ng-if="cardId" class="icon big-icon-svg">
<div class="bg icon-bitpay-card"></div>
</i>
</span>
<span ng-class="{'m10l':isCard}">{{toName || toAddress}}</span>
<span class="m10l">{{toName || toAddress}}</span>
</div>
</div>

View File

@ -1,4 +1,4 @@
<ion-view id="wallet-backup-phrase">
<ion-view id="wallet-backup-phrase" hide-tabs>
<ion-nav-bar class="bar-royal">
<ion-nav-title>
{{viewTitle}}

View File

@ -1,4 +1,4 @@
<ion-view id="backup-warning" class="onboarding" ng-controller="backupWarningController">
<ion-view id="backup-warning" class="onboarding" ng-controller="backupWarningController" hide-tabs>
<ion-nav-bar>
<ion-nav-buttons side="primary">
<button class="button button-back button-clear" ng-click="goBack()">

View File

@ -2,9 +2,9 @@
<ion-nav-bar class="bar-royal">
<ion-nav-back-button>
</ion-nav-back-button>
<ion-nav-title>BitPay Card</ion-nav-title>
<ion-nav-buttons side="secondary" ng-show="bitpayCard.bitpayCardAuthenticated">
<button class="button back-button" ui-sref="tabs.bitpayCard.preferences">
<ion-nav-title>BitPay Visa&reg; Card</ion-nav-title>
<ion-nav-buttons side="secondary">
<button class="button back-button" ng-show="!error" ui-sref="tabs.bitpayCard.preferences">
<i class="icon ion-ios-gear-outline"></i>
</button>
</ion-nav-buttons>
@ -16,145 +16,77 @@
Sandbox version. Only for testing purpose
</div>
<div class="m20t text-center size-12 text-gray" ng-show="!bitpayCard.bitpayCardAuthenticated && bitpayCard.loadingSession">
Loading...
</div>
<div ng-show="!bitpayCard.bitpayCardAuthenticated && !bitpayCard.loadingSession">
<div class="text-center m20t">
<img src="img/bitpay-card-visa.svg" width="200">
<div class="oh pr" ng-show="!error">
<div class="amount">
<div ng-if="bitpayCard.bitpayCardCurrentBalance" ng-click="bitpayCard.update()">
<div class="size-36 m20b">${{bitpayCard.bitpayCardCurrentBalance}}</div>
<a class="button button-positive button-small"
ui-sref="tabs.bitpayCard.amount({'cardId': cardId, 'toName': 'BitPay Card'})" translate>
Add Funds
</a>
</div>
<h4 class="text-center">
<span ng-show="!bitpayCard.bitpayCardTwoFactorPending">Login to your account</span>
<span ng-show="bitpayCard.bitpayCardTwoFactorPending">2-Step Verification</span>
</h4>
<form
ng-show="!bitpayCard.bitpayCardTwoFactorPending"
name="authenticateForm"
ng-submit="bitpayCard.authenticate(email, password)"
novalidate>
<div class="card list">
<label class="item item-input item-stacked-label">
<span class="input-label">Email</span>
<input name="email"
type="email"
ng-model="email"
ng-disabled="bitpayCard.authenticating"
required>
</label>
<label class="item item-input item-stacked-label">
<span class="input-label">Password</span>
<input name="password"
type="password"
ng-model="password"
ng-disabled="bitpayCard.authenticating"
required>
</label>
</div>
<input class="button button-block button-positive"
type="submit"
ng-disabled="!authenticateForm.$valid || bitpayCard.authenticating"
value="Login">
</form>
<p ng-show="bitpayCard.bitpayCardTwoFactorPending" class="size-12 text-center text-gray">
Enter the verification code generated by the authenticator app on your phone.
</p>
<form
ng-show="bitpayCard.bitpayCardTwoFactorPending"
name="authenticate2FAForm"
ng-submit="bitpayCard.authenticate2FA(twoFactorCode)"
novalidate>
<div class="list">
<label class="item item-input item-stacked-label">
<span class="input-label">Verification Code</span>
<input name="twoFactorCode"
type="text"
ng-model="twoFactorCode"
ng-disabled="bitpayCard.authenticating"
required>
</label>
</div>
<input class="button button-block button-positive"
type="submit"
ng-disabled="!authenticate2FAForm.$valid || bitpayCard.authenticating"
value="Login">
</form>
</div>
<div ng-show="bitpayCard.bitpayCardAuthenticated">
<div class="oh pr">
<div class="amount">
<div ng-if="!loadingHistory && bitpayCard.bitpayCardCurrentBalance" ng-click="bitpayCard.update()">
<div class="size-36 m20b">${{bitpayCard.bitpayCardCurrentBalance}}</div>
<a class="button button-positive button-small" ui-sref="tabs.bitpayCard.amount({'isCard': true, 'toName': 'BitPay Card'})">
<i class="icon ion-ios-plus-empty"></i> {{'Add Funds'|translate}}
</a>
</div>
<div ng-if="loadingHistory" class="m10t">
<strong class="size-36">...</strong>
</div>
<div ng-if="!bitpayCard.bitpayCardCurrentBalance" class="m10t">
<strong class="size-36">...</strong>
</div>
</div>
<div class="wallet-details-wallet-info">
<img style="height:0.6em" ng-show="loadingHistory" src="img/icon-sync-white.svg">
</div>
</div>
<div ng-show="error" class="text-center m10t assertive">
{{error}}
</div>
<div
class="m10t text-center padding"
ng-if="!loadingHistory && !bitpayCard.bitpayCardTransactionHistory[0] && !error">
<i class="icon ion-ios-arrow-thin-up size-24"></i>
<h1>Get started</h1>
<h4>Your BitPay Card is ready. Add funds to your card to start using your card at stores and ATMs worldwide.</h4>
</div>
<div class="list" ng-if="bitpayCard.bitpayCardTransactionHistory[0] && !error">
<div class="item item-divider">
<select class="select-style" ng-model="dateRange.value" ng-change="bitpayCard.update()">
<option value="last30Days">Recent Activity</option>
<option value="lastMonth">Last Month</option>
<option value="all">All Activity</option>
</select>
</div>
<div
class="m10t text-center padding"
ng-show="!bitpayCard.bitpayCardTransactionHistory[0] &&
!bitpayCard.bitpayCardInvoiceHistory[0] && (!loadingHistory || !bitpayCardCached)">
<i class="icon ion-ios-arrow-thin-up size-24"></i>
<h1>Get started</h1>
<h4>Your BitPay Card is ready. Add funds to your card to start using your card at stores and ATMs worldwide.</h4>
</div>
<div class="list" ng-if="bitpayCardCached">
<div class="item item-divider">
<select class="select-style" ng-model="dateRange" ng-change="bitpayCard.update(dateRange)">
<option value="last30Days">Recent Activity</option>
<option value="lastMonth">Last Month</option>
<option value="all">All Activity</option>
</select>
ng-repeat="tx in bitpayCard.bitpayCardTransactionHistory | orderBy: ['pending','-timestamp']"
class="item row">
<div class="col col-10">
<img class="m5t" ng-src="img/mcc-icons/{{tx.icon}}.svg" width="22">
</div>
<div
ng-repeat="tx in bitpayCard.bitpayCardTransactionHistory | orderBy: ['pending','-timestamp']"
class="item row"
ng-init="bitpayCard.getMerchantInfo(tx)">
<div class="col col-10" ng-init="icon = bitpayCard.getIconName(tx)">
<img class="m5t" ng-src="img/mcc-icons/{{icon}}.svg" width="22">
</div>
<div class="col">
<div class="size-12 text-bold">
{{tx.merchant.name}}
</div>
<div class="size-12">
{{tx.merchant.city}}, {{tx.merchant.state}}
</div>
<div class="col col-50">
<div class="size-12 text-bold">
{{tx.merchant.name}}
</div>
<div
ng-init="desc = bitpayCard.processDescription(tx)"
class="col size-12">
{{desc}}
<div class="size-12">
{{tx.merchant.city}}, {{tx.merchant.state}}
</div>
<div class="col">
<img ng-show="!tx.pending" ng-src="img/check.svg" width="14">
<img ng-show="tx.pending" ng-src="img/sync.svg" width="14">
</div>
<div class="col text-right size-12 text-gray">
<div class="size-14"
ng-class="{
'text-success': tx.amount.indexOf('-') == -1 && !tx.pending,
'text-gray': tx.amount.indexOf('-') == -1 && tx.pending}">
{{tx.amount | currency:'$':2 }}
</div>
<time>{{tx.timestamp | amTimeAgo}}</time>
</div>
<!--
<div class="col size-12">
{{tx.desc}}
</div>
-->
<div class="col col-20 text-center p10t">
<img ng-show="!tx.pending" ng-src="img/check.svg" width="14">
<img ng-show="tx.pending" ng-src="img/sync.svg" width="14">
</div>
<div class="col col-20 text-right size-12 text-gray">
<div class="size-14"
ng-class="{
'text-success': tx.amount.indexOf('-') == -1 && !tx.pending,
'text-gray': tx.amount.indexOf('-') == -1 && tx.pending}">
{{tx.amount | currency:'$':2 }}
</div>
<time ng-if="!tx.pending">{{tx.timestamp | amCalendar}}</time>
<span ng-if="tx.pending" class="tu" translate>Pending</span>
</div>
</div>
</div>

View File

@ -0,0 +1,45 @@
<ion-view id="bitpayCard-intro" hide-tabs>
<ion-nav-bar class="bar-stable">
<ion-nav-back-button>
</ion-nav-back-button>
<ion-nav-title></ion-nav-title>
<ion-nav-buttons side="secondary">
<button class="button back-button button-clear" ng-click="orderBitPayCard()">
<i class="icon ion-ios-information-outline"></i>
</button>
</ion-nav-buttons>
</ion-nav-bar>
<ion-content scroll="false">
<div class="text-center padding">
<img src="img/bitpay-card-visa.svg" width="100%">
</div>
<ion-slide-box>
<ion-slide>
<p translate>
Turn bitcoin into dollars, swipe anywhere Visa&reg; is accepted.
</p>
</ion-slide>
<ion-slide>
<p translate>
<span translate>Get local cash anywhere you go, from any Visa&reg;-compatible ATM.</span>
<div translate class="size-10 text-center">
*ATM bank fees may apply
</div>
</p>
</ion-slide>
<ion-slide>
<p translate>
Pay 0% fees to turn bitcoin into dollars.
</p>
</ion-slide>
</ion-slide-box>
<div class="cta-button">
<button class="button button-block button-primary" ng-click="orderBitPayCard()" translate>
Order the BitPay Card
</button>
<button class="button button-block button-transparent text-white m10t" ng-click="connectBitPayCard()" translate>
Connect my BitPay Card
</button>
</div>
</ion-content>
</ion-view>

View File

@ -105,7 +105,7 @@
</div>
<div class="m10t">
<button class="button button-positive"
ng-click="openExternalLink('https://www.amazon.com/gc/redeem?claimCode=' + buy.giftCard.claimCode, '_system')">
ng-click="openExternalLink('https://www.amazon.com/gc/redeem?claimCode=' + buy.giftCard.claimCode)">
Redeem Now
</button>
</div>

View File

@ -1,4 +1,4 @@
<ion-view id="view-confirm">
<ion-view id="view-confirm" hide-tabs>
<ion-nav-bar class="bar-royal">
<ion-nav-title>
{{'Confirm'|translate}}
@ -22,9 +22,22 @@
<div class="info">
<div class="item">
<span class="label" translate>To</span>
<span class="payment-proposal-to" copy-to-clipboard="toAddress">
<img src="img/icon-bitcoin-small.svg">
<contact class="ellipsis" address="{{toAddress}}">{{toAddress}}</contact>
<span class="payment-proposal-to">
<img ng-if="!cardId" src="img/icon-bitcoin-small.svg">
<img ng-if="cardId" src="img/icon-card.svg" width="34">
<contact ng-if="!toName && !_paypro" class="ellipsis" address="{{toAddress}}" copy-to-clipboard="toAddress">
{{toAddress}}
</contact>
<span class="m15l size-14" ng-if="toName && !_paypro" copy-to-clipboard="toAddress">{{toName}}</span>
<div ng-if="_paypro" ng-click="openPPModal(_paypro)" class="m15l size-14 w100p pointer">
<i ng-show="_paypro.verified && _paypro.caTrusted" class="ion-locked" style="color:green"></i>
<i ng-show="!_paypro.caTrusted" class="ion-unlocked" style="color:red"></i>
<span class="ellipsis" ng-show="!toName">{{_paypro.domain || _paypro.toAddress}}</span>
<span ng-show="toName">{{toName}}</span>
</div>
<!-- <contact ng-if="!tx.hasMultiplesOutputs" class="ellipsis" address="{{toAddress}}"></contact>
<span ng-if="tx.hasMultiplesOutputs" translate>Multiple recipients</span> -->
</span>
@ -42,20 +55,19 @@
</div>
<i class="icon bp-arrow-right"></i>
</a>
<a class="item single-line item-icon-right" ng-hide="insuffientFunds">
<span class="label" translate>{{'Add Memo'|translate}}</span>
<span class="item-note">
<a class="item single-line item-icon-right" ng-hide="insuffientFunds" ng-click="showDescriptionPopup()">
<span class="label" translate>Add Memo</span>
<span class="item-note m10l">
{{description}}
</span>
<i class="icon bp-arrow-right"></i>
</a>
<a class="item single-line item-icon-right" ng-hide="insuffientFunds">
<span class="label" translate>Fee</span>
<div class="item single-line" ng-hide="insuffientFunds">
<span class="label" translate>Fee: {{feeLevel}}</span>
<span class="item-note">
{{fee}}
{{fee || '...'}}
</span>
<i class="icon bp-arrow-right"></i>
</a>
</div>
</div>
</div>
<click-to-accept

View File

@ -1,21 +1,21 @@
<ion-view>
<ion-view id="export">
<ion-nav-bar class="bar-royal">
<ion-nav-title>{{'Export wallet' | translate}}</ion-nav-title>
<ion-nav-back-button>
</ion-nav-back-button>
</ion-nav-bar>
<ion-content ng-init="file = true">
<div class="row text-center">
<div class="col" ng-click="file = true" ng-style="file && {'border-bottom': '2px solid'}">
<ion-content>
<div class="row text-center top-tabs">
<div class="col" ng-click="file.value = true" ng-style="file.value && {'border-bottom-style': 'solid'}">
<span class="" translate>File/Text</span>
</div>
<div class="col" ng-click="file = false" ng-style="!file && {'border-bottom': '2px solid'}">
<div class="col" ng-click="generateQrCode();" ng-style="!file.value && {'border-bottom-style': 'solid'}">
<span class="" translate>QR Code</span>
</div>
</div>
<div ng-include="'views/tab-export-file.html'" ng-if="file"></div>
<div ng-include="'views/tab-export-qrCode.html'" ng-if="!file"></div>
<div ng-include="'views/tab-export-file.html'" ng-if="file.value"></div>
<div ng-include="'views/tab-export-qrCode.html'" ng-if="!file.value"></div>
</ion-content>
</ion-view>

View File

@ -1,4 +1,4 @@
<ion-view>
<ion-view id="glidera">
<ion-nav-bar class="bar-royal">
<ion-nav-back-button>
</ion-nav-back-button>
@ -22,22 +22,21 @@
<img src="img/glidera-logo.png" ng-click="update(token, permissions)" width="200">
</div>
<div class="text-center small-10 small-centered columns" ng-show="!showOauthForm">
<p class="m10b">You can buy and sell Bitcoin with a US bank account directly in Copay.</p>
<p class="glidera-lead">You can buy and sell Bitcoin with a US bank account directly in this app.</p>
<p class="m20t padding text-gray size-12 text-left">
<p class="disclosure">
DISCLOSURE.<br>
Glidera Inc. (Glidera) is providing the service of buying or selling bitcoins to Copay users. To enable this
Glidera Inc. (Glidera) is providing the service of buying or selling bitcoin to BitPay users. To enable this
service, Glidera has registered with US Treasury Departments FinCEN as a Money Service Business
(#31000042625755). Users of Copay must agree to the service agreement presented by Glidera prior to obtaining
(#31000042625755). Users of BitPay must agree to the service agreement presented by Glidera prior to obtaining
Glideras service of buying or selling bitcoins. Service available in U.S. and Canada only. In U.S. (buy & sell) CA, GA, IL, KS,
MA, MD, MO, MT, MN, SC, TX, AZ, CO, DE, ME, NJ, PA, TN, UT, NV, WI. In Canada (buy & sell) AB, BC, MB, NB, NL, NS, NT, NU,
ON, PE, SK, YT.
</p>
<p class="m20t text-gray size-12">Connect your Glidera account to get started</p>
<button class="button button-standard button-primary"
ng-click="openExternalLink(glidera.getAuthenticateUrl(), '_system'); showOauthForm = true">
ng-click="openExternalLink(glidera.getAuthenticateUrl()); showOauthForm = true">
Connect to Glidera
</button>
<div class="m10t">
@ -96,8 +95,7 @@
<h4 class="text-bold"> Complete Setup</h4>
<div>Your Glidera account is not ready to transact. Please, verify it at <b>Glidera.io</b></div>
<a class="button"
ng-init="glideraUrl = network == 'testnet' ? 'https://sandbox.glidera.io/login' :
'https://glidera.io/login'"
ng-init="glideraUrl = network == 'testnet' ? 'https://sandbox.glidera.io/login' : 'https://glidera.io/login'"
ng-click="openExternalLink(glideraUrl)">
Go to Glidera
</a>

View File

@ -1,4 +1,4 @@
<ion-view ng-controller="tabsController" ng-init="importInit()" class="settings">
<ion-view id="import" ng-controller="tabsController" ng-init="importInit()" class="settings">
<ion-nav-bar class="bar-royal">
<ion-nav-title>{{'Import Wallet' | translate}}</ion-nav-title>
<ion-nav-back-button>
@ -6,14 +6,17 @@
</ion-nav-bar>
<ion-content ng-controller="importController" ng-init="phrase = true; init()">
<div class="row text-center">
<div class="col" ng-click="phrase = true; file = hardware = false; showAdv = false" ng-style="phrase && {'border-bottom': '2px solid'}">
<div class="row text-center top-tabs">
<div class="col" ng-click="phrase = true; file = hardware = false; showAdv = false" ng-style="phrase &&
{'border-bottom-style': 'solid'}">
<span translate>Recovery phrase</span>
</div>
<div class="col" ng-click="file = true; phrase = hardware = false; showAdv = false" ng-style="file && {'border-bottom': '2px solid'}">
<div class="col" ng-click="file = true; phrase = hardware = false; showAdv = false" ng-style="file &&
{'border-bottom-style': 'solid'}">
<span translate>File/Text</span>
</div>
<div class="col" ng-click="hardware = true; phrase = file = false; showAdv = false" ng-style="hardware && {'border-bottom': '2px solid'}">
<div class="col" ng-click="hardware = true; phrase = file = false; showAdv = false" ng-style="hardware &&
{'border-bottom-style': 'solid'}">
<span translate>Hardware wallet</span>
</div>
</div>

View File

@ -1,14 +1,17 @@
<li class="p10 oh" copy-to-clipboard="output.toAddress">
<div class="item" copy-to-clipboard="output.toAddress">
<span class="text-gray" translate>To</span>:
<span class="right enable_text_select">{{output.toAddress || output.address}}</span>
</li>
<li class="p10" copy-to-clipboard="output.amountStr">
</div>
<div class="item" copy-to-clipboard="output.amountStr">
<span class="text-gray" translate>Amount</span>:
<span class="right enable_text_select">{{output.amountStr}}
<span ng-show="output.alternativeAmountStr" class="label gray radius">{{output.alternativeAmountStr}}</span>
<span ng-show="output.alternativeAmountStr">({{output.alternativeAmountStr}})</span>
</span>
</li>
<li class="p10 oh" copy-to-clipboard="output.message">
</div>
<div class="item" copy-to-clipboard="output.message" ng-show-"output.message">
<span class="text-gray" translate>Note</span>:
<span class="right enable_text_select">{{output.message}}</span>
</li>
</div>

View File

@ -23,7 +23,7 @@
</div>
<div class="m10t" ng-show="card.cardStatus == 'Fulfilled'">
<button class="button button-positive"
ng-click="openExternalLink('https://www.amazon.com/gc/redeem?claimCode=' + card.claimCode, '_system')">
ng-click="openExternalLink('https://www.amazon.com/gc/redeem?claimCode=' + card.claimCode)">
Redeem Now
</button>
</div>

View File

@ -10,7 +10,7 @@
<ion-content class="has-header" scroll="false">
<ion-scroll ng-include="'views/includes/terms.html'" direction="y" ng-style="{'height': '60%'}"></ion-scroll>
<div id="agree-to-terms">
<a ng-click="openExternalLink('https://copay.io/disclaimer')" ng-show="lang != 'en'" translate>Official English Disclaimer</a>
<a ng-click="openExternalLink('https://copay.io/disclaimer', true, 'View Terms of Service', 'The official English Terms of Service are available on the BitPay website. Would you like to view them?', 'Open Website', 'Go Back')" ng-show="lang != 'en'" translate>Official English Disclaimer</a>
<ion-checkbox ng-model="terms.accept3"></ion-checkbox>
<p translate>I have read, understood, and agree with the Terms of use.</p>
<button ng-disabled="!terms.accept3" class="button button-block button-positive" ng-click="termsModal.hide(); confirm()" translate>Confirm & Finish</button>

Some files were not shown because too many files have changed in this diff Show More