refactor(old): remove unused code

This commit is contained in:
Jason Dreyzehner 2016-10-03 14:21:46 -04:00
parent d1b2f6adb1
commit aad5227542
16 changed files with 0 additions and 3800 deletions

View File

@ -1,82 +0,0 @@
'use strict';
angular.module('copayApp.services').factory('go', function($window, $ionicSideMenuDelegate, $rootScope, $location, $state, $timeout, $log, profileService, platformInfo, nodeWebkit) {
var root = {};
root.openExternalLink = function(url, target) {
if (platformInfo.isNW) {
nodeWebkit.openExternalLink(url);
} else {
target = target || '_blank';
var ref = window.open(url, target, 'location=no');
}
};
root.is = function(name) {
return $state.is(name);
};
root.path = function(path, cb) {
$state.transitionTo(path)
.then(function() {
if (cb) return cb();
}, function() {
if (cb) return cb('animation in progress');
});
};
root.toggleLeftMenu = function() {
$ionicSideMenuDelegate.toggleLeft();
};
root.walletHome = function() {
var wallet = profileService.getWallet($stateParams.walletId);
if (wallet && !wallet.isComplete()) {
$log.debug("Wallet not complete at startup... redirecting");
$state.transitionTo('wallet.details', {
walletId: wallet.credentials.walletId
})
} else {
root.path('tabs.home');
}
};
root.confirm = function(params) {
$state.transitionTo('send.confirm', params)
};
root.send = function() {
root.path('tabs.send');
};
root.addWallet = function() {
$state.transitionTo('add');
};
root.preferences = function() {
$state.transitionTo('preferences');
};
root.preferencesGlobal = function() {
$state.transitionTo('tabs.settings');
};
root.reload = function() {
$state.reload();
};
// Global go. This should be in a better place TODO
// We don't do a 'go' directive, to use the benefits of ng-touch with ng-click
$rootScope.go = function(path) {
root.path(path);
};
$rootScope.openExternalLink = function(url, target) {
root.openExternalLink(url, target);
};
return root;
});

File diff suppressed because it is too large Load Diff

View File

@ -1,14 +0,0 @@
<a ng-click="index.setTab(item, false, 0, true)"
ng-style="{'color': index.tab == item.link ? index.backgroundColor : '#A5B2BF'}"
id="menu-{{item.link}}">
<i class="size-18 {{item.icon[index.tab == item.link]}} db"></i>
<span class="size-10 tu">
{{ item.title|translate }}
<span class="label round"
ng-style="{'background-color':index.backgroundColor}"
ng-if="item.link=='walletHome' && index.pendingTxProposalsCountForUs > 0">
{{ index.pendingTxProposalsCountForUs }}
</span>
</span>
<div ng-if="item.link == 'walletHome'" class="menu-wallet-home"></div>
</a>

View File

@ -1,18 +0,0 @@
<div class="dr-notification-wrapper" ng-repeat="noti in queue" ng-click="removeNotification(noti)">
<div class="dr-notification animated bounceInDown">
<div class="dr-notification-image" ng-switch on="noti.image">
<i class="{{noti.icon}}" ng-switch-when="false"></i>
<img ng-src="{{noti.image}}" ng-switch-default />
</div>
<div class="dr-notification-content">
<h3 class="dr-notification-title">{{noti.title|translate}}</h3>
<div class="dr-notification-text label radius" ng-show="noti.userData.color"
ng-style="{'background-color':noti.userData.color}">{{noti.content|translate}}
</div>
<div class="dr-notification-text" ng-show="!noti.userData.color"
>{{noti.content|translate}}
</div>
</div>
</div>
</div>

View File

@ -1,260 +0,0 @@
'use strict';
angular.module('copayApp.services').
factory('notification', function($timeout, platformInfo) {
var isCordova = platformInfo.isCordova;
var notifications = [];
/*
ls.getItem('notifications', function(err, data) {
if (data) {
notifications = JSON.parse(data);
}
});
*/
var queue = [];
var settings = {
info: {
duration: 6000,
enabled: true
},
funds: {
duration: 7000,
enabled: true
},
version: {
duration: 60000,
enabled: true
},
warning: {
duration: 7000,
enabled: true
},
error: {
duration: 7000,
enabled: true
},
success: {
duration: 5000,
enabled: true
},
progress: {
duration: 0,
enabled: true
},
custom: {
duration: 35000,
enabled: true
},
details: true,
localStorage: false,
html5Mode: false,
html5DefaultIcon: 'img/favicon.ico'
};
function html5Notify(icon, title, content, ondisplay, onclose) {
if (window.webkitNotifications && window.webkitNotifications.checkPermission() === 0) {
if (!icon) {
icon = 'img/favicon.ico';
}
var noti = window.webkitNotifications.createNotification(icon, title, content);
if (typeof ondisplay === 'function') {
noti.ondisplay = ondisplay;
}
if (typeof onclose === 'function') {
noti.onclose = onclose;
}
noti.show();
} else {
settings.html5Mode = false;
}
}
return {
/* ========== SETTINGS RELATED METHODS =============*/
disableHtml5Mode: function() {
settings.html5Mode = false;
},
disableType: function(notificationType) {
settings[notificationType].enabled = false;
},
enableHtml5Mode: function() {
// settings.html5Mode = true;
settings.html5Mode = this.requestHtml5ModePermissions();
},
enableType: function(notificationType) {
settings[notificationType].enabled = true;
},
getSettings: function() {
return settings;
},
toggleType: function(notificationType) {
settings[notificationType].enabled = !settings[notificationType].enabled;
},
toggleHtml5Mode: function() {
settings.html5Mode = !settings.html5Mode;
},
requestHtml5ModePermissions: function() {
if (window.webkitNotifications) {
if (window.webkitNotifications.checkPermission() === 0) {
return true;
} else {
window.webkitNotifications.requestPermission(function() {
if (window.webkitNotifications.checkPermission() === 0) {
settings.html5Mode = true;
} else {
settings.html5Mode = false;
}
});
return false;
}
} else {
return false;
}
},
/* ============ QUERYING RELATED METHODS ============*/
getAll: function() {
// Returns all notifications that are currently stored
return notifications;
},
getQueue: function() {
return queue;
},
/* ============== NOTIFICATION METHODS ==============*/
info: function(title, content, userData) {
return this.awesomeNotify('info', 'fi-info', title, content, userData);
},
funds: function(title, content, userData) {
return this.awesomeNotify('funds', 'icon-receive', title, content, userData);
},
version: function(title, content, severe) {
return this.awesomeNotify('version', severe ? 'fi-alert' : 'fi-flag', title, content);
},
error: function(title, content, userData) {
return this.awesomeNotify('error', 'fi-x', title, content, userData);
},
success: function(title, content, userData) {
return this.awesomeNotify('success', 'fi-check', title, content, userData);
},
warning: function(title, content, userData) {
return this.awesomeNotify('warning', 'fi-alert', title, content, userData);
},
new: function(title, content, userData) {
return this.awesomeNotify('warning', 'fi-plus', title, content, userData);
},
sent: function(title, content, userData) {
return this.awesomeNotify('warning', 'icon-paperplane', title, content, userData);
},
awesomeNotify: function(type, icon, title, content, userData) {
/**
* Supposed to wrap the makeNotification method for drawing icons using font-awesome
* rather than an image.
*
* Need to find out how I'm going to make the API take either an image
* resource, or a font-awesome icon and then display either of them.
* Also should probably provide some bits of color, could do the coloring
* through classes.
*/
// image = '<i class="icon-' + image + '"></i>';
return this.makeNotification(type, false, icon, title, content, userData);
},
notify: function(image, title, content, userData) {
// Wraps the makeNotification method for displaying notifications with images
// rather than icons
return this.makeNotification('custom', image, true, title, content, userData);
},
makeNotification: function(type, image, icon, title, content, userData) {
var notification = {
'type': type,
'image': image,
'icon': icon,
'title': title,
'content': content,
'timestamp': +new Date(),
'userData': userData
};
notifications.push(notification);
if (settings.html5Mode) {
html5Notify(image, title, content, function() {
// inner on display function
}, function() {
// inner on close function
});
}
//this is done because html5Notify() changes the variable settings.html5Mode
if (!settings.html5Mode) {
queue.push(notification);
$timeout(function removeFromQueueTimeout() {
queue.splice(queue.indexOf(notification), 1);
}, settings[type].duration);
}
// Mobile notification
if (window && window.navigator && window.navigator.vibrate) {
window.navigator.vibrate([200, 100, 200]);
};
if (document.hidden && (type == 'info' || type == 'funds') && !isCordova) {
new window.Notification(title, {
body: content,
icon: 'img/notification.png'
});
}
this.save();
return notification;
},
/* ============ PERSISTENCE METHODS ============ */
save: function() {
// Save all the notifications into localStorage
if (settings.localStorage) {
localStorage.setItem('notifications', JSON.stringify(notifications));
}
},
restore: function() {
// Load all notifications from localStorage
},
clear: function() {
notifications = [];
this.save();
}
};
}
);

View File

@ -1,115 +0,0 @@
'use strict';
angular.module('copayApp.services')
.factory('notificationService', function profileServiceFactory($filter, notification, lodash, configService, gettext) {
var root = {};
var groupingTime = 5000;
var lastNotificationOnWallet = {};
root.getLast = function(walletId) {
var last = lastNotificationOnWallet[walletId];
if (!last) return null;
return Date.now() - last.ts < groupingTime ? last : null;
};
root.storeLast = function(notificationData, walletId) {
if (notificationData.type == 'NewAddress')
return;
lastNotificationOnWallet[walletId] = {
creatorId: notificationData.creatorId,
type: notificationData.type,
ts: Date.now(),
};
};
root.shouldSkip = function(notificationData, last) {
if (!last) return false;
// rules...
if (last.type === 'NewTxProposal' &&
notificationData.type === 'TxProposalAcceptedBy')
return true;
if (last.type === 'TxProposalFinallyAccepted' &&
notificationData.type === 'NewOutgoingTx')
return true;
if (last.type === 'TxProposalRejectedBy' &&
notificationData.type === 'TxProposalFinallyRejected')
return true;
return false;
};
root.newBWCNotification = function(notificationData, walletId, walletName) {
var last = root.getLast(walletId);
root.storeLast(notificationData, walletId);
if (root.shouldSkip(notificationData, last))
return;
var config = configService.getSync();
config.colorFor = config.colorFor || {};
var color = config.colorFor[walletId] || '#4A90E2';
var name = config.aliasFor[walletId] || walletName;
switch (notificationData.type) {
case 'NewTxProposal':
notification.new(gettext('New Payment Proposal'),
name, {
color: color
});
break;
case 'TxProposalAcceptedBy':
notification.success(gettext('Payment Proposal Signed by Copayer'),
name, {
color: color
});
break;
case 'TxProposalRejectedBy':
notification.error(gettext('Payment Proposal Rejected by Copayer'),
name, {
color: color
});
break;
case 'TxProposalFinallyRejected':
notification.error(gettext('Payment Proposal Rejected'),
name, {
color: color
});
break;
case 'NewOutgoingTx':
notification.sent(gettext('Payment Sent'),
name, {
color: color
});
break;
case 'NewIncomingTx':
notification.funds(gettext('Funds received'),
name, {
color: color
});
break;
case 'ScanFinished':
notification.success(gettext('Scan Finished'),
name, {
color: color
});
break;
case 'NewCopayer':
// No UX notification
break;
case 'BalanceUpdated':
// No UX notification
break;
}
};
return root;
});

View File

@ -1,86 +0,0 @@
<div class="topbar-container" ng-include="'views/includes/topbar.html'"
ng-init="titleSection='Global preferences'; closeToHome = true; noColor = true">
</div>
<div class="content preferences" ng-controller="preferencesGlobalController" ng-init="init()">
<h4></h4>
<ul>
<li href ui-sref="preferencesLanguage">
<div class="right text-gray">
{{currentLanguageName|translate}}
<i class="icon-arrow-right3 size-24 right"></i>
</div>
<div translate>Language</div>
</li>
</ul>
<h4></h4>
<ul class="no-bullet m0">
<li href ui-sref="preferencesUnit">
<div class="right text-gray">
{{unitName}}
<i class="icon-arrow-right3 size-24 right"></i>
</div>
<div translate>Unit</div>
</li>
<li href ui-sref="preferencesAltCurrency">
<div class="right text-gray">
{{selectedAlternative.name}}
<i class="icon-arrow-right3 size-24 right"></i>
</div>
<div translate>Alternative Currency</div>
</li>
</ul>
<h4></h4>
<ul class="no-bullet m0">
<li href ui-sref="preferencesFee">
<div class="right text-gray">
{{feeOpts[currentFeeLevel]|translate}}
<i class="icon-arrow-right3 size-24 right"></i>
</div>
<div translate>Bitcoin Network Fee Policy</div>
</li>
</ul>
<ion-toggle ng-model="spendUnconfirmed" toggle-class="toggle-balanced" ng-change="spendUnconfirmedChange()">
<span class="toggle-label" translate>Use Unconfirmed Funds</span>
</ion-toggle>
<div ng-show="usePushNotifications && PNEnabledByUser">
<h4></h4>
<ion-toggle ng-model="pushNotifications" toggle-class="toggle-balanced" ng-change="pushNotificationsChange()">
<span class="toggle-label" translate>Enable push notifications</span>
</ion-toggle>
</div>
<div class="m20t" ng-show="usePushNotifications && !PNEnabledByUser && isIOSApp">
<div class="text-left text-gray size-12 m10" translate>Push notifications for Copay are currently disabled. Enable them in the Settings app.</div>
<ul class="no-bullet m0" ng-click="openSettings()">
<li ng-style="{'color':index.backgroundColor}" translate>Open Settings app</li>
</ul>
</div>
<h4></h4>
<ion-toggle ng-show="!index.isWindowsPhoneApp" ng-model="glideraEnabled" toggle-class="toggle-balanced" ng-change="glideraChange()">
<span class="toggle-label" translate>Enable Glidera Service</span>
</ion-toggle>
<h4></h4>
<ion-toggle ng-show="!index.isWindowsPhoneApp" ng-model="coinbaseEnabled" toggle-class="toggle-balanced" ng-change="coinbaseChange()">
<span class="toggle-label" translate>Enable Coinbase Service</span>
</ion-toggle>
<h4></h4>
<ul class="no-bullet m0">
<li href ui-sref="about">
<i class="icon-arrow-right3 size-24 right text-gray"></i>
<div translate>About Copay</div>
</li>
</ul>
<h4></h4>
</div>
<div class="extra-margin-bottom"></div>

View File

@ -1,94 +0,0 @@
'use strict';
angular.module('copayApp.controllers').controller('preferencesGlobalController',
function($scope, $rootScope, $log, configService, uxLanguage, platformInfo, pushNotificationsService, profileService, feeService) {
$scope.init = function() {
var config = configService.getSync();
var isCordova = platformInfo.isCordova;
if (isCordova && StatusBar.isVisible) {
StatusBar.backgroundColorByHexString("#4B6178");
}
$scope.unitName = config.wallet.settings.unitName;
$scope.currentLanguageName = uxLanguage.getCurrentLanguageName();
$scope.selectedAlternative = {
name: config.wallet.settings.alternativeName,
isoCode: config.wallet.settings.alternativeIsoCode
};
$scope.feeOpts = feeService.feeOpts;
$scope.currentFeeLevel = feeService.getCurrentFeeLevel();
$scope.usePushNotifications = isCordova && !platformInfo.isWP;
$scope.PNEnabledByUser = true;
$scope.isIOSApp = platformInfo.isIOS && isCordova;
if ($scope.isIOSApp) {
cordova.plugins.diagnostic.isRemoteNotificationsEnabled(function(isEnabled) {
$scope.PNEnabledByUser = isEnabled;
$scope.$digest();
});
}
$scope.spendUnconfirmed = config.wallet.spendUnconfirmed;
$scope.glideraEnabled = config.glidera.enabled;
$scope.coinbaseEnabled = config.coinbase.enabled;
$scope.pushNotifications = config.pushNotifications.enabled;
};
$scope.openSettings = function() {
cordova.plugins.diagnostic.switchToSettings(function() {
$log.debug('switched to settings');
}, function(err) {
$log.debug(err);
});
}
$scope.spendUnconfirmedChange = function() {
var opts = {
wallet: {
spendUnconfirmed: $scope.spendUnconfirmed
}
};
configService.set(opts, function(err) {
if (err) $log.debug(err);
});
};
$scope.pushNotificationsChange = function() {
var opts = {
pushNotifications: {
enabled: $scope.pushNotifications
}
};
configService.set(opts, function(err) {
if (opts.pushNotifications.enabled)
pushNotificationsService.enableNotifications(profileService.walletClients);
else
pushNotificationsService.disableNotifications(profileService.walletClients);
if (err) $log.debug(err);
});
};
$scope.glideraChange = function() {
var opts = {
glidera: {
enabled: $scope.glideraEnabled
}
};
configService.set(opts, function(err) {
$rootScope.$emit('Local/GlideraUpdated');
if (err) $log.debug(err);
});
};
$scope.coinbaseChange = function() {
var opts = {
coinbase: {
enabled: $scope.coinbaseEnabled
}
};
configService.set(opts, function(err) {
$rootScope.$emit('Local/CoinbaseUpdated');
if (err) $log.debug(err);
});
};
});

View File

@ -1,57 +0,0 @@
<div class="sidebar" ng-controller="sidebarController as sidebar">
<header>
<img ng-if="sidebar.isWindowsPhoneApp" src="img/logo-negative.png" alt="Copay" width="80">
<img ng-if="!sidebar.isWindowsPhoneApp" src="img/logo-negative.svg" alt="Copay" width="80">
<div ng-include="'views/includes/version.html'"></div>
</header>
<ion-content>
<ul class="pr">
<li ng-show="sidebar.wallets[0]"
ng-repeat="item in sidebar.wallets track by $index" ng-class="{'selected': item.id == index.walletId}" class="nav-item"
menu-toggle href ui-sref="walletHome" on-tap="sidebar.switchWallet(item.id, index.walletId)">
<div class="avatar-wallet" ng-style="{'background-color':item.color}">
<i class="icon-wallet size-21"></i>
</div>
<div class="name-wallet" ng-class="{'m8t':item.n == 1}">{{item.name || item.id}}</div>
<div class="size-12" ng-show="item.n > 1" translate>{{item.m}}-of-{{item.n}}</div>
</li>
<li menu-toggle href ui-sref="add">
<i class="icon-arrow-right3 size-18 right m10t vm"></i>
<i class="fi-plus size-24 icon vm"></i>
<div class="tu text-bold">
<span class="size-12" translate>Add wallet</span>
</div>
<div translate>Create, join or import</div>
</li>
<li menu-toggle href ui-sref="bitpayCard" ng-show="index.isComplete && sidebar.bitpayCardEnabled">
<i class="icon-arrow-right3 size-18 right m10t vm"></i>
<i class="fi-credit-card size-24 icon vm"></i>
<div class="tu text-bold m10t">
<span class="size-12" translate>BitPay Card</span>
</div>
</li>
<li ng-show="!index.noFocusedWallet && !index.isWindowsPhoneApp && (index.glideraEnabled || index.coinbaseEnabled)" menu-toggle href ui-sref="buyandsell">
<i class="icon-arrow-right3 size-18 right m10t vm"></i>
<i class="icon-bank size-24 icon vm"></i>
<div class="tu text-bold m5t">
<span class="size-12" translate>Buy and Sell</span>
</div>
</li>
<li menu-toggle href ui-sref="amazon" ng-show="index.isComplete">
<i class="icon-arrow-right3 size-18 right m10t vm"></i>
<i class="fi-shopping-bag size-24 icon vm"></i>
<div class="tu text-bold m10t">
<span class="size-12" translate>Gift Cards</span>
</div>
</li>
<li ng-show="!index.noFocusedWallet" menu-toggle href ui-sref="preferencesGlobal">
<i class="icon-arrow-right3 size-18 right m10t vm"></i>
<i class="fi-widget size-24 icon vm"></i>
<div class="tu text-bold">
<span class="size-12" translate>Settings</span>
</div>
<div translate>Global preferences</div>
</li>
</ul>
</ion-content>
</div>

View File

@ -1,194 +0,0 @@
'use strict';
angular.module('copayApp.controllers').controller('tabScanController', function($scope, $timeout, $ionicModal, $log, $ionicPopup, configService, gettextCatalog, platformInfo, bitcore, lodash, $state, incomingData) {
var isCordova = platformInfo.isCordova;
var isWP = platformInfo.isWP;
var isIOS = platformInfo.isIOS;
var _showAlert = function(title, msg, cb) {
$log.warn(title + ":"+ msg);
var alertPopup = $ionicPopup.alert({
title: title,
template: msg
});
if (!cb) cb = function(res) {};
alertPopup.then(cb);
};
var _dataScanned = function(data) {
$log.debug('Scanned:' + data);
if (!incomingData.redir(data)) {
$log.warn('Fail to process scanned data');
_showAlert('Bad bitcoin address', 'Could not recognize the bitcoin address', function(res) {
$scope.init();
});
}
};
var onSuccess = function(result) {
$timeout(function() {
window.plugins.spinnerDialog.hide();
}, 100);
if (isWP && result.cancelled) return;
$timeout(function() {
var data = isIOS ? result : result.text;
// Check if the current page is tabs.scan
if ($state.is('tabs.scan')) {
_dataScanned(data);
return;
}
}, 1000);
};
var onError = function(error) {
$timeout(function() {
window.plugins.spinnerDialog.hide();
}, 100);
};
$scope.cordovaOpenScanner = function() {
window.plugins.spinnerDialog.show(null, gettextCatalog.getString('Preparing camera...'), true);
$timeout(function() {
if (isIOS) {
cloudSky.zBar.scan({}, onSuccess, onError);
} else {
cordova.plugins.barcodeScanner.scan(onSuccess, onError);
}
if ($scope.beforeScan) {
$scope.beforeScan();
}
}, 100);
};
$scope.modalOpenScanner = function() {
$ionicModal.fromTemplateUrl('views/modals/scanner.html', {
scope: $scope,
animation: 'slide-in-up'
}).then(function(modal) {
$scope.scannerModal = modal;
$scope.scannerModal.show();
});
};
$scope.openScanner = function() {
if (isCordova) {
$scope.cordovaOpenScanner();
} else {
$scope.modalOpenScanner();
}
};
// QR code Scanner
var video;
var canvas;
var $video;
var context;
var localMediaStream;
var prevResult;
var scanTimer;
var _scan = function(evt) {
if (localMediaStream) {
context.drawImage(video, 0, 0, 300, 225);
try {
qrcode.decode();
} catch (e) {
//qrcodeError(e);
}
}
scanTimer = $timeout(_scan, 800);
};
var _scanStop = function() {
$timeout.cancel(scanTimer);
if (localMediaStream && localMediaStream.active) {
var localMediaStreamTrack = localMediaStream.getTracks();
for (var i = 0; i < localMediaStreamTrack.length; i++) {
localMediaStreamTrack[i].stop();
}
} else {
try {
localMediaStream.stop();
} catch (e) {
// Older Chromium not support the STOP function
};
}
localMediaStream = null;
if (video && video.src) video.src = '';
};
qrcode.callback = function(data) {
if (prevResult != data) {
prevResult = data;
return;
}
// Check if the current page is tabs.scan
_scanStop();
if ($state.is('tabs.scan')) {
_dataScanned(data);
return;
}
$scope.cancel();
};
var _successCallback = function(stream) {
video.src = (window.URL && window.URL.createObjectURL(stream)) || stream;
localMediaStream = stream;
video.play();
$timeout(_scan, 1000);
};
var _videoError = function(err) {
$scope.cancel();
};
var setScanner = function() {
navigator.getUserMedia = navigator.getUserMedia ||
navigator.webkitGetUserMedia || navigator.mozGetUserMedia ||
navigator.msGetUserMedia;
window.URL = window.URL || window.webkitURL ||
window.mozURL || window.msURL;
};
$scope.init = function() {
if (isCordova) {
$scope.cordovaOpenScanner();
return;
}
setScanner();
$timeout(function() {
if ($scope.beforeScan) {
$scope.beforeScan();
}
canvas = document.getElementById('qr-canvas');
context = canvas.getContext('2d');
video = document.getElementById('qrcode-scanner-video');
$video = angular.element(video);
canvas.width = 300;
canvas.height = 225;
context.clearRect(0, 0, 300, 225);
navigator.getUserMedia({
video: true
}, _successCallback, _videoError);
}, 500);
};
$scope.cancel = function() {
_scanStop();
$scope.scannerModal.hide();
$scope.scannerModal.remove();
};
$scope.$on("$destroy", function(){
_scanStop();
});
});

View File

@ -1,28 +0,0 @@
<nav ng-controller="topbarController as topbar"
class="tab-bar" ng-style="{'background-color': noColor ? '#4B6178' : index.backgroundColor}">
<section class="left-small">
<a id="hamburger" class="p10" ng-show="!goBackToState && !closeToHome && !index.noFocusedWallet && index.physicalScreenWidth < 768"
on-tap="index.toggleLeftMenu()"><i class="fi-list size-24"></i>
</a>
<a ng-show="goBackToState" ng-click="$root.go(goBackToState); goBackToState = null"><i class="icon-arrow-left3 icon-back"></i>
<span class="text-back" translate>Back</span>
</a>
<a ng-show="closeToHome" class="p10 "
ng-click="topbar.goHome(); index.setCompactTxHistory(); closeToHome = null">
<span class="text-close" translate>Close</span>
</a>
</section>
<section class="right-small" ng-show="showPreferences && !index.noFocusedWallet">
<a class="p10" ng-click="topbar.goPreferences(); index.setCompactTxHistory()">
<i class="fi-widget size-24"></i>
</a>
</section>
<section class="middle tab-bar-section">
<h1 class="title ellipsis">
{{(titleSection|translate) || (index.alias || index.walletName)}}
</h1>
</section>
</nav>

View File

@ -1,13 +0,0 @@
'use strict';
angular.module('copayApp.controllers').controller('topbarController', function(go) {
this.goHome = function() {
go.walletHome();
};
this.goPreferences = function() {
go.preferences();
};
});

View File

@ -1,11 +0,0 @@
<div class="row columns p20" ng-controller="uriController">
<div class="text-center">
<logo width="146"></logo>
<div class="text-white" ng-include="'views/includes/version.html'"></div>
</div>
<h3 class="text-center" translate>
Please wait to be redirected...
</h3>
</div>

View File

@ -1,12 +0,0 @@
'use strict';
angular.module('copayApp.controllers').controller('uriController',
function($stateParams, $log, openURLService) {
/* This is only for BROWSER links, it is not excecuted on mobile devices */
$log.info('DEEP LINK from Browser:' + $stateParams.url);
openURLService.handleURL({
url: $stateParams.url
});
});

View File

@ -1,534 +0,0 @@
<div class="topbar-container"
ng-include="'views/includes/topbar.html'"
ng-init="showPreferences = true" ng-show="!index.noFocusedWallet">
</div>
<div ng-controller="walletHomeController as home">
<div class="row columns m30tp" ng-show="index.noFocusedWallet">
<div class="text-center size-12 text-warning m20b">
<i class="fi-alert"></i> <span translate>You do not have any wallet</span>
</div>
<button class="button black round expand" href ui-sref="add" translate>Create</button>
</div>
<div class="onGoingProcess" ng-show="index.updating">
<div class="onGoingProcess-content" ng-style="{'background-color':index.backgroundColor}">
<div class="spinner">
<div class="rect1"></div>
<div class="rect2"></div>
<div class="rect3"></div>
<div class="rect4"></div>
<div class="rect5"></div>
</div>
<span translate>Updating Wallet...</span>
</div>
</div>
<div class="oh" ng-show="!index.noFocusedWallet">
<!--
WalletHome
-->
<div id="walletHome" class="walletHome tab-view tab-in">
<ion-content on-release="index.allowPullToRefresh = true;"
on-drag-right="index.allowRefresher()" delegate-handle="my-handle" overflow-scroll="true">
<ion-refresher
ng-if="index.allowPullToRefresh && index.isCordova"
pulling-icon="ion-ios-refresh"
spinner="ios-small"
on-refresh="index.updateAll({triggerTxUpdate: true})">
</ion-refresher>
<div class="oh pr">
<div ng-style="{'background-color':index.backgroundColor}" class="amount">
<div ng-if="!index.notAuthorized && !index.updating">
<div class="m15t" ng-show="index.updateError" ng-click='index.updateAll({triggerTxUpdate: true})'>
<span class="size-12 db m10b">{{index.updateError|translate}}</span>
<button class="outline white tiny round" translate>Tap to retry</button>
</div>
<div ng-show="index.walletScanStatus == 'error'" ng-click='index.retryScan()'>
<span translate>Scan status finished with error</span>
<br><span translate>Tap to retry</span>
</div>
<div ng-click='index.updateAll({triggerTxUpdate: true})' ng-show="!index.updateError && index.walletScanStatus != 'error' && !index.shouldHideBalance" on-hold="index.onHold()">
<strong class="size-36">{{index.totalBalanceStr}}</strong>
<div class="size-14" ng-if="index.totalBalanceAlternative">{{index.totalBalanceAlternative}} {{index.alternativeIsoCode}}</div>
<div class="size-14" ng-if="index.pendingAmount">
<span translate>Pending Confirmation</span>: {{index.pendingAmountStr}}
</div>
</div>
<div ng-show="!index.updateError && index.walletScanStatus != 'error' && index.shouldHideBalance" on-hold="index.onHold()">
<strong class="size-24" translate>[Balance Hidden]</strong>
<div class="size-14" translate>
Tap and hold to show
</div>
</div>
</div>
<div ng-if="index.updating">
<div class="size-36">
<strong>...</strong>
</div>
</div>
</div> <!-- amount -->
<div class="wallet-info">
<span ng-include="'views/includes/walletInfo.html'"></span>
</div>
<div class="camera-icon" ng-show="index.isComplete">
<qr-scanner on-scan="home.onQrCodeScanned(data)"></qr-scanner>
</div>
</div> <!-- oh -->
<div class="p60b">
<div class="oh pr m20t" ng-show="index.incorrectDerivation">
<div class="text-center text-warning">
<i class="fi-alert"></i>
<span translate>
WARNING: Key derivation is not working on this device/wallet. Actions cannot be performed on this wallet.
</span>
</div>
</div>
<div class="oh pr m20t" ng-show="index.notAuthorized && !index.updating">
<div class="text-center text-warning">
<i class="fi-alert"></i>
<span translate>
WARNING: Wallet not registered
</span>
</div>
<div class="text-center text-gray m15r m15l" translate>
This wallet is not registered at the given Bitcore Wallet Service (BWS). You can recreate it from the local information.
</div>
<div class="text-center m10t ">
<span class="button outline round dark-gray tiny"
ng-click="index.recreate()">
<span translate>Recreate</span>
</span>
</div>
</div>
<div class="release size-12" ng-show="newRelease" ng-click="$root.openExternalLink('https://github.com/bitpay/copay/releases/latest')">
<span>{{newRelease}}</span><i class="icon-arrow-right3 right size-18"></i>
</div>
<div ng-if="index.txps[0]">
<h4 ng-show="index.requiresMultipleSignatures" class="title m0" translate>Payment Proposals</h4>
<h4 ng-show="!index.requiresMultipleSignatures" class="title m0" translate>Unsent transactions</h4>
<div ng-repeat="tx in index.txps">
<div ng-include="index.txTemplateUrl"></div>
</div>
<div class="text-gray text-center size-12 p10t"
ng-show="index.lockedBalanceSat">
<span translate>Total Locked Balance</span>:
<b>{{index.lockedBalanceStr}} </b>
<span> {{index.lockedBalanceAlternative}}
{{index.alternativeIsoCode}} </span>
</div>
</div>
<!-- Activity -->
<h4 class="title" ng-click="index.startSearch(); openSearchModal()" ng-show="!index.notAuthorized">
<span translate>Activity</span>
<i class="dib m5l size-16 pointer fi-magnifying-glass"></i>
</h4>
<div class="oh pr m20t text-gray size-12 text-center"
ng-show="!index.loadingWallet && !index.txHistory[0] && !index.updatingTxHistory && !index.txHistoryError && !index.updateError && !index.notAuthorized"
translate>No transactions yet
</div>
<div class="oh pr" ng-show="(index.txHistory[0] || index.txProgress > 5) && !index.notAuthorized">
<div ng-show="index.updatingTxHistory && index.txProgress > 5">
<div class="row p20 text-center">
<div class="columns large-12 medium-12 small-12 m10b">
<ion-spinner class="spinner-dark" icon="lines"></ion-spinner>
</div>
<div class="size-12 text-gray m20t">
<div translate>{{index.txProgress}} transactions downloaded</div>
<div translate>Updating transaction history. Please stand by.</div>
</div>
</div>
</div>
<div ng-if="index.txHistory[0] && index.updatingTxHistory && index.newTx" class="row collapse last-transactions-content animated fadeInDown">
<div class="large-6 medium-6 small-6 columns size-14">
<div class="m10r left">
<img src="img/icon-new.svg" width="40">
</div>
<div class="m10t" style="background:#eee; width: 8em; margin-left: 52px; line-height:0.6em">
<span>&nbsp;</span>
</div>
<div style="margin-top:5px; background:#eee; width: 6em; margin-left: 52px; line-height:0.6em">
<span>&nbsp;</span>
</div>
</div>
</div>
<div ng-repeat="btx in index.txHistory track by btx.txid"
ng-click="home.openTxModal(btx)"
class="row collapse last-transactions-content">
<div class="large-6 medium-6 small-6 columns size-14">
<div class="m10r left">
<img src="img/icon-receive-history.svg" alt="sync" width="40" ng-show="btx.action == 'received'">
<img src="img/icon-sent-history.svg" alt="sync" width="40" ng-show="btx.action == 'sent'">
<img src="img/icon-moved.svg" alt="sync" width="40" ng-show="btx.action == 'moved'">
</div>
<div class="m10t">
<span ng-show="btx.action == 'received'">
<span class="ellipsis">
<span ng-if="btx.note.body">{{btx.note.body}}</span>
<span ng-if="!btx.note.body" translate> Received</span>
</span>
</span>
<span ng-show="btx.action == 'sent'">
<span class="ellipsis">
<span ng-if="btx.message">{{btx.message}}</span>
<span ng-if="!btx.message && btx.note.body">{{btx.note.body}}</span>
<span ng-if="!btx.message && !btx.note.body && index.addressbook[btx.addressTo]">{{index.addressbook[btx.addressTo]}}</span>
<span ng-if="!btx.message && !btx.note.body && !index.addressbook[btx.addressTo]" translate> Sent</span>
</span>
</span>
<span ng-show="btx.action == 'moved'">
<span class="ellipsis">
<span ng-if="btx.note.body">{{btx.note.body}}</span>
<span ng-if="!btx.note.body" translate>Moved</span>
</span>
</span>
<span class="label tu warning radius" ng-show="btx.action == 'invalid'" translate>Invalid</span>
</div>
</div>
<div class="large-5 medium-5 small-5 columns text-right" >
<span class="size-16" ng-class="{'text-bold': btx.recent}">
<span ng-if="btx.action == 'received'">+</span>
<span ng-if="btx.action == 'sent'">-</span>
<span class="size-12" ng-if="btx.action == 'invalid'" translate>
(possible double spend)
</span>
<span ng-if="btx.action != 'invalid'">
{{btx.amountStr}}
</span>
</span>
<div class="size-12 text-gray">
<time ng-if="btx.time">{{btx.time * 1000 | amTimeAgo}}</time>
<span translate class="text-warning"
ng-show="!btx.time && (!btx.confirmations || btx.confirmations == 0)">
Unconfirmed
</span>
</div>
</div>
<div class="large-1 medium-1 small-1 columns text-right m10t">
<i class="icon-arrow-right3 size-18"></i>
</div>
</div>
<div class="row m20t text-center" ng-show="index.historyRendering && !index.ching">
<div class="columns large-12 medium-12 small-12">
<ion-spinner class="spinner-stable" icon="lines"></ion-spinner>
</div>
</div>
<ion-infinite-scroll
ng-if="index.historyShowMore"
on-infinite="index.showMore()"
distance="1%">
</ion-infinite-scroll>
</div>
</div>
</ion-content>
<div class="extra-margin-bottom"></div>
</div> <!-- END WalletHome -->
<!--
receive
-->
<div id="receive" class="receive tab-view">
<div ng-show="index.needsBackup" class="p60t columns text-center">
<div class="circle-icon">
<i class="fi-alert size-48"></i>
</div>
<h5 translate>Backup Needed</h5>
<p class="text-gray m20b columns" translate>
Before receiving funds, you must backup your wallet. If this device is lost, it is impossible to access your funds without a backup.
</p>
<button class="m20t button black expand round" href ui-sref="backup" ng-style="{'background-color':index.backgroundColor}" >
<span translate>Backup now</span>
</button>
</div>
<div ng-show="!index.needsBackup">
<div class="box-notification m20t" ng-show="home.addrError">
<span class="text-warning">
{{home.addrError|translate}}
</span>
</div>
<div class="row">
<!-- Address-->
<div class="large-12 columns">
<h2 class="text-center m10t" translate>My Bitcoin address</h2>
<div class="text-center" ng-click="home.copyToClipboard(home.addr, $event)" ng-show="home.addr || home.generatingAddress">
<qrcode size="220" data="bitcoin:{{home.addr}}"></qrcode>
<div ng-show="home.generatingAddress" style="position:relative; top:-226px; height:0px">
<div style="height:220px; width:220px; margin:auto; background: white">
<ion-spinner class="spinner-stable" icon="lines" style="margin-top: 85px"></ion-spinner>
</div>
</div>
<div class="m10t" >
<h4 ng-class="{'enable_text_select': !index.isCordova}" class="size-12">
{{home.generatingAddress ? '...' : home.addr}}
</h4>
</div>
</div>
</div>
</div>
<div class="row m20t">
<div class="small-12 columns" ng-show="index.isCordova && home.addr">
<button class="button outline light-gray small round expand"
on-tap="home.shareAddress(home.addr)"
ng-disabled="home.generatingAddress">
<span translate>Share address</span>
</button>
</div>
<div class="small-12 columns" ng-show="home.addr">
<button class="button expand small round m10b" ng-click="openAmountModal(home.addr)"
ng-style="{'background-color':index.backgroundColor}"
ng-disabled="home.generatingAddress">
<span translate>Request a specific amount</span>
</button>
</div>
</div>
<div class="row m10t" ng-show="home.addr">
<div class="large-12 columns">
<div class="line-t size-10 text-gray m10b p10t" ng-show="!index.isSingleAddress">
<span translate> Share this wallet address to receive payments. To protect your privacy, new addresses are generated automatically once you use them.</span>
<a ng-show="!home.generatingAddress" ng-click="home.setAddress(true)" translate>Generate new address</a>
</div>
<div class="line-t size-10 text-gray m10b p10t" ng-show="index.isSingleAddress">
<span translate> Share this wallet address to receive payments</span>.
</div>
</div>
</div>
</div>
<div class="extra-margin-bottom"></div>
</div> <!-- END Receive -->
<!--
send
-->
<div id="send" class="send tab-view">
<div class="pr p25b">
<h4 class="title m0" ng-show="!index.updating">
<available-balance ng-show="!index.shouldHideBalance"></available-balance>
<span
ng-show="home.lockedCurrentFeePerKb || home.lockAmount"
class="text-gray" translate>Send Max</span>
<a
ng-show="index.availableBalanceSat > 0 && !home.lockedCurrentFeePerKb && !home.lockAmount"
ng-click="home.sendMax(index.availableBalanceSat)"
translate>Send Max
</a>
<div ng-show="!home.paymentExpired && home._paypro">
<span translate>Payment expires</span>
<time> {{home.remainingTimeStr}}</time>
</div>
</h4>
<div class="camera-icon" ng-show="index.isComplete">
<qr-scanner on-scan="home.onQrCodeScanned(data)"></qr-scanner>
</div>
</div>
<div class="box-notification m20t" ng-show="home.error" ng-click="home.resetError()">
<span class="text-warning">
{{home.error|translate}}
</span>
</div>
<div class="row m20t">
<div class="large-12 large-centered columns">
<form name="sendForm" novalidate>
<div ng-hide="home._paypro">
<div class="row collapse">
<label for="address" class="left" >
<span translate>To</span>
</label>
<span ng-hide="sendForm.address.$pristine">
<span class="has-error right size-12" ng-show="sendForm.address.$invalid && _address">
<i class="icon-close-circle size-14"></i>
<span class="vm" translate>Not valid</span>
</span>
<small class="right text-primary" ng-show="!sendForm.address.$invalid">
<i class="icon-checkmark-circle size-14"></i>
</small>
</span>
</div>
<div class="input">
<input ng-show="sendForm.address.$invalid" class="m0" type="text" id="address" name="address" ng-disabled=" home.lockAddress" ng-attr-placeholder="{{'Bitcoin address'|translate}}" ng-model="_address" valid-address required ng-focus="home.formFocus('address')" ng-blur="home.formFocus(false)">
<div class="addressbook-input" ng-show="!sendForm.address.$invalid && _address">
{{index.addressbook[_address] || _address}}
</div>
<a class="postfix size-12 m0 text-gray"
ng-style="{'color':index.backgroundColor}"
ng-click="home.openAddressbookModal(index.otherWallets, _address)">
<i class="icon-wallet text-bold size-18"></i>
</a>
</div>
</div>
<div ng-show="home._paypro">
<div class="row collapse" ng-click="home.openPPModal(home._paypro)">
<label for="domain">
<span translate>Payment to</span>
</label>
<div class="input block">
<input class="p45li" type="text" id="domain" name="domain" ng-model="home._paypro.domain" ng-disabled="1">
<i ng-show="home._paypro.verified && home._paypro.caTrusted" class="fi-lock color-greeni"></i>
<i ng-show="!home._paypro.caTrusted" class="fi-unlock color-yellowi"></i>
</div>
</div>
</div>
<div class="row">
<div class="large-12 medium-12 columns">
<div class="right" ng-hide="sendForm.amount.$pristine && !sendForm.amount.$modelValue ">
<span class="has-error right size-12" ng-show="sendForm.amount.$invalid">
<i class="icon-close-circle size-14"></i>
<span clas="vm" translate>Not valid</span>
</span>
<small class="text-primary right" ng-show="!sendForm.amount.$invalid && !sendForm.alternative.$invalid">
<i class="icon-checkmark-circle size-14"></i>
</small>
</div>
<div>
<label for="amount">
<span translate>Amount</span><span ng-show="showAlternative"> [{{home.alternativeIsoCode}}]</span>
</label>
<div class="input">
<div ng-if="index.isCordova">
<input
type="number"
ng-readonly="!home.lockAmount"
ng-show="!showAlternative"
id="amount"
ng-disabled="home.lockAmount"
name="amount"
ng-attr-placeholder="{{'Amount in'|translate}} {{home.unitName}}"
ng-model="_amount"
valid-amount
required
autocomplete="off"
ng-click="openInputAmountModal()"
ignore-mouse-wheel>
<input
type="number"
ng-readonly="!home.lockAmount"
ng-show="showAlternative"
id="alternative"
ng-disabled="!home.isRateAvailable || home.lockAmount"
name="alternative"
ng-attr-placeholder="{{'Amount in'|translate}} {{ home.alternativeName }}"
ng-model="_alternative"
required
autocomplete="off"
ng-click="openInputAmountModal()"
ignore-mouse-wheel>
</div>
<div ng-if="!index.isCordova">
<input
type="number"
ng-show="!showAlternative"
id="amount"
ng-disabled="home.lockAmount"
name="amount"
ng-attr-placeholder="{{'Amount in'|translate}} {{home.unitName}}"
ng-model="_amount"
valid-amount
required
autocomplete="off"
ignore-mouse-wheel>
<input
type="number"
ng-show="showAlternative"
id="alternative"
ng-disabled="!home.isRateAvailable || home.lockAmount"
name="alternative"
ng-attr-placeholder="{{'Amount in'|translate}} {{ home.alternativeName }}"
ng-model="_alternative"
required
autocomplete="off"
ignore-mouse-wheel>
</div>
<a class="postfix button" ng-show="!showAlternative" ng-style="{'background-color':index.backgroundColor}" ng-click="showAlternative = !showAlternative">{{home.unitName}}</a>
<a class="postfix button black" ng-show="showAlternative" ng-click="showAlternative = !showAlternative">{{home.alternativeIsoCode}}</a>
</div>
</div>
</div>
</div>
<div class="row" ng-hide="home.hideNote">
<div class="large-12 columns">
<label for="comment"><span translate>Description</span>
<small translate ng-hide="!sendForm.comment.$pristine">optional</small>
<small translate class="has-error" ng-show="sendForm.comment.$invalid && !sendForm.comment.$pristine">too long!</small>
</label>
<div class="input">
<textarea id="comment" name="comment"
ng-maxlength="500" ng-model="_comment" ng-focus="home.formFocus('msg')"
ng-blur="home.formFocus(false)"></textarea>
</div>
</div>
</div>
<div class="row">
<div class="large-6 medium-6 small-6 columns" ng-show="(home._paypro || home.lockAddress ||
home.lockAmount || !sendForm.amount.$pristine)">
<a ng-click="home.resetForm(sendForm)" class="button expand outline dark-gray round" translate>Cancel</a>
</div>
<div class="columns" ng-class="{'small-6 medium-6 large-6':(home._paypro || home.lockAddress ||
home.lockAmount || !sendForm.amount.$pristine)}">
<button class="button black round expand" ng-disabled="sendForm.$invalid || home.paymentExpired || index.updating"
ng-style="{'background-color':index.backgroundColor}" ng-click="home.submitForm()" translate>
Send
</button>
</div>
</div>
</form>
</div>
</div>
<div class="extra-margin-bottom"></div>
</div> <!-- END Send -->
<div id="{{view.id}}" class="{{view.class}} tab-view" ng-repeat="view in index.addonViews" ng-include="view.template">
</div>
</div>
</div>
<div class="extra-margin-bottom"></div>
<div class="bottom-bar row collapse p0i">
<div>
<div class="row collapse p0i">
<div class="medium-4 small-4 columns text-center bottombar-item">
<a href="#/tabs/home" >
<span class="size-10 tu">
tabs
</span>
</a>
</div>
</div>
</div>
</div>

View File

@ -1,944 +0,0 @@
'use strict';
angular.module('copayApp.controllers').controller('walletHomeController', function($scope, $rootScope, $interval, $timeout, $filter, $log, $ionicModal, $ionicPopover, notification, txStatus, profileService, lodash, configService, rateService, storageService, bitcore, gettext, gettextCatalog, platformInfo, addressService, ledger, bwcError, confirmDialog, txFormatService, addressbookService, go, feeService, walletService, fingerprintService, nodeWebkit, ongoingProcess) {
var isCordova = platformInfo.isCordova;
var isWP = platformInfo.isWP;
var isAndroid = platformInfo.isAndroid;
var isChromeApp = platformInfo.isChromeApp;
var self = this;
$rootScope.shouldHideMenuBar = false;
$rootScope.wpInputFocused = false;
var config = configService.getSync();
var configWallet = config.wallet;
var walletSettings = configWallet.settings;
var ret = {};
// INIT. Global value
ret.unitToSatoshi = walletSettings.unitToSatoshi;
ret.satToUnit = 1 / ret.unitToSatoshi;
ret.unitName = walletSettings.unitName;
ret.alternativeIsoCode = walletSettings.alternativeIsoCode;
ret.alternativeName = walletSettings.alternativeName;
ret.alternativeAmount = 0;
ret.unitDecimals = walletSettings.unitDecimals;
ret.isCordova = isCordova;
ret.addresses = [];
ret.isMobile = platformInfo.isMobile;
ret.isWindowsPhoneApp = platformInfo.isWP;
ret.countDown = null;
ret.sendMaxInfo = {};
ret.showAlternative = false;
ret.fromInputAmount = null;
var vanillaScope = ret;
var disableScannerListener = $rootScope.$on('dataScanned', function(event, data) {
if (!data) return;
self.setForm(data);
$rootScope.$emit('Local/SetTab', 'send');
var form = $scope.sendForm;
if (form.address.$invalid && !ongoingProcess.get('fetchingPayPro')) {
self.resetForm();
self.error = gettext('Could not recognize a valid Bitcoin QR Code');
}
});
var disablePaymentUriListener = $rootScope.$on('paymentUri', function(event, uri) {
$rootScope.$emit('Local/SetTab', 'send');
$timeout(function() {
self.setForm(uri);
}, 100);
});
var disableAddrListener = $rootScope.$on('Local/AddressIsUsed', function() {
self.setAddress(true);
});
var disableFocusListener = $rootScope.$on('Local/NewFocusedWalletReady', function() {
self.addr = null;
self.resetForm();
$scope.search = '';
if (profileService.focusedClient && profileService.focusedClient.isComplete()) {
self.setAddress();
self.setSendFormInputs();
}
$log.debug('Cleaning WalletHome Instance');
lodash.each(self, function(v, k) {
if (lodash.isFunction(v)) return;
if (!lodash.isUndefined(vanillaScope[k])) {
self[k] = vanillaScope[k];
return;
}
if (k == 'isRateAvailable') return;
delete self[k];
});
});
var disableResumeListener = $rootScope.$on('Local/Resume', function() {
// This is needed then the apps go to sleep
self.bindTouchDown();
});
var disableTabListener = $rootScope.$on('Local/TabChanged', function(e, tab) {
// This will slow down switch, do not add things here!
switch (tab) {
case 'receive':
// just to be sure we have an address
self.setAddress();
break;
case 'send':
self.resetError();
};
});
$scope.$on('$destroy', function() {
disableAddrListener();
disableScannerListener();
disablePaymentUriListener();
disableTabListener();
disableFocusListener();
disableResumeListener();
$rootScope.shouldHideMenuBar = false;
});
if (isCordova && StatusBar.isVisible) {
var backgroundColor = profileService.focusedClient ? profileService.focusedClient.backgroundColor : "#4B6178";
StatusBar.backgroundColorByHexString(backgroundColor);
}
this.onQrCodeScanned = function(data) {
if (data) go.send();
$rootScope.$emit('dataScanned', data);
};
rateService.whenAvailable(function() {
self.isRateAvailable = true;
$rootScope.$digest();
});
var getClipboard = function(cb) {
if (!isCordova || platformInfo.isWP) return cb();
window.cordova.plugins.clipboard.paste(function(value) {
var fc = profileService.focusedClient;
var Address = bitcore.Address;
var networkName = fc.credentials.network;
if (Address.isValid(value, networkName) && !$scope.newAddress) {
return cb(value);
}
});
};
var handleEncryptedWallet = function(client, cb) {
if (!walletService.isEncrypted(client)) return cb();
$rootScope.$emit('Local/NeedsPassword', false, function(err, password) {
if (err) return cb(err);
return cb(walletService.unlock(client, password));
});
};
var accept_msg = gettextCatalog.getString('Accept');
var cancel_msg = gettextCatalog.getString('Cancel');
var confirm_msg = gettextCatalog.getString('Confirm');
this.openAddressbookModal = function(wallets, address) {
$scope.wallets = wallets;
$scope.address = address;
$scope.self = self;
$ionicModal.fromTemplateUrl('views/modals/addressbook.html', {
scope: $scope
}).then(function(modal) {
$scope.addressbookModal = modal;
$scope.addressbookModal.show();
});
};
var GLIDERA_LOCK_TIME = 6 * 60 * 60;
// isGlidera flag is a security measure so glidera status is not
// only determined by the tx.message
this.openTxpModal = function(tx, copayers, isGlidera) {
$scope.self = self;
$scope.tx = tx;
$scope.copayers = copayers;
$scope.isGlidera = isGlidera;
$scope.error = null;
$scope.loading = null;
$scope.paymentExpired = null;
$scope.currentSpendUnconfirmed = configWallet.spendUnconfirmed;
$ionicModal.fromTemplateUrl('views/modals/txp-details.html', {
scope: $scope
}).then(function(modal) {
$scope.txpDetailsModal = modal;
$scope.txpDetailsModal.show();
});
};
this.setAddress = function(forceNew) {
self.addrError = null;
var client = profileService.focusedClient;
if (!client || !client.isComplete()) return;
// Address already set?
if (!forceNew && self.addr) {
return;
}
self.generatingAddress = true;
$timeout(function() {
addressService.getAddress(client.credentials.walletId, forceNew, function(err, addr) {
self.generatingAddress = false;
if (err) {
self.addrError = err;
} else {
if (addr)
self.addr = addr;
}
$scope.$digest();
});
});
};
this.copyToClipboard = function(addr, $event) {
var showPopover = function() {
$ionicPopover.fromTemplateUrl('views/includes/copyToClipboard.html', {
scope: $scope
}).then(function(popover) {
$scope.popover = popover;
$scope.popover.show($event);
});
$scope.close = function() {
$scope.popover.hide();
}
$timeout(function() {
$scope.popover.hide(); //close the popover after 0.7 seconds
}, 700);
$scope.$on('$destroy', function() {
$scope.popover.remove();
});
};
if (isCordova) {
window.cordova.plugins.clipboard.copy(addr);
window.plugins.toast.showShortCenter(gettextCatalog.getString('Copied to clipboard'));
} else if (platformInfo.isNW) {
nodeWebkit.writeToClipboard(addr);
showPopover($event);
}
};
this.shareAddress = function(addr) {
if (isCordova) {
window.plugins.socialsharing.share('bitcoin:' + addr, null, null, null);
}
};
// Send
this.resetError = function() {
this.error = this.success = null;
};
this.bindTouchDown = function(tries) {
var self = this;
tries = tries || 0;
if (tries > 5) return;
var e = document.getElementById('menu-walletHome');
if (!e) return $timeout(function() {
self.bindTouchDown(++tries);
}, 500);
// on touchdown elements
$log.debug('Binding touchstart elements...');
['hamburger', 'menu-walletHome', 'menu-send', 'menu-receive'].forEach(function(id) {
var e = document.getElementById(id);
if (e) e.addEventListener('touchstart', function() {
try {
event.preventDefault();
} catch (e) {};
angular.element(e).triggerHandler('click');
}, true);
});
}
this.hideMenuBar = lodash.debounce(function(hide) {
if (hide) {
$rootScope.shouldHideMenuBar = true;
} else {
$rootScope.shouldHideMenuBar = false;
}
$rootScope.$digest();
}, 100);
this.formFocus = function(what) {
if (isCordova && this.isWindowsPhoneApp) {
this.hideMenuBar(what);
}
var self = this;
if (isCordova && !this.isWindowsPhoneApp && what == 'address') {
getClipboard(function(value) {
if (value) {
document.getElementById("amount").focus();
$timeout(function() {
window.plugins.toast.showShortCenter(gettextCatalog.getString('Pasted from clipboard'));
self.setForm(value);
}, 100);
}
});
}
};
this.setSendFormInputs = function() {
var unitToSat = this.unitToSatoshi;
var satToUnit = 1 / unitToSat;
/**
* Setting the two related amounts as properties prevents an infinite
* recursion for watches while preserving the original angular updates
*
*/
Object.defineProperty($scope,
"_alternative", {
get: function() {
return $scope.__alternative;
},
set: function(newValue) {
$scope.__alternative = newValue;
if (self.isRateAvailable) {
$scope._amount = parseFloat((rateService.fromFiat(newValue, self.alternativeIsoCode) * satToUnit).toFixed(self.unitDecimals), 10);
} else {
$scope.__amount = null;
}
},
enumerable: true,
configurable: true
});
Object.defineProperty($scope,
"_amount", {
get: function() {
return $scope.__amount;
},
set: function(newValue) {
$scope.__amount = newValue;
if (self.isRateAvailable) {
$scope.__alternative = parseFloat((rateService.toFiat(newValue * self.unitToSatoshi, self.alternativeIsoCode)).toFixed(2), 10);
} else {
$scope.__alternative = null;
}
self.alternativeAmount = $scope.__alternative;
self.resetError();
},
enumerable: true,
configurable: true
});
Object.defineProperty($scope,
"_address", {
get: function() {
return $scope.__address;
},
set: function(newValue) {
$scope.__address = self.onAddressChange(newValue);
if ($scope.sendForm && $scope.sendForm.address.$valid) {
self.lockAddress = true;
}
},
enumerable: true,
configurable: true
});
var fc = profileService.focusedClient;
// ToDo: use a credential's (or fc's) function for this
this.hideNote = !fc.credentials.sharedEncryptingKey;
};
this.setSendError = function(err) {
var fc = profileService.focusedClient;
var prefix =
fc.credentials.m > 1 ? gettextCatalog.getString('Could not create payment proposal') : gettextCatalog.getString('Could not send payment');
this.error = bwcError.msg(err, prefix);
$timeout(function() {
$scope.$digest();
}, 1);
};
this.setAmount = function(amount, useAlternativeAmount) {
$scope.showAlternative = useAlternativeAmount;
self.fromInputAmount = true;
self.setForm(null, amount, null);
};
this.submitForm = function() {
if (!$scope._amount || !$scope._address) return;
var client = profileService.focusedClient;
var unitToSat = this.unitToSatoshi;
var currentSpendUnconfirmed = configWallet.spendUnconfirmed;
var outputs = [];
this.resetError();
if (isCordova && this.isWindowsPhoneApp)
$rootScope.shouldHideMenuBar = true;
var form = $scope.sendForm;
var comment = form.comment.$modelValue;
// ToDo: use a credential's (or fc's) function for this
if (comment && !client.credentials.sharedEncryptingKey) {
var msg = 'Could not add message to imported wallet without shared encrypting key';
$log.warn(msg);
return self.setSendError(gettext(msg));
}
if (form.amount.$modelValue * unitToSat > Number.MAX_SAFE_INTEGER) {
var msg = 'Amount too big';
$log.warn(msg);
return self.setSendError(gettext(msg));
};
$timeout(function() {
var paypro = self._paypro;
var address, amount;
address = form.address.$modelValue;
amount = parseInt((form.amount.$modelValue * unitToSat).toFixed(0));
outputs.push({
'toAddress': address,
'amount': amount,
'message': comment
});
var txp = {};
if (!lodash.isEmpty(self.sendMaxInfo)) {
txp.sendMax = true;
txp.inputs = self.sendMaxInfo.inputs;
txp.fee = self.sendMaxInfo.fee;
} else {
txp.amount = amount;
}
txp.toAddress = address;
txp.outputs = outputs;
txp.message = comment;
txp.payProUrl = paypro ? paypro.url : null;
txp.excludeUnconfirmedUtxos = configWallet.spendUnconfirmed ? false : true;
txp.feeLevel = walletSettings.feeLevel || 'normal';
ongoingProcess.set('creatingTx', true);
walletService.createTx(client, txp, function(err, createdTxp) {
ongoingProcess.set('creatingTx', false);
if (err) {
return self.setSendError(err);
}
if (!client.canSign() && !client.isPrivKeyExternal()) {
$log.info('No signing proposal: No private key');
ongoingProcess.set('sendingTx', true);
walletService.publishTx(client, createdTxp, function(err, publishedTxp) {
ongoingProcess.set('sendingTx', false);
if (err) {
return self.setSendError(err);
}
self.resetForm();
go.walletHome();
var type = txStatus.notify(createdTxp);
$scope.openStatusModal(type, createdTxp, function() {
return $scope.$emit('Local/TxProposalAction');
});
});
} else {
$rootScope.$emit('Local/NeedsConfirmation', createdTxp, function(accept) {
if (accept) self.confirmTx(createdTxp);
else self.resetForm();
});
}
});
}, 100);
};
this.confirmTx = function(txp) {
var client = profileService.focusedClient;
var self = this;
fingerprintService.check(client, function(err) {
if (err) {
return self.setSendError(err);
}
handleEncryptedWallet(client, function(err) {
if (err) {
return self.setSendError(err);
}
ongoingProcess.set('sendingTx', true);
walletService.publishTx(client, txp, function(err, publishedTxp) {
ongoingProcess.set('sendingTx', false);
if (err) {
return self.setSendError(err);
}
ongoingProcess.set('signingTx', true);
walletService.signTx(client, publishedTxp, function(err, signedTxp) {
ongoingProcess.set('signingTx', false);
walletService.lock(client);
if (err) {
$scope.$emit('Local/TxProposalAction');
return self.setSendError(
err.message ?
err.message :
gettext('The payment was created but could not be completed. Please try again from home screen'));
}
if (signedTxp.status == 'accepted') {
ongoingProcess.set('broadcastingTx', true);
walletService.broadcastTx(client, signedTxp, function(err, broadcastedTxp) {
ongoingProcess.set('broadcastingTx', false);
if (err) {
return self.setSendError(err);
}
self.resetForm();
go.walletHome();
var type = txStatus.notify(broadcastedTxp);
$scope.openStatusModal(type, broadcastedTxp, function() {
$scope.$emit('Local/TxProposalAction', broadcastedTxp.status == 'broadcasted');
});
});
} else {
self.resetForm();
go.walletHome();
var type = txStatus.notify(signedTxp);
$scope.openStatusModal(type, signedTxp, function() {
$scope.$emit('Local/TxProposalAction');
});
}
});
});
});
});
};
$scope.openStatusModal = function(type, txp, cb) {
var fc = profileService.focusedClient;
$scope.type = type;
$scope.tx = txFormatService.processTx(txp);
$scope.color = fc.backgroundColor;
$scope.cb = cb;
$ionicModal.fromTemplateUrl('views/modals/tx-status.html', {
scope: $scope,
animation: 'slide-in-up'
}).then(function(modal) {
$scope.txStatusModal = modal;
$scope.txStatusModal.show();
});
};
$scope.openSearchModal = function() {
var fc = profileService.focusedClient;
$scope.color = fc.backgroundColor;
$scope.self = self;
$ionicModal.fromTemplateUrl('views/modals/search.html', {
scope: $scope,
focusFirstInput: true
}).then(function(modal) {
$scope.searchModal = modal;
$scope.searchModal.show();
});
};
$scope.openCustomInputAmountModal = function(addr) {
var fc = profileService.focusedClient;
$scope.color = fc.backgroundColor;
$scope.self = self;
$scope.addr = addr;
$ionicModal.fromTemplateUrl('views/modals/customAmount.html', {
scope: $scope
}).then(function(modal) {
$scope.customAmountModal = modal;
$scope.customAmountModal.show();
});
};
$scope.openAmountModal = function(addr) {
if (isCordova)
$scope.openInputAmountModal(addr);
else
$scope.openCustomInputAmountModal(addr);
};
$scope.openInputAmountModal = function(addr) {
var fc = profileService.focusedClient;
$scope.color = fc.backgroundColor;
$scope.showAlternativeAmount = $scope.showAlternative || null;
if ($scope.showAlternativeAmount) {
$scope.amount = $scope.sendForm.alternative.$viewValue || null;
} else {
$scope.amount = $scope.sendForm.amount.$viewValue || null;
}
$scope.self = self;
$scope.addr = addr;
$ionicModal.fromTemplateUrl('views/modals/inputAmount.html', {
scope: $scope
}).then(function(modal) {
$scope.inputAmountModal = modal;
$scope.inputAmountModal.show();
});
};
this.setForm = function(to, amount, comment) {
var form = $scope.sendForm;
if (to) {
form.address.$setViewValue(to);
form.address.$isValid = true;
form.address.$render();
this.lockAddress = true;
}
if (amount) {
form.amount.$setViewValue("" + amount);
form.amount.$isValid = true;
form.amount.$render();
if (!this.fromInputAmount)
this.lockAmount = true;
this.fromInputAmount = false;
}
if (comment) {
form.comment.$setViewValue(comment);
form.comment.$isValid = true;
form.comment.$render();
}
};
this.resetForm = function() {
this.resetError();
this.sendMaxInfo = {};
if (this.countDown) $interval.cancel(this.countDown);
this._paypro = null;
this.lockAddress = false;
this.lockAmount = false;
this._amount = this._address = null;
var form = $scope.sendForm;
if (form && form.amount) {
form.amount.$pristine = true;
form.amount.$setViewValue('');
form.amount.$render();
form.comment.$setViewValue('');
form.comment.$render();
form.$setPristine();
if (form.address) {
form.address.$pristine = true;
form.address.$setViewValue('');
form.address.$render();
}
}
$timeout(function() {
$rootScope.$digest();
}, 1);
};
this.setFromPayPro = function(uri, cb) {
if (!cb) cb = function() {};
var fc = profileService.focusedClient;
if (isChromeApp) {
this.error = gettext('Payment Protocol not supported on Chrome App');
return cb(true);
}
var satToUnit = 1 / this.unitToSatoshi;
var self = this;
/// Get information of payment if using Payment Protocol
ongoingProcess.set('fetchingPayPro', true);
$log.debug('Fetch PayPro Request...', uri);
$timeout(function() {
fc.fetchPayPro({
payProUrl: uri,
}, function(err, paypro) {
ongoingProcess.set('fetchingPayPro', false);
if (err) {
$log.warn('Could not fetch payment request:', err);
self.resetForm();
var msg = err.toString();
if (msg.match('HTTP')) {
msg = gettext('Could not fetch payment information');
}
self.error = msg;
$timeout(function() {
$rootScope.$digest();
}, 1);
return cb(true);
}
if (!paypro.verified) {
self.resetForm();
$log.warn('Failed to verify payment protocol signatures');
self.error = gettext('Payment Protocol Invalid');
$timeout(function() {
$rootScope.$digest();
}, 1);
return cb(true);
}
self._paypro = paypro;
self.setForm(paypro.toAddress, (paypro.amount * satToUnit).toFixed(self.unitDecimals), paypro.memo);
_paymentTimeControl(paypro.expires);
return cb();
});
}, 1);
};
function _paymentTimeControl(expirationTime) {
self.paymentExpired = false;
setExpirationTime();
self.countDown = $interval(function() {
setExpirationTime();
}, 1000);
function setExpirationTime() {
var now = Math.floor(Date.now() / 1000);
if (now > expirationTime) {
setExpiredValues();
return;
}
var totalSecs = expirationTime - now;
var m = Math.floor(totalSecs / 60);
var s = totalSecs % 60;
self.remainingTimeStr = ('0' + m).slice(-2) + ":" + ('0' + s).slice(-2);
};
function setExpiredValues() {
self.paymentExpired = true;
self.remainingTimeStr = null;
self._paypro = null;
self.error = gettext('Cannot sign: The payment request has expired');
if (self.countDown) $interval.cancel(self.countDown);
};
};
this.setFromUri = function(uri) {
var self = this;
function sanitizeUri(uri) {
// Fixes when a region uses comma to separate decimals
var regex = /[\?\&]amount=(\d+([\,\.]\d+)?)/i;
var match = regex.exec(uri);
if (!match || match.length === 0) {
return uri;
}
var value = match[0].replace(',', '.');
var newUri = uri.replace(regex, value);
return newUri;
};
var satToUnit = 1 / this.unitToSatoshi;
// URI extensions for Payment Protocol with non-backwards-compatible request
if ((/^bitcoin:\?r=[\w+]/).exec(uri)) {
uri = decodeURIComponent(uri.replace('bitcoin:?r=', ''));
this.setFromPayPro(uri, function(err) {
if (err) {
return err;
}
});
} else {
uri = sanitizeUri(uri);
if (!bitcore.URI.isValid(uri)) {
return uri;
}
var parsed = new bitcore.URI(uri);
var addr = parsed.address ? parsed.address.toString() : '';
var message = parsed.message;
var amount = parsed.amount ?
(parsed.amount.toFixed(0) * satToUnit).toFixed(this.unitDecimals) : 0;
if (parsed.r) {
this.setFromPayPro(parsed.r, function(err) {
if (err && addr && amount) {
self.setForm(addr, amount, message);
return addr;
}
});
} else {
this.setForm(addr, amount, message);
return addr;
}
}
};
this.onAddressChange = function(value) {
this.resetError();
if (!value) return '';
if (this._paypro)
return value;
if (value.indexOf('bitcoin:') === 0) {
return this.setFromUri(value);
} else if (/^https?:\/\//.test(value)) {
return this.setFromPayPro(value);
} else {
return value;
}
};
// History
function strip(number) {
return (parseFloat(number.toPrecision(12)));
}
this.getUnitName = function() {
return this.unitName;
};
this.getAlternativeIsoCode = function() {
return this.alternativeIsoCode;
};
this.openTxModal = function(btx) {
var self = this;
$scope.btx = lodash.cloneDeep(btx);
$scope.self = self;
$ionicModal.fromTemplateUrl('views/modals/tx-details.html', {
scope: $scope,
hideDelay: 500
}).then(function(modal) {
$scope.txDetailsModal = modal;
$scope.txDetailsModal.show();
});
};
this.hasAction = function(actions, action) {
return actions.hasOwnProperty('create');
};
this.sendMax = function(availableBalanceSat) {
if (availableBalanceSat == 0) {
this.error = gettext("Cannot create transaction. Insufficient funds");
return;
}
var self = this;
var fc = profileService.focusedClient;
this.error = null;
ongoingProcess.set('calculatingFee', true);
$timeout(function() {
feeService.getCurrentFeeValue(function(err, feePerKb) {
ongoingProcess.set('calculatingFee', false);
if (err || !lodash.isNumber(feePerKb)) {
self.error = gettext('Could not get fee value');
return;
}
var opts = {};
opts.feePerKb = feePerKb;
opts.returnInputs = true;
var config = configService.getSync();
opts.excludeUnconfirmedUtxos = !config.wallet.spendUnconfirmed;
ongoingProcess.set('retrivingInputs', true);
fc.getSendMaxInfo(opts, function(err, resp) {
ongoingProcess.set('retrivingInputs', false);
if (err) {
self.error = err;
$scope.$apply();
return;
}
if (resp.amount == 0) {
self.error = gettext("Not enough funds for fee");
$scope.$apply();
return;
}
var msg = gettextCatalog.getString("{{fee}} will be deducted for bitcoin networking fees", {
fee: profileService.formatAmount(resp.fee) + ' ' + self.unitName
});
var warningMsg = verifyExcludedUtxos();
if (!lodash.isEmpty(warningMsg))
msg += '. \n' + warningMsg;
confirmDialog.show(msg, function(confirmed) {
if (confirmed) {
self.sendMaxInfo = resp;
var amount = parseFloat((resp.amount * self.satToUnit).toFixed(self.unitDecimals));
self.setForm(null, amount, null);
} else {
self.resetForm();
}
});
function verifyExcludedUtxos() {
var warningMsg = [];
if (resp.utxosBelowFee > 0) {
warningMsg.push(gettextCatalog.getString("Note: a total of {{amountBelowFeeStr}} were excluded. These funds come from UTXOs smaller than the network fee provided.", {
amountBelowFeeStr: profileService.formatAmount(resp.amountBelowFee) + ' ' + self.unitName
}));
}
if (resp.utxosAboveMaxSize > 0) {
warningMsg.push(gettextCatalog.getString("Note: a total of {{amountAboveMaxSizeStr}} were excluded. The maximum size allowed for a transaction was exceeded", {
amountAboveMaxSizeStr: profileService.formatAmount(resp.amountAboveMaxSize) + ' ' + self.unitName
}));
}
return warningMsg.join('\n');
}
});
});
}, 10);
};
/* Start setup */
lodash.assign(self, vanillaScope);
this.bindTouchDown();
if (profileService.focusedClient) {
this.setAddress();
this.setSendFormInputs();
}
});