2015-03-06 07:00:10 -08:00
'use strict' ;
2016-08-08 08:14:04 -07:00
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 ) {
2016-05-31 10:55:08 -07:00
var isCordova = platformInfo . isCordova ;
var isWP = platformInfo . isWP ;
var isAndroid = platformInfo . isAndroid ;
2016-05-17 07:09:22 -07:00
var isChromeApp = platformInfo . isChromeApp ;
2015-03-06 07:00:10 -08:00
2015-04-23 08:27:43 -07:00
var self = this ;
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 = [ ] ;
2016-05-31 10:55:08 -07:00
ret . isMobile = platformInfo . isMobile ;
ret . isWindowsPhoneApp = platformInfo . isWP ;
2016-03-30 08:52:19 -07:00
ret . countDown = null ;
2016-04-29 07:25:30 -07:00
ret . sendMaxInfo = { } ;
2016-07-18 07:28:03 -07:00
ret . showAlternative = false ;
2016-07-20 07:27:15 -07:00
ret . fromInputAmount = null ;
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 ) {
2016-07-05 06:22:14 -07:00
if ( ! data ) return ;
2015-04-23 08:27:43 -07:00
self . setForm ( data ) ;
2015-04-28 14:11:06 -07:00
$rootScope . $emit ( 'Local/SetTab' , 'send' ) ;
2016-06-09 11:03:14 -07:00
var form = $scope . sendForm ;
2016-06-15 11:06:12 -07:00
if ( form . address . $invalid && ! ongoingProcess . get ( 'fetchingPayPro' ) ) {
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-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 ( ) ;
2016-02-03 08:35:52 -08:00
$rootScope . shouldHideMenuBar = false ;
2015-04-23 08:27:43 -07:00
} ) ;
2016-08-10 08:14:35 -07:00
if ( isCordova && StatusBar . isVisible ) {
var backgroundColor = profileService . focusedClient ? profileService . focusedClient . backgroundColor : "#4B6178" ;
StatusBar . backgroundColorByHexString ( backgroundColor ) ;
2016-08-10 08:08:39 -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 ) {
2016-05-31 10:55:08 -07:00
if ( ! isCordova || platformInfo . isWP ) return cb ( ) ;
2016-02-08 13:36:30 -08:00
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 ) ;
}
} ) ;
} ;
2016-05-09 11:56:44 -07:00
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 ) ) ;
} ) ;
} ;
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' ) ;
2016-05-26 11:02:00 -07:00
this . openAddressbookModal = function ( wallets , address ) {
$scope . wallets = wallets ;
$scope . address = address ;
$scope . self = self ;
2015-06-27 09:48:25 -07:00
2016-05-26 11:02:00 -07:00
$ionicModal . fromTemplateUrl ( 'views/modals/addressbook.html' , {
scope : $scope
} ) . then ( function ( modal ) {
$scope . addressbookModal = modal ;
$scope . addressbookModal . show ( ) ;
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 ) {
2016-05-27 11:16:25 -07:00
$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 ( ) ;
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 ;
2016-06-23 07:58:32 -07:00
var client = profileService . focusedClient ;
if ( ! client || ! client . isComplete ( ) ) return ;
2015-05-13 08:41:05 -07:00
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 ( ) {
2016-06-23 07:58:32 -07:00
addressService . getAddress ( client . 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-08-05 12:55:08 -07:00
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 ) ;
} ) ;
2016-08-08 08:09:36 -07:00
$scope . close = function ( ) {
$scope . popover . hide ( ) ;
}
2016-08-05 12:55:08 -07:00
$timeout ( function ( ) {
2016-08-08 08:09:36 -07:00
$scope . popover . hide ( ) ; //close the popover after 0.7 seconds
} , 700 ) ;
2016-08-05 12:55:08 -07:00
$scope . $on ( '$destroy' , function ( ) {
$scope . popover . remove ( ) ;
} ) ;
} ;
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' ) ) ;
2016-05-31 10:55:08 -07:00
} else if ( platformInfo . isNW ) {
2015-05-28 06:52:33 -07:00
nodeWebkit . writeToClipboard ( addr ) ;
2016-08-05 12:55:08 -07:00
showPopover ( $event ) ;
2015-04-23 08:27:43 -07:00
}
} ;
this . shareAddress = function ( addr ) {
if ( isCordova ) {
window . plugins . socialsharing . share ( 'bitcoin:' + addr , null , null , null ) ;
}
} ;
2016-02-04 05:33:41 -08:00
// Send
2015-04-28 08:06:04 -07:00
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
2016-06-09 11:03:14 -07:00
this . hideMenuBar = lodash . debounce ( function ( hide ) {
if ( hide ) {
$rootScope . shouldHideMenuBar = true ;
} else {
$rootScope . shouldHideMenuBar = false ;
2016-06-09 07:50:12 -07:00
}
2016-06-09 11:03:14 -07:00
$rootScope . $digest ( ) ;
} , 100 ) ;
this . formFocus = function ( what ) {
2016-06-28 12:30:53 -07:00
if ( isCordova && this . isWindowsPhoneApp ) {
2016-06-09 11:03:14 -07:00
this . hideMenuBar ( what ) ;
2015-04-23 08:27:43 -07: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
} ;
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 ;
2016-07-20 07:27:15 -07:00
if ( self . isRateAvailable ) {
2015-04-23 08:27:43 -07:00
$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 ;
2016-07-20 07:27:15 -07:00
if ( self . isRateAvailable ) {
2015-04-23 08:27:43 -07:00
$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 ) ;
2016-06-09 11:03:14 -07:00
if ( $scope . sendForm && $scope . sendForm . address . $valid ) {
2015-10-22 14:43:32 -07:00
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
2016-07-11 07:46:48 -07:00
this . error = bwcError . 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-08-09 11:28:03 -07:00
this . setAmount = function ( amount , useAlternativeAmount ) {
2016-07-20 06:37:12 -07:00
$scope . showAlternative = useAlternativeAmount ;
2016-07-20 07:27:15 -07:00
self . fromInputAmount = true ;
2016-08-09 11:28:03 -07:00
self . setForm ( null , amount , null ) ;
2016-07-19 13:10:40 -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 ;
2016-05-09 11:56:44 -07:00
var client = 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 ( ) ;
2016-06-28 12:30:53 -07:00
if ( isCordova && this . isWindowsPhoneApp )
$rootScope . shouldHideMenuBar = true ;
2015-04-23 08:27:43 -07:00
2016-06-09 11:03:14 -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
2016-05-09 11:56:44 -07:00
if ( comment && ! client . credentials . sharedEncryptingKey ) {
2015-09-14 08:12:24 -07:00
var msg = 'Could not add message to imported wallet without shared encrypting key' ;
$log . warn ( msg ) ;
return self . setSendError ( gettext ( msg ) ) ;
}
2016-06-23 14:46:48 -07:00
if ( form . amount . $modelValue * unitToSat > Number . MAX _SAFE _INTEGER ) {
2016-06-21 08:58:59 -07:00
var msg = 'Amount too big' ;
$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-05-09 11:56:44 -07:00
var txp = { } ;
2016-04-29 07:25:30 -07:00
2016-04-29 11:33:09 -07:00
if ( ! lodash . isEmpty ( self . sendMaxInfo ) ) {
2016-05-09 11:56:44 -07:00
txp . sendMax = true ;
txp . inputs = self . sendMaxInfo . inputs ;
txp . fee = self . sendMaxInfo . fee ;
2016-05-17 07:09:22 -07:00
} else {
2016-05-09 11:56:44 -07:00
txp . amount = amount ;
2016-04-29 07:25:30 -07:00
}
2016-05-09 11:56:44 -07:00
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' ;
2016-01-29 11:39:32 -08:00
2016-06-13 07:44:58 -07:00
ongoingProcess . set ( 'creatingTx' , true ) ;
2016-05-09 11:56:44 -07:00
walletService . createTx ( client , txp , function ( err , createdTxp ) {
2016-06-13 07:44:58 -07:00
ongoingProcess . set ( 'creatingTx' , false ) ;
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-05-09 11:56:44 -07:00
if ( ! client . canSign ( ) && ! client . isPrivKeyExternal ( ) ) {
2016-01-29 11:39:32 -08:00
$log . info ( 'No signing proposal: No private key' ) ;
2016-06-30 09:05:15 -07:00
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' ) ;
} ) ;
2015-07-24 08:11:07 -07:00
} ) ;
2016-01-29 11:39:32 -08:00
} else {
2016-05-09 11:56:44 -07:00
$rootScope . $emit ( 'Local/NeedsConfirmation' , createdTxp , function ( accept ) {
if ( accept ) self . confirmTx ( createdTxp ) ;
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-05-17 07:09:22 -07:00
} ;
2016-01-27 11:41:12 -08:00
2016-02-25 07:07:47 -08:00
this . confirmTx = function ( txp ) {
2016-05-09 11:56:44 -07:00
var client = profileService . focusedClient ;
2016-01-27 11:41:12 -08:00
var self = this ;
2016-04-29 08:14:09 -07:00
2016-05-09 11:56:44 -07:00
fingerprintService . check ( client , function ( err ) {
2016-01-27 11:41:12 -08:00
if ( err ) {
2016-01-29 11:39:32 -08:00
return self . setSendError ( err ) ;
2016-01-27 11:41:12 -08:00
}
2016-04-29 08:14:09 -07:00
2016-05-09 11:56:44 -07:00
handleEncryptedWallet ( client , function ( err ) {
2016-01-29 11:39:32 -08:00
if ( err ) {
2016-05-09 11:56:44 -07:00
return self . setSendError ( err ) ;
}
2016-04-29 08:14:09 -07:00
2016-06-13 07:44:58 -07:00
ongoingProcess . set ( 'sendingTx' , true ) ;
2016-05-09 11:56:44 -07:00
walletService . publishTx ( client , txp , function ( err , publishedTxp ) {
2016-06-14 13:14:37 -07:00
ongoingProcess . set ( 'sendingTx' , false ) ;
2016-04-29 08:14:09 -07:00
if ( err ) {
2016-05-09 11:56:44 -07:00
return self . setSendError ( err ) ;
}
2016-04-29 08:14:09 -07:00
2016-06-13 07:44:58 -07:00
ongoingProcess . set ( 'signingTx' , true ) ;
2016-05-09 11:56:44 -07:00
walletService . signTx ( client , publishedTxp , function ( err , signedTxp ) {
2016-06-13 07:44:58 -07:00
ongoingProcess . set ( 'signingTx' , false ) ;
2016-05-09 11:56:44 -07:00
walletService . lock ( client ) ;
if ( err ) {
$scope . $emit ( 'Local/TxProposalAction' ) ;
return self . setSendError (
2016-05-17 07:09:22 -07:00
err . message ?
err . message :
gettext ( 'The payment was created but could not be completed. Please try again from home screen' ) ) ;
2016-05-09 11:56:44 -07:00
}
2016-05-17 07:09:22 -07:00
2016-05-09 11:56:44 -07:00
if ( signedTxp . status == 'accepted' ) {
2016-06-13 07:44:58 -07:00
ongoingProcess . set ( 'broadcastingTx' , true ) ;
2016-05-09 11:56:44 -07:00
walletService . broadcastTx ( client , signedTxp , function ( err , broadcastedTxp ) {
2016-06-13 07:44:58 -07:00
ongoingProcess . set ( 'broadcastingTx' , false ) ;
2016-05-09 11:56:44 -07:00
if ( err ) {
return self . setSendError ( err ) ;
}
self . resetForm ( ) ;
go . walletHome ( ) ;
2016-06-16 10:57:30 -07:00
var type = txStatus . notify ( broadcastedTxp ) ;
$scope . openStatusModal ( type , broadcastedTxp , function ( ) {
2016-05-09 11:56:44 -07:00
$scope . $emit ( 'Local/TxProposalAction' , broadcastedTxp . status == 'broadcasted' ) ;
} ) ;
} ) ;
} else {
self . resetForm ( ) ;
go . walletHome ( ) ;
2016-06-16 10:57:30 -07:00
var type = txStatus . notify ( signedTxp ) ;
$scope . openStatusModal ( type , signedTxp , function ( ) {
2016-05-09 11:56:44 -07:00
$scope . $emit ( 'Local/TxProposalAction' ) ;
} ) ;
}
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
} ) ;
} ;
2016-06-16 10:57:30 -07:00
$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 ( ) ;
} ) ;
} ;
2016-05-26 06:40:20 -07:00
$scope . openSearchModal = function ( ) {
2016-05-26 11:02:00 -07:00
var fc = profileService . focusedClient ;
$scope . color = fc . backgroundColor ;
$scope . self = self ;
2016-05-17 07:09:22 -07:00
2016-05-26 11:02:00 -07:00
$ionicModal . fromTemplateUrl ( 'views/modals/search.html' , {
scope : $scope ,
focusFirstInput : true
} ) . then ( function ( modal ) {
$scope . searchModal = modal ;
$scope . searchModal . show ( ) ;
} ) ;
2016-05-17 07:09:22 -07:00
} ;
2016-08-08 12:00:26 -07:00
$scope . openCustomInputAmountModal = function ( addr ) {
2016-07-22 13:15:11 -07:00
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 ( ) ;
} ) ;
} ;
2016-08-08 12:00:26 -07:00
$scope . openAmountModal = function ( addr ) {
2016-07-25 07:23:05 -07:00
if ( isCordova )
2016-08-08 12:00:26 -07:00
$scope . openInputAmountModal ( addr ) ;
2016-07-25 07:23:05 -07:00
else
2016-08-08 12:00:26 -07:00
$scope . openCustomInputAmountModal ( addr ) ;
2016-07-25 07:23:05 -07:00
} ;
2016-07-19 08:00:58 -07:00
$scope . openInputAmountModal = function ( addr ) {
2016-07-18 10:50:39 -07:00
var fc = profileService . focusedClient ;
$scope . color = fc . backgroundColor ;
$scope . showAlternativeAmount = $scope . showAlternative || null ;
2016-08-10 08:08:39 -07:00
if ( $scope . showAlternativeAmount ) {
$scope . amount = $scope . sendForm . alternative . $viewValue || null ;
} else {
$scope . amount = $scope . sendForm . amount . $viewValue || null ;
}
2016-07-18 10:50:39 -07:00
$scope . self = self ;
2016-08-08 12:00:26 -07:00
$scope . addr = addr ;
2016-07-18 10:50:39 -07:00
$ionicModal . fromTemplateUrl ( 'views/modals/inputAmount.html' , {
scope : $scope
} ) . then ( function ( modal ) {
$scope . inputAmountModal = modal ;
$scope . inputAmountModal . show ( ) ;
} ) ;
} ;
2015-12-02 07:21:26 -08:00
this . setForm = function ( to , amount , comment ) {
2016-06-09 11:03:14 -07:00
var form = $scope . sendForm ;
2015-04-23 08:27:43 -07:00
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 ( ) ;
2016-08-10 12:55:33 -07:00
if ( ! this . fromInputAmount )
this . lockAmount = true ;
2016-07-20 07:27:15 -07:00
this . fromInputAmount = false ;
2015-04-23 08:27:43 -07:00
}
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 ;
this . lockAddress = false ;
this . lockAmount = false ;
this . _amount = this . _address = null ;
2016-06-09 11:03:14 -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
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-06-13 07:44:58 -07:00
ongoingProcess . set ( 'fetchingPayPro' , true ) ;
2015-04-23 08:27:43 -07:00
$log . debug ( 'Fetch PayPro Request...' , uri ) ;
$timeout ( function ( ) {
fc . fetchPayPro ( {
payProUrl : uri ,
} , function ( err , paypro ) {
2016-06-13 07:44:58 -07:00
ongoingProcess . set ( 'fetchingPayPro' , false ) ;
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-05-20 07:37:13 -07:00
this . openTxModal = function ( btx ) {
var self = this ;
2016-06-23 14:46:48 -07:00
$scope . btx = lodash . cloneDeep ( btx ) ;
2016-05-20 07:37:13 -07:00
$scope . self = self ;
$ionicModal . fromTemplateUrl ( 'views/modals/tx-details.html' , {
scope : $scope ,
hideDelay : 500
} ) . then ( function ( modal ) {
$scope . txDetailsModal = modal ;
$scope . txDetailsModal . show ( ) ;
} ) ;
2016-02-09 13:48:53 -08:00
} ;
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 ;
2016-06-13 07:44:58 -07:00
ongoingProcess . set ( 'calculatingFee' , true ) ;
2016-02-22 14:56:53 -08:00
2016-08-01 10:16:47 -07:00
$timeout ( function ( ) {
2015-06-28 07:03:28 -07:00
2016-08-01 10:16:47 -07:00
feeService . getCurrentFeeValue ( function ( err , feePerKb ) {
ongoingProcess . set ( 'calculatingFee' , false ) ;
if ( err || ! lodash . isNumber ( feePerKb ) ) {
self . error = gettext ( 'Could not get fee value' ) ;
2016-04-13 10:05:36 -07:00
return ;
}
2016-08-01 10:16:47 -07:00
var opts = { } ;
opts . feePerKb = feePerKb ;
opts . returnInputs = true ;
var config = configService . getSync ( ) ;
opts . excludeUnconfirmedUtxos = ! config . wallet . spendUnconfirmed ;
ongoingProcess . set ( 'retrivingInputs' , true ) ;
2016-04-13 10:05:36 -07:00
2016-08-01 10:16:47 -07:00
fc . getSendMaxInfo ( opts , function ( err , resp ) {
ongoingProcess . set ( 'retrivingInputs' , false ) ;
2016-04-13 10:05:36 -07:00
2016-08-01 10:16:47 -07:00
if ( err ) {
self . error = err ;
$scope . $apply ( ) ;
return ;
2016-04-13 10:05:36 -07:00
}
2016-08-01 10:16:47 -07:00
if ( resp . amount == 0 ) {
self . error = gettext ( "Not enough funds for fee" ) ;
$scope . $apply ( ) ;
return ;
2016-04-13 10:05:36 -07:00
}
2016-08-01 10:16:47 -07:00
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' ) ;
2016-04-13 10:05:36 -07:00
}
2016-08-01 10:16:47 -07:00
} ) ;
2015-12-23 13:05:22 -08:00
} ) ;
2016-08-01 10:16:47 -07:00
} , 10 ) ;
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
} ) ;