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
node_js:
- '4.0'
- '6'
before_install:
- npm install -g bower
- npm install -g grunt-cli
- export DISPLAY=:99.0
- sh -e /etc/init.d/xvfb start
install:
- bower install
- npm install
after_success:
- npm run exec:coveralls
- npm run apply:bitpay
# after_success:
# - 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)
[![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.

View File

@ -116,6 +116,9 @@ mkdir /Volumes/"${VOL_NAME}"/.background
cp "${DMG_BACKGROUND_IMG}" /Volumes/"${VOL_NAME}"/.background/
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
cp "${DMG_VOLUME_ICON}" /Volumes/"${VOL_NAME}"/.VolumeIcon.icns
`npm bin`/fileicon set /Volumes/"${VOL_NAME}"/ /Volumes/"${VOL_NAME}"/.VolumeIcon.icns

View File

@ -45,6 +45,10 @@
"bugs": {
"url": "*GITHUBREPOBUGS*"
},
"engines": {
"node": "6",
"npm": "3"
},
"dependencies": {
"adm-zip": "^0.4.7",
"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",
"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",
"test": "./node_modules/.bin/grunt test-coveralls",
"test": "echo \"no package tests configured\"",
"clean": "trash platforms && trash plugins && cordova prepare",
"unstage-package": "git reset package.json",
"clean-all": "git clean -dfx"
@ -114,7 +118,6 @@
"ionic": "^2.1.0",
"trash-cli": "^1.4.0",
"lodash": "^4.3.0",
"fileicon": "^0.1.8",
"pre-commit": "^1.1.3"
},
"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
echo "Exporting all assets from src.sketch..."
# sketchtool is installed by install.sh
sketchtool export layers src.sketch
function postprocess {
# $1 = distribution name
echo "Beginning postprocessing for $1..."
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
echo "Removing raw background pngs..."
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 {

View File

@ -1,2 +1,8 @@
echo "Installing sketchtool... (this requires Sketch.app)"
# This installs sketchtool: https://www.sketchapp.com/tool/
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
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

Binary file not shown.

View File

@ -39,7 +39,7 @@ angular.module('copayApp.controllers').controller('activityController',
ongoingProcess.set('loadingTxInfo', false);
if (err) {
$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);
});
@ -61,7 +61,7 @@ angular.module('copayApp.controllers').controller('activityController',
if (!tx) {
$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;

View File

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

View File

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

View File

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

View File

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

View File

@ -4,12 +4,14 @@ angular.module('copayApp.controllers').controller('bitpayCardController', functi
var self = this;
var runningBalance;
$scope.dateRange = { value: 'last30Days'};
$scope.dateRange = {
value: 'last30Days'
};
$scope.network = bitpayCardService.getEnvironment();
var updateHistoryFromCache = function(cb) {
bitpayCardService.getBitpayDebitCardsHistory($scope.cardId, function(err, data) {
if (err || lodash.isEmpty(data)) return cb();
if (err ||  lodash.isEmpty(data)) return cb();
$scope.historyCached = true;
self.bitpayCardTransactionHistory = data.transactions;
self.bitpayCardCurrentBalance = data.balance;
@ -19,8 +21,8 @@ angular.module('copayApp.controllers').controller('bitpayCardController', functi
var setDateRange = function(preset) {
var startDate, endDate;
preset = preset || 'last30Days';
switch(preset) {
preset = preset ||  'last30Days';
switch (preset) {
case 'last30Days':
startDate = moment().subtract(30, 'days').toISOString();
endDate = moment().toISOString();
@ -138,7 +140,7 @@ angular.module('copayApp.controllers').controller('bitpayCardController', functi
disableAnimate: true
});
$state.go('tabs.home');
popupService.showAlert(null, msg);
popupService.showAlert(gettextCatalog.getString('Error'), msg);
} else {
updateHistoryFromCache(function() {
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;
bitpayCardService.bitAuthPair(obj, function(err, data) {
if (err) {
popupService.showAlert(null, err);
popupService.showAlert(gettextCatalog.getString('Error'), err);
return;
}
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 cancel = gettextCatalog.getString('Go back');
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() {
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;
$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() {
$scope.$apply();
}, 100);
});
};
$scope.$on('accepted', function(event) {
@ -286,6 +289,7 @@ angular.module('copayApp.controllers').controller('confirmController', function(
};
$scope.approve = function(onSendStatusChange) {
if ($scope.paypro && $scope.paymentExpired.value) {
popupService.showAlert(null, gettextCatalog.getString('The payment request has expired'));
$scope.sendStatus = '';

View File

@ -60,7 +60,7 @@ angular.module('copayApp.controllers').controller('importController',
};
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.testnetEnabled = info.network == 'testnet' ? true : false;
@ -236,7 +236,7 @@ angular.module('copayApp.controllers').controller('importController',
var words = $scope.formData.words || null;
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) {
return _importExtendedPrivateKey(words, opts);
} 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]+/);
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;
}
}
@ -280,7 +280,7 @@ angular.module('copayApp.controllers').controller('importController',
$scope.importHW = function(form) {
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;
}
@ -290,7 +290,7 @@ angular.module('copayApp.controllers').controller('importController',
if ($scope.seedSource.id == 'trezor') {
if (account < 1) {
popupService.showAlert(gettextCatalog.getString('Invalid account number'));
popupService.showAlert(gettextCatalog.getString('Error'), gettextCatalog.getString('Invalid account number'));
return;
}
account = account - 1;

View File

@ -1,7 +1,7 @@
'use strict';
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 listeners = [];
var notifications = [];
@ -12,6 +12,7 @@ angular.module('copayApp.controllers').controller('tabHomeController',
$scope.homeTip = $stateParams.fromOnboarding;
$scope.isCordova = platformInfo.isCordova;
$scope.isAndroid = platformInfo.isAndroid;
$scope.isNW = platformInfo.isNW;
$scope.$on("$ionicView.afterEnter", function() {
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) {
wallet = profileService.getWallet(n.walletId);
@ -41,7 +57,7 @@ angular.module('copayApp.controllers').controller('tabHomeController',
ongoingProcess.set('loadingTxInfo', false);
if (err) {
$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);
});
@ -63,7 +79,7 @@ angular.module('copayApp.controllers').controller('tabHomeController',
if (!tx) {
$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;
@ -181,13 +197,13 @@ angular.module('copayApp.controllers').controller('tabHomeController',
});
};
var nextStep = function() {
lodash.each(['AmazonGiftCards', 'BitpayCard', 'BuyAndSell'], function(service) {
var nextStep = function(cb) {
var i = 0;
var services = ['AmazonGiftCards', 'BitpayCard', 'BuyAndSell'];
lodash.each(services, function(service) {
storageService.getNextStep(service, function(err, value) {
$scope.externalServices[service] = value ? true : false;
$timeout(function() {
$ionicScrollDelegate.resize();
}, 10);
if (++i == services.length) return cb();
});
});
};
@ -227,7 +243,6 @@ angular.module('copayApp.controllers').controller('tabHomeController',
};
$scope.$on("$ionicView.enter", function(event, data) {
nextStep();
updateAllWallets();
addressbookService.list(function(err, ab) {
@ -248,22 +263,28 @@ angular.module('copayApp.controllers').controller('tabHomeController',
];
configService.whenAvailable(function() {
var config = configService.getSync();
var isWindowsPhoneApp = platformInfo.isWP && platformInfo.isCordova;
nextStep(function() {
var config = configService.getSync();
var isWindowsPhoneApp = platformInfo.isWP && platformInfo.isCordova;
$scope.glideraEnabled = config.glidera.enabled && !isWindowsPhoneApp;
$scope.coinbaseEnabled = config.coinbase.enabled && !isWindowsPhoneApp;
$scope.amazonEnabled = config.amazon.enabled;
$scope.bitpayCardEnabled = config.bitpayCard.enabled;
$scope.glideraEnabled = config.glidera.enabled && !isWindowsPhoneApp;
$scope.coinbaseEnabled = config.coinbase.enabled && !isWindowsPhoneApp;
$scope.amazonEnabled = config.amazon.enabled;
$scope.bitpayCardEnabled = config.bitpayCard.enabled;
var buyAndSellEnabled = !$scope.externalServices.BuyAndSell && ($scope.glideraEnabled || $scope.coinbaseEnabled);
var amazonEnabled = !$scope.externalServices.AmazonGiftCards && $scope.amazonEnabled;
var bitpayCardEnabled = !$scope.externalServices.BitpayCard && $scope.bitpayCardEnabled;
var buyAndSellEnabled = !$scope.externalServices.BuyAndSell && ($scope.glideraEnabled || $scope.coinbaseEnabled);
var amazonEnabled = !$scope.externalServices.AmazonGiftCards && $scope.amazonEnabled;
var bitpayCardEnabled = !$scope.externalServices.BitpayCard && $scope.bitpayCardEnabled;
$scope.nextStepEnabled = buyAndSellEnabled || amazonEnabled || bitpayCardEnabled;
$scope.recentTransactionsEnabled = config.recentTransactions.enabled;
$scope.nextStepEnabled = buyAndSellEnabled || amazonEnabled || bitpayCardEnabled;
$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.isNW = platformInfo.isNW;
var showModalTimeout;
$scope.shareAddress = function(addr) {
if ($scope.generatingAddress) return;
@ -21,11 +20,6 @@ angular.module('copayApp.controllers').controller('tabReceiveController', functi
$scope.generatingAddress = false;
if (err) popupService.showAlert(gettextCatalog.getString('Error'), err);
$scope.addr = addr;
if ($scope.wallet.showBackupNeededModal) {
showModalTimeout = $timeout(function() {
$scope.openBackupNeededModal();
}, 2000);
}
$timeout(function() {
$scope.$apply();
}, 10);
@ -59,7 +53,6 @@ angular.module('copayApp.controllers').controller('tabReceiveController', functi
$scope.close = function() {
$scope.BackupNeededModal.hide();
$scope.BackupNeededModal.remove();
profileService.setBackupNeededModalFlag($scope.wallet.credentials.walletId);
};
$scope.doBackup = function() {
@ -84,16 +77,13 @@ angular.module('copayApp.controllers').controller('tabReceiveController', functi
return;
}
$scope.wallet = wallet;
$scope.generatingAddress = false;
$log.debug('Wallet changed: ' + wallet.name);
$timeout(function() {
$scope.setAddress();
$scope.setAddress(false);
}, 100);
});
$scope.$on("$ionicView.leave", function(event, data) {
$timeout.cancel(showModalTimeout);
});
$scope.$on("$ionicView.beforeEnter", function(event, data) {
$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) {

View File

@ -4,7 +4,7 @@ angular.module('copayApp.controllers').controller('tabsController', function($ro
$scope.onScan = function(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);
};
$scope.$on("$ionicView.beforeEnter", function(event, data){
$scope.$on("$ionicView.beforeEnter", function(event, data) {
$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))
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);
});

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) {
storageService.setBackupFlag(walletId, function(err) {
if (err) $log.error(err);
@ -117,11 +101,6 @@ angular.module('copayApp.services')
wallet.balanceHidden = val;
});
_showBackupNeededModal(wallet, function(val) {
if (wallet.needsBackup) wallet.showBackupNeededModal = val;
else wallet.showBackupNeededModal = false;
});
wallet.removeAllListeners();
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) {
storage.set('amazonGiftCards-' + network, gcs, cb);
};

View File

@ -83,11 +83,11 @@ angular.module('copayApp.services').factory('walletService', function($log, $tim
wallet.notAuthorized = true;
$state.go('tabs.home');
} 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 {
var msg = ""
$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);

View File

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

View File

@ -19,4 +19,9 @@
text-align: left;
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;
border-color: rgba(221, 221, 221, 0.3);
}
&-explaination, &-button-group {
&-explanation, &-button-group {
padding: 0 1rem;
margin: 1rem 0;
}

View File

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

View File

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

View File

@ -7,7 +7,7 @@
</ion-nav-back-button>
</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="item head">
<div class="sending-label">
@ -46,10 +46,13 @@
<span ng-if="tx.hasMultiplesOutputs" translate>Multiple recipients</span> -->
</span>
</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">
<span class="badge badge-energized" translate>Insufficient funds</span>
</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>
<div class="wallet">
<i class="icon big-icon-svg">
@ -59,14 +62,14 @@
</div>
<i class="icon bp-arrow-right"></i>
</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="item-note m10l">
{{description}}
</span>
<i class="icon bp-arrow-right"></i>
</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="item-note">
{{fee || '...'}}

View File

@ -4,8 +4,8 @@
</ion-nav-back-button>
<ion-nav-title>Glidera</ion-nav-title>
<ion-nav-buttons side="secondary">
<button class="button back-button" ng-show="token" ui-sref="tabs.buyandsell.glidera.preferences">
<i class="icon ion-ios-gear-outline"></i>
<button class="button no-border" ui-sref="tabs.buyandsell.glidera.preferences">
<i class="icon ion-ios-settings"></i>
</button>
</ion-nav-buttons>
</ion-nav-bar>
@ -28,7 +28,7 @@
</div>
<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-text">Connect your Glidera account to get started.</p>
<button class="button button-standard button-primary"
@ -76,32 +76,28 @@
</a>
</div>
<div class="list"
<div class="list card"
ng-show="status && status.userCanTransact">
<a ng-show="status.userCanBuy"
class="item item-icon-right"
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
<i class="icon bp-arrow-right"></i>
</a>
<a class="item item-icon-right"
ng-show="status.userCanSell"
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
<i class="icon bp-arrow-right"></i>
</a>
</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">
Activity
</div>
<div ng-show="txs.length == 0 "
class="item">
No activity in your account
</div>
<a ng-repeat="tx in txs"
ng-click="glidera.openTxModal(token, tx)"
class="item">

View File

@ -7,7 +7,7 @@
<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>
<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>

View File

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

View File

@ -41,6 +41,12 @@
<div>{{wallet.name}}</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()">
<span class="label" translate>Memo</span>
<span class="item-note m10l">
@ -54,7 +60,6 @@
{{btx.feeStr}}
</span>
</div>
<div ng-if="actionList[0]">
<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">
@ -72,7 +77,7 @@
</div>
<div>
<span class="item-note">
<time>{{ a.time * 1000 | amDateFormat:'hh:mm a'}}</time>
<time>{{ a.time * 1000 | amTimeAgo}}</time>
</span>
</div>
</div>

View File

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

View File

@ -7,7 +7,7 @@
<ion-content>
<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-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.

View File

@ -7,7 +7,7 @@
</ion-nav-back-button>
</ion-nav-bar>
<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-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>

View File

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

View File

@ -7,7 +7,7 @@
</ion-nav-back-button>
</ion-nav-bar>
<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-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">

View File

@ -5,7 +5,7 @@
</ion-nav-back-button>
</ion-nav-bar>
<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">
<span translate>Export to file</span>
</a>
@ -18,8 +18,14 @@
{{err | translate}}
</span>
</a>
<div class="item item-divider"></div>
<a class="item" ng-click="clearTransactionHistory()">
<span class="assertive" translate>Clear cache</span>
<span translate>Clear cache</span>
</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-view>

View File

@ -90,7 +90,7 @@
<div class="item item-divider" translate>
Latest Wallet Addresses
</div>
<div class="settings-explaination">
<div class="settings-explanation">
<div class="settings-description" translate>
Only &ldquo;main&rdquo; addresses are shown below. This excludes &ldquo;change&rdquo; address.
</div>
@ -99,7 +99,7 @@
<span>{{a.address}}</span>
<span class="item-note">{{a.path}} &middot; {{a.createdOn *1000 | amDateFormat:'MMMM Do YYYY, h:mm a' }}</span>
</div>
<div class="settings-explaination">
<div class="settings-explanation">
<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.
</div>

View File

@ -10,6 +10,9 @@
spinner="ios-small"
on-refresh="onRefresh()">
</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="item item-icon-right item-heading">
<a ng-click="hideHomeTip()"><i class="icon ion-ios-close-empty close-home-tip"></i></a>

View File

@ -46,7 +46,7 @@
</div>
</article>
<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">
<i class="icon ion-alert"></i><span translate>Wallet not backed up</span><i class="icon ion-ios-arrow-thin-right"></i>
</div>

View File

@ -2,73 +2,75 @@
<ion-nav-bar class="bar-royal">
<ion-nav-title>{{'Send' | translate}}</ion-nav-title>
</ion-nav-bar>
<ion-content scroll="false" class="ng-hide" ng-show="!hasFunds">
<div class="zero-state">
<i class="icon zero-state-icon">
<img src="img/tab-icons/ico-send-selected.svg"/>
</i>
<div class="zero-state-heading" translate>Start sending 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-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-cta">
<button class="button button-standard button-primary" ng-click="buyBitcoin()" ng-show="hasWallets" translate>Buy Bitcoin</button>
<button class="button button-standard button-primary" ng-click="createWallet()" ng-show="!hasWallets" translate>Create bitcoin wallet</button>
<button class="button button-standard button-secondary" ui-sref="tabs.receive" ng-show="hasWallets" translate>Show bitcoin address</button>
</div>
</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
<ion-content>
<div ng-if="hasFunds == false" class="zero-state">
<div>
<i class="icon zero-state-icon">
<img src="img/tab-icons/ico-send-selected.svg"/>
</i>
<div class="zero-state-heading" translate>Start sending 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-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-cta">
<button class="button button-standard button-primary" ng-click="buyBitcoin()" ng-show="hasWallets" translate>Buy Bitcoin</button>
<button class="button button-standard button-primary" ng-click="createWallet()" ng-show="!hasWallets" translate>Create bitcoin wallet</button>
<button class="button button-standard button-secondary" ui-sref="tabs.receive" ng-show="hasWallets" translate>Show bitcoin address</button>
</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="list">
<div class="item item-heading">
<span translate>Transfer to Wallet</span>
<div class="card" ng-if="hasWallets && !oneWallet">
<div class="list">
<div class="item item-heading">
<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>
<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>
</ion-content>

View File

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