fix merge conflicts

This commit is contained in:
Marty Alcala 2016-10-27 11:44:03 -04:00
commit d4513ba46b
53 changed files with 264 additions and 205 deletions

View File

@ -1,13 +1,12 @@
sudo: required
dist: trusty
language: node_js language: node_js
node_js: node_js:
- '4.0' - '6'
before_install: before_install:
- npm install -g bower
- npm install -g grunt-cli
- export DISPLAY=:99.0 - export DISPLAY=:99.0
- sh -e /etc/init.d/xvfb start - sh -e /etc/init.d/xvfb start
install: install:
- bower install - npm run apply:bitpay
- npm install # after_success:
after_success: # - npm run exec:coveralls
- npm run exec:coveralls

View File

@ -1,9 +1,9 @@
<img src="https://raw.githubusercontent.com/bitpay/copay/master/public/img/logo.png" alt="Copay" width="300"> <img src="https://raw.githubusercontent.com/bitpay/copay/master/resources/copay/android/icon/drawable-xxxhdpi-icon.png" alt="Copay" width="79">
[![Build Status](https://secure.travis-ci.org/bitpay/copay.svg)](http://travis-ci.org/bitpay/copay) [![Build Status](https://secure.travis-ci.org/bitpay/copay.svg)](http://travis-ci.org/bitpay/copay)
[![Crowdin](https://d322cqt584bo4o.cloudfront.net/copay/localized.png)](https://crowdin.com/project/copay) [![Crowdin](https://d322cqt584bo4o.cloudfront.net/copay/localized.png)](https://crowdin.com/project/copay)
Copay is an easy-to-use, open-source, multiplatform, multisignature, secure bitcoin wallet platform for both individuals and companies. Copay uses [Bitcore Wallet Service](https://github.com/bitpay/bitcore-wallet-service) (BWS) for peer synchronization and network interfacing. Copay is a secure bitcoin wallet platform for both desktop and mobile devices. Copay uses [Bitcore Wallet Service](https://github.com/bitpay/bitcore-wallet-service) (BWS) for peer synchronization and network interfacing.
Binary versions of Copay are available for download at [Copay.io](https://copay.io/#download). Copay Binaries are signed with the key `copay@bitpay.com` See the section [`How to Verify Copay Signatures`](https://github.com/bitpay/copay#how-to-verify-copay-signatures) for details. Binary versions of Copay are available for download at [Copay.io](https://copay.io/#download). Copay Binaries are signed with the key `copay@bitpay.com` See the section [`How to Verify Copay Signatures`](https://github.com/bitpay/copay#how-to-verify-copay-signatures) for details.

View File

@ -116,6 +116,9 @@ mkdir /Volumes/"${VOL_NAME}"/.background
cp "${DMG_BACKGROUND_IMG}" /Volumes/"${VOL_NAME}"/.background/ cp "${DMG_BACKGROUND_IMG}" /Volumes/"${VOL_NAME}"/.background/
echo "Adding volume icon to disk image" echo "Adding volume icon to disk image"
# we install this here to avoid trying to install it on linux or windows, where
# it fails to install
npm install fileicon
# use fileicon node_module # use fileicon node_module
cp "${DMG_VOLUME_ICON}" /Volumes/"${VOL_NAME}"/.VolumeIcon.icns cp "${DMG_VOLUME_ICON}" /Volumes/"${VOL_NAME}"/.VolumeIcon.icns
`npm bin`/fileicon set /Volumes/"${VOL_NAME}"/ /Volumes/"${VOL_NAME}"/.VolumeIcon.icns `npm bin`/fileicon set /Volumes/"${VOL_NAME}"/ /Volumes/"${VOL_NAME}"/.VolumeIcon.icns

View File

@ -45,6 +45,10 @@
"bugs": { "bugs": {
"url": "*GITHUBREPOBUGS*" "url": "*GITHUBREPOBUGS*"
}, },
"engines": {
"node": "6",
"npm": "3"
},
"dependencies": { "dependencies": {
"adm-zip": "^0.4.7", "adm-zip": "^0.4.7",
"angular": "1.4.6", "angular": "1.4.6",
@ -103,7 +107,7 @@
"sign:android": "rm -f platforms/android/build/outputs/apk/android-release-signed-aligned.apk; jarsigner -verbose -sigalg SHA1withRSA -digestalg SHA1 -keystore ../copay.keystore -signedjar platforms/android/build/outputs/apk/android-release-signed.apk platforms/android/build/outputs/apk/android-release-unsigned.apk copay_play && ../android-sdk-macosx/build-tools/21.1.1/zipalign -v 4 platforms/android/build/outputs/apk/android-release-signed.apk platforms/android/build/outputs/apk/android-release-signed-aligned.apk", "sign:android": "rm -f platforms/android/build/outputs/apk/android-release-signed-aligned.apk; jarsigner -verbose -sigalg SHA1withRSA -digestalg SHA1 -keystore ../copay.keystore -signedjar platforms/android/build/outputs/apk/android-release-signed.apk platforms/android/build/outputs/apk/android-release-unsigned.apk copay_play && ../android-sdk-macosx/build-tools/21.1.1/zipalign -v 4 platforms/android/build/outputs/apk/android-release-signed.apk platforms/android/build/outputs/apk/android-release-signed-aligned.apk",
"apply:copay": "npm i && cd app-template && node apply.js copay && cordova prepare", "apply:copay": "npm i && cd app-template && node apply.js copay && cordova prepare",
"apply:bitpay": "npm i && cd app-template && node apply.js bitpay && cordova prepare", "apply:bitpay": "npm i && cd app-template && node apply.js bitpay && cordova prepare",
"test": "./node_modules/.bin/grunt test-coveralls", "test": "echo \"no package tests configured\"",
"clean": "trash platforms && trash plugins && cordova prepare", "clean": "trash platforms && trash plugins && cordova prepare",
"unstage-package": "git reset package.json", "unstage-package": "git reset package.json",
"clean-all": "git clean -dfx" "clean-all": "git clean -dfx"
@ -114,7 +118,6 @@
"ionic": "^2.1.0", "ionic": "^2.1.0",
"trash-cli": "^1.4.0", "trash-cli": "^1.4.0",
"lodash": "^4.3.0", "lodash": "^4.3.0",
"fileicon": "^0.1.8",
"pre-commit": "^1.1.3" "pre-commit": "^1.1.3"
}, },
"pre-commit": "unstage-package" "pre-commit": "unstage-package"

View File

Before

Width:  |  Height:  |  Size: 373 KiB

After

Width:  |  Height:  |  Size: 373 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 195 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 139 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 108 KiB

View File

@ -2,9 +2,11 @@
# export all slices marked for export to the proper directory # export all slices marked for export to the proper directory
echo "Exporting all assets from src.sketch..." echo "Exporting all assets from src.sketch..."
# sketchtool is installed by install.sh
sketchtool export layers src.sketch sketchtool export layers src.sketch
function postprocess { function postprocess {
# $1 = distribution name
echo "Beginning postprocessing for $1..." echo "Beginning postprocessing for $1..."
echo "Postprocessing assets for macOS..." echo "Postprocessing assets for macOS..."
@ -15,6 +17,14 @@ function postprocess {
tiffutil -cathidpicheck $1/mac/dmg-background.png $1/mac/dmg-background@2x.png -out $1/mac/dmg-background.tiff tiffutil -cathidpicheck $1/mac/dmg-background.png $1/mac/dmg-background@2x.png -out $1/mac/dmg-background.tiff
echo "Removing raw background pngs..." echo "Removing raw background pngs..."
rm $1/mac/dmg-background.png $1/mac/dmg-background@2x.png rm $1/mac/dmg-background.png $1/mac/dmg-background@2x.png
echo "Postprocessing assets for Windows..."
echo "Combining windows/ico pngs into a single ICO file..."
# convert ships with imagemagick
convert $1/windows/ico/ico_16x16.png $1/windows/ico/ico_24x24.png $1/windows/ico/ico_32x32.png $1/windows/ico/ico_48x48.png $1/windows/ico/ico_64x64.png $1/windows/ico/ico_128x128.png $1/windows/ico/ico_256x256.png $1/windows/icon.ico
echo "Removing raw windows/ico pngs..."
rm -r $1/windows/ico/* && rmdir $1/windows/ico
} }
function iconset { function iconset {

View File

@ -1,2 +1,8 @@
echo "Installing sketchtool... (this requires Sketch.app)"
# This installs sketchtool: https://www.sketchapp.com/tool/ # This installs sketchtool: https://www.sketchapp.com/tool/
sh /Applications/Sketch.app/Contents/Resources/sketchtool/install.sh sh /Applications/Sketch.app/Contents/Resources/sketchtool/install.sh
echo "Installing imagemagick... (This requires Homebrew)"
# This requires Homebrew: http://brew.sh/
brew install imagemagick
# imagemagick provides the `convert` utility, used to generate ICO files

View File

@ -4,9 +4,9 @@ This directory contains a `src.sketch` file from which all other assets are expo
## Requirements ## Requirements
You'll need [Sketch](https://www.sketchapp.com/) to make any changes to this directory. You'll need [Sketch](https://www.sketchapp.com/) to make any changes to this directory. You'll also need to have [Homebrew](http://brew.sh/) installed to regenerate all assets.
Sketch is only available for macOS, and several processes in `generate.sh` require utilities that ship with the OS, so this process must be performed on macOS. Sketch and Homebrew are only available for macOS, and several processes in `generate.sh` require utilities that ship with macOS, so this process requires macOS.
## Install sketchtool ## Install sketchtool

Binary file not shown.

View File

@ -39,7 +39,7 @@ angular.module('copayApp.controllers').controller('activityController',
ongoingProcess.set('loadingTxInfo', false); ongoingProcess.set('loadingTxInfo', false);
if (err) { if (err) {
$log.warn('No txp found'); $log.warn('No txp found');
return popupService.showAlert(null, gettextCatalog.getString('Transaction not found')); return popupService.showAlert(gettextCatalog.getString('Error'), gettextCatalog.getString('Transaction not found'));
} }
txpModalService.open(_txp); txpModalService.open(_txp);
}); });
@ -61,7 +61,7 @@ angular.module('copayApp.controllers').controller('activityController',
if (!tx) { if (!tx) {
$log.warn('No tx found'); $log.warn('No tx found');
return popupService.showAlert(null, gettextCatalog.getString('Transaction not found')); return popupService.showAlert(gettextCatalog.getString('Error'), gettextCatalog.getString('Transaction not found'));
} }
$scope.wallet = wallet; $scope.wallet = wallet;

View File

@ -47,7 +47,7 @@ angular.module('copayApp.controllers').controller('addressbookListController', f
$timeout(function() { $timeout(function() {
addressbookService.remove(addr, function(err, ab) { addressbookService.remove(addr, function(err, ab) {
if (err) { if (err) {
popupService.showAlert(err); popupService.showAlert(gettextCatalog.getString('Error'), err);
return; return;
} }
initAddressbook(); initAddressbook();
@ -56,7 +56,7 @@ angular.module('copayApp.controllers').controller('addressbookListController', f
}, 100); }, 100);
}; };
$scope.$on("$ionicView.beforeEnter", function(event, data){ $scope.$on("$ionicView.beforeEnter", function(event, data) {
initAddressbook(); initAddressbook();
}); });

View File

@ -27,7 +27,7 @@ angular.module('copayApp.controllers').controller('addressbookAddController', fu
$timeout(function() { $timeout(function() {
addressbookService.add(addressbook, function(err, ab) { addressbookService.add(addressbook, function(err, ab) {
if (err) { if (err) {
popupService.showAlert(err); popupService.showAlert(gettextCatalog.getString('Error'), err);
return; return;
} }
if ($scope.fromSendTab) $scope.goHome(); if ($scope.fromSendTab) $scope.goHome();

View File

@ -12,7 +12,7 @@ angular.module('copayApp.controllers').controller('amazonController',
var initAmazon = function() { var initAmazon = function() {
amazonService.getPendingGiftCards(function(err, gcds) { amazonService.getPendingGiftCards(function(err, gcds) {
if (err) { if (err) {
popupService.showAlert(err); popupService.showAlert(gettextCatalog.getString('Error'), err);
return; return;
} }
$scope.giftCards = lodash.isEmpty(gcds) ? null : gcds; $scope.giftCards = lodash.isEmpty(gcds) ? null : gcds;
@ -31,7 +31,7 @@ angular.module('copayApp.controllers').controller('amazonController',
$log.debug("creating gift card"); $log.debug("creating gift card");
amazonService.createGiftCard(dataFromStorage, function(err, giftCard) { amazonService.createGiftCard(dataFromStorage, function(err, giftCard) {
if (err) { if (err) {
popupService.showAlert(bwcError.msg(err)); popupService.showAlert(gettextCatalog.getString('Error'), bwcError.msg(err));
return; return;
} }
if (giftCard.status != 'PENDING') { if (giftCard.status != 'PENDING') {
@ -51,7 +51,7 @@ angular.module('copayApp.controllers').controller('amazonController',
$log.debug("Saving new gift card"); $log.debug("Saving new gift card");
amazonService.getPendingGiftCards(function(err, gcds) { amazonService.getPendingGiftCards(function(err, gcds) {
if (err) { if (err) {
popupService.showAlert(err); popupService.showAlert(gettextCatalog.getString('Error'), err);
return; return;
} }
$scope.giftCards = gcds; $scope.giftCards = gcds;

View File

@ -131,7 +131,6 @@ angular.module('copayApp.controllers').controller('backupController',
} }
profileService.setBackupFlag(wallet.credentials.walletId); profileService.setBackupFlag(wallet.credentials.walletId);
profileService.setBackupNeededModalFlag(wallet.credentials.walletId);
return cb(); return cb();
}, 1); }, 1);
}; };

View File

@ -4,12 +4,14 @@ angular.module('copayApp.controllers').controller('bitpayCardController', functi
var self = this; var self = this;
var runningBalance; var runningBalance;
$scope.dateRange = { value: 'last30Days'}; $scope.dateRange = {
value: 'last30Days'
};
$scope.network = bitpayCardService.getEnvironment(); $scope.network = bitpayCardService.getEnvironment();
var updateHistoryFromCache = function(cb) { var updateHistoryFromCache = function(cb) {
bitpayCardService.getBitpayDebitCardsHistory($scope.cardId, function(err, data) { bitpayCardService.getBitpayDebitCardsHistory($scope.cardId, function(err, data) {
if (err || lodash.isEmpty(data)) return cb(); if (err ||  lodash.isEmpty(data)) return cb();
$scope.historyCached = true; $scope.historyCached = true;
self.bitpayCardTransactionHistory = data.transactions; self.bitpayCardTransactionHistory = data.transactions;
self.bitpayCardCurrentBalance = data.balance; self.bitpayCardCurrentBalance = data.balance;
@ -19,8 +21,8 @@ angular.module('copayApp.controllers').controller('bitpayCardController', functi
var setDateRange = function(preset) { var setDateRange = function(preset) {
var startDate, endDate; var startDate, endDate;
preset = preset || 'last30Days'; preset = preset ||  'last30Days';
switch(preset) { switch (preset) {
case 'last30Days': case 'last30Days':
startDate = moment().subtract(30, 'days').toISOString(); startDate = moment().subtract(30, 'days').toISOString();
endDate = moment().toISOString(); endDate = moment().toISOString();
@ -138,7 +140,7 @@ angular.module('copayApp.controllers').controller('bitpayCardController', functi
disableAnimate: true disableAnimate: true
}); });
$state.go('tabs.home'); $state.go('tabs.home');
popupService.showAlert(null, msg); popupService.showAlert(gettextCatalog.getString('Error'), msg);
} else { } else {
updateHistoryFromCache(function() { updateHistoryFromCache(function() {
self.update(); self.update();
@ -147,4 +149,3 @@ angular.module('copayApp.controllers').controller('bitpayCardController', functi
}); });
}); });

View File

@ -24,11 +24,13 @@ angular.module('copayApp.controllers').controller('bitpayCardIntroController', f
obj.otp = otp; obj.otp = otp;
bitpayCardService.bitAuthPair(obj, function(err, data) { bitpayCardService.bitAuthPair(obj, function(err, data) {
if (err) { if (err) {
popupService.showAlert(null, err); popupService.showAlert(gettextCatalog.getString('Error'), err);
return; return;
} }
var title = gettextCatalog.getString('Add BitPay Card Account?'); var title = gettextCatalog.getString('Add BitPay Card Account?');
var msg = gettextCatalog.getString('Would you like to add this account ({{email}}) to your wallet?', {email: obj.email}); var msg = gettextCatalog.getString('Would you like to add this account ({{email}}) to your wallet?', {
email: obj.email
});
var ok = gettextCatalog.getString('Add Account'); var ok = gettextCatalog.getString('Add Account');
var cancel = gettextCatalog.getString('Go back'); var cancel = gettextCatalog.getString('Go back');
popupService.showConfirm(title, msg, ok, cancel, function(res) { popupService.showConfirm(title, msg, ok, cancel, function(res) {
@ -43,7 +45,9 @@ angular.module('copayApp.controllers').controller('bitpayCardIntroController', f
}); });
$state.go('tabs.home').then(function() { $state.go('tabs.home').then(function() {
if (data.cards[0]) { if (data.cards[0]) {
$state.transitionTo('tabs.bitpayCard', {id: data.cards[0].id}); $state.transitionTo('tabs.bitpayCard', {
id: data.cards[0].id
});
} }
}); });
}); });

View File

@ -84,6 +84,9 @@ angular.module('copayApp.controllers').controller('confirmController', function(
$scope.insuffientFunds = true; $scope.insuffientFunds = true;
$log.warn('No wallet available to make the payment'); $log.warn('No wallet available to make the payment');
$timeout(function() {
$scope.$apply();
});
} }
} }
}); });
@ -99,7 +102,7 @@ angular.module('copayApp.controllers').controller('confirmController', function(
$timeout(function() { $timeout(function() {
$scope.$apply(); $scope.$apply();
}, 100); });
}; };
$scope.$on('accepted', function(event) { $scope.$on('accepted', function(event) {
@ -286,6 +289,7 @@ angular.module('copayApp.controllers').controller('confirmController', function(
}; };
$scope.approve = function(onSendStatusChange) { $scope.approve = function(onSendStatusChange) {
if ($scope.paypro && $scope.paymentExpired.value) { if ($scope.paypro && $scope.paymentExpired.value) {
popupService.showAlert(null, gettextCatalog.getString('The payment request has expired')); popupService.showAlert(null, gettextCatalog.getString('The payment request has expired'));
$scope.sendStatus = ''; $scope.sendStatus = '';

View File

@ -60,7 +60,7 @@ angular.module('copayApp.controllers').controller('importController',
}; };
if (info.type == 1 && info.hasPassphrase) if (info.type == 1 && info.hasPassphrase)
popupService.showAlert(gettextCatalog.getString('Password required. Make sure to enter your password in advanced options')); popupService.showAlert(gettextCatalog.getString('Error'), gettextCatalog.getString('Password required. Make sure to enter your password in advanced options'));
$scope.formData.derivationPath = info.derivationPath; $scope.formData.derivationPath = info.derivationPath;
$scope.formData.testnetEnabled = info.network == 'testnet' ? true : false; $scope.formData.testnetEnabled = info.network == 'testnet' ? true : false;
@ -236,7 +236,7 @@ angular.module('copayApp.controllers').controller('importController',
var words = $scope.formData.words || null; var words = $scope.formData.words || null;
if (!words) { if (!words) {
popupService.showAlert(gettextCatalog.getString('Please enter the recovery phrase')); popupService.showAlert(gettextCatalog.getString('Error'), gettextCatalog.getString('Please enter the recovery phrase'));
} else if (words.indexOf('xprv') == 0 || words.indexOf('tprv') == 0) { } else if (words.indexOf('xprv') == 0 || words.indexOf('tprv') == 0) {
return _importExtendedPrivateKey(words, opts); return _importExtendedPrivateKey(words, opts);
} else if (words.indexOf('xpub') == 0 || words.indexOf('tpuv') == 0) { } else if (words.indexOf('xpub') == 0 || words.indexOf('tpuv') == 0) {
@ -245,7 +245,7 @@ angular.module('copayApp.controllers').controller('importController',
var wordList = words.split(/[\u3000\s]+/); var wordList = words.split(/[\u3000\s]+/);
if ((wordList.length % 3) != 0) { if ((wordList.length % 3) != 0) {
popupService.showAlert(gettextCatalog.getString('Wrong number of recovery words:') + wordList.length); popupService.showAlert(gettextCatalog.getString('Error'), gettextCatalog.getString('Wrong number of recovery words: ') + wordList.length);
return; return;
} }
} }
@ -280,7 +280,7 @@ angular.module('copayApp.controllers').controller('importController',
$scope.importHW = function(form) { $scope.importHW = function(form) {
if (form.$invalid || $scope.formData.ccount < 0) { if (form.$invalid || $scope.formData.ccount < 0) {
popupService.showAlert(gettextCatalog.getString('There is an error in the form')); popupService.showAlert(gettextCatalog.getString('Error'), gettextCatalog.getString('There is an error in the form'));
return; return;
} }
@ -290,7 +290,7 @@ angular.module('copayApp.controllers').controller('importController',
if ($scope.seedSource.id == 'trezor') { if ($scope.seedSource.id == 'trezor') {
if (account < 1) { if (account < 1) {
popupService.showAlert(gettextCatalog.getString('Invalid account number')); popupService.showAlert(gettextCatalog.getString('Error'), gettextCatalog.getString('Invalid account number'));
return; return;
} }
account = account - 1; account = account - 1;

View File

@ -1,7 +1,7 @@
'use strict'; 'use strict';
angular.module('copayApp.controllers').controller('tabHomeController', angular.module('copayApp.controllers').controller('tabHomeController',
function($rootScope, $timeout, $scope, $state, $stateParams, $ionicModal, $ionicScrollDelegate, gettextCatalog, lodash, popupService, ongoingProcess, profileService, walletService, configService, $log, platformInfo, storageService, txpModalService, $window, bitpayCardService, startupService, addressbookService) { function($rootScope, $timeout, $scope, $state, $stateParams, $ionicModal, $ionicScrollDelegate, gettextCatalog, lodash, popupService, ongoingProcess, externalLinkService, latestReleaseService, profileService, walletService, configService, $log, platformInfo, storageService, txpModalService, $window, bitpayCardService, startupService, addressbookService) {
var wallet; var wallet;
var listeners = []; var listeners = [];
var notifications = []; var notifications = [];
@ -12,6 +12,7 @@ angular.module('copayApp.controllers').controller('tabHomeController',
$scope.homeTip = $stateParams.fromOnboarding; $scope.homeTip = $stateParams.fromOnboarding;
$scope.isCordova = platformInfo.isCordova; $scope.isCordova = platformInfo.isCordova;
$scope.isAndroid = platformInfo.isAndroid; $scope.isAndroid = platformInfo.isAndroid;
$scope.isNW = platformInfo.isNW;
$scope.$on("$ionicView.afterEnter", function() { $scope.$on("$ionicView.afterEnter", function() {
startupService.ready(); startupService.ready();
@ -23,6 +24,21 @@ angular.module('copayApp.controllers').controller('tabHomeController',
}); });
} }
if ($scope.isNW) {
latestReleaseService.checkLatestRelease(function(err, newRelease) {
if (err) {
$log.warn(err);
return;
}
if (newRelease) $scope.newRelease = true;
});
}
$scope.openExternalLink = function(url, optIn, title, message, okText, cancelText) {
externalLinkService.open(url, optIn, title, message, okText, cancelText);
};
$scope.openNotificationModal = function(n) { $scope.openNotificationModal = function(n) {
wallet = profileService.getWallet(n.walletId); wallet = profileService.getWallet(n.walletId);
@ -41,7 +57,7 @@ angular.module('copayApp.controllers').controller('tabHomeController',
ongoingProcess.set('loadingTxInfo', false); ongoingProcess.set('loadingTxInfo', false);
if (err) { if (err) {
$log.warn('No txp found'); $log.warn('No txp found');
return popupService.showAlert(null, gettextCatalog.getString('Transaction not found')); return popupService.showAlert(gettextCatalog.getString('Error'), gettextCatalog.getString('Transaction not found'));
} }
txpModalService.open(_txp); txpModalService.open(_txp);
}); });
@ -63,7 +79,7 @@ angular.module('copayApp.controllers').controller('tabHomeController',
if (!tx) { if (!tx) {
$log.warn('No tx found'); $log.warn('No tx found');
return popupService.showAlert(null, gettextCatalog.getString('Transaction not found')); return popupService.showAlert(gettextCatalog.getString('Error'), gettextCatalog.getString('Transaction not found'));
} }
$scope.wallet = wallet; $scope.wallet = wallet;
@ -181,13 +197,13 @@ angular.module('copayApp.controllers').controller('tabHomeController',
}); });
}; };
var nextStep = function() { var nextStep = function(cb) {
lodash.each(['AmazonGiftCards', 'BitpayCard', 'BuyAndSell'], function(service) { var i = 0;
var services = ['AmazonGiftCards', 'BitpayCard', 'BuyAndSell'];
lodash.each(services, function(service) {
storageService.getNextStep(service, function(err, value) { storageService.getNextStep(service, function(err, value) {
$scope.externalServices[service] = value ? true : false; $scope.externalServices[service] = value ? true : false;
$timeout(function() { if (++i == services.length) return cb();
$ionicScrollDelegate.resize();
}, 10);
}); });
}); });
}; };
@ -227,7 +243,6 @@ angular.module('copayApp.controllers').controller('tabHomeController',
}; };
$scope.$on("$ionicView.enter", function(event, data) { $scope.$on("$ionicView.enter", function(event, data) {
nextStep();
updateAllWallets(); updateAllWallets();
addressbookService.list(function(err, ab) { addressbookService.list(function(err, ab) {
@ -248,22 +263,28 @@ angular.module('copayApp.controllers').controller('tabHomeController',
]; ];
configService.whenAvailable(function() { configService.whenAvailable(function() {
var config = configService.getSync(); nextStep(function() {
var isWindowsPhoneApp = platformInfo.isWP && platformInfo.isCordova; var config = configService.getSync();
var isWindowsPhoneApp = platformInfo.isWP && platformInfo.isCordova;
$scope.glideraEnabled = config.glidera.enabled && !isWindowsPhoneApp; $scope.glideraEnabled = config.glidera.enabled && !isWindowsPhoneApp;
$scope.coinbaseEnabled = config.coinbase.enabled && !isWindowsPhoneApp; $scope.coinbaseEnabled = config.coinbase.enabled && !isWindowsPhoneApp;
$scope.amazonEnabled = config.amazon.enabled; $scope.amazonEnabled = config.amazon.enabled;
$scope.bitpayCardEnabled = config.bitpayCard.enabled; $scope.bitpayCardEnabled = config.bitpayCard.enabled;
var buyAndSellEnabled = !$scope.externalServices.BuyAndSell && ($scope.glideraEnabled || $scope.coinbaseEnabled); var buyAndSellEnabled = !$scope.externalServices.BuyAndSell && ($scope.glideraEnabled || $scope.coinbaseEnabled);
var amazonEnabled = !$scope.externalServices.AmazonGiftCards && $scope.amazonEnabled; var amazonEnabled = !$scope.externalServices.AmazonGiftCards && $scope.amazonEnabled;
var bitpayCardEnabled = !$scope.externalServices.BitpayCard && $scope.bitpayCardEnabled; var bitpayCardEnabled = !$scope.externalServices.BitpayCard && $scope.bitpayCardEnabled;
$scope.nextStepEnabled = buyAndSellEnabled || amazonEnabled || bitpayCardEnabled; $scope.nextStepEnabled = buyAndSellEnabled || amazonEnabled || bitpayCardEnabled;
$scope.recentTransactionsEnabled = config.recentTransactions.enabled; $scope.recentTransactionsEnabled = config.recentTransactions.enabled;
if ($scope.bitpayCardEnabled) bitpayCardCache(); if ($scope.bitpayCardEnabled) bitpayCardCache();
$timeout(function() {
$ionicScrollDelegate.resize();
$scope.$apply();
}, 10);
});
}); });
}); });

View File

@ -4,7 +4,6 @@ angular.module('copayApp.controllers').controller('tabReceiveController', functi
$scope.isCordova = platformInfo.isCordova; $scope.isCordova = platformInfo.isCordova;
$scope.isNW = platformInfo.isNW; $scope.isNW = platformInfo.isNW;
var showModalTimeout;
$scope.shareAddress = function(addr) { $scope.shareAddress = function(addr) {
if ($scope.generatingAddress) return; if ($scope.generatingAddress) return;
@ -21,11 +20,6 @@ angular.module('copayApp.controllers').controller('tabReceiveController', functi
$scope.generatingAddress = false; $scope.generatingAddress = false;
if (err) popupService.showAlert(gettextCatalog.getString('Error'), err); if (err) popupService.showAlert(gettextCatalog.getString('Error'), err);
$scope.addr = addr; $scope.addr = addr;
if ($scope.wallet.showBackupNeededModal) {
showModalTimeout = $timeout(function() {
$scope.openBackupNeededModal();
}, 2000);
}
$timeout(function() { $timeout(function() {
$scope.$apply(); $scope.$apply();
}, 10); }, 10);
@ -59,7 +53,6 @@ angular.module('copayApp.controllers').controller('tabReceiveController', functi
$scope.close = function() { $scope.close = function() {
$scope.BackupNeededModal.hide(); $scope.BackupNeededModal.hide();
$scope.BackupNeededModal.remove(); $scope.BackupNeededModal.remove();
profileService.setBackupNeededModalFlag($scope.wallet.credentials.walletId);
}; };
$scope.doBackup = function() { $scope.doBackup = function() {
@ -84,16 +77,13 @@ angular.module('copayApp.controllers').controller('tabReceiveController', functi
return; return;
} }
$scope.wallet = wallet; $scope.wallet = wallet;
$scope.generatingAddress = false;
$log.debug('Wallet changed: ' + wallet.name); $log.debug('Wallet changed: ' + wallet.name);
$timeout(function() { $timeout(function() {
$scope.setAddress(); $scope.setAddress(false);
}, 100); }, 100);
}); });
$scope.$on("$ionicView.leave", function(event, data) {
$timeout.cancel(showModalTimeout);
});
$scope.$on("$ionicView.beforeEnter", function(event, data) { $scope.$on("$ionicView.beforeEnter", function(event, data) {
$scope.wallets = profileService.getWallets(); $scope.wallets = profileService.getWallets();
}); });

View File

@ -149,6 +149,9 @@ angular.module('copayApp.controllers').controller('tabSendController', function(
} }
}); });
}); });
if ($scope.hasFunds != true) {
$ionicScrollDelegate.freezeScroll(true);
}
}; };
$scope.$on("$ionicView.beforeEnter", function(event, data) { $scope.$on("$ionicView.beforeEnter", function(event, data) {

View File

@ -4,7 +4,7 @@ angular.module('copayApp.controllers').controller('tabsController', function($ro
$scope.onScan = function(data) { $scope.onScan = function(data) {
if (!incomingData.redir(data)) { if (!incomingData.redir(data)) {
popupService.showAlert(null, gettextCatalog.getString('Invalid data')); popupService.showAlert(gettextCatalog.getString('Error'), gettextCatalog.getString('Invalid data'));
} }
}; };
@ -22,7 +22,7 @@ angular.module('copayApp.controllers').controller('tabsController', function($ro
}, 1); }, 1);
}; };
$scope.$on("$ionicView.beforeEnter", function(event, data){ $scope.$on("$ionicView.beforeEnter", function(event, data) {
$rootScope.hideTabs = ''; $rootScope.hideTabs = '';
}); });

View File

@ -23,7 +23,7 @@ angular.module('copayApp.services')
if (latest.major < current.major || (latest.major == current.major && latest.minor <= current.minor)) if (latest.major < current.major || (latest.major == current.major && latest.minor <= current.minor))
return cb(null, false); return cb(null, false);
$log.debug('A new version of Copay is available: ' + latestVersion); $log.debug('A new version is available: ' + latestVersion);
return cb(null, true); return cb(null, true);
}); });

View File

@ -38,22 +38,6 @@ angular.module('copayApp.services')
}); });
} }
root.setBackupNeededModalFlag = function(walletId) {
storageService.setBackupNeededModalFlag(walletId, true, function(err) {
if (err) $log.error(err);
$log.debug('Backup warning modal flag stored');
root.wallet[walletId].showBackupNeededModal = false;
});
};
function _showBackupNeededModal(wallet, cb) {
storageService.getBackupNeededModalFlag(wallet.credentials.walletId, function(err, val) {
if (err) $log.error(err);
if (val) return cb(false);
return cb(true);
});
};
root.setBackupFlag = function(walletId) { root.setBackupFlag = function(walletId) {
storageService.setBackupFlag(walletId, function(err) { storageService.setBackupFlag(walletId, function(err) {
if (err) $log.error(err); if (err) $log.error(err);
@ -117,11 +101,6 @@ angular.module('copayApp.services')
wallet.balanceHidden = val; wallet.balanceHidden = val;
}); });
_showBackupNeededModal(wallet, function(val) {
if (wallet.needsBackup) wallet.showBackupNeededModal = val;
else wallet.showBackupNeededModal = false;
});
wallet.removeAllListeners(); wallet.removeAllListeners();
wallet.on('report', function(n) { wallet.on('report', function(n) {

View File

@ -373,15 +373,6 @@ angular.module('copayApp.services')
}); });
}; };
root.setBackupNeededModalFlag = function(walletId, val, cb) {
storage.set('showBackupNeededModal-' + walletId, val, cb);
};
root.getBackupNeededModalFlag = function(walletId, cb) {
storage.get('showBackupNeededModal-' + walletId, cb);
};
root.setAmazonGiftCards = function(network, gcs, cb) { root.setAmazonGiftCards = function(network, gcs, cb) {
storage.set('amazonGiftCards-' + network, gcs, cb); storage.set('amazonGiftCards-' + network, gcs, cb);
}; };

View File

@ -83,11 +83,11 @@ angular.module('copayApp.services').factory('walletService', function($log, $tim
wallet.notAuthorized = true; wallet.notAuthorized = true;
$state.go('tabs.home'); $state.go('tabs.home');
} else if (err instanceof errors.NOT_FOUND) { } else if (err instanceof errors.NOT_FOUND) {
popupService.showAlert(gettextCatalog.getString('Could not access Wallet Service: Not found')); popupService.showAlert(gettextCatalog.getString('Error'), gettextCatalog.getString('Could not access Wallet Service: Not found'));
} else { } else {
var msg = "" var msg = ""
$rootScope.$emit('Local/ClientError', (err.error ? err.error : err)); $rootScope.$emit('Local/ClientError', (err.error ? err.error : err));
popupService.showAlert(bwcError.msg(err, gettextCatalog.getString('Error at Wallet Service'))); popupService.showAlert(gettextCatalog.getString('Error'), bwcError.msg(err, gettextCatalog.getString('Error at Wallet Service')));
} }
}; };
root.handleError = lodash.debounce(_handleError, 1000); root.handleError = lodash.debounce(_handleError, 1000);

View File

@ -69,5 +69,9 @@
} }
} }
.modal-backdrop.active {
background: rgba(0, 0, 0, .8);
}
@import "backup-needed-modal"; @import "backup-needed-modal";
@import "screenshot-warning-model"; @import "screenshot-warning-model";

View File

@ -19,4 +19,9 @@
text-align: left; text-align: left;
margin: 1rem; margin: 1rem;
} }
.item-img-buy,
.item-img-sell {
vertical-align: middle;
margin-right: 10px;
}
} }

View File

@ -103,4 +103,19 @@
} }
} }
} }
.release {
cursor: pointer;
cursor: hand;
background-color: #E9E9EC;
padding-top: 30px;
padding-bottom: 20px;
text-align: center;
color: #444;
i {
margin-top: 1px;
font-size: 20px;
margin-left: 10px;
position: absolute;
}
}
} }

View File

@ -71,4 +71,5 @@
} }
} }
} }
.scroll{height: 100%;}
} }

View File

@ -3,7 +3,7 @@
color: $dark-gray; color: $dark-gray;
border-color: rgba(221, 221, 221, 0.3); border-color: rgba(221, 221, 221, 0.3);
} }
&-explaination, &-button-group { &-explanation, &-button-group {
padding: 0 1rem; padding: 0 1rem;
margin: 1rem 0; margin: 1rem 0;
} }

View File

@ -61,6 +61,12 @@
.tx-search:before { .tx-search:before {
font-size: 22px !important; font-size: 22px !important;
} }
.description {
background-color: #f2f2f2;
text-align: center;
font-size: 13px;
color: $mid-gray;
}
} }
.wallet-details-wallet-info { .wallet-details-wallet-info {

View File

@ -38,7 +38,7 @@
</div> </div>
<div class="item item-divider" translate>Experimental Features</div> <div class="item item-divider" translate>Experimental Features</div>
<div class="settings-explaination"> <div class="settings-explanation">
<div class="settings-description" translate> <div class="settings-description" translate>
These features aren't quite ready for primetime. They may change, stop working, or disappear at any time. These features aren't quite ready for primetime. They may change, stop working, or disappear at any time.
</div> </div>

View File

@ -7,7 +7,7 @@
</ion-nav-back-button> </ion-nav-back-button>
</ion-nav-bar> </ion-nav-bar>
<ion-content ng-class="{'slide-to-pay': !hasClick && !insuffientFunds}"> <ion-content ng-class="{'slide-to-pay': !hasClick && !insuffientFunds && !noMatchingWallet}">
<div class="list"> <div class="list">
<div class="item head"> <div class="item head">
<div class="sending-label"> <div class="sending-label">
@ -46,10 +46,13 @@
<span ng-if="tx.hasMultiplesOutputs" translate>Multiple recipients</span> --> <span ng-if="tx.hasMultiplesOutputs" translate>Multiple recipients</span> -->
</span> </span>
</div> </div>
<div class="text-center" ng-show="noMatchingWallet">
<span class="badge badge-energized" translate>No wallets available</span>
</div>
<div class="text-center" ng-show="insuffientFunds"> <div class="text-center" ng-show="insuffientFunds">
<span class="badge badge-energized" translate>Insufficient funds</span> <span class="badge badge-energized" translate>Insufficient funds</span>
</div> </div>
<a class="item item-icon-right" ng-hide="insuffientFunds" ng-click="showWalletSelector()"> <a class="item item-icon-right" ng-hide="insuffientFunds || noMatchingWallet" ng-click="showWalletSelector()">
<span class="label" translate>From</span> <span class="label" translate>From</span>
<div class="wallet"> <div class="wallet">
<i class="icon big-icon-svg"> <i class="icon big-icon-svg">
@ -59,14 +62,14 @@
</div> </div>
<i class="icon bp-arrow-right"></i> <i class="icon bp-arrow-right"></i>
</a> </a>
<a class="item single-line item-icon-right" ng-hide="insuffientFunds" ng-click="showDescriptionPopup()"> <a class="item single-line item-icon-right" ng-hide="insuffientFunds || noMatchingWallet" ng-click="showDescriptionPopup()">
<span class="label" translate>Add Memo</span> <span class="label" translate>Add Memo</span>
<span class="item-note m10l"> <span class="item-note m10l">
{{description}} {{description}}
</span> </span>
<i class="icon bp-arrow-right"></i> <i class="icon bp-arrow-right"></i>
</a> </a>
<div class="item single-line" ng-hide="insuffientFunds"> <div class="item single-line" ng-hide="insuffientFunds || noMatchingWallet">
<span class="label" translate>Fee: {{feeLevel}}</span> <span class="label" translate>Fee: {{feeLevel}}</span>
<span class="item-note"> <span class="item-note">
{{fee || '...'}} {{fee || '...'}}

View File

@ -4,8 +4,8 @@
</ion-nav-back-button> </ion-nav-back-button>
<ion-nav-title>Glidera</ion-nav-title> <ion-nav-title>Glidera</ion-nav-title>
<ion-nav-buttons side="secondary"> <ion-nav-buttons side="secondary">
<button class="button back-button" ng-show="token" ui-sref="tabs.buyandsell.glidera.preferences"> <button class="button no-border" ui-sref="tabs.buyandsell.glidera.preferences">
<i class="icon ion-ios-gear-outline"></i> <i class="icon ion-ios-settings"></i>
</button> </button>
</ion-nav-buttons> </ion-nav-buttons>
</ion-nav-bar> </ion-nav-bar>
@ -28,7 +28,7 @@
</div> </div>
<div class="text-center small-10 small-centered columns" ng-show="!showOauthForm"> <div class="text-center small-10 small-centered columns" ng-show="!showOauthForm">
<p class="glidera-lead">You can buy and sell Bitcoin with a US bank account directly in this app.</p> <p class="glidera-lead">You can buy and sell Bitcoin with a US bank account directly in this app.</p>
<p class="glidera-text">Connect your Glidera account to get started.</p> <p class="glidera-text">Connect your Glidera account to get started.</p>
<button class="button button-standard button-primary" <button class="button button-standard button-primary"
@ -76,32 +76,28 @@
</a> </a>
</div> </div>
<div class="list" <div class="list card"
ng-show="status && status.userCanTransact"> ng-show="status && status.userCanTransact">
<a ng-show="status.userCanBuy" <a ng-show="status.userCanBuy"
class="item item-icon-right" class="item item-icon-right"
href ui-sref="tabs.buyandsell.glidera.buy"> href ui-sref="tabs.buyandsell.glidera.buy">
<img src="img/buy-bitcoin.svg" alt="buy bitcoin" width="30"> <img src="img/buy-bitcoin.svg" alt="buy bitcoin" width="45" class="item-img-buy">
Buy Bitcoin Buy Bitcoin
<i class="icon bp-arrow-right"></i> <i class="icon bp-arrow-right"></i>
</a> </a>
<a class="item item-icon-right" <a class="item item-icon-right"
ng-show="status.userCanSell" ng-show="status.userCanSell"
href ui-sref="tabs.buyandsell.glidera.sell"> href ui-sref="tabs.buyandsell.glidera.sell">
<img src="img/sell-bitcoin.svg" alt="buy bitcoin" width="30"> <img src="img/sell-bitcoin.svg" alt="buy bitcoin" width="45" class="item-img-sell">
Sell Bitcoin Sell Bitcoin
<i class="icon bp-arrow-right"></i> <i class="icon bp-arrow-right"></i>
</a> </a>
</div> </div>
<div class="list card" ng-show="permissions && permissions.transaction_history"> <div class="list card" ng-show="permissions && permissions.transaction_history && txs.length > 0">
<div class="item item-heading"> <div class="item item-heading">
Activity Activity
</div> </div>
<div ng-show="txs.length == 0 "
class="item">
No activity in your account
</div>
<a ng-repeat="tx in txs" <a ng-repeat="tx in txs"
ng-click="glidera.openTxModal(token, tx)" ng-click="glidera.openTxModal(token, tx)"
class="item"> class="item">

View File

@ -7,7 +7,7 @@
<div class="popup-modal-heading" translate>Backup Needed</div> <div class="popup-modal-heading" translate>Backup Needed</div>
<div class="popup-modal-message" translate>Now is a good time to backup your wallet. If this device is lost, it is impossible to access your funds without a backup.</div> <div class="popup-modal-message" translate>Now is a good time to backup your wallet. If this device is lost, it is impossible to access your funds without a backup.</div>
<button class="button button-clear" ng-click="doBackup()" translate>Backup now</button> <button class="button button-clear" ng-click="doBackup()" translate>Backup now</button>
<button class="button button-secondary button-clear" ng-click="close()" translate>Remind me later</button> <button class="button button-secondary button-clear" ng-click="close()" translate>I'll do it later</button>
</div> </div>
</div> </div>
</div> </div>

View File

@ -4,8 +4,8 @@
{{tx.amountStr}} {{tx.amountStr}}
</div> </div>
<span ng-show="!tx.merchant"> <span ng-show="!tx.merchant">
<span ng-show="addressbook[tx.toAddress] && addressbook[tx.toAddress].name && !tx.message"> <span ng-show="addressbook[tx.toAddress] && !tx.message">
{{addressbook[tx.toAddress].name}} {{addressbook[tx.toAddress].name || addressbook[tx.toAddress]}}
</span> </span>
<span class="ellipsis" ng-show="!addressbook[tx.toAddress] && tx.message"> <span class="ellipsis" ng-show="!addressbook[tx.toAddress] && tx.message">
{{tx.message}} {{tx.message}}

View File

@ -41,6 +41,12 @@
<div>{{wallet.name}}</div> <div>{{wallet.name}}</div>
</div> </div>
</div> </div>
<div class="item single-line">
<span class="label" translate>Created by</span>
<span class="item-note">
{{btx.creatorName}} <time>{{ (btx.ts || btx.createdOn ) * 1000 | amDateFormat:'MM/DD/YYYY hh:mm a'}}</time>
</span>
</div>
<a class="item single-line item-icon-right" ng-hide="insuffientFunds" ng-click="showCommentPopup()"> <a class="item single-line item-icon-right" ng-hide="insuffientFunds" ng-click="showCommentPopup()">
<span class="label" translate>Memo</span> <span class="label" translate>Memo</span>
<span class="item-note m10l"> <span class="item-note m10l">
@ -54,7 +60,6 @@
{{btx.feeStr}} {{btx.feeStr}}
</span> </span>
</div> </div>
<div ng-if="actionList[0]"> <div ng-if="actionList[0]">
<div class="item item-divider" translate>Timeline</div> <div class="item item-divider" translate>Timeline</div>
<div class="item timeline-item" ng-class="{'action-created' : a.type == 'created' || a.type == 'accept', 'action-rejected' : a.type == 'reject'}" ng-repeat="a in actionList | orderBy: 'time' :true track by $index"> <div class="item timeline-item" ng-class="{'action-created' : a.type == 'created' || a.type == 'accept', 'action-rejected' : a.type == 'reject'}" ng-repeat="a in actionList | orderBy: 'time' :true track by $index">
@ -72,7 +77,7 @@
</div> </div>
<div> <div>
<span class="item-note"> <span class="item-note">
<time>{{ a.time * 1000 | amDateFormat:'hh:mm a'}}</time> <time>{{ a.time * 1000 | amTimeAgo}}</time>
</span> </span>
</div> </div>
</div> </div>

View File

@ -85,8 +85,8 @@
{{tx.creatorName}} <time>{{ (tx.ts || tx.createdOn ) * 1000 | amDateFormat:'MM/DD/YYYY hh:mm a'}}</time> {{tx.creatorName}} <time>{{ (tx.ts || tx.createdOn ) * 1000 | amDateFormat:'MM/DD/YYYY hh:mm a'}}</time>
</span> </span>
</div> </div>
<div class="item" ng-show="tx.message"> <div class="item single-line" ng-show="tx.message">
{{'Memo'|translate}} <span class="label" translate>{{'Memo'|translate}}</span>
<span class="item-note"> <span class="item-note">
{{tx.message}} {{tx.message}}
</span> </span>
@ -141,7 +141,7 @@
</div> </div>
<div> <div>
<span class="item-note"> <span class="item-note">
<time>{{ a.time * 1000 | amDateFormat:'hh:mm a'}}</time> <time>{{ a.time * 1000 | amTimeAgo}}</time>
</span> </span>
</div> </div>
</div> </div>

View File

@ -7,7 +7,7 @@
<ion-content> <ion-content>
<div class="settings" class="row" ng-show="needsBackup"> <div class="settings" class="row" ng-show="needsBackup">
<div class="settings-explaination"> <div class="settings-explanation">
<div class="settings-heading" translate>Backup Needed</div> <div class="settings-heading" translate>Backup Needed</div>
<div class="settings-description" translate> <div class="settings-description" translate>
Before receiving funds, you must backup your wallet. If this device is lost, it is impossible to access your funds without a backup. Before receiving funds, you must backup your wallet. If this device is lost, it is impossible to access your funds without a backup.

View File

@ -7,7 +7,7 @@
</ion-nav-back-button> </ion-nav-back-button>
</ion-nav-bar> </ion-nav-bar>
<ion-content> <ion-content>
<div class="settings-explaination"> <div class="settings-explanation">
<div class="settings-heading" translate>What do you call this wallet?</div> <div class="settings-heading" translate>What do you call this wallet?</div>
<div class="settings-description" translate>When this wallet was created, it was called &ldquo;{{walletName}}&rdquo;. You can change the name displayed on this device below.</div> <div class="settings-description" translate>When this wallet was created, it was called &ldquo;{{walletName}}&rdquo;. You can change the name displayed on this device below.</div>
</div> </div>

View File

@ -7,7 +7,7 @@
</ion-nav-back-button> </ion-nav-back-button>
</ion-nav-bar> </ion-nav-bar>
<ion-content> <ion-content>
<div class="settings-explaination"> <div class="settings-explanation">
<div class="settings-description" translate> <div class="settings-description" translate>
You'll receive email notifications about payments sent and received from {{wallet.name}}. You'll receive email notifications about payments sent and received from {{wallet.name}}.
</div> </div>

View File

@ -7,7 +7,7 @@
</ion-nav-back-button> </ion-nav-back-button>
</ion-nav-bar> </ion-nav-bar>
<ion-content> <ion-content>
<div class="settings-explaination"> <div class="settings-explanation">
<div class="settings-heading" translate>Bitcoin transactions include a fee collected by miners on the network.</div> <div class="settings-heading" translate>Bitcoin transactions include a fee collected by miners on the network.</div>
<div class="settings-description" translate>The higher the fee, the greater the incentive a miner has to include that transaction in a block. Current fees are determined based on network load and the selected policy.</div> <div class="settings-description" translate>The higher the fee, the greater the incentive a miner has to include that transaction in a block. Current fees are determined based on network load and the selected policy.</div>
<div class="estimates" ng-repeat="fee in feeLevels.livenet" ng-if="fee.level == currentFeeLevel"> <div class="estimates" ng-repeat="fee in feeLevels.livenet" ng-if="fee.level == currentFeeLevel">

View File

@ -5,7 +5,7 @@
</ion-nav-back-button> </ion-nav-back-button>
</ion-nav-bar> </ion-nav-bar>
<ion-content> <ion-content>
<div class="item item-divider"></div> <div class="item item-divider" ng-show="!isCordova"></div>
<a class="item ng-hide" href ng-show="csvReady && !isCordova" ng-csv="csvContent" csv-header="csvHeader" filename="Copay-{{wallet.name}}.csv"> <a class="item ng-hide" href ng-show="csvReady && !isCordova" ng-csv="csvContent" csv-header="csvHeader" filename="Copay-{{wallet.name}}.csv">
<span translate>Export to file</span> <span translate>Export to file</span>
</a> </a>
@ -18,8 +18,14 @@
{{err | translate}} {{err | translate}}
</span> </span>
</a> </a>
<div class="item item-divider"></div>
<a class="item" ng-click="clearTransactionHistory()"> <a class="item" ng-click="clearTransactionHistory()">
<span class="assertive" translate>Clear cache</span> <span translate>Clear cache</span>
</a> </a>
<div class="settings-explanation">
<div class="settings-description" translate>
The transaction history and every new incoming transaction are cached in the app. This feature clean this up and synchronizes again from the server
</div>
</div>
</ion-content> </ion-content>
</ion-view> </ion-view>

View File

@ -90,7 +90,7 @@
<div class="item item-divider" translate> <div class="item item-divider" translate>
Latest Wallet Addresses Latest Wallet Addresses
</div> </div>
<div class="settings-explaination"> <div class="settings-explanation">
<div class="settings-description" translate> <div class="settings-description" translate>
Only &ldquo;main&rdquo; addresses are shown below. This excludes &ldquo;change&rdquo; address. Only &ldquo;main&rdquo; addresses are shown below. This excludes &ldquo;change&rdquo; address.
</div> </div>
@ -99,7 +99,7 @@
<span>{{a.address}}</span> <span>{{a.address}}</span>
<span class="item-note">{{a.path}} &middot; {{a.createdOn *1000 | amDateFormat:'MMMM Do YYYY, h:mm a' }}</span> <span class="item-note">{{a.path}} &middot; {{a.createdOn *1000 | amDateFormat:'MMMM Do YYYY, h:mm a' }}</span>
</div> </div>
<div class="settings-explaination"> <div class="settings-explanation">
<div class="settings-description" translate> <div class="settings-description" translate>
Please note: due to resource constraints, this list of addresses is not verified locally. A compromised BWS node could return addresses which are not controlled by this wallet. Please note: due to resource constraints, this list of addresses is not verified locally. A compromised BWS node could return addresses which are not controlled by this wallet.
</div> </div>

View File

@ -10,6 +10,9 @@
spinner="ios-small" spinner="ios-small"
on-refresh="onRefresh()"> on-refresh="onRefresh()">
</ion-refresher> </ion-refresher>
<div class="release" ng-show="newRelease" ng-click="openExternalLink('https://github.com/bitpay/copay/releases/latest', true, 'Update Available', 'An update to this app is available. For your security, please update to the latest version.', 'View Update', 'Go Back')">
<span translate>An update to this app is available</span><span><i class="icon bp-arrow-right"></i></span>
</div>
<div class="list card homeTip" ng-if="homeTip"> <div class="list card homeTip" ng-if="homeTip">
<div class="item item-icon-right item-heading"> <div class="item item-icon-right item-heading">
<a ng-click="hideHomeTip()"><i class="icon ion-ios-close-empty close-home-tip"></i></a> <a ng-click="hideHomeTip()"><i class="icon ion-ios-close-empty close-home-tip"></i></a>

View File

@ -46,7 +46,7 @@
</div> </div>
</article> </article>
<article ng-if="wallet && wallet.isComplete()"> <article ng-if="wallet && wallet.isComplete()">
<div class="row backup" ng-show="!wallet.showBackupNeededModal && wallet.needsBackup" ng-click="goToBackupFlow()"> <div class="row backup" ng-show="wallet.needsBackup" ng-click="openBackupNeededModal()">
<div class="text-center col center-block"> <div class="text-center col center-block">
<i class="icon ion-alert"></i><span translate>Wallet not backed up</span><i class="icon ion-ios-arrow-thin-right"></i> <i class="icon ion-alert"></i><span translate>Wallet not backed up</span><i class="icon ion-ios-arrow-thin-right"></i>
</div> </div>

View File

@ -2,73 +2,75 @@
<ion-nav-bar class="bar-royal"> <ion-nav-bar class="bar-royal">
<ion-nav-title>{{'Send' | translate}}</ion-nav-title> <ion-nav-title>{{'Send' | translate}}</ion-nav-title>
</ion-nav-bar> </ion-nav-bar>
<ion-content scroll="false" class="ng-hide" ng-show="!hasFunds"> <ion-content>
<div class="zero-state"> <div ng-if="hasFunds == false" class="zero-state">
<i class="icon zero-state-icon"> <div>
<img src="img/tab-icons/ico-send-selected.svg"/> <i class="icon zero-state-icon">
</i> <img src="img/tab-icons/ico-send-selected.svg"/>
<div class="zero-state-heading" translate>Start sending bitcoin</div> </i>
<div class="zero-state-description" ng-show="hasWallets" translate>To get started, buy bitcoin or share your address. You can receive bitcoin from any wallet or service.</div> <div class="zero-state-heading" translate>Start sending bitcoin</div>
<div class="zero-state-description" ng-show="!hasWallets" translate>To get started, you'll need to create a bitcoin wallet and get some bitcoin.</div> <div class="zero-state-description" ng-show="hasWallets" translate>To get started, buy bitcoin or share your address. You can receive bitcoin from any wallet or service.</div>
<div class="zero-state-cta"> <div class="zero-state-description" ng-show="!hasWallets" translate>To get started, you'll need to create a bitcoin wallet and get some bitcoin.</div>
<button class="button button-standard button-primary" ng-click="buyBitcoin()" ng-show="hasWallets" translate>Buy Bitcoin</button> <div class="zero-state-cta">
<button class="button button-standard button-primary" ng-click="createWallet()" ng-show="!hasWallets" translate>Create bitcoin wallet</button> <button class="button button-standard button-primary" ng-click="buyBitcoin()" ng-show="hasWallets" translate>Buy Bitcoin</button>
<button class="button button-standard button-secondary" ui-sref="tabs.receive" ng-show="hasWallets" translate>Show bitcoin address</button> <button class="button button-standard button-primary" ng-click="createWallet()" ng-show="!hasWallets" translate>Create bitcoin wallet</button>
</div> <button class="button button-standard button-secondary" ui-sref="tabs.receive" ng-show="hasWallets" translate>Show bitcoin address</button>
</div>
</ion-content>
<ion-content class="ng-hide has-funds" ng-show="hasFunds">
<div class="item item-heading send-heading" translate>Recipient</div>
<label class="item item-input bitcoin-address">
<i class="icon icon-svg placeholder-icon"><img src="img/icon-bitcoin-symbol.svg"></i>
<div class="qr-scan-icon" ng-style="{'width': '100%'}">
<a on-tap="openScanner()" ng-style="{'top': '6px'}">
<i class="icon ion-qr-scanner"></i>
</a>
<input type="text" placeholder="{{'Search or enter bitcoin address' | translate}}" ng-model="formData.search" ng-change="findContact(formData.search)" ng-model-onblur>
</div>
</label>
<div class="card">
<div class="item item-icon-right item-heading">
<span translate>Contacts</span>
<a ng-if="hasContacts" ui-sref="tabs.send.addressbook">
<i class="icon ion-ios-plus-empty list-add-button"></i>
</a>
</div>
<div class="list">
<a class="item item-icon-left item-icon-right" ng-if="!hasContacts" ui-sref="tabs.send.addressbook">
<i class="icon big-icon-svg">
<gravatar class="send-gravatar" name="" width="30" email=""></gravatar>
</i>
<span translate>Add a Contact</span>
<i class="icon bp-arrow-right"></i>
</a>
<a class="item item-icon-left item-icon-right" ng-repeat="item in list" ng-if="hasContacts && !item.isWallet" ng-click="goToAmount(item)">
<i class="icon big-icon-svg">
<gravatar class="send-gravatar" name="{{item.name}}" width="30" email="{{item.email}}"></gravatar>
</i>
{{item.name}}
<i class="icon bp-arrow-right"></i>
</a>
<div class="show-more" ng-if="contactsShowMore" ng-click="showMore()" translate>
Show more
</div> </div>
</div> </div>
</div> </div>
<div ng-if="hasFunds" class="has-funds">
<div class="item item-heading send-heading" translate>Recipient</div>
<label class="item item-input bitcoin-address">
<i class="icon icon-svg placeholder-icon"><img src="img/icon-bitcoin-symbol.svg"></i>
<div class="qr-scan-icon" ng-style="{'width': '100%'}">
<a on-tap="openScanner()" ng-style="{'top': '6px'}">
<i class="icon ion-qr-scanner"></i>
</a>
<input type="text" placeholder="{{'Search or enter bitcoin address' | translate}}" ng-model="formData.search" ng-change="findContact(formData.search)" ng-model-onblur>
</div>
</label>
<div class="card">
<div class="item item-icon-right item-heading">
<span translate>Contacts</span>
<a ng-if="hasContacts" ui-sref="tabs.send.addressbook">
<i class="icon ion-ios-plus-empty list-add-button"></i>
</a>
</div>
<div class="list">
<a class="item item-icon-left item-icon-right" ng-if="!hasContacts" ui-sref="tabs.send.addressbook">
<i class="icon big-icon-svg">
<gravatar class="send-gravatar" name="" width="30" email=""></gravatar>
</i>
<span translate>Add a Contact</span>
<i class="icon bp-arrow-right"></i>
</a>
<a class="item item-icon-left item-icon-right" ng-repeat="item in list" ng-if="hasContacts && !item.isWallet" ng-click="goToAmount(item)">
<i class="icon big-icon-svg">
<gravatar class="send-gravatar" name="{{item.name}}" width="30" email="{{item.email}}"></gravatar>
</i>
{{item.name}}
<i class="icon bp-arrow-right"></i>
</a>
<div class="show-more" ng-if="contactsShowMore" ng-click="showMore()" translate>
Show more
</div>
</div>
</div>
<div class="card" ng-if="hasWallets && !oneWallet"> <div class="card" ng-if="hasWallets && !oneWallet">
<div class="list"> <div class="list">
<div class="item item-heading"> <div class="item item-heading">
<span translate>Transfer to Wallet</span> <span translate>Transfer to Wallet</span>
</div>
<a class="item item-icon-left item-icon-right" ng-repeat="item in list" ng-if="hasWallets && item.isWallet" ng-click="goToAmount(item)">
<!-- <i ng-show="item.isWallet" class="icon ion-briefcase size-21" ng-style="{'color':item.color}"></i> -->
<i class="icon big-icon-svg" ng-if="item.isWallet">
<img src="img/icon-wallet.svg" ng-style="{'background-color': item.color}" class="bg"/>
</i>
{{item.name}}
<i class="icon bp-arrow-right"></i>
</a>
</div> </div>
<a class="item item-icon-left item-icon-right" ng-repeat="item in list" ng-if="hasWallets && item.isWallet" ng-click="goToAmount(item)">
<!-- <i ng-show="item.isWallet" class="icon ion-briefcase size-21" ng-style="{'color':item.color}"></i> -->
<i class="icon big-icon-svg" ng-if="item.isWallet">
<img src="img/icon-wallet.svg" ng-style="{'background-color': item.color}" class="bg"/>
</i>
{{item.name}}
<i class="icon bp-arrow-right"></i>
</a>
</div> </div>
</div> </div>
</ion-content> </ion-content>

View File

@ -99,7 +99,7 @@
<div ng-repeat="tx in txps" class="item item-icon-left" ng-click="openTxpModal(tx)"> <div ng-repeat="tx in txps" class="item item-icon-left" ng-click="openTxpModal(tx)">
<span ng-include="'views/includes/txp.html'"></span> <span ng-include="'views/includes/txp.html'"></span>
</div> </div>
<div class="item item-footer" ng-show="status.lockedBalanceSat"> <div class="item item-footer description" ng-show="status.lockedBalanceSat">
<span translate>Total Locked Balance</span>: <span translate>Total Locked Balance</span>:
<b>{{status.lockedBalanceStr}} </b> <b>{{status.lockedBalanceStr}} </b>
<span> {{status.lockedBalanceAlternative}} {{status.alternativeIsoCode}} </span> <span> {{status.lockedBalanceAlternative}} {{status.alternativeIsoCode}} </span>
@ -158,10 +158,10 @@
<div ng-show="btx.action == 'sent'" class="ellipsis"> <div ng-show="btx.action == 'sent'" class="ellipsis">
<div ng-if="btx.message">{{btx.message}}</div> <div ng-if="btx.message">{{btx.message}}</div>
<div ng-if="!btx.message && btx.note.body">{{btx.note.body}}</div> <div ng-if="!btx.message && btx.note.body">{{btx.note.body}}</div>
<div ng-if="!btx.message && !btx.note.body && addressbook[btx.addressTo] && addressbook[btx.addressTo].name"> <div ng-if="!btx.message && !btx.note.body && addressbook[btx.addressTo]">
{{addressbook[btx.addressTo].name}} {{addressbook[btx.addressTo].name || addressbook[btx.addressTo]}}
</div> </div>
<div ng-if="!btx.message && !btx.note.body && !(addressbook[btx.addressTo] && addressbook[btx.addressTo].name)" translate>Sent</div> <div ng-if="!btx.message && !btx.note.body && !addressbook[btx.addressTo]" translate>Sent</div>
</div> </div>
<div ng-show="btx.action == 'moved'" class="ellipsis"> <div ng-show="btx.action == 'moved'" class="ellipsis">