Merged wallet/sprint20 into wallet/task/475.

# Conflicts:
#	src/js/services/bitcoincomService.js
This commit is contained in:
Brendon Duncan 2018-07-18 10:47:43 +12:00
commit ef121f52fa
43 changed files with 789 additions and 999 deletions

View file

@ -1,6 +1,6 @@
'use strict';
angular.module('copayApp.controllers').controller('addressbookAddController', function($scope, $state, $stateParams, $timeout, $ionicHistory, gettextCatalog, addressbookService, popupService, configService, bitcoinCashJsService) {
angular.module('copayApp.controllers').controller('addressbookAddController', function($scope, $state, $stateParams, $timeout, $ionicHistory, gettextCatalog, addressbookService, popupService, configService, bitcoinCashJsService, platformInfo) {
var config = configService.getSync();
var defaults = configService.getDefaults();

View file

@ -38,9 +38,11 @@ angular.module('copayApp.controllers').controller('amountController', function($
$scope.minShapeshiftAmount = parseFloat(data.stateParams.minShapeshiftAmount);
$scope.maxShapeshiftAmount = parseFloat(data.stateParams.maxShapeshiftAmount);
$scope.shapeshiftOrderId = data.stateParams.shapeshiftOrderId;
$scope.fromWalletId = data.stateParams.fromWalletId;
}
// To get the wallet from with the new flow
$scope.fromWalletId = data.stateParams.fromWalletId;
if (data.stateParams.noPrefix) {
$scope.showWarningMessage = data.stateParams.noPrefix != 0;
if ($scope.showWarningMessage) {
@ -458,7 +460,8 @@ angular.module('copayApp.controllers').controller('amountController', function($
amount: $scope.useSendMax ? null : _amount,
currency: unit.id.toUpperCase(),
coin: coin,
useSendMax: $scope.useSendMax
useSendMax: $scope.useSendMax,
fromWalletId: $scope.fromWalletId
});
} else {
var amount = _amount;
@ -479,6 +482,7 @@ angular.module('copayApp.controllers').controller('amountController', function($
toColor: $scope.toColor,
coin: coin,
useSendMax: $scope.useSendMax,
fromWalletId: $scope.fromWalletId
};
if ($scope.shapeshiftOrderId) {

View file

@ -1,7 +1,9 @@
'use strict';
angular.module('copayApp.controllers').controller('buyBitcoindotcomController',
function($scope, $timeout, $ionicModal, $log, $state, $ionicHistory, lodash, bitcoincomService, externalLinkService, popupService) {
function($scope, platformInfo, externalLinkService) {
$scope.os = platformInfo.isAndroid ? 'android' : platformInfo.isIOS ? 'ios' : 'desktop';
$scope.openExternalLink = function(url) {
externalLinkService.open(url);

View file

@ -1,53 +0,0 @@
'use strict';
angular.module('copayApp.controllers').controller('rateAppController', function($scope, $state, $stateParams, $window, lodash, externalLinkService, configService, platformInfo, feedbackService, ongoingProcess, popupService, appConfigService) {
$scope.score = parseInt($stateParams.score);
$scope.appName = appConfigService.nameCase;
var isAndroid = platformInfo.isAndroid;
var isIOS = platformInfo.isIOS;
var config = configService.getSync();
$scope.skip = function() {
var dataSrc = {
"Email": lodash.values(config.emailFor)[0] || ' ',
"Feedback": ' ',
"Score": $stateParams.score,
"AppVersion": $window.version,
"Platform": ionic.Platform.platform(),
"DeviceVersion": ionic.Platform.version()
};
feedbackService.send(dataSrc, function(err) {
if (err) {
// try to send, but not essential, since the user didn't add a message
$log.warn('Could not send feedback.');
}
});
$state.go('tabs.rate.complete', {
score: $stateParams.score,
skipped: true
});
};
$scope.sendFeedback = function() {
$state.go('tabs.rate.send', {
score: $scope.score
});
};
$scope.goAppStore = function() {
var defaults = configService.getDefaults();
var url;
if (isAndroid)
url = defaults.rateApp.bitcoincom.android;
if (isIOS)
url = defaults.rateApp.bitcoincom.ios;
externalLinkService.open(url);
$state.go('tabs.rate.complete', {
score: $stateParams.score,
skipped: true,
rated: true
});
};
});

View file

@ -1,60 +0,0 @@
'use strict';
angular.module('copayApp.controllers').controller('rateCardController', function($scope, $state, $timeout, $log, gettextCatalog, platformInfo, storageService, appConfigService) {
$scope.isCordova = platformInfo.isCordova;
$scope.score = 0;
$scope.appName = appConfigService.nameCase;
$scope.goFeedbackFlow = function() {
$scope.hideCard();
if ($scope.isCordova && $scope.score == 5) {
$state.go('tabs.rate.rateApp', {
score: $scope.score
});
} else {
$state.go('tabs.rate.send', {
score: $scope.score
});
}
};
$scope.setScore = function(score) {
$scope.score = score;
switch ($scope.score) {
case 1:
$scope.button_title = gettextCatalog.getString("I think this app is terrible.");
break;
case 2:
$scope.button_title = gettextCatalog.getString("I don't like it");
break;
case 3:
$scope.button_title = gettextCatalog.getString("Meh - it's alright");
break;
case 4:
$scope.button_title = gettextCatalog.getString("I like the app");
break;
case 5:
$scope.button_title = gettextCatalog.getString("This app is fantastic!");
break;
}
$timeout(function() {
$scope.$apply();
});
};
$scope.hideCard = function() {
$log.debug('Feedback card dismissed.')
storageService.getFeedbackInfo(function(error, info) {
var feedbackInfo = JSON.parse(info);
feedbackInfo.sent = true;
storageService.setFeedbackInfo(JSON.stringify(feedbackInfo), function() {
$scope.showRateCard.value = false;
$timeout(function() {
$scope.$apply();
}, 100);
});
});
}
});

View file

@ -1,102 +0,0 @@
'use strict';
angular.module('copayApp.controllers').controller('sendController', function($scope, $state, $log, $timeout, $stateParams, $ionicNavBarDelegate, $ionicHistory, $ionicConfig, $window, gettextCatalog, popupService, configService, lodash, feedbackService, ongoingProcess, platformInfo, appConfigService) {
$scope.sendFeedback = function(feedback, goHome) {
var config = configService.getSync();
var dataSrc = {
"Email": lodash.values(config.emailFor)[0] || ' ',
"Feedback": goHome ? ' ' : feedback,
"Score": $stateParams.score || ' ',
"AppVersion": $window.version,
"Platform": ionic.Platform.platform(),
"DeviceVersion": ionic.Platform.version()
};
if (!goHome) ongoingProcess.set('sendingFeedback', true);
feedbackService.send(dataSrc, function(err) {
if (goHome) return;
ongoingProcess.set('sendingFeedback', false);
if (err) {
popupService.showAlert(gettextCatalog.getString('Error'), gettextCatalog.getString('Feedback could not be submitted. Please try again later.'));
return;
}
if (!$stateParams.score) {
popupService.showAlert(gettextCatalog.getString('Thank you!'), gettextCatalog.getString('A member of the team will review your feedback as soon as possible.'), function() {
$scope.feedback.value = '';
$ionicHistory.nextViewOptions({
disableAnimate: false,
historyRoot: true
});
$ionicHistory.goBack();
}, gettextCatalog.getString('Finish'));
return;
}
$state.go('tabs.rate.complete', {
score: $stateParams.score
});
});
if (goHome) $state.go('tabs.home');
};
$scope.$on("$ionicView.beforeLeave", function(event, data) {
$ionicConfig.views.swipeBackEnabled(true);
});
$scope.$on("$ionicView.enter", function(event, data) {
if ($scope.score)
$ionicConfig.views.swipeBackEnabled(false);
});
$scope.$on("$ionicView.beforeEnter", function(event, data) {
$scope.isCordova = platformInfo.isCordova;
$scope.score = (data.stateParams && data.stateParams.score) ? parseInt(data.stateParams.score) : null;
$scope.feedback = {};
switch ($scope.score) {
case 1:
$scope.reaction = "Ouch!";
$scope.comment = gettextCatalog.getString("There's obviously something we're doing wrong.") + ' ' + gettextCatalog.getString("How could we improve your experience?");
break;
case 2:
$scope.reaction = gettextCatalog.getString("Oh no!");
$scope.comment = gettextCatalog.getString("There's obviously something we're doing wrong.") + ' ' + gettextCatalog.getString("How could we improve your experience?");
break;
case 3:
$scope.reaction = "Hmm...";
$scope.comment = gettextCatalog.getString("We'd love to do better.") + ' ' + gettextCatalog.getString("How could we improve your experience?");
break;
case 4:
$scope.reaction = gettextCatalog.getString("Thanks!");
$scope.comment = gettextCatalog.getString("That's exciting to hear. We'd love to earn that fifth star from you how could we improve your experience?");
break;
case 5:
$scope.reaction = gettextCatalog.getString("Thank you!");
$scope.comment = gettextCatalog.getString("We're always looking for ways to improve {{appName}}.", {
appName: appConfigService.nameCase
}) + ' ' + gettextCatalog.getString("Is there anything we could do better?");
break;
default:
$scope.justFeedback = true;
$scope.comment = gettextCatalog.getString("We're always looking for ways to improve {{appName}}. How could we improve your experience?", {
appName: appConfigService.nameCase
});
break;
}
});
$scope.$on("$ionicView.afterEnter", function() {
$scope.showForm = true;
});
$scope.goBack = function() {
$ionicHistory.nextViewOptions({
disableAnimate: false,
historyRoot: true
});
$ionicHistory.goBack();
};
});

View file

@ -1,6 +1,6 @@
'use strict';
angular.module('copayApp.controllers').controller('completeController', function($scope, $stateParams, $timeout, $log, $ionicHistory, $state, $ionicNavBarDelegate, $ionicConfig, platformInfo, configService, storageService, lodash, appConfigService, gettextCatalog) {
angular.module('copayApp.controllers').controller('shareAppController', function($scope, $stateParams, $timeout, $log, $ionicHistory, $state, $ionicNavBarDelegate, $ionicConfig, platformInfo, configService, storageService, lodash, appConfigService, gettextCatalog) {
$scope.isCordova = platformInfo.isCordova;
$scope.title = gettextCatalog.getString("Share {{appName}}", {
appName: appConfigService.nameCase
@ -57,28 +57,8 @@ angular.module('copayApp.controllers').controller('completeController', function
$ionicConfig.views.swipeBackEnabled(true);
});
$scope.$on("$ionicView.enter", function() {
if (!$scope.fromSettings)
$ionicConfig.views.swipeBackEnabled(false);
});
$scope.$on("$ionicView.beforeEnter", function(event, data) {
$scope.score = (data.stateParams && data.stateParams.score) ? parseInt(data.stateParams.score) : null;
$scope.skipped = (data.stateParams && data.stateParams.skipped) ? true : false;
$scope.rated = (data.stateParams && data.stateParams.rated) ? true : false;
$scope.fromSettings = (data.stateParams && data.stateParams.fromSettings) ? true : false;
if (!$scope.fromSettings) {
$ionicNavBarDelegate.showBackButton(false);
} else {
$ionicNavBarDelegate.showBackButton(true);
}
storageService.getFeedbackInfo(function(error, info) {
var feedbackInfo = lodash.isString(info) ? JSON.parse(info) : null;
feedbackInfo.sent = true;
storageService.setFeedbackInfo(JSON.stringify(feedbackInfo), function() {});
});
$ionicNavBarDelegate.showBackButton(true);
if (!$scope.isCordova) return;
$scope.animate = true;
@ -133,13 +113,4 @@ angular.module('copayApp.controllers').controller('completeController', function
}
}, 100);
});
$scope.close = function() {
$ionicHistory.nextViewOptions({
disableAnimate: false,
historyRoot: true
});
if ($scope.score == 5) $ionicHistory.goBack(-3);
else $ionicHistory.goBack(-2);
};
});

View file

@ -1,7 +1,7 @@
'use strict';
angular.module('copayApp.controllers').controller('tabHomeController',
function($rootScope, $timeout, $scope, $state, $stateParams, $ionicModal, $ionicScrollDelegate, $window, gettextCatalog, lodash, popupService, ongoingProcess, bannerService, externalLinkService, latestReleaseService, profileService, walletService, configService, $log, platformInfo, storageService, txpModalService, appConfigService, startupService, addressbookService, feedbackService, bwcError, nextStepsService, buyAndSellService, homeIntegrationsService, bitpayCardService, pushNotificationsService, timeService, bitcoincomService, pricechartService, firebaseEventsService, servicesService, shapeshiftService, $ionicNavBarDelegate, signVerifyMessageService) {
function($rootScope, $timeout, $scope, $state, $stateParams, $ionicModal, $ionicScrollDelegate, $window, gettextCatalog, lodash, popupService, ongoingProcess, bannerService, externalLinkService, latestReleaseService, profileService, walletService, configService, $log, platformInfo, storageService, txpModalService, appConfigService, startupService, addressbookService, bwcError, nextStepsService, buyAndSellService, homeIntegrationsService, bitpayCardService, pushNotificationsService, timeService, bitcoincomService, pricechartService, firebaseEventsService, servicesService, shapeshiftService, $ionicNavBarDelegate, signVerifyMessageService) {
var wallet;
var listeners = [];
var notifications = [];
@ -14,7 +14,6 @@ angular.module('copayApp.controllers').controller('tabHomeController',
$scope.isAndroid = platformInfo.isAndroid;
$scope.isWindowsPhoneApp = platformInfo.isCordova && platformInfo.isWP;
$scope.isNW = platformInfo.isNW;
$scope.showRateCard = {};
$scope.showServices = false;
$scope.bannerIsLoading = true;
$scope.bannerImageUrl = '';
@ -52,43 +51,6 @@ angular.module('copayApp.controllers').controller('tabHomeController',
}
});
}
storageService.getFeedbackInfo(function(error, info) {
if ($scope.isWindowsPhoneApp) {
$scope.showRateCard.value = false;
return;
}
if (!info) {
initFeedBackInfo();
} else {
var feedbackInfo = JSON.parse(info);
//Check if current version is greater than saved version
var currentVersion = $scope.version;
var savedVersion = feedbackInfo.version;
var isVersionUpdated = feedbackService.isVersionUpdated(currentVersion, savedVersion);
if (!isVersionUpdated) {
initFeedBackInfo();
return;
}
var now = moment().unix();
var timeExceeded = (now - feedbackInfo.time) >= 24 * 7 * 60 * 60;
$scope.showRateCard.value = timeExceeded && !feedbackInfo.sent;
$timeout(function() {
$scope.$apply();
});
}
});
function initFeedBackInfo() {
var feedbackInfo = {};
feedbackInfo.time = moment().unix();
feedbackInfo.version = $scope.version;
feedbackInfo.sent = false;
storageService.setFeedbackInfo(JSON.stringify(feedbackInfo), function() {
$scope.showRateCard.value = false;
});
};
});
$scope.$on("$ionicView.enter", function(event, data) {

View file

@ -1,32 +1,139 @@
'use strict';
angular.module('copayApp.controllers').controller('tabSendController', function($scope, $rootScope, $log, $timeout, $ionicScrollDelegate, addressbookService, profileService, lodash, $state, walletService, incomingData, popupService, platformInfo, bwcError, gettextCatalog, scannerService, configService, bitcoinCashJsService, $ionicNavBarDelegate) {
angular.module('copayApp.controllers').controller('tabSendController', function($scope, $rootScope, $log, $timeout, $ionicScrollDelegate, $ionicLoading, addressbookService, profileService, lodash, $state, walletService, incomingData, popupService, platformInfo, bwcError, gettextCatalog, scannerService, configService, bitcoinCashJsService, $ionicPopup, $ionicNavBarDelegate, clipboardService) {
var clipboardHasAddress = false;
var clipboardHasContent = false;
var originalList;
var CONTACTS_SHOW_LIMIT;
var currentContactsPage;
$scope.isChromeApp = platformInfo.isChromeApp;
$scope.sectionDisplay = {
transferToWallet: false
$scope.displayBalanceAsFiat = true;
$scope.walletSelectorTitleForce = true;
$scope.addContact = function() {
$state.go('tabs.send.addressbook');
};
$scope.pasteClipboard = function() {
if ($scope.clipboardHasAddress || $scope.clipboardHasContent) {
clipboardService.readFromClipboard(function(text) {
$scope.$apply(function() {
$scope.formData.search = text;
$scope.findContact($scope.formData.search);
});
});
} else {
$ionicPopup.alert({
title: gettextCatalog.getString('Clipboard'),
template: gettextCatalog.getString('Your Clipboard is empty')
});
}
};
$scope.$on("$ionicView.enter", function(event, data) {
clipboardService.readFromClipboard(function(text) {
if (text.length > 200) {
text = text.substring(0, 200);
}
$scope.clipboardHasAddress = false;
$scope.clipboardHasContent = false;
if ((text.indexOf('bitcoincash:') === 0 || text[0] === 'C' || text[0] === 'H' || text[0] === 'p' || text[0] === 'q') && text.replace('bitcoincash:', '').length === 42) { // CashAddr
$scope.clipboardHasAddress = true;
} else if ((text[0] === "1" || text[0] === "3" || text.substring(0, 3) === "bc1") && text.length >= 26 && text.length <= 35) { // Legacy Addresses
$scope.clipboardHasAddress = true;
} else if (text.length > 1) {
$scope.clipboardHasContent = true;
}
});
$ionicNavBarDelegate.showBar(true);
if (!$scope.hasWallets) {
$scope.checkingBalance = false;
return;
}
updateHasFunds();
updateContactsList(function() {
updateList();
});
});
var wallets;
var walletsBch;
var walletsBtc;
var walletToWalletFrom = false;
$scope.onWalletSelect = function(wallet) {
if (!$scope.walletToWalletFrom) {
$scope.walletToWalletFrom = wallet;
if (wallet.coin === 'bch') {
$scope.showWalletsBch = true;
} else if (wallet.coin === 'btc') {
$scope.showWalletsBtc = true;
}
$scope.walletSelectorTitleTo = gettextCatalog.getString('Send to');
} else {
$ionicLoading.show();
walletService.getAddress(wallet, true, function(err, addr) {
$ionicLoading.hide();
return $state.transitionTo('tabs.send.amount', {
displayAddress: $scope.walletToWalletFrom.coin === 'bch' ? bitcoinCashJsService.translateAddresses(addr).cashaddr : addr,
recipientType: 'wallet',
fromWalletId: $scope.walletToWalletFrom.id,
toAddress: addr,
coin: $scope.walletToWalletFrom.coin
});
});
}
};
$scope.showWalletSelector = function() {
$scope.walletToWalletFrom = false;
$scope.walletSelectorTitleFrom = gettextCatalog.getString('Send from');
$scope.showWallets = true;
};
$scope.findContact = function(search) {
if (incomingData.redir(search)) {
return;
}
if (!search || search.length < 1) {
$scope.list = originalList;
$timeout(function() {
$scope.$apply();
});
return;
}
var result = lodash.filter(originalList, function(item) {
var val = item.name;
return lodash.startsWith(val.toLowerCase(), search.toLowerCase());
});
$scope.list = result;
};
var hasWallets = function() {
$scope.walletsWithFunds = profileService.getWallets({
onlyComplete: true,
hasFunds: true
});
$scope.wallets = profileService.getWallets({
onlyComplete: true
onlyComplete: true,
});
$scope.walletsBch = profileService.getWallets({
onlyComplete: true,
coin: 'bch'
});
$scope.walletsBtc = profileService.getWallets({
onlyComplete: true,
coin: 'btc'
});
$scope.hasWallets = lodash.isEmpty($scope.wallets) ? false : true;
};
// THIS is ONLY to show the 'buy bitcoins' message
// does not has any other function.
var updateHasFunds = function() {
if ($rootScope.everHasFunds) {
$scope.hasFunds = true;
return;
}
$scope.hasFunds = false;
var index = 0;
lodash.each($scope.wallets, function(w) {
@ -41,10 +148,9 @@ angular.module('copayApp.controllers').controller('tabSendController', function(
$scope.hasFunds = true;
} else if (status.availableBalanceSat > 0) {
$scope.hasFunds = true;
$rootScope.everHasFunds = true;
}
if (index == $scope.wallets.length) {
if (index === $scope.wallets.length) {
$scope.checkingBalance = false;
$timeout(function() {
$scope.$apply();
@ -54,52 +160,6 @@ angular.module('copayApp.controllers').controller('tabSendController', function(
});
};
var updateWalletsList = function() {
var config = configService.getSync();
var networkResult = lodash.countBy($scope.wallets, 'network');
$scope.showTransferCard = $scope.hasWallets && (networkResult.livenet > 1 || networkResult.testnet > 1);
if ($scope.showTransferCard) {
var walletsToTransfer = $scope.wallets;
if (!(networkResult.livenet > 1)) {
walletsToTransfer = lodash.filter(walletsToTransfer, function(item) {
return item.network == 'testnet';
});
}
if (!(networkResult.testnet > 1)) {
walletsToTransfer = lodash.filter(walletsToTransfer, function(item) {
return item.network == 'livenet';
});
}
var walletList = [];
lodash.each(walletsToTransfer, function(v) {
var displayBalanceAsFiat =
// BD got v.status as undefined here once during development, just
// after creating a new wallet.
v.status &&
v.status.alternativeBalanceAvailable &&
config.wallet.settings.priceDisplay === 'fiat';
walletList.push({
color: v.color,
name: v.name,
recipientType: 'wallet',
coin: v.coin,
network: v.network,
balanceString: displayBalanceAsFiat ?
v.status.totalBalanceAlternative + ' ' + v.status.alternativeIsoCode :
v.cachedBalance,
getAddress: function(cb) {
walletService.getAddress(v, false, cb);
},
});
});
originalList = originalList.concat(walletList);
}
}
var updateContactsList = function(cb) {
var config = configService.getSync();
var defaults = configService.getDefaults();
@ -118,16 +178,14 @@ angular.module('copayApp.controllers').controller('tabSendController', function(
recipientType: 'contact',
coin: v.coin,
displayCoin: (v.coin == 'bch'
? (config.bitcoinCashAlias || defaults.bitcoinCashAlias)
: (config.bitcoinAlias || defaults.bitcoinAlias)).toUpperCase(),
? (config.bitcoinCashAlias || defaults.bitcoinCashAlias)
: (config.bitcoinAlias || defaults.bitcoinAlias)).toUpperCase(),
getAddress: function(cb) {
return cb(null, k);
},
});
});
var contacts = completeContacts.slice(0, (currentContactsPage + 1) * CONTACTS_SHOW_LIMIT);
$scope.contactsShowMore = completeContacts.length > contacts.length;
originalList = originalList.concat(contacts);
originalList = completeContacts;
return cb();
});
};
@ -140,28 +198,6 @@ angular.module('copayApp.controllers').controller('tabSendController', function(
}, 10);
};
$scope.openScanner = function() {
var isWindowsPhoneApp = platformInfo.isCordova && platformInfo.isWP;
if (!isWindowsPhoneApp) {
$state.go('tabs.scan');
return;
}
scannerService.useOldScanner(function(err, contents) {
if (err) {
popupService.showAlert(gettextCatalog.getString('Error'), err);
return;
}
incomingData.redir(contents);
});
};
$scope.showMore = function() {
currentContactsPage++;
updateWalletsList();
};
$scope.searchInFocus = function() {
$scope.searchFocus = true;
};
@ -172,28 +208,6 @@ angular.module('copayApp.controllers').controller('tabSendController', function(
}
};
$scope.findContact = function(search) {
if (incomingData.redir(search)) {
return;
}
if (!search || search.length < 2) {
$scope.list = originalList;
$timeout(function() {
$scope.$apply();
});
return;
}
var result = lodash.filter(originalList, function(item) {
var val = item.name;
return lodash.includes(val.toLowerCase(), search.toLowerCase());
});
$scope.list = result;
};
$scope.goToAmount = function(item) {
$timeout(function() {
item.getAddress(function(err, addr) {
@ -236,34 +250,19 @@ angular.module('copayApp.controllers').controller('tabSendController', function(
};
$scope.$on("$ionicView.beforeEnter", function(event, data) {
$scope.isIOS = platformInfo.isIOS && platformInfo.isCordova;
$scope.showWalletsBch = $scope.showWalletsBtc = $scope.showWallets = false;
$scope.checkingBalance = true;
$scope.formData = {
search: null
};
originalList = [];
CONTACTS_SHOW_LIMIT = 10;
currentContactsPage = 0;
hasWallets();
});
$scope.$on("$ionicView.enter", function(event, data) {
$ionicNavBarDelegate.showBar(true);
if (!$scope.hasWallets) {
$scope.checkingBalance = false;
return;
}
updateHasFunds();
updateWalletsList();
updateContactsList(function() {
updateList();
configService.whenAvailable(function(_config) {
$scope.displayBalanceAsFiat = _config.wallet.settings.priceDisplay === 'fiat';
});
});
$scope.toggle = function(section) {
$scope.sectionDisplay[section] = !$scope.sectionDisplay[section];
$timeout(function() {
$ionicScrollDelegate.resize();
$scope.$apply();
}, 10);
};
});

View file

@ -45,6 +45,18 @@ angular.module('copayApp.controllers').controller('tabSettingsController', funct
});
};
$scope.sendFeedback = function() {
var mailToLink = 'mailto:wallet@bitcoin.com?subject=Feedback%20for%20Bitcoin.com%20Wallet';
if (platformInfo.isNW) {
nw.Shell.openExternal(mailToLink);
} else if (platformInfo.isCordova) {
var mailWindow = window.open(mailToLink, '_system');
mailWindow.close(); // XX SP: bugfix for some browsers in cordova to change the view entirely
} else {
window.location.href = mailToLink;
}
};
$scope.openExternalLink = function() {
var appName = appConfigService.name;
var url = appName == 'copay' ? 'https://github.com/bitcoin-com/wallet/issues' : 'https://www.bitcoin.com/wallet-support';

View file

@ -1,7 +1,7 @@
'use strict';
angular.module('copayApp.directives')
.directive('gravatar', function(md5) {
.directive('gravatar', function(md5, $http) {
return {
restrict: 'AE',
replace: true,
@ -9,13 +9,24 @@ angular.module('copayApp.directives')
name: '@',
height: '@',
width: '@',
email: '@'
email: '@',
url: '@'
},
link: function(scope, el, attr) {
if (typeof scope.email === "string") {
scope.emailHash = md5.createHash(scope.email.toLowerCase() || '');
var req = {
method: 'GET',
url: 'https://secure.gravatar.com/'+scope.emailHash+'.json',
};
scope.url = 'img/contact-placeholder.svg';
$http(req).then(function (response) {
scope.url = 'https://secure.gravatar.com/avatar/'+scope.emailHash+'.jpg?s='+scope.width+'&d=mm';
}, function (error) {
scope.url = 'img/contact-placeholder.svg';
});
}
},
template: '<img class="gravatar" alt="{{ name }}" height="{{ height }}" width="{{ width }}" src="https://secure.gravatar.com/avatar/{{ emailHash }}.jpg?s={{ width }}&d=mm">'
template: '<img class="gravatar" alt="{{ name }}" height="{{ height }}" width="{{ width }}" src="{{ url }}">'
};
});

View file

@ -8,15 +8,21 @@ angular.module('copayApp.directives')
transclude: true,
scope: {
title: '=walletSelectorTitle',
forceTitle: '=walletSelectorForceTitle',
show: '=walletSelectorShow',
wallets: '=walletSelectorWallets',
selectedWallet: '=walletSelectorSelectedWallet',
onSelect: '=walletSelectorOnSelect',
onHide: '=walletSelectorOnHide',
displayBalanceAsFiat : '=walletSelectorDisplayBalanceAsFiat'
},
link: function(scope, element, attrs) {
console.log(scope, element, attrs);
scope.hide = function() {
scope.show = false;
if (typeof scope.onHide === "function") {
scope.onHide()
}
};
scope.selectWallet = function(wallet) {
$timeout(function() {

View file

@ -795,63 +795,15 @@ angular.module('copayApp').config(function(historicLogProvider, $provide, $logPr
fromOnboarding: null
},
})
/*
*
* Feedback
*
*/
.state('tabs.feedback', {
url: '/feedback',
views: {
'tab-settings@tabs': {
templateUrl: 'views/feedback/send.html',
controller: 'sendController'
}
}
})
.state('tabs.shareApp', {
url: '/shareApp/:score/:skipped/:fromSettings',
url: '/shareApp',
views: {
'tab-settings@tabs': {
controller: 'completeController',
templateUrl: 'views/feedback/complete.html'
controller: 'shareAppController',
templateUrl: 'views/shareApp.html'
}
}
})
.state('tabs.rate', {
url: '/rate',
abstract: true
})
.state('tabs.rate.send', {
url: '/send/:score',
views: {
'tab-home@tabs': {
templateUrl: 'views/feedback/send.html',
controller: 'sendController'
}
}
})
.state('tabs.rate.complete', {
url: '/complete/:score/:skipped',
views: {
'tab-home@tabs': {
controller: 'completeController',
templateUrl: 'views/feedback/complete.html'
}
}
})
.state('tabs.rate.rateApp', {
url: '/rateApp/:score',
views: {
'tab-home@tabs': {
controller: 'rateAppController',
templateUrl: 'views/feedback/rateApp.html'
}
}
})
/*
*
* Buy or Sell Bitcoin

View file

@ -1,12 +1,13 @@
'use strict';
angular.module('copayApp.services').factory('bitcoincomService', function(gettextCatalog, $http, $log, lodash, moment, storageService, configService, platformInfo, nextStepsService, homeIntegrationsService) {
angular.module('copayApp.services').factory('bitcoincomService', function(gettextCatalog, nextStepsService, platformInfo) {
var root = {};
var credentials = {};
/*
* Development: 'testnet'
* Production: 'livenet'
*/
*/
var os = platformInfo.isAndroid ? 'android' : platformInfo.isIOS ? 'ios' : 'desktop';
credentials.NETWORK = 'livenet';
//credentials.NETWORK = 'testnet';
@ -20,28 +21,28 @@ angular.module('copayApp.services').factory('bitcoincomService', function(gettex
name: 'games',
title: gettextCatalog.getString('Bitcoin Cash Games'),
icon: 'icon-games',
href: 'http://cashgames.bitcoin.com'
href: 'https://cashgames.bitcoin.com'
};
var newsItem = {
name: 'news',
title: gettextCatalog.getString('News'),
icon: 'icon-news',
href: 'http://news.bitcoin.com'
href: 'https://news.bitcoin.com/?utm_source=WalletApp&utm_medium=' + os + '&utm_campaign=News'
};
var poolItem = {
name: 'pool',
title: gettextCatalog.getString('Mining Pool'),
icon: 'icon-mining',
href: 'http://pool.bitcoin.com'
href: 'https://pool.bitcoin.com/?utm_source=WalletApp&utm_medium=' + os + '&utm_campaign=Pool'
};
var toolsItem = {
name: 'tools',
title: gettextCatalog.getString('Tools'),
icon: 'icon-tools',
href: 'http://tools.bitcoin.com'
href: 'https://tools.bitcoin.com/?utm_source=WalletApp&utm_medium=' + os + '&utm_campaign=Tools'
};
var priceChartItem = {
@ -55,7 +56,7 @@ angular.module('copayApp.services').factory('bitcoincomService', function(gettex
name: 'faucet',
title: gettextCatalog.getString('Free Bitcoin Cash'),
icon: 'icon-faucet',
href: 'https://free.bitcoin.com/'
href: 'https://free.bitcoin.com/?utm_source=WalletApp&utm_medium=' + os + '&utm_campaign=Faucet'
};
var _getBitPay = function(endpoint) {

View file

@ -17,7 +17,27 @@ angular.module('copayApp.services').factory('clipboardService', function ($http,
// No supported
return;
}
};
root.readFromClipboard = function (cb) {
$log.debug("Read from clipboard");
if (platformInfo.isCordova) {
cordova.plugins.clipboard.paste(function(text) {
cb(text);
})
} else if (platformInfo.isNW) {
cb(nodeWebkitService.readFromClipboard());
} else {
navigator.clipboard.readText()
.then(function (text) {
cb(text);
})
.catch(function (err) {
$log.debug("Clipboard reading is not supported in browser..");
});
return;
}
};
return root;

View file

@ -1,59 +0,0 @@
'use strict';
angular.module('copayApp.services').factory('feedbackService', function($http, $log, $httpParamSerializer, configService) {
var root = {};
// var URL = "https://script.google.com/macros/s/1pWGRxVSUX9CxPqNAKZTppWHtDvyVtZv9HteY_TRQbWc/exec";
var URL = "https://wallet-data.bitcoin.com/feedback.php";
root.send = function(dataSrc, cb) {
$http(_post(dataSrc)).then(function() {
$log.info("SUCCESS: Feedback sent");
return cb();
}, function(err) {
$log.info("ERROR: Feedback sent anyway.");
return cb(err);
});
};
var _post = function(dataSrc) {
return {
method: 'POST',
url: URL,
headers: {
'content-type': 'application/x-www-form-urlencoded; charset=UTF-8'
},
data: $httpParamSerializer(dataSrc)
};
};
root.isVersionUpdated = function(currentVersion, savedVersion) {
if (!verifyTagFormat(currentVersion))
return 'Cannot verify the format of version tag: ' + currentVersion;
if (!verifyTagFormat(savedVersion))
return 'Cannot verify the format of the saved version tag: ' + savedVersion;
var current = formatTagNumber(currentVersion);
var saved = formatTagNumber(savedVersion);
if (saved.major > current.major || (saved.major == current.major && saved.minor > current.minor))
return false;
return true;
function verifyTagFormat(tag) {
var regex = /^v?\d+\.\d+\.\d+$/i;
return regex.exec(tag);
};
function formatTagNumber(tag) {
var formattedNumber = tag.replace(/^v/i, '').split('.');
return {
major: +formattedNumber[0],
minor: +formattedNumber[1],
patch: +formattedNumber[2]
};
};
};
return root;
});

View file

@ -228,10 +228,12 @@ angular.module('copayApp.services').factory('incomingData', function($log, $stat
} else if (/^https?:\/\//.test(data)) {
payproService.getPayProDetails(data, coin, function(err, details) {
if (err) {
root.showMenu({
data: data,
type: 'url'
});
if ($state.includes('tabs.scan')) {
root.showMenu({
data: data,
type: 'url'
});
}
return;
}
handlePayPro(details);

View file

@ -217,14 +217,6 @@ angular.module('copayApp.services')
});
};
root.setFeedbackInfo = function(feedbackValues, cb) {
storage.set('feedback', feedbackValues, cb);
};
root.getFeedbackInfo = function(cb) {
storage.get('feedback', cb);
};
root.storeFocusedWalletId = function(id, cb) {
storage.set('focusedWalletId', id || '', cb);
};

View file

@ -16,6 +16,8 @@
&.button-primary,
&.button-secondary,
&.button-light,
&.button-white,
&.button-green,
&.button-assertive {
&.button-standard {
@extend %button-standard;
@ -33,6 +35,10 @@
}
}
@mixin button-shadow() {
box-shadow: 0 2px 11px 0 #C1C1C1;;
}
.button {
&.button-secondary {
@include button-style($v-button-secondary-bg, $v-button-secondary-border, $v-button-secondary-active-bg, $v-button-secondary-active-border, $v-button-secondary-color);
@ -47,7 +53,24 @@
}
.button {
border-radius: 6px;
&.button-full {
display: block;
}
&-green {
@include button-style(#719561, #FFF, #606060, #FFF, #FFF);
@include button-clear(#FFF);
@include button-outline(#C1C1C1);
border: 0px;
@include button-shadow();
}
&-white {
@include button-style(#FFF, #C1C1C1, #C1C1C1, #FFF, #606060);
@include button-clear(#FFF);
@include button-outline(#C1C1C1);
@include button-shadow();
&.activated {
color: #FFF;
}
}
}

View file

@ -124,7 +124,6 @@
position: relative;
height: 70px;
border-color: $royal;
background-color: $royal;
padding-top: 20px;
margin-bottom: 50px;
text-align: center;

View file

@ -1,38 +0,0 @@
#rate-app {
background-color: #ffffff;
text-align: center;
.skip-rating {
color: $v-dark-gray;
position: absolute;
top: 5px;
right: 10px;
padding: 15px;
}
.icon-svg > img {
width: 80px;
height: 80px;
margin-top: 15px;
}
.feedback-title {
font-size: 20px;
font-weight: bold;
color: $v-dark-gray;
margin: 80px 50px 10px;
text-align: center;
}
.share-the-love-illustration {
width: 5rem;
margin: 1rem;
}
.subtitle {
padding: 10px 30px 20px 40px;
color: $v-mid-gray;
}
.rate-buttons {
bottom: 0;
width: 100%;
position: absolute;
background-color: $v-subtle-gray;
padding: 30px 0 15px;
}
}

View file

@ -1,18 +0,0 @@
#rate-card {
.item-heading {
font-weight: 700;
}
.row {
border: none;
}
.item-icon-right {
margin: 0;
}
.feedback-flow-button {
margin-bottom: 20px;
}
.icon-svg > img {
height: 1.8rem;
margin-bottom: 5px;
}
}

View file

@ -1,54 +0,0 @@
#send-feedback {
@extend .deflash-blue;
background-color: #ffffff;
.row {
border: none;
}
.skip {
color: rgba(255, 255, 255, 0.3);
}
.feedback-heading {
padding-top: 20px
}
.feedback-title {
padding-left: 10px;
font-size: 20px;
font-weight: bold;
color: $v-dark-gray;
}
.rating {
text-align: right;
padding-right: 15px;
}
.comment {
padding: 0 20px 20px;
font-size: 1rem;
line-height: 1.5em;
font-weight: 300;
color: $v-dark-gray;
}
.user-feedback {
border-top: 1px solid $v-subtle-gray;
border-bottom: 1px solid $v-subtle-gray;
padding: 20px;
width: 100%;
margin-bottom: 20px;
-webkit-appearance: none;
}
.send-feedback-star {
height: 1rem;
margin-left: 5px;
}
.form-fade-in {
opacity: 0;
animation-name: fadeIn;
animation-duration: .5s;
animation-fill-mode: forwards;
animation-timing-function: ease-in;
}
}
@keyframes fadeIn {
from { opacity: 0; }
to { opacity: 1; }
}

View file

@ -1,6 +1,6 @@
#complete {
#share-app {
background-color: #fff;
.complete-layout {
.share-app-layout {
display: flex;
flex-direction: column;
height: 100%;
@ -22,17 +22,6 @@
width: 5rem;
margin: 1rem;
}
.send-feedback-illustration {
height: 16rem;
margin: 1rem;
}
.feedback-title {
font-size: 20px;
font-weight: bold;
color: $v-dark-gray;
margin: 20px 10px;
text-align: center;
}
.subtitle {
padding: 10px 30px 20px;
text-align: center;

View file

@ -1,12 +1,28 @@
#tab-send {
@extend .deflash-blue;
&-header{
height: 300px;
width: 100%;
}
&-contacts {
height: calc(100vh - 300px - 50px - 44px); /* screen size - button container - bottom-tab-menu - header top */
&.ios {
height: calc(100vh - 300px - 50px - 44px - 18px); // Remove the notification-bar height on iOS
}
overflow: scroll;
}
.input {
width: 100%;
input {
width: 100%;
height: auto;
}
&.item {
height: 55px;
height: 57px;
background: #FFF;
border: 1px #D9D9D9 solid;
&::placeholder {
color: #DCDCDC;
}
}
i {
&.left {
@ -19,45 +35,22 @@
}
}
}
.qr-scan-icon {
cursor: pointer;
cursor: hand;
border-left: 1px solid rgb(228, 228, 228);
padding-left: 10px;
}
.qr-icon {
line-height: 20px;
}
.zero-state-cta {
padding-bottom: 3vh;
left: 0;
}
.send-heading {
font-size: 14px;
font-weight: bold;
padding: 0 0 16px 0;
border: none;
}
.send-header-wrapper {
padding: 10px;
background-color: white;
box-shadow: 0px 5px 10px 0px #cccccc;
}
.search-wrapper {
.send-wrapper {
&:after {
display: block;
position: relative;
height: 1px;
background: #DEDEDE;
bottom: 0;
content: '';
margin: 10px 6px 0px;
}
padding: 18px 9px 9px 9px;
background-color: #f2f2f2;
border-radius: 3px;
border: none;
.svg#Bitcoin_Symbol {
width: 14px;
.st0 {
fill: #cccccc;
}
}
&.focus {
background: none;
.svg#Bitcoin_Symbol {
display: none;
}
.search-input {
padding-left: 30px;
&:focus::-webkit-input-placeholder {
@ -65,57 +58,88 @@
}
}
}
.buttons {
margin: auto;
margin-top: 18px;
.button {
&-clipboard-paste {
margin-left: 0;
.address {
display: none;
}
.icon {
background: url(../img/icon-clipboard-paste.svg);
width: 15px;
height: 19px;
display: inline-block;
margin-bottom: 4px;
}
&.contains-address, &.contains-content {
.address {
display: none;
}
background: #FAB915;
color: #FFF !important;
border: 0;
@include button-shadow();
.icon {
background: url(../img/icon-clipboard-paste-white.svg);
}
&.contains-address {
.address {
display: inline;
}
.non-address {
display: none;
}
}
}
}
span {
font-size: 14px;
}
img {
height: 16px;
width: auto;
margin: 2px 0 4px;
}
height: 60px;
line-height: 16px;
margin-right: 0px;
width: 95%;
max-width: none;
padding: 2px;
&-qr {
font-weight: bold;
max-width: none;
width: 100%;
height: 95px;
margin-top: 20px;
img {
vertical-align: middle;
margin-right: 12px;
width: 43px;
height: 43px;
}
span {
font-size: 19px;
}
}
}
}
}
.abs-v-center {
position: absolute;
top: 50%;
transform: translateY(-50%);
}
.search-input {
background-color: transparent;
padding-left: 30px;
}
.separator-left {
border-left: 1px solid #d9d9df;
padding-left: 10px;
height: 70%;
}
.bitcoin-address {
border-top: none;
padding-bottom: .5rem;
@media(max-width: 480px) {
input {
font-size: 14px;
}
}
.icon {
line-height: 31px;
padding-top: 2px;
padding-bottom: 1px;
}
}
.show-more {
text-align: center;
padding: 20px;
font-size: 16px;
color: #387ef5;
font-weight: bold;
}
.sendTip {
padding-top: 5vh;
text-align: center;
& > .item-heading {
margin-top: 10px;
background: 0 none;
}
img {
content: $v-tab-send-selected-icon;
}
.item {
border-style: none;
}
& > .title {
font-size: 20px;
font-weight: bold;
color: $v-dark-gray;
margin: 20px 10px;
}
@ -123,34 +147,64 @@
font-size: 1rem;
line-height: 1.5em;
font-weight: 300;
color: $v-dark-gray;
color: #6F6F70;
margin: 20px 1em 2.5em;
}
.big-icon-svg{
.bg.green{
.big-icon-svg {
.bg.green {
padding: 0 10px;
box-shadow: none;
}
}
.buttons {
margin-top: 18px;
.button {
font-weight: bold;
font-size: 19px;
}
}
.button-first-contact img {
height: 19px;
width: 19px;
margin-right: 6px;
vertical-align: sub;
}
}
.item-heading {
line-height: 16px;
font-size: 14px;
font-weight: bold;
.subtitle {
color: #B5B2B2;
font-size: 12px;
font-weight: 300;
}
}
.list {
.item {
font-weight: 600;
p {
font-weight: normal;
}
&.item-icon-left {
padding-left: 64px;
}
color: #444;
border-top: none;
padding-top: 1.5rem;
padding-bottom: 1.5rem;
//border-top: none;
padding-top: 0.6rem;
padding-bottom: 0.6rem;;
.big-icon-svg {
left:5px;
& > .bg{
width:30px;
height:30px;
box-shadow: none;
}
left: 5px;
& > .bg {
width: 30px;
height: 30px;
box-shadow: none;
}
}
&:before {
display: block;
position: absolute;
width: 80%;
width: 100%;
height: 1px;
background: rgba(221, 221, 221, 0.3);
top: 0;
@ -163,7 +217,7 @@
&.item-heading {
&:before {
top: 99%;
width:100%;
width: 100%;
}
}
&:nth-child(2) {
@ -176,5 +230,40 @@
}
}
}
.scroll{height: 100%;}
.scroll {
height: 100%;
}
.card.contacts {
margin: 4px 4px 16px 4px;
border-radius: 6px;
box-shadow: 0px 2px 1px 0 #C1C1C1;
.gravatar {
border-radius: 30px;
height: 40px;
width: 40px;
}
}
///* iPhone 5/SE and other small screen devices */
@media only screen and (min-device-width : 320px) and (max-device-width : 568px) {
.send-wrapper .buttons .button-qr {
height: 60px;
span {
font-size: 16px;
}
}
#tab-send-header {
height: 270px;
}
#tab-send-contacts {
height: calc(100vh - 270px - 50px - 44px); /* screen size - button container - bottom-tab-menu - header top */
&.ios {
height: calc(100vh - 270px - 50px - 44px - 18px); // Remove the notification-bar height on iOS
}
}
}
}

View file

@ -22,10 +22,7 @@
@import "wallet-backup-phrase";
@import "zero-state";
@import "onboarding/onboarding";
@import "feedback/rateCard";
@import "feedback/send";
@import "feedback/complete";
@import "feedback/rateApp";
@import "shareApp";
@import "includes/actionSheet";
@import "export";
@import "import";