2015-03-06 07:00:10 -08:00
'use strict' ;
2016-03-08 10:05:01 -08:00
angular . module ( 'copayApp.controllers' ) . controller ( 'walletHomeController' , function ( $scope , $rootScope , $interval , $timeout , $filter , $modal , $log , notification , txStatus , isCordova , isMobile , profileService , lodash , configService , rateService , storageService , bitcore , isChromeApp , gettext , gettextCatalog , nodeWebkit , addressService , ledger , bwsError , confirmDialog , txFormatService , animationService , addressbookService , go , feeService , txService ) {
2015-03-06 07:00:10 -08:00
2015-04-23 08:27:43 -07:00
var self = this ;
2016-01-11 11:46:50 -08:00
window . ignoreMobilePause = false ;
2016-02-03 08:35:52 -08:00
$rootScope . shouldHideMenuBar = false ;
2015-04-23 08:27:43 -07:00
$rootScope . wpInputFocused = false ;
2015-10-07 12:17:19 -07:00
var config = configService . getSync ( ) ;
var configWallet = config . wallet ;
var walletSettings = configWallet . settings ;
2016-02-18 12:16:07 -08:00
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 = isMobile . any ( ) ;
ret . isWindowsPhoneApp = isMobile . Windows ( ) && isCordova ;
2016-03-30 08:52:19 -07:00
ret . countDown = null ;
2016-04-29 07:25:30 -07:00
ret . sendMaxInfo = { } ;
2016-02-18 12:16:07 -08:00
var vanillaScope = ret ;
2016-02-23 07:00:46 -08:00
2015-04-23 08:27:43 -07:00
var disableScannerListener = $rootScope . $on ( 'dataScanned' , function ( event , data ) {
self . setForm ( data ) ;
2015-04-28 14:11:06 -07:00
$rootScope . $emit ( 'Local/SetTab' , 'send' ) ;
2015-07-15 12:15:05 -07:00
var form = $scope . sendForm ;
2015-09-18 11:29:55 -07:00
if ( form . address . $invalid && ! self . blockUx ) {
2015-07-15 12:15:05 -07:00
self . resetForm ( ) ;
self . error = gettext ( 'Could not recognize a valid Bitcoin QR Code' ) ;
}
2015-04-23 08:27:43 -07:00
} ) ;
var disablePaymentUriListener = $rootScope . $on ( 'paymentUri' , function ( event , uri ) {
2015-12-09 11:55:56 -08:00
$rootScope . $emit ( 'Local/SetTab' , 'send' ) ;
2015-04-23 08:27:43 -07:00
$timeout ( function ( ) {
self . setForm ( uri ) ;
} , 100 ) ;
} ) ;
2016-02-18 12:16:07 -08:00
var disableAddrListener = $rootScope . $on ( 'Local/AddressIsUsed' , function ( ) {
2015-06-27 09:22:56 -07:00
self . setAddress ( true ) ;
2015-04-23 08:27:43 -07:00
} ) ;
2016-03-09 06:53:47 -08:00
var disableFocusListener = $rootScope . $on ( 'Local/NewFocusedWalletReady' , function ( ) {
2016-02-18 12:16:07 -08:00
self . addr = null ;
2015-04-23 22:42:10 -07:00
self . resetForm ( ) ;
2016-02-18 12:55:57 -08:00
$scope . search = '' ;
2016-02-18 11:26:02 -08:00
2016-03-28 11:39:35 -07:00
if ( profileService . focusedClient && profileService . focusedClient . isComplete ( ) ) {
2016-02-23 05:33:40 -08:00
self . setAddress ( ) ;
self . setSendFormInputs ( ) ;
}
2016-02-18 11:26:02 -08:00
2016-02-18 12:16:07 -08:00
$log . debug ( 'Cleaning WalletHome Instance' ) ;
lodash . each ( self , function ( v , k ) {
if ( lodash . isFunction ( v ) ) return ;
2016-04-29 08:14:09 -07:00
if ( ! lodash . isUndefined ( vanillaScope [ k ] ) ) {
2016-02-23 04:54:35 -08:00
self [ k ] = vanillaScope [ k ] ;
return ;
}
2016-02-23 05:33:40 -08:00
if ( k == 'isRateAvailable' ) return ;
2016-02-18 12:16:07 -08:00
delete self [ k ] ;
} ) ;
2015-04-23 22:42:10 -07:00
} ) ;
2015-04-23 11:19:30 -07:00
2015-04-28 14:00:49 -07:00
var disableResumeListener = $rootScope . $on ( 'Local/Resume' , function ( ) {
2015-04-28 15:26:22 -07:00
// This is needed then the apps go to sleep
2015-05-04 09:27:12 -07:00
self . bindTouchDown ( ) ;
2015-04-28 14:00:49 -07:00
} ) ;
2015-04-23 22:42:10 -07:00
var disableTabListener = $rootScope . $on ( 'Local/TabChanged' , function ( e , tab ) {
2015-04-28 12:58:40 -07:00
// This will slow down switch, do not add things here!
2015-04-23 22:42:10 -07:00
switch ( tab ) {
2015-04-23 11:19:30 -07:00
case 'receive' :
2015-04-28 12:58:40 -07:00
// just to be sure we have an address
self . setAddress ( ) ;
2015-04-23 11:19:30 -07:00
break ;
2015-04-28 12:58:40 -07:00
case 'send' :
self . resetError ( ) ;
2015-04-23 11:19:30 -07:00
} ;
} ) ;
2015-07-16 09:08:27 -07:00
var disableOngoingProcessListener = $rootScope . $on ( 'Addon/OngoingProcess' , function ( e , name ) {
self . setOngoingProcess ( name ) ;
} ) ;
2015-04-23 08:27:43 -07:00
$scope . $on ( '$destroy' , function ( ) {
disableAddrListener ( ) ;
disableScannerListener ( ) ;
disablePaymentUriListener ( ) ;
2015-04-23 11:19:30 -07:00
disableTabListener ( ) ;
2015-04-23 22:42:10 -07:00
disableFocusListener ( ) ;
2015-04-28 14:00:49 -07:00
disableResumeListener ( ) ;
2015-07-16 09:08:27 -07:00
disableOngoingProcessListener ( ) ;
2016-02-03 08:35:52 -08:00
$rootScope . shouldHideMenuBar = false ;
2015-04-23 08:27:43 -07:00
} ) ;
2015-12-01 12:16:39 -08:00
this . onQrCodeScanned = function ( data ) {
2015-12-08 07:14:44 -08:00
if ( data ) go . send ( ) ;
2015-12-01 12:16:39 -08:00
$rootScope . $emit ( 'dataScanned' , data ) ;
} ;
2015-04-23 08:27:43 -07:00
rateService . whenAvailable ( function ( ) {
self . isRateAvailable = true ;
$rootScope . $digest ( ) ;
} ) ;
2016-02-08 13:36:30 -08:00
var getClipboard = function ( cb ) {
if ( ! isCordova || isMobile . Windows ( ) ) 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 ) ;
}
} ) ;
} ;
2015-07-29 08:37:51 -07:00
var accept _msg = gettextCatalog . getString ( 'Accept' ) ;
var cancel _msg = gettextCatalog . getString ( 'Cancel' ) ;
var confirm _msg = gettextCatalog . getString ( 'Confirm' ) ;
2015-12-02 07:21:26 -08:00
this . openDestinationAddressModal = function ( wallets , address ) {
2015-09-30 10:14:15 -07:00
$rootScope . modalOpened = true ;
2015-10-22 14:43:32 -07:00
var fc = profileService . focusedClient ;
2015-12-02 07:21:26 -08:00
self . lockAddress = false ;
self . _address = null ;
2015-06-19 18:01:38 -07:00
var ModalInstanceCtrl = function ( $scope , $modalInstance ) {
2015-09-25 09:10:05 -07:00
$scope . wallets = wallets ;
2015-10-22 14:43:32 -07:00
$scope . editAddressbook = false ;
$scope . addAddressbookEntry = false ;
$scope . selectedAddressbook = { } ;
2015-10-23 07:12:23 -07:00
$scope . newAddress = address ;
2015-12-01 12:33:53 -08:00
$scope . walletName = fc . credentials . walletName ;
2015-12-02 13:20:22 -08:00
$scope . color = fc . backgroundColor ;
2015-09-25 09:10:05 -07:00
$scope . addressbook = {
'address' : ( $scope . newAddress || '' ) ,
'label' : ''
} ;
2015-10-22 14:43:32 -07:00
2016-02-02 12:07:39 -08:00
$scope . checkClipboard = function ( ) {
2016-02-24 10:24:16 -08:00
if ( ! $scope . newAddress ) {
getClipboard ( function ( value ) {
$scope . newAddress = value ;
} ) ;
}
2016-02-02 12:07:39 -08:00
} ;
2015-10-23 07:12:23 -07:00
$scope . beforeQrCodeScann = function ( ) {
$scope . error = null ;
$scope . addAddressbookEntry = true ;
$scope . editAddressbook = false ;
} ;
$scope . onQrCodeScanned = function ( data , addressbookForm ) {
$timeout ( function ( ) {
var form = addressbookForm ;
if ( data && form ) {
data = data . replace ( 'bitcoin:' , '' ) ;
form . address . $setViewValue ( data ) ;
form . address . $isValid = true ;
form . address . $render ( ) ;
}
$scope . $digest ( ) ;
} , 100 ) ;
} ;
2015-10-22 14:43:32 -07:00
$scope . selectAddressbook = function ( addr ) {
$modalInstance . close ( addr ) ;
} ;
$scope . toggleEditAddressbook = function ( ) {
$scope . editAddressbook = ! $scope . editAddressbook ;
$scope . selectedAddressbook = { } ;
$scope . addAddressbookEntry = false ;
} ;
$scope . toggleSelectAddressbook = function ( addr ) {
$scope . selectedAddressbook [ addr ] = $scope . selectedAddressbook [ addr ] ? false : true ;
} ;
$scope . toggleAddAddressbookEntry = function ( ) {
2015-10-23 07:12:23 -07:00
$scope . error = null ;
2015-09-25 09:10:05 -07:00
$scope . addressbook = {
'address' : ( $scope . newAddress || '' ) ,
'label' : ''
} ;
2015-10-22 14:43:32 -07:00
$scope . addAddressbookEntry = ! $scope . addAddressbookEntry ;
} ;
$scope . list = function ( ) {
$scope . error = null ;
addressbookService . list ( function ( err , ab ) {
2015-09-25 09:10:05 -07:00
if ( err ) {
2015-10-22 14:43:32 -07:00
$scope . error = err ;
return ;
}
$scope . list = ab ;
2016-04-21 07:14:48 -07:00
$timeout ( function ( ) {
$scope . $digest ( ) ;
} ) ;
2015-10-22 14:43:32 -07:00
} ) ;
} ;
$scope . add = function ( addressbook ) {
$scope . error = null ;
2015-10-22 15:06:23 -07:00
$timeout ( function ( ) {
addressbookService . add ( addressbook , function ( err , ab ) {
2015-09-25 09:10:05 -07:00
if ( err ) {
2015-10-22 15:06:23 -07:00
$scope . error = err ;
return ;
}
2015-11-02 07:04:18 -08:00
$rootScope . $emit ( 'Local/AddressbookUpdated' , ab ) ;
2015-10-22 15:06:23 -07:00
$scope . list = ab ;
$scope . editAddressbook = true ;
$scope . toggleEditAddressbook ( ) ;
$scope . $digest ( ) ;
} ) ;
} , 100 ) ;
2015-10-22 14:43:32 -07:00
} ;
$scope . remove = function ( addr ) {
$scope . error = null ;
2015-10-22 15:06:23 -07:00
$timeout ( function ( ) {
addressbookService . remove ( addr , function ( err , ab ) {
2015-09-25 09:10:05 -07:00
if ( err ) {
2015-10-22 15:06:23 -07:00
$scope . error = err ;
return ;
}
2015-11-02 07:04:18 -08:00
$rootScope . $emit ( 'Local/AddressbookUpdated' , ab ) ;
2015-10-22 15:06:23 -07:00
$scope . list = ab ;
$scope . $digest ( ) ;
} ) ;
} , 100 ) ;
2015-10-22 14:43:32 -07:00
} ;
2015-09-30 10:14:15 -07:00
2015-06-19 18:01:38 -07:00
$scope . cancel = function ( ) {
$modalInstance . dismiss ( 'cancel' ) ;
} ;
2015-06-27 09:48:25 -07:00
$scope . selectWallet = function ( walletId , walletName ) {
2016-03-14 08:21:55 -07:00
profileService . isBackupNeeded ( walletId , function ( needsBackup ) {
$scope . needsBackup = { } ;
$scope . needsBackup [ walletId ] = needsBackup ;
if ( needsBackup ) return ;
2015-08-12 07:08:33 -07:00
2016-03-14 08:21:55 -07:00
$scope . gettingAddress = true ;
$scope . selectedWalletName = walletName ;
$timeout ( function ( ) {
$scope . $apply ( ) ;
} ) ;
2015-08-12 07:08:33 -07:00
2016-03-14 08:21:55 -07:00
addressService . getAddress ( walletId , false , function ( err , addr ) {
$scope . gettingAddress = false ;
2015-08-12 07:08:33 -07:00
2016-03-14 08:21:55 -07:00
if ( err ) {
self . error = err ;
$modalInstance . dismiss ( 'cancel' ) ;
return ;
}
2015-08-12 07:08:33 -07:00
2016-02-19 05:27:59 -08:00
$modalInstance . close ( addr ) ;
} ) ;
2015-06-27 09:48:25 -07:00
} ) ;
} ;
2015-06-19 18:01:38 -07:00
} ;
2015-06-27 09:48:25 -07:00
2015-06-19 18:01:38 -07:00
var modalInstance = $modal . open ( {
2015-10-22 14:43:32 -07:00
templateUrl : 'views/modals/destination-address.html' ,
2015-09-30 22:13:33 -07:00
windowClass : animationService . modalAnimated . slideUp ,
2015-06-19 18:01:38 -07:00
controller : ModalInstanceCtrl ,
} ) ;
2015-09-30 10:14:15 -07:00
var disableCloseModal = $rootScope . $on ( 'closeModal' , function ( ) {
modalInstance . dismiss ( 'cancel' ) ;
} ) ;
2015-06-19 18:01:38 -07:00
modalInstance . result . finally ( function ( ) {
2015-09-30 10:14:15 -07:00
$rootScope . modalOpened = false ;
disableCloseModal ( ) ;
2015-06-19 18:01:38 -07:00
var m = angular . element ( document . getElementsByClassName ( 'reveal-modal' ) ) ;
2015-09-30 22:13:33 -07:00
m . addClass ( animationService . modalAnimated . slideOutDown ) ;
2015-06-19 18:01:38 -07:00
} ) ;
2015-06-27 09:48:25 -07:00
modalInstance . result . then ( function ( addr ) {
if ( addr ) {
self . setForm ( addr ) ;
}
2016-02-02 12:07:39 -08:00
} , function ( ) {
// onRejected
self . resetForm ( ) ;
2015-06-27 09:48:25 -07:00
} ) ;
2015-06-19 18:01:38 -07:00
} ;
2015-09-14 05:46:45 -07:00
var GLIDERA _LOCK _TIME = 6 * 60 * 60 ;
2016-01-16 15:04:01 -08:00
// isGlidera flag is a security measure so glidera status is not
2015-09-08 18:39:33 -07:00
// only determined by the tx.message
this . openTxpModal = function ( tx , copayers , isGlidera ) {
2015-09-30 10:14:15 -07:00
$rootScope . modalOpened = true ;
2016-03-30 08:52:19 -07:00
var self = this ;
2015-03-06 07:00:10 -08:00
var fc = profileService . focusedClient ;
2015-12-02 06:57:13 -08:00
var currentSpendUnconfirmed = configWallet . spendUnconfirmed ;
2015-03-06 07:00:10 -08:00
var ModalInstanceCtrl = function ( $scope , $modalInstance ) {
2016-02-19 08:29:41 -08:00
$scope . paymentExpired = null ;
2016-02-05 12:36:25 -08:00
checkPaypro ( ) ;
2015-03-06 07:00:10 -08:00
$scope . error = null ;
$scope . copayers = copayers
2015-05-13 06:56:08 -07:00
$scope . copayerId = fc . credentials . copayerId ;
2015-09-04 06:17:59 -07:00
$scope . canSign = fc . canSign ( ) || fc . isPrivKeyExternal ( ) ;
2015-03-06 07:00:10 -08:00
$scope . loading = null ;
$scope . color = fc . backgroundColor ;
2015-11-14 13:08:04 -08:00
$scope . isShared = fc . credentials . n > 1 ;
2016-02-19 08:29:41 -08:00
var now = Math . floor ( Date . now ( ) / 1000 ) ;
2015-09-08 17:11:13 -07:00
2015-09-08 18:39:33 -07:00
// ToDo: use tx.customData instead of tx.message
if ( tx . message === 'Glidera transaction' && isGlidera ) {
tx . isGlidera = true ;
if ( tx . canBeRemoved ) {
2015-09-14 05:46:45 -07:00
tx . canBeRemoved = ( Date . now ( ) / 1000 - ( tx . ts || tx . createdOn ) ) > GLIDERA _LOCK _TIME ;
2015-09-08 18:39:33 -07:00
}
2015-09-08 17:11:13 -07:00
}
2015-09-08 18:39:33 -07:00
$scope . tx = tx ;
2015-08-11 13:59:41 -07:00
$scope . currentSpendUnconfirmed = currentSpendUnconfirmed ;
2015-03-06 07:00:10 -08:00
$scope . getShortNetworkName = function ( ) {
return fc . credentials . networkName . substring ( 0 , 4 ) ;
} ;
2016-02-05 12:36:25 -08:00
function checkPaypro ( ) {
2016-02-19 08:29:41 -08:00
if ( tx . payProUrl && ! isChromeApp ) {
2016-02-05 12:36:25 -08:00
fc . fetchPayPro ( {
payProUrl : tx . payProUrl ,
} , function ( err , paypro ) {
2016-02-10 08:04:00 -08:00
if ( err ) return ;
2016-02-05 12:36:25 -08:00
tx . paypro = paypro ;
2016-03-22 12:37:55 -07:00
paymentTimeControl ( tx . paypro . expires ) ;
2016-02-05 12:36:25 -08:00
} ) ;
}
} ;
2016-03-22 12:37:55 -07:00
function paymentTimeControl ( expirationTime ) {
$scope . paymentExpired = false ;
setExpirationTime ( ) ;
2016-03-30 08:52:19 -07:00
self . countDown = $interval ( function ( ) {
2016-03-22 12:37:55 -07:00
setExpirationTime ( ) ;
} , 1000 ) ;
function setExpirationTime ( ) {
2016-04-01 05:58:28 -07:00
var now = Math . floor ( Date . now ( ) / 1000 ) ;
if ( now > expirationTime ) {
2016-02-19 08:29:41 -08:00
$scope . paymentExpired = true ;
2016-03-30 08:52:19 -07:00
if ( self . countDown ) $interval . cancel ( self . countDown ) ;
2016-04-01 05:58:28 -07:00
return ;
2016-02-19 08:29:41 -08:00
}
2016-04-01 05:58:28 -07:00
var totalSecs = expirationTime - now ;
var m = Math . floor ( totalSecs / 60 ) ;
var s = totalSecs % 60 ;
$scope . expires = ( '0' + m ) . slice ( - 2 ) + ":" + ( '0' + s ) . slice ( - 2 ) ;
2016-03-22 12:37:55 -07:00
} ;
2016-02-19 08:29:41 -08:00
} ;
2015-09-03 14:14:38 -07:00
lodash . each ( [ 'TxProposalRejectedBy' , 'TxProposalAcceptedBy' , 'transactionProposalRemoved' , 'TxProposalRemoved' , 'NewOutgoingTx' , 'UpdateTx' ] , function ( eventName ) {
2015-03-06 07:00:10 -08:00
$rootScope . $on ( eventName , function ( ) {
fc . getTx ( $scope . tx . id , function ( err , tx ) {
if ( err ) {
2016-01-22 13:16:50 -08:00
if ( err . message && err . message == 'TX_NOT_FOUND' &&
2015-03-06 07:00:10 -08:00
( eventName == 'transactionProposalRemoved' || eventName == 'TxProposalRemoved' ) ) {
$scope . tx . removed = true ;
2015-06-18 07:17:35 -07:00
$scope . tx . canBeRemoved = false ;
2015-03-06 07:00:10 -08:00
$scope . tx . pendingForUs = false ;
$scope . $apply ( ) ;
return ;
}
return ;
}
var action = lodash . find ( tx . actions , {
copayerId : fc . credentials . copayerId
} ) ;
2016-02-05 12:36:25 -08:00
2015-09-09 12:17:08 -07:00
$scope . tx = txFormatService . processTx ( tx ) ;
2016-02-05 12:36:25 -08:00
2015-03-06 07:00:10 -08:00
if ( ! action && tx . status == 'pending' )
$scope . tx . pendingForUs = true ;
2016-02-05 12:36:25 -08:00
2015-03-06 07:00:10 -08:00
$scope . updateCopayerList ( ) ;
$scope . $apply ( ) ;
} ) ;
} ) ;
} ) ;
$scope . updateCopayerList = function ( ) {
lodash . map ( $scope . copayers , function ( cp ) {
lodash . each ( $scope . tx . actions , function ( ac ) {
if ( cp . id == ac . copayerId ) {
cp . action = ac . type ;
}
} ) ;
} ) ;
} ;
$scope . sign = function ( txp ) {
2015-09-25 09:10:05 -07:00
var fc = profileService . focusedClient ;
2016-01-20 10:25:54 -08:00
$scope . error = null ;
2015-03-06 07:00:10 -08:00
$scope . loading = true ;
2016-01-20 10:25:54 -08:00
2016-02-11 11:06:30 -08:00
txService . prepareAndSignAndBroadcast ( txp , {
2016-01-21 09:52:58 -08:00
reporterFn : self . setOngoingProcess . bind ( self )
} , function ( err , txp ) {
$scope . loading = false ;
$scope . $emit ( 'UpdateTx' ) ;
2015-10-07 12:17:19 -07:00
2016-01-21 09:52:58 -08:00
if ( err ) {
$scope . error = err ;
$timeout ( function ( ) {
$scope . $digest ( ) ;
2015-10-07 12:17:19 -07:00
} ) ;
2016-01-21 09:52:58 -08:00
return ;
}
$modalInstance . close ( txp ) ;
return ;
} ) ;
2015-03-06 07:00:10 -08:00
} ;
$scope . reject = function ( txp ) {
2016-01-06 12:19:31 -08:00
self . setOngoingProcess ( gettextCatalog . getString ( 'Rejecting payment' ) ) ;
2015-03-06 07:00:10 -08:00
$scope . loading = true ;
$scope . error = null ;
$timeout ( function ( ) {
fc . rejectTxProposal ( txp , null , function ( err , txpr ) {
2015-04-24 11:02:14 -07:00
self . setOngoingProcess ( ) ;
2015-03-06 07:00:10 -08:00
$scope . loading = false ;
if ( err ) {
2015-09-03 14:14:38 -07:00
$scope . $emit ( 'UpdateTx' ) ;
2015-08-13 12:47:10 -07:00
$scope . error = bwsError . msg ( err , gettextCatalog . getString ( 'Could not reject payment' ) ) ;
2015-03-06 07:00:10 -08:00
$scope . $digest ( ) ;
} else {
$modalInstance . close ( txpr ) ;
}
} ) ;
} , 100 ) ;
} ;
$scope . remove = function ( txp ) {
2016-01-06 12:19:31 -08:00
self . setOngoingProcess ( gettextCatalog . getString ( 'Deleting payment' ) ) ;
2015-03-06 07:00:10 -08:00
$scope . loading = true ;
$scope . error = null ;
$timeout ( function ( ) {
fc . removeTxProposal ( txp , function ( err , txpb ) {
2015-04-24 11:02:14 -07:00
self . setOngoingProcess ( ) ;
2015-03-06 07:00:10 -08:00
$scope . loading = false ;
// Hacky: request tries to parse an empty response
2015-04-21 09:06:44 -07:00
if ( err && ! ( err . message && err . message . match ( /Unexpected/ ) ) ) {
2015-09-03 14:14:38 -07:00
$scope . $emit ( 'UpdateTx' ) ;
2015-08-13 12:47:10 -07:00
$scope . error = bwsError . msg ( err , gettextCatalog . getString ( 'Could not delete payment proposal' ) ) ;
2015-03-06 07:00:10 -08:00
$scope . $digest ( ) ;
return ;
2015-04-21 09:06:44 -07:00
}
2015-03-06 07:00:10 -08:00
$modalInstance . close ( ) ;
} ) ;
} , 100 ) ;
} ;
$scope . broadcast = function ( txp ) {
2016-01-06 12:19:31 -08:00
self . setOngoingProcess ( gettextCatalog . getString ( 'Broadcasting Payment' ) ) ;
2015-03-06 07:00:10 -08:00
$scope . loading = true ;
$scope . error = null ;
$timeout ( function ( ) {
2015-05-22 13:16:55 -07:00
fc . broadcastTxProposal ( txp , function ( err , txpb , memo ) {
2015-04-24 11:02:14 -07:00
self . setOngoingProcess ( ) ;
2015-03-06 07:00:10 -08:00
$scope . loading = false ;
if ( err ) {
2015-08-13 12:47:10 -07:00
$scope . error = bwsError . msg ( err , gettextCatalog . getString ( 'Could not broadcast payment' ) ) ;
2015-03-06 07:00:10 -08:00
$scope . $digest ( ) ;
} else {
2015-05-22 13:16:55 -07:00
if ( memo )
$log . info ( memo ) ;
2015-05-29 08:39:17 -07:00
$modalInstance . close ( txpb ) ;
2015-03-06 07:00:10 -08:00
}
} ) ;
} , 100 ) ;
} ;
2016-02-16 11:13:50 -08:00
$scope . copyToClipboard = function ( addr ) {
2015-04-26 22:31:07 -07:00
if ( ! addr ) return ;
2016-02-16 11:13:50 -08:00
self . copyToClipboard ( addr ) ;
2015-04-26 22:31:07 -07:00
} ;
2015-12-04 12:04:13 -08:00
$scope . cancel = lodash . debounce ( function ( ) {
2015-04-21 22:48:00 -07:00
$modalInstance . dismiss ( 'cancel' ) ;
2015-12-04 12:04:13 -08:00
} , 0 , 1000 ) ;
2015-03-06 07:00:10 -08:00
} ;
var modalInstance = $modal . open ( {
templateUrl : 'views/modals/txp-details.html' ,
2015-09-30 22:13:33 -07:00
windowClass : animationService . modalAnimated . slideRight ,
2015-03-06 07:00:10 -08:00
controller : ModalInstanceCtrl ,
} ) ;
2015-09-30 10:14:15 -07:00
var disableCloseModal = $rootScope . $on ( 'closeModal' , function ( ) {
modalInstance . dismiss ( 'cancel' ) ;
} ) ;
2015-05-07 15:02:38 -07:00
modalInstance . result . finally ( function ( ) {
2015-09-30 10:14:15 -07:00
$rootScope . modalOpened = false ;
disableCloseModal ( ) ;
2015-05-07 15:02:38 -07:00
var m = angular . element ( document . getElementsByClassName ( 'reveal-modal' ) ) ;
2015-09-30 22:13:33 -07:00
m . addClass ( animationService . modalAnimated . slideOutRight ) ;
2015-05-07 15:02:38 -07:00
} ) ;
2015-05-29 08:39:17 -07:00
modalInstance . result . then ( function ( txp ) {
2015-05-12 06:59:10 -07:00
self . setOngoingProcess ( ) ;
2015-03-06 07:00:10 -08:00
if ( txp ) {
2015-05-08 07:38:34 -07:00
txStatus . notify ( txp , function ( ) {
2016-01-22 07:39:44 -08:00
$scope . $emit ( 'Local/TxProposalAction' , txp . status == 'broadcasted' ) ;
2015-05-08 07:38:34 -07:00
} ) ;
2015-05-12 06:59:10 -07:00
} else {
$timeout ( function ( ) {
2016-01-22 07:39:44 -08:00
$scope . $emit ( 'Local/TxProposalAction' ) ;
2015-05-12 06:59:10 -07:00
} , 100 ) ;
2015-03-06 07:00:10 -08:00
}
} ) ;
} ;
2015-06-27 09:22:56 -07:00
this . setAddress = function ( forceNew ) {
2015-05-29 07:46:33 -07:00
self . addrError = null ;
2015-04-23 08:27:43 -07:00
var fc = profileService . focusedClient ;
2015-05-13 08:41:05 -07:00
if ( ! fc )
return ;
2015-06-27 09:22:56 -07:00
// Address already set?
2016-02-18 12:16:07 -08:00
if ( ! forceNew && self . addr ) {
2015-05-30 19:15:43 -07:00
return ;
}
2015-06-27 09:22:56 -07:00
self . generatingAddress = true ;
2015-04-23 08:27:43 -07:00
$timeout ( function ( ) {
2015-06-28 07:03:28 -07:00
addressService . getAddress ( fc . credentials . walletId , forceNew , function ( err , addr ) {
2015-06-27 09:22:56 -07:00
self . generatingAddress = false ;
2015-08-12 07:49:13 -07:00
if ( err ) {
2015-08-12 07:08:33 -07:00
self . addrError = err ;
2015-08-12 07:49:13 -07:00
} else {
2015-08-12 07:08:33 -07:00
if ( addr )
2016-02-18 12:16:07 -08:00
self . addr = addr ;
2015-04-23 08:27:43 -07:00
}
2015-06-27 09:22:56 -07:00
$scope . $digest ( ) ;
2015-04-23 08:27:43 -07:00
} ) ;
} ) ;
} ;
2016-02-16 11:13:50 -08:00
this . copyToClipboard = function ( addr ) {
2015-04-23 08:27:43 -07:00
if ( isCordova ) {
2015-06-08 07:15:30 -07:00
window . cordova . plugins . clipboard . copy ( addr ) ;
2015-07-29 08:42:05 -07:00
window . plugins . toast . showShortCenter ( gettextCatalog . getString ( 'Copied to clipboard' ) ) ;
2015-05-28 06:52:33 -07:00
} else if ( nodeWebkit . isDefined ( ) ) {
nodeWebkit . writeToClipboard ( addr ) ;
2015-04-23 08:27:43 -07:00
}
} ;
this . shareAddress = function ( addr ) {
if ( isCordova ) {
if ( isMobile . Android ( ) || isMobile . Windows ( ) ) {
window . ignoreMobilePause = true ;
}
window . plugins . socialsharing . share ( 'bitcoin:' + addr , null , null , null ) ;
}
} ;
2015-06-24 13:55:08 -07:00
this . openCustomizedAmountModal = function ( addr ) {
2015-09-30 10:14:15 -07:00
$rootScope . modalOpened = true ;
2015-06-24 13:55:08 -07:00
var self = this ;
var fc = profileService . focusedClient ;
var ModalInstanceCtrl = function ( $scope , $modalInstance ) {
$scope . addr = addr ;
$scope . color = fc . backgroundColor ;
$scope . unitName = self . unitName ;
$scope . alternativeAmount = self . alternativeAmount ;
$scope . alternativeName = self . alternativeName ;
$scope . alternativeIsoCode = self . alternativeIsoCode ;
$scope . isRateAvailable = self . isRateAvailable ;
$scope . unitToSatoshi = self . unitToSatoshi ;
$scope . unitDecimals = self . unitDecimals ;
var satToUnit = 1 / self . unitToSatoshi ;
$scope . showAlternative = false ;
2015-09-11 10:44:52 -07:00
$scope . isCordova = isCordova ;
2015-06-24 13:55:08 -07:00
Object . defineProperty ( $scope ,
"_customAlternative" , {
get : function ( ) {
return $scope . customAlternative ;
} ,
set : function ( newValue ) {
$scope . customAlternative = newValue ;
if ( typeof ( newValue ) === 'number' && $scope . isRateAvailable ) {
$scope . customAmount = parseFloat ( ( rateService . fromFiat ( newValue , $scope . alternativeIsoCode ) * satToUnit ) . toFixed ( $scope . unitDecimals ) , 10 ) ;
2015-08-07 13:21:22 -07:00
} else {
$scope . customAmount = null ;
2015-06-24 13:55:08 -07:00
}
} ,
enumerable : true ,
configurable : true
} ) ;
Object . defineProperty ( $scope ,
"_customAmount" , {
get : function ( ) {
return $scope . customAmount ;
} ,
set : function ( newValue ) {
$scope . customAmount = newValue ;
if ( typeof ( newValue ) === 'number' && $scope . isRateAvailable ) {
$scope . customAlternative = parseFloat ( ( rateService . toFiat ( newValue * $scope . unitToSatoshi , $scope . alternativeIsoCode ) ) . toFixed ( 2 ) , 10 ) ;
} else {
2015-08-07 13:21:22 -07:00
$scope . customAlternative = null ;
2015-06-24 13:55:08 -07:00
}
$scope . alternativeAmount = $scope . customAlternative ;
} ,
enumerable : true ,
configurable : true
} ) ;
$scope . submitForm = function ( form ) {
var satToBtc = 1 / 100000000 ;
var amount = form . amount . $modelValue ;
2015-06-26 10:25:53 -07:00
var amountSat = parseInt ( ( amount * $scope . unitToSatoshi ) . toFixed ( 0 ) ) ;
$timeout ( function ( ) {
$scope . customizedAmountUnit = amount + ' ' + $scope . unitName ;
$scope . customizedAlternativeUnit = $filter ( 'noFractionNumber' ) ( form . alternative . $modelValue , 2 ) + ' ' + $scope . alternativeIsoCode ;
if ( $scope . unitName == 'bits' ) {
2015-06-26 11:08:59 -07:00
amount = ( amountSat * satToBtc ) . toFixed ( 8 ) ;
2015-06-26 10:25:53 -07:00
}
$scope . customizedAmountBtc = amount ;
} , 1 ) ;
2015-06-24 13:55:08 -07:00
} ;
$scope . toggleAlternative = function ( ) {
$scope . showAlternative = ! $scope . showAlternative ;
} ;
2015-09-11 10:44:52 -07:00
$scope . shareAddress = function ( uri ) {
if ( isCordova ) {
if ( isMobile . Android ( ) || isMobile . Windows ( ) ) {
window . ignoreMobilePause = true ;
}
window . plugins . socialsharing . share ( uri , null , null , null ) ;
}
} ;
2015-06-24 13:55:08 -07:00
$scope . cancel = function ( ) {
$modalInstance . dismiss ( 'cancel' ) ;
} ;
} ;
var modalInstance = $modal . open ( {
templateUrl : 'views/modals/customized-amount.html' ,
2015-09-30 22:13:33 -07:00
windowClass : animationService . modalAnimated . slideUp ,
2015-06-24 13:55:08 -07:00
controller : ModalInstanceCtrl ,
} ) ;
2015-09-30 10:14:15 -07:00
var disableCloseModal = $rootScope . $on ( 'closeModal' , function ( ) {
modalInstance . dismiss ( 'cancel' ) ;
} ) ;
2015-06-24 13:55:08 -07:00
modalInstance . result . finally ( function ( ) {
2015-09-30 10:14:15 -07:00
$rootScope . modalOpened = false ;
disableCloseModal ( ) ;
2015-06-24 13:55:08 -07:00
var m = angular . element ( document . getElementsByClassName ( 'reveal-modal' ) ) ;
2015-09-30 22:13:33 -07:00
m . addClass ( animationService . modalAnimated . slideOutDown ) ;
2015-06-24 13:55:08 -07:00
} ) ;
} ;
2016-02-04 05:33:41 -08:00
// Send
2015-04-28 08:06:04 -07:00
this . canShowAlternative = function ( ) {
return $scope . showAlternative ;
} ;
this . showAlternative = function ( ) {
$scope . showAlternative = true ;
} ;
this . hideAlternative = function ( ) {
$scope . showAlternative = false ;
} ;
2015-04-23 08:27:43 -07:00
this . resetError = function ( ) {
2015-05-29 08:39:17 -07:00
this . error = this . success = null ;
2015-04-23 08:27:43 -07:00
} ;
2015-04-28 12:08:43 -07:00
this . bindTouchDown = function ( tries ) {
var self = this ;
tries = tries || 0 ;
if ( tries > 5 ) return ;
var e = document . getElementById ( 'menu-walletHome' ) ;
2015-04-28 16:13:28 -07:00
if ( ! e ) return $timeout ( function ( ) {
2015-04-28 12:08:43 -07:00
self . bindTouchDown ( ++ tries ) ;
} , 500 ) ;
// on touchdown elements
$log . debug ( 'Binding touchstart elements...' ) ;
2015-12-02 13:20:22 -08:00
[ 'hamburger' , 'menu-walletHome' , 'menu-send' , 'menu-receive' ] . forEach ( function ( id ) {
2015-04-28 12:08:43 -07:00
var e = document . getElementById ( id ) ;
if ( e ) e . addEventListener ( 'touchstart' , function ( ) {
2015-05-04 09:27:12 -07:00
try {
event . preventDefault ( ) ;
} catch ( e ) { } ;
2015-04-28 12:08:43 -07:00
angular . element ( e ) . triggerHandler ( 'click' ) ;
2015-05-04 09:27:12 -07:00
} , true ) ;
2015-04-28 12:08:43 -07:00
} ) ;
}
2015-04-23 08:27:43 -07:00
2015-04-28 12:08:43 -07:00
this . hideMenuBar = lodash . debounce ( function ( hide ) {
2015-04-23 08:27:43 -07:00
if ( hide ) {
2016-02-03 08:35:52 -08:00
$rootScope . shouldHideMenuBar = true ;
2015-04-28 12:08:43 -07:00
this . bindTouchDown ( ) ;
2015-04-23 08:27:43 -07:00
} else {
2016-02-03 08:35:52 -08:00
$rootScope . shouldHideMenuBar = false ;
2015-04-23 08:27:43 -07:00
}
$rootScope . $digest ( ) ;
} , 100 ) ;
this . formFocus = function ( what ) {
2015-04-24 13:34:18 -07:00
if ( isCordova && ! this . isWindowsPhoneApp ) {
2015-04-28 12:08:43 -07:00
this . hideMenuBar ( what ) ;
2015-04-23 08:27:43 -07:00
}
2016-02-04 05:33:41 -08:00
2016-02-08 13:36:30 -08:00
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 ) ;
}
} ) ;
}
2015-04-23 08:27:43 -07:00
if ( ! this . isWindowsPhoneApp ) return
if ( ! what ) {
this . hideAddress = false ;
this . hideAmount = false ;
} else {
if ( what == 'amount' ) {
this . hideAddress = true ;
} else if ( what == 'msg' ) {
this . hideAddress = true ;
this . hideAmount = true ;
}
}
$timeout ( function ( ) {
$rootScope . $digest ( ) ;
} , 1 ) ;
} ;
2015-04-28 12:58:40 -07:00
this . setSendFormInputs = function ( ) {
2015-04-23 08:27:43 -07:00
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 ( typeof ( newValue ) === 'number' && self . isRateAvailable ) {
$scope . _amount = parseFloat ( ( rateService . fromFiat ( newValue , self . alternativeIsoCode ) * satToUnit ) . toFixed ( self . unitDecimals ) , 10 ) ;
2015-08-07 13:21:22 -07:00
} else {
$scope . _ _amount = null ;
2015-04-23 08:27:43 -07:00
}
} ,
enumerable : true ,
configurable : true
} ) ;
Object . defineProperty ( $scope ,
"_amount" , {
get : function ( ) {
return $scope . _ _amount ;
} ,
set : function ( newValue ) {
$scope . _ _amount = newValue ;
if ( typeof ( newValue ) === 'number' && self . isRateAvailable ) {
$scope . _ _alternative = parseFloat ( ( rateService . toFiat ( newValue * self . unitToSatoshi , self . alternativeIsoCode ) ) . toFixed ( 2 ) , 10 ) ;
} else {
2015-08-07 13:21:22 -07:00
$scope . _ _alternative = null ;
2015-04-23 08:27:43 -07:00
}
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 ) ;
2015-10-22 14:43:32 -07:00
if ( $scope . sendForm && $scope . sendForm . address . $valid ) {
self . lockAddress = true ;
}
2015-04-23 08:27:43 -07:00
} ,
enumerable : true ,
configurable : true
} ) ;
2015-09-14 08:19:52 -07:00
var fc = profileService . focusedClient ;
// ToDo: use a credential's (or fc's) function for this
this . hideNote = ! fc . credentials . sharedEncryptingKey ;
2015-04-23 08:27:43 -07:00
} ;
2015-05-12 06:48:00 -07:00
this . setSendError = function ( err ) {
2015-04-23 11:23:21 -07:00
var fc = profileService . focusedClient ;
2015-08-12 07:08:33 -07:00
var prefix =
2015-08-13 12:47:10 -07:00
fc . credentials . m > 1 ? gettextCatalog . getString ( 'Could not create payment proposal' ) : gettextCatalog . getString ( 'Could not send payment' ) ;
2015-04-23 08:27:43 -07:00
2015-08-12 07:49:13 -07:00
this . error = bwsError . msg ( err , prefix ) ;
2016-02-01 11:18:44 -08:00
2015-04-23 08:27:43 -07:00
$timeout ( function ( ) {
$scope . $digest ( ) ;
} , 1 ) ;
} ;
2016-03-08 10:05:01 -08:00
// subscription
2015-04-23 08:27:43 -07:00
this . setOngoingProcess = function ( name ) {
var self = this ;
2015-04-23 22:42:10 -07:00
self . blockUx = ! ! name ;
if ( isCordova ) {
if ( name ) {
2015-04-28 13:49:09 -07:00
window . plugins . spinnerDialog . hide ( ) ;
2015-04-23 22:42:10 -07:00
window . plugins . spinnerDialog . show ( null , name + '...' , true ) ;
} else {
window . plugins . spinnerDialog . hide ( ) ;
}
} else {
2015-04-28 13:19:22 -07:00
self . onGoingProcess = name ;
2015-04-28 13:28:49 -07:00
$timeout ( function ( ) {
$rootScope . $apply ( ) ;
} ) ;
2015-04-28 13:19:22 -07:00
} ;
2015-04-23 08:27:43 -07:00
} ;
2015-12-23 13:05:22 -08:00
this . submitForm = function ( ) {
2016-03-11 11:50:48 -08:00
if ( ! $scope . _amount || ! $scope . _address ) return ;
2015-04-23 11:23:21 -07:00
var fc = profileService . focusedClient ;
2015-04-23 08:27:43 -07:00
var unitToSat = this . unitToSatoshi ;
2015-12-02 06:57:13 -08:00
var currentSpendUnconfirmed = configWallet . spendUnconfirmed ;
2015-08-12 07:49:13 -07:00
2016-01-27 11:41:12 -08:00
var outputs = [ ] ;
2016-01-20 10:25:54 -08:00
this . resetError ( ) ;
2015-07-02 10:42:53 -07:00
if ( isCordova && this . isWindowsPhoneApp ) {
this . hideAddress = false ;
this . hideAmount = false ;
}
2015-04-23 08:27:43 -07:00
2015-04-23 22:42:10 -07:00
var form = $scope . sendForm ;
2015-09-14 08:12:24 -07:00
var comment = form . comment . $modelValue ;
// ToDo: use a credential's (or fc's) function for this
if ( comment && ! fc . credentials . sharedEncryptingKey ) {
var msg = 'Could not add message to imported wallet without shared encrypting key' ;
$log . warn ( msg ) ;
return self . setSendError ( gettext ( msg ) ) ;
}
2015-04-23 08:27:43 -07:00
$timeout ( function ( ) {
var paypro = self . _paypro ;
var address , amount ;
address = form . address . $modelValue ;
amount = parseInt ( ( form . amount . $modelValue * unitToSat ) . toFixed ( 0 ) ) ;
2016-01-27 11:41:12 -08:00
outputs . push ( {
2016-02-18 12:16:07 -08:00
'toAddress' : address ,
2016-01-27 11:41:12 -08:00
'amount' : amount ,
'message' : comment
} ) ;
2016-04-29 07:25:30 -07:00
var opts = { } ;
if ( self . sendMaxInfo ) {
opts . sendMax = true ;
opts . inputs = self . sendMaxInfo . inputs ;
opts . fee = self . sendMaxInfo . fee ;
} else {
opts . amount = amount ;
}
opts . toAddress = address ;
opts . outputs = outputs ;
opts . message = comment ;
opts . payProUrl = paypro ? paypro . url : null ;
opts . lockedCurrentFeePerKb = self . lockedCurrentFeePerKb ;
2016-01-29 11:39:32 -08:00
self . setOngoingProcess ( gettextCatalog . getString ( 'Creating transaction' ) ) ;
2016-02-11 11:06:30 -08:00
txService . createTx ( opts , function ( err , txp ) {
2016-01-29 11:39:32 -08:00
self . setOngoingProcess ( ) ;
2015-10-07 12:17:19 -07:00
if ( err ) {
2016-01-21 09:52:58 -08:00
return self . setSendError ( err ) ;
2015-10-07 12:17:19 -07:00
}
2015-07-24 08:11:07 -07:00
2016-01-29 11:39:32 -08:00
if ( ! fc . canSign ( ) && ! fc . isPrivKeyExternal ( ) ) {
2016-01-27 11:41:12 -08:00
self . setOngoingProcess ( ) ;
2016-01-29 11:39:32 -08:00
$log . info ( 'No signing proposal: No private key' ) ;
self . resetForm ( ) ;
txStatus . notify ( txp , function ( ) {
return $scope . $emit ( 'Local/TxProposalAction' ) ;
2015-07-24 08:11:07 -07:00
} ) ;
2016-01-29 11:39:32 -08:00
return ;
} else {
2016-02-18 12:16:07 -08:00
$rootScope . $emit ( 'Local/NeedsConfirmation' , txp , function ( accept ) {
2016-02-25 07:07:47 -08:00
if ( accept ) self . confirmTx ( txp ) ;
2016-01-29 11:39:32 -08:00
else self . resetForm ( ) ;
} ) ;
}
2015-04-23 08:27:43 -07:00
} ) ;
2016-01-29 11:39:32 -08:00
2015-04-23 08:27:43 -07:00
} , 100 ) ;
2016-02-04 05:33:41 -08:00
} ;
2016-01-27 11:41:12 -08:00
2016-02-25 07:07:47 -08:00
this . confirmTx = function ( txp ) {
2016-01-27 11:41:12 -08:00
var self = this ;
2016-04-29 08:14:09 -07:00
$log . info ( 'at .confirmTx' ) ;
2016-04-13 10:08:03 -07:00
txService . prepare ( { } , function ( err ) {
2016-04-29 08:14:09 -07:00
$log . info ( 'after .prepare:' , err ) ;
2016-01-27 11:41:12 -08:00
if ( err ) {
2016-04-29 08:14:09 -07:00
self . setOngoingProcess ( ) ;
$log . warn ( 'confirmTx/Prepare error:' , err ) ;
2016-01-29 11:39:32 -08:00
return self . setSendError ( err ) ;
2016-01-27 11:41:12 -08:00
}
2016-01-29 11:39:32 -08:00
self . setOngoingProcess ( gettextCatalog . getString ( 'Sending transaction' ) ) ;
2016-04-13 10:08:03 -07:00
txService . publishTx ( txp , { } , function ( err , txpPublished ) {
2016-04-29 08:14:09 -07:00
$log . info ( 'after .publishTx:' , err ) ;
2016-01-29 11:39:32 -08:00
if ( err ) {
self . setOngoingProcess ( ) ;
self . setSendError ( err ) ;
2016-04-29 08:14:09 -07:00
return ;
}
2016-02-25 05:41:04 -08:00
2016-04-29 08:14:09 -07:00
txService . signAndBroadcast ( txpPublished , {
reporterFn : self . setOngoingProcess . bind ( self )
} , function ( err , txp ) {
$log . info ( 'after .signAndBroadcast:' , err ) ;
self . resetForm ( ) ;
self . setOngoingProcess ( ) ;
if ( err ) {
self . error = err . message ? err . message : gettext ( 'The payment was created but could not be completed. Please try again from home screen' ) ;
$scope . $emit ( 'Local/TxProposalAction' ) ;
$timeout ( function ( ) {
$scope . $digest ( ) ;
} , 1 ) ;
return ;
}
$log . info ( 'Transaction status:' , txp . status ) ;
go . walletHome ( ) ;
txStatus . notify ( txp , function ( ) {
$scope . $emit ( 'Local/TxProposalAction' , txp . status == 'broadcasted' ) ;
2016-02-25 05:41:04 -08:00
} ) ;
2016-04-29 08:14:09 -07:00
} ) ;
2016-01-29 11:39:32 -08:00
} ) ;
2016-01-27 11:41:12 -08:00
} ) ;
} ;
2015-12-02 07:21:26 -08:00
this . setForm = function ( to , amount , comment ) {
2015-04-23 08:27:43 -07:00
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 ( ) ;
this . lockAmount = true ;
}
if ( comment ) {
form . comment . $setViewValue ( comment ) ;
form . comment . $isValid = true ;
form . comment . $render ( ) ;
}
} ;
2015-04-23 22:42:10 -07:00
this . resetForm = function ( ) {
2015-04-23 08:27:43 -07:00
this . resetError ( ) ;
2016-04-29 07:25:30 -07:00
this . sendMaxInfo = { } ;
2016-03-30 08:52:19 -07:00
if ( this . countDown ) $interval . cancel ( this . countDown ) ;
2015-04-23 08:27:43 -07:00
this . _paypro = null ;
2015-12-23 13:05:22 -08:00
this . lockedCurrentFeePerKb = null ;
2015-04-23 08:27:43 -07:00
this . lockAddress = false ;
this . lockAmount = false ;
this . _amount = this . _address = null ;
2015-04-23 22:42:10 -07:00
var form = $scope . sendForm ;
2015-08-13 10:54:53 -07:00
2015-04-23 08:27:43 -07:00
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 ) ;
2016-02-04 05:33:41 -08:00
} ;
2015-04-23 08:27:43 -07:00
this . openPPModal = function ( paypro ) {
2015-09-30 10:14:15 -07:00
$rootScope . modalOpened = true ;
2015-04-23 08:27:43 -07:00
var ModalInstanceCtrl = function ( $scope , $modalInstance ) {
2015-04-23 11:23:21 -07:00
var fc = profileService . focusedClient ;
2015-04-23 08:27:43 -07:00
var satToUnit = 1 / self . unitToSatoshi ;
$scope . paypro = paypro ;
$scope . alternative = self . alternativeAmount ;
$scope . alternativeIsoCode = self . alternativeIsoCode ;
$scope . isRateAvailable = self . isRateAvailable ;
$scope . unitTotal = ( paypro . amount * satToUnit ) . toFixed ( self . unitDecimals ) ;
$scope . unitName = self . unitName ;
$scope . color = fc . backgroundColor ;
$scope . cancel = function ( ) {
$modalInstance . dismiss ( 'cancel' ) ;
} ;
} ;
2015-05-07 15:02:38 -07:00
var modalInstance = $modal . open ( {
2015-04-23 08:27:43 -07:00
templateUrl : 'views/modals/paypro.html' ,
2015-09-30 22:13:33 -07:00
windowClass : animationService . modalAnimated . slideUp ,
2015-04-23 08:27:43 -07:00
controller : ModalInstanceCtrl ,
} ) ;
2015-05-07 15:02:38 -07:00
2015-09-30 10:14:15 -07:00
var disableCloseModal = $rootScope . $on ( 'closeModal' , function ( ) {
modalInstance . dismiss ( 'cancel' ) ;
} ) ;
2015-05-07 15:02:38 -07:00
modalInstance . result . finally ( function ( ) {
2015-09-30 10:14:15 -07:00
$rootScope . modalOpened = false ;
disableCloseModal ( ) ;
2015-05-07 15:02:38 -07:00
var m = angular . element ( document . getElementsByClassName ( 'reveal-modal' ) ) ;
2015-09-30 22:13:33 -07:00
m . addClass ( animationService . modalAnimated . slideOutDown ) ;
2015-05-07 15:02:38 -07:00
} ) ;
2015-04-23 08:27:43 -07:00
} ;
2015-09-16 08:32:22 -07:00
this . setFromPayPro = function ( uri , cb ) {
if ( ! cb ) cb = function ( ) { } ;
2015-04-23 11:23:21 -07:00
var fc = profileService . focusedClient ;
2015-04-23 08:27:43 -07:00
if ( isChromeApp ) {
2015-04-29 15:19:10 -07:00
this . error = gettext ( 'Payment Protocol not supported on Chrome App' ) ;
2015-09-16 08:32:22 -07:00
return cb ( true ) ;
2015-04-23 08:27:43 -07:00
}
var satToUnit = 1 / this . unitToSatoshi ;
var self = this ;
2015-04-30 09:03:30 -07:00
/// Get information of payment if using Payment Protocol
2016-01-06 12:19:31 -08:00
self . setOngoingProcess ( gettextCatalog . getString ( 'Fetching Payment Information' ) ) ;
2015-04-23 08:27:43 -07:00
$log . debug ( 'Fetch PayPro Request...' , uri ) ;
$timeout ( function ( ) {
fc . fetchPayPro ( {
payProUrl : uri ,
} , function ( err , paypro ) {
2015-04-23 22:42:10 -07:00
self . setOngoingProcess ( ) ;
2015-04-23 08:27:43 -07:00
if ( err ) {
2015-09-16 08:32:22 -07:00
$log . warn ( 'Could not fetch payment request:' , err ) ;
2015-04-23 22:42:10 -07:00
self . resetForm ( ) ;
2015-04-23 08:27:43 -07:00
var msg = err . toString ( ) ;
if ( msg . match ( 'HTTP' ) ) {
2015-04-29 15:19:10 -07:00
msg = gettext ( 'Could not fetch payment information' ) ;
2015-04-23 08:27:43 -07:00
}
self . error = msg ;
2015-09-18 08:07:51 -07:00
$timeout ( function ( ) {
$rootScope . $digest ( ) ;
} , 1 ) ;
return cb ( true ) ;
}
if ( ! paypro . verified ) {
self . resetForm ( ) ;
2016-01-16 15:04:01 -08:00
$log . warn ( 'Failed to verify payment protocol signatures' ) ;
2015-09-18 08:07:51 -07:00
self . error = gettext ( 'Payment Protocol Invalid' ) ;
$timeout ( function ( ) {
$rootScope . $digest ( ) ;
} , 1 ) ;
2015-09-16 08:32:22 -07:00
return cb ( true ) ;
2015-04-23 08:27:43 -07:00
}
2015-09-16 08:32:22 -07:00
self . _paypro = paypro ;
self . setForm ( paypro . toAddress , ( paypro . amount * satToUnit ) . toFixed ( self . unitDecimals ) , paypro . memo ) ;
2016-02-10 13:26:30 -08:00
_paymentTimeControl ( paypro . expires ) ;
2015-09-16 08:32:22 -07:00
return cb ( ) ;
2015-04-23 08:27:43 -07:00
} ) ;
} , 1 ) ;
} ;
2016-03-22 12:37:55 -07:00
function _paymentTimeControl ( expirationTime ) {
self . paymentExpired = false ;
setExpirationTime ( ) ;
2016-03-30 08:52:19 -07:00
self . countDown = $interval ( function ( ) {
2016-03-22 12:37:55 -07:00
setExpirationTime ( ) ;
} , 1000 ) ;
2016-02-10 13:26:30 -08:00
2016-03-22 12:37:55 -07:00
function setExpirationTime ( ) {
2016-03-31 08:50:35 -07:00
var now = Math . floor ( Date . now ( ) / 1000 ) ;
if ( now > expirationTime ) {
2016-03-30 08:52:19 -07:00
setExpiredValues ( ) ;
return ;
2016-02-04 05:33:41 -08:00
}
2016-03-31 08:50:35 -07:00
var totalSecs = expirationTime - now ;
var m = Math . floor ( totalSecs / 60 ) ;
var s = totalSecs % 60 ;
2016-03-31 10:11:00 -07:00
self . remainingTimeStr = ( '0' + m ) . slice ( - 2 ) + ":" + ( '0' + s ) . slice ( - 2 ) ;
2016-03-22 12:37:55 -07:00
} ;
2016-02-10 13:26:30 -08:00
2016-03-30 08:52:19 -07:00
function setExpiredValues ( ) {
2016-02-10 13:26:30 -08:00
self . paymentExpired = true ;
2016-03-22 12:37:55 -07:00
self . remainingTimeStr = null ;
2016-02-10 13:26:30 -08:00
self . _paypro = null ;
self . error = gettext ( 'Cannot sign: The payment request has expired' ) ;
2016-03-30 08:52:19 -07:00
if ( self . countDown ) $interval . cancel ( self . countDown ) ;
2016-02-10 13:26:30 -08:00
} ;
2016-03-08 10:05:01 -08:00
} ;
2016-02-04 05:33:41 -08:00
2015-04-23 08:27:43 -07:00
this . setFromUri = function ( uri ) {
2015-09-16 08:32:22 -07:00
var self = this ;
2015-09-25 09:10:05 -07:00
2015-04-23 08:27:43 -07:00
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 ;
2015-09-29 08:51:08 -07:00
// 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 ) {
2015-09-29 08:27:14 -07:00
if ( err ) {
return err ;
}
2015-09-25 09:10:05 -07:00
} ) ;
2015-09-29 08:27:14 -07:00
} else {
uri = sanitizeUri ( uri ) ;
2015-04-23 08:27:43 -07:00
2015-09-29 08:27:14 -07:00
if ( ! bitcore . URI . isValid ( uri ) ) {
return uri ;
}
var parsed = new bitcore . URI ( uri ) ;
2015-09-16 08:32:22 -07:00
2015-09-29 08:27:14 -07:00
var addr = parsed . address ? parsed . address . toString ( ) : '' ;
var message = parsed . message ;
2015-04-23 08:27:43 -07:00
2015-09-29 08:27:14 -07:00
var amount = parsed . amount ?
( parsed . amount . toFixed ( 0 ) * satToUnit ) . toFixed ( this . unitDecimals ) : 0 ;
2015-04-23 08:27:43 -07:00
2015-09-16 08:32:22 -07:00
2015-09-29 08:27:14 -07:00
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 ;
}
2015-09-16 08:32:22 -07:00
}
2015-04-23 08:27:43 -07:00
} ;
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 ;
}
} ;
2016-02-04 05:33:41 -08:00
// History
2015-04-23 08:27:43 -07:00
function strip ( number ) {
return ( parseFloat ( number . toPrecision ( 12 ) ) ) ;
}
this . getUnitName = function ( ) {
return this . unitName ;
} ;
this . getAlternativeIsoCode = function ( ) {
return this . alternativeIsoCode ;
} ;
2016-02-10 12:35:26 -08:00
this . openTxModal = function ( tx ) {
2016-02-09 13:48:53 -08:00
$rootScope . $emit ( 'Local/TxModal' , tx ) ;
} ;
2015-04-23 08:27:43 -07:00
this . hasAction = function ( actions , action ) {
return actions . hasOwnProperty ( 'create' ) ;
} ;
2016-04-13 10:05:36 -07:00
this . sendMax = function ( availableBalanceSat ) {
if ( availableBalanceSat == 0 ) {
this . error = gettext ( "Cannot create transaction. Insufficient funds" ) ;
return ;
}
2015-06-28 07:03:28 -07:00
var self = this ;
2016-04-13 10:05:36 -07:00
var fc = profileService . focusedClient ;
2016-02-22 14:56:53 -08:00
this . error = null ;
this . setOngoingProcess ( gettextCatalog . getString ( 'Calculating fee' ) ) ;
feeService . getCurrentFeeValue ( function ( err , feePerKb ) {
2016-04-13 10:05:36 -07:00
if ( err || ! lodash . isNumber ( feePerKb ) ) {
self . setOngoingProcess ( ) ;
2016-02-22 14:56:53 -08:00
self . error = gettext ( 'Could not get fee value' ) ;
return ;
}
2016-04-13 10:05:36 -07:00
var opts = { } ;
opts . feePerKb = feePerKb ;
opts . returnInputs = true ;
var config = configService . getSync ( ) ;
opts . excludeUnconfirmedUtxos = ! config . wallet . spendUnconfirmed ;
self . setOngoingProcess ( gettextCatalog . getString ( 'Retrieving inputs information' ) ) ;
2016-02-22 14:56:53 -08:00
2016-04-13 10:05:36 -07:00
fc . getSendMaxInfo ( opts , function ( err , resp ) {
self . setOngoingProcess ( ) ;
if ( err ) {
self . error = err ;
$scope . $apply ( ) ;
return ;
}
2015-06-28 07:03:28 -07:00
2016-04-13 10:05:36 -07:00
if ( resp . amount == 0 ) {
2016-04-21 13:20:50 -07:00
self . error = gettext ( "Not enough funds for fee" ) ;
2016-04-13 10:05:36 -07:00
$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 ) {
2016-04-29 07:25:30 -07:00
self . sendMaxInfo = resp ;
var amount = parseFloat ( ( resp . amount * self . satToUnit ) . toFixed ( self . unitDecimals ) ) ;
self . setForm ( null , amount , null ) ;
2016-04-13 10:05:36 -07:00
} 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' ) ;
2015-12-24 06:08:49 -08:00
}
2015-12-23 13:05:22 -08:00
} ) ;
2015-09-25 09:39:41 -07:00
} ) ;
2015-08-12 07:49:13 -07:00
} ;
2015-06-19 11:00:27 -07:00
2015-06-27 09:22:56 -07:00
/* Start setup */
2016-02-18 12:16:07 -08:00
lodash . assign ( self , vanillaScope ) ;
2015-06-19 11:00:27 -07:00
2015-05-11 11:25:42 -07:00
this . bindTouchDown ( ) ;
2015-09-14 11:01:49 -07:00
if ( profileService . focusedClient ) {
this . setAddress ( ) ;
this . setSendFormInputs ( ) ;
}
2016-02-18 12:16:07 -08:00
2015-03-06 07:00:10 -08:00
} ) ;