Merge pull request #4247 from JDonadio/ionic/integration
Ionic/integration
This commit is contained in:
commit
13c91f4278
104 changed files with 3248 additions and 3223 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
|
@ -24,7 +24,8 @@ webkitbuilds/*
|
|||
!webkitbuilds/build-osx.sh
|
||||
!webkitbuilds/Background.png
|
||||
|
||||
|
||||
#fonts
|
||||
public/fonts
|
||||
|
||||
# chrome extensions
|
||||
browser-extensions/chrome/copay-chrome-extension
|
||||
|
|
|
|||
54
Gruntfile.js
54
Gruntfile.js
|
|
@ -61,7 +61,7 @@ module.exports = function(grunt) {
|
|||
'src/js/routes.js',
|
||||
'src/js/services/*.js',
|
||||
'src/js/models/*.js',
|
||||
'src/js/controllers/*.js'
|
||||
'src/js/controllers/**/*.js'
|
||||
],
|
||||
tasks: ['concat:js']
|
||||
}
|
||||
|
|
@ -93,17 +93,13 @@ module.exports = function(grunt) {
|
|||
'bower_components/angular/angular.js',
|
||||
'bower_components/angular-ui-router/release/angular-ui-router.js',
|
||||
'bower_components/angular-foundation/mm-foundation-tpls.js',
|
||||
'bower_components/angular-touch/angular-touch.js',
|
||||
'bower_components/fastclick/lib/fastclick.js',
|
||||
'bower_components/stateful-fastclick/dist/stateful-fastclick.js',
|
||||
'bower_components/angular-stateful-fastclick/lib/angular-stateful-fastclick.js',
|
||||
'bower_components/angular-moment/angular-moment.js',
|
||||
'bower_components/ng-lodash/build/ng-lodash.js',
|
||||
'bower_components/angular-qrcode/angular-qrcode.js',
|
||||
'bower_components/angular-gettext/dist/angular-gettext.js',
|
||||
'bower_components/angular-ui-switch/angular-ui-switch.js',
|
||||
'bower_components/angular-sanitize/angular-sanitize.js',
|
||||
'bower_components/ng-csv/build/ng-csv.js',
|
||||
'bower_components/angular-mocks/angular-mocks.js',
|
||||
'angular-bitcore-wallet-client/angular-bitcore-wallet-client.js'
|
||||
],
|
||||
dest: 'public/lib/angular.js'
|
||||
|
|
@ -116,7 +112,7 @@ module.exports = function(grunt) {
|
|||
'src/js/filters/*.js',
|
||||
'src/js/models/*.js',
|
||||
'src/js/services/*.js',
|
||||
'src/js/controllers/*.js',
|
||||
'src/js/controllers/**/*.js',
|
||||
'src/js/translations.js',
|
||||
'src/js/version.js',
|
||||
'src/js/coinbase.js',
|
||||
|
|
@ -134,11 +130,38 @@ module.exports = function(grunt) {
|
|||
src: [
|
||||
'bower_components/angular/angular-csp.css',
|
||||
'bower_components/foundation/css/foundation.css',
|
||||
'bower_components/animate.css/animate.css',
|
||||
'bower_components/angular-ui-switch/angular-ui-switch.css'
|
||||
'bower_components/animate.css/animate.css'
|
||||
],
|
||||
dest: 'public/css/foundation.css',
|
||||
}
|
||||
},
|
||||
ionic_js: {
|
||||
src: [
|
||||
'bower_components/ionic/release/js/ionic.bundle.min.js'
|
||||
],
|
||||
dest: 'public/lib/ionic.bundle.js'
|
||||
},
|
||||
ionic_css: {
|
||||
src: [
|
||||
'bower_components/ionic/release/css/ionic.min.css'
|
||||
],
|
||||
dest: 'public/css/ionic.css',
|
||||
},
|
||||
ui_components_js: {
|
||||
src: [
|
||||
'bower_components/jquery/dist/jquery.js',
|
||||
'bower_components/roundSlider/dist/roundslider.min.js',
|
||||
'bower_components/angular-gridster/dist/angular-gridster.min.js',
|
||||
'bower_components/javascript-detect-element-resize/detect-element-resize.js'
|
||||
],
|
||||
dest: 'public/lib/ui-components.js'
|
||||
},
|
||||
ui_components_css: {
|
||||
src: [
|
||||
'bower_components/roundSlider/dist/roundslider.min.css',
|
||||
'bower_components/angular-gridster/dist/angular-gridster.min.css'
|
||||
],
|
||||
dest: 'public/css/ui-components.css',
|
||||
},
|
||||
},
|
||||
uglify: {
|
||||
options: {
|
||||
|
|
@ -156,11 +179,10 @@ module.exports = function(grunt) {
|
|||
files: {
|
||||
'i18n/po/template.pot': [
|
||||
'public/index.html',
|
||||
'public/views/*.html',
|
||||
'public/views/**/*.html',
|
||||
'src/js/routes.js',
|
||||
'src/js/services/*.js',
|
||||
'src/js/controllers/*.js'
|
||||
'src/js/controllers/**/*.js'
|
||||
]
|
||||
}
|
||||
},
|
||||
|
|
@ -182,6 +204,12 @@ module.exports = function(grunt) {
|
|||
src: 'bower_components/foundation-icon-fonts/foundation-icons.*',
|
||||
dest: 'public/icons/'
|
||||
},
|
||||
ionic_fonts: {
|
||||
expand: true,
|
||||
flatten: true,
|
||||
src: 'bower_components/ionic/release/fonts/ionicons.*',
|
||||
dest: 'public/fonts/'
|
||||
},
|
||||
linux: {
|
||||
files: [{
|
||||
expand: true,
|
||||
|
|
@ -242,7 +270,7 @@ module.exports = function(grunt) {
|
|||
}
|
||||
});
|
||||
|
||||
grunt.registerTask('default', ['nggettext_compile', 'exec:version', 'exec:coinbase', 'browserify', 'sass', 'concat', 'copy:icons']);
|
||||
grunt.registerTask('default', ['nggettext_compile', 'exec:version', 'exec:coinbase', 'browserify', 'sass', 'concat', 'copy:icons', 'copy:ionic_fonts']);
|
||||
grunt.registerTask('prod', ['default', 'uglify']);
|
||||
grunt.registerTask('translate', ['nggettext_extract']);
|
||||
grunt.registerTask('test', ['karma:unit']);
|
||||
|
|
|
|||
1
angular-bitcore-wallet-client/index.js
vendored
1
angular-bitcore-wallet-client/index.js
vendored
|
|
@ -30,6 +30,7 @@ bwcModule.provider("bwcService", function() {
|
|||
};
|
||||
|
||||
service.getClient = function(walletData, opts) {
|
||||
opts = opts || {};
|
||||
|
||||
//note opts use `baseurl` all lowercase;
|
||||
var bwc = new Client({
|
||||
|
|
|
|||
10
bower.json
10
bower.json
|
|
@ -14,23 +14,19 @@
|
|||
"angular-moment": "0.10.1",
|
||||
"angular-qrcode": "monospaced/angular-qrcode#~6.2.1",
|
||||
"angular-ui-router": "0.2.18",
|
||||
"angular-ui-switch": "0.1.1",
|
||||
"animate.css": "3.5.1",
|
||||
"foundation": "5.5.3",
|
||||
"foundation-icon-fonts": "*",
|
||||
"ionic": "1.3.1",
|
||||
"moment": "2.10.3",
|
||||
"ng-lodash": "0.2.3",
|
||||
"qrcode-decoder-js": "*",
|
||||
"trezor-connect": "~1.0.1",
|
||||
"ng-csv": "~0.3.6",
|
||||
"angular-touch": "^1.5.5",
|
||||
"fastclick": "^1.0.6",
|
||||
"stateful-fastclick": "^1.0.2",
|
||||
"angular-stateful-fastclick": "^1.0.2"
|
||||
"ng-csv": "~0.3.6"
|
||||
},
|
||||
"resolutions": {
|
||||
"angular": "1.4.6",
|
||||
"qrcode-generator": "0.1.0",
|
||||
"fastclick": "1.0.6"
|
||||
"angular-ui-router": "0.2.18"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
./index.html
|
||||
./index.html
|
||||
./css/*.css
|
||||
./font/*
|
||||
./fonts/*
|
||||
./icons/*
|
||||
./img/*
|
||||
./img/**/*
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<widget id="com.bitpay.copay"
|
||||
version="1.12.14"
|
||||
android-versionCode="104"
|
||||
ios-CFBundleVersion="1.12.14">
|
||||
version="2.0.0"
|
||||
android-versionCode="105"
|
||||
ios-CFBundleVersion="2.0.0">
|
||||
<name>Copay</name>
|
||||
<description>
|
||||
A secure bitcoin wallet for friends and companies.
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Package xmlns="http://schemas.microsoft.com/appx/2010/manifest" xmlns:m2="http://schemas.microsoft.com/appx/2013/manifest" xmlns:m3="http://schemas.microsoft.com/appx/2014/manifest" xmlns:mp="http://schemas.microsoft.com/appx/2014/phone/manifest">
|
||||
<Identity Name="18C7659D.CopayWallet" Publisher="CN=F89609D1-EB3E-45FD-A58A-C2E3895FCE7B" Version="1.12.14.0" />
|
||||
<Identity Name="18C7659D.CopayWallet" Publisher="CN=F89609D1-EB3E-45FD-A58A-C2E3895FCE7B" Version="2.0.0.0" />
|
||||
<mp:PhoneIdentity PhoneProductId="5381aa50-9069-11e4-84cc-293caf9cbdc8" PhonePublisherId="F89609D1-EB3E-45FD-A58A-C2E3895FCE7B" />
|
||||
<Properties>
|
||||
<DisplayName>Copay Bitcoin Wallet</DisplayName>
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@
|
|||
<Language code="pl" />
|
||||
<Language code="cs" />
|
||||
</Languages>
|
||||
<App Author="Bitpay Inc." BitsPerPixel="32" Description="A multisignature Bitcoin Wallet" Genre="apps.normal" ProductID="{5381aa50-9069-11e4-84cc-293caf9cbdc8}" Publisher="Copay Bitcoin Wallet" PublisherID="{31cdd08b-457c-413d-b440-f6665eec847d}" RuntimeType="Silverlight" Title="Copay Bitcoin Wallet" Version="1.12.14.0" xmlns="" NotificationService="MPN">
|
||||
<App Author="Bitpay Inc." BitsPerPixel="32" Description="A multisignature Bitcoin Wallet" Genre="apps.normal" ProductID="{5381aa50-9069-11e4-84cc-293caf9cbdc8}" Publisher="Copay Bitcoin Wallet" PublisherID="{31cdd08b-457c-413d-b440-f6665eec847d}" RuntimeType="Silverlight" Title="Copay Bitcoin Wallet" Version="2.0.0.0" xmlns="" NotificationService="MPN">
|
||||
<IconPath IsRelative="true" IsResource="false">Assets\icon@2.png</IconPath>
|
||||
<Capabilities>
|
||||
<Capability Name="ID_CAP_WEBBROWSERCOMPONENT" />
|
||||
|
|
|
|||
|
|
@ -2,8 +2,8 @@
|
|||
"name": "copay",
|
||||
"description": "A multisignature wallet",
|
||||
"author": "BitPay",
|
||||
"version": "1.12.14",
|
||||
"androidVersionCode": "104",
|
||||
"version": "2.0.0",
|
||||
"androidVersionCode": "105",
|
||||
"keywords": [
|
||||
"wallet",
|
||||
"copay",
|
||||
|
|
@ -42,7 +42,7 @@
|
|||
"url": "https://github.com/bitpay/copay/issues"
|
||||
},
|
||||
"dependencies": {
|
||||
"bitcore-wallet-client": "2.5.0",
|
||||
"bitcore-wallet-client": "~2.5.0",
|
||||
"express": "^4.11.2",
|
||||
"fs": "0.0.2",
|
||||
"grunt": "^0.4.5",
|
||||
|
|
@ -91,6 +91,7 @@
|
|||
"load-grunt-tasks": "^3.5.0",
|
||||
"mocha": "^2.4.5",
|
||||
"phantomjs-prebuilt": "^2.1.7",
|
||||
"ionic": "^1.7.14",
|
||||
"xcode": "^0.8.2"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,46 +6,45 @@
|
|||
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=1, user-scalable=no">
|
||||
<meta name="msapplication-tap-highlight" content="no">
|
||||
<meta name="format-detection" content="telephone=no">
|
||||
<link rel="stylesheet" type="text/css" href="css/ionic.css">
|
||||
<link rel="stylesheet" type="text/css" href="css/foundation.css">
|
||||
<link rel="stylesheet" type="text/css" href="icons/foundation-icons.css">
|
||||
<link rel="stylesheet" type="text/css" href="css/ui-components.css">
|
||||
<link rel="stylesheet" type="text/css" href="css/copay.css">
|
||||
<title>Copay - Multisignature Wallet</title>
|
||||
<link rel="shortcut icon" href="img/favicon.ico">
|
||||
</head>
|
||||
<body ng-cloak class="ng-cloak">
|
||||
|
||||
<div class="page" ng-controller="indexController as index">
|
||||
<div notifications="right top"></div>
|
||||
<div ng-include="'views/includes/tx-details.html'" ng-if="index.showTx"></div>
|
||||
<div ng-include="'views/includes/password.html'" ng-if="index.askPassword"></div>
|
||||
<div ng-include="'views/includes/alert.html'" ng-if="index.showAlert"></div>
|
||||
<div ng-include="'views/includes/confirm-tx.html'" ng-if="index.confirmTx"></div>
|
||||
|
||||
<div class="off-canvas-wrap" id="off-canvas-wrap"
|
||||
ng-swipe-disable-mouse
|
||||
ng-swipe-left="index.closeMenu()"
|
||||
ng-swipe-right="index.openMenu()">
|
||||
<div class="inner-wrap">
|
||||
<div ng-include="'views/includes/sidebar.html'" ng-if="index.hasProfile"></div>
|
||||
<div id="sectionContainer">
|
||||
<div id="mainSection">
|
||||
<section ui-view="main"
|
||||
ng-class="{
|
||||
'main': index.hasProfile
|
||||
}"></section>
|
||||
</div>
|
||||
</div>
|
||||
<a class="close-menu" ng-click="index.closeMenu()"></a>
|
||||
<ion-side-menus class="page" ng-controller="indexController as index">
|
||||
|
||||
<!-- Left menu -->
|
||||
<ion-side-menu side="left">
|
||||
<div ng-include="'views/includes/sidebar.html'" ng-if="index.hasProfile"></div>
|
||||
</ion-side-menu>
|
||||
|
||||
<!-- Main content -->
|
||||
|
||||
<ion-side-menu-content on-drag="index.onDrag()">
|
||||
<div notifications="right top"></div>
|
||||
<div id="sectionContainer">
|
||||
<div id="mainSection">
|
||||
<section ui-view="main"
|
||||
ng-class="{
|
||||
'main': index.hasProfile
|
||||
}"></section>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</ion-side-menu-content>
|
||||
</ion-side-menus>
|
||||
|
||||
<script src="lib/ionic.bundle.js"></script>
|
||||
<script src="lib/angular.js"></script>
|
||||
|
||||
<!-- DO NOT DELETE THIS COMMET -->
|
||||
<!-- PLACEHOLDER: CORDOVA SRIPT -->
|
||||
|
||||
<script src="lib/ui-components.js"></script>
|
||||
<script src="js/copay.js"></script>
|
||||
|
||||
</body>
|
||||
|
|
|
|||
|
|
@ -19,17 +19,17 @@
|
|||
</section>
|
||||
</nav>
|
||||
|
||||
<div class="content preferences">
|
||||
<div class="box-notification" ng-show="wordsC.error">
|
||||
<span class="text-warning">
|
||||
{{wordsC.error|translate}}
|
||||
</span>
|
||||
</div>
|
||||
<div class="box-notification" ng-show="wordsC.error">
|
||||
<span class="text-warning">
|
||||
{{wordsC.error|translate}}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<!--
|
||||
## STEP 1
|
||||
-->
|
||||
|
||||
<div class="content preferences text-center">
|
||||
<div ng-show="wordsC.step == 1">
|
||||
<div ng-show="wordsC.mnemonicWords || (wordsC.credentialsEncrypted && !wordsC.deleted)" class="row">
|
||||
<h5 class="text-center" translate>Write your wallet recovery phrase</h5>
|
||||
|
|
@ -37,21 +37,21 @@
|
|||
<span translate>
|
||||
To restore this {{index.m}}-{{index.n}} <b>shared</b> wallet you will need
|
||||
</span>:
|
||||
<ol class="m10t columns size-14 text-gray">
|
||||
<li translate>Your wallet recovery phrase and access to the server that coordinated the initial wallet creation. You still need {{index.m}} keys to spend.</li>
|
||||
<li translate><b>OR</b> the wallet recovery phrase of <b>all</b> copayers in the wallet</li>
|
||||
<li translate><b>OR</b> 1 wallet export file and the remaining quorum of wallet recovery phrases (e.g. in a 3-5 wallet: 1 wallet export file + 2 wallet recovery phrases of any of the other copayers).</li>
|
||||
</ol>
|
||||
<div class="m10t columns size-14 text-gray">
|
||||
<span translate>Your wallet recovery phrase and access to the server that coordinated the initial wallet creation. You still need {{index.m}} keys to spend.</span>
|
||||
<span translate><b>OR</b> the wallet recovery phrase of <b>all</b> copayers in the wallet</span>
|
||||
<span translate><b>OR</b> 1 wallet export file and the remaining quorum of wallet recovery phrases (e.g. in a 3-5 wallet: 1 wallet export file + 2 wallet recovery phrases of any of the other copayers).</span>
|
||||
</div>
|
||||
</span>
|
||||
</div>
|
||||
<div class="size-14 text-gray columns" ng-show="(index.n>1 && index.m == index.n )">
|
||||
<span translate>
|
||||
To restore this {{index.m}}-{{index.n}} <b>shared</b> wallet you will need
|
||||
</span>:
|
||||
<ol class="m10t columns size-14 text-gray">
|
||||
<li translate>Your wallet recovery phrase and access to the server that coordinated the initial wallet creation. You still need {{index.m}} keys to spend.</li>
|
||||
<li translate><b>OR</b> the wallet recovery phrases of <b>all</b> copayers in the wallet</li>
|
||||
</ol>
|
||||
<div class="m10t columns size-14 text-gray">
|
||||
<span translate>Your wallet recovery phrase and access to the server that coordinated the initial wallet creation. You still need {{index.m}} keys to spend.</span>
|
||||
<span translate><b>OR</b> the wallet recovery phrases of <b>all</b> copayers in the wallet</span>
|
||||
</div>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -97,7 +97,7 @@
|
|||
<div class="button-box">
|
||||
<button
|
||||
ng-show="!wordsC.deleted"
|
||||
ng-disabled="wordsC.credentialsEncrypted"
|
||||
ng-disabled="wordsC.credentialsEncrypted || wordsC.error"
|
||||
class="round expand m0"
|
||||
ng-style="{'background-color':index.backgroundColor}"
|
||||
ng-click="wordsC.goToStep(2);"
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<div
|
||||
class="topbar-container"
|
||||
<div
|
||||
class="topbar-container"
|
||||
ng-include="'views/includes/topbar.html'"
|
||||
ng-init="titleSection='Buy'; goBackToState = 'coinbase'; noColor = true">
|
||||
</div>
|
||||
|
|
@ -9,13 +9,7 @@
|
|||
|
||||
<div class="onGoingProcess" ng-show="buy.loading">
|
||||
<div class="onGoingProcess-content" ng-style="{'background-color': '#2b71b1'}">
|
||||
<div class="spinner">
|
||||
<div class="rect1"></div>
|
||||
<div class="rect2"></div>
|
||||
<div class="rect3"></div>
|
||||
<div class="rect4"></div>
|
||||
<div class="rect5"></div>
|
||||
</div>
|
||||
<ion-spinner class="spinner-stable" icon="lines"></ion-spinner>
|
||||
<span>{{buy.loading}}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -32,42 +26,42 @@
|
|||
|
||||
<div class="row m20t"
|
||||
ng-show="index.coinbaseAccount && !buy.buyInfo && !buy.receiveInfo">
|
||||
<div class="columns">
|
||||
<div class="columns">
|
||||
|
||||
<form name="buyCoinbaseForm"
|
||||
<form name="buyCoinbaseForm"
|
||||
ng-submit="buy.buyRequest(index.coinbaseToken, index.coinbaseAccount)" novalidate>
|
||||
|
||||
<div ng-if="index.coinbaseToken" ng-init="buy.getPaymentMethods(index.coinbaseToken)">
|
||||
<label>Payment method</label>
|
||||
<select
|
||||
ng-model="selectedPaymentMethod.id"
|
||||
<select
|
||||
ng-model="selectedPaymentMethod.id"
|
||||
ng-options="item.id as item.name for item in buy.paymentMethods">
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<label>Amount
|
||||
<span
|
||||
<label>Amount
|
||||
<span
|
||||
ng-if="index.coinbaseToken"
|
||||
ng-init="buy.getPrice(index.coinbaseToken)"
|
||||
ng-show="buy.buyPrice"
|
||||
ng-show="buy.buyPrice"
|
||||
class="size-11 text-light right">
|
||||
1 BTC <i class="icon-arrow-right"></i> {{buy.buyPrice.amount}} {{buy.buyPrice.currency}}
|
||||
</span>
|
||||
</label>
|
||||
|
||||
|
||||
<div class="input">
|
||||
<input ng-show="!showAlternative" type="number" id="amount"
|
||||
<input ng-show="!showAlternative" type="number" id="amount"
|
||||
name="amount" ng-attr-placeholder="{{'Amount in ' + (showAlternative ? 'USD' : 'BTC')}}"
|
||||
ng-minlength="0.00000001" ng-maxlength="10000000000"
|
||||
ng-minlength="0.00000001" ng-maxlength="10000000000"
|
||||
ng-model="amount" autocomplete="off" ng-disabled="buy.loading">
|
||||
|
||||
<input ng-show="showAlternative" type="number" id="fiat"
|
||||
name="fiat" ng-attr-placeholder="{{'Amount in ' + (showAlternative ? 'USD' : 'BTC')}}"
|
||||
|
||||
<input ng-show="showAlternative" type="number" id="fiat"
|
||||
name="fiat" ng-attr-placeholder="{{'Amount in ' + (showAlternative ? 'USD' : 'BTC')}}"
|
||||
ng-model="fiat" autocomplete="off" ng-disabled="buy.loading">
|
||||
|
||||
<a ng-show="!showAlternative" class="postfix button"
|
||||
<a ng-show="!showAlternative" class="postfix button"
|
||||
ng-click="showAlternative = true; amount = null">BTC</a>
|
||||
<a ng-show="showAlternative" class="postfix button black"
|
||||
<a ng-show="showAlternative" class="postfix button black"
|
||||
ng-click="showAlternative = false; fiat = null">USD</a>
|
||||
</div>
|
||||
|
||||
|
|
@ -87,7 +81,7 @@
|
|||
<i class="db fi-arrow-down size-24 m10v"></i>
|
||||
</div>
|
||||
|
||||
<div
|
||||
<div
|
||||
ng-if="index.coinbaseToken"
|
||||
ng-init="buy.init(index.coinbaseTestnet)"
|
||||
ng-click="openWalletsModal(buy.otherWallets)">
|
||||
|
|
@ -102,7 +96,7 @@
|
|||
</div>
|
||||
|
||||
<div class="input m20t">
|
||||
<input class="button black expand round"
|
||||
<input class="button black expand round"
|
||||
ng-disabled="buy.loading || (!amount && !fiat) || !selectedPaymentMethod"
|
||||
ng-style="{'background-color': '#2b71b1'}"
|
||||
type="submit" value="{{'Continue'}}">
|
||||
|
|
@ -146,11 +140,11 @@
|
|||
<li class="line-b p15">
|
||||
<span class="m10 text-normal text-bold">Total</span>
|
||||
<span class="right text-gray">{{buy.buyInfo.total.amount}} {{buy.buyInfo.total.currency}}</span>
|
||||
</li>
|
||||
</li>
|
||||
<li class="line-b p15">
|
||||
<span class="m10 text-normal text-bold">Payout at</span>
|
||||
<span class="right text-gray">{{buy.buyInfo.payout_at | amCalendar}}</span>
|
||||
</li>
|
||||
</li>
|
||||
<li class="line-b p15">
|
||||
<span class="m10 text-normal text-bold">Deposit into Copay Wallet</span>
|
||||
<span class="right text-gray">{{buy.selectedWalletName}}</span>
|
||||
|
|
@ -158,7 +152,7 @@
|
|||
</ul>
|
||||
<div class="row">
|
||||
<div class="columns">
|
||||
<button class="button black round expand"
|
||||
<button class="button black round expand"
|
||||
ng-style="{'background-color': '#2b71b1'}"
|
||||
ng-click="buy.confirmBuy(index.coinbaseToken, index.coinbaseAccount, buy.buyInfo)"
|
||||
ng-disabled="buy.loading">
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<div
|
||||
class="topbar-container"
|
||||
<div
|
||||
class="topbar-container"
|
||||
ng-include="'views/includes/topbar.html'"
|
||||
ng-init="titleSection='Buy'; goBackToState = 'glidera'; noColor = true">
|
||||
</div>
|
||||
|
|
@ -9,13 +9,7 @@
|
|||
|
||||
<div class="onGoingProcess" ng-show="buy.loading">
|
||||
<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>
|
||||
<ion-spinner class="spinner-stable" icon="lines"></ion-spinner>
|
||||
<span>{{buy.loading}}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -24,7 +18,7 @@
|
|||
<h4 class="title m0 text-left">
|
||||
<span class="text-light">Daily buy limit</span>:
|
||||
{{index.glideraLimits.dailyBuy|currency:'':2}} {{index.glideraLimits.currency}}
|
||||
(remaining {{index.glideraLimits.dailyBuyRemaining|currency:'':2}} {{index.glideraLimits.currency}})
|
||||
(remaining {{index.glideraLimits.dailyBuyRemaining|currency:'':2}} {{index.glideraLimits.currency}})
|
||||
<br>
|
||||
<span class="text-light">Monthly buy limit</span>:
|
||||
{{index.glideraLimits.monthlyBuy|currency:'':2}} {{index.glideraLimits.currency}}
|
||||
|
|
@ -43,16 +37,16 @@
|
|||
|
||||
<div ng-show="!buy.show2faCodeInput && !buy.success">
|
||||
|
||||
<form name="buyPriceForm"
|
||||
<form name="buyPriceForm"
|
||||
ng-submit="buy.get2faCode(index.glideraToken)" novalidate>
|
||||
|
||||
<div ng-if="index.glideraToken"
|
||||
<div ng-if="index.glideraToken"
|
||||
ng-init="buy.init(index.glideraTestnet)"
|
||||
ng-click="openWalletsModal(buy.otherWallets)">
|
||||
<label>Wallet</label>
|
||||
<div class="input">
|
||||
<input type="text" id="address" name="address" ng-disabled="buy.selectedWalletId"
|
||||
ng-attr-placeholder="{{'Choose your destination wallet'}}"
|
||||
<input type="text" id="address" name="address" ng-disabled="buy.selectedWalletId"
|
||||
ng-attr-placeholder="{{'Choose your destination wallet'}}"
|
||||
ng-model="buy.selectedWalletName" required>
|
||||
<a class="postfix size-12 m0 text-gray">
|
||||
<i class="icon-wallet size-18"></i>
|
||||
|
|
@ -62,41 +56,41 @@
|
|||
|
||||
<label>Amount in {{showAlternative ? 'USD' : 'BTC'}}</label>
|
||||
<div class="input">
|
||||
<input ng-show="!showAlternative" type="number" id="qty"
|
||||
<input ng-show="!showAlternative" type="number" id="qty"
|
||||
name="qty" ng-attr-placeholder="{{'Amount'}}"
|
||||
ng-minlength="0.00000001" ng-maxlength="10000000000"
|
||||
ng-minlength="0.00000001" ng-maxlength="10000000000"
|
||||
ng-model="qty" autocomplete="off" ng-change="buy.getBuyPrice(index.glideraToken, {'qty': qty})">
|
||||
|
||||
<input ng-show="showAlternative" type="number" id="fiat"
|
||||
name="fiat" ng-attr-placeholder="{{'Amount'}}"
|
||||
|
||||
<input ng-show="showAlternative" type="number" id="fiat"
|
||||
name="fiat" ng-attr-placeholder="{{'Amount'}}"
|
||||
ng-model="fiat" autocomplete="off" ng-change="buy.getBuyPrice(index.glideraToken, {'fiat': fiat})">
|
||||
|
||||
<a ng-show="!showAlternative" class="postfix"
|
||||
|
||||
<a ng-show="!showAlternative" class="postfix"
|
||||
ng-click="showAlternative = true; qty = null; buy.buyPrice = null">BTC</a>
|
||||
<a ng-show="showAlternative" class="postfix"
|
||||
<a ng-show="showAlternative" class="postfix"
|
||||
ng-click="showAlternative = false; fiat = null; buy.buyPrice = null">USD</a>
|
||||
|
||||
<div class="text-center text-gray size-12 m20b" ng-show="!buy.gettingBuyPrice && buy.buyPrice.qty">
|
||||
Buy
|
||||
<span ng-show="qty">{{buy.buyPrice.subtotal|currency:'':2}} {{buy.buyPrice.currency}} in Bitcoin</span>
|
||||
<span ng-show="fiat">{{buy.buyPrice.qty}} BTC</span>
|
||||
Buy
|
||||
<span ng-show="qty">{{buy.buyPrice.subtotal|currency:'':2}} {{buy.buyPrice.currency}} in Bitcoin</span>
|
||||
<span ng-show="fiat">{{buy.buyPrice.qty}} BTC</span>
|
||||
at {{buy.buyPrice.price}} {{buy.buyPrice.currency}}/BTC
|
||||
</div>
|
||||
<div class="text-center text-gray size-12 m20b" ng-show="!buy.gettingBuyPrice && !buy.buyPrice.qty">
|
||||
(Enter the amount to get the exchange rate)
|
||||
</div>
|
||||
|
||||
|
||||
<div class="text-center text-gray size-12 m20b" ng-show="buy.gettingBuyPrice">
|
||||
...
|
||||
</div>
|
||||
|
||||
<input class="button black expand round"
|
||||
<input class="button black expand round"
|
||||
ng-style="{'background-color':index.backgroundColor}"
|
||||
type="submit" value="{{'Continue'}}"
|
||||
type="submit" value="{{'Continue'}}"
|
||||
ng-disabled="index.glideraLimits.transactDisabledPendingFirstTransaction || !buy.buyPrice.qty ||
|
||||
!buy.selectedWalletId || buy.loading">
|
||||
</div>
|
||||
</form>
|
||||
</form>
|
||||
</div>
|
||||
<div ng-show="buy.show2faCodeInput && !buy.success">
|
||||
<div class="m10t text-center">
|
||||
|
|
@ -105,10 +99,10 @@
|
|||
A SMS containing a confirmation code was sent to your phone. <br>
|
||||
Please, enter the code below
|
||||
</p>
|
||||
<form name="buyForm"
|
||||
<form name="buyForm"
|
||||
ng-submit="buy.sendRequest(index.glideraToken, index.glideraPermissions, twoFaCode)" novalidate>
|
||||
<input type="number" ng-model="twoFaCode" required>
|
||||
<input class="button black expand round"
|
||||
<input class="button black expand round"
|
||||
ng-style="{'background-color':index.backgroundColor}"
|
||||
type="submit" value="{{'Buy'}}" ng-disabled="buyForm.$invalid || buy.loading">
|
||||
</form>
|
||||
|
|
@ -134,6 +128,6 @@
|
|||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="extra-margin-bottom"></div>
|
||||
|
|
|
|||
|
|
@ -28,13 +28,7 @@
|
|||
|
||||
<div class="onGoingProcess" ng-show="coinbase.loading || index.coinbaseLoading">
|
||||
<div class="onGoingProcess-content" ng-style="{'background-color': '#2b71b1'}">
|
||||
<div class="spinner">
|
||||
<div class="rect1"></div>
|
||||
<div class="rect2"></div>
|
||||
<div class="rect3"></div>
|
||||
<div class="rect4"></div>
|
||||
<div class="rect5"></div>
|
||||
</div>
|
||||
<ion-spinner class="spinner-stable" icon="lines"></ion-spinner>
|
||||
<span ng-show="coinbase.loading">Connecting to Coinbase...</span>
|
||||
<span ng-show="index.coinbaseLoading">{{index.coinbaseLoading}}</span>
|
||||
</div>
|
||||
|
|
@ -156,7 +150,7 @@
|
|||
<span ng-show="tx.type == 'sell' && tx.status == 'completed'">Sold</span>
|
||||
<span ng-show="tx.type == 'buy' && tx.status == 'completed'">Bought</span>
|
||||
<span class="text-bold">
|
||||
<span ng-if="tx.type == 'sell' || (tx.type == 'send' && tx.from)">-</span>{{tx.amount.amount.replace('-','')}}
|
||||
<span ng-if="tx.type == 'sell' || (tx.type == 'send' && tx.from)">-</span>{{tx.amount.amount.replace('-','')}}
|
||||
{{tx.amount.currency}}
|
||||
</span>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<div
|
||||
class="topbar-container"
|
||||
<div
|
||||
class="topbar-container"
|
||||
ng-include="'views/includes/topbar.html'"
|
||||
ng-init="titleSection='Coinbase'; closeToHome = true">
|
||||
</div>
|
||||
|
|
@ -8,13 +8,7 @@
|
|||
|
||||
<div class="onGoingProcess" ng-show="coinbase.loading">
|
||||
<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>
|
||||
<ion-spinner class="spinner-stable" icon="lines"></ion-spinner>
|
||||
<span ng-show="coinbase.loading">Connecting to Coinbase...</span>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -22,10 +16,10 @@
|
|||
<div class="row m20t">
|
||||
<div class="large-12 columns">
|
||||
<div class="text-center">
|
||||
<img src="img/coinbase-logo.png"
|
||||
<img src="img/coinbase-logo.png"
|
||||
ng-click="index.updateCoinbase()" width="100">
|
||||
</div>
|
||||
|
||||
|
||||
<div class="m10t text-center" ng-show="coinbase.error">
|
||||
<div class="notification m10b size-12 text-warning">{{coinbase.error}}</div>
|
||||
<button class="outline dark-gray tiny round" ng-click="coinbase.submitOauthCode(coinbase.code)">Try again</button>
|
||||
|
|
|
|||
|
|
@ -13,13 +13,7 @@
|
|||
<qrcode size="220" error-correction-level="L" data="{{index.walletSecret}}"></qrcode>
|
||||
<div ng-show="!index.walletSecret" style="position:relative; top:-226px; height:0px">
|
||||
<div style="height:220px; width:220px; margin:auto; background: white">
|
||||
<div class="spinner" style="margin-top:85px">
|
||||
<div class="rect1"></div>
|
||||
<div class="rect2"></div>
|
||||
<div class="rect3"></div>
|
||||
<div class="rect4"></div>
|
||||
<div class="rect5"></div>
|
||||
</div>
|
||||
<ion-spinner class="spinner-stable" icon="lines"></ion-spinner>
|
||||
</div>
|
||||
</div>
|
||||
<div class="secret">
|
||||
|
|
|
|||
|
|
@ -6,30 +6,18 @@
|
|||
|
||||
|
||||
|
||||
<div class="content p20b" ng-controller="createController as create" ng-init="create.setTotalCopayers(1)">
|
||||
<ion-content overflow-scroll="true" class="content p20b" ng-controller="createController as create" ng-init="create.setTotalCopayers(1)">
|
||||
|
||||
<div class="onGoingProcess" ng-show="create.loading && !create.hwWallet">
|
||||
<div class="onGoingProcess-content" ng-style="{'background-color':'#222'}">
|
||||
<div class="spinner">
|
||||
<div class="rect1"></div>
|
||||
<div class="rect2"></div>
|
||||
<div class="rect3"></div>
|
||||
<div class="rect4"></div>
|
||||
<div class="rect5"></div>
|
||||
</div>
|
||||
<ion-spinner class="spinner-stable" icon="lines"></ion-spinner>
|
||||
<span translate>Creating Wallet...</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="onGoingProcess" ng-show="create.hwWallet">
|
||||
<div class="onGoingProcess-content" ng-style="{'background-color':'#222'}" style="max-height:6.5em" >
|
||||
<div class="spinner">
|
||||
<div class="rect1"></div>
|
||||
<div class="rect2"></div>
|
||||
<div class="rect3"></div>
|
||||
<div class="rect4"></div>
|
||||
<div class="rect5"></div>
|
||||
</div>
|
||||
<ion-spinner class="spinner-stable" icon="lines"></ion-spinner>
|
||||
<span translate>Connecting to {{create.hwWallet}} Wallet...</span>
|
||||
<span ng-show="create.hwWallet=='Trezor'" translate>Please accept to export two public keys from the Trezor app</span>
|
||||
</div>
|
||||
|
|
@ -164,10 +152,11 @@
|
|||
<input type="text" class="form-control" name="derivationPath" ng-model="derivationPath">
|
||||
</label>
|
||||
</div>
|
||||
<div class="oh" ng-show="create.seedSourceId == 'new'">
|
||||
<label for="network-name" >
|
||||
<div class="oh" ng-show="create.seedSourceId == 'new'">
|
||||
<label for="network-name" class="dbi">
|
||||
<span>Testnet</span>
|
||||
<switch id="network-name" name="isTestnet" ng-model="isTestnet" class="green right m5t m10b"></switch>
|
||||
<ion-toggle ng-model="testnetEnabled" toggle-class="toggle-balanced" class="bct">
|
||||
</ion-toggle>
|
||||
</label>
|
||||
</div>
|
||||
</div> <!-- columns -->
|
||||
|
|
@ -184,5 +173,5 @@
|
|||
</div> <!-- large-12 columns -->
|
||||
</div> <!-- row -->
|
||||
</form>
|
||||
</div>
|
||||
</ion-content>
|
||||
<div class="extra-margin-bottom"></div>
|
||||
|
|
|
|||
|
|
@ -25,20 +25,14 @@
|
|||
</div>
|
||||
<div class="row">
|
||||
|
||||
<div class="onGoingProcess" ng-show="creatingProfile">
|
||||
<div class="onGoingProcess" ng-show="disclaimer.creatingProfile">
|
||||
<div class="onGoingProcess-content" ng-style="{'background-color':'#222'}">
|
||||
<div class="spinner">
|
||||
<div class="rect1"></div>
|
||||
<div class="rect2"></div>
|
||||
<div class="rect3"></div>
|
||||
<div class="rect4"></div>
|
||||
<div class="rect5"></div>
|
||||
</div>
|
||||
<ion-spinner class="spinner-stable" icon="lines"></ion-spinner>
|
||||
<span translate>Creating Wallet...</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="start-button columns button-box">
|
||||
<button ng-disabled="creatingProfile" ng-click="disclaimer.accept()" class="button black expand round size-12 text-spacing m0" translate>
|
||||
<button ng-disabled="disclaimer.creatingProfile" ng-click="disclaimer.accept()" class="button black expand round size-12 text-spacing m0" translate>
|
||||
I AGREE. GET STARTED
|
||||
</button>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -4,8 +4,6 @@
|
|||
ng-init="titleSection='Export'; goBackToState = 'preferencesAdvanced'">
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="content preferences" ng-controller="exportController as exportC">
|
||||
<h4></h4>
|
||||
<div ng-show="!exportC.backupWalletPlainText">
|
||||
|
|
@ -19,7 +17,6 @@
|
|||
<div class="text-warning size-14 m20b" ng-show="exportC.isEncrypted">
|
||||
<i class="fi-alert size-12"></i>
|
||||
<span translate> A spending password is set for this wallet. Exporting keeps the spending password in the export archive.</span>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -29,16 +26,12 @@
|
|||
<div class="columns">
|
||||
<label for="password" translate>Set up a password </label>
|
||||
<div class="input">
|
||||
<input type="password" class="form-control"
|
||||
placeholder="{{'Your password'|translate}}"
|
||||
name="password" ng-model="exportC.password">
|
||||
<input type="password" class="form-control" placeholder="{{'Your password'|translate}}" name="password" ng-model="exportC.password">
|
||||
</div>
|
||||
|
||||
<label for="password" translate>Repeat the password</label>
|
||||
<div class="input">
|
||||
<input type="password" class="form-control"
|
||||
placeholder="{{'Repeat password'|translate}}"
|
||||
name="password" ng-model="exportC.repeatpassword">
|
||||
<input type="password" class="form-control" placeholder="{{'Repeat password'|translate}}" name="password" ng-model="exportC.repeatpassword">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -46,20 +39,13 @@
|
|||
|
||||
<h4></h4>
|
||||
|
||||
<ul class="no-bullet m0">
|
||||
<li>
|
||||
<switch id="no-metaData" name="metaData" ng-model="metaData" class="green right"></switch>
|
||||
<span translate>Include address book and history cache</span>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<ul class="no-bullet m0" ng-show="index.canSign">
|
||||
<li>
|
||||
<switch id="no-sign" name="noSign" ng-model="noSign" class="green right"></switch>
|
||||
<span translate>Do not include private key</span>
|
||||
</li>
|
||||
</ul>
|
||||
<ion-toggle ng-model="metaDataEnabled" toggle-class="toggle-balanced" class="r0">
|
||||
<span class="toggle-label" translate>Include address book and history cache</span>
|
||||
</ion-toggle>
|
||||
|
||||
<ion-toggle ng-model="noSignEnabled" toggle-class="toggle-balanced" class="r0">
|
||||
<span class="toggle-label" translate>Do not include private key</span>
|
||||
</ion-toggle>
|
||||
|
||||
<div class="box-notification" ng-show="!index.canSign">
|
||||
<span class="text-warning size-14">
|
||||
|
|
@ -80,7 +66,6 @@
|
|||
</span>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="row">
|
||||
<div class="columns">
|
||||
<button class="black round expand m20t" ng-click="exportC.downloadWalletBackup()"
|
||||
|
|
@ -106,7 +91,7 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<div class="row" ng-show="exportC.backupWalletPlainText">
|
||||
|
|
|
|||
|
|
@ -10,13 +10,7 @@
|
|||
|
||||
<div class="onGoingProcess" ng-show="glidera.loading || index.glideraLoading">
|
||||
<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>
|
||||
<ion-spinner class="spinner-stable" icon="lines"></ion-spinner>
|
||||
<span ng-show="glidera.loading">Connecting to Glidera...</span>
|
||||
<span ng-show="index.glideraLoading">{{index.glideraLoading}}</span>
|
||||
</div>
|
||||
|
|
@ -146,9 +140,9 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<ul class="no-bullet m0 size-14"
|
||||
<ul class="no-bullet m0 size-14"
|
||||
ng-show="index.glideraStatus && index.glideraStatus.userCanTransact">
|
||||
<li ng-show="index.glideraStatus.userCanBuy"
|
||||
<li ng-show="index.glideraStatus.userCanBuy"
|
||||
class="line-b line-t p20 pointer"
|
||||
ng-click="$root.go('buyGlidera')">
|
||||
<img src="img/buy-bitcoin.svg" alt="buy bitcoin" width="40">
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<div
|
||||
class="topbar-container"
|
||||
<div
|
||||
class="topbar-container"
|
||||
ng-include="'views/includes/topbar.html'"
|
||||
ng-init="titleSection='Glidera'; closeToHome = true">
|
||||
</div>
|
||||
|
|
@ -8,13 +8,7 @@
|
|||
|
||||
<div class="onGoingProcess" ng-show="glidera.loading">
|
||||
<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>
|
||||
<ion-spinner class="spinner-stable" icon="lines"></ion-spinner>
|
||||
<span ng-show="glidera.loading">Connecting to Glidera...</span>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -22,10 +16,10 @@
|
|||
<div class="row m20t">
|
||||
<div class="large-12 columns">
|
||||
<div class="text-center">
|
||||
<img src="img/glidera-logo.png"
|
||||
<img src="img/glidera-logo.png"
|
||||
ng-click="index.updateGlidera()" width="100">
|
||||
</div>
|
||||
|
||||
|
||||
<div class="m10t text-center" ng-show="glidera.error">
|
||||
<div class="notification m10b size-12 text-warning">{{glidera.error}}</div>
|
||||
<button class="outline dark-gray tiny round" ng-click="glidera.submitOauthCode(glidera.code)">Try again</button>
|
||||
|
|
|
|||
|
|
@ -4,28 +4,16 @@
|
|||
ng-init="titleSection='Import wallet'; goBackToState = 'add'; noColor = true">
|
||||
</div>
|
||||
|
||||
<div class="content p20b" ng-controller="importController as import" ng-init="type='12'">
|
||||
<ion-content overflow-scroll="true" class="content p20b" ng-controller="importController as import" ng-init="type='12'">
|
||||
<div class="onGoingProcess" ng-show="import.loading && !import.hwWallet">
|
||||
<div class="onGoingProcess-content" ng-style="{'background-color':'#222'}">
|
||||
<div class="spinner">
|
||||
<div class="rect1"></div>
|
||||
<div class="rect2"></div>
|
||||
<div class="rect3"></div>
|
||||
<div class="rect4"></div>
|
||||
<div class="rect5"></div>
|
||||
</div>
|
||||
<ion-spinner class="spinner-stable" icon="lines"></ion-spinner>
|
||||
<span translate>Importing wallet...</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="onGoingProcess" ng-show="import.hwWallet">
|
||||
<div class="onGoingProcess-content" ng-style="{'background-color':'#222'}">
|
||||
<div class="spinner">
|
||||
<div class="rect1"></div>
|
||||
<div class="rect2"></div>
|
||||
<div class="rect3"></div>
|
||||
<div class="rect4"></div>
|
||||
<div class="rect5"></div>
|
||||
</div>
|
||||
<ion-spinner class="spinner-stable" icon="lines"></ion-spinner>
|
||||
<span translate>Connecting to {{import.hwWallet}} Wallet...</span>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -51,7 +39,7 @@
|
|||
</span>
|
||||
</div>
|
||||
<div class="row large-12 columns">
|
||||
<form name="importForm12" ng-submit="import.importMnemonic(importForm12)" novalidate>
|
||||
<form name="importForm12" ng-submit="import.importMnemonic(importForm12)" novalidate>
|
||||
|
||||
<div >
|
||||
<label for="words">
|
||||
|
|
@ -84,7 +72,7 @@
|
|||
<input type="text" class="form-control" name="derivationPath" ng-model="derivationPath">
|
||||
</label>
|
||||
</div>
|
||||
|
||||
|
||||
<label for="bws" class="oh">
|
||||
<span>Wallet Service URL</span>
|
||||
<input type="text" id="bwsurl" name="bwsurl" ng-model="bwsurl">
|
||||
|
|
@ -107,7 +95,7 @@
|
|||
</span>
|
||||
</div>
|
||||
<div class="row large-12 columns">
|
||||
<form name="importForm" ng-submit="import.importBlob(importForm)" novalidate>
|
||||
<form name="importForm" ng-submit="import.importBlob(importForm)" novalidate>
|
||||
|
||||
<div ng-show="!index.isSafari && !index.isCordova" class="line-b m10b">
|
||||
<label for="backupFile">
|
||||
|
|
@ -172,7 +160,7 @@
|
|||
{{import.error|translate}}
|
||||
</span>
|
||||
</div>
|
||||
<div class="row large-12 columns">
|
||||
<div class="row large-12 columns">
|
||||
|
||||
<form name="importForm3" ng-submit="import.importHW(importForm3)" novalidate>
|
||||
<div class="large-12 columns">
|
||||
|
|
@ -232,6 +220,6 @@
|
|||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ion-content>
|
||||
|
||||
<div class="extra-margin-bottom"></div>
|
||||
|
|
|
|||
|
|
@ -1,14 +1,9 @@
|
|||
<div class="modalMask">
|
||||
</div>
|
||||
|
||||
<div class="alertModal">
|
||||
<div class="columns m20t">
|
||||
<div class="m20t size-14 text-center">
|
||||
<i class="fi-alert"></i>
|
||||
{{index.showAlert.msg|translate}}
|
||||
</div>
|
||||
<div class="text-center m20t" ng-click="index.showAlert.close()">
|
||||
<a class="button outline light-gray round tiny small-4">OK</a>
|
||||
</div>
|
||||
<div class="columns m20t">
|
||||
<div class="m20t size-14 text-center">
|
||||
<i class="fi-alert"></i>
|
||||
{{msg|translate}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-center m20t" ng-click="close()">
|
||||
<a class="button outline light-gray round tiny small-4">OK</a>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,47 +1,39 @@
|
|||
<div class="modalMask"></div>
|
||||
|
||||
<div class="confirmTxModal" ng-controller="confirmTxController as confirm" ng-init="tx = index.confirmTx.txp">
|
||||
|
||||
<div class="confirmHead" ng-style="{'background-color':index.backgroundColor}">
|
||||
<h1 class="m0 text-center text-white size-18" translate>Send bitcoin</h1>
|
||||
<div class="m20t">
|
||||
<label class="size-14 text-center">
|
||||
<span translate>Send bitcoin</span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="text-center">
|
||||
<div class="size-36">{{tx.amountStr}}</div>
|
||||
<div class="size-12 label gray radius" ng-show="tx.alternativeAmountStr">{{tx.alternativeAmountStr}}</div>
|
||||
<i class="db fi-arrow-down size-24 m10v"></i>
|
||||
<div class="payment-proposal-to" ng-click="copyToClipboard(tx.toAddress)">
|
||||
<i class="fi-bitcoin left m10l"></i>
|
||||
<contact ng-if="!tx.hasMultiplesOutputs" class="dib enable_text_select ellipsis m5t m5b m15l size-14" address="{{tx.toAddress}}"></contact>
|
||||
<span ng-if="tx.hasMultiplesOutputs" translate>
|
||||
Multiple recipients
|
||||
</span>
|
||||
</div>
|
||||
<div class="p10">
|
||||
<div class="size-36">{{tx.amountStr}}</div>
|
||||
<div class="size-12 label gray radius" ng-show="tx.alternativeAmountStr">{{tx.alternativeAmountStr}}</div>
|
||||
<i class="db fi-arrow-down size-24 m10v"></i>
|
||||
<div class="payment-proposal-to" ng-click="copyToClipboard(tx.toAddress)">
|
||||
<i class="fi-bitcoin left m10l"></i>
|
||||
<contact ng-if="!tx.hasMultiplesOutputs" class="dib enable_text_select ellipsis m5t m5b m15l size-14" address="{{tx.toAddress}}"></contact>
|
||||
<span ng-if="tx.hasMultiplesOutputs" translate>
|
||||
Multiple recipients
|
||||
</span>
|
||||
<div class="m10t size-12" ng-init="processFee(tx.amount, tx.fee)">
|
||||
<div ng-show="!showPercentage" ng-click="showPercentage = true">
|
||||
<span translate>Fee</span> <span class="tl">({{feeLevel|translate}})</span>:
|
||||
<span class="text-bold">{{tx.feeStr}}</span>
|
||||
<span class="label gray radius">{{feeAlternativeStr}}</span>
|
||||
</div>
|
||||
<div class="m10t size-12" ng-init="confirm.processFee(tx.amount, tx.fee)">
|
||||
<div ng-show="!showPercentage" ng-click="showPercentage = true">
|
||||
<span translate>Fee</span> <span class="tl">({{confirm.feeLevel|translate}})</span>:
|
||||
<span class="text-bold">{{tx.feeStr}}</span>
|
||||
<span class="label gray radius">{{confirm.feeAlternativeStr}}</span>
|
||||
</div>
|
||||
<div ng-show="showPercentage" ng-click="showPercentage = false" translate>
|
||||
{{confirm.feeRateStr}} of the transaction
|
||||
</div>
|
||||
<div ng-show="showPercentage" ng-click="showPercentage = false" translate>
|
||||
{{feeRateStr}} of the transaction
|
||||
</div>
|
||||
<div class="row m20t">
|
||||
<div class="large-6 medium-6 small-6 columns">
|
||||
<button
|
||||
ng-click="confirm.close(index.confirmTx.callback)"
|
||||
class="small m10b round outline dark-gray expand" translate>
|
||||
Cancel
|
||||
</button>
|
||||
</div>
|
||||
<div class="large-6 medium-6 small-6 columns">
|
||||
<button
|
||||
ng-click="confirm.accept(index.confirmTx.callback)"
|
||||
class="small m10b round expand"
|
||||
ng-style="{'background-color':index.backgroundColor}" translate>
|
||||
Confirm
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row m20t dib">
|
||||
<div class="half-row left">
|
||||
<button ng-click="cancel()" class="round outline dark-gray expand">
|
||||
<span class="size-12" translate>Cancel</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="half-row left">
|
||||
<button ng-click="accept()" class="round expand" ng-style="{'background-color':index.backgroundColor}">
|
||||
<span class="size-12" translate>Confirm</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<div class="bottom-bar row collapse" ng-show="!index.notAuthorized">
|
||||
<div class="bottom-bar row collapse p0i" ng-show="!index.notAuthorized">
|
||||
<div>
|
||||
<div class="row collapse">
|
||||
<div class="row collapse p0i">
|
||||
<div class="medium-4 small-4 columns text-center bottombar-item"
|
||||
ng-repeat="item in index.menu">
|
||||
<span ng-include="'views/includes/menu-item.html'"></span>
|
||||
|
|
|
|||
13
public/views/includes/note.html
Normal file
13
public/views/includes/note.html
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
<div class="columns m20t">
|
||||
<label class="size-14 text-center">
|
||||
<span ng-show="!comment" translate>Enter a new comment</span>
|
||||
<span ng-show="comment" translate>Edit comment</span>
|
||||
</label>
|
||||
<input type="text" ng-model="data.comment" autofocus>
|
||||
</div>
|
||||
<div class="small-6 columns">
|
||||
<button class="round outline dark-gray expand" ng-click="commentPopupClose()" translate>CANCEL</button>
|
||||
</div>
|
||||
<div class="small-6 columns">
|
||||
<button ng-style="{'background-color': index.backgroundColor}" class="round outline expand" ng-click="commentPopupSave()" translate>SAVE</button>
|
||||
</div>
|
||||
|
|
@ -1,49 +1,42 @@
|
|||
|
||||
<div class="modalMask">
|
||||
</div>
|
||||
|
||||
<div ng-controller="passwordController as pass" class="passModal"
|
||||
ng-class="{'animated bounceInDown':index.askPassword}"
|
||||
>
|
||||
<div class="columns m20t">
|
||||
<label class="size-14 text-center" for="password" ng-if="index.askPassword.isSetup">
|
||||
<span ng-show="!pass.isVerification" translate>Set up a spending password</span>
|
||||
<span ng-show="pass.isVerification" translate>Repeat the spending password</span>
|
||||
<label class="size-14 text-center" for="password" ng-if="isSetup">
|
||||
<span ng-show="!isVerification" translate>Set up a spending password</span>
|
||||
<span ng-show="isVerification" translate>Repeat the spending password</span>
|
||||
</label>
|
||||
<label class="size-14 text-center" for="password" ng-if="!index.askPassword.isSetup">
|
||||
<label class="size-14 text-center" for="password" ng-if="!isSetup">
|
||||
<span translate>Enter your spending password</span>
|
||||
</label>
|
||||
|
||||
<div class="input m20t">
|
||||
<input type="password" placeholder="{{'Your spending password'|translate}}"
|
||||
id="passwordInput" name="password" ng-model="password">
|
||||
id="passwordInput" name="password" ng-model="data.password" autofocus>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="small-6 columns">
|
||||
<button
|
||||
class="round small-6 columns outline dark-gray expand"
|
||||
ng-click="pass.close(index.askPassword.callback)"
|
||||
ng-disabled="pass.loading" translate>
|
||||
CANCEL
|
||||
ng-click="cancel()"
|
||||
ng-disabled="loading">
|
||||
<span class="size-12" translate>CANCEL</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="small-6 columns">
|
||||
<button class="round expand"
|
||||
ng-click="pass.set(index.askPassword.isSetup, index.askPassword.callback)"
|
||||
ng-disabled="!password || pass.loading"
|
||||
ng-click="set()"
|
||||
ng-disabled="!data.password || loading"
|
||||
ng-style="{'background-color':index.backgroundColor}">
|
||||
<span ng-if="index.askPassword.isSetup" translate>SET</span>
|
||||
<span ng-if="!index.askPassword.isSetup">OK</span>
|
||||
<span ng-if="isSetup" class="size-12" translate>SET</span>
|
||||
<span ng-if="!isSetup" class="size-12">OK</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p class="text-warning size-12 columns m20t text-center" ng-show="index.askPassword.isSetup">
|
||||
<p class="text-warning size-12 columns m20t text-center" ng-show="isSetup">
|
||||
<i class="fi-alert"></i>
|
||||
<span translate ng-show="!pass.error"> Your wallet key will be encrypted. The Spending Password cannot be recovered. Be sure to write it down</span>
|
||||
<span ng-show="!error" translate> Your wallet key will be encrypted. The Spending Password cannot be recovered. Be sure to write it down</span>
|
||||
|
||||
<span ng-show="pass.error">{{pass.error|translate}}</span>
|
||||
<span ng-show="error">{{error|translate}}</span>
|
||||
</p>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,51 +1,53 @@
|
|||
<nav class="sidebar left-off-canvas-menu" ng-controller="sidebarController as sidebar">
|
||||
<nav class="sidebar" ng-controller="sidebarController as sidebar">
|
||||
<header>
|
||||
<logo ng-if="!sidebar.isWindowsPhoneApp" negative="true" width="80"></logo>
|
||||
<img ng-if="sidebar.isWindowsPhoneApp" src="img/logo-negative.png" alt="Copay" width="80">
|
||||
<div ng-include="'views/includes/version.html'"></div>
|
||||
</header>
|
||||
<ul class="off-canvas-list">
|
||||
<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">
|
||||
<a ng-click="sidebar.closeMenu(); sidebar.switchWallet(item.id, index.walletId)" class="oh">
|
||||
<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>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a ng-click="sidebar.closeMenu(); $root.go('add')" class="oh">
|
||||
<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>
|
||||
<ion-content overflow-scroll="true">
|
||||
<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">
|
||||
<a menu-toggle ng-click="$root.go('walletHome'); sidebar.switchWallet(item.id, index.walletId)" class="oh">
|
||||
<div class="avatar-wallet"
|
||||
ng-style="{'background-color':item.color}">
|
||||
<i class="icon-wallet size-21"></i>
|
||||
</div>
|
||||
<div translate>Create, join or import</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>
|
||||
</a>
|
||||
</li>
|
||||
<li ng-show="!index.isWindowsPhoneApp && index.isComplete && (index.glideraEnabled || index.coinbaseEnabled)">
|
||||
<a ng-click="sidebar.closeMenu(); $root.go('buyandsell')" class="oh">
|
||||
<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 & Sell</span>
|
||||
</div>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a ng-click="sidebar.closeMenu(); $root.go('preferencesGlobal')" class="oh">
|
||||
<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>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<a menu-toggle ng-click="$root.go('add')" class="oh">
|
||||
<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>
|
||||
</a>
|
||||
</li>
|
||||
<li ng-show="!index.isWindowsPhoneApp && index.isComplete && (index.glideraEnabled || index.coinbaseEnabled)">
|
||||
<a menu-toggle ng-click="$root.go('buyandsell')" class="oh">
|
||||
<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 & Sell</span>
|
||||
</div>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a menu-toggle ng-click="$root.go('preferencesGlobal')" class="oh">
|
||||
<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>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</ion-content>
|
||||
</nav>
|
||||
|
|
|
|||
|
|
@ -1,9 +1,8 @@
|
|||
<nav ng-controller="topbarController as topbar"
|
||||
class="tab-bar"
|
||||
ng-style="{'background-color': noColor ? '#4B6178' : index.backgroundColor}">
|
||||
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"
|
||||
ng-click="index.openMenu()"><i class="fi-list size-24"></i>
|
||||
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>
|
||||
|
|
|
|||
|
|
@ -1,159 +0,0 @@
|
|||
<div
|
||||
ng-controller="txController as txc"
|
||||
class="txModal"
|
||||
ng-swipe-disable-mouse
|
||||
ng-swipe-right="txc.cancel()">
|
||||
|
||||
<nav class="tab-bar" ng-style="{'background-color':txc.color}">
|
||||
<section class="left-small">
|
||||
<a ng-click="txc.cancel()">
|
||||
<i class="icon-arrow-left3 icon-back"></i>
|
||||
<span class="text-back" translate>Back</span>
|
||||
</a>
|
||||
</section>
|
||||
<section class="middle tab-bar-section">
|
||||
<h1 class="title ellipsis" translate>
|
||||
Transaction
|
||||
</h1>
|
||||
</section>
|
||||
</nav>
|
||||
|
||||
<div class="txModal-content">
|
||||
<div class="header-modal text-center" ng-init="txc.getAlternativeAmount(index.showTx)">
|
||||
<div ng-show="index.showTx.action != 'invalid'">
|
||||
<div ng-show="index.showTx.action == 'received'">
|
||||
<img src="img/icon-receive-history.svg" alt="sync" width="50">
|
||||
<p class="m0 text-gray size-14" translate>Received</p>
|
||||
</div>
|
||||
<div ng-show="index.showTx.action == 'sent'">
|
||||
<img src="img/icon-sent-history.svg" alt="sync" width="50">
|
||||
<p class="m0 text-gray size-14" translate>Sent</p>
|
||||
</div>
|
||||
<div ng-show="index.showTx.action == 'moved'">
|
||||
<img src="img/icon-moved.svg" alt="sync" width="50">
|
||||
<p class="m0 text-gray size-14" translate>Moved</p>
|
||||
</div>
|
||||
|
||||
<div class="size-36" ng-click="txc.copyToClipboard(index.showTx.amountStr)">
|
||||
<span ng-if="index.showTx.action == 'received'">+</span>
|
||||
<span ng-if="index.showTx.action == 'sent'">-</span>
|
||||
<span class="enable_text_select">{{index.showTx.amountStr}}</span>
|
||||
</div>
|
||||
<div class="alternative-amount" ng-click="showRate=!showRate" ng-init="showRate = false">
|
||||
<span class="label gray radius" ng-show="!showRate && alternativeAmountStr">
|
||||
{{alternativeAmountStr}}
|
||||
</span>
|
||||
<span class="size-12" ng-show="showRate && alternativeAmountStr">
|
||||
{{rateStr}} ({{rateDate | amDateFormat:'MM/DD/YYYY HH:mm a'}})
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div ng-show="index.showTx.action == 'invalid'">
|
||||
-
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h4 class="title m0" translate>Details</h4>
|
||||
|
||||
<ul class="no-bullet size-14 m0">
|
||||
|
||||
<li ng-if="!index.showTx.hasMultiplesOutputs && index.showTx.addressTo && index.showTx.addressTo != 'N/A'" class="line-b p10 oh"
|
||||
ng-click="txc.copyToClipboard(index.showTx.addressTo)">
|
||||
<span class="text-gray" translate>To</span>
|
||||
<span class="right">
|
||||
<span ng-if="index.showTx.merchant">
|
||||
<span ng-show="index.showTx.merchant.pr.ca"><i class="fi-lock color-greeni"></i> {{index.showTx.merchant.domain}}</span>
|
||||
<span ng-show="!index.showTx.merchant.pr.ca"><i class="fi-unlock color-yellowi"></i> {{index.showTx.merchant.domain}}</span>
|
||||
</span>
|
||||
<span ng-if="!index.showTx.merchant">
|
||||
<span ng-show="index.showTx.labelTo">{{index.showTx.labelTo}}</span>
|
||||
<contact ng-show="!index.showTx.labelTo" class="enable_text_select" address="{{index.showTx.addressTo}}"></contact>
|
||||
</span>
|
||||
</span>
|
||||
</li>
|
||||
|
||||
<li ng-show="index.showTx.hasMultiplesOutputs" class="line-b p10 oh"
|
||||
ng-click="showMultiplesOutputs = !showMultiplesOutputs">
|
||||
<span class="text-gray" translate>Recipients</span>
|
||||
<span class="right">{{index.showTx.recipientCount}}
|
||||
<i ng-show="showMultiplesOutputs" class="icon-arrow-up3 size-24"></i>
|
||||
<i ng-show="!showMultiplesOutputs" class="icon-arrow-down3 size-24"></i>
|
||||
</span>
|
||||
</li>
|
||||
|
||||
<div class="line-b" ng-show="index.showTx.hasMultiplesOutputs && showMultiplesOutputs"
|
||||
ng-repeat="output in index.showTx.outputs"
|
||||
ng-include="'views/includes/output.html'">
|
||||
</div>
|
||||
|
||||
<li ng-if="index.showTx.action == 'invalid'" class="line-b p10 oh">
|
||||
<span class="right" translate>
|
||||
This transaction has become invalid; possibly due to a double spend attempt.
|
||||
</span>
|
||||
<li ng-if="index.showTx.time" class="line-b p10 oh">
|
||||
<span class="text-gray" translate>Date</span>
|
||||
<span class="right enable_text_select">
|
||||
<time>{{ index.showTx.time * 1000 | amDateFormat:'MM/DD/YYYY HH:mm a'}}</time>
|
||||
<time>({{ index.showTx.time * 1000 | amTimeAgo}})</time>
|
||||
</span>
|
||||
</li>
|
||||
<li class="line-b p10" ng-show="index.showTx.action != 'received'"
|
||||
ng-click="txc.copyToClipboard(index.showTx.feeStr)">
|
||||
<span class="text-gray" translate>Fee</span>
|
||||
<span class="right enable_text_select">{{index.showTx.feeStr}}</span>
|
||||
</li>
|
||||
<li class="line-b p10 oh" ng-if="index.showTx.message && index.showTx.action != 'received'"
|
||||
ng-click="txc.copyToClipboard(index.showTx.message)">
|
||||
<span class="text-gray" translate>Note</span>
|
||||
<span class="right enable_text_select">{{index.showTx.message}}</span>
|
||||
</li>
|
||||
<li ng-if="index.showTx.merchant" class="line-b p10 oh"
|
||||
ng-click="txc.copyToClipboard(index.showTx.merchant.pr.pd.memo)">
|
||||
<span class="text-gray" translate>Merchant message</span>
|
||||
<span class="right enable_text_select">
|
||||
{{index.showTx.merchant.pr.pd.memo}}
|
||||
</span>
|
||||
</li>
|
||||
<li ng-if="index.showTx.time" class="line-b p10 oh">
|
||||
<span class="text-gray" translate>Confirmations</span>
|
||||
<span class="right" >
|
||||
<span class="text-warning" ng-show="!index.showTx.confirmations || index.showTx.confirmations == 0" translate>
|
||||
Unconfirmed
|
||||
</span>
|
||||
<span class="label gray radius" ng-show="index.showTx.confirmations>0 && !index.showTx.safeConfirmed">
|
||||
{{index.showTx.confirmations}}
|
||||
</span>
|
||||
<span class="label gray radius" ng-show="index.showTx.safeConfirmed">
|
||||
{{index.showTx.safeConfirmed}}
|
||||
</span>
|
||||
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<div ng-if="index.showTx.actions[0] && txc.isShared">
|
||||
<h4 class="title m0" translate>Participants</h4>
|
||||
<ul class="no-bullet size-14 m0">
|
||||
<li class="line-b p10 text-gray" ng-repeat="c in index.showTx.actions">
|
||||
<i class="icon-contact size-24"></i>
|
||||
<span class="right">
|
||||
<i ng-if="c.type == 'reject'" class="fi-x icon-sign x db"></i>
|
||||
<i ng-if="c.type == 'accept'" class="fi-check icon-sign check db"></i>
|
||||
</span>
|
||||
{{c.copayerName}} <span ng-if="c.copayerId == txc.copayerId">({{'Me'|translate}})</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div ng-show="index.showTx.txid" class="tx-details-blockchain">
|
||||
<div class="text-center m20t">
|
||||
<button class="button outline round dark-gray tiny" ng-click="$root.openExternalLink('https://' +
|
||||
(txc.getShortNetworkName() == 'test' ? 'test-' : '') + 'insight.bitpay.com/tx/' + index.showTx.txid)">
|
||||
<span class="text-gray" translate>See it on the blockchain</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="extra-margin-bottom"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -17,4 +17,3 @@ src="img/icon-ledger-white.svg">
|
|||
|
||||
<img style="height:0.6em" class="animated flash infinite" ng-show="index.loadingWallet ||
|
||||
index.updatingTxHistory" src="img/icon-sync-white.svg">
|
||||
|
||||
|
|
|
|||
|
|
@ -5,42 +5,30 @@
|
|||
</div>
|
||||
|
||||
|
||||
<div class="content p20v" ng-controller="joinController as join">
|
||||
<ion-content overflow-scroll="true" class="content p20v" ng-controller="joinController as join">
|
||||
<div class="onGoingProcess" ng-show="join.loading && !join.hwWallet">
|
||||
<div class="onGoingProcess-content" ng-style="{'background-color':'#222'}">
|
||||
<div class="spinner">
|
||||
<div class="rect1"></div>
|
||||
<div class="rect2"></div>
|
||||
<div class="rect3"></div>
|
||||
<div class="rect4"></div>
|
||||
<div class="rect5"></div>
|
||||
</div>
|
||||
<ion-spinner class="spinner-stable" icon="lines"></ion-spinner>
|
||||
<span translate>Joining Wallet...</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="onGoingProcess" ng-show="join.hwWallet">
|
||||
<div class="onGoingProcess-content" ng-style="{'background-color':'#222'}" style="max-height:6.5em">
|
||||
<div class="spinner">
|
||||
<div class="rect1"></div>
|
||||
<div class="rect2"></div>
|
||||
<div class="rect3"></div>
|
||||
<div class="rect4"></div>
|
||||
<div class="rect5"></div>
|
||||
</div>
|
||||
<ion-spinner class="spinner-stable" icon="lines"></ion-spinner>
|
||||
<span translate>Connecting to {{join.hwWallet}} Wallet...</span>
|
||||
<span ng-show="join.hwWallet=='Trezor'" translate>Please accept to export two public keys from the Trezor app</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<form name="joinForm" ng-submit="join.join(joinForm)" novalidate>
|
||||
<div class="box-notification m20b" ng-show="join.error ">
|
||||
<div class="box-notification m20b" ng-show="join.error">
|
||||
<span class="text-warning">
|
||||
{{join.error|translate}}
|
||||
</span>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="large-12 columns">
|
||||
<div class="large-12 columns">
|
||||
|
||||
<div>
|
||||
<label><span translate>Your nickname</span>
|
||||
|
|
@ -153,6 +141,6 @@
|
|||
</div> <!-- large-12 columns -->
|
||||
</div> <!-- row -->
|
||||
</form>
|
||||
</div>
|
||||
</ion-content>
|
||||
|
||||
<div class="extra-margin-bottom"></div>
|
||||
|
|
|
|||
135
public/views/modals/addressbook.html
Normal file
135
public/views/modals/addressbook.html
Normal file
|
|
@ -0,0 +1,135 @@
|
|||
<ion-modal-view ng-controller="addressbookController as addressbookC">
|
||||
<div ng-init="wallets[0] ? selectedWalletsOpt = true : selectedWalletsOpt = false; checkClipboard()">
|
||||
<ion-content ng-style="{'background-color': '#f6f7f9'}">
|
||||
<nav class="tab-bar" ng-style="{'background-color':color}">
|
||||
<section class="left-small">
|
||||
<a ng-show="!editAddressbook && !addAddressbookEntry" ng-click="cancel()" class="p10">
|
||||
<span class="text-close" translate>Close</span>
|
||||
</a>
|
||||
</section>
|
||||
|
||||
<section class="middle tab-bar-section">
|
||||
<h1 class="title ellipsis">
|
||||
{{walletName}}
|
||||
</h1>
|
||||
</section>
|
||||
|
||||
<section class="right-small" ng-show="!selectedWalletsOpt" ng-click="toggleEditAddressbook()">
|
||||
<a ng-show="!editAddressbook && !addAddressbookEntry" href class="p10">
|
||||
<span class="text-close" translate>Edit</span>
|
||||
</a>
|
||||
<a ng-show="editAddressbook && !addAddressbookEntry" href class="p10">
|
||||
<span class="text-close" translate>Done</span>
|
||||
</a>
|
||||
</section>
|
||||
</nav>
|
||||
|
||||
<div class="modal-content p20b">
|
||||
<div class="create-tab small-only-text-center" ng-show="!editAddressbook && !addAddressbookEntry">
|
||||
<div class="row">
|
||||
<div class="tab-container small-6 medium-3 large-2"
|
||||
ng-class="{'selected':selectedWalletsOpt}"
|
||||
ng-style="{'border-color':selectedWalletsOpt ? color : 'inherit'}"
|
||||
ng-click="selectedWalletsOpt = true">
|
||||
<a href ng-style="{'color':selectedWalletsOpt ? color : 'inherit'}" translate> My wallets</a>
|
||||
</div>
|
||||
<div class="tab-container small-6 medium-3 large-2"
|
||||
ng-class="{'selected':!selectedWalletsOpt}"
|
||||
ng-style="{'border-color':!selectedWalletsOpt ? color : 'inherit'}"
|
||||
ng-click="selectedWalletsOpt = false">
|
||||
<a href ng-style="{'color':!selectedWalletsOpt ? color : 'inherit'}" translate>My contacts</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div ng-show="selectedWalletsOpt">
|
||||
<div class="onGoingProcess" ng-if="gettingAddress">
|
||||
<div class="onGoingProcess-content" ng-style="{'background-color':'#222'}">
|
||||
<ion-spinner class="spinner-stable" icon="lines"></ion-spinner>
|
||||
<span translate> Getting address for wallet {{selectedWalletName}} ...</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div ng-if="!gettingAddress">
|
||||
<ul class="no-bullet">
|
||||
<li class="line-b" ng-repeat="w in wallets">
|
||||
<a ng-click="selectWallet(w.id, w.name)" class="db oh">
|
||||
<div class="avatar-wallet"
|
||||
ng-style="{'background-color':w.color}">
|
||||
<i class="icon-wallet size-21"></i>
|
||||
</div>
|
||||
<div class="ellipsis name-wallet text-bold">{{w.name || w.id}}
|
||||
<span class="has-error right text-light size-12" ng-show="errorSelectedWallet[w.id]">
|
||||
<i class="icon-close-circle size-14"></i>
|
||||
<span class="vm">{{errorSelectedWallet[w.id] }}</span>
|
||||
</span>
|
||||
</div>
|
||||
<div class="size-12">{{w.m}} of {{w.n}}
|
||||
<span ng-show="w.network=='testnet'">[Testnet]</span>
|
||||
</div>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div ng-show="!selectedWalletsOpt" class="m20b">
|
||||
<ul ng-show="!addAddressbookEntry" class="no-bullet m0" ng-init="list()">
|
||||
<li class="p10 line-b" ng-repeat="(addr, label) in list">
|
||||
<a ng-show="selectedAddressbook[addr]" class="removeAddressbook" ng-click="remove(addr)" translate>Remove</a>
|
||||
<a ng-show="editAddressbook" class="selectAddressbook" ng-click="toggleSelectAddressbook(addr)">
|
||||
<i class="fi-trash"></i></a>
|
||||
<div ng-click="selectAddressbook(addr)">
|
||||
<i class="icon-contact left size-42 m10r text-gray"></i>
|
||||
<div class="">
|
||||
<span>{{label}}</span>
|
||||
<div class="size-12 text-gray ellipsis">{{addr}}</div>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
<li class="p10" ng-show="!editAddressbook">
|
||||
<a ng-click="toggleAddAddressbookEntry()" class="p0i">
|
||||
<i class="fi-plus size-24 m20r lh icon"></i>
|
||||
<span class="size-12 tu text-bold" translate>Add a new entry</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<div ng-show="addAddressbookEntry">
|
||||
<h4 translate>Add a new entry</h4>
|
||||
<form name="addressbookForm" class="p10" no-validate>
|
||||
<div class="text-warning size-12 m10b" ng-show="error">{{error|translate}}</div>
|
||||
<span ng-hide="addressbookForm.address.$pristine">
|
||||
<span class="has-error right size-12" ng-show="addressbookForm.address.$invalid && addressbook.address">
|
||||
<i class="icon-close-circle size-14"></i>
|
||||
<span class="vm" translate>Not valid</span>
|
||||
</span>
|
||||
<small class="right text-primary" ng-show="!addressbookForm.address.$invalid">
|
||||
<i class="icon-checkmark-circle size-14"></i>
|
||||
</small>
|
||||
</span>
|
||||
|
||||
<label translate>Address</label>
|
||||
<div class="input">
|
||||
<input type="text" id="address" name="address" ng-model="addressbook.address" valid-address required>
|
||||
<div class="qr-scanner-input">
|
||||
<qr-scanner on-scan="onQrCodeScanned(data, addressbookForm)" before-scan="beforeQrCodeScann()"></qr-scanner>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<label translate>Label</label>
|
||||
<input type="text" id="label" name="label" ng-model="addressbook.label" required>
|
||||
<div class="row">
|
||||
<div class="columns large-6 medium-6 small-6">
|
||||
<input type="button" class="button expand outline dark-gray round" ng-click="toggleAddAddressbookEntry()" value="{{'Cancel'|translate}}">
|
||||
</div>
|
||||
<div class="columns large-6 medium-6 small-6">
|
||||
<input type="submit" class="button expand round black" ng-click="add(addressbook)" value="{{'Save'|translate}}" ng-disabled="!addressbookForm.$valid">
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ion-content>
|
||||
</div>
|
||||
</ion-modal-view>
|
||||
|
|
@ -1,87 +1,91 @@
|
|||
<nav class="tab-bar" ng-style="{'background-color':color}">
|
||||
<section class="left-small">
|
||||
<a ng-click="cancel()" class="p10">
|
||||
<span class="text-close" translate>Close</span>
|
||||
</a>
|
||||
</section>
|
||||
<section class="middle tab-bar-section">
|
||||
<h1 class="title ellipsis" translate>
|
||||
Request a specific amount
|
||||
</h1>
|
||||
</section>
|
||||
</nav>
|
||||
<ion-modal-view ng-controller="customAmountController">
|
||||
<ion-content>
|
||||
<nav class="tab-bar" ng-style="{'background-color':color}">
|
||||
<section class="left-small">
|
||||
<a ng-click="cancel()" class="p10">
|
||||
<span class="text-close" translate>Close</span>
|
||||
</a>
|
||||
</section>
|
||||
|
||||
<div class="modal-content fix-modals-touch">
|
||||
<div class="m20b" ng-show="customizedAmountBtc">
|
||||
<h4 class="title m0" translate>QR Code</h4>
|
||||
<ul class="no-bullet size-14 m0">
|
||||
<li class="line-b p10 oh text-center">
|
||||
<qrcode size="220" data="bitcoin:{{addr + '?amount=' + customizedAmountBtc}}"></qrcode>
|
||||
<div class="m10t text-center" ng-show="isCordova">
|
||||
<span class="button outline dark-gray tiny round"
|
||||
ng-click="shareAddress('bitcoin:' + addr + '?amount=' + customizedAmountBtc)">
|
||||
<i class="fi-share"></i>
|
||||
<span translate>Share address</span>
|
||||
</span>
|
||||
<section class="middle tab-bar-section">
|
||||
<h1 class="title ellipsis" translate>Request a specific amount</h1>
|
||||
</section>
|
||||
</nav>
|
||||
|
||||
<div class="modal-content fix-modals-touch">
|
||||
<div class="m20b" ng-show="customizedAmountBtc">
|
||||
<h4 class="title m0" translate>QR Code</h4>
|
||||
<ul class="no-bullet size-14 m0">
|
||||
<li class="line-b p10 oh text-center">
|
||||
<qrcode size="220" data="bitcoin:{{addr + '?amount=' + customizedAmountBtc}}"></qrcode>
|
||||
<div class="m10t text-center" ng-show="isCordova">
|
||||
<span class="button outline dark-gray tiny round"
|
||||
ng-click="shareAddress('bitcoin:' + addr + '?amount=' + customizedAmountBtc)">
|
||||
<i class="fi-share"></i>
|
||||
<span translate>Share address</span>
|
||||
</span>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h4 class="title m0" translate>Details</h4>
|
||||
<ul class="no-bullet size-14 m0">
|
||||
<li class="line-b p10 oh">
|
||||
<span class="text-gray" translate>Address</span>:
|
||||
<span class="right">
|
||||
<span class="text-gray enable_text_select">{{addr}}</span>
|
||||
</span>
|
||||
</li>
|
||||
<li class="line-b p10 oh">
|
||||
<span class="text-gray" translate>Amount</span>:
|
||||
<span class="right">
|
||||
{{customizedAmountUnit}}
|
||||
<span class="label gray radius">{{customizedAlternativeUnit}}</span>
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
<h4 class="title m0" translate>Details</h4>
|
||||
<ul class="no-bullet size-14 m0">
|
||||
<li class="line-b p10 oh">
|
||||
<span class="text-gray" translate>Address</span>:
|
||||
<span class="right">
|
||||
<span class="text-gray enable_text_select">{{addr}}</span>
|
||||
</span>
|
||||
</li>
|
||||
<li class="line-b p10 oh">
|
||||
<span class="text-gray" translate>Amount</span>:
|
||||
<span class="right">
|
||||
{{customizedAmountUnit}}
|
||||
<span class="label gray radius">{{customizedAlternativeUnit}}</span>
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div ng-show="!customizedAmountBtc" class="row m20t">
|
||||
<div class="large-12 large-centered columns">
|
||||
<form name="amountForm" ng-submit="submitForm(amountForm)" novalidate>
|
||||
<div class="right" ng-hide="amountForm.amount.$pristine && !amountForm.amount.$modelValue ">
|
||||
<span class="has-error right size-12" ng-if="amountForm.amount.$invalid">
|
||||
<i class="icon-close-circle size-14"></i>
|
||||
<span clas="vm" translate>Not valid</span>
|
||||
</span>
|
||||
<small class="text-primary right" ng-if="!amountForm.amount.$invalid">
|
||||
<i class="icon-checkmark-circle size-14"></i>
|
||||
</small>
|
||||
</div>
|
||||
<div ng-if="!showAlternative">
|
||||
<label for="amount">
|
||||
<span translate>Amount</span>
|
||||
</label>
|
||||
<div class="input">
|
||||
<input type="number" id="amount" name="amount" ng-attr-placeholder="{{'Amount in'|translate}} {{unitName}}"
|
||||
ng-model="_customAmount" ng-minlength="0.00000001" ng-maxlength="10000000000" valid-amount required autocomplete="off">
|
||||
<input type="number" id="alternative" name="alternative" ng-model="_customAlternative" style="display:none">
|
||||
<a class="postfix button" ng-style="{'background-color':color}" ng-click="toggleAlternative()">{{unitName}}</a>
|
||||
</div>
|
||||
</div>
|
||||
<div ng-if="showAlternative">
|
||||
<label for="alternative"><span translate>Amount</span> [{{ alternativeIsoCode }}]
|
||||
</label>
|
||||
<div class="input">
|
||||
<input type="number" id="alternative" name="alternative" ng-attr-placeholder="{{'Amount in'|translate}} {{alternativeName}}"
|
||||
ng-model="_customAlternative" required autocomplete="off" required>
|
||||
<input type="number" id="amount" name="amount" ng-model="_customAmount" style="display:none">
|
||||
<a class="postfix button black" ng-click="toggleAlternative()"> {{ alternativeIsoCode }}</a>
|
||||
</div>
|
||||
</div>
|
||||
<button type="submit" class="button black round expand" ng-disabled="amountForm.$invalid" ng-style="{'background-color':color}" translate>
|
||||
Generate QR Code
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="extra-margin-bottom"></div>
|
||||
</div>
|
||||
<div ng-show="!customizedAmountBtc" class="row m20t">
|
||||
<div class="large-12 large-centered columns">
|
||||
<form name="amountForm" ng-submit="submitForm(amountForm)" novalidate>
|
||||
<div class="right" ng-hide="amountForm.amount.$pristine && !amountForm.amount.$modelValue ">
|
||||
<span class="has-error right size-12" ng-if="amountForm.amount.$invalid">
|
||||
<i class="icon-close-circle size-14"></i>
|
||||
<span clas="vm" translate>Not valid</span>
|
||||
</span>
|
||||
<small class="text-primary right" ng-if="!amountForm.amount.$invalid">
|
||||
<i class="icon-checkmark-circle size-14"></i>
|
||||
</small>
|
||||
</div>
|
||||
<div ng-if="!showAlternative">
|
||||
<label for="amount">
|
||||
<span translate>Amount</span>
|
||||
</label>
|
||||
<div class="input">
|
||||
<input type="number" id="amount" name="amount" ng-attr-placeholder="{{'Amount in'|translate}} {{unitName}}"
|
||||
ng-model="_customAmount" ng-minlength="0.00000001" ng-maxlength="10000000000" valid-amount required autocomplete="off">
|
||||
<input type="number" id="alternative" name="alternative" ng-model="_customAlternative" style="display:none">
|
||||
<a class="postfix button" ng-style="{'background-color':color}" ng-click="toggleAlternative()">{{unitName}}</a>
|
||||
</div>
|
||||
</div>
|
||||
<div ng-if="showAlternative">
|
||||
<label for="alternative"><span translate>Amount</span> [{{ alternativeIsoCode }}]
|
||||
</label>
|
||||
<div class="input">
|
||||
<input type="number" id="alternative" name="alternative" ng-attr-placeholder="{{'Amount in'|translate}} {{alternativeName}}"
|
||||
ng-model="_customAlternative" required autocomplete="off" required>
|
||||
<input type="number" id="amount" name="amount" ng-model="_customAmount" style="display:none">
|
||||
<a class="postfix button black" ng-click="toggleAlternative()"> {{ alternativeIsoCode }}</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="button black round expand" ng-disabled="amountForm.$invalid" ng-style="{'background-color':color}" translate>
|
||||
Generate QR Code
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ion-content>
|
||||
</ion-view-modal>
|
||||
|
|
|
|||
|
|
@ -1,156 +0,0 @@
|
|||
<div ng-init="wallets[0] ? selectedWalletsOpt = true : selectedWalletsOpt = false; checkClipboard()">
|
||||
<nav class="tab-bar" ng-style="{'background-color':color}">
|
||||
<section class="left-small">
|
||||
<a ng-show="!editAddressbook && !addAddressbookEntry" ng-click="cancel()" class="p10">
|
||||
<span class="text-close" translate>Close</span>
|
||||
</a>
|
||||
</section>
|
||||
|
||||
<section class="middle tab-bar-section">
|
||||
<h1 class="title ellipsis">
|
||||
{{walletName}}
|
||||
</h1>
|
||||
</section>
|
||||
|
||||
<section class="right-small" ng-show="!selectedWalletsOpt" ng-click="toggleEditAddressbook()">
|
||||
<a ng-show="!editAddressbook && !addAddressbookEntry" href class="p10">
|
||||
<span class="text-close" translate>Edit</span>
|
||||
</a>
|
||||
<a ng-show="editAddressbook && !addAddressbookEntry" href class="p10">
|
||||
<span class="text-close" translate>Done</span>
|
||||
</a>
|
||||
</section>
|
||||
</nav>
|
||||
|
||||
<div class="modal-content fix-modals-touch p20b">
|
||||
<div class="create-tab small-only-text-center" ng-show="!editAddressbook && !addAddressbookEntry">
|
||||
<div class="row">
|
||||
<div class="tab-container small-6 medium-3 large-2"
|
||||
ng-class="{'selected':selectedWalletsOpt}"
|
||||
ng-style="{'border-color':selectedWalletsOpt ? color : 'inherit'}"
|
||||
ng-click="selectedWalletsOpt = true">
|
||||
<a href
|
||||
ng-style="{'color':selectedWalletsOpt ? color : 'inherit'}" translate> My wallets</a>
|
||||
</div>
|
||||
<div class="tab-container small-6 medium-3 large-2"
|
||||
ng-class="{'selected':!selectedWalletsOpt}"
|
||||
ng-style="{'border-color':!selectedWalletsOpt ? color : 'inherit'}"
|
||||
ng-click="selectedWalletsOpt = false">
|
||||
<a href
|
||||
ng-style="{'color':!selectedWalletsOpt ? color : 'inherit'}" translate>My contacts</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div ng-show="selectedWalletsOpt">
|
||||
<div class="onGoingProcess" ng-if="gettingAddress">
|
||||
<div class="onGoingProcess-content" ng-style="{'background-color':'#222'}">
|
||||
<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> Getting address for wallet {{selectedWalletName}} ...</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div ng-if="!gettingAddress">
|
||||
<ul class="no-bullet">
|
||||
<li class="line-b" ng-repeat="w in wallets">
|
||||
<a ng-click="selectWallet(w.id, w.name)" class="db oh">
|
||||
<div class="avatar-wallet"
|
||||
ng-style="{'background-color':w.color}">
|
||||
<i class="icon-wallet size-21"></i>
|
||||
</div>
|
||||
<div class="ellipsis name-wallet text-bold">{{w.name || w.id}}
|
||||
<span class="has-error right text-light size-12" ng-show="errorSelectedWallet[w.id]">
|
||||
<i class="icon-close-circle size-14"></i>
|
||||
<span class="vm">{{errorSelectedWallet[w.id] }}</span>
|
||||
</span>
|
||||
</div>
|
||||
<div class="size-12">{{w.m}} of {{w.n}}
|
||||
<span ng-show="w.network=='testnet'">[Testnet]</span>
|
||||
</div>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div ng-show="!selectedWalletsOpt" class="m20b">
|
||||
<ul ng-show="!addAddressbookEntry" class="no-bullet m0" ng-init="list()">
|
||||
<li class="p10 line-b" ng-repeat="(addr, label) in list">
|
||||
<a ng-show="selectedAddressbook[addr]"
|
||||
class="removeAddressbook"
|
||||
ng-click="remove(addr)" translate>Remove</a>
|
||||
<a class="selectAddressbook"
|
||||
ng-show="editAddressbook"
|
||||
ng-click="toggleSelectAddressbook(addr)">
|
||||
<i class="fi-trash"></i></a>
|
||||
<div ng-click="selectAddressbook(addr)">
|
||||
|
||||
<i class="icon-contact left size-42 m10r text-gray"></i>
|
||||
<div class="">
|
||||
<span>{{label}}</span>
|
||||
<div class="size-12 text-gray ellipsis">{{addr}}</div>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
<li class="p10" ng-show="!editAddressbook">
|
||||
<a ng-click="toggleAddAddressbookEntry()" class="p0i">
|
||||
<i class="fi-plus size-24 m20r lh icon"></i>
|
||||
<span class="size-12 tu text-bold" translate>Add a new entry</span>
|
||||
<span class="size-10" ng-show="!list[newAddress] && newAddress">({{newAddress}})</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<div ng-show="addAddressbookEntry">
|
||||
<h4 translate>Add a new entry</h4>
|
||||
<form name="addressbookForm" class="p10" no-validate>
|
||||
<div class="text-warning size-12 m10b" ng-show="error">{{error|translate}}</div>
|
||||
<span ng-hide="addressbookForm.address.$pristine">
|
||||
<span class="has-error right size-12" ng-show="addressbookForm.address.$invalid && addressbook.address">
|
||||
<i class="icon-close-circle size-14"></i>
|
||||
<span class="vm" translate>Not valid</span>
|
||||
</span>
|
||||
<small class="right text-primary" ng-show="!addressbookForm.address.$invalid">
|
||||
<i class="icon-checkmark-circle size-14"></i>
|
||||
</small>
|
||||
</span>
|
||||
|
||||
<label translate>Address</label>
|
||||
<div class="input">
|
||||
<input type="text" id="address" name="address" ng-model="addressbook.address" valid-address required>
|
||||
<div class="qr-scanner-input">
|
||||
<qr-scanner on-scan="onQrCodeScanned(data, addressbookForm)" before-scan="beforeQrCodeScann()"></qr-scanner>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<label translate>Label</label>
|
||||
<input type="text" id="label" name="label" ng-model="addressbook.label" required>
|
||||
<div class="row">
|
||||
<div class="columns large-6 medium-6 small-6">
|
||||
<input type="button"
|
||||
class="button expand outline dark-gray round"
|
||||
ng-click="newAddress = ''; toggleAddAddressbookEntry()"
|
||||
value="{{'Cancel'|translate}}">
|
||||
</div>
|
||||
<div class="columns large-6 medium-6 small-6">
|
||||
<input type="submit"
|
||||
class="button expand round black"
|
||||
value="{{'Save'|translate}}"
|
||||
ng-disabled="!addressbookForm.$valid"
|
||||
ng-click="newAddress = ''; add(addressbook)">
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="extra-margin-bottom"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -1,63 +1,66 @@
|
|||
<nav class="tab-bar" ng-style="{'background-color':color}">
|
||||
<section class="left-small">
|
||||
<a ng-click="cancel()" class="p10">
|
||||
<span class="text-close" translate>Close</span>
|
||||
</a>
|
||||
</section>
|
||||
<section class="middle tab-bar-section">
|
||||
<h1 class="title ellipsis" translate>
|
||||
Payment request
|
||||
</h1>
|
||||
</section>
|
||||
</nav>
|
||||
<ion-modal-view ng-controller="payproController as payproC">
|
||||
<nav class="tab-bar" ng-style="{'background-color':color}">
|
||||
<section class="left-small">
|
||||
<a ng-click="cancel()" class="p10">
|
||||
<span class="text-close" translate>Close</span>
|
||||
</a>
|
||||
</section>
|
||||
|
||||
<div class="modal-content fix-modals-touch">
|
||||
<div class="header-modal text-center">
|
||||
<div class="size-42">
|
||||
{{unitTotal}} {{unitName}}
|
||||
</div>
|
||||
<div class="size-18 m5t text-gray" ng-show="alternative">
|
||||
{{ alternative }} {{ alternativeIsoCode }}
|
||||
</div>
|
||||
</div>
|
||||
<section class="middle tab-bar-section">
|
||||
<h1 class="title" translate>Payment request</h1>
|
||||
</section>
|
||||
</nav>
|
||||
|
||||
<h4 class="title m0" translate>Details</h4>
|
||||
<ul class="no-bullet size-14 m0">
|
||||
<li class="line-b p10 oh">
|
||||
<span class="text-gray" translate>Pay To</span>
|
||||
<span class="right enable_text_select">{{paypro.domain}}</span>
|
||||
</li>
|
||||
<li class="line-b p10 oh" ng-if="paypro.toAddress">
|
||||
<span class="text-gray" translate>Address</span>
|
||||
<span class="right enable_text_select">{{paypro.toAddress}}</span>
|
||||
</li>
|
||||
<li class="line-b p10 oh">
|
||||
<span class="text-gray" translate>Certified by</span>
|
||||
<span class="right text-right">
|
||||
<span ng-show="paypro.caTrusted">
|
||||
<i class="fi-lock color-greeni"></i>
|
||||
{{paypro.caName}}<br>
|
||||
<span translate>(Trusted)</span>
|
||||
</span>
|
||||
<span ng-show="!paypro.caTrusted">
|
||||
<span ng-show="paypro.selfSigned">
|
||||
<i class="fi-unlock color-yellowi"></i> <span translate>Self-signed Certificate</span>
|
||||
<ion-content ng-style="{'background-color': '#F6F7F9'}">
|
||||
<div class="modal-content">
|
||||
<div class="header-modal text-center p50t">
|
||||
<div class="size-42">
|
||||
{{unitTotal}} {{unitName}}
|
||||
</div>
|
||||
<div class="size-18 m5t text-gray" ng-show="alternative">
|
||||
{{ alternative }} {{ alternativeIsoCode }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h4 class="title m10l" translate>Details</h4>
|
||||
<ul class="no-bullet size-14 m10t">
|
||||
<li class="line-b p10 oh">
|
||||
<span class="text-gray" translate>Pay To</span>
|
||||
<span class="right enable_text_select">{{paypro.domain}}</span>
|
||||
</li>
|
||||
<li class="line-b p10 oh" ng-if="paypro.toAddress">
|
||||
<span class="text-gray" translate>Address</span>
|
||||
<span class="right enable_text_select">{{paypro.toAddress}}</span>
|
||||
</li>
|
||||
<li class="line-b p10 oh">
|
||||
<span class="text-gray" translate>Certified by</span>
|
||||
<span class="right text-right">
|
||||
<span ng-show="paypro.caTrusted">
|
||||
<i class="fi-lock color-greeni"></i>
|
||||
{{paypro.caName}}<br>
|
||||
<span translate>(Trusted)</span>
|
||||
</span>
|
||||
<span ng-show="!paypro.caTrusted">
|
||||
<span ng-show="paypro.selfSigned">
|
||||
<i class="fi-unlock color-yellowi"></i> <span translate>Self-signed Certificate</span>
|
||||
</span>
|
||||
<span ng-show="!paypro.selfSigned">
|
||||
<i class="fi-unlock color-yellowi"></i>{{paypro.caName}}<br>
|
||||
<span translate>WARNING: UNTRUSTED CERTIFICATE</span>
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
<span ng-show="!paypro.selfSigned">
|
||||
<i class="fi-unlock color-yellowi"></i>{{paypro.caName}}<br>
|
||||
<span translate>WARNING: UNTRUSTED CERTIFICATE</span>
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
</li>
|
||||
<li class="line-b p10 oh" ng-if="paypro.memo">
|
||||
<span class="text-gray" translate>Memo</span>
|
||||
<span class="right">{{paypro.memo}}</span>
|
||||
</li>
|
||||
<li class="line-b p10 oh" ng-if="paypro.expires">
|
||||
<span class="text-gray" translate>Expires</span>
|
||||
<span class="right">{{paypro.expires * 1000 | amTimeAgo }}</span>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="extra-margin-bottom"></div>
|
||||
</div>
|
||||
</li>
|
||||
<li class="line-b p10 oh" ng-if="paypro.memo">
|
||||
<span class="text-gray" translate>Memo</span>
|
||||
<span class="right">{{paypro.memo}}</span>
|
||||
</li>
|
||||
<li class="line-b p10 oh" ng-if="paypro.expires">
|
||||
<span class="text-gray" translate>Expires</span>
|
||||
<span class="right">{{paypro.expires * 1000 | amTimeAgo }}</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="extra-margin-bottom"></div>
|
||||
</ion-content>
|
||||
</ion-modal-view>
|
||||
|
|
|
|||
82
public/views/modals/search.html
Normal file
82
public/views/modals/search.html
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
<ion-modal-view ng-controller="searchController">
|
||||
<ion-content>
|
||||
<nav class="tab-bar" ng-style="{'background-color':color}">
|
||||
<section class="left-small">
|
||||
<a ng-click="cancel(); index.cancelSearch()" class="p10">
|
||||
<span class="text-close" translate>Close</span>
|
||||
</a>
|
||||
</section>
|
||||
|
||||
<section class="middle tab-bar-section">
|
||||
<h1 class="title ellipsis" translate>Search Transactions</h1>
|
||||
</section>
|
||||
</nav>
|
||||
|
||||
<div class="row searchBar searchLabel">
|
||||
<i class="fi-magnifying-glass size-14"></i>
|
||||
<form>
|
||||
<input name="search" type="search" ng-model="search" ng-init="search = ''" ng-change="index.updateSearchInput(search)"
|
||||
placeholder="{{'Search transactions' | translate}}">
|
||||
</input>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div ng-repeat="btx in index.txHistorySearchResults 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'" translate>Moved</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>
|
||||
<ion-infinite-scroll
|
||||
ng-if="index.historyShowMore && index.isSearching"
|
||||
on-infinite="index.showMore()"
|
||||
distance="1%">
|
||||
</ion-infinite-scroll>
|
||||
</ion-content>
|
||||
</ion-modal-view>
|
||||
|
|
@ -1,148 +1,166 @@
|
|||
<nav class="tab-bar" ng-style="{'background-color':color}">
|
||||
<section class="left-small">
|
||||
<a ng-click="cancel()">
|
||||
<i class="icon-arrow-left3 icon-back"></i>
|
||||
<span class="text-back" translate>Back</span>
|
||||
</a>
|
||||
</section>
|
||||
<section class="middle tab-bar-section">
|
||||
<h1 class="title ellipsis" translate>
|
||||
Transaction
|
||||
</h1>
|
||||
</section>
|
||||
</nav>
|
||||
<ion-modal-view ng-controller="txDetailsController">
|
||||
<nav class="tab-bar" ng-style="{'background-color':color}">
|
||||
<section class="left-small">
|
||||
<a ng-click="cancel()" class="p10">
|
||||
<span class="text-close" translate>Close</span>
|
||||
</a>
|
||||
</section>
|
||||
<section class="middle tab-bar-section">
|
||||
<h1 class="title ellipsis" translate>Transaction</h1>
|
||||
</section>
|
||||
</nav>
|
||||
|
||||
<div class="modal-content fix-modals-touch"
|
||||
ng-swipe-disable-mouse
|
||||
ng-swipe-right="cancel()">
|
||||
<div class="header-modal text-center" ng-init="getAlternativeAmount()">
|
||||
<div ng-show="btx.action != 'invalid'">
|
||||
<div ng-show="btx.action == 'received'">
|
||||
<img src="img/icon-receive-history.svg" alt="sync" width="50">
|
||||
<p class="m0 text-gray size-14" translate>Received</p>
|
||||
<ion-content ng-style="{'background-color': '#F6F7F9'}">
|
||||
<div class="modal-content">
|
||||
<div class="header-modal text-center" ng-init="getAlternativeAmount(btx)">
|
||||
<div ng-show="btx.action != 'invalid'">
|
||||
<div ng-show="btx.action == 'received'">
|
||||
<img src="img/icon-receive-history.svg" alt="sync" width="50">
|
||||
<p class="m0 text-gray size-14" translate>Received</p>
|
||||
</div>
|
||||
<div ng-show="btx.action == 'sent'">
|
||||
<img src="img/icon-sent-history.svg" alt="sync" width="50">
|
||||
<p class="m0 text-gray size-14" translate>Sent</p>
|
||||
</div>
|
||||
<div ng-show="btx.action == 'moved'">
|
||||
<img src="img/icon-moved.svg" alt="sync" width="50">
|
||||
<p class="m0 text-gray size-14" translate>Moved</p>
|
||||
</div>
|
||||
|
||||
<div class="size-36" ng-click="copyToClipboard(btx.amountStr)">
|
||||
<span class="enable_text_select">{{btx.amountStr}}</span>
|
||||
</div>
|
||||
<div class="alternative-amount" ng-click="showRate=!showRate" ng-init="showRate = false">
|
||||
<span class="label gray radius" ng-show="!showRate && alternativeAmountStr">
|
||||
{{alternativeAmountStr}}
|
||||
</span>
|
||||
<span class="size-12" ng-show="showRate && alternativeAmountStr">
|
||||
{{rateStr}} ({{rateDate | amDateFormat:'MM/DD/YYYY HH:mm a'}})
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div ng-show="btx.action == 'invalid'">
|
||||
-
|
||||
</div>
|
||||
</div>
|
||||
<div ng-show="btx.action == 'sent'">
|
||||
<img src="img/icon-sent-history.svg" alt="sync" width="50">
|
||||
<p class="m0 text-gray size-14" translate>Sent</p>
|
||||
|
||||
<h4 class="title m0" translate>Details</h4>
|
||||
|
||||
<ul class="no-bullet size-14 m0">
|
||||
<li ng-if="!btx.hasMultiplesOutputs && btx.addressTo && btx.addressTo != 'N/A'" class="line-b p10 oh"
|
||||
ng-click="copyToClipboard(btx.addressTo)">
|
||||
<span class="text-gray" translate>To</span>
|
||||
<span class="right">
|
||||
<span ng-if="btx.merchant">
|
||||
<span ng-show="btx.merchant.pr.ca"><i class="fi-lock color-greeni"></i> {{btx.merchant.domain}}</span>
|
||||
<span ng-show="!btx.merchant.pr.ca"><i class="fi-unlock color-yellowi"></i> {{btx.merchant.domain}}</span>
|
||||
</span>
|
||||
<span ng-if="!btx.merchant">
|
||||
<span ng-show="btx.labelTo">{{btx.labelTo}}</span>
|
||||
<contact ng-show="!btx.labelTo" class="enable_text_select" address="{{btx.addressTo}}"></contact>
|
||||
</span>
|
||||
</span>
|
||||
</li>
|
||||
|
||||
<li ng-show="btx.hasMultiplesOutputs" class="line-b p10 oh"
|
||||
ng-click="showMultiplesOutputs = !showMultiplesOutputs">
|
||||
<span class="text-gray" translate>Recipients</span>
|
||||
<span class="right">{{btx.recipientCount}}
|
||||
<i ng-show="showMultiplesOutputs" class="icon-arrow-up3 size-24"></i>
|
||||
<i ng-show="!showMultiplesOutputs" class="icon-arrow-down3 size-24"></i>
|
||||
</span>
|
||||
</li>
|
||||
|
||||
<div class="line-b" ng-show="btx.hasMultiplesOutputs && showMultiplesOutputs"
|
||||
ng-repeat="output in btx.outputs"
|
||||
ng-include="'views/includes/output.html'">
|
||||
</div>
|
||||
|
||||
<li ng-if="btx.action == 'invalid'" class="line-b p10 oh">
|
||||
<span class="right" translate>
|
||||
This transaction has become invalid; possibly due to a double spend attempt.
|
||||
</span>
|
||||
</li>
|
||||
|
||||
<li ng-if="btx.time" class="line-b p10 oh">
|
||||
<span class="text-gray" translate>Date</span>
|
||||
<span class="right enable_text_select">
|
||||
<time>{{ btx.time * 1000 | amDateFormat:'MM/DD/YYYY HH:mm a'}}</time>
|
||||
<time>({{ btx.time * 1000 | amTimeAgo}})</time>
|
||||
</span>
|
||||
</li>
|
||||
|
||||
<li class="line-b p10" ng-show="btx.action != 'received'"
|
||||
ng-click="copyToClipboard(btx.feeStr)">
|
||||
<span class="text-gray" translate>Fee</span>
|
||||
<span class="right enable_text_select">{{btx.feeStr}}</span>
|
||||
</li>
|
||||
|
||||
<li class="line-b p10 oh" ng-if="btx.message && btx.action != 'received'"
|
||||
ng-click="copyToClipboard(btx.message)">
|
||||
<span class="text-gray" translate>Description</span>
|
||||
<span class="right enable_text_select">{{btx.message}}</span>
|
||||
</li>
|
||||
|
||||
<li ng-if="btx.merchant" class="line-b p10 oh"
|
||||
ng-click="copyToClipboard(btx.merchant.pr.pd.memo)">
|
||||
<span class="text-gray" translate>Merchant message</span>
|
||||
<span class="right enable_text_select">
|
||||
{{btx.merchant.pr.pd.memo}}
|
||||
</span>
|
||||
</li>
|
||||
|
||||
<li ng-if="btx.time" class="line-b p10 oh">
|
||||
<span class="text-gray" translate>Confirmations</span>
|
||||
<span class="right" >
|
||||
<span class="text-warning" ng-show="!btx.confirmations || btx.confirmations == 0" translate>
|
||||
Unconfirmed
|
||||
</span>
|
||||
<span class="label gray radius" ng-show="btx.confirmations>0 && !btx.safeConfirmed">
|
||||
{{btx.confirmations}}
|
||||
</span>
|
||||
<span class="label gray radius" ng-show="btx.safeConfirmed">
|
||||
{{btx.safeConfirmed}}
|
||||
</span>
|
||||
</span>
|
||||
</li>
|
||||
|
||||
<li class="p10 oh" ng-show="btx.note">
|
||||
<span class="text-gray" translate>Comment</span>
|
||||
<span class="right enable_text_select">{{btx.note.body}}</span><br>
|
||||
<span class="right text-italic text-gray size-12">
|
||||
<span translated>Edited by</span> <span>{{btx.note.editedByName}}</span>,
|
||||
<time>{{btx.note.editedOn * 1000 | amTimeAgo}}</time></span>
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<div ng-if="btx.actions[0] && isShared">
|
||||
<h4 class="title m0" translate>Participants</h4>
|
||||
<ul class="no-bullet size-14 m0">
|
||||
<li class="line-b p10 text-gray" ng-repeat="c in btx.actions">
|
||||
<i class="icon-contact size-24"></i>
|
||||
<span class="right">
|
||||
<i ng-if="c.type == 'reject'" class="fi-x icon-sign x db"></i>
|
||||
<i ng-if="c.type == 'accept'" class="fi-check icon-sign check db"></i>
|
||||
</span>
|
||||
{{c.copayerName}} <span ng-if="c.copayerId == copayerId">({{'Me'|translate}})</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div ng-show="btx.action == 'moved'">
|
||||
<img src="img/icon-moved.svg" alt="sync" width="50">
|
||||
<p class="m0 text-gray size-14" translate>Moved</p>
|
||||
</div>
|
||||
|
||||
<div class="size-36">
|
||||
<span ng-if="btx.action == 'received'">+</span><span ng-if="btx.action == 'sent'">-</span>{{btx.amountStr}}
|
||||
</div>
|
||||
<div class="alternative-amount" ng-click="showRate=!showRate" ng-init="showRate = false">
|
||||
<span class="label gray radius" ng-show="!showRate && alternativeAmountStr">
|
||||
{{alternativeAmountStr}}
|
||||
</span>
|
||||
<span class="size-12" ng-show="showRate && alternativeAmountStr">
|
||||
{{rateStr}} ({{rateDate | amDateFormat:'MM/DD/YYYY HH:mm a'}})
|
||||
</span>
|
||||
|
||||
<div ng-show="btx.txid" class="tx-details-blockchain">
|
||||
<div class="text-center m20t">
|
||||
<button class="button outline round dark-gray tiny" ng-click="$root.openExternalLink('https://' +
|
||||
(getShortNetworkName() == 'test' ? 'test-' : '') + 'insight.bitpay.com/tx/' + btx.txid)">
|
||||
<span class="text-gray" translate>See it on the blockchain</span>
|
||||
</button>
|
||||
<button class="button outline round dark-gray tiny" ng-click="showCommentPopup()">
|
||||
<span class="text-gray" translate ng-show="!btx.note">Add a comment</i></span>
|
||||
<span class="text-gray" translate ng-show="btx.note">Edit comment</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div ng-show="btx.action == 'invalid'">
|
||||
-
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h4 class="title m0" translate>Details</h4>
|
||||
|
||||
<ul class="no-bullet size-14 m0">
|
||||
|
||||
<li ng-if="!btx.hasMultiplesOutputs && btx.addressTo && btx.addressTo != 'N/A'" class="line-b p10 oh" ng-click="copyToClipboard(btx.addressTo)">
|
||||
<span class="text-gray" translate>To</span>
|
||||
<span class="right">
|
||||
<span ng-if="btx.merchant">
|
||||
<span ng-show="btx.merchant.pr.ca"><i class="fi-lock color-greeni"></i> {{btx.merchant.domain}}</span>
|
||||
<span ng-show="!btx.merchant.pr.ca"><i class="fi-unlock color-yellowi"></i> {{btx.merchant.domain}}</span>
|
||||
</span>
|
||||
<span ng-if="!btx.merchant">
|
||||
<span ng-show="btx.labelTo">{{btx.labelTo}}</span>
|
||||
<contact ng-show="!btx.labelTo" class="enable_text_select" address="{{btx.addressTo}}"></contact>
|
||||
</span>
|
||||
</span>
|
||||
</li>
|
||||
|
||||
<li ng-show="btx.hasMultiplesOutputs" class="line-b p10 oh"
|
||||
ng-click="showMultiplesOutputs = !showMultiplesOutputs">
|
||||
<span class="text-gray" translate>Recipients</span>
|
||||
<span class="right">{{btx.recipientCount}}
|
||||
<i ng-show="showMultiplesOutputs" class="icon-arrow-up3 size-24"></i>
|
||||
<i ng-show="!showMultiplesOutputs" class="icon-arrow-down3 size-24"></i>
|
||||
</span>
|
||||
</li>
|
||||
|
||||
<div class="line-b" ng-show="btx.hasMultiplesOutputs && showMultiplesOutputs"
|
||||
ng-repeat="output in btx.outputs"
|
||||
ng-include="'views/includes/output.html'">
|
||||
</div>
|
||||
|
||||
<li ng-if="btx.action == 'invalid'" class="line-b p10 oh">
|
||||
<span class="right" translate>
|
||||
This transaction has become invalid; possibly due to a double spend attempt.
|
||||
</span>
|
||||
<li ng-if="btx.time" class="line-b p10 oh">
|
||||
<span class="text-gray" translate>Date</span>
|
||||
<span class="right enable_text_select">
|
||||
<time>{{ btx.time * 1000 | amDateFormat:'MM/DD/YYYY HH:mm a'}}</time>
|
||||
<time>({{ btx.time * 1000 | amTimeAgo}})</time>
|
||||
</span>
|
||||
</li>
|
||||
<li class="line-b p10" ng-show="btx.action != 'received'" ng-click="copyToClipboard(btx.feeStr)">
|
||||
<span class="text-gray" translate>Fee</span>
|
||||
<span class="right enable_text_select">{{btx.feeStr}}</span>
|
||||
</li>
|
||||
<li class="line-b p10 oh" ng-show="btx.message && btx.action != 'received'" ng-click="copyToClipboard(btx.message)">
|
||||
<span class="text-gray" translate>Note</span>
|
||||
<span class="right enable_text_select">{{btx.message}}</span>
|
||||
</li>
|
||||
<li ng-if="btx.merchant" class="line-b p10 oh" ng-click="copyToClipboard(btx.merchant.pr.pd.memo)">
|
||||
<span class="text-gray" translate>Merchant message</span>
|
||||
<span class="right enable_text_select">
|
||||
{{btx.merchant.pr.pd.memo}}
|
||||
</span>
|
||||
</li>
|
||||
<li ng-if="btx.time" class="line-b p10 oh">
|
||||
<span class="text-gray" translate>Confirmations</span>
|
||||
<span class="right" >
|
||||
<span class="text-warning" ng-show="!btx.confirmations || btx.confirmations == 0" translate>
|
||||
Unconfirmed
|
||||
</span>
|
||||
<span class="label gray radius" ng-show="btx.confirmations>0 && !btx.safeConfirmed">
|
||||
{{btx.confirmations}}
|
||||
</span>
|
||||
<span class="label gray radius" ng-show="btx.safeConfirmed">
|
||||
{{btx.safeConfirmed}}
|
||||
</span>
|
||||
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<div ng-if="btx.actions[0] && isShared">
|
||||
<h4 class="title m0" translate>Participants</h4>
|
||||
<ul class="no-bullet size-14 m0">
|
||||
<li class="line-b p10 text-gray" ng-repeat="c in btx.actions">
|
||||
<i class="icon-contact size-24"></i>
|
||||
<span class="right">
|
||||
<i ng-if="c.type == 'reject'" class="fi-x icon-sign x db"></i>
|
||||
<i ng-if="c.type == 'accept'" class="fi-check icon-sign check db"></i>
|
||||
</span>
|
||||
{{c.copayerName}} <span ng-if="c.copayerId == copayerId">({{'Me'|translate}})</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div ng-show="btx.txid" class="tx-details-blockchain">
|
||||
<div class="text-center m20t">
|
||||
<button class="button outline round dark-gray tiny" ng-click="$root.openExternalLink('https://' +
|
||||
(getShortNetworkName() == 'test' ? 'test-' : '') + 'insight.bitpay.com/tx/' + btx.txid)">
|
||||
<span class="text-gray" translate>See it on the blockchain</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="extra-margin-bottom"></div>
|
||||
</div>
|
||||
</ion-content>
|
||||
</ion-modal-view>
|
||||
|
|
|
|||
|
|
@ -1,193 +1,181 @@
|
|||
<nav class="tab-bar" ng-style="{'background-color':color}">
|
||||
<section class="left-small">
|
||||
<a ng-click="cancel()">
|
||||
<i class="icon-arrow-left3 icon-back"></i>
|
||||
<span class="text-back" translate>Back</span>
|
||||
</a>
|
||||
</section>
|
||||
<section class="middle tab-bar-section">
|
||||
<h1 class="title ellipsis" translate>
|
||||
Payment Proposal
|
||||
</h1>
|
||||
</section>
|
||||
</nav>
|
||||
<div class="modal-content fix-modals-touch"
|
||||
ng-swipe-disable-mouse
|
||||
ng-swipe-right="cancel()"
|
||||
ng-init="updateCopayerList()">
|
||||
<div class="payment-proposal-head"
|
||||
ng-style="{'background-color':color}">
|
||||
<div class="size-36">{{tx.amountStr}}</div>
|
||||
<div class="size-14 text-light" ng-show="tx.alternativeAmountStr">{{tx.alternativeAmountStr}}</div>
|
||||
<i class="db fi-arrow-down size-24 m10v"></i>
|
||||
<span class="payment-proposal-to"
|
||||
ng-click="copyToClipboard(tx.toAddress)">
|
||||
<i class="fi-bitcoin left"></i>
|
||||
<contact ng-if="!tx.hasMultiplesOutputs" class="dib enable_text_select ellipsis m5t m5b size-14" address="{{tx.toAddress}}"></contact>
|
||||
<span ng-if="tx.hasMultiplesOutputs" translate>
|
||||
Multiple recipients
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
<ion-modal-view ng-controller="txpDetailsController">
|
||||
<nav class="tab-bar" ng-style="{'background-color':color}">
|
||||
<section class="left-small">
|
||||
<a ng-click="cancel()" class="p10">
|
||||
<span class="text-close" translate>Close</span>
|
||||
</a>
|
||||
</section>
|
||||
|
||||
<div class="oh">
|
||||
<div class="box-notification" ng-show="error">
|
||||
<span class="text-warning size-14">
|
||||
{{error|translate}}
|
||||
</span>
|
||||
</div>
|
||||
<section class="middle tab-bar-section">
|
||||
<h1 class="title ellipsis" translate>Payment Proposal</h1>
|
||||
</section>
|
||||
</nav>
|
||||
|
||||
<div class="row" ng-if="tx.removed">
|
||||
<div class="column m20t text-center text-warning size-12" translate>
|
||||
The payment was removed by creator
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row p20t white" ng-if="tx.pendingForUs">
|
||||
<div class="large-6 medium-6 small-6 columns" ng-show="isShared">
|
||||
<button class="button outline round dark-gray expand" ng-click="reject(tx);"
|
||||
ng-disabled="loading">
|
||||
<i class="fi-x"></i>
|
||||
<span translate>Reject</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="large-6 medium-6 small-6 columns text-right" ng-show="canSign">
|
||||
<button class="button primary round expand" ng-click="sign(tx)"
|
||||
ng-style="{'background-color':color}"
|
||||
ng-disabled="loading || paymentExpired">
|
||||
<i class="fi-check"></i>
|
||||
<span translate>Accept</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="text-center text-gray size-12 m20t" ng-show="tx.status != 'pending'">
|
||||
<div ng-show="tx.status=='accepted' && !tx.isGlidera">
|
||||
<div class="m10b" translate>Payment accepted, but not yet broadcasted</div>
|
||||
|
||||
<button class="primary round m0"
|
||||
ng-style="{'background-color':color}"
|
||||
ng-click="broadcast(tx)"
|
||||
ng-disabled="loading"
|
||||
> <i class="fi-upload-cloud"></i>
|
||||
<span translate>Broadcast Payment</span>
|
||||
</button>
|
||||
</div>
|
||||
<div ng-show="tx.status=='accepted' && tx.isGlidera" >
|
||||
<div class="m10h" translate>Payment accepted. It will be broadcasted by Glidera. In case there is a problem, it can be deleted 6 hours after it was created.</div>
|
||||
</div>
|
||||
<div class="text-success"
|
||||
ng-show="tx.status=='broadcasted'" translate>
|
||||
Payment Sent
|
||||
</div>
|
||||
<div class="text-warning"
|
||||
ng-show="tx.status=='rejected'" translate>
|
||||
Payment Rejected
|
||||
</div>
|
||||
<div class="onGoingProcess" ng-show="loading">
|
||||
<div class="onGoingProcess-content" ng-style="{'background-color':index.backgroundColor}">
|
||||
<ion-spinner class="spinner-stable" icon="lines"></ion-spinner>
|
||||
<span>{{loading|translate}}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h4 class="title m0" translate>Details</h4>
|
||||
<ul class="no-bullet size-14 m0">
|
||||
<li class="line-b p10 oh" ng-show="tx.message">
|
||||
<span class="text-gray" translate>Note</span>
|
||||
<span class="right">{{tx.message}}</span>
|
||||
</li>
|
||||
<ion-content ng-style="{'background-color': '#F6F7F9'}">
|
||||
<div class="modal-content fix-modals-touch" ng-init="updateCopayerList()">
|
||||
<div class="payment-proposal-head" ng-style="{'background-color':color}">
|
||||
<div class="size-36">{{tx.amountStr}}</div>
|
||||
<div class="size-14 text-light" ng-show="tx.alternativeAmountStr">{{tx.alternativeAmountStr}}</div>
|
||||
<i class="db fi-arrow-down size-24 m10v"></i>
|
||||
<span class="payment-proposal-to" ng-click="copyToClipboard(tx.toAddress)">
|
||||
<i class="fi-bitcoin left"></i>
|
||||
<contact ng-if="!tx.hasMultiplesOutputs" class="dib enable_text_select ellipsis m5t m5b size-14" address="{{tx.toAddress}}"></contact>
|
||||
<span ng-if="tx.hasMultiplesOutputs" translate>Multiple recipients</span>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<li ng-show="tx.hasMultiplesOutputs" class="line-b p10 oh"
|
||||
ng-click="showMultiplesOutputs = !showMultiplesOutputs">
|
||||
<span class="text-gray" translate>Recipients</span>
|
||||
<span class="right">{{tx.recipientCount}}
|
||||
<i ng-show="showMultiplesOutputs" class="icon-arrow-up3 size-24"></i>
|
||||
<i ng-show="!showMultiplesOutputs" class="icon-arrow-down3 size-24"></i>
|
||||
</span>
|
||||
</li>
|
||||
<div class="oh">
|
||||
<div class="box-notification" ng-show="error">
|
||||
<span class="text-warning size-14">{{error|translate}}</span>
|
||||
</div>
|
||||
|
||||
<div class="line-b" ng-show="tx.hasMultiplesOutputs && showMultiplesOutputs"
|
||||
ng-repeat="output in tx.outputs"
|
||||
ng-include="'views/includes/output.html'">
|
||||
</div>
|
||||
<div class="row" ng-if="tx.removed">
|
||||
<div class="column m20t text-center text-warning size-12" translate>
|
||||
The payment was removed by creator
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<li class="line-b p10">
|
||||
<span class="text-gray" translate>Fee</span>
|
||||
<span class="right">{{tx.feeStr}}</span>
|
||||
</li>
|
||||
<li class="line-b p10">
|
||||
<span class="text-gray" translate>Time</span>
|
||||
<span class="right">
|
||||
<time>{{ (tx.ts || tx.createdOn ) * 1000 | amTimeAgo}}</time>
|
||||
</span>
|
||||
</li>
|
||||
<li class="line-b p10 oh">
|
||||
<span class="text-gray" translate>Created by</span>
|
||||
<span class="right">{{tx.creatorName}}</span>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="p10 text-center size-12" ng-show="!currentSpendUnconfirmed && tx.hasUnconfirmedInputs">
|
||||
<span class="text-warning" translate>Warning: this transaction has unconfirmed inputs</span>
|
||||
</div>
|
||||
<div class="row p20t white" ng-if="tx.pendingForUs">
|
||||
<div class="large-6 medium-6 small-6 columns" ng-show="isShared">
|
||||
<button class="button outline round dark-gray expand" ng-click="reject(tx)" ng-disabled="loading">
|
||||
<i class="fi-x"></i>
|
||||
<span translate>Reject</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="large-6 medium-6 small-6 columns text-right" ng-show="canSign">
|
||||
<button class="button primary round expand" ng-click="sign(tx)" ng-style="{'background-color':color}" ng-disabled="loading || paymentExpired">
|
||||
<i class="fi-check"></i>
|
||||
<span translate>Accept</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div ng-if="tx.paypro">
|
||||
<h4 class="title m0" translate>Payment details</h4>
|
||||
<ul class="no-bullet size-14 m0">
|
||||
<li class="line-b p10">
|
||||
<span class="text-gray" translate>To</span>
|
||||
<span class="right">
|
||||
<span>
|
||||
<span ng-show="tx.merchant.pr.ca"><i class="fi-lock"></i> {{tx.paypro.domain}}</span>
|
||||
<span ng-show="!tx.merchant.pr.ca"><i class="fi-unlock"></i> {{tx.paypro.domain}}</span>
|
||||
<div class="text-center text-gray size-12 m20t" ng-show="tx.status != 'pending'">
|
||||
<div ng-show="tx.status=='accepted' && !tx.isGlidera">
|
||||
<div class="m10b" translate>Payment accepted, but not yet broadcasted</div>
|
||||
|
||||
<button class="primary round m0" ng-style="{'background-color':color}" ng-click="broadcast(tx)" ng-disabled="loading">
|
||||
<i class="fi-upload-cloud"></i>
|
||||
<span translate>Broadcast Payment</span>
|
||||
</button>
|
||||
</div>
|
||||
<div ng-show="tx.status=='accepted' && tx.isGlidera" >
|
||||
<div class="m10h" translate>Payment accepted. It will be broadcasted by Glidera. In case there is a problem, it can be deleted 6 hours after it was created.</div>
|
||||
</div>
|
||||
<div class="text-success" ng-show="tx.status == 'broadcasted'" translate>Payment Sent</div>
|
||||
<div class="text-warning" ng-show="tx.status=='rejected'" translate>Payment Rejected</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h4 class="title m0" translate>Details</h4>
|
||||
|
||||
<ul class="no-bullet size-14 m0">
|
||||
<li class="line-b p10 oh" ng-show="tx.message">
|
||||
<span class="text-gray" translate>Description</span>
|
||||
<span class="right">{{tx.message}}</span>
|
||||
</li>
|
||||
|
||||
<li ng-show="tx.hasMultiplesOutputs" class="line-b p10 oh" ng-click="showMultiplesOutputs = !showMultiplesOutputs">
|
||||
<span class="text-gray" translate>Recipients</span>
|
||||
<span class="right">{{tx.recipientCount}}
|
||||
<i ng-show="showMultiplesOutputs" class="icon-arrow-up3 size-24"></i>
|
||||
<i ng-show="!showMultiplesOutputs" class="icon-arrow-down3 size-24"></i>
|
||||
</span>
|
||||
<contact address="{{tx.toAddress}}" ng-hide="tx.merchant"></contact>
|
||||
</span>
|
||||
</li>
|
||||
<li class="line-b p10" ng-if="paymentExpired">
|
||||
<span class="text-gray" translate>Expired</span>
|
||||
<span class="right text-alert">
|
||||
<time>{{tx.paypro.expires * 1000 | amTimeAgo }}</time>
|
||||
</span>
|
||||
</li>
|
||||
<li class="line-b p10" ng-if="!paymentExpired">
|
||||
<span class="text-gray" translate>Expires</span>
|
||||
<span class="right">
|
||||
<time>{{expires}}</time>
|
||||
</span>
|
||||
</li>
|
||||
<li class="line-b p10">
|
||||
<span class="text-gray">Merchant Message</span>
|
||||
<span class="db">{{tx.paypro.pr.pd.memo}}</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<div ng-if="tx.actions[0] && !txRejected && !txBroadcasted">
|
||||
<h4 class="title m0">
|
||||
<div class="right size-12 text-gray m10r">
|
||||
{{tx.requiredSignatures}}/{{tx.walletN}}
|
||||
<div class="line-b" ng-show="tx.hasMultiplesOutputs && showMultiplesOutputs"
|
||||
ng-repeat="output in tx.outputs" ng-include="'views/includes/output.html'">
|
||||
</div>
|
||||
|
||||
<li class="line-b p10">
|
||||
<span class="text-gray" translate>Fee</span>
|
||||
<span class="right">{{tx.feeStr}}</span>
|
||||
</li>
|
||||
|
||||
<li class="line-b p10">
|
||||
<span class="text-gray" translate>Time</span>
|
||||
<span class="right">
|
||||
<time>{{ (tx.ts || tx.createdOn ) * 1000 | amTimeAgo}}</time>
|
||||
</span>
|
||||
</li>
|
||||
|
||||
<li class="line-b p10 oh">
|
||||
<span class="text-gray" translate>Created by</span>
|
||||
<span class="right">{{tx.creatorName}}</span>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<div class="p10 text-center size-12" ng-show="!currentSpendUnconfirmed && tx.hasUnconfirmedInputs">
|
||||
<span class="text-warning" translate>Warning: this transaction has unconfirmed inputs</span>
|
||||
</div>
|
||||
<span translate>Participants</span>
|
||||
</h4>
|
||||
<ul class="no-bullet size-14 m0">
|
||||
<li class="line-b p10 text-gray" ng-repeat="ac in tx.actions">
|
||||
<i class="icon-contact size-24"></i>
|
||||
<span class="right">
|
||||
<i ng-if="ac.type == 'reject'" class="fi-x icon-sign x db"></i>
|
||||
<i ng-if="ac.type == 'accept'" class="fi-check icon-sign check db"></i>
|
||||
</span>
|
||||
{{ac.copayerName}} <span ng-if="ac.copayerId == copayerId">({{'Me'|translate}})</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="columns text-center m20t" ng-if="tx.canBeRemoved || (tx.status == 'accepted' && !tx.broadcastedOn)">
|
||||
<div class="text-gray size-12 m20b" ng-show="!tx.isGlidera && isShared" translate>
|
||||
* A payment proposal can be deleted if 1) you are the creator, and no other copayer has signed, or 2) 24 hours have passed since the proposal was created.
|
||||
<div ng-if="tx.paypro">
|
||||
<h4 class="title m0" translate>Payment details</h4>
|
||||
<ul class="no-bullet size-14 m0">
|
||||
<li class="line-b p10">
|
||||
<span class="text-gray" translate>To</span>
|
||||
<span class="right">
|
||||
<span>
|
||||
<span ng-show="tx.merchant.pr.ca"><i class="fi-lock"></i> {{tx.paypro.domain}}</span>
|
||||
<span ng-show="!tx.merchant.pr.ca"><i class="fi-unlock"></i> {{tx.paypro.domain}}</span>
|
||||
</span>
|
||||
<contact address="{{tx.toAddress}}" ng-hide="tx.merchant"></contact>
|
||||
</span>
|
||||
</li>
|
||||
<li class="line-b p10" ng-if="paymentExpired">
|
||||
<span class="text-gray" translate>Expired</span>
|
||||
<span class="right text-alert">
|
||||
<time>{{tx.paypro.expires * 1000 | amTimeAgo }}</time>
|
||||
</span>
|
||||
</li>
|
||||
<li class="line-b p10" ng-if="!paymentExpired">
|
||||
<span class="text-gray" translate>Expires</span>
|
||||
<span class="right">
|
||||
<time>{{expires}}</time>
|
||||
</span>
|
||||
</li>
|
||||
<li class="line-b p10">
|
||||
<span class="text-gray">Merchant Message</span>
|
||||
<span class="db">{{tx.paypro.pr.pd.memo}}</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div ng-if="tx.actions[0] && !txRejected && !txBroadcasted">
|
||||
<h4 class="title m0">
|
||||
<div class="right size-12 text-gray m10r">
|
||||
{{tx.requiredSignatures}}/{{tx.walletN}}
|
||||
</div>
|
||||
<span translate>Participants</span>
|
||||
</h4>
|
||||
<ul class="no-bullet size-14 m0">
|
||||
<li class="line-b p10 text-gray" ng-repeat="ac in tx.actions">
|
||||
<i class="icon-contact size-24"></i>
|
||||
<span class="right">
|
||||
<i ng-if="ac.type == 'reject'" class="fi-x icon-sign x db"></i>
|
||||
<i ng-if="ac.type == 'accept'" class="fi-check icon-sign check db"></i>
|
||||
</span>
|
||||
{{ac.copayerName}} <span ng-if="ac.copayerId == copayerId">({{'Me'|translate}})</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="columns text-center m20t" ng-if="tx.canBeRemoved || (tx.status == 'accepted' && !tx.broadcastedOn)">
|
||||
<div class="text-gray size-12 m20b" ng-show="!tx.isGlidera && isShared" translate>
|
||||
* A payment proposal can be deleted if 1) you are the creator, and no other copayer has signed, or 2) 24 hours have passed since the proposal was created.
|
||||
</div>
|
||||
<button class="tiny round outline dark-gray warning" ng-click="remove(tx)" ng-disabled="loading">
|
||||
<i class="fi-trash size-14 m5r"></i>
|
||||
<span translate>Delete Payment Proposal</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<button class="tiny round outline dark-gray warning" ng-click="remove(tx)"
|
||||
ng-disabled="loading">
|
||||
<i class="fi-trash size-14 m5r"></i>
|
||||
<span translate>Delete Payment Proposal</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="extra-margin-bottom"></div>
|
||||
</div>
|
||||
</ion-content>
|
||||
</ion-modal-view>
|
||||
|
|
|
|||
|
|
@ -8,13 +8,7 @@
|
|||
<h4 ng-show="!paperWallet.error"></h4>
|
||||
<div class="onGoingProcess" ng-show="paperWallet.scanning || paperWallet.sending">
|
||||
<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>
|
||||
<ion-spinner class="spinner-stable" icon="lines"></ion-spinner>
|
||||
<span ng-show="paperWallet.scanning" translate>Scanning wallet funds...</span>
|
||||
<span ng-show="paperWallet.sending" translate>Sending funds...</span>
|
||||
</div>
|
||||
|
|
@ -24,7 +18,7 @@
|
|||
</div>
|
||||
<form ng-show="!paperWallet.balance" class="oh">
|
||||
<div class="row">
|
||||
<div class="large-12 medium-12 columns">
|
||||
<div class="large-12 medium-12 columns">
|
||||
<div class="input">
|
||||
<label for="inputData" translate>Paper Wallet Private Key</label>
|
||||
<input type="text" placeholder="{{'Paste your paper wallet private key here'|translate}}" ng-model="inputData" id="inputData" ng-change="paperWallet.onData(inputData)">
|
||||
|
|
@ -42,12 +36,12 @@
|
|||
</div>
|
||||
<button
|
||||
ng-disabled="paperWallet.scanning || !paperWallet.scannedKey"
|
||||
ng-style="{'background-color':index.backgroundColor}"
|
||||
class="button black round expand"
|
||||
ng-click="paperWallet.scanFunds()"
|
||||
ng-style="{'background-color':index.backgroundColor}"
|
||||
class="button black round expand"
|
||||
ng-click="paperWallet.scanFunds()"
|
||||
translate>Scan Wallet Funds
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
|
@ -60,11 +54,11 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<button
|
||||
<button
|
||||
ng-disabled="paperWallet.sending || paperWallet.balanceSat <= 0"
|
||||
ng-style="{'background-color':index.backgroundColor}"
|
||||
class="button black round expand"
|
||||
ng-click="paperWallet.sweepWallet()"
|
||||
ng-style="{'background-color':index.backgroundColor}"
|
||||
class="button black round expand"
|
||||
ng-click="paperWallet.sweepWallet()"
|
||||
translate>Sweep Wallet
|
||||
</button>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
|
||||
<div
|
||||
class="topbar-container"
|
||||
<div
|
||||
class="topbar-container"
|
||||
ng-include="'views/includes/topbar.html'"
|
||||
ng-init="titleSection='Wallet Preferences'; closeToHome = true">
|
||||
</div>
|
||||
|
|
@ -33,17 +33,17 @@
|
|||
<div class="right text-gray">
|
||||
<span ng-style="{'color':index.backgroundColor}">█</span>
|
||||
<i class="icon-arrow-right3 size-24"></i>
|
||||
</div>
|
||||
</div>
|
||||
<div translate>Color</div>
|
||||
</li>
|
||||
|
||||
<li ng-show="index.isPrivKeyExternal">
|
||||
<div class="right text-gray">
|
||||
{{preferences.externalSource}}
|
||||
{{preferences.externalSource}}
|
||||
<!-- (Accont {{preferences.externalAccount}}) -->
|
||||
</div>
|
||||
<div translate>Hardware wallet</div>
|
||||
</li>
|
||||
</li>
|
||||
|
||||
<li ng-click="$root.go('backup')" ng-hide="index.isPrivKeyExternal">
|
||||
<div class="right text-gray">
|
||||
|
|
@ -56,7 +56,7 @@
|
|||
</li>
|
||||
|
||||
<li ng-click="$root.go('preferencesAdvanced')">
|
||||
<i class="icon-arrow-right3 size-24 right text-gray"></i>
|
||||
<i class="icon-arrow-right3 size-24 right text-gray"></i>
|
||||
<div translate>Advanced</div>
|
||||
</li>
|
||||
|
||||
|
|
@ -67,20 +67,13 @@
|
|||
</h4>
|
||||
|
||||
<div ng-show="!index.noFocusedWallet && index.canSign">
|
||||
<ion-toggle ng-model="encryptEnabled" toggle-class="toggle-balanced" ng-change="encryptChange()">
|
||||
<span class="toggle-label" translate>Request Spending Password</span>
|
||||
</ion-toggle>
|
||||
|
||||
<ul class="no-bullet m0">
|
||||
|
||||
<li>
|
||||
<switch id="network-name" name="encrypt" ng-model="encrypt" class="green right"></switch>
|
||||
<div translate>Request Spending Password</div>
|
||||
</li>
|
||||
|
||||
<li ng-show="preferences.touchidAvailable">
|
||||
<switch id="touchid" name="touchid" ng-model="touchid" class="green right"></switch>
|
||||
<div translate>Scan Fingerprint</div>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<ion-toggle ng-model="touchidEnabled" toggle-class="toggle-balanced" ng-change="touchidChange()" ng-show="preferences.touchidAvailable">
|
||||
<span class="toggle-label" translate>Scan Fingerprint</span>
|
||||
</ion-toggle>
|
||||
</div>
|
||||
<div ng-show ="!deleted">
|
||||
|
||||
|
|
|
|||
|
|
@ -8,13 +8,7 @@
|
|||
<div class="content preferences" ng-controller="preferencesDeleteWalletController as preferences">
|
||||
<div class="onGoingProcess" ng-show="isDeletingWallet">
|
||||
<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>
|
||||
<ion-spinner class="spinner-stable" icon="lines"></ion-spinner>
|
||||
<span translate>Deleting Wallet...</span>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<div
|
||||
class="topbar-container"
|
||||
<div
|
||||
class="topbar-container"
|
||||
ng-include="'views/includes/topbar.html'"
|
||||
ng-init="titleSection='Email Notifications'; goBackToState = 'preferences'">
|
||||
</div>
|
||||
|
|
@ -11,13 +11,7 @@
|
|||
<h4></h4>
|
||||
<div class="onGoingProcess" ng-show="prefEmail.saving && !index.isOffline">
|
||||
<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>
|
||||
<ion-spinner class="spinner-stable" icon="lines"></ion-spinner>
|
||||
<span translate>Saving preferences...</span>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -8,13 +8,7 @@
|
|||
<div class="preferences-fee" ng-show="prefFee.loading">
|
||||
<div class="row p20 text-center">
|
||||
<div class="columns large-12 medium-12 small-12 m10b">
|
||||
<div class="spinner">
|
||||
<div class="rect1"></div>
|
||||
<div class="rect2"></div>
|
||||
<div class="rect3"></div>
|
||||
<div class="rect4"></div>
|
||||
<div class="rect5"></div>
|
||||
</div>
|
||||
<ion-spinner class="spinner-stable" icon="lines"></ion-spinner>
|
||||
</div>
|
||||
<div class="size-12 text-gray m20t" translate>
|
||||
Loading...
|
||||
|
|
|
|||
|
|
@ -1,14 +1,11 @@
|
|||
|
||||
<div
|
||||
class="topbar-container"
|
||||
ng-include="'views/includes/topbar.html'"
|
||||
<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 as prefGlobal" ng-init="prefGlobal.init()">
|
||||
<h4></h4>
|
||||
<ul class="no-bullet m0 ">
|
||||
<ul class="no-bullet m0">
|
||||
<li ng-click="$root.go('preferencesLanguage')">
|
||||
<div class="right text-gray">
|
||||
{{prefGlobal.currentLanguageName|translate}}
|
||||
|
|
@ -18,7 +15,8 @@
|
|||
</li>
|
||||
</ul>
|
||||
<h4></h4>
|
||||
<ul class="no-bullet m0 ">
|
||||
|
||||
<ul class="no-bullet m0">
|
||||
<li ng-click="$root.go('preferencesUnit')">
|
||||
<div class="right text-gray">
|
||||
{{prefGlobal.unitName}}
|
||||
|
|
@ -26,6 +24,7 @@
|
|||
</div>
|
||||
<div translate>Unit</div>
|
||||
</li>
|
||||
|
||||
<li ng-click="$root.go('preferencesAltCurrency')">
|
||||
<div class="right text-gray">
|
||||
{{prefGlobal.selectedAlternative.name}}
|
||||
|
|
@ -35,7 +34,8 @@
|
|||
</li>
|
||||
</ul>
|
||||
<h4></h4>
|
||||
<ul class="no-bullet m0 ">
|
||||
|
||||
<ul class="no-bullet m0">
|
||||
<li ng-click="$root.go('preferencesFee')">
|
||||
<div class="right text-gray">
|
||||
{{prefGlobal.feeOpts[prefGlobal.currentFeeLevel]|translate}}
|
||||
|
|
@ -43,41 +43,37 @@
|
|||
</div>
|
||||
<div translate>Bitcoin Network Fee Policy</div>
|
||||
</li>
|
||||
<li>
|
||||
<switch id="spend-unconfirmed" name="spendUnconfirmed" ng-model="spendUnconfirmed" class="green right"></switch>
|
||||
<div translate>Use Unconfirmed Funds</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="prefGlobal.usePushNotifications && PNEnabledByUser">
|
||||
<h4></h4>
|
||||
<ul class="no-bullet m0">
|
||||
<li>
|
||||
<switch id="push-notifications" name="pushNotifications" ng-model="pushNotifications" class="green right"></switch>
|
||||
<div translate>Enable push notifications</div>
|
||||
</li>
|
||||
</ul>
|
||||
<ion-toggle ng-model="pushNotifications" toggle-class="toggle-balanced" ng-change="pushNotificationsChange()">
|
||||
<span class="toggle-label" translate>Enable push notifications</span>
|
||||
</ion-toggle>
|
||||
</div>
|
||||
<h4></h4>
|
||||
<ul class="no-bullet m0 ">
|
||||
<li>
|
||||
<switch id="glidera-enabled" name="glideraEnabled" ng-model="glideraEnabled" class="green right"></switch>
|
||||
<div translate>Enable Glidera Service</div>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<ion-toggle ng-model="glideraEnabled" toggle-class="toggle-balanced" ng-change="glideraChange()">
|
||||
<span class="toggle-label" translate>Enable Glidera Service</span>
|
||||
</ion-toggle>
|
||||
<h4></h4>
|
||||
<ul class="no-bullet m0">
|
||||
<li>
|
||||
<switch id="coinbase-enabled" name="coinbaseEnabled" ng-model="coinbaseEnabled" class="green right"></switch>
|
||||
<div translate>Enable Coinbase Service</div>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<ion-toggle 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 ng-click="$root.go('about')">
|
||||
<i class="icon-arrow-right3 size-24 right text-gray"></i>
|
||||
<div translate>About Copay</div>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<div ng-show="prefGlobal.usePushNotifications && !PNEnabledByUser && isIOSApp">
|
||||
<div class="text-centered 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="prefGlobal.openSettings()">
|
||||
|
|
|
|||
|
|
@ -6,14 +6,22 @@
|
|||
<div class="content preferences" ng-controller="preferencesHistory as history">
|
||||
<h4></h4>
|
||||
<ul class="no-bullet m0">
|
||||
<li ng-if="!index.isCordova" ng-init="index.csvHistory()">
|
||||
<a ng-style="{'color':index.backgroundColor}" ng-csv="index.csvContent"
|
||||
csv-header="index.csvHeader"
|
||||
filename="{{index.csvFilename }}" translate>
|
||||
<li ng-if="!index.isCordova" ng-init="history.csvHistory()">
|
||||
<a style="color:#444"
|
||||
ng-csv="history.csvContent"
|
||||
csv-header="history.csvHeader"
|
||||
ng-show="history.csvReady"
|
||||
filename="Copay-{{index.alias || index.walletName}}.csv" translate>
|
||||
Export to file
|
||||
</a>
|
||||
<a style="color:#777"
|
||||
ng-show="!history.csvReady"
|
||||
translate>
|
||||
Export to file [preparing...]
|
||||
</a>
|
||||
|
||||
</li>
|
||||
<li ng-style="{'color':index.backgroundColor}" ng-click="history.clearTransactionHistory();" translate>
|
||||
<li ng-click="history.clearTransactionHistory();" translate>
|
||||
Clear cache
|
||||
</li>
|
||||
</ul>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<div
|
||||
class="topbar-container"
|
||||
<div
|
||||
class="topbar-container"
|
||||
ng-include="'views/includes/topbar.html'"
|
||||
ng-init="titleSection='Sell'; goBackToState = 'coinbase'; noColor = true">
|
||||
</div>
|
||||
|
|
@ -9,13 +9,7 @@
|
|||
|
||||
<div class="onGoingProcess" ng-show="sell.loading">
|
||||
<div class="onGoingProcess-content" ng-style="{'background-color': '#2b71b1'}">
|
||||
<div class="spinner">
|
||||
<div class="rect1"></div>
|
||||
<div class="rect2"></div>
|
||||
<div class="rect3"></div>
|
||||
<div class="rect4"></div>
|
||||
<div class="rect5"></div>
|
||||
</div>
|
||||
<ion-spinner class="spinner-stable" icon="lines"></ion-spinner>
|
||||
<span>{{sell.loading}}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -30,25 +24,25 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row m20t"
|
||||
<div class="row m20t"
|
||||
ng-show="index.coinbaseAccount && !sell.sellInfo && !sell.sendInfo">
|
||||
<div class="columns">
|
||||
<form
|
||||
name="sellCoinbaseForm"
|
||||
ng-submit="sell.depositFunds(index.coinbaseToken, index.coinbaseAccount)"
|
||||
name="sellCoinbaseForm"
|
||||
ng-submit="sell.depositFunds(index.coinbaseToken, index.coinbaseAccount)"
|
||||
novalidate>
|
||||
|
||||
|
||||
<div ng-show="!showPriceSensitivity">
|
||||
|
||||
<div
|
||||
<div
|
||||
ng-if="index.coinbaseToken"
|
||||
ng-init="sell.init(index.coinbaseTestnet)"
|
||||
ng-click="openWalletsModal(sell.otherWallets)">
|
||||
<label>Copay Wallet</label>
|
||||
<div class="input">
|
||||
<input type="text" id="address" name="address" ng-disabled="sell.selectedWalletId"
|
||||
ng-attr-placeholder="{{'Choose your source wallet'}}"
|
||||
<input type="text" id="address" name="address" ng-disabled="sell.selectedWalletId"
|
||||
ng-attr-placeholder="{{'Choose your source wallet'}}"
|
||||
ng-model="sell.selectedWalletName" required>
|
||||
<a class="postfix size-12 m0 text-gray">
|
||||
<i class="icon-wallet size-18"></i>
|
||||
|
|
@ -57,28 +51,28 @@
|
|||
</div>
|
||||
|
||||
<label>
|
||||
Amount
|
||||
<span
|
||||
Amount
|
||||
<span
|
||||
ng-if="index.coinbaseToken"
|
||||
ng-init="sell.getPrice(index.coinbaseToken)"
|
||||
ng-show="sell.sellPrice"
|
||||
ng-show="sell.sellPrice"
|
||||
class="size-11 text-light right">
|
||||
1 BTC <i class="icon-arrow-right"></i> {{sell.sellPrice.amount}} {{sell.sellPrice.currency}}
|
||||
</span>
|
||||
</label>
|
||||
<div class="input">
|
||||
<input ng-show="!showAlternative" type="number" id="amount"
|
||||
<input ng-show="!showAlternative" type="number" id="amount"
|
||||
name="amount" ng-attr-placeholder="{{'Amount in ' + (showAlternative ? 'USD' : 'BTC')}}"
|
||||
ng-minlength="0.00000001" ng-maxlength="10000000000"
|
||||
ng-minlength="0.00000001" ng-maxlength="10000000000"
|
||||
ng-model="amount" autocomplete="off" ng-disabled="sell.loading">
|
||||
|
||||
<input ng-show="showAlternative" type="number" id="fiat"
|
||||
name="fiat" ng-attr-placeholder="{{'Amount in ' + (showAlternative ? 'USD' : 'BTC')}}"
|
||||
|
||||
<input ng-show="showAlternative" type="number" id="fiat"
|
||||
name="fiat" ng-attr-placeholder="{{'Amount in ' + (showAlternative ? 'USD' : 'BTC')}}"
|
||||
ng-model="fiat" autocomplete="off" ng-disabled="sell.loading">
|
||||
|
||||
<a ng-show="!showAlternative" class="postfix button"
|
||||
<a ng-show="!showAlternative" class="postfix button"
|
||||
ng-click="showAlternative = true; amount = null">BTC</a>
|
||||
<a ng-show="showAlternative" class="postfix button black"
|
||||
<a ng-show="showAlternative" class="postfix button black"
|
||||
ng-click="showAlternative = false; fiat = null">USD</a>
|
||||
</div>
|
||||
|
||||
|
|
@ -100,13 +94,13 @@
|
|||
|
||||
<div ng-if="index.coinbaseToken" ng-init="sell.getPaymentMethods(index.coinbaseToken)">
|
||||
<label>Deposit into</label>
|
||||
<select
|
||||
ng-model="selectedPaymentMethod.id"
|
||||
<select
|
||||
ng-model="selectedPaymentMethod.id"
|
||||
ng-options="item.id as item.name for item in sell.paymentMethods">
|
||||
</select>
|
||||
</div>
|
||||
<div class="input m20t">
|
||||
<a href class="button black expand round"
|
||||
<a href class="button black expand round"
|
||||
ng-disabled="sell.loading || (!amount && !fiat)"
|
||||
ng-style="{'background-color': '#2b71b1'}"
|
||||
ng-click="showPriceSensitivity = true">Continue</a>
|
||||
|
|
@ -114,20 +108,20 @@
|
|||
</div>
|
||||
|
||||
<div ng-show="showPriceSensitivity">
|
||||
<h1>Price Sensitivity</h1>
|
||||
<h1>Price Sensitivity</h1>
|
||||
<p class="size-14 text-gray">
|
||||
Coinbase has not yet implemented an immediate method to sell bitcoin from a wallet. To make this sale, funds
|
||||
will be sent to your Coinbase account, and sold when Coinbase accepts the transaction (usually one
|
||||
hour).
|
||||
</p>
|
||||
<label>At what percentage lower price would you accept to sell?</label>
|
||||
<select
|
||||
ng-model="selectedPriceSensitivity"
|
||||
<select
|
||||
ng-model="selectedPriceSensitivity"
|
||||
ng-options="item as item.name for item in priceSensitivity track by item.value">
|
||||
</select>
|
||||
<p class="size-12 text-gray">
|
||||
Estimated sale value: {{sell.sellPrice.amount * amount | currency : 'USD ' : 2}} <br>
|
||||
Still sell if price fall until:
|
||||
Still sell if price fall until:
|
||||
{{(sell.sellPrice.amount - (selectedPriceSensitivity.value / 100) * sell.sellPrice.amount) * amount | currency : 'USD ' : 2}}
|
||||
</p>
|
||||
|
||||
|
|
@ -136,7 +130,7 @@
|
|||
<a href class="button outline dark-gray expand round" ng-click="showPriceSensitivity = false">Back</a>
|
||||
</div>
|
||||
<div class="columns large-6 medium-6 small-6">
|
||||
<input class="button black expand round"
|
||||
<input class="button black expand round"
|
||||
ng-disabled="sell.loading || (!amount && !fiat)"
|
||||
ng-style="{'background-color': '#2b71b1'}"
|
||||
type="submit" value="Confirm">
|
||||
|
|
@ -182,15 +176,15 @@
|
|||
<li class="line-b p15">
|
||||
<span class="m10 text-normal text-bold">Total</span>
|
||||
<span class="right text-gray">{{sell.sellInfo.total.amount}} {{sell.sellInfo.total.currency}}</span>
|
||||
</li>
|
||||
</li>
|
||||
<li class="line-b p15">
|
||||
<span class="m10 text-normal text-bold">Payout at</span>
|
||||
<span class="right text-gray">{{sell.sellInfo.payout_at | amCalendar}}</span>
|
||||
</li>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="row">
|
||||
<div class="columns">
|
||||
<button class="button black round expand"
|
||||
<button class="button black round expand"
|
||||
ng-style="{'background-color': '#2b71b1'}"
|
||||
ng-click="sell.confirmSell(index.coinbaseToken, index.coinbaseAccount, sell.sellInfo)"
|
||||
ng-disabled="sell.loading">
|
||||
|
|
@ -210,6 +204,6 @@
|
|||
ng-click="$root.go('coinbase')">OK</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
<div class="extra-margin-bottom"></div>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<div
|
||||
class="topbar-container"
|
||||
<div
|
||||
class="topbar-container"
|
||||
ng-include="'views/includes/topbar.html'"
|
||||
ng-init="titleSection='Sell'; goBackToState = 'glidera'; noColor = true">
|
||||
</div>
|
||||
|
|
@ -9,13 +9,7 @@
|
|||
|
||||
<div class="onGoingProcess" ng-show="sell.loading">
|
||||
<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>
|
||||
<ion-spinner class="spinner-stable" icon="lines"></ion-spinner>
|
||||
<span>{{sell.loading}}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -24,7 +18,7 @@
|
|||
<h4 class="title m0 text-left">
|
||||
<span class="text-light">Daily sell limit</span>:
|
||||
{{index.glideraLimits.dailySell|currency:'':2}} {{index.glideraLimits.currency}}
|
||||
(remaining {{index.glideraLimits.dailySellRemaining|currency:'':2}} {{index.glideraLimits.currency}})
|
||||
(remaining {{index.glideraLimits.dailySellRemaining|currency:'':2}} {{index.glideraLimits.currency}})
|
||||
<br>
|
||||
<span class="text-light">Monthly sell limit</span>:
|
||||
{{index.glideraLimits.monthlySell|currency:'':2}} {{index.glideraLimits.currency}}
|
||||
|
|
@ -42,16 +36,16 @@
|
|||
</div>
|
||||
|
||||
<div ng-show="!sell.show2faCodeInput && !sell.success">
|
||||
<form name="sellPriceForm"
|
||||
<form name="sellPriceForm"
|
||||
ng-submit="sell.get2faCode(index.glideraToken)" novalidate>
|
||||
|
||||
<div ng-if="index.glideraToken"
|
||||
<div ng-if="index.glideraToken"
|
||||
ng-init="sell.init(index.glideraTestnet)"
|
||||
ng-click="openWalletsModal(sell.otherWallets)">
|
||||
<label>Wallet</label>
|
||||
<div class="input">
|
||||
<input type="text" id="address" name="address" ng-disabled="sell.selectedWalletId"
|
||||
ng-attr-placeholder="{{'Choose your source wallet'}}"
|
||||
<input type="text" id="address" name="address" ng-disabled="sell.selectedWalletId"
|
||||
ng-attr-placeholder="{{'Choose your source wallet'}}"
|
||||
ng-model="sell.selectedWalletName" required>
|
||||
<a class="postfix size-12 m0 text-gray">
|
||||
<i class="icon-wallet size-18"></i>
|
||||
|
|
@ -61,24 +55,24 @@
|
|||
|
||||
<label><span>Amount in</span> {{showAlternative ? 'USD' : 'BTC'}}</label>
|
||||
<div class="input">
|
||||
<input ng-show="!showAlternative" type="number" id="qty"
|
||||
<input ng-show="!showAlternative" type="number" id="qty"
|
||||
name="qty" ng-attr-placeholder="{{'Amount'}}"
|
||||
ng-minlength="0.00000001" ng-maxlength="10000000000"
|
||||
ng-minlength="0.00000001" ng-maxlength="10000000000"
|
||||
ng-model="qty" autocomplete="off" ng-change="sell.getSellPrice(index.glideraToken, {'qty': qty})">
|
||||
|
||||
<input ng-show="showAlternative" type="number" id="fiat"
|
||||
name="fiat" ng-attr-placeholder="{{'Amount'}}"
|
||||
<input ng-show="showAlternative" type="number" id="fiat"
|
||||
name="fiat" ng-attr-placeholder="{{'Amount'}}"
|
||||
ng-model="fiat" autocomplete="off" ng-change="sell.getSellPrice(index.glideraToken, {'fiat': fiat})">
|
||||
|
||||
<a ng-show="!showAlternative" class="postfix"
|
||||
<a ng-show="!showAlternative" class="postfix"
|
||||
ng-click="showAlternative = true; qty = null; sell.sellPrice = null">BTC</a>
|
||||
<a ng-show="showAlternative" class="postfix"
|
||||
<a ng-show="showAlternative" class="postfix"
|
||||
ng-click="showAlternative = false; fiat = null; sell.sellPrice = null">USD</a>
|
||||
|
||||
|
||||
<div class="text-center text-gray size-12 m20b" ng-show="!sell.gettingSellPrice && sell.sellPrice.qty">
|
||||
Sell
|
||||
<span ng-show="qty">{{sell.sellPrice.subtotal|currency:'':2}} {{sell.sellPrice.currency}} in Bitcoin</span>
|
||||
<span ng-show="fiat">{{sell.sellPrice.qty}} BTC</span>
|
||||
Sell
|
||||
<span ng-show="qty">{{sell.sellPrice.subtotal|currency:'':2}} {{sell.sellPrice.currency}} in Bitcoin</span>
|
||||
<span ng-show="fiat">{{sell.sellPrice.qty}} BTC</span>
|
||||
at {{sell.sellPrice.price|currency:'':2}} {{sell.sellPrice.currency}}/BTC
|
||||
|
||||
</div>
|
||||
|
|
@ -90,13 +84,13 @@
|
|||
...
|
||||
</div>
|
||||
|
||||
<input class="button black expand round"
|
||||
<input class="button black expand round"
|
||||
ng-style="{'background-color':index.backgroundColor}"
|
||||
type="submit" value="{{'Continue'}}"
|
||||
type="submit" value="{{'Continue'}}"
|
||||
ng-disabled="index.glideraLimits.transactDisabledPendingFirstTransaction || !sell.sellPrice.qty ||
|
||||
!sell.selectedWalletId || sell.loading">
|
||||
</div>
|
||||
</form>
|
||||
</form>
|
||||
</div>
|
||||
<div ng-show="sell.show2faCodeInput && !sell.success">
|
||||
<div class="m10t text-center">
|
||||
|
|
@ -105,10 +99,10 @@
|
|||
A SMS containing a confirmation code was sent to your phone. <br>
|
||||
Please, enter the code below
|
||||
</p>
|
||||
<form name="sellForm"
|
||||
<form name="sellForm"
|
||||
ng-submit="sell.createTx(index.glideraToken, index.glideraPermissions, twoFaCode)" novalidate>
|
||||
<input type="number" ng-model="twoFaCode" required>
|
||||
<input class="button black expand round"
|
||||
<input class="button black expand round"
|
||||
ng-style="{'background-color':index.backgroundColor}"
|
||||
type="submit" value="{{'Sell'}}" ng-disabled="sellForm.$invalid || sell.loading">
|
||||
</form>
|
||||
|
|
|
|||
|
|
@ -18,26 +18,14 @@
|
|||
|
||||
<div class="onGoingProcess" ng-show="index.isOffline">
|
||||
<div class="onGoingProcess-content" ng-style="{'background-color':'#222'}">
|
||||
<div class="spinner">
|
||||
<div class="rect1"></div>
|
||||
<div class="rect2"></div>
|
||||
<div class="rect3"></div>
|
||||
<div class="rect4"></div>
|
||||
<div class="rect5"></div>
|
||||
</div>
|
||||
<ion-spinner class="spinner-stable" icon="lines"></ion-spinner>
|
||||
<span translate>Reconnecting to Wallet Service...</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="onGoingProcess" ng-show="index.anyOnGoingProcess && !index.isOffline">
|
||||
<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>
|
||||
<ion-spinner class="spinner-stable" icon="lines"></ion-spinner>
|
||||
<span translate ng-show="
|
||||
index.onGoingProcessName == 'openingWallet'
|
||||
|| index.onGoingProcessName == 'updatingStatus'
|
||||
|
|
@ -52,13 +40,7 @@
|
|||
|
||||
<div class="onGoingProcess" ng-show="home.onGoingProcess && !index.anyOnGoingProces && !index.isOffline">
|
||||
<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>
|
||||
<ion-spinner class="spinner-stable" icon="lines"></ion-spinner>
|
||||
{{home.onGoingProcess}}...
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -71,242 +53,221 @@
|
|||
|
||||
-->
|
||||
|
||||
<div id="walletHome" class="walletHome tab-view tab-in">
|
||||
<div class="oh pr" ng-show="!index.isSearching">
|
||||
<div class="amount" ng-style="{'background-color':index.backgroundColor}">
|
||||
<div ng-if="!index.anyOnGoingProcess && !index.notAuthorized">
|
||||
<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'">
|
||||
<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}}
|
||||
<ion-content id="walletHome" class="walletHome tab-view tab-in" scroll="false" >
|
||||
<ion-content overflow-scroll="true">
|
||||
<ion-refresher
|
||||
ng-if="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.anyOnGoingProcess && !index.notAuthorized">
|
||||
<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>
|
||||
</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-if="index.anyOnGoingProcess">
|
||||
<div class="size-36">
|
||||
<strong>...</strong>
|
||||
</div>
|
||||
</div>
|
||||
</div> <!-- amount -->
|
||||
<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 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="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.anyOnGoingProcess">
|
||||
<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 && !index.isSearching" 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 class="oh pr" ng-if="index.txps[0]" ng-show="!index.isSearching">
|
||||
<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 class="last-transactions pr" 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 -->
|
||||
|
||||
<div
|
||||
class="oh pr m20t text-gray size-12 text-center"
|
||||
ng-show="!index.loadingWallet && !index.txHistory[0] && !index.updatingTxHistory && !index.txHistoryError && !index.updateError" translate>
|
||||
No transactions yet
|
||||
</div>
|
||||
|
||||
<div class="oh pr" ng-show="index.txHistory[0] || index.txProgress > 5">
|
||||
<h4 class="title" ng-click="index.startSearch(); search=''" ng-show="!index.isSearching">
|
||||
<span translate>Activity</span>
|
||||
<i class="dib m5l size-16 pointer fi-magnifying-glass"></i>
|
||||
</h4>
|
||||
|
||||
<div ng-show="index.updatingTxHistory && index.txProgress > 5">
|
||||
<div class="row p20 text-center">
|
||||
<div class="columns large-12 medium-12 small-12 m10b">
|
||||
<div class="spinner">
|
||||
<div class="rect1"></div>
|
||||
<div class="rect2"></div>
|
||||
<div class="rect3"></div>
|
||||
<div class="rect4"></div>
|
||||
<div class="rect5"></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" transalate>
|
||||
Tap and hold to show
|
||||
</div>
|
||||
</div>
|
||||
</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 ng-if="index.anyOnGoingProcess">
|
||||
<div class="size-36">
|
||||
<strong>...</strong>
|
||||
</div>
|
||||
</div>
|
||||
<div class="m10t" style="background:#eee; width: 8em; margin-left: 52px; line-height:0.6em">
|
||||
<span> </span>
|
||||
</div>
|
||||
<div style="margin-top:5px; background:#eee; width: 6em; margin-left: 52px; line-height:0.6em">
|
||||
<span> </span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div> <!-- amount -->
|
||||
|
||||
<div ng-show="index.isSearching" class="row searchBar">
|
||||
<div class="small-11 columns">
|
||||
<div class="searchLabel">
|
||||
<i class="fi-magnifying-glass size-14"></i>
|
||||
<form>
|
||||
<input name="search"
|
||||
type="search"
|
||||
ng-change="index.updateSearchInput(search)"
|
||||
placeholder="{{'Search transactions' | translate}}"
|
||||
ng-model="search">
|
||||
</input>
|
||||
</form>
|
||||
<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="m50b">
|
||||
<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="small-1 columns">
|
||||
<a ng-click="index.cancelSearch()" translate>Cancel</a>
|
||||
</div>
|
||||
</div>
|
||||
<div ng-repeat="btx in index.txHistorySearchResults 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 class="oh pr m20t" ng-show="index.notAuthorized && !index.anyOnGoingProcess">
|
||||
<div class="text-center text-warning">
|
||||
<i class="fi-alert"></i>
|
||||
<span translate>
|
||||
WARNING: Wallet not registered
|
||||
</span>
|
||||
</div>
|
||||
<div class="m10t">
|
||||
<span ng-show="btx.action == 'received'" translate>Received</span>
|
||||
<span ng-show="btx.action == 'sent'">
|
||||
{{index.addressbook[btx.addressTo]}}
|
||||
<span ng-show="!index.addressbook[btx.addressTo] && btx.message">
|
||||
<span class="ellipsis">{{btx.message}}</span>
|
||||
<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 class="oh pr" 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> </span>
|
||||
</div>
|
||||
<div style="margin-top:5px; background:#eee; width: 6em; margin-left: 52px; line-height:0.6em">
|
||||
<span> </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'" translate>Moved</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>
|
||||
<span ng-show="!index.addressbook[btx.addressTo] && !btx.message">
|
||||
<span translate> Sent</span>
|
||||
</span>
|
||||
</span>
|
||||
<span ng-show="btx.action == 'moved'" translate>Moved</span>
|
||||
<span class="label tu warning radius" ng-show="btx.action == 'invalid'" translate>Invalid</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>
|
||||
|
||||
<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 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>
|
||||
</div>
|
||||
<div class="large-1 medium-1 small-1 columns text-right m10t">
|
||||
<i class="icon-arrow-right3 size-18"></i>
|
||||
|
||||
<div class="text-gray text-center size-12 p10t" ng-if="index.historyShowMore">
|
||||
<span class="size-12">{{index.completeHistory.length - index.txHistory.length}}</span>
|
||||
<span class="size-12" translate> more</span> <i class="icon-arrow-down4"></i>
|
||||
</div>
|
||||
|
||||
<ion-infinite-scroll
|
||||
ng-if="index.historyShowMore"
|
||||
on-infinite="index.showMore()"
|
||||
distance="1%">
|
||||
</ion-infinite-scroll>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row m20t text-center" ng-show="index.historyRendering && !index.isSearching">
|
||||
<div class="columns large-12 medium-12 small-12">
|
||||
<div class="spinner">
|
||||
<div class="rect1"></div>
|
||||
<div class="rect2"></div>
|
||||
<div class="rect3"></div>
|
||||
<div class="rect4"></div>
|
||||
<div class="rect5"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="m20t text-center">
|
||||
<a class="text-gray size-12"
|
||||
ng-show="index.historyShowMore"
|
||||
ng-click="index.showMore()">
|
||||
<span translate>Show more</span>
|
||||
<span ng-if="!index.isSearching">
|
||||
({{index.completeHistory.length - index.txHistory.length}})
|
||||
</span>
|
||||
<span ng-if="index.isSearching">
|
||||
({{index.result.length - index.txHistorySearchResults.length}})
|
||||
</span>
|
||||
<i class="icon-arrow-down4"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</ion-content>
|
||||
<div class="extra-margin-bottom"></div>
|
||||
</div> <!-- END WalletHome -->
|
||||
</ion-content> <!-- END WalletHome -->
|
||||
|
||||
<!--
|
||||
|
||||
|
|
@ -342,13 +303,7 @@
|
|||
<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">
|
||||
<div class="spinner" style="margin-top:85px">
|
||||
<div class="rect1"></div>
|
||||
<div class="rect2"></div>
|
||||
<div class="rect3"></div>
|
||||
<div class="rect4"></div>
|
||||
<div class="rect5"></div>
|
||||
</div>
|
||||
<ion-spinner class="spinner-stable" icon="lines" style="margin-top: 85px"></ion-spinner>
|
||||
</div>
|
||||
</div>
|
||||
<div class="m10t" >
|
||||
|
|
@ -396,7 +351,7 @@
|
|||
<div id="send" class="send tab-view">
|
||||
<div class="pr p25b">
|
||||
<h4 class="title m0">
|
||||
<available-balance></available-balance>
|
||||
<available-balance ng-show="!index.shouldHideBalance"></available-balance>
|
||||
<span
|
||||
ng-show="home.lockedCurrentFeePerKb || home.blockUx || home.lockAmount"
|
||||
class="text-gray" translate>Send Max</span>
|
||||
|
|
@ -446,7 +401,7 @@
|
|||
</div>
|
||||
<a class="postfix size-12 m0 text-gray"
|
||||
ng-style="{'color':index.backgroundColor}"
|
||||
ng-click="home.openDestinationAddressModal(index.otherWallets, _address)">
|
||||
ng-click="home.openAddressbookModal(index.otherWallets, _address)">
|
||||
<i class="icon-wallet text-bold size-18"></i>
|
||||
</a>
|
||||
</div>
|
||||
|
|
@ -500,7 +455,7 @@
|
|||
</div>
|
||||
<div class="row" ng-hide="home.hideNote">
|
||||
<div class="large-12 columns">
|
||||
<label for="comment"><span translate>Note</span>
|
||||
<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>
|
||||
|
|
|
|||
100
src/css/ionic-migration.css
Normal file
100
src/css/ionic-migration.css
Normal file
|
|
@ -0,0 +1,100 @@
|
|||
/*
|
||||
*
|
||||
* Ionic migration CSS
|
||||
* These styles reapply foundation.css styles AFTER iconic.css has been applied.
|
||||
* This has the effect of allowing all foundation styles be available at the same time as all Ionic styles being available.
|
||||
* Where there are conflicts between foundation and css, this file resolves the conflict with a compromise which the developer must manage.
|
||||
* This stylesheet is merged into copay.css.
|
||||
*/
|
||||
|
||||
/* Foundation.css styles re-applied. These styles are exactly as described in foundation.css */
|
||||
|
||||
|
||||
/* Conflicting styles that are customized as a compromise. These styles are a merge or compromise of foundation.css and ionic.css. */
|
||||
|
||||
.row {
|
||||
display: inherit;
|
||||
}
|
||||
|
||||
.behind {
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
.popup-container.active .popup {
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
.popup-container .popup {
|
||||
width: 300px;
|
||||
}
|
||||
|
||||
.popup-head {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.popup-body {
|
||||
padding: 0px;
|
||||
}
|
||||
|
||||
.bct {
|
||||
background-color: transparent !important;
|
||||
margin-top: -25px;
|
||||
padding-bottom: 30px;
|
||||
right: -25px !important;
|
||||
border-width: 0px;
|
||||
}
|
||||
|
||||
.r0 {
|
||||
right: 0px !important;
|
||||
}
|
||||
|
||||
.item-toggle .toggle {
|
||||
right: 28px;
|
||||
}
|
||||
|
||||
.toggle-label {
|
||||
color: rgb(41, 55, 68);
|
||||
font-size: 14px;
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
button, .button {
|
||||
min-width: inherit;
|
||||
min-height: inherit;
|
||||
text-overflow: inherit;
|
||||
}
|
||||
|
||||
.modal-open {
|
||||
pointer-events: inherit;
|
||||
}
|
||||
|
||||
/* Defeat Ionic .row+.row in transaction history */
|
||||
.last-transactions-content+.last-transactions-content, .row+.last-transactions-content {
|
||||
padding: 0.8rem 1rem;
|
||||
cursor: pointer;
|
||||
margin: inherit;
|
||||
}
|
||||
|
||||
/* Add margins to the session log */
|
||||
.row.columns.large-centered.medium-centered {
|
||||
padding-left: 0.9375rem;
|
||||
padding-right: 0.9375rem;
|
||||
}
|
||||
|
||||
/* Defeat Ionic .row+.row in backup failed view */
|
||||
.backup .row {
|
||||
margin-top: 10px;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/* Override the default modal size for large devices */
|
||||
@media (min-width: 680px) {
|
||||
.modal {
|
||||
top: 10%;
|
||||
right: 20%;
|
||||
bottom: 10%;
|
||||
left: 20%;
|
||||
min-height: 240px;
|
||||
width: 60%;
|
||||
}
|
||||
}
|
||||
|
|
@ -6,11 +6,10 @@ var modules = [
|
|||
'mm.foundation',
|
||||
'monospaced.qrcode',
|
||||
'gettext',
|
||||
'ionic',
|
||||
'ngLodash',
|
||||
'uiSwitch',
|
||||
'ngSanitize',
|
||||
'ngSanitize',
|
||||
'ngCsv',
|
||||
'ngTouch',
|
||||
'bwcModule',
|
||||
'copayApp.filters',
|
||||
'copayApp.services',
|
||||
|
|
|
|||
|
|
@ -16,14 +16,11 @@ angular.module('copayApp.controllers').controller('backupController',
|
|||
});
|
||||
};
|
||||
|
||||
if (fc.credentials && !fc.credentials.mnemonicEncrypted && !fc.credentials.mnemonic)
|
||||
self.deleted = true;
|
||||
|
||||
if (fc.isPrivKeyEncrypted() && !self.deleted) {
|
||||
if (fc.isPrivKeyEncrypted() && !isDeletedSeed()) {
|
||||
self.credentialsEncrypted = true;
|
||||
passwordRequest();
|
||||
} else {
|
||||
if (!self.deleted)
|
||||
if (!isDeletedSeed())
|
||||
initWords();
|
||||
}
|
||||
|
||||
|
|
@ -34,12 +31,18 @@ angular.module('copayApp.controllers').controller('backupController',
|
|||
self.shuffledMnemonicWords = shuffledWords(self.mnemonicWords);
|
||||
self.customWords = [];
|
||||
self.step = 1;
|
||||
self.deleted = false;
|
||||
self.deleted = isDeletedSeed();
|
||||
self.credentialsEncrypted = false;
|
||||
self.selectComplete = false;
|
||||
self.backupError = false;
|
||||
};
|
||||
|
||||
function isDeletedSeed() {
|
||||
if (lodash.isEmpty(fc.credentials.mnemonic) && lodash.isEmpty(fc.credentials.mnemonicEncrypted))
|
||||
return true;
|
||||
return false;
|
||||
};
|
||||
|
||||
self.goToStep = function(n) {
|
||||
self.step = n;
|
||||
if (self.step == 1)
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
'use strict';
|
||||
|
||||
angular.module('copayApp.controllers').controller('buyCoinbaseController',
|
||||
function($scope, $modal, $log, $timeout, lodash, profileService, coinbaseService, animationService, bwsError, addressService, walletService) {
|
||||
function($scope, $modal, $log, $timeout, lodash, profileService, coinbaseService, animationService, bwsError, addressService) {
|
||||
|
||||
window.ignoreMobilePause = true;
|
||||
var self = this;
|
||||
|
|
@ -72,7 +72,7 @@ angular.module('copayApp.controllers').controller('buyCoinbaseController',
|
|||
|
||||
$scope.selectWallet = function(walletId, walletName) {
|
||||
var client = profileService.getClient(walletId);
|
||||
walletService.isReady(client, function(err) {
|
||||
profileService.isReady(client, function(err) {
|
||||
if (err) {
|
||||
self.error = {errors: [{ message: err }]};
|
||||
$modalInstance.dismiss('cancel');
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
'use strict';
|
||||
|
||||
angular.module('copayApp.controllers').controller('buyGlideraController',
|
||||
function($scope, $timeout, $modal, profileService, addressService, glideraService, bwsError, lodash, animationService, walletService) {
|
||||
function($scope, $timeout, $modal, profileService, addressService, glideraService, bwsError, lodash, animationService) {
|
||||
|
||||
var self = this;
|
||||
this.show2faCodeInput = null;
|
||||
|
|
@ -51,7 +51,7 @@ angular.module('copayApp.controllers').controller('buyGlideraController',
|
|||
|
||||
$scope.selectWallet = function(walletId, walletName) {
|
||||
var client = profileService.getClient(walletId);
|
||||
walletService.isReady(client, function(err) {
|
||||
profileService.isReady(client, function(err) {
|
||||
if (err) {
|
||||
self.error = err;
|
||||
$modalInstance.dismiss('cancel');
|
||||
|
|
|
|||
|
|
@ -1,23 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
angular.module('copayApp.controllers').controller('confirmTxController', function(configService, feeService, rateService) {
|
||||
|
||||
|
||||
this.processFee = function(amount, fee) {
|
||||
var walletSettings = configService.getSync().wallet.settings;
|
||||
var feeAlternativeIsoCode = walletSettings.alternativeIsoCode;
|
||||
|
||||
this.feeLevel = feeService.feeOpts[feeService.getCurrentFeeLevel()];
|
||||
this.feeAlternativeStr = parseFloat((rateService.toFiat(fee, feeAlternativeIsoCode)).toFixed(2), 10) + ' ' + feeAlternativeIsoCode;
|
||||
this.feeRateStr = (fee / (amount + fee) * 100).toFixed(2) + '%' ;
|
||||
};
|
||||
|
||||
this.close = function(cb) {
|
||||
return cb();
|
||||
};
|
||||
|
||||
this.accept = function(cb) {
|
||||
return cb(true);
|
||||
};
|
||||
|
||||
});
|
||||
|
|
@ -54,15 +54,15 @@ angular.module('copayApp.controllers').controller('copayersController',
|
|||
|
||||
modalInstance.result.then(function(ok) {
|
||||
if (ok) {
|
||||
_deleteWallet();
|
||||
doDeleteWallet();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
var _deleteWallet = function() {
|
||||
var doDeleteWallet = function() {
|
||||
var fc = profileService.focusedClient;
|
||||
var walletName = fc.credentials.walletName;
|
||||
profileService.deleteWalletFC({}, function(err) {
|
||||
profileService.deleteWalletClient(fc, function(err) {
|
||||
if (err) {
|
||||
self.error = err.message || err;
|
||||
$timeout(function() {
|
||||
|
|
|
|||
|
|
@ -1,13 +1,12 @@
|
|||
'use strict';
|
||||
|
||||
angular.module('copayApp.controllers').controller('createController',
|
||||
function($scope, $location, $anchorScroll, $rootScope, $timeout, $log, lodash, go, profileService, configService, gettext, ledger, trezor, platformInfo, derivationPathHelper) {
|
||||
function($scope, $ionicScrollDelegate, $rootScope, $timeout, $log, lodash, go, profileService, configService, gettext, ledger, trezor, platformInfo, derivationPathHelper) {
|
||||
|
||||
var isChromeApp = platformInfo.isChromeApp;
|
||||
var isCordova = platformInfo.isCordova;
|
||||
var isDevel = platformInfo.isDevel;
|
||||
|
||||
|
||||
var self = this;
|
||||
var defaults = configService.getDefaults();
|
||||
this.isWindowsPhoneApp = platformInfo.isWP && isCordova;
|
||||
|
|
@ -79,7 +78,6 @@ angular.module('copayApp.controllers').controller('createController',
|
|||
self.seedSourceId = $scope.seedSource.id;
|
||||
};
|
||||
|
||||
|
||||
this.setSeedSource = function(src) {
|
||||
self.seedSourceId = $scope.seedSource.id;
|
||||
|
||||
|
|
@ -91,6 +89,7 @@ angular.module('copayApp.controllers').controller('createController',
|
|||
this.create = function(form) {
|
||||
if (form && form.$invalid) {
|
||||
this.error = gettext('Please enter the required fields');
|
||||
$ionicScrollDelegate.scrollTop();
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -99,7 +98,7 @@ angular.module('copayApp.controllers').controller('createController',
|
|||
n: $scope.totalCopayers,
|
||||
name: $scope.walletName,
|
||||
myName: $scope.totalCopayers > 1 ? $scope.myName : null,
|
||||
networkName: $scope.isTestnet ? 'testnet' : 'livenet',
|
||||
networkName: $scope.testnetEnabled ? 'testnet' : 'livenet',
|
||||
bwsurl: $scope.bwsurl,
|
||||
walletPrivKey: $scope._walletPrivKey, // Only for testing
|
||||
};
|
||||
|
|
@ -117,6 +116,7 @@ angular.module('copayApp.controllers').controller('createController',
|
|||
var pathData = derivationPathHelper.parse($scope.derivationPath);
|
||||
if (!pathData) {
|
||||
this.error = gettext('Invalid derivation path');
|
||||
$ionicScrollDelegate.scrollTop();
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -130,6 +130,7 @@ angular.module('copayApp.controllers').controller('createController',
|
|||
|
||||
if (setSeed && !opts.mnemonic && !opts.extendedPrivateKey) {
|
||||
this.error = gettext('Please enter the wallet recovery phrase');
|
||||
$ionicScrollDelegate.scrollTop();
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -137,10 +138,11 @@ angular.module('copayApp.controllers').controller('createController',
|
|||
var account = $scope.account;
|
||||
if (!account || account < 1) {
|
||||
this.error = gettext('Invalid account number');
|
||||
$ionicScrollDelegate.scrollTop();
|
||||
return;
|
||||
}
|
||||
|
||||
if ( self.seedSourceId == 'trezor')
|
||||
if (self.seedSourceId == 'trezor')
|
||||
account = account - 1;
|
||||
|
||||
opts.account = account;
|
||||
|
|
@ -151,6 +153,7 @@ angular.module('copayApp.controllers').controller('createController',
|
|||
self.hwWallet = false;
|
||||
if (err) {
|
||||
self.error = err;
|
||||
$ionicScrollDelegate.scrollTop();
|
||||
$scope.$apply();
|
||||
return;
|
||||
}
|
||||
|
|
@ -166,12 +169,12 @@ angular.module('copayApp.controllers').controller('createController',
|
|||
self.loading = true;
|
||||
$timeout(function() {
|
||||
|
||||
profileService.createWallet(opts, function(err, walletId) {
|
||||
profileService.createWallet(opts, function(err) {
|
||||
self.loading = false;
|
||||
if (err) {
|
||||
$log.warn(err);
|
||||
self.error = err;
|
||||
scrollUp('notification');
|
||||
$ionicScrollDelegate.scrollTop();
|
||||
$timeout(function() {
|
||||
$rootScope.$apply();
|
||||
});
|
||||
|
|
@ -183,12 +186,6 @@ angular.module('copayApp.controllers').controller('createController',
|
|||
}, 100);
|
||||
}
|
||||
|
||||
function scrollUp(location){
|
||||
if(!location) return;
|
||||
$location.hash(location);
|
||||
$anchorScroll();
|
||||
};
|
||||
|
||||
this.formFocus = function(what) {
|
||||
if (!this.isWindowsPhoneApp) return
|
||||
|
||||
|
|
|
|||
|
|
@ -1,49 +1,53 @@
|
|||
'use strict';
|
||||
|
||||
angular.module('copayApp.controllers').controller('disclaimerController',
|
||||
function($scope, $timeout, $log, profileService, applicationService, gettextCatalog, uxLanguage, go) {
|
||||
function($scope, $timeout, $log, $ionicSideMenuDelegate, profileService, applicationService, gettextCatalog, uxLanguage, go) {
|
||||
var self = this;
|
||||
self.tries = 0;
|
||||
$scope.creatingProfile = true;
|
||||
|
||||
var create = function(noWallet) {
|
||||
profileService.create({
|
||||
noWallet: noWallet
|
||||
}, function(err) {
|
||||
self.creatingProfile = true;
|
||||
|
||||
var create = function(opts) {
|
||||
opts = opts || {};
|
||||
$log.debug('Creating profile');
|
||||
profileService.create(opts, function(err) {
|
||||
if (err) {
|
||||
$log.warn(err);
|
||||
$scope.error = err;
|
||||
$scope.$apply();
|
||||
$timeout(function() {
|
||||
|
||||
return $timeout(function() {
|
||||
$log.warn('Retrying to create profile......');
|
||||
if (self.tries == 3) {
|
||||
self.tries == 0;
|
||||
create(true);
|
||||
return create({
|
||||
noWallet: true
|
||||
});
|
||||
} else {
|
||||
self.tries += 1;
|
||||
create(false);
|
||||
return create();
|
||||
}
|
||||
}, 3000);
|
||||
} else {
|
||||
$scope.error = "";
|
||||
$scope.creatingProfile = false;
|
||||
}
|
||||
};
|
||||
|
||||
$scope.error = "";
|
||||
self.creatingProfile = false;
|
||||
});
|
||||
};
|
||||
|
||||
this.init = function() {
|
||||
this.init = function(opts) {
|
||||
$ionicSideMenuDelegate.canDragContent(false);
|
||||
self.lang = uxLanguage.currentLanguage;
|
||||
|
||||
profileService.getProfile(function(err, profile) {
|
||||
if (!profile) {
|
||||
create(false);
|
||||
create(opts);
|
||||
} else {
|
||||
$log.debug('There is a profile already');
|
||||
$scope.creatingProfile = false;
|
||||
$log.info('There is already a profile');
|
||||
self.creatingProfile = false;
|
||||
profileService.bindProfile(profile, function(err) {
|
||||
if (!err || !err.message || !err.message.match('NONAGREEDDISCLAIMER')) {
|
||||
$log.debug('Disclaimer already accepted at #disclaimer. Redirect to Wallet Home.')
|
||||
$log.debug('Disclaimer already accepted at #disclaimer. Redirect to Wallet Home.');
|
||||
$ionicSideMenuDelegate.canDragContent(true);
|
||||
go.walletHome();
|
||||
}
|
||||
});
|
||||
|
|
@ -54,7 +58,10 @@ angular.module('copayApp.controllers').controller('disclaimerController',
|
|||
this.accept = function() {
|
||||
profileService.setDisclaimerAccepted(function(err) {
|
||||
if (err) $log.error(err);
|
||||
else go.walletHome();
|
||||
else {
|
||||
$ionicSideMenuDelegate.canDragContent(true);
|
||||
go.walletHome();
|
||||
}
|
||||
});
|
||||
};
|
||||
});
|
||||
|
|
|
|||
|
|
@ -8,18 +8,18 @@ angular.module('copayApp.controllers').controller('exportController',
|
|||
|
||||
self.error = null;
|
||||
self.success = null;
|
||||
$scope.metaData = true;
|
||||
$scope.metaDataEnabled = true;
|
||||
var fc = profileService.focusedClient;
|
||||
self.isEncrypted = fc.isPrivKeyEncrypted();
|
||||
|
||||
self.downloadWalletBackup = function() {
|
||||
self.getMetaData($scope.metaData, function(err, txsFromLocal, localAddressBook) {
|
||||
self.getMetaData($scope.metaDataEnabled, function(err, txsFromLocal, localAddressBook) {
|
||||
if (err) {
|
||||
self.error = true;
|
||||
return;
|
||||
}
|
||||
var opts = {
|
||||
noSign: $scope.noSign,
|
||||
noSign: $scope.noSignEnabled,
|
||||
historyCache: txsFromLocal,
|
||||
addressBook: localAddressBook
|
||||
};
|
||||
|
|
@ -29,7 +29,6 @@ angular.module('copayApp.controllers').controller('exportController',
|
|||
self.error = true;
|
||||
return;
|
||||
}
|
||||
|
||||
$rootScope.$emit('Local/BackupDone');
|
||||
notification.success(gettext('Success'), gettext('Encrypted export file saved'));
|
||||
go.walletHome();
|
||||
|
|
@ -83,13 +82,13 @@ angular.module('copayApp.controllers').controller('exportController',
|
|||
}
|
||||
|
||||
self.getBackup = function(cb) {
|
||||
self.getMetaData($scope.metaData, function(err, txsFromLocal, localAddressBook) {
|
||||
self.getMetaData($scope.metaDataEnabled, function(err, txsFromLocal, localAddressBook) {
|
||||
if (err) {
|
||||
self.error = true;
|
||||
return cb(null);
|
||||
}
|
||||
var opts = {
|
||||
noSign: $scope.noSign,
|
||||
noSign: $scope.noSignEnabled,
|
||||
historyCache: txsFromLocal,
|
||||
addressBook: localAddressBook
|
||||
};
|
||||
|
|
@ -139,7 +138,7 @@ angular.module('copayApp.controllers').controller('exportController',
|
|||
var ew = backup;
|
||||
if (!ew) return;
|
||||
|
||||
if ($scope.noSign)
|
||||
if ($scope.noSignEnabled)
|
||||
name = name + '(No Private Key)';
|
||||
|
||||
var subject = 'Copay Wallet Backup: ' + name;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
'use strict';
|
||||
|
||||
angular.module('copayApp.controllers').controller('importController',
|
||||
function($scope, $rootScope, $location, $timeout, $log, profileService, configService, notification, go, sjcl, gettext, lodash, ledger, trezor, derivationPathHelper, platformInfo) {
|
||||
function($scope, $rootScope, $ionicScrollDelegate, $timeout, $log, profileService, configService, notification, go, sjcl, gettext, lodash, ledger, trezor, derivationPathHelper, platformInfo) {
|
||||
|
||||
var isChromeApp = platformInfo.isChromeApp;
|
||||
var isDevel = platformInfo.isDevel;
|
||||
|
|
@ -39,8 +39,6 @@ angular.module('copayApp.controllers').controller('importController',
|
|||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
this.setType = function(type) {
|
||||
$scope.type = type;
|
||||
this.error = null;
|
||||
|
|
@ -60,6 +58,7 @@ angular.module('copayApp.controllers').controller('importController',
|
|||
|
||||
if (err) {
|
||||
self.error = err;
|
||||
$ionicScrollDelegate.scrollTop();
|
||||
$timeout(function() {
|
||||
$rootScope.$apply();
|
||||
});
|
||||
|
|
@ -75,6 +74,7 @@ angular.module('copayApp.controllers').controller('importController',
|
|||
self.loading = false;
|
||||
if (err) {
|
||||
self.error = err;
|
||||
$ionicScrollDelegate.scrollTop();
|
||||
} else {
|
||||
$rootScope.$emit('Local/WalletImported', walletId);
|
||||
notification.success(gettext('Success'), gettext('Your wallet has been imported correctly'));
|
||||
|
|
@ -92,6 +92,7 @@ angular.module('copayApp.controllers').controller('importController',
|
|||
self.loading = false;
|
||||
if (err) {
|
||||
self.error = err;
|
||||
$ionicScrollDelegate.scrollTop();
|
||||
return $timeout(function() {
|
||||
$scope.$apply();
|
||||
});
|
||||
|
|
@ -111,6 +112,7 @@ angular.module('copayApp.controllers').controller('importController',
|
|||
self.loading = false;
|
||||
if (err) {
|
||||
self.error = err;
|
||||
$ionicScrollDelegate.scrollTop();
|
||||
return $timeout(function() {
|
||||
$scope.$apply();
|
||||
});
|
||||
|
|
@ -136,7 +138,7 @@ angular.module('copayApp.controllers').controller('importController',
|
|||
this.importBlob = function(form) {
|
||||
if (form.$invalid) {
|
||||
this.error = gettext('There is an error in the form');
|
||||
|
||||
$ionicScrollDelegate.scrollTop();
|
||||
$timeout(function() {
|
||||
$scope.$apply();
|
||||
});
|
||||
|
|
@ -149,6 +151,7 @@ angular.module('copayApp.controllers').controller('importController',
|
|||
|
||||
if (!backupFile && !backupText) {
|
||||
this.error = gettext('Please, select your backup file');
|
||||
$ionicScrollDelegate.scrollTop();
|
||||
$timeout(function() {
|
||||
$scope.$apply();
|
||||
});
|
||||
|
|
@ -168,7 +171,7 @@ angular.module('copayApp.controllers').controller('importController',
|
|||
this.importMnemonic = function(form) {
|
||||
if (form.$invalid) {
|
||||
this.error = gettext('There is an error in the form');
|
||||
|
||||
$ionicScrollDelegate.scrollTop();
|
||||
$timeout(function() {
|
||||
$scope.$apply();
|
||||
});
|
||||
|
|
@ -183,6 +186,7 @@ angular.module('copayApp.controllers').controller('importController',
|
|||
var pathData = derivationPathHelper.parse($scope.derivationPath);
|
||||
if (!pathData) {
|
||||
this.error = gettext('Invalid derivation path');
|
||||
$ionicScrollDelegate.scrollTop();
|
||||
return;
|
||||
}
|
||||
opts.account = pathData.account;
|
||||
|
|
@ -194,13 +198,16 @@ angular.module('copayApp.controllers').controller('importController',
|
|||
|
||||
if (!words) {
|
||||
this.error = gettext('Please enter the recovery phrase');
|
||||
$ionicScrollDelegate.scrollTop();
|
||||
} else if (words.indexOf('xprv') == 0 || words.indexOf('tprv') == 0) {
|
||||
return _importExtendedPrivateKey(words, opts);
|
||||
} else {
|
||||
var wordList = words.split(/[\u3000\s]+/);
|
||||
|
||||
if ((wordList.length % 3) != 0)
|
||||
if ((wordList.length % 3) != 0) {
|
||||
this.error = gettext('Wrong number of recovery words:') + wordList.length;
|
||||
$ionicScrollDelegate.scrollTop();
|
||||
}
|
||||
}
|
||||
|
||||
if (this.error) {
|
||||
|
|
@ -222,6 +229,7 @@ angular.module('copayApp.controllers').controller('importController',
|
|||
self.hwWallet = false;
|
||||
if (err) {
|
||||
self.error = err;
|
||||
$ionicScrollDelegate.scrollTop();
|
||||
$scope.$apply();
|
||||
return;
|
||||
}
|
||||
|
|
@ -235,6 +243,7 @@ angular.module('copayApp.controllers').controller('importController',
|
|||
self.loading = false;
|
||||
if (err) {
|
||||
self.error = err;
|
||||
$ionicScrollDelegate.scrollTop();
|
||||
return $timeout(function() {
|
||||
$scope.$apply();
|
||||
});
|
||||
|
|
@ -247,8 +256,9 @@ angular.module('copayApp.controllers').controller('importController',
|
|||
};
|
||||
|
||||
this.importHW = function(form) {
|
||||
if (form.$invalid || $scope.account < 0 ) {
|
||||
if (form.$invalid || $scope.account < 0) {
|
||||
this.error = gettext('There is an error in the form');
|
||||
$ionicScrollDelegate.scrollTop();
|
||||
$timeout(function() {
|
||||
$scope.$apply();
|
||||
});
|
||||
|
|
@ -256,11 +266,12 @@ angular.module('copayApp.controllers').controller('importController',
|
|||
}
|
||||
this.error = '';
|
||||
|
||||
var account = + $scope.account;
|
||||
|
||||
var account = +$scope.account;
|
||||
|
||||
if (self.seedSourceId == 'trezor') {
|
||||
if ( account < 1) {
|
||||
if (account < 1) {
|
||||
this.error = gettext('Invalid account number');
|
||||
$ionicScrollDelegate.scrollTop();
|
||||
return;
|
||||
}
|
||||
account = account - 1;
|
||||
|
|
@ -296,6 +307,7 @@ angular.module('copayApp.controllers').controller('importController',
|
|||
self.hwWallet = false;
|
||||
if (err) {
|
||||
self.error = err;
|
||||
$ionicScrollDelegate.scrollTop();
|
||||
$scope.$apply();
|
||||
return;
|
||||
}
|
||||
|
|
@ -309,6 +321,7 @@ angular.module('copayApp.controllers').controller('importController',
|
|||
self.loading = false;
|
||||
if (err) {
|
||||
self.error = err;
|
||||
$ionicScrollDelegate.scrollTop();
|
||||
return $timeout(function() {
|
||||
$scope.$apply();
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
'use strict';
|
||||
|
||||
angular.module('copayApp.controllers').controller('indexController', function($rootScope, $scope, $log, $filter, $timeout, latestReleaseService, bwcService, pushNotificationsService, lodash, go, profileService, configService, rateService, storageService, addressService, gettext, gettextCatalog, amMoment, addonManager, bwsError, txFormatService, uxLanguage, glideraService, coinbaseService, platformInfo, addressbookService, walletService) {
|
||||
angular.module('copayApp.controllers').controller('indexController', function($rootScope, $scope, $log, $filter, $timeout, $ionicSideMenuDelegate, $ionicScrollDelegate, $ionicPopup, latestReleaseService, feeService, bwcService, pushNotificationsService, lodash, go, profileService, configService, rateService, storageService, addressService, gettext, gettextCatalog, amMoment, addonManager, bwsError, txFormatService, uxLanguage, glideraService, coinbaseService, platformInfo, addressbookService) {
|
||||
var self = this;
|
||||
var SOFT_CONFIRMATION_LIMIT = 12;
|
||||
var errors = bwcService.getErrors();
|
||||
|
|
@ -16,7 +16,7 @@ angular.module('copayApp.controllers').controller('indexController', function($r
|
|||
ret.isWindowsPhoneApp = platformInfo.isWP;
|
||||
ret.onGoingProcess = {};
|
||||
ret.historyShowLimit = 10;
|
||||
ret.historyShowMoreLimit = 100;
|
||||
ret.historyShowMoreLimit = 10;
|
||||
ret.isSearching = false;
|
||||
ret.prevState = 'walletHome';
|
||||
|
||||
|
|
@ -69,6 +69,23 @@ angular.module('copayApp.controllers').controller('indexController', function($r
|
|||
go.walletHome();
|
||||
};
|
||||
|
||||
self.onDrag = function() {
|
||||
if (!isCordova)
|
||||
$ionicSideMenuDelegate.canDragContent(false);
|
||||
};
|
||||
|
||||
self.hideBalance = function() {
|
||||
storageService.getHideBalanceFlag(self.walletId, function(err, shouldHideBalance) {
|
||||
if (err) self.shouldHideBalance = false;
|
||||
else self.shouldHideBalance = (shouldHideBalance == 'true') ? true : false;
|
||||
});
|
||||
}
|
||||
|
||||
self.onHold = function() {
|
||||
self.shouldHideBalance = !self.shouldHideBalance;
|
||||
storageService.setHideBalanceFlag(self.walletId, self.shouldHideBalance, function() {});
|
||||
}
|
||||
|
||||
self.setOngoingProcess = function(processName, isOn) {
|
||||
$log.debug('onGoingProcess', processName, isOn);
|
||||
self[processName] = isOn;
|
||||
|
|
@ -153,6 +170,8 @@ angular.module('copayApp.controllers').controller('indexController', function($r
|
|||
self.initGlidera();
|
||||
self.initCoinbase();
|
||||
|
||||
self.hideBalance();
|
||||
|
||||
self.setCustomBWSFlag();
|
||||
|
||||
if (!self.isComplete) {
|
||||
|
|
@ -165,7 +184,7 @@ angular.module('copayApp.controllers').controller('indexController', function($r
|
|||
}
|
||||
}
|
||||
|
||||
walletService.isBackupNeeded(fc, function(needsBackup) {
|
||||
profileService.needsBackup(fc, function(needsBackup) {
|
||||
self.needsBackup = needsBackup;
|
||||
self.openWallet(function() {
|
||||
if (!self.isComplete) {
|
||||
|
|
@ -249,53 +268,6 @@ angular.module('copayApp.controllers').controller('indexController', function($r
|
|||
};
|
||||
|
||||
|
||||
self._updateRemotePreferencesFor = function(clients, prefs, cb) {
|
||||
var client = clients.shift();
|
||||
|
||||
if (!client)
|
||||
return cb();
|
||||
|
||||
$log.debug('Saving remote preferences', client.credentials.walletName, prefs);
|
||||
client.savePreferences(prefs, function(err) {
|
||||
// we ignore errors here
|
||||
if (err) $log.warn(err);
|
||||
|
||||
self._updateRemotePreferencesFor(clients, prefs, cb);
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
self.updateRemotePreferences = function(opts, cb) {
|
||||
var prefs = opts.preferences || {};
|
||||
var fc = profileService.focusedClient;
|
||||
|
||||
// Update this JIC.
|
||||
var config = configService.getSync().wallet.settings;
|
||||
|
||||
//prefs.email (may come from arguments)
|
||||
prefs.language = self.defaultLanguageIsoCode;
|
||||
prefs.unit = config.unitCode;
|
||||
|
||||
var clients = [];
|
||||
if (opts.saveAll) {
|
||||
clients = lodash.values(profileService.walletClients);
|
||||
} else {
|
||||
clients = [fc];
|
||||
};
|
||||
|
||||
self._updateRemotePreferencesFor(clients, prefs, function(err) {
|
||||
if (err) return cb(err);
|
||||
if (!fc) return cb();
|
||||
|
||||
fc.getPreferences(function(err, preferences) {
|
||||
if (err) {
|
||||
return cb(err);
|
||||
}
|
||||
self.preferences = preferences;
|
||||
return cb();
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
var _walletStatusHash = function(walletStatus) {
|
||||
var bal;
|
||||
|
|
@ -307,7 +279,9 @@ angular.module('copayApp.controllers').controller('indexController', function($r
|
|||
return bal;
|
||||
};
|
||||
|
||||
// TODO move this to wallet service
|
||||
self.updateAll = function(opts, initStatusHash, tries) {
|
||||
$scope.$broadcast('scroll.refreshComplete');
|
||||
tries = tries || 0;
|
||||
opts = opts || {};
|
||||
var fc = profileService.focusedClient;
|
||||
|
|
@ -575,6 +549,11 @@ angular.module('copayApp.controllers').controller('indexController', function($r
|
|||
self.hasUnsafeConfirmed = true;
|
||||
}
|
||||
|
||||
if (tx.note) {
|
||||
delete tx.note.encryptedEditedByName;
|
||||
delete tx.note.encryptedBody;
|
||||
}
|
||||
|
||||
if (!txHistoryUnique[tx.txid]) {
|
||||
ret.push(tx);
|
||||
txHistoryUnique[tx.txid] = true;
|
||||
|
|
@ -677,148 +656,6 @@ angular.module('copayApp.controllers').controller('indexController', function($r
|
|||
}
|
||||
};
|
||||
|
||||
self.csvHistory = function() {
|
||||
|
||||
function saveFile(name, data) {
|
||||
var chooser = document.querySelector(name);
|
||||
chooser.addEventListener("change", function(evt) {
|
||||
var fs = require('fs');
|
||||
fs.writeFile(this.value, data, function(err) {
|
||||
if (err) {
|
||||
$log.debug(err);
|
||||
}
|
||||
});
|
||||
}, false);
|
||||
chooser.click();
|
||||
}
|
||||
|
||||
function formatDate(date) {
|
||||
var dateObj = new Date(date);
|
||||
if (!dateObj) {
|
||||
$log.debug('Error formating a date');
|
||||
return 'DateError'
|
||||
}
|
||||
if (!dateObj.toJSON()) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return dateObj.toJSON();
|
||||
}
|
||||
|
||||
function formatString(str) {
|
||||
if (!str) return '';
|
||||
|
||||
if (str.indexOf('"') !== -1) {
|
||||
//replace all
|
||||
str = str.replace(new RegExp('"', 'g'), '\'');
|
||||
}
|
||||
|
||||
//escaping commas
|
||||
str = '\"' + str + '\"';
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
var step = 6;
|
||||
var unique = {};
|
||||
|
||||
function getHistory(cb) {
|
||||
storageService.getTxHistory(c.walletId, function(err, txs) {
|
||||
if (err) return cb(err);
|
||||
|
||||
var txsFromLocal = [];
|
||||
try {
|
||||
txsFromLocal = JSON.parse(txs);
|
||||
} catch (ex) {
|
||||
$log.warn(ex);
|
||||
}
|
||||
|
||||
allTxs.push(txsFromLocal);
|
||||
return cb(null, lodash.flatten(allTxs));
|
||||
});
|
||||
}
|
||||
|
||||
if (isCordova) {
|
||||
$log.info('CSV generation not available in mobile');
|
||||
return;
|
||||
}
|
||||
var fc = profileService.focusedClient;
|
||||
var c = fc.credentials;
|
||||
if (!fc.isComplete()) return;
|
||||
var self = this;
|
||||
var allTxs = [];
|
||||
|
||||
$log.debug('Generating CSV from History');
|
||||
self.setOngoingProcess('generatingCSV', true);
|
||||
|
||||
getHistory(function(err, txs) {
|
||||
self.setOngoingProcess('generatingCSV', false);
|
||||
if (err) {
|
||||
self.handleError(err);
|
||||
} else {
|
||||
$log.debug('Wallet Transaction History:', txs);
|
||||
|
||||
self.satToUnit = 1 / self.unitToSatoshi;
|
||||
var data = txs;
|
||||
var satToBtc = 1 / 100000000;
|
||||
self.csvContent = [];
|
||||
self.csvFilename = 'Copay-' + (self.alias || self.walletName) + '.csv';
|
||||
self.csvHeader = ['Date', 'Destination', 'Note', 'Amount', 'Currency', 'Txid', 'Creator', 'Copayers'];
|
||||
|
||||
var _amount, _note, _copayers, _creator;
|
||||
data.forEach(function(it, index) {
|
||||
var amount = it.amount;
|
||||
|
||||
if (it.action == 'moved')
|
||||
amount = 0;
|
||||
|
||||
_copayers = '';
|
||||
_creator = '';
|
||||
|
||||
if (it.actions && it.actions.length > 1) {
|
||||
for (var i = 0; i < it.actions.length; i++) {
|
||||
_copayers += it.actions[i].copayerName + ':' + it.actions[i].type + ' - ';
|
||||
}
|
||||
_creator = (it.creatorName && it.creatorName != 'undefined') ? it.creatorName : '';
|
||||
}
|
||||
_copayers = formatString(_copayers);
|
||||
_creator = formatString(_creator);
|
||||
_amount = (it.action == 'sent' ? '-' : '') + (amount * satToBtc).toFixed(8);
|
||||
_note = formatString((it.message ? it.message : ''));
|
||||
|
||||
if (it.action == 'moved')
|
||||
_note += ' Moved:' + (it.amount * satToBtc).toFixed(8)
|
||||
|
||||
self.csvContent.push({
|
||||
'Date': formatDate(it.time * 1000),
|
||||
'Destination': formatString(it.addressTo),
|
||||
'Note': _note,
|
||||
'Amount': _amount,
|
||||
'Currency': 'BTC',
|
||||
'Txid': it.txid,
|
||||
'Creator': _creator,
|
||||
'Copayers': _copayers
|
||||
});
|
||||
|
||||
if (it.fees && (it.action == 'moved' || it.action == 'sent')) {
|
||||
var _fee = (it.fees * satToBtc).toFixed(8)
|
||||
self.csvContent.push({
|
||||
'Date': formatDate(it.time * 1000),
|
||||
'Destination': 'Bitcoin Network Fees',
|
||||
'Note': '',
|
||||
'Amount': '-' + _fee,
|
||||
'Currency': 'BTC',
|
||||
'Txid': '',
|
||||
'Creator': '',
|
||||
'Copayers': ''
|
||||
});
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
self.removeAndMarkSoftConfirmedTx = function(txs) {
|
||||
return lodash.filter(txs, function(tx) {
|
||||
if (tx.confirmations >= SOFT_CONFIRMATION_LIMIT)
|
||||
|
|
@ -879,6 +716,7 @@ angular.module('copayApp.controllers').controller('indexController', function($r
|
|||
|
||||
var confirmedTxs = self.removeAndMarkSoftConfirmedTx(txsFromLocal);
|
||||
var endingTxid = confirmedTxs[0] ? confirmedTxs[0].txid : null;
|
||||
var endingTs = confirmedTxs[0] ? confirmedTxs[0].time : null;
|
||||
|
||||
|
||||
// First update
|
||||
|
|
@ -933,24 +771,52 @@ angular.module('copayApp.controllers').controller('indexController', function($r
|
|||
var newHistory = lodash.uniq(lodash.compact(txs.concat(confirmedTxs)), function(x) {
|
||||
return x.txid;
|
||||
});
|
||||
var historyToSave = JSON.stringify(newHistory);
|
||||
|
||||
lodash.each(txs, function(tx) {
|
||||
tx.recent = true;
|
||||
})
|
||||
|
||||
$log.debug('Tx History synced. Total Txs: ' + newHistory.length);
|
||||
function updateNotes(cb2) {
|
||||
if (!endingTs) return cb2();
|
||||
|
||||
// Final update
|
||||
if (walletId == profileService.focusedClient.credentials.walletId) {
|
||||
self.completeHistory = newHistory;
|
||||
self.setCompactTxHistory();
|
||||
$log.debug('Syncing notes from: ' + endingTs);
|
||||
client.getTxNotes({
|
||||
minTs: endingTs
|
||||
}, function(err, notes) {
|
||||
if (err) {
|
||||
$log.warn(err);
|
||||
return cb2();
|
||||
};
|
||||
lodash.each(notes, function(note) {
|
||||
$log.debug('Note for ' + note.txid);
|
||||
lodash.each(newHistory, function(tx) {
|
||||
if (tx.txid == note.txid) {
|
||||
$log.debug('...updating note for ' + note.txid);
|
||||
tx.note = note;
|
||||
}
|
||||
});
|
||||
});
|
||||
return cb2();
|
||||
});
|
||||
}
|
||||
|
||||
return storageService.setTxHistory(historyToSave, walletId, function() {
|
||||
$log.debug('Tx History saved.');
|
||||
updateNotes(function() {
|
||||
var historyToSave = JSON.stringify(newHistory);
|
||||
|
||||
return cb();
|
||||
lodash.each(txs, function(tx) {
|
||||
tx.recent = true;
|
||||
})
|
||||
|
||||
$log.debug('Tx History synced. Total Txs: ' + newHistory.length);
|
||||
|
||||
// Final update
|
||||
if (walletId == profileService.focusedClient.credentials.walletId) {
|
||||
self.completeHistory = newHistory;
|
||||
self.setCompactTxHistory();
|
||||
}
|
||||
|
||||
return storageService.setTxHistory(historyToSave, walletId, function() {
|
||||
$log.debug('Tx History saved.');
|
||||
|
||||
return cb();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -965,12 +831,12 @@ angular.module('copayApp.controllers').controller('indexController', function($r
|
|||
self.historyShowMore = false;
|
||||
} else {
|
||||
self.txHistory = self.completeHistory.slice(0, self.nextTxHistory);
|
||||
self.txHistorySearchResults = self.txHistory;
|
||||
$log.debug('Total txs: ', self.txHistorySearchResults.length + '/' + self.completeHistory.length);
|
||||
if (self.txHistorySearchResults.length >= self.completeHistory.length)
|
||||
$log.debug('Total txs: ', self.txHistory.length + '/' + self.completeHistory.length);
|
||||
if (self.txHistory.length >= self.completeHistory.length)
|
||||
self.historyShowMore = false;
|
||||
}
|
||||
self.nextTxHistory += self.historyShowMoreLimit;
|
||||
$scope.$broadcast('scroll.infiniteScrollComplete');
|
||||
}, 100);
|
||||
};
|
||||
|
||||
|
|
@ -993,6 +859,7 @@ angular.module('copayApp.controllers').controller('indexController', function($r
|
|||
if (isCordova)
|
||||
window.plugins.toast.hide();
|
||||
self.throttleSearch();
|
||||
$ionicScrollDelegate.resize();
|
||||
}
|
||||
|
||||
self.throttleSearch = lodash.throttle(function() {
|
||||
|
|
@ -1005,8 +872,9 @@ angular.module('copayApp.controllers').controller('indexController', function($r
|
|||
if (tx.addressTo && self.addressbook && self.addressbook[tx.addressTo]) addrbook = self.addressbook[tx.addressTo] || '';
|
||||
var searchableDate = computeSearchableDate(new Date(tx.time * 1000));
|
||||
var message = tx.message ? tx.message : '';
|
||||
var comment = tx.note ? tx.note.body : '';
|
||||
var addressTo = tx.addressTo ? tx.addressTo : '';
|
||||
return ((tx.amountStr + message + addressTo + addrbook + searchableDate).toString()).toLowerCase();
|
||||
return ((tx.amountStr + message + addressTo + addrbook + searchableDate + comment).toString()).toLowerCase();
|
||||
}
|
||||
|
||||
function computeSearchableDate(date) {
|
||||
|
|
@ -1095,7 +963,6 @@ angular.module('copayApp.controllers').controller('indexController', function($r
|
|||
self.isSearching = false;
|
||||
self.nextTxHistory = self.historyShowMoreLimit;
|
||||
self.txHistory = self.completeHistory ? self.completeHistory.slice(0, self.historyShowLimit) : null;
|
||||
self.txHistorySearchResults = self.txHistory;
|
||||
self.historyShowMore = self.completeHistory ? self.completeHistory.length > self.historyShowLimit : null;
|
||||
};
|
||||
|
||||
|
|
@ -1109,15 +976,23 @@ angular.module('copayApp.controllers').controller('indexController', function($r
|
|||
|
||||
self.showErrorPopup = function(msg, cb) {
|
||||
$log.warn('Showing err popup:' + msg);
|
||||
self.showAlert = {
|
||||
msg: msg,
|
||||
close: function() {
|
||||
self.showAlert = null;
|
||||
if (cb) return cb();
|
||||
},
|
||||
};
|
||||
$timeout(function() {
|
||||
$rootScope.$apply();
|
||||
|
||||
function openErrorPopup(msg, cb) {
|
||||
$scope.msg = msg;
|
||||
|
||||
self.errorPopup = $ionicPopup.show({
|
||||
templateUrl: 'views/includes/alert.html',
|
||||
scope: $scope,
|
||||
});
|
||||
|
||||
$scope.close = function() {
|
||||
return cb();
|
||||
};
|
||||
}
|
||||
|
||||
openErrorPopup(msg, function() {
|
||||
self.errorPopup.close();
|
||||
if (cb) return cb();
|
||||
});
|
||||
};
|
||||
|
||||
|
|
@ -1134,23 +1009,21 @@ angular.module('copayApp.controllers').controller('indexController', function($r
|
|||
return;
|
||||
}
|
||||
|
||||
profileService.setWalletClients();
|
||||
profileService.bindWalletClient(fc, {
|
||||
force: true
|
||||
});
|
||||
self.startScan(self.walletId);
|
||||
});
|
||||
};
|
||||
|
||||
self.openMenu = function() {
|
||||
self.toggleLeftMenu = function() {
|
||||
profileService.isDisclaimerAccepted(function(val) {
|
||||
if (val) go.swipe(true);
|
||||
if (val) go.toggleLeftMenu();
|
||||
else
|
||||
$log.debug('Disclaimer not accepted, cannot open menu');
|
||||
});
|
||||
};
|
||||
|
||||
self.closeMenu = function() {
|
||||
go.swipe();
|
||||
};
|
||||
|
||||
self.retryScan = function() {
|
||||
var self = this;
|
||||
self.startScan(self.walletId);
|
||||
|
|
@ -1175,14 +1048,6 @@ angular.module('copayApp.controllers').controller('indexController', function($r
|
|||
});
|
||||
};
|
||||
|
||||
self.setUxLanguage = function(cb) {
|
||||
uxLanguage.update(function(lang) {
|
||||
var userLang = lang;
|
||||
self.defaultLanguageIsoCode = userLang;
|
||||
self.defaultLanguageName = uxLanguage.getName(userLang);
|
||||
if (cb) return cb();
|
||||
});
|
||||
};
|
||||
|
||||
self.initGlidera = function(accessToken) {
|
||||
self.glideraEnabled = configService.getSync().glidera.enabled;
|
||||
|
|
@ -1354,16 +1219,19 @@ angular.module('copayApp.controllers').controller('indexController', function($r
|
|||
self.coinbasePendingTransactions = lodash.isEmpty(txs) ? null : txs;
|
||||
lodash.forEach(txs, function(dataFromStorage, txId) {
|
||||
if ((dataFromStorage.type == 'sell' && dataFromStorage.status == 'completed') ||
|
||||
(dataFromStorage.type == 'buy' && dataFromStorage.status == 'completed') ||
|
||||
dataFromStorage.status == 'error' ||
|
||||
(dataFromStorage.type == 'send' && dataFromStorage.status == 'completed')) return;
|
||||
(dataFromStorage.type == 'buy' && dataFromStorage.status == 'completed') ||
|
||||
dataFromStorage.status == 'error' ||
|
||||
(dataFromStorage.type == 'send' && dataFromStorage.status == 'completed')) return;
|
||||
coinbaseService.getTransaction(accessToken, accountId, txId, function(err, tx) {
|
||||
if (err) {
|
||||
if (err.errors[0] && err.errors[0].id == 'expired_token') {
|
||||
self.refreshCoinbaseToken();
|
||||
return;
|
||||
}
|
||||
coinbaseService.savePendingTransaction(dataFromStorage, {status: 'error', error: err}, function(err) {
|
||||
coinbaseService.savePendingTransaction(dataFromStorage, {
|
||||
status: 'error',
|
||||
error: err
|
||||
}, function(err) {
|
||||
if (err) $log.debug(err);
|
||||
});
|
||||
return;
|
||||
|
|
@ -1377,7 +1245,10 @@ angular.module('copayApp.controllers').controller('indexController', function($r
|
|||
self.refreshCoinbaseToken();
|
||||
return;
|
||||
}
|
||||
coinbaseService.savePendingTransaction(dataFromStorage, {status: 'error', error: err}, function(err) {
|
||||
coinbaseService.savePendingTransaction(dataFromStorage, {
|
||||
status: 'error',
|
||||
error: err
|
||||
}, function(err) {
|
||||
if (err) $log.debug(err);
|
||||
});
|
||||
return;
|
||||
|
|
@ -1387,8 +1258,15 @@ angular.module('copayApp.controllers').controller('indexController', function($r
|
|||
if (variance < dataFromStorage.price_sensitivity.value) {
|
||||
self.sellPending(tx.data);
|
||||
} else {
|
||||
var error = {errors: [{ message: 'Price falls over the selected percentage' }]};
|
||||
coinbaseService.savePendingTransaction(dataFromStorage, {status: 'error', error: error}, function(err) {
|
||||
var error = {
|
||||
errors: [{
|
||||
message: 'Price falls over the selected percentage'
|
||||
}]
|
||||
};
|
||||
coinbaseService.savePendingTransaction(dataFromStorage, {
|
||||
status: 'error',
|
||||
error: error
|
||||
}, function(err) {
|
||||
if (err) $log.debug(err);
|
||||
});
|
||||
}
|
||||
|
|
@ -1406,8 +1284,8 @@ angular.module('copayApp.controllers').controller('indexController', function($r
|
|||
|
||||
}, 1000);
|
||||
|
||||
var _updateCoinbasePendingTransactions = function (obj/*, …*/) {
|
||||
for (var i=1; i<arguments.length; i++) {
|
||||
var _updateCoinbasePendingTransactions = function(obj /*, …*/ ) {
|
||||
for (var i = 1; i < arguments.length; i++) {
|
||||
for (var prop in arguments[i]) {
|
||||
var val = arguments[i][prop];
|
||||
if (typeof val == "object")
|
||||
|
|
@ -1453,21 +1331,31 @@ angular.module('copayApp.controllers').controller('indexController', function($r
|
|||
self.refreshCoinbaseToken();
|
||||
return;
|
||||
}
|
||||
coinbaseService.savePendingTransaction(tx, {status: 'error', error: err}, function(err) {
|
||||
coinbaseService.savePendingTransaction(tx, {
|
||||
status: 'error',
|
||||
error: err
|
||||
}, function(err) {
|
||||
if (err) $log.debug(err);
|
||||
});
|
||||
} else {
|
||||
if (!res.data.id) {
|
||||
coinbaseService.savePendingTransaction(tx, {status: 'error', error: err}, function(err) {
|
||||
coinbaseService.savePendingTransaction(tx, {
|
||||
status: 'error',
|
||||
error: err
|
||||
}, function(err) {
|
||||
if (err) $log.debug(err);
|
||||
});
|
||||
return;
|
||||
}
|
||||
coinbaseService.getTransaction(self.coinbaseToken, self.coinbaseAccount.id, res.data.id, function(err, sendTx) {
|
||||
coinbaseService.savePendingTransaction(tx, {remove: true}, function(err) {
|
||||
coinbaseService.savePendingTransaction(tx, {
|
||||
remove: true
|
||||
}, function(err) {
|
||||
coinbaseService.savePendingTransaction(sendTx.data, {}, function(err) {
|
||||
$timeout(function() {
|
||||
self.updateCoinbase({updateAccount: true});
|
||||
self.updateCoinbase({
|
||||
updateAccount: true
|
||||
});
|
||||
}, 1000);
|
||||
});
|
||||
});
|
||||
|
|
@ -1486,22 +1374,32 @@ angular.module('copayApp.controllers').controller('indexController', function($r
|
|||
self.refreshCoinbaseToken();
|
||||
return;
|
||||
}
|
||||
coinbaseService.savePendingTransaction(tx, {status: 'error', error: err}, function(err) {
|
||||
coinbaseService.savePendingTransaction(tx, {
|
||||
status: 'error',
|
||||
error: err
|
||||
}, function(err) {
|
||||
if (err) $log.debug(err);
|
||||
});
|
||||
} else {
|
||||
if (!res.data.transaction) {
|
||||
coinbaseService.savePendingTransaction(tx, {status: 'error', error: err}, function(err) {
|
||||
coinbaseService.savePendingTransaction(tx, {
|
||||
status: 'error',
|
||||
error: err
|
||||
}, function(err) {
|
||||
if (err) $log.debug(err);
|
||||
});
|
||||
return;
|
||||
}
|
||||
coinbaseService.savePendingTransaction(tx, {remove: true}, function(err) {
|
||||
coinbaseService.savePendingTransaction(tx, {
|
||||
remove: true
|
||||
}, function(err) {
|
||||
coinbaseService.getTransaction(self.coinbaseToken, self.coinbaseAccount.id, res.data.transaction.id, function(err, updatedTx) {
|
||||
coinbaseService.savePendingTransaction(updatedTx.data, {}, function(err) {
|
||||
if (err) $log.debug(err);
|
||||
$timeout(function() {
|
||||
self.updateCoinbase({updateAccount: true});
|
||||
self.updateCoinbase({
|
||||
updateAccount: true
|
||||
});
|
||||
}, 1000);
|
||||
});
|
||||
});
|
||||
|
|
@ -1560,28 +1458,6 @@ angular.module('copayApp.controllers').controller('indexController', function($r
|
|||
self.updateAll();
|
||||
});
|
||||
|
||||
$rootScope.$on('Local/ProfileBound', function() {
|
||||
storageService.getRemotePrefsStoredFlag(function(err, val) {
|
||||
if (err || val) return;
|
||||
self.updateRemotePreferences({
|
||||
saveAll: true
|
||||
}, function() {
|
||||
$log.debug('Remote preferences saved');
|
||||
storageService.setRemotePrefsStoredFlag(function() {});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
$rootScope.$on('Local/LanguageSettingUpdated', function() {
|
||||
self.setUxLanguage(function() {
|
||||
self.updateRemotePreferences({
|
||||
saveAll: true
|
||||
}, function() {
|
||||
$log.debug('Remote preferences saved')
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
$rootScope.$on('Local/GlideraUpdated', function(event, accessToken) {
|
||||
self.initGlidera(accessToken);
|
||||
});
|
||||
|
|
@ -1608,19 +1484,6 @@ angular.module('copayApp.controllers').controller('indexController', function($r
|
|||
self.updateAll({
|
||||
triggerTxUpdate: true,
|
||||
});
|
||||
self.updateRemotePreferences({
|
||||
saveAll: true
|
||||
}, function() {
|
||||
$log.debug('Remote preferences saved')
|
||||
});
|
||||
});
|
||||
|
||||
$rootScope.$on('Local/EmailSettingUpdated', function(event, email, cb) {
|
||||
self.updateRemotePreferences({
|
||||
preferences: {
|
||||
email: email || null
|
||||
},
|
||||
}, cb);
|
||||
});
|
||||
|
||||
$rootScope.$on('Local/WalletCompleted', function(event, walletId) {
|
||||
|
|
@ -1632,14 +1495,6 @@ angular.module('copayApp.controllers').controller('indexController', function($r
|
|||
}
|
||||
});
|
||||
|
||||
$rootScope.$on('Local/ProfileCreated', function(event) {
|
||||
self.updateRemotePreferences({
|
||||
saveAll: true
|
||||
}, function() {
|
||||
$log.debug('Remote preferences saved');
|
||||
});
|
||||
});
|
||||
|
||||
self.debouncedUpdate = lodash.throttle(function() {
|
||||
self.updateAll({
|
||||
quiet: true
|
||||
|
|
@ -1692,13 +1547,6 @@ angular.module('copayApp.controllers').controller('indexController', function($r
|
|||
});
|
||||
});
|
||||
|
||||
$rootScope.$on('Local/TxModal', function(event, tx) {
|
||||
self.showTx = tx;
|
||||
$timeout(function() {
|
||||
$rootScope.$apply();
|
||||
});
|
||||
});
|
||||
|
||||
$rootScope.$on('NewIncomingTx', function() {
|
||||
self.newTx = true;
|
||||
self.updateAll({
|
||||
|
|
@ -1800,13 +1648,13 @@ angular.module('copayApp.controllers').controller('indexController', function($r
|
|||
});
|
||||
|
||||
$rootScope.$on('Local/NoWallets', function(event) {
|
||||
|
||||
$timeout(function() {
|
||||
self.hasProfile = true;
|
||||
self.noFocusedWallet = true;
|
||||
self.isComplete = null;
|
||||
self.walletName = null;
|
||||
self.setUxLanguage();
|
||||
uxLanguage.update();
|
||||
|
||||
profileService.isDisclaimerAccepted(function(v) {
|
||||
if (v) {
|
||||
go.path('import');
|
||||
|
|
@ -1816,7 +1664,7 @@ angular.module('copayApp.controllers').controller('indexController', function($r
|
|||
});
|
||||
|
||||
$rootScope.$on('Local/NewFocusedWallet', function() {
|
||||
self.setUxLanguage();
|
||||
uxLanguage.update();
|
||||
self.setFocusedWallet();
|
||||
self.updateHistory();
|
||||
storageService.getCleanAndScanAddresses(function(err, walletId) {
|
||||
|
|
@ -1840,29 +1688,93 @@ angular.module('copayApp.controllers').controller('indexController', function($r
|
|||
});
|
||||
|
||||
$rootScope.$on('Local/NeedsConfirmation', function(event, txp, cb) {
|
||||
self.confirmTx = {
|
||||
txp: txFormatService.processTx(txp),
|
||||
callback: function(accept) {
|
||||
self.confirmTx = null;
|
||||
return cb(accept);
|
||||
}
|
||||
};
|
||||
$timeout(function() {
|
||||
$rootScope.$apply();
|
||||
|
||||
function openConfirmationPopup(txp, cb) {
|
||||
|
||||
$scope.tx = txFormatService.processTx(txp);
|
||||
|
||||
self.confirmationPopup = $ionicPopup.show({
|
||||
templateUrl: 'views/includes/confirm-tx.html',
|
||||
scope: $scope,
|
||||
});
|
||||
|
||||
$scope.processFee = function(amount, fee) {
|
||||
var walletSettings = configService.getSync().wallet.settings;
|
||||
var feeAlternativeIsoCode = walletSettings.alternativeIsoCode;
|
||||
|
||||
$scope.feeLevel = feeService.feeOpts[feeService.getCurrentFeeLevel()];
|
||||
$scope.feeAlternativeStr = parseFloat((rateService.toFiat(fee, feeAlternativeIsoCode)).toFixed(2), 10) + ' ' + feeAlternativeIsoCode;
|
||||
$scope.feeRateStr = (fee / (amount + fee) * 100).toFixed(2) + '%';
|
||||
};
|
||||
|
||||
$scope.cancel = function() {
|
||||
return cb();
|
||||
};
|
||||
|
||||
$scope.accept = function() {
|
||||
return cb(true);
|
||||
};
|
||||
}
|
||||
|
||||
openConfirmationPopup(txp, function(accept) {
|
||||
self.confirmationPopup.close();
|
||||
return cb(accept);
|
||||
});
|
||||
});
|
||||
|
||||
$rootScope.$on('Local/NeedsPassword', function(event, isSetup, cb) {
|
||||
self.askPassword = {
|
||||
isSetup: isSetup,
|
||||
callback: function(err, pass) {
|
||||
self.askPassword = null;
|
||||
return cb(err, pass);
|
||||
},
|
||||
|
||||
function openPasswordPopup(isSetup, cb) {
|
||||
$scope.data = {};
|
||||
$scope.data.password = null;
|
||||
$scope.isSetup = isSetup;
|
||||
$scope.isVerification = false;
|
||||
$scope.loading = false;
|
||||
var pass = null;
|
||||
|
||||
self.passwordPopup = $ionicPopup.show({
|
||||
templateUrl: 'views/includes/password.html',
|
||||
scope: $scope,
|
||||
});
|
||||
|
||||
$scope.cancel = function() {
|
||||
return cb('No spending password given');
|
||||
};
|
||||
|
||||
$scope.set = function() {
|
||||
$scope.loading = true;
|
||||
$scope.error = null;
|
||||
|
||||
$timeout(function() {
|
||||
if (isSetup && !$scope.isVerification) {
|
||||
$scope.loading = false;
|
||||
$scope.isVerification = true;
|
||||
pass = $scope.data.password;
|
||||
$scope.data.password = null;
|
||||
return;
|
||||
}
|
||||
if (isSetup && pass != $scope.data.password) {
|
||||
$scope.loading = false;
|
||||
$scope.error = gettext('Spending Passwords do not match');
|
||||
$scope.isVerification = false;
|
||||
$scope.data.password = null;
|
||||
pass = null;
|
||||
return;
|
||||
}
|
||||
return cb(null, $scope.data.password);
|
||||
}, 100);
|
||||
};
|
||||
};
|
||||
$timeout(function() {
|
||||
$rootScope.$apply();
|
||||
|
||||
openPasswordPopup(isSetup, function(err, pass) {
|
||||
self.passwordPopup.close();
|
||||
return cb(err, pass);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
$rootScope.$on('Local/EmailUpdated', function(event, email) {
|
||||
self.preferences.email = email;
|
||||
});
|
||||
|
||||
lodash.each(['NewCopayer', 'CopayerUpdated'], function(eventName) {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
'use strict';
|
||||
|
||||
angular.module('copayApp.controllers').controller('joinController',
|
||||
function($scope, $rootScope, $timeout, go, notification, profileService, configService, storageService, applicationService, $modal, gettext, lodash, ledger, trezor, platformInfo, derivationPathHelper) {
|
||||
function($scope, $rootScope, $timeout, $ionicScrollDelegate, go, notification, profileService, configService, storageService, applicationService, $modal, gettext, lodash, ledger, trezor, platformInfo, derivationPathHelper) {
|
||||
|
||||
var isChromeApp = platformInfo.isChromeApp;
|
||||
var isDevel = platformInfo.isDevel;
|
||||
|
|
@ -18,7 +18,6 @@ angular.module('copayApp.controllers').controller('joinController',
|
|||
$scope.joinForm.secret.$render();
|
||||
};
|
||||
|
||||
|
||||
var updateSeedSourceSelect = function() {
|
||||
self.seedOptions = [{
|
||||
id: 'new',
|
||||
|
|
@ -56,6 +55,7 @@ angular.module('copayApp.controllers').controller('joinController',
|
|||
this.join = function(form) {
|
||||
if (form && form.$invalid) {
|
||||
self.error = gettext('Please enter the required fields');
|
||||
$ionicScrollDelegate.scrollTop();
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -65,7 +65,7 @@ angular.module('copayApp.controllers').controller('joinController',
|
|||
bwsurl: $scope.bwsurl,
|
||||
}
|
||||
|
||||
var setSeed = self.seedSourceId =='set';
|
||||
var setSeed = self.seedSourceId == 'set';
|
||||
if (setSeed) {
|
||||
var words = form.privateKey.$modelValue;
|
||||
if (words.indexOf(' ') == -1 && words.indexOf('prv') == 1 && words.length > 108) {
|
||||
|
|
@ -78,6 +78,7 @@ angular.module('copayApp.controllers').controller('joinController',
|
|||
var pathData = derivationPathHelper.parse($scope.derivationPath);
|
||||
if (!pathData) {
|
||||
this.error = gettext('Invalid derivation path');
|
||||
$ionicScrollDelegate.scrollTop();
|
||||
return;
|
||||
}
|
||||
opts.account = pathData.account;
|
||||
|
|
@ -91,6 +92,7 @@ angular.module('copayApp.controllers').controller('joinController',
|
|||
if (setSeed && !opts.mnemonic && !opts.extendedPrivateKey) {
|
||||
|
||||
this.error = gettext('Please enter the wallet recovery phrase');
|
||||
$ionicScrollDelegate.scrollTop();
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -98,14 +100,14 @@ angular.module('copayApp.controllers').controller('joinController',
|
|||
var account = $scope.account;
|
||||
if (!account || account < 1) {
|
||||
this.error = gettext('Invalid account number');
|
||||
|
||||
$ionicScrollDelegate.scrollTop();
|
||||
return;
|
||||
}
|
||||
|
||||
if ( self.seedSourceId == 'trezor')
|
||||
if (self.seedSourceId == 'trezor')
|
||||
account = account - 1;
|
||||
|
||||
opts.account = account;
|
||||
opts.account = account;
|
||||
self.hwWallet = self.seedSourceId == 'ledger' ? 'Ledger' : 'Trezor';
|
||||
var src = self.seedSourceId == 'ledger' ? ledger : trezor;
|
||||
|
||||
|
|
@ -113,6 +115,7 @@ angular.module('copayApp.controllers').controller('joinController',
|
|||
self.hwWallet = false;
|
||||
if (err) {
|
||||
self.error = err;
|
||||
$ionicScrollDelegate.scrollTop();
|
||||
$scope.$apply();
|
||||
return;
|
||||
}
|
||||
|
|
@ -132,6 +135,7 @@ angular.module('copayApp.controllers').controller('joinController',
|
|||
if (err) {
|
||||
self.loading = false;
|
||||
self.error = err;
|
||||
$ionicScrollDelegate.scrollTop();
|
||||
$rootScope.$apply();
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
153
src/js/controllers/modals/addressbook.js
Normal file
153
src/js/controllers/modals/addressbook.js
Normal file
|
|
@ -0,0 +1,153 @@
|
|||
'use strict';
|
||||
|
||||
angular.module('copayApp.controllers').controller('addressbookController', function($rootScope, $scope, $timeout, profileService, addressService, addressbookService) {
|
||||
var self = $scope.self;
|
||||
|
||||
var fc = profileService.focusedClient;
|
||||
self.lockAddress = false;
|
||||
self._address = null;
|
||||
$scope.editAddressbook = false;
|
||||
$scope.addAddressbookEntry = false;
|
||||
$scope.selectedAddressbook = {};
|
||||
$scope.newAddress = address;
|
||||
$scope.walletName = fc.credentials.walletName;
|
||||
$scope.color = fc.backgroundColor;
|
||||
$scope.addressbook = {
|
||||
'address': ($scope.newAddress || ''),
|
||||
'label': ''
|
||||
};
|
||||
|
||||
$scope.checkClipboard = function() {
|
||||
if (!$scope.newAddress) {
|
||||
getClipboard(function(value) {
|
||||
$scope.newAddress = value;
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
$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);
|
||||
};
|
||||
|
||||
$scope.toggleEditAddressbook = function() {
|
||||
$scope.editAddressbook = !$scope.editAddressbook;
|
||||
$scope.selectedAddressbook = {};
|
||||
$scope.addAddressbookEntry = false;
|
||||
};
|
||||
|
||||
$scope.selectAddressbook = function(addr) {
|
||||
self.setForm(addr);
|
||||
$scope.cancel();
|
||||
};
|
||||
|
||||
$scope.toggleSelectAddressbook = function(addr) {
|
||||
$scope.selectedAddressbook[addr] = $scope.selectedAddressbook[addr] ? false : true;
|
||||
};
|
||||
|
||||
$scope.toggleAddAddressbookEntry = function() {
|
||||
$scope.error = null;
|
||||
$scope.addressbook = {
|
||||
'address': '',
|
||||
'label': ''
|
||||
};
|
||||
$scope.addAddressbookEntry = !$scope.addAddressbookEntry;
|
||||
};
|
||||
|
||||
$scope.list = function() {
|
||||
$scope.error = null;
|
||||
addressbookService.list(function(err, ab) {
|
||||
if (err) {
|
||||
$scope.error = err;
|
||||
return;
|
||||
}
|
||||
$scope.list = ab;
|
||||
$timeout(function() {
|
||||
$scope.$digest();
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
$scope.add = function(addressbook) {
|
||||
$scope.error = null;
|
||||
$timeout(function() {
|
||||
addressbookService.add(addressbook, function(err, ab) {
|
||||
if (err) {
|
||||
$scope.error = err;
|
||||
return;
|
||||
}
|
||||
$rootScope.$emit('Local/AddressbookUpdated', ab);
|
||||
$scope.list = ab;
|
||||
$scope.editAddressbook = true;
|
||||
$scope.toggleEditAddressbook();
|
||||
$scope.$digest();
|
||||
});
|
||||
}, 100);
|
||||
};
|
||||
|
||||
$scope.remove = function(addr) {
|
||||
$scope.error = null;
|
||||
$timeout(function() {
|
||||
addressbookService.remove(addr, function(err, ab) {
|
||||
if (err) {
|
||||
$scope.error = err;
|
||||
return;
|
||||
}
|
||||
$rootScope.$emit('Local/AddressbookUpdated', ab);
|
||||
$scope.list = ab;
|
||||
$scope.$digest();
|
||||
});
|
||||
}, 100);
|
||||
};
|
||||
|
||||
$scope.selectWallet = function(walletId, walletName) {
|
||||
var client = profileService.getClient(walletId);
|
||||
$scope.errorSelectedWallet = {};
|
||||
|
||||
profileService.isReady(client, function(err) {
|
||||
if (err) $scope.errorSelectedWallet[walletId] = err;
|
||||
else {
|
||||
$scope.gettingAddress = true;
|
||||
$scope.selectedWalletName = walletName;
|
||||
$timeout(function() {
|
||||
$scope.$apply();
|
||||
});
|
||||
|
||||
addressService.getAddress(walletId, false, function(err, addr) {
|
||||
$scope.gettingAddress = false;
|
||||
if (err) {
|
||||
self.error = err;
|
||||
$scope.cancelAddress();
|
||||
return;
|
||||
}
|
||||
|
||||
self.setForm(addr);
|
||||
$scope.cancel();
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
$scope.cancelAddress = function() {
|
||||
self.resetForm();
|
||||
$scope.cancel();
|
||||
};
|
||||
|
||||
$scope.cancel = function() {
|
||||
$scope.addressbookModal.hide();
|
||||
};
|
||||
});
|
||||
82
src/js/controllers/modals/customAmount.js
Normal file
82
src/js/controllers/modals/customAmount.js
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
'use strict';
|
||||
|
||||
angular.module('copayApp.controllers').controller('customAmountController', function($scope, $timeout, $filter, platformInfo, rateService) {
|
||||
var self = $scope.self;
|
||||
|
||||
$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;
|
||||
$scope.isCordova = platformInfo.isCordova;
|
||||
|
||||
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);
|
||||
} else {
|
||||
$scope.customAmount = null;
|
||||
}
|
||||
},
|
||||
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 {
|
||||
$scope.customAlternative = null;
|
||||
}
|
||||
$scope.alternativeAmount = $scope.customAlternative;
|
||||
},
|
||||
enumerable: true,
|
||||
configurable: true
|
||||
});
|
||||
|
||||
$scope.submitForm = function(form) {
|
||||
var satToBtc = 1 / 100000000;
|
||||
var amount = form.amount.$modelValue;
|
||||
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') {
|
||||
amount = (amountSat * satToBtc).toFixed(8);
|
||||
}
|
||||
$scope.customizedAmountBtc = amount;
|
||||
}, 1);
|
||||
};
|
||||
|
||||
$scope.toggleAlternative = function() {
|
||||
$scope.showAlternative = !$scope.showAlternative;
|
||||
};
|
||||
|
||||
$scope.shareAddress = function(uri) {
|
||||
if (platformInfo.isCordova) {
|
||||
if (platformInfo.isAndroid || platformInfo.isWP) {
|
||||
window.ignoreMobilePause = true;
|
||||
}
|
||||
window.plugins.socialsharing.share(uri, null, null, null);
|
||||
}
|
||||
};
|
||||
|
||||
$scope.cancel = function() {
|
||||
$scope.customAmountModal.hide();
|
||||
};
|
||||
});
|
||||
15
src/js/controllers/modals/paypro.js
Normal file
15
src/js/controllers/modals/paypro.js
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
'use strict';
|
||||
|
||||
angular.module('copayApp.controllers').controller('payproController', function($scope) {
|
||||
var self = $scope.self;
|
||||
|
||||
$scope.alternative = self.alternativeAmount;
|
||||
$scope.alternativeIsoCode = self.alternativeIsoCode;
|
||||
$scope.isRateAvailable = self.isRateAvailable;
|
||||
$scope.unitTotal = ($scope.paypro.amount * self.satToUnit).toFixed(self.unitDecimals);
|
||||
$scope.unitName = self.unitName;
|
||||
|
||||
$scope.cancel = function() {
|
||||
$scope.payproModal.hide();
|
||||
};
|
||||
});
|
||||
10
src/js/controllers/modals/search.js
Normal file
10
src/js/controllers/modals/search.js
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
'use strict';
|
||||
|
||||
angular.module('copayApp.controllers').controller('searchController', function($scope) {
|
||||
var self = $scope.self;
|
||||
$scope.search = '';
|
||||
|
||||
$scope.cancel = function() {
|
||||
$scope.searchModal.hide();
|
||||
};
|
||||
});
|
||||
93
src/js/controllers/modals/txDetails.js
Normal file
93
src/js/controllers/modals/txDetails.js
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
'use strict';
|
||||
|
||||
angular.module('copayApp.controllers').controller('txDetailsController', function($rootScope, $log, $scope, $filter, $ionicPopup, gettextCatalog, profileService, configService, lodash) {
|
||||
|
||||
var self = $scope.self;
|
||||
var fc = profileService.focusedClient;
|
||||
var config = configService.getSync();
|
||||
var configWallet = config.wallet;
|
||||
var walletSettings = configWallet.settings;
|
||||
|
||||
$scope.alternativeIsoCode = walletSettings.alternativeIsoCode;
|
||||
$scope.color = fc.backgroundColor;
|
||||
$scope.copayerId = fc.credentials.copayerId;
|
||||
$scope.isShared = fc.credentials.n > 1;
|
||||
$scope.showCommentPopup = function() {
|
||||
$scope.data = {
|
||||
comment: $scope.btx.note ? $scope.btx.note.body : '',
|
||||
};
|
||||
|
||||
var commentPopup = $ionicPopup.show({
|
||||
templateUrl: "views/includes/note.html",
|
||||
scope: $scope,
|
||||
});
|
||||
|
||||
$scope.commentPopupClose = function() {
|
||||
commentPopup.close();
|
||||
};
|
||||
|
||||
$scope.commentPopupSave = function() {
|
||||
$log.debug('Saving note');
|
||||
var args = {
|
||||
txid: $scope.btx.txid,
|
||||
};
|
||||
|
||||
if (!lodash.isEmpty($scope.data.comment)) {
|
||||
args.body = $scope.data.comment;
|
||||
};
|
||||
|
||||
fc.editTxNote(args, function(err) {
|
||||
if (err) {
|
||||
$log.debug('Could not save tx comment');
|
||||
return;
|
||||
}
|
||||
// This is only to refresh the current screen data
|
||||
$scope.btx.note = null;
|
||||
if (args.body) {
|
||||
$scope.btx.note = {};
|
||||
$scope.btx.note.body = $scope.data.comment;
|
||||
$scope.btx.note.editedByName = fc.credentials.copayerName;
|
||||
$scope.btx.note.editedOn = Math.floor(Date.now() / 1000);
|
||||
}
|
||||
$scope.btx.searcheableString = null;
|
||||
commentPopup.close();
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
$scope.getAlternativeAmount = function() {
|
||||
var satToBtc = 1 / 100000000;
|
||||
|
||||
fc.getFiatRate({
|
||||
code: $scope.alternativeIsoCode,
|
||||
ts: $scope.btx.time * 1000
|
||||
}, function(err, res) {
|
||||
if (err) {
|
||||
$log.debug('Could not get historic rate');
|
||||
return;
|
||||
}
|
||||
if (res && res.rate) {
|
||||
var alternativeAmountBtc = ($scope.btx.amount * satToBtc).toFixed(8);
|
||||
$scope.rateDate = res.fetchedOn;
|
||||
$scope.rateStr = res.rate + ' ' + $scope.alternativeIsoCode;
|
||||
$scope.alternativeAmountStr = $filter('noFractionNumber')(alternativeAmountBtc * res.rate, 2) + ' ' + $scope.alternativeIsoCode;
|
||||
$scope.$apply();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
$scope.getShortNetworkName = function() {
|
||||
var n = fc.credentials.network;
|
||||
return n.substring(0, 4);
|
||||
};
|
||||
|
||||
$scope.copyToClipboard = function(addr) {
|
||||
if (!addr) return;
|
||||
self.copyToClipboard(addr);
|
||||
};
|
||||
|
||||
$scope.cancel = function() {
|
||||
$scope.txDetailsModal.hide();
|
||||
};
|
||||
|
||||
});
|
||||
240
src/js/controllers/modals/txpDetails.js
Normal file
240
src/js/controllers/modals/txpDetails.js
Normal file
|
|
@ -0,0 +1,240 @@
|
|||
'use strict';
|
||||
|
||||
angular.module('copayApp.controllers').controller('txpDetailsController', function($scope, $rootScope, $timeout, $interval, platformInfo, txStatus, $ionicScrollDelegate, txFormatService, fingerprintService, bwsError, gettextCatalog, lodash, profileService, walletService) {
|
||||
var self = $scope.self;
|
||||
var tx = $scope.tx;
|
||||
var copayers = $scope.copayers;
|
||||
var isGlidera = $scope.isGlidera;
|
||||
var now = Math.floor(Date.now() / 1000);
|
||||
var fc = profileService.focusedClient;
|
||||
$scope.loading = null;
|
||||
$scope.copayerId = fc.credentials.copayerId;
|
||||
$scope.isShared = fc.credentials.n > 1;
|
||||
$scope.canSign = fc.canSign() || fc.isPrivKeyExternal();
|
||||
$scope.color = fc.backgroundColor;
|
||||
|
||||
checkPaypro();
|
||||
|
||||
// ToDo: use tx.customData instead of tx.message
|
||||
if (tx.message === 'Glidera transaction' && isGlidera) {
|
||||
tx.isGlidera = true;
|
||||
if (tx.canBeRemoved) {
|
||||
tx.canBeRemoved = (Date.now() / 1000 - (tx.ts || tx.createdOn)) > GLIDERA_LOCK_TIME;
|
||||
}
|
||||
}
|
||||
|
||||
$scope.sign = function(txp) {
|
||||
$scope.error = null;
|
||||
$scope.loading = 'Signing Transaction';
|
||||
|
||||
fingerprintService.check(fc, function(err) {
|
||||
if (err) {
|
||||
$scope.error = err;
|
||||
$scope.loading = null;
|
||||
return;
|
||||
}
|
||||
|
||||
handleEncryptedWallet(function(err) {
|
||||
if (err) {
|
||||
$scope.error = err;
|
||||
$scope.loading = null;
|
||||
return;
|
||||
}
|
||||
|
||||
walletService.signTx(fc, txp, function(err, signedTxp) {
|
||||
walletService.lock(fc);
|
||||
if (err) {
|
||||
$scope.error = err;
|
||||
$scope.loading = null;
|
||||
return;
|
||||
}
|
||||
|
||||
if (signedTxp.status == 'accepted') {
|
||||
$scope.loading = 'Broadcasting Transaction';
|
||||
walletService.broadcastTx(fc, signedTxp, function(err, broadcastedTxp) {
|
||||
$scope.loading = null;
|
||||
$scope.$emit('UpdateTx');
|
||||
$scope.close(broadcastedTxp);
|
||||
if (err) {
|
||||
$scope.error = err;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
$scope.loading = null;
|
||||
$scope.$emit('UpdateTx');
|
||||
$scope.close(signedTxp);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
$scope.reject = function(txp) {
|
||||
$scope.loading = 'Rejecting payment';
|
||||
$scope.error = null;
|
||||
|
||||
$timeout(function() {
|
||||
walletService.rejectTx(fc, txp, function(err, txpr) {
|
||||
$scope.loading = null;
|
||||
|
||||
if (err) {
|
||||
$scope.$emit('UpdateTx');
|
||||
$scope.error = bwsError.msg(err, gettextCatalog.getString('Could not reject payment'));
|
||||
$scope.$digest();
|
||||
} else {
|
||||
$scope.close(txpr);
|
||||
}
|
||||
});
|
||||
}, 10);
|
||||
};
|
||||
|
||||
$scope.remove = function(txp) {
|
||||
$scope.loading = 'Deleting Payment';
|
||||
$scope.error = null;
|
||||
|
||||
$timeout(function() {
|
||||
walletService.removeTx(fc, txp, function(err) {
|
||||
$scope.loading = null;
|
||||
|
||||
// Hacky: request tries to parse an empty response
|
||||
if (err && !(err.message && err.message.match(/Unexpected/))) {
|
||||
$scope.$emit('UpdateTx');
|
||||
$scope.error = bwsError.msg(err, gettextCatalog.getString('Could not delete payment proposal'));
|
||||
$scope.$digest();
|
||||
return;
|
||||
}
|
||||
$scope.close();
|
||||
});
|
||||
}, 10);
|
||||
};
|
||||
|
||||
$scope.broadcast = function(txp) {
|
||||
$scope.loading = 'Broadcasting Payment';
|
||||
$scope.error = null;
|
||||
|
||||
$timeout(function() {
|
||||
walletService.broadcastTx(fc, txp, function(err, txpb) {
|
||||
$scope.loading = null;
|
||||
|
||||
if (err) {
|
||||
$scope.error = bwsError.msg(err, gettextCatalog.getString('Could not broadcast payment'));
|
||||
$scope.$digest();
|
||||
return;
|
||||
}
|
||||
$scope.close(txpb);
|
||||
});
|
||||
}, 10);
|
||||
};
|
||||
|
||||
$scope.getShortNetworkName = function() {
|
||||
return fc.credentials.networkName.substring(0, 4);
|
||||
};
|
||||
|
||||
function checkPaypro() {
|
||||
if (tx.payProUrl && !platformInfo.isChromeApp) {
|
||||
fc.fetchPayPro({
|
||||
payProUrl: tx.payProUrl,
|
||||
}, function(err, paypro) {
|
||||
if (err) return;
|
||||
tx.paypro = paypro;
|
||||
paymentTimeControl(tx.paypro.expires);
|
||||
$timeout(function() {
|
||||
$ionicScrollDelegate.resize();
|
||||
}, 100);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
function paymentTimeControl(expirationTime) {
|
||||
$scope.paymentExpired = false;
|
||||
setExpirationTime();
|
||||
|
||||
self.countDown = $interval(function() {
|
||||
setExpirationTime();
|
||||
}, 1000);
|
||||
|
||||
function setExpirationTime() {
|
||||
var now = Math.floor(Date.now() / 1000);
|
||||
if (now > expirationTime) {
|
||||
$scope.paymentExpired = true;
|
||||
if (self.countDown) $interval.cancel(self.countDown);
|
||||
return;
|
||||
}
|
||||
var totalSecs = expirationTime - now;
|
||||
var m = Math.floor(totalSecs / 60);
|
||||
var s = totalSecs % 60;
|
||||
$scope.expires = ('0' + m).slice(-2) + ":" + ('0' + s).slice(-2);
|
||||
};
|
||||
};
|
||||
|
||||
lodash.each(['TxProposalRejectedBy', 'TxProposalAcceptedBy', 'transactionProposalRemoved', 'TxProposalRemoved', 'NewOutgoingTx', 'UpdateTx'], function(eventName) {
|
||||
$scope.$on(eventName, function() {
|
||||
fc.getTx($scope.tx.id, function(err, tx) {
|
||||
if (err) {
|
||||
if (err.message && err.message == 'TX_NOT_FOUND' &&
|
||||
(eventName == 'transactionProposalRemoved' || eventName == 'TxProposalRemoved')) {
|
||||
$scope.tx.removed = true;
|
||||
$scope.tx.canBeRemoved = false;
|
||||
$scope.tx.pendingForUs = false;
|
||||
$scope.$apply();
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
var action = lodash.find(tx.actions, {
|
||||
copayerId: fc.credentials.copayerId
|
||||
});
|
||||
|
||||
$scope.tx = txFormatService.processTx(tx);
|
||||
|
||||
if (!action && tx.status == 'pending')
|
||||
$scope.tx.pendingForUs = true;
|
||||
|
||||
$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;
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
function handleEncryptedWallet(cb) {
|
||||
if (!walletService.isEncrypted(fc)) return cb();
|
||||
$rootScope.$emit('Local/NeedsPassword', false, function(err, password) {
|
||||
if (err) return cb(err);
|
||||
return cb(null, walletService.unlock(fc, password));
|
||||
});
|
||||
};
|
||||
|
||||
$scope.copyToClipboard = function(addr) {
|
||||
if (!addr) return;
|
||||
self.copyToClipboard(addr);
|
||||
};
|
||||
|
||||
$scope.close = function(txp) {
|
||||
$scope.loading = null;
|
||||
if (txp) {
|
||||
txStatus.notify(txp, function() {
|
||||
$scope.$emit('Local/TxProposalAction', txp.status == 'broadcasted');
|
||||
});
|
||||
} else {
|
||||
$timeout(function() {
|
||||
$scope.$emit('Local/TxProposalAction');
|
||||
}, 100);
|
||||
}
|
||||
$scope.cancel();
|
||||
};
|
||||
|
||||
$scope.cancel = function() {
|
||||
$scope.txpDetailsModal.hide();
|
||||
};
|
||||
});
|
||||
|
|
@ -1,43 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
angular.module('copayApp.controllers').controller('passwordController',
|
||||
function($rootScope, $scope, $timeout, profileService, notification, go, gettext) {
|
||||
|
||||
var pass1;
|
||||
|
||||
this.isVerification = false;
|
||||
|
||||
document.getElementById("passwordInput").focus();
|
||||
|
||||
this.close = function(cb) {
|
||||
return cb('No spending password given');
|
||||
};
|
||||
|
||||
this.set = function(isSetup, cb) {
|
||||
this.loading = true;
|
||||
this.error = false;
|
||||
|
||||
var self = this;
|
||||
|
||||
$timeout(function() {
|
||||
if (isSetup && !self.isVerification) {
|
||||
self.loading = false;
|
||||
document.getElementById("passwordInput").focus();
|
||||
self.isVerification = true;
|
||||
pass1 = $scope.password;
|
||||
$scope.password = null;
|
||||
return;
|
||||
}
|
||||
if (isSetup && pass1 != $scope.password) {
|
||||
self.loading = false;
|
||||
self.error = gettext('Spending Passwords do not match');
|
||||
self.isVerification = false;
|
||||
$scope.password = null;
|
||||
pass1 = null;
|
||||
return;
|
||||
}
|
||||
return cb(null, $scope.password);
|
||||
}, 100);
|
||||
};
|
||||
|
||||
});
|
||||
|
|
@ -3,86 +3,119 @@
|
|||
angular.module('copayApp.controllers').controller('preferencesController',
|
||||
function($scope, $rootScope, $timeout, $log, configService, profileService, fingerprintService, walletService) {
|
||||
|
||||
var fc = profileService.focusedClient;
|
||||
var self = this;
|
||||
var fc;
|
||||
var config = configService.getSync();
|
||||
$scope.deleted = false;
|
||||
if (fc.credentials && !fc.credentials.mnemonicEncrypted && !fc.credentials.mnemonic) {
|
||||
$scope.deleted = true;
|
||||
}
|
||||
|
||||
var disableFocusListener = $rootScope.$on('Local/NewFocusedWalletReady', function() {
|
||||
self.init();
|
||||
});
|
||||
|
||||
$scope.$on('$destroy', function() {
|
||||
disableFocusListener();
|
||||
});
|
||||
|
||||
this.init = function() {
|
||||
fc = profileService.focusedClient;
|
||||
if (fc) {
|
||||
$scope.encrypt = walletService.isEncrypted(fc);
|
||||
$scope.encryptEnabled = walletService.isEncrypted(fc);
|
||||
this.externalSource = fc.getPrivKeyExternalSourceName() == 'ledger' ? "Ledger" : null;
|
||||
// TODO externalAccount
|
||||
//this.externalIndex = fc.getExternalIndex();
|
||||
}
|
||||
|
||||
this.touchidAvailable = fingerprintService.isAvailable();
|
||||
$scope.touchid = config.touchIdFor ? config.touchIdFor[fc.credentials.walletId] : null;
|
||||
$scope.touchidEnabled = config.touchIdFor ? config.touchIdFor[fc.credentials.walletId] : null;
|
||||
|
||||
$scope.deleted = false;
|
||||
if (fc.credentials && !fc.credentials.mnemonicEncrypted && !fc.credentials.mnemonic) {
|
||||
$scope.deleted = true;
|
||||
}
|
||||
};
|
||||
|
||||
var handleEncryptedWallet = function(client, cb) {
|
||||
var handleEncryptedWallet = function(cb) {
|
||||
$rootScope.$emit('Local/NeedsPassword', false, function(err, password) {
|
||||
if (err) return cb(err);
|
||||
return cb(walletService.unlock(client, password));
|
||||
return cb(walletService.unlock(fc, password));
|
||||
});
|
||||
};
|
||||
|
||||
var unwatchEncrypt = $scope.$watch('encrypt', function(val) {
|
||||
var fc = profileService.focusedClient;
|
||||
$scope.encryptChange = function() {
|
||||
var self = this;
|
||||
if (!fc) return;
|
||||
var val = $scope.encryptEnabled;
|
||||
|
||||
var setPrivateKeyEncryption = function(password, cb) {
|
||||
$log.debug('Encrypting private key for', fc.credentials.walletName);
|
||||
|
||||
fc.setPrivateKeyEncryption(password);
|
||||
fc.lock();
|
||||
profileService.updateCredentials(fc.export(), function() {
|
||||
$log.debug('Wallet encrypted');
|
||||
return cb();
|
||||
});
|
||||
};
|
||||
|
||||
var disablePrivateKeyEncryption = function(cb) {
|
||||
$log.debug('Disabling private key encryption for', fc.credentials.walletName);
|
||||
|
||||
try {
|
||||
fc.disablePrivateKeyEncryption();
|
||||
} catch (e) {
|
||||
return cb(e);
|
||||
}
|
||||
profileService.updateCredentials(fc.export(), function() {
|
||||
$log.debug('Wallet encryption disabled');
|
||||
return cb();
|
||||
});
|
||||
};
|
||||
|
||||
if (val && !walletService.isEncrypted(fc)) {
|
||||
$rootScope.$emit('Local/NeedsPassword', true, function(err, password) {
|
||||
if (err || !password) {
|
||||
$scope.encrypt = false;
|
||||
$scope.encryptEnabled = false;
|
||||
return;
|
||||
}
|
||||
profileService.setPrivateKeyEncryptionFC(password, function() {
|
||||
setPrivateKeyEncryption(password, function() {
|
||||
$rootScope.$emit('Local/NewEncryptionSetting');
|
||||
$scope.encrypt = true;
|
||||
$scope.encryptEnabled = true;
|
||||
});
|
||||
});
|
||||
} else {
|
||||
if (!val && walletService.isEncrypted(fc)) {
|
||||
handleEncryptedWallet(fc, function(err) {
|
||||
handleEncryptedWallet(function(err) {
|
||||
if (err) {
|
||||
$scope.encrypt = true;
|
||||
$scope.encryptEnabled = true;
|
||||
return;
|
||||
}
|
||||
profileService.disablePrivateKeyEncryptionFC(function(err) {
|
||||
disablePrivateKeyEncryption(function(err) {
|
||||
$rootScope.$emit('Local/NewEncryptionSetting');
|
||||
if (err) {
|
||||
$scope.encrypt = true;
|
||||
$scope.encryptEnabled = true;
|
||||
$log.error(err);
|
||||
return;
|
||||
}
|
||||
$scope.encrypt = false;
|
||||
$scope.encryptEnabled = false;
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
var unwatchRequestTouchid = $scope.$watch('touchid', function(newVal, oldVal) {
|
||||
if (newVal == oldVal || $scope.touchidError) {
|
||||
$scope.touchidError = false;
|
||||
return;
|
||||
}
|
||||
$scope.touchidChange = function() {
|
||||
var walletId = fc.credentials.walletId;
|
||||
|
||||
var opts = {
|
||||
touchIdFor: {}
|
||||
};
|
||||
opts.touchIdFor[walletId] = newVal;
|
||||
opts.touchIdFor[walletId] = $scope.touchidEnabled;
|
||||
|
||||
fingerprintService.check(fc, function(err) {
|
||||
if (err) {
|
||||
$log.debug(err);
|
||||
$timeout(function() {
|
||||
$scope.touchidError = true;
|
||||
$scope.touchid = oldVal;
|
||||
$scope.touchidEnabled = false;
|
||||
}, 100);
|
||||
return;
|
||||
}
|
||||
|
|
@ -90,14 +123,9 @@ angular.module('copayApp.controllers').controller('preferencesController',
|
|||
if (err) {
|
||||
$log.debug(err);
|
||||
$scope.touchidError = true;
|
||||
$scope.touchid = oldVal;
|
||||
$scope.touchidEnabled = false;
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
$scope.$on('$destroy', function() {
|
||||
unwatchEncrypt();
|
||||
unwatchRequestTouchid();
|
||||
});
|
||||
};
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
'use strict';
|
||||
|
||||
angular.module('copayApp.controllers').controller('preferencesAltCurrencyController',
|
||||
function($scope, $timeout, $log, configService, rateService, lodash, go) {
|
||||
function($scope, $timeout, $log, configService, rateService, lodash, go, profileService, walletService) {
|
||||
this.hideAdv = true;
|
||||
this.hidePriv = true;
|
||||
this.hideSecret = true;
|
||||
|
|
@ -51,6 +51,9 @@ angular.module('copayApp.controllers').controller('preferencesAltCurrencyControl
|
|||
if (err) $log.warn(err);
|
||||
go.preferencesGlobal();
|
||||
$scope.$emit('Local/UnitSettingUpdated');
|
||||
walletService.updateRemotePreferences(profileService.walletClients, {}, function() {
|
||||
$log.debug('Remote preferences saved');
|
||||
});
|
||||
$timeout(function() {
|
||||
$scope.$apply();
|
||||
}, 100);
|
||||
|
|
|
|||
|
|
@ -40,19 +40,19 @@ angular.module('copayApp.controllers').controller('preferencesDeleteWalletContro
|
|||
|
||||
modalInstance.result.then(function(ok) {
|
||||
if (ok) {
|
||||
_deleteWallet();
|
||||
doDeleteWallet();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
var _deleteWallet = function() {
|
||||
var doDeleteWallet = function() {
|
||||
$scope.isDeletingWallet = true;
|
||||
var fc = profileService.focusedClient;
|
||||
var name = fc.credentials.walletName;
|
||||
var walletName = (fc.alias || '') + ' [' + name + ']';
|
||||
var self = this;
|
||||
|
||||
profileService.deleteWalletFC({}, function(err) {
|
||||
profileService.deleteWalletClient(fc, function(err) {
|
||||
$scope.isDeletingWallet = false;
|
||||
if (err) {
|
||||
self.error = err.message || err;
|
||||
|
|
@ -72,7 +72,7 @@ angular.module('copayApp.controllers').controller('preferencesDeleteWalletContro
|
|||
delete_msg,
|
||||
function(buttonIndex) {
|
||||
if (buttonIndex == 1) {
|
||||
_deleteWallet();
|
||||
doDeleteWallet();
|
||||
}
|
||||
},
|
||||
confirm_msg, [accept_msg, cancel_msg]
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ angular.module('copayApp.controllers').controller('preferencesDeleteWordsControl
|
|||
confirmDialog.show(msg, function(ok) {
|
||||
if (ok) {
|
||||
fc.clearMnemonic();
|
||||
profileService.updateCredentialsFC(function() {
|
||||
profileService.updateCredentials(fc.export(), function() {
|
||||
notification.success(successMsg);
|
||||
go.walletHome();
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,14 +1,22 @@
|
|||
'use strict';
|
||||
|
||||
angular.module('copayApp.controllers').controller('preferencesEmailController',
|
||||
function($scope, go, profileService, gettext, $log) {
|
||||
function($rootScope, go, profileService, gettext, $log, walletService) {
|
||||
this.save = function(form) {
|
||||
var self = this;
|
||||
this.error = null;
|
||||
|
||||
var fc = profileService.focusedClient;
|
||||
this.saving = true;
|
||||
$scope.$emit('Local/EmailSettingUpdated', self.email, function() {
|
||||
var email = self.email || '';
|
||||
|
||||
walletService.updateRemotePreferences(fc, {
|
||||
email: email,
|
||||
}, function(err) {
|
||||
|
||||
if (!err)
|
||||
$rootScope.$emit('Local/EmailUpdated', email);
|
||||
|
||||
self.saving = false;
|
||||
go.path('preferences');
|
||||
});
|
||||
|
|
|
|||
|
|
@ -36,24 +36,22 @@ angular.module('copayApp.controllers').controller('preferencesGlobalController',
|
|||
});
|
||||
}
|
||||
|
||||
var unwatchSpendUnconfirmed = $scope.$watch('spendUnconfirmed', function(newVal, oldVal) {
|
||||
if (newVal == oldVal) return;
|
||||
$scope.spendUnconfirmedChange = function() {
|
||||
var opts = {
|
||||
wallet: {
|
||||
spendUnconfirmed: newVal
|
||||
spendUnconfirmed: $scope.spendUnconfirmed
|
||||
}
|
||||
};
|
||||
configService.set(opts, function(err) {
|
||||
$rootScope.$emit('Local/SpendUnconfirmedUpdated', newVal);
|
||||
$rootScope.$emit('Local/SpendUnconfirmedUpdated', $scope.spendUnconfirmed);
|
||||
if (err) $log.debug(err);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
var unwatchPushNotifications = $scope.$watch('pushNotifications', function(newVal, oldVal) {
|
||||
if (newVal == oldVal) return;
|
||||
$scope.pushNotificationsChange = function() {
|
||||
var opts = {
|
||||
pushNotifications: {
|
||||
enabled: newVal
|
||||
enabled: $scope.pushNotifications
|
||||
}
|
||||
};
|
||||
configService.set(opts, function(err) {
|
||||
|
|
@ -63,38 +61,29 @@ angular.module('copayApp.controllers').controller('preferencesGlobalController',
|
|||
pushNotificationsService.disableNotifications(profileService.walletClients);
|
||||
if (err) $log.debug(err);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
var unwatchGlideraEnabled = $scope.$watch('glideraEnabled', function(newVal, oldVal) {
|
||||
if (newVal == oldVal) return;
|
||||
$scope.glideraChange = function() {
|
||||
var opts = {
|
||||
glidera: {
|
||||
enabled: newVal
|
||||
enabled: $scope.glideraEnabled
|
||||
}
|
||||
};
|
||||
configService.set(opts, function(err) {
|
||||
$rootScope.$emit('Local/GlideraUpdated');
|
||||
if (err) $log.debug(err);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
var unwatchCoinbaseEnabled = $scope.$watch('coinbaseEnabled', function(newVal, oldVal) {
|
||||
if (newVal == oldVal) return;
|
||||
$scope.coinbaseChange = function() {
|
||||
var opts = {
|
||||
coinbase: {
|
||||
enabled: newVal
|
||||
enabled: $scope.coinbaseEnabled
|
||||
}
|
||||
};
|
||||
configService.set(opts, function(err) {
|
||||
$rootScope.$emit('Local/CoinbaseUpdated');
|
||||
if (err) $log.debug(err);
|
||||
});
|
||||
});
|
||||
|
||||
$scope.$on('$destroy', function() {
|
||||
unwatchSpendUnconfirmed();
|
||||
unwatchGlideraEnabled();
|
||||
unwatchCoinbaseEnabled();
|
||||
unwatchPushNotifications();
|
||||
});
|
||||
};
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,9 +1,129 @@
|
|||
'use strict';
|
||||
|
||||
angular.module('copayApp.controllers').controller('preferencesHistory',
|
||||
function($scope, $log, $timeout, storageService, go, profileService) {
|
||||
function($scope, $log, $timeout, storageService, go, profileService, lodash) {
|
||||
var fc = profileService.focusedClient;
|
||||
var c = fc.credentials;
|
||||
this.csvReady = false;
|
||||
|
||||
|
||||
this.csvHistory = function(cb) {
|
||||
|
||||
function formatDate(date) {
|
||||
var dateObj = new Date(date);
|
||||
if (!dateObj) {
|
||||
$log.debug('Error formating a date');
|
||||
return 'DateError'
|
||||
}
|
||||
if (!dateObj.toJSON()) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return dateObj.toJSON();
|
||||
}
|
||||
|
||||
var step = 6;
|
||||
var unique = {};
|
||||
function getHistory(cb) {
|
||||
storageService.getTxHistory(c.walletId, function(err, txs) {
|
||||
if (err) return cb(err);
|
||||
|
||||
var txsFromLocal = [];
|
||||
try {
|
||||
txsFromLocal = JSON.parse(txs);
|
||||
} catch (ex) {
|
||||
$log.warn(ex);
|
||||
}
|
||||
|
||||
allTxs.push(txsFromLocal);
|
||||
return cb(null, lodash.flatten(allTxs));
|
||||
});
|
||||
}
|
||||
|
||||
var fc = profileService.focusedClient;
|
||||
var c = fc.credentials;
|
||||
|
||||
if (!fc.isComplete())
|
||||
return;
|
||||
|
||||
var self = this;
|
||||
var allTxs = [];
|
||||
|
||||
$log.debug('Generating CSV from History');
|
||||
getHistory(function(err, txs) {
|
||||
if (err || !txs) {
|
||||
$log.warn('Failed to generate CSV:', err);
|
||||
if (cb) return cb(err);
|
||||
return;
|
||||
}
|
||||
|
||||
$log.debug('Wallet Transaction History Length:', txs.length);
|
||||
|
||||
self.satToUnit = 1 / self.unitToSatoshi;
|
||||
var data = txs;
|
||||
var satToBtc = 1 / 100000000;
|
||||
self.csvContent = [];
|
||||
self.csvFilename = 'Copay-' + (self.alias || self.walletName) + '.csv';
|
||||
self.csvHeader = ['Date', 'Destination', 'Description', 'Amount', 'Currency', 'Txid', 'Creator', 'Copayers', 'Comment'];
|
||||
|
||||
var _amount, _note, _copayers, _creator, _comment;
|
||||
data.forEach(function(it, index) {
|
||||
var amount = it.amount;
|
||||
|
||||
if (it.action == 'moved')
|
||||
amount = 0;
|
||||
|
||||
_copayers = '';
|
||||
_creator = '';
|
||||
|
||||
if (it.actions && it.actions.length > 1) {
|
||||
for (var i = 0; i < it.actions.length; i++) {
|
||||
_copayers += it.actions[i].copayerName + ':' + it.actions[i].type + ' - ';
|
||||
}
|
||||
_creator = (it.creatorName && it.creatorName != 'undefined') ? it.creatorName : '';
|
||||
}
|
||||
_amount = (it.action == 'sent' ? '-' : '') + (amount * satToBtc).toFixed(8);
|
||||
_note = it.message || '';
|
||||
_comment = it.note ? it.note.body : '';
|
||||
|
||||
if (it.action == 'moved')
|
||||
_note += ' Moved:' + (it.amount * satToBtc).toFixed(8)
|
||||
|
||||
self.csvContent.push({
|
||||
'Date': formatDate(it.time * 1000),
|
||||
'Destination': it.addressTo || '',
|
||||
'Description': _note,
|
||||
'Amount': _amount,
|
||||
'Currency': 'BTC',
|
||||
'Txid': it.txid,
|
||||
'Creator': _creator,
|
||||
'Copayers': _copayers,
|
||||
'Comment': _comment
|
||||
});
|
||||
|
||||
if (it.fees && (it.action == 'moved' || it.action == 'sent')) {
|
||||
var _fee = (it.fees * satToBtc).toFixed(8)
|
||||
self.csvContent.push({
|
||||
'Date': formatDate(it.time * 1000),
|
||||
'Destination': 'Bitcoin Network Fees',
|
||||
'Description': '',
|
||||
'Amount': '-' + _fee,
|
||||
'Currency': 'BTC',
|
||||
'Txid': '',
|
||||
'Creator': '',
|
||||
'Copayers': ''
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
self.csvReady = true;
|
||||
if (cb)
|
||||
return cb();
|
||||
return;
|
||||
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
this.clearTransactionHistory = function() {
|
||||
storageService.removeTxHistory(c.walletId, function(err) {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
'use strict';
|
||||
|
||||
angular.module('copayApp.controllers').controller('preferencesLanguageController',
|
||||
function($scope, $log, $timeout, configService, uxLanguage, go) {
|
||||
function($scope, $log, $timeout, configService, profileService, uxLanguage, walletService, go) {
|
||||
|
||||
this.availableLanguages = uxLanguage.getLanguages();
|
||||
this.currentLanguage = uxLanguage.getCurrentLanguage();
|
||||
|
|
@ -19,10 +19,18 @@ angular.module('copayApp.controllers').controller('preferencesLanguageController
|
|||
configService.set(opts, function(err) {
|
||||
if (err) $log.warn(err);
|
||||
go.preferencesGlobal();
|
||||
$scope.$emit('Local/LanguageSettingUpdated');
|
||||
$timeout(function() {
|
||||
$scope.$apply();
|
||||
}, 100);
|
||||
|
||||
|
||||
uxLanguage.update(function() {
|
||||
$timeout(function() {
|
||||
$scope.$apply();
|
||||
}, 100);
|
||||
|
||||
walletService.updateRemotePreferences(profileService.getClients(), {},
|
||||
function() {
|
||||
$log.debug('Remote preferences saved');
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
'use strict';
|
||||
|
||||
angular.module('copayApp.controllers').controller('preferencesUnitController',
|
||||
function($scope, $timeout, $log, configService, go) {
|
||||
function($scope, $timeout, $log, configService, go, walletService, profileService) {
|
||||
var config = configService.getSync();
|
||||
this.unitName = config.wallet.settings.unitName;
|
||||
this.unitOpts = [
|
||||
|
|
@ -54,6 +54,9 @@ angular.module('copayApp.controllers').controller('preferencesUnitController',
|
|||
if (err) $log.warn(err);
|
||||
go.preferencesGlobal();
|
||||
$scope.$emit('Local/UnitSettingUpdated');
|
||||
walletService.updateRemotePreferences(profileService.getClients(), {}, function() {
|
||||
$log.debug('Remote preferences saved');
|
||||
});
|
||||
$timeout(function() {
|
||||
$scope.$apply();
|
||||
}, 100);
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
'use strict';
|
||||
|
||||
angular.module('copayApp.controllers').controller('sidebarController',
|
||||
function($rootScope, $timeout, lodash, profileService, configService, go, platformInfo) {
|
||||
function($rootScope, $timeout, $ionicScrollDelegate, lodash, profileService, configService, go, platformInfo) {
|
||||
var self = this;
|
||||
self.isWindowsPhoneApp = platformInfo.isWP && platformInfo.isCordova;
|
||||
self.walletSelection = false;
|
||||
|
|
@ -20,11 +20,6 @@ angular.module('copayApp.controllers').controller('sidebarController',
|
|||
self.setWallets();
|
||||
});
|
||||
|
||||
|
||||
self.closeMenu = function() {
|
||||
go.swipe();
|
||||
};
|
||||
|
||||
self.signout = function() {
|
||||
profileService.signout();
|
||||
};
|
||||
|
|
@ -33,6 +28,7 @@ angular.module('copayApp.controllers').controller('sidebarController',
|
|||
if (selectedWalletId == currentWalletId) return;
|
||||
self.walletSelection = false;
|
||||
profileService.setAndStoreFocus(selectedWalletId, function() {});
|
||||
$ionicScrollDelegate.$getByHandle('transactions').scrollTop();
|
||||
};
|
||||
|
||||
self.toggleWalletSelection = function() {
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
'use strict';
|
||||
|
||||
angular.module('copayApp.controllers').controller('walletHomeController', function($scope, $rootScope, $interval, $timeout, $filter, $modal, $log, notification, txStatus, profileService, lodash, configService, rateService, storageService, bitcore, gettext, gettextCatalog, platformInfo, addressService, ledger, bwsError, confirmDialog, txFormatService, animationService, addressbookService, go, feeService, walletService, fingerprintService, nodeWebkit) {
|
||||
angular.module('copayApp.controllers').controller('walletHomeController', function($scope, $rootScope, $interval, $timeout, $filter, $modal, $log, $ionicModal, notification, txStatus, profileService, lodash, configService, rateService, storageService, bitcore, gettext, gettextCatalog, platformInfo, addressService, ledger, bwsError, confirmDialog, txFormatService, animationService, addressbookService, go, feeService, walletService, fingerprintService, nodeWebkit) {
|
||||
|
||||
var isCordova = platformInfo.isCordova;
|
||||
var isWP = platformInfo.isWP;
|
||||
var isAndroid = platformInfo.isAndroid;
|
||||
var isChromeApp = platformInfo.isChromeApp;
|
||||
var isChromeApp = platformInfo.isChromeApp;
|
||||
|
||||
var self = this;
|
||||
window.ignoreMobilePause = false;
|
||||
|
|
@ -144,178 +144,16 @@ angular.module('copayApp.controllers').controller('walletHomeController', functi
|
|||
var cancel_msg = gettextCatalog.getString('Cancel');
|
||||
var confirm_msg = gettextCatalog.getString('Confirm');
|
||||
|
||||
this.openDestinationAddressModal = function(wallets, address) {
|
||||
$rootScope.modalOpened = true;
|
||||
var fc = profileService.focusedClient;
|
||||
self.lockAddress = false;
|
||||
self._address = null;
|
||||
this.openAddressbookModal = function(wallets, address) {
|
||||
$scope.wallets = wallets;
|
||||
$scope.address = address;
|
||||
$scope.self = self;
|
||||
|
||||
var ModalInstanceCtrl = function($scope, $modalInstance) {
|
||||
$scope.wallets = wallets;
|
||||
$scope.editAddressbook = false;
|
||||
$scope.addAddressbookEntry = false;
|
||||
$scope.selectedAddressbook = {};
|
||||
$scope.newAddress = address;
|
||||
$scope.walletName = fc.credentials.walletName;
|
||||
$scope.color = fc.backgroundColor;
|
||||
$scope.addressbook = {
|
||||
'address': ($scope.newAddress || ''),
|
||||
'label': ''
|
||||
};
|
||||
|
||||
$scope.checkClipboard = function() {
|
||||
if (!$scope.newAddress) {
|
||||
getClipboard(function(value) {
|
||||
$scope.newAddress = value;
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
$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);
|
||||
};
|
||||
|
||||
$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() {
|
||||
$scope.error = null;
|
||||
$scope.addressbook = {
|
||||
'address': ($scope.newAddress || ''),
|
||||
'label': ''
|
||||
};
|
||||
$scope.addAddressbookEntry = !$scope.addAddressbookEntry;
|
||||
};
|
||||
|
||||
$scope.list = function() {
|
||||
$scope.error = null;
|
||||
addressbookService.list(function(err, ab) {
|
||||
if (err) {
|
||||
$scope.error = err;
|
||||
return;
|
||||
}
|
||||
$scope.list = ab;
|
||||
$timeout(function() {
|
||||
$scope.$digest();
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
$scope.add = function(addressbook) {
|
||||
$scope.error = null;
|
||||
$timeout(function() {
|
||||
addressbookService.add(addressbook, function(err, ab) {
|
||||
if (err) {
|
||||
$scope.error = err;
|
||||
return;
|
||||
}
|
||||
$rootScope.$emit('Local/AddressbookUpdated', ab);
|
||||
$scope.list = ab;
|
||||
$scope.editAddressbook = true;
|
||||
$scope.toggleEditAddressbook();
|
||||
$scope.$digest();
|
||||
});
|
||||
}, 100);
|
||||
};
|
||||
|
||||
$scope.remove = function(addr) {
|
||||
$scope.error = null;
|
||||
$timeout(function() {
|
||||
addressbookService.remove(addr, function(err, ab) {
|
||||
if (err) {
|
||||
$scope.error = err;
|
||||
return;
|
||||
}
|
||||
$rootScope.$emit('Local/AddressbookUpdated', ab);
|
||||
$scope.list = ab;
|
||||
$scope.$digest();
|
||||
});
|
||||
}, 100);
|
||||
};
|
||||
|
||||
$scope.cancel = function() {
|
||||
$modalInstance.dismiss('cancel');
|
||||
};
|
||||
|
||||
$scope.selectWallet = function(walletId, walletName) {
|
||||
var client = profileService.getClient(walletId);
|
||||
$scope.errorSelectedWallet = {};
|
||||
|
||||
walletService.isReady(client, function(err) {
|
||||
if (err) $scope.errorSelectedWallet[walletId] = err;
|
||||
else {
|
||||
$scope.gettingAddress = true;
|
||||
$scope.selectedWalletName = walletName;
|
||||
$timeout(function() {
|
||||
$scope.$apply();
|
||||
});
|
||||
|
||||
addressService.getAddress(walletId, false, function(err, addr) {
|
||||
$scope.gettingAddress = false;
|
||||
|
||||
if (err) {
|
||||
self.error = err;
|
||||
$modalInstance.dismiss('cancel');
|
||||
return;
|
||||
}
|
||||
|
||||
$modalInstance.close(addr);
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
var modalInstance = $modal.open({
|
||||
templateUrl: 'views/modals/destination-address.html',
|
||||
windowClass: animationService.modalAnimated.slideUp,
|
||||
controller: ModalInstanceCtrl,
|
||||
});
|
||||
|
||||
var disableCloseModal = $rootScope.$on('closeModal', function() {
|
||||
modalInstance.dismiss('cancel');
|
||||
});
|
||||
|
||||
modalInstance.result.finally(function() {
|
||||
$rootScope.modalOpened = false;
|
||||
disableCloseModal();
|
||||
var m = angular.element(document.getElementsByClassName('reveal-modal'));
|
||||
m.addClass(animationService.modalAnimated.slideOutDown);
|
||||
});
|
||||
|
||||
modalInstance.result.then(function(addr) {
|
||||
if (addr) {
|
||||
self.setForm(addr);
|
||||
}
|
||||
}, function() {
|
||||
// onRejected
|
||||
self.resetForm();
|
||||
$ionicModal.fromTemplateUrl('views/modals/addressbook.html', {
|
||||
scope: $scope
|
||||
}).then(function(modal) {
|
||||
$scope.addressbookModal = modal;
|
||||
$scope.addressbookModal.show();
|
||||
});
|
||||
};
|
||||
|
||||
|
|
@ -323,260 +161,21 @@ angular.module('copayApp.controllers').controller('walletHomeController', functi
|
|||
// isGlidera flag is a security measure so glidera status is not
|
||||
// only determined by the tx.message
|
||||
this.openTxpModal = function(tx, copayers, isGlidera) {
|
||||
$rootScope.modalOpened = true;
|
||||
var self = this;
|
||||
var fc = profileService.focusedClient;
|
||||
var currentSpendUnconfirmed = configWallet.spendUnconfirmed;
|
||||
var ModalInstanceCtrl = function($scope, $modalInstance) {
|
||||
$scope.paymentExpired = null;
|
||||
checkPaypro();
|
||||
$scope.error = null;
|
||||
$scope.copayers = copayers
|
||||
$scope.copayerId = fc.credentials.copayerId;
|
||||
$scope.canSign = fc.canSign() || fc.isPrivKeyExternal();
|
||||
$scope.loading = null;
|
||||
$scope.color = fc.backgroundColor;
|
||||
$scope.isShared = fc.credentials.n > 1;
|
||||
var now = Math.floor(Date.now() / 1000);
|
||||
$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;
|
||||
|
||||
// ToDo: use tx.customData instead of tx.message
|
||||
if (tx.message === 'Glidera transaction' && isGlidera) {
|
||||
tx.isGlidera = true;
|
||||
if (tx.canBeRemoved) {
|
||||
tx.canBeRemoved = (Date.now() / 1000 - (tx.ts || tx.createdOn)) > GLIDERA_LOCK_TIME;
|
||||
}
|
||||
}
|
||||
$scope.tx = tx;
|
||||
$scope.currentSpendUnconfirmed = currentSpendUnconfirmed;
|
||||
|
||||
$scope.getShortNetworkName = function() {
|
||||
return fc.credentials.networkName.substring(0, 4);
|
||||
};
|
||||
|
||||
function checkPaypro() {
|
||||
if (tx.payProUrl && !isChromeApp) {
|
||||
fc.fetchPayPro({
|
||||
payProUrl: tx.payProUrl,
|
||||
}, function(err, paypro) {
|
||||
if (err) return;
|
||||
tx.paypro = paypro;
|
||||
paymentTimeControl(tx.paypro.expires);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
function paymentTimeControl(expirationTime) {
|
||||
$scope.paymentExpired = false;
|
||||
setExpirationTime();
|
||||
|
||||
self.countDown = $interval(function() {
|
||||
setExpirationTime();
|
||||
}, 1000);
|
||||
|
||||
function setExpirationTime() {
|
||||
var now = Math.floor(Date.now() / 1000);
|
||||
if (now > expirationTime) {
|
||||
$scope.paymentExpired = true;
|
||||
if (self.countDown) $interval.cancel(self.countDown);
|
||||
return;
|
||||
}
|
||||
var totalSecs = expirationTime - now;
|
||||
var m = Math.floor(totalSecs / 60);
|
||||
var s = totalSecs % 60;
|
||||
$scope.expires = ('0' + m).slice(-2) + ":" + ('0' + s).slice(-2);
|
||||
};
|
||||
};
|
||||
|
||||
lodash.each(['TxProposalRejectedBy', 'TxProposalAcceptedBy', 'transactionProposalRemoved', 'TxProposalRemoved', 'NewOutgoingTx', 'UpdateTx'], function(eventName) {
|
||||
$rootScope.$on(eventName, function() {
|
||||
fc.getTx($scope.tx.id, function(err, tx) {
|
||||
if (err) {
|
||||
if (err.message && err.message == 'TX_NOT_FOUND' &&
|
||||
(eventName == 'transactionProposalRemoved' || eventName == 'TxProposalRemoved')) {
|
||||
$scope.tx.removed = true;
|
||||
$scope.tx.canBeRemoved = false;
|
||||
$scope.tx.pendingForUs = false;
|
||||
$scope.$apply();
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
var action = lodash.find(tx.actions, {
|
||||
copayerId: fc.credentials.copayerId
|
||||
});
|
||||
|
||||
$scope.tx = txFormatService.processTx(tx);
|
||||
|
||||
if (!action && tx.status == 'pending')
|
||||
$scope.tx.pendingForUs = true;
|
||||
|
||||
$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) {
|
||||
var client = profileService.focusedClient;
|
||||
$scope.error = null;
|
||||
$scope.loading = true;
|
||||
|
||||
fingerprintService.check(client, function(err) {
|
||||
if (err) {
|
||||
$scope.loading = false;
|
||||
$scope.error = err;
|
||||
return;
|
||||
}
|
||||
|
||||
handleEncryptedWallet(client, function(err) {
|
||||
if (err) {
|
||||
$scope.loading = false;
|
||||
$scope.error = err;
|
||||
return;
|
||||
}
|
||||
|
||||
walletService.signTx(client, txp, function(err, signedTxp) {
|
||||
walletService.lock(client);
|
||||
if (err) {
|
||||
$scope.loading = false;
|
||||
$scope.error = err;
|
||||
return;
|
||||
}
|
||||
|
||||
if (signedTxp.status == 'accepted') {
|
||||
walletService.broadcastTx(client, signedTxp, function(err, broadcastedTxp) {
|
||||
$scope.loading = false;
|
||||
$scope.$emit('UpdateTx');
|
||||
$modalInstance.close(broadcastedTxp);
|
||||
if (err) {
|
||||
$scope.loading = false;
|
||||
$scope.error = err;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
$scope.loading = false;
|
||||
$scope.$emit('UpdateTx');
|
||||
$modalInstance.close(signedTxp);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
$scope.reject = function(txp) {
|
||||
var client = profileService.focusedClient;
|
||||
self.setOngoingProcess(gettextCatalog.getString('Rejecting payment'));
|
||||
$scope.loading = true;
|
||||
$scope.error = null;
|
||||
|
||||
$timeout(function() {
|
||||
walletService.rejectTx(client, txp, function(err, txpr) {
|
||||
self.setOngoingProcess();
|
||||
$scope.loading = false;
|
||||
if (err) {
|
||||
$scope.$emit('UpdateTx');
|
||||
$scope.error = bwsError.msg(err, gettextCatalog.getString('Could not reject payment'));
|
||||
$scope.$digest();
|
||||
} else {
|
||||
$modalInstance.close(txpr);
|
||||
}
|
||||
});
|
||||
}, 100);
|
||||
};
|
||||
|
||||
|
||||
$scope.remove = function(txp) {
|
||||
var client = profileService.focusedClient;
|
||||
self.setOngoingProcess(gettextCatalog.getString('Deleting payment'));
|
||||
$scope.loading = true;
|
||||
$scope.error = null;
|
||||
$timeout(function() {
|
||||
walletService.removeTx(client, txp, function(err) {
|
||||
self.setOngoingProcess();
|
||||
$scope.loading = false;
|
||||
|
||||
// Hacky: request tries to parse an empty response
|
||||
if (err && !(err.message && err.message.match(/Unexpected/))) {
|
||||
$scope.$emit('UpdateTx');
|
||||
$scope.error = bwsError.msg(err, gettextCatalog.getString('Could not delete payment proposal'));
|
||||
$scope.$digest();
|
||||
return;
|
||||
}
|
||||
$modalInstance.close();
|
||||
});
|
||||
}, 100);
|
||||
};
|
||||
|
||||
$scope.broadcast = function(txp) {
|
||||
var client = profileService.focusedClient;
|
||||
self.setOngoingProcess(gettextCatalog.getString('Broadcasting Payment'));
|
||||
$scope.loading = true;
|
||||
$scope.error = null;
|
||||
$timeout(function() {
|
||||
walletService.broadcastTx(client, txp, function(err, txpb) {
|
||||
self.setOngoingProcess();
|
||||
$scope.loading = false;
|
||||
if (err) {
|
||||
$scope.error = bwsError.msg(err, gettextCatalog.getString('Could not broadcast payment'));
|
||||
$scope.$digest();
|
||||
return;
|
||||
}
|
||||
$modalInstance.close(txpb);
|
||||
});
|
||||
}, 100);
|
||||
};
|
||||
|
||||
$scope.copyToClipboard = function(addr) {
|
||||
if (!addr) return;
|
||||
self.copyToClipboard(addr);
|
||||
};
|
||||
|
||||
$scope.cancel = lodash.debounce(function() {
|
||||
$modalInstance.dismiss('cancel');
|
||||
}, 0, 1000);
|
||||
};
|
||||
|
||||
var modalInstance = $modal.open({
|
||||
templateUrl: 'views/modals/txp-details.html',
|
||||
windowClass: animationService.modalAnimated.slideRight,
|
||||
controller: ModalInstanceCtrl,
|
||||
$ionicModal.fromTemplateUrl('views/modals/txp-details.html', {
|
||||
scope: $scope
|
||||
}).then(function(modal) {
|
||||
$scope.txpDetailsModal = modal;
|
||||
$scope.txpDetailsModal.show();
|
||||
});
|
||||
|
||||
var disableCloseModal = $rootScope.$on('closeModal', function() {
|
||||
modalInstance.dismiss('cancel');
|
||||
});
|
||||
|
||||
modalInstance.result.finally(function() {
|
||||
$rootScope.modalOpened = false;
|
||||
disableCloseModal();
|
||||
var m = angular.element(document.getElementsByClassName('reveal-modal'));
|
||||
m.addClass(animationService.modalAnimated.slideOutRight);
|
||||
});
|
||||
|
||||
modalInstance.result.then(function(txp) {
|
||||
self.setOngoingProcess();
|
||||
if (txp) {
|
||||
txStatus.notify(txp, function() {
|
||||
$scope.$emit('Local/TxProposalAction', txp.status == 'broadcasted');
|
||||
});
|
||||
} else {
|
||||
$timeout(function() {
|
||||
$scope.$emit('Local/TxProposalAction');
|
||||
}, 100);
|
||||
}
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
this.setAddress = function(forceNew) {
|
||||
|
|
@ -626,105 +225,17 @@ angular.module('copayApp.controllers').controller('walletHomeController', functi
|
|||
};
|
||||
|
||||
this.openCustomizedAmountModal = function(addr) {
|
||||
$rootScope.modalOpened = true;
|
||||
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;
|
||||
$scope.isCordova = isCordova;
|
||||
|
||||
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);
|
||||
} else {
|
||||
$scope.customAmount = null;
|
||||
}
|
||||
},
|
||||
enumerable: true,
|
||||
configurable: true
|
||||
});
|
||||
$scope.color = fc.backgroundColor;
|
||||
$scope.self = self;
|
||||
$scope.addr = addr;
|
||||
|
||||
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 {
|
||||
$scope.customAlternative = null;
|
||||
}
|
||||
$scope.alternativeAmount = $scope.customAlternative;
|
||||
},
|
||||
enumerable: true,
|
||||
configurable: true
|
||||
});
|
||||
|
||||
$scope.submitForm = function(form) {
|
||||
var satToBtc = 1 / 100000000;
|
||||
var amount = form.amount.$modelValue;
|
||||
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') {
|
||||
amount = (amountSat * satToBtc).toFixed(8);
|
||||
}
|
||||
$scope.customizedAmountBtc = amount;
|
||||
}, 1);
|
||||
};
|
||||
|
||||
$scope.toggleAlternative = function() {
|
||||
$scope.showAlternative = !$scope.showAlternative;
|
||||
};
|
||||
|
||||
$scope.shareAddress = function(uri) {
|
||||
if (isCordova) {
|
||||
if (isAndroid || isWP) {
|
||||
window.ignoreMobilePause = true;
|
||||
}
|
||||
window.plugins.socialsharing.share(uri, null, null, null);
|
||||
}
|
||||
};
|
||||
|
||||
$scope.cancel = function() {
|
||||
$modalInstance.dismiss('cancel');
|
||||
};
|
||||
};
|
||||
|
||||
var modalInstance = $modal.open({
|
||||
templateUrl: 'views/modals/customized-amount.html',
|
||||
windowClass: animationService.modalAnimated.slideUp,
|
||||
controller: ModalInstanceCtrl,
|
||||
});
|
||||
|
||||
var disableCloseModal = $rootScope.$on('closeModal', function() {
|
||||
modalInstance.dismiss('cancel');
|
||||
});
|
||||
|
||||
modalInstance.result.finally(function() {
|
||||
$rootScope.modalOpened = false;
|
||||
disableCloseModal();
|
||||
var m = angular.element(document.getElementsByClassName('reveal-modal'));
|
||||
m.addClass(animationService.modalAnimated.slideOutDown);
|
||||
$ionicModal.fromTemplateUrl('views/modals/customized-amount.html', {
|
||||
scope: $scope
|
||||
}).then(function(modal) {
|
||||
$scope.customAmountModal = modal;
|
||||
$scope.customAmountModal.show();
|
||||
});
|
||||
};
|
||||
|
||||
|
|
@ -955,7 +466,7 @@ angular.module('copayApp.controllers').controller('walletHomeController', functi
|
|||
txp.sendMax = true;
|
||||
txp.inputs = self.sendMaxInfo.inputs;
|
||||
txp.fee = self.sendMaxInfo.fee;
|
||||
}else {
|
||||
} else {
|
||||
txp.amount = amount;
|
||||
}
|
||||
|
||||
|
|
@ -988,7 +499,7 @@ angular.module('copayApp.controllers').controller('walletHomeController', functi
|
|||
});
|
||||
|
||||
}, 100);
|
||||
};
|
||||
};
|
||||
|
||||
this.confirmTx = function(txp) {
|
||||
var client = profileService.focusedClient;
|
||||
|
|
@ -1018,11 +529,11 @@ angular.module('copayApp.controllers').controller('walletHomeController', functi
|
|||
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'));
|
||||
err.message ?
|
||||
err.message :
|
||||
gettext('The payment was created but could not be completed. Please try again from home screen'));
|
||||
}
|
||||
|
||||
|
||||
if (signedTxp.status == 'accepted') {
|
||||
self.setOngoingProcess(gettextCatalog.getString('Broadcasting transaction'));
|
||||
walletService.broadcastTx(client, signedTxp, function(err, broadcastedTxp) {
|
||||
|
|
@ -1049,6 +560,20 @@ angular.module('copayApp.controllers').controller('walletHomeController', functi
|
|||
});
|
||||
};
|
||||
|
||||
$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();
|
||||
});
|
||||
};
|
||||
|
||||
this.setForm = function(to, amount, comment) {
|
||||
var form = $scope.sendForm;
|
||||
if (to) {
|
||||
|
|
@ -1106,37 +631,16 @@ angular.module('copayApp.controllers').controller('walletHomeController', functi
|
|||
};
|
||||
|
||||
this.openPPModal = function(paypro) {
|
||||
$rootScope.modalOpened = true;
|
||||
var ModalInstanceCtrl = function($scope, $modalInstance) {
|
||||
var fc = profileService.focusedClient;
|
||||
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;
|
||||
var fc = profileService.focusedClient;
|
||||
$scope.color = fc.backgroundColor;
|
||||
$scope.self = self;
|
||||
$scope.paypro = paypro;
|
||||
|
||||
$scope.cancel = function() {
|
||||
$modalInstance.dismiss('cancel');
|
||||
};
|
||||
};
|
||||
var modalInstance = $modal.open({
|
||||
templateUrl: 'views/modals/paypro.html',
|
||||
windowClass: animationService.modalAnimated.slideUp,
|
||||
controller: ModalInstanceCtrl,
|
||||
});
|
||||
|
||||
var disableCloseModal = $rootScope.$on('closeModal', function() {
|
||||
modalInstance.dismiss('cancel');
|
||||
});
|
||||
|
||||
modalInstance.result.finally(function() {
|
||||
$rootScope.modalOpened = false;
|
||||
disableCloseModal();
|
||||
var m = angular.element(document.getElementsByClassName('reveal-modal'));
|
||||
m.addClass(animationService.modalAnimated.slideOutDown);
|
||||
$ionicModal.fromTemplateUrl('views/modals/paypro.html', {
|
||||
scope: $scope
|
||||
}).then(function(modal) {
|
||||
$scope.payproModal = modal;
|
||||
$scope.payproModal.show();
|
||||
});
|
||||
};
|
||||
|
||||
|
|
@ -1308,8 +812,19 @@ angular.module('copayApp.controllers').controller('walletHomeController', functi
|
|||
return this.alternativeIsoCode;
|
||||
};
|
||||
|
||||
this.openTxModal = function(tx) {
|
||||
$rootScope.$emit('Local/TxModal', tx);
|
||||
this.openTxModal = function(btx) {
|
||||
var self = this;
|
||||
|
||||
$scope.btx = 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) {
|
||||
|
|
|
|||
|
|
@ -1,67 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
/*
|
||||
* This is a modification from https://github.com/angular/angular.js/blob/master/src/ngTouch/swipe.js
|
||||
*/
|
||||
|
||||
|
||||
function makeSwipeDirective(directiveName, direction, eventName) {
|
||||
angular.module('copayApp.directives')
|
||||
.directive(directiveName, ['$parse', '$swipe',
|
||||
function($parse, $swipe) {
|
||||
// The maximum vertical delta for a swipe should be less than 75px.
|
||||
var MAX_VERTICAL_DISTANCE = 75;
|
||||
// Vertical distance should not be more than a fraction of the horizontal distance.
|
||||
var MAX_VERTICAL_RATIO = 0.4;
|
||||
// At least a 30px lateral motion is necessary for a swipe.
|
||||
var MIN_HORIZONTAL_DISTANCE = 30;
|
||||
|
||||
return function(scope, element, attr) {
|
||||
var swipeHandler = $parse(attr[directiveName]);
|
||||
|
||||
var startCoords, valid;
|
||||
|
||||
function validSwipe(coords) {
|
||||
// Check that it's within the coordinates.
|
||||
// Absolute vertical distance must be within tolerances.
|
||||
// Horizontal distance, we take the current X - the starting X.
|
||||
// This is negative for leftward swipes and positive for rightward swipes.
|
||||
// After multiplying by the direction (-1 for left, +1 for right), legal swipes
|
||||
// (ie. same direction as the directive wants) will have a positive delta and
|
||||
// illegal ones a negative delta.
|
||||
// Therefore this delta must be positive, and larger than the minimum.
|
||||
if (!startCoords) return false;
|
||||
var deltaY = Math.abs(coords.y - startCoords.y);
|
||||
var deltaX = (coords.x - startCoords.x) * direction;
|
||||
return valid && // Short circuit for already-invalidated swipes.
|
||||
deltaY < MAX_VERTICAL_DISTANCE &&
|
||||
deltaX > 0 &&
|
||||
deltaX > MIN_HORIZONTAL_DISTANCE &&
|
||||
deltaY / deltaX < MAX_VERTICAL_RATIO;
|
||||
}
|
||||
|
||||
var pointerTypes = ['touch'];
|
||||
$swipe.bind(element, {
|
||||
'start': function(coords, event) {
|
||||
startCoords = coords;
|
||||
valid = true;
|
||||
},
|
||||
'move': function(coords, event) {
|
||||
if (validSwipe(coords)) {
|
||||
scope.$apply(function() {
|
||||
element.triggerHandler(eventName);
|
||||
swipeHandler(scope, {
|
||||
$event: event
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
}, pointerTypes);
|
||||
};
|
||||
}
|
||||
]);
|
||||
}
|
||||
|
||||
// Left is negative X-coordinate, right is positive.
|
||||
makeSwipeDirective('ngSwipeLeft', -1, 'swipeleft');
|
||||
makeSwipeDirective('ngSwipeRight', 1, 'swiperight');
|
||||
|
|
@ -27,60 +27,6 @@ angular.element(document).ready(function() {
|
|||
|
||||
document.addEventListener('deviceready', function() {
|
||||
|
||||
var secondBackButtonPress = 'false';
|
||||
var intval = setInterval(function() {
|
||||
secondBackButtonPress = 'false';
|
||||
}, 5000);
|
||||
|
||||
document.addEventListener('pause', function() {
|
||||
if (!window.ignoreMobilePause) {
|
||||
setTimeout(function() {
|
||||
window.location = '#/cordova/pause///';
|
||||
}, 100);
|
||||
}
|
||||
}, false);
|
||||
|
||||
document.addEventListener('resume', function() {
|
||||
if (!window.ignoreMobilePause) {
|
||||
setTimeout(function() {
|
||||
window.location = '#/cordova/resume///';
|
||||
}, 100);
|
||||
}
|
||||
setTimeout(function() {
|
||||
var loc = window.location;
|
||||
var ignoreMobilePause = loc.toString().match(/(buy|sell|buycoinbase|sellcoinbase)/) ? true : false;
|
||||
window.ignoreMobilePause = ignoreMobilePause;
|
||||
}, 100);
|
||||
}, false);
|
||||
|
||||
// Back button event
|
||||
|
||||
document.addEventListener('backbutton', function() {
|
||||
|
||||
var loc = window.location;
|
||||
var fromDisclaimer = loc.toString().match(/disclaimer/) ? 'true' : '';
|
||||
var fromHome = loc.toString().match(/index\.html#\/$/) ? 'true' : '';
|
||||
if (!window.ignoreMobilePause) {
|
||||
window.location = '#/cordova/backbutton/' + fromHome + '/' + fromDisclaimer + '/' + secondBackButtonPress;
|
||||
if (secondBackButtonPress == 'true') {
|
||||
clearInterval(intval);
|
||||
} else {
|
||||
secondBackButtonPress = 'true';
|
||||
}
|
||||
}
|
||||
setTimeout(function() {
|
||||
window.ignoreMobilePause = false;
|
||||
}, 100);
|
||||
}, false);
|
||||
|
||||
document.addEventListener('menubutton', function() {
|
||||
window.location = '#/preferences';
|
||||
}, false);
|
||||
|
||||
setTimeout(function() {
|
||||
navigator.splashscreen.hide();
|
||||
}, 1000);
|
||||
|
||||
window.plugins.webintent.getUri(handleBitcoinURI);
|
||||
window.plugins.webintent.onNewIntent(handleBitcoinURI);
|
||||
window.handleOpenURL = handleBitcoinURI;
|
||||
|
|
@ -96,6 +42,7 @@ angular.element(document).ready(function() {
|
|||
|
||||
startAngular();
|
||||
}, false);
|
||||
|
||||
} else {
|
||||
try {
|
||||
window.handleOpenURL = handleBitcoinURI;
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ Profile.fromObj = function(obj) {
|
|||
x.credentials = obj.credentials;
|
||||
x.disclaimerAccepted = obj.disclaimerAccepted;
|
||||
x.checked = obj.checked || {};
|
||||
x.checkedUA = obj.checkedUA || {};
|
||||
|
||||
if (x.credentials[0] && typeof x.credentials[0] != 'object')
|
||||
throw ("credentials should be an object");
|
||||
|
|
@ -39,5 +40,63 @@ Profile.fromString = function(str) {
|
|||
};
|
||||
|
||||
Profile.prototype.toObj = function() {
|
||||
delete this.dirty;
|
||||
return JSON.stringify(this);
|
||||
};
|
||||
|
||||
|
||||
Profile.prototype.hasWallet = function(walletId) {
|
||||
for (var i in this.credentials) {
|
||||
var c = this.credentials[i];
|
||||
if (c.walletId == walletId) return true;
|
||||
};
|
||||
return false;
|
||||
};
|
||||
|
||||
Profile.prototype.isChecked = function(ua, walletId) {
|
||||
return !!(this.checkedUA == ua && this.checked[walletId]);
|
||||
};
|
||||
|
||||
Profile.prototype.setChecked = function(ua, walletId) {
|
||||
if (this.checkedUA != ua) {
|
||||
this.checkedUA = ua;
|
||||
this.checked = {};
|
||||
}
|
||||
this.checked[walletId] = true;
|
||||
this.dirty = true;
|
||||
};
|
||||
|
||||
|
||||
Profile.prototype.addWallet = function(credentials) {
|
||||
if (this.hasWallet(credentials.walletId))
|
||||
return false;
|
||||
|
||||
this.credentials.push(credentials);
|
||||
this.dirty = true;
|
||||
return true;
|
||||
};
|
||||
|
||||
Profile.prototype.updateWallet = function(credentials) {
|
||||
if (!this.hasWallet(credentials.walletId))
|
||||
return false;
|
||||
|
||||
this.credentials = this.credentials.filter(function(c) {
|
||||
return c.walletId != walletId;
|
||||
});
|
||||
|
||||
this.addWallet(credentials);
|
||||
this.dirty = true;
|
||||
return true;
|
||||
};
|
||||
|
||||
Profile.prototype.deleteWallet = function(walletId) {
|
||||
if (!this.hasWallet(walletId))
|
||||
return false;
|
||||
|
||||
this.credentials = this.credentials.filter(function(c) {
|
||||
return c.walletId != walletId;
|
||||
});
|
||||
|
||||
this.dirty = true;
|
||||
return true;
|
||||
};
|
||||
|
|
|
|||
123
src/js/routes.js
123
src/js/routes.js
|
|
@ -12,11 +12,8 @@ if (window && window.navigator) {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
//Setting up route
|
||||
angular
|
||||
.module('copayApp')
|
||||
.config(function(historicLogProvider, $provide, $logProvider, $stateProvider, $urlRouterProvider, $compileProvider) {
|
||||
angular.module('copayApp').config(function(historicLogProvider, $provide, $logProvider, $stateProvider, $urlRouterProvider, $compileProvider) {
|
||||
$urlRouterProvider.otherwise('/');
|
||||
|
||||
$logProvider.debugEnabled(true);
|
||||
|
|
@ -45,7 +42,7 @@ angular
|
|||
v = JSON.stringify(v);
|
||||
}
|
||||
// Trim output in mobile
|
||||
if (window.cordova) {
|
||||
if (platformInfo.isCordova) {
|
||||
v = v.toString();
|
||||
if (v.length > 3000) {
|
||||
v = v.substr(0, 2997) + '...';
|
||||
|
|
@ -59,7 +56,7 @@ angular
|
|||
});
|
||||
|
||||
try {
|
||||
if (window.cordova)
|
||||
if (platformInfo.isCordova)
|
||||
console.log(args.join(' '));
|
||||
|
||||
historicLog.add(level, args.join(' '));
|
||||
|
|
@ -148,11 +145,6 @@ angular
|
|||
},
|
||||
}
|
||||
})
|
||||
.state('importProfile', {
|
||||
url: '/importProfile',
|
||||
templateUrl: 'views/importProfile.html',
|
||||
needProfile: false
|
||||
})
|
||||
.state('importLegacy', {
|
||||
url: '/importLegacy',
|
||||
needProfile: true,
|
||||
|
|
@ -526,49 +518,83 @@ angular
|
|||
templateUrl: 'views/add.html'
|
||||
},
|
||||
}
|
||||
})
|
||||
.state('cordova', {
|
||||
url: '/cordova/:status/:fromHome/:fromDisclaimer/:secondBackButtonPress',
|
||||
views: {
|
||||
'main': {
|
||||
controller: function($rootScope, $state, $stateParams, $timeout, go, platformInfo, gettextCatalog) {
|
||||
|
||||
switch ($stateParams.status) {
|
||||
case 'resume':
|
||||
$rootScope.$emit('Local/Resume');
|
||||
break;
|
||||
case 'backbutton':
|
||||
|
||||
if ($stateParams.fromDisclaimer == 'true')
|
||||
navigator.app.exitApp();
|
||||
|
||||
if (platformInfo.isCordova && $stateParams.fromHome == 'true' && !$rootScope.modalOpened) {
|
||||
if ($stateParams.secondBackButtonPress == 'true') {
|
||||
navigator.app.exitApp();
|
||||
} else {
|
||||
window.plugins.toast.showShortBottom(gettextCatalog.getString('Press again to exit'));
|
||||
}
|
||||
} else {
|
||||
$rootScope.$emit('closeModal');
|
||||
}
|
||||
break;
|
||||
};
|
||||
$timeout(function() {
|
||||
$rootScope.$emit('Local/SetTab', 'walletHome', true);
|
||||
}, 100);
|
||||
go.walletHome();
|
||||
}
|
||||
}
|
||||
},
|
||||
needProfile: false
|
||||
});
|
||||
})
|
||||
.run(function($rootScope, $state, $log, uriHandler, platformInfo, profileService, $timeout, uxLanguage, animationService) {
|
||||
.run(function($rootScope, $state, $log, $timeout, $ionicPlatform, uriHandler, platformInfo, profileService, uxLanguage, animationService, go, gettextCatalog) {
|
||||
|
||||
$ionicPlatform.ready(function() {
|
||||
if (platformInfo.isCordova) {
|
||||
|
||||
$ionicPlatform.registerBackButtonAction(function(event) {
|
||||
event.preventDefault();
|
||||
}, 100);
|
||||
|
||||
var secondBackButtonPress = false;
|
||||
var intval = setInterval(function() {
|
||||
secondBackButtonPress = false;
|
||||
}, 5000);
|
||||
|
||||
$ionicPlatform.on('pause', function() {
|
||||
// Nothing to do
|
||||
});
|
||||
|
||||
$ionicPlatform.on('resume', function() {
|
||||
if (!window.ignoreMobilePause) {
|
||||
$rootScope.$emit('Local/Resume');
|
||||
}
|
||||
setTimeout(function() {
|
||||
var loc = window.location;
|
||||
var ignoreMobilePause = loc.toString().match(/(buy|sell|buycoinbase|sellcoinbase)/) ? true : false;
|
||||
window.ignoreMobilePause = ignoreMobilePause;
|
||||
}, 100);
|
||||
});
|
||||
|
||||
$ionicPlatform.on('backbutton', function(event) {
|
||||
|
||||
var loc = window.location;
|
||||
var fromDisclaimer = loc.toString().match(/disclaimer/) ? 'true' : '';
|
||||
var fromHome = loc.toString().match(/index\.html#\/$/) ? 'true' : '';
|
||||
|
||||
if (fromDisclaimer == 'true')
|
||||
navigator.app.exitApp();
|
||||
|
||||
if (platformInfo.isMobile && fromHome == 'true') {
|
||||
if (secondBackButtonPress)
|
||||
navigator.app.exitApp();
|
||||
else
|
||||
window.plugins.toast.showShortBottom(gettextCatalog.getString('Press again to exit'));
|
||||
}
|
||||
|
||||
if (secondBackButtonPress)
|
||||
clearInterval(intval);
|
||||
else
|
||||
secondBackButtonPress = true;
|
||||
|
||||
$timeout(function() {
|
||||
$rootScope.$emit('Local/SetTab', 'walletHome', true);
|
||||
}, 100);
|
||||
|
||||
go.walletHome();
|
||||
|
||||
setTimeout(function() {
|
||||
window.ignoreMobilePause = false;
|
||||
}, 100);
|
||||
});
|
||||
|
||||
$ionicPlatform.on('menubutton', function() {
|
||||
window.location = '#/preferences';
|
||||
});
|
||||
|
||||
setTimeout(function() {
|
||||
navigator.splashscreen.hide();
|
||||
}, 1000);
|
||||
}
|
||||
});
|
||||
|
||||
uxLanguage.init();
|
||||
|
||||
// Register URI handler, not for mobileApp
|
||||
if (!platformInfo.isCordova) {
|
||||
if (!platformInfo.isMobile) {
|
||||
uriHandler.register();
|
||||
}
|
||||
|
||||
|
|
@ -606,6 +632,7 @@ angular
|
|||
throw new Error(err); // TODO
|
||||
}
|
||||
} else {
|
||||
profileService.storeProfileIfDirty();
|
||||
$log.debug('Profile loaded ... Starting UX.');
|
||||
$state.transitionTo(toState.name || toState, toParams);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,25 +31,18 @@ angular.module('copayApp.services')
|
|||
});
|
||||
$log.debug("case 3");
|
||||
} else {
|
||||
// We're screwed, blob constructor unsupported entirely
|
||||
// We're screwed, blob constructor unsupported entirely
|
||||
$log.debug("Error");
|
||||
}
|
||||
}
|
||||
return out;
|
||||
};
|
||||
|
||||
var a = document.createElement("a");
|
||||
document.body.appendChild(a);
|
||||
a.style.display = "none";
|
||||
|
||||
var a = angular.element('<a></a>');
|
||||
var blob = new NewBlob(ew, 'text/plain;charset=utf-8');
|
||||
var url = window.URL.createObjectURL(blob);
|
||||
a.href = url;
|
||||
a.download = filename;
|
||||
a.click();
|
||||
$timeout(function() {
|
||||
window.URL.revokeObjectURL(url);
|
||||
}, 250);
|
||||
a.attr('href',window.URL.createObjectURL(blob));
|
||||
a.attr('download', filename);
|
||||
a[0].click();
|
||||
return cb();
|
||||
};
|
||||
|
||||
|
|
@ -92,4 +85,4 @@ angular.module('copayApp.services')
|
|||
_download(ew, filename, cb)
|
||||
};
|
||||
return root;
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,36 +1,10 @@
|
|||
'use strict';
|
||||
|
||||
angular.module('copayApp.services').factory('go', function($window, $rootScope, $location, $state, $timeout, $log, profileService, platformInfo, nodeWebkit) {
|
||||
angular.module('copayApp.services').factory('go', function($window, $ionicSideMenuDelegate, $rootScope, $location, $state, $timeout, $log, profileService, platformInfo, nodeWebkit) {
|
||||
var root = {};
|
||||
|
||||
var hideSidebars = function() {
|
||||
if (typeof document === 'undefined')
|
||||
return;
|
||||
|
||||
var elem = document.getElementById('off-canvas-wrap');
|
||||
elem.className = 'off-canvas-wrap';
|
||||
};
|
||||
|
||||
var toggleSidebar = function(invert) {
|
||||
if (typeof document === 'undefined')
|
||||
return;
|
||||
|
||||
var elem = document.getElementById('off-canvas-wrap');
|
||||
var leftbarActive = elem.className.indexOf('move-right') >= 0;
|
||||
|
||||
if (invert) {
|
||||
if (profileService.profile && !$rootScope.hideNavigation) {
|
||||
elem.className = 'off-canvas-wrap move-right';
|
||||
}
|
||||
} else {
|
||||
if (leftbarActive) {
|
||||
hideSidebars();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
root.openExternalLink = function(url, target) {
|
||||
if ( platformInfo.isNW) {
|
||||
if (platformInfo.isNW) {
|
||||
nodeWebkit.openExternalLink(url);
|
||||
} else {
|
||||
target = target || '_blank';
|
||||
|
|
@ -49,11 +23,10 @@ angular.module('copayApp.services').factory('go', function($window, $rootScope,
|
|||
}, function() {
|
||||
if (cb) return cb('animation in progress');
|
||||
});
|
||||
hideSidebars();
|
||||
};
|
||||
|
||||
root.swipe = function(invert) {
|
||||
toggleSidebar(invert);
|
||||
root.toggleLeftMenu = function() {
|
||||
$ionicSideMenuDelegate.toggleLeft();
|
||||
};
|
||||
|
||||
root.walletHome = function() {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
'use strict';
|
||||
angular.module('copayApp.services')
|
||||
.factory('profileService', function profileServiceFactory($rootScope, $timeout, $filter, $log, sjcl, lodash, storageService, bwcService, configService, notificationService, pushNotificationsService, gettext, gettextCatalog, bwsError, uxLanguage, bitcore, platformInfo) {
|
||||
.factory('profileService', function profileServiceFactory($rootScope, $timeout, $filter, $log, sjcl, lodash, storageService, bwcService, configService, notificationService, pushNotificationsService, gettext, gettextCatalog, bwsError, uxLanguage, bitcore, platformInfo, walletService) {
|
||||
|
||||
|
||||
var isChromeApp = platformInfo.isChromeApp;
|
||||
|
|
@ -62,34 +62,22 @@ angular.module('copayApp.services')
|
|||
});
|
||||
};
|
||||
|
||||
root.setWalletClient = function(credentials) {
|
||||
if (root.walletClients[credentials.walletId] &&
|
||||
root.walletClients[credentials.walletId].started) {
|
||||
return;
|
||||
// Adds a wallet client to profileService
|
||||
root.bindWalletClient = function(client, opts) {
|
||||
var opts = opts || {};
|
||||
var walletId = client.credentials.walletId;
|
||||
|
||||
if ((root.walletClients[walletId] && root.walletClients[walletId].started) || opts.force) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
var getBaseURL = function(walletId) {
|
||||
var config = configService.getSync();
|
||||
var defaults = configService.getDefaults();
|
||||
|
||||
return ((config.bwsFor && config.bwsFor[walletId]) || defaults.bws.url);
|
||||
};
|
||||
|
||||
$log.debug('Importing wallet:' + credentials.walletId);
|
||||
var skipKeyValidation = root.profile.checked[credentials.walletId] == platformInfo.ua;
|
||||
var client = bwcService.getClient(JSON.stringify(credentials), {
|
||||
baseurl: getBaseURL(credentials.walletId),
|
||||
skipKeyValidation: skipKeyValidation,
|
||||
});
|
||||
root.walletClients[credentials.walletId] = client;
|
||||
root.walletClients[walletId] = client;
|
||||
root.walletClients[walletId].started = true;
|
||||
root.walletClients[walletId].doNotVerifyPayPro = isChromeApp;
|
||||
|
||||
if (client.incorrectDerivation) {
|
||||
$log.warn('Key Derivation failed for wallet:' + credentials.walletId);
|
||||
storageService.clearLastAddress(credentials.walletId, function() {});
|
||||
} else if (!skipKeyValidation) {
|
||||
root.profile.checked[credentials.walletId] = platformInfo.ua;
|
||||
storageService.storeProfileThrottled(root.profile, function() {});
|
||||
$log.warn('Key Derivation failed for wallet:' + walletId);
|
||||
storageService.clearLastAddress(walletId, function() {});
|
||||
}
|
||||
|
||||
client.removeAllListeners();
|
||||
|
|
@ -100,9 +88,9 @@ angular.module('copayApp.services')
|
|||
client.on('notification', function(n) {
|
||||
$log.debug('BWC Notification:', n);
|
||||
notificationService.newBWCNotification(n,
|
||||
client.credentials.walletId, client.credentials.walletName);
|
||||
walletId, client.credentials.walletName);
|
||||
|
||||
if (root.focusedClient.credentials.walletId == client.credentials.walletId) {
|
||||
if (root.focusedClient.credentials.walletId == walletId) {
|
||||
$rootScope.$emit(n.type, n);
|
||||
} else {
|
||||
$rootScope.$apply();
|
||||
|
|
@ -112,17 +100,13 @@ angular.module('copayApp.services')
|
|||
client.on('walletCompleted', function() {
|
||||
$log.debug('Wallet completed');
|
||||
|
||||
root.updateCredentialsFC(function() {
|
||||
$rootScope.$emit('Local/WalletCompleted', client.credentials.walletId);
|
||||
root.updateCredentials(client.export(), function() {
|
||||
$rootScope.$emit('Local/WalletCompleted', walletId);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
root.walletClients[credentials.walletId].started = true;
|
||||
root.walletClients[credentials.walletId].doNotVerifyPayPro = isChromeApp;
|
||||
|
||||
if (client.hasPrivKeyEncrypted() && !client.isPrivKeyEncrypted()) {
|
||||
$log.warn('Auto locking unlocked wallet:' + credentials.walletId);
|
||||
$log.warn('Auto locking unlocked wallet:' + walletId);
|
||||
client.lock();
|
||||
}
|
||||
|
||||
|
|
@ -133,15 +117,35 @@ angular.module('copayApp.services')
|
|||
}
|
||||
client.setNotificationsInterval(BACKGROUND_UPDATE_PERIOD);
|
||||
});
|
||||
}
|
||||
|
||||
root.setWalletClients = function() {
|
||||
var credentials = root.profile.credentials;
|
||||
lodash.each(credentials, function(credential) {
|
||||
//$log.info("Credentials:", credentials);
|
||||
root.setWalletClient(credential);
|
||||
return true;
|
||||
};
|
||||
|
||||
|
||||
// Used when reading wallets from the profile
|
||||
root.bindWallet = function(credentials) {
|
||||
if (!credentials.walletId)
|
||||
throw 'bindWallet should receive credentials JSON';
|
||||
|
||||
$log.debug('Bind wallet:' + credentials.walletId);
|
||||
|
||||
// Create the client
|
||||
var getBaseURL = function(walletId) {
|
||||
var config = configService.getSync();
|
||||
var defaults = configService.getDefaults();
|
||||
return ((config.bwsFor && config.bwsFor[walletId]) || defaults.bws.url);
|
||||
};
|
||||
|
||||
var skipKeyValidation = root.profile.isChecked(platformInfo.ua, credentials.walletId);
|
||||
var client = bwcService.getClient(JSON.stringify(credentials), {
|
||||
baseurl: getBaseURL(credentials.walletId),
|
||||
skipKeyValidation: skipKeyValidation,
|
||||
});
|
||||
$rootScope.$emit('Local/WalletListUpdated');
|
||||
|
||||
if (!skipKeyValidation && !client.incorrectDerivation)
|
||||
root.profile.setChecked(platformInfo.ua, credentials.walletId);
|
||||
|
||||
return root.bindWalletClient(client);
|
||||
};
|
||||
|
||||
root.bindProfile = function(profile, cb) {
|
||||
|
|
@ -150,11 +154,15 @@ angular.module('copayApp.services')
|
|||
configService.get(function(err) {
|
||||
$log.debug('Preferences read');
|
||||
if (err) return cb(err);
|
||||
root.setWalletClients();
|
||||
|
||||
lodash.each(root.profile.credentials, function(credentials) {
|
||||
root.bindWallet(credentials);
|
||||
});
|
||||
$rootScope.$emit('Local/WalletListUpdated');
|
||||
|
||||
storageService.getFocusedWalletId(function(err, focusedWalletId) {
|
||||
if (err) return cb(err);
|
||||
root._setFocus(focusedWalletId, function() {
|
||||
$rootScope.$emit('Local/ProfileBound');
|
||||
if (usePushNotifications)
|
||||
root.pushNotificationsInit();
|
||||
root.isDisclaimerAccepted(function(val) {
|
||||
|
|
@ -222,12 +230,11 @@ angular.module('copayApp.services')
|
|||
});
|
||||
};
|
||||
|
||||
root._seedWallet = function(opts, cb) {
|
||||
var seedWallet = function(opts, cb) {
|
||||
opts = opts || {};
|
||||
var walletClient = bwcService.getClient(null, opts);
|
||||
var network = opts.networkName || 'livenet';
|
||||
|
||||
|
||||
if (opts.mnemonic) {
|
||||
try {
|
||||
opts.mnemonic = root._normalizeMnemonic(opts.mnemonic);
|
||||
|
|
@ -285,45 +292,54 @@ angular.module('copayApp.services')
|
|||
return cb(null, walletClient);
|
||||
};
|
||||
|
||||
root._createNewProfile = function(opts, cb) {
|
||||
|
||||
if (opts.noWallet) {
|
||||
return cb(null, Profile.create());
|
||||
}
|
||||
|
||||
root._seedWallet({}, function(err, walletClient) {
|
||||
if (err) return cb(err);
|
||||
|
||||
var walletName = gettextCatalog.getString('Personal Wallet');
|
||||
var me = gettextCatalog.getString('me');
|
||||
walletClient.createWallet(walletName, me, 1, 1, {
|
||||
network: 'livenet'
|
||||
}, function(err) {
|
||||
if (err) return bwsError.cb(err, gettext('Error creating wallet'), cb);
|
||||
var p = Profile.create({
|
||||
credentials: [JSON.parse(walletClient.export())],
|
||||
});
|
||||
return cb(null, p);
|
||||
});
|
||||
})
|
||||
};
|
||||
|
||||
root.createWallet = function(opts, cb) {
|
||||
// Creates a wallet on BWC/BWS
|
||||
var doCreateWallet = function(opts, cb) {
|
||||
$log.debug('Creating Wallet:', opts);
|
||||
root._seedWallet(opts, function(err, walletClient) {
|
||||
seedWallet(opts, function(err, walletClient) {
|
||||
if (err) return cb(err);
|
||||
|
||||
walletClient.createWallet(opts.name, opts.myName || 'me', opts.m, opts.n, {
|
||||
var name = opts.name || gettextCatalog.getString('Personal Wallet');
|
||||
var myName = opts.myName || gettextCatalog.getString('me');
|
||||
|
||||
walletClient.createWallet(name, myName, opts.m, opts.n, {
|
||||
network: opts.networkName,
|
||||
walletPrivKey: opts.walletPrivKey,
|
||||
}, function(err, secret) {
|
||||
if (err) return bwsError.cb(err, gettext('Error creating wallet'), cb);
|
||||
|
||||
root._addWalletClient(walletClient, opts, cb);
|
||||
})
|
||||
return cb(null, walletClient, secret);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
// Creates the default Copay profile and its wallet
|
||||
root.createDefaultProfile = function(opts, cb) {
|
||||
var p = Profile.create();
|
||||
|
||||
if (opts.noWallet) {
|
||||
return cb(null, p);
|
||||
}
|
||||
|
||||
opts.m = 1;
|
||||
opts.n = 1;
|
||||
opts.network = 'livenet';
|
||||
|
||||
doCreateWallet(opts, function(err, walletClient) {
|
||||
if (err) return bwsError.cb(err, gettext('Error creating wallet'), cb);
|
||||
p.addWallet(JSON.parse(walletClient.export()));
|
||||
return cb(null, p);
|
||||
});
|
||||
};
|
||||
|
||||
// create and store a wallet
|
||||
root.createWallet = function(opts, cb) {
|
||||
doCreateWallet(opts, function(err, walletClient, secret) {
|
||||
root.addAndBindWalletClient(walletClient, {
|
||||
bwsurl: opts.bwsurl
|
||||
}, cb);
|
||||
});
|
||||
};
|
||||
|
||||
// joins and stores a wallet
|
||||
root.joinWallet = function(opts, cb) {
|
||||
var walletClient = bwcService.getClient();
|
||||
$log.debug('Joining Wallet:', opts);
|
||||
|
|
@ -344,12 +360,14 @@ angular.module('copayApp.services')
|
|||
opts.networkName = walletData.network;
|
||||
$log.debug('Joining Wallet:', opts);
|
||||
|
||||
root._seedWallet(opts, function(err, walletClient) {
|
||||
seedWallet(opts, function(err, walletClient) {
|
||||
if (err) return cb(err);
|
||||
|
||||
walletClient.joinWallet(opts.secret, opts.myName || 'me', {}, function(err) {
|
||||
if (err) return bwsError.cb(err, gettext('Could not join wallet'), cb);
|
||||
root._addWalletClient(walletClient, opts, cb);
|
||||
root.addAndBindWalletClient(walletClient, {
|
||||
bwsurl: opts.bwsurl
|
||||
}, cb);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
|
@ -358,39 +376,31 @@ angular.module('copayApp.services')
|
|||
return root.walletClients[walletId];
|
||||
};
|
||||
|
||||
root.deleteWalletFC = function(opts, cb) {
|
||||
var fc = root.focusedClient;
|
||||
var walletId = fc.credentials.walletId;
|
||||
root.deleteWalletClient = function(client, cb) {
|
||||
var walletId = client.credentials.walletId;
|
||||
|
||||
pushNotificationsService.unsubscribe(root.getClient(walletId), function(err) {
|
||||
if (err) $log.warn('Unsubscription error: ' + err.message);
|
||||
else $log.debug('Unsubscribed from push notifications service');
|
||||
});
|
||||
|
||||
$log.debug('Deleting Wallet:', fc.credentials.walletName);
|
||||
$log.debug('Deleting Wallet:', client.credentials.walletName);
|
||||
client.removeAllListeners();
|
||||
|
||||
fc.removeAllListeners();
|
||||
root.profile.credentials = lodash.reject(root.profile.credentials, {
|
||||
walletId: walletId
|
||||
});
|
||||
root.profile.deleteWallet(walletId);
|
||||
|
||||
delete root.walletClients[walletId];
|
||||
root.focusedClient = null;
|
||||
|
||||
storageService.clearLastAddress(walletId, function(err) {
|
||||
|
||||
storageService.removeAllWalletData(walletId, function(err) {
|
||||
if (err) $log.warn(err);
|
||||
});
|
||||
|
||||
storageService.removeTxHistory(walletId, function(err) {
|
||||
if (err) $log.warn(err);
|
||||
});
|
||||
|
||||
storageService.clearBackupFlag(walletId, function(err) {
|
||||
if (err) $log.warn(err);
|
||||
});
|
||||
|
||||
$timeout(function() {
|
||||
root.setWalletClients();
|
||||
$rootScope.$emit('Local/WalletListUpdated');
|
||||
|
||||
root.setAndStoreFocus(null, function() {
|
||||
storageService.storeProfile(root.profile, function(err) {
|
||||
if (err) return cb(err);
|
||||
|
|
@ -419,59 +429,74 @@ angular.module('copayApp.services')
|
|||
});
|
||||
}
|
||||
|
||||
root._addWalletClient = function(walletClient, opts, cb) {
|
||||
var walletId = walletClient.credentials.walletId;
|
||||
// Adds and bind a new client to the profile
|
||||
root.addAndBindWalletClient = function(client, opts, cb) {
|
||||
var walletId = client.credentials.walletId
|
||||
|
||||
// check if exist
|
||||
var w = lodash.find(root.profile.credentials, {
|
||||
'walletId': walletId
|
||||
if (!root.profile.addWallet(JSON.parse(client.export())))
|
||||
return cb(gettext('Wallet already in Copay'));
|
||||
|
||||
root.bindWalletClient(client);
|
||||
$rootScope.$emit('Local/WalletListUpdated', client);
|
||||
|
||||
var saveBwsUrl = function(cb) {
|
||||
var defaults = configService.getDefaults();
|
||||
var bwsFor = {};
|
||||
bwsFor[walletId] = opts.bwsurl || defaults.bws.url;
|
||||
|
||||
// Dont save the default
|
||||
if (bwsFor[walletId] == defaults.bws.url)
|
||||
return cb();
|
||||
|
||||
configService.set({
|
||||
bwsFor: bwsFor,
|
||||
}, function(err) {
|
||||
if (err) $log.warn(err);
|
||||
return cb();
|
||||
});
|
||||
};
|
||||
|
||||
var handleImportedClient = function(cb) {
|
||||
if (!opts.isImport) return cb();
|
||||
$rootScope.$emit('Local/BackupDone', walletId);
|
||||
|
||||
if (!client.isComplete())
|
||||
return cb();
|
||||
|
||||
storageService.setCleanAndScanAddresses(walletId, cb);
|
||||
};
|
||||
|
||||
walletService.updateRemotePreferences(client, {}, function() {
|
||||
$log.debug('Remote preferences saved for:' + walletId)
|
||||
});
|
||||
if (w) {
|
||||
return cb(gettext('Wallet already in Copay' + ": ") + w.walletName);
|
||||
}
|
||||
|
||||
var config = configService.getSync();
|
||||
var defaults = configService.getDefaults();
|
||||
var bwsFor = {};
|
||||
bwsFor[walletId] = opts.bwsurl || defaults.bws.url;
|
||||
|
||||
configService.set({
|
||||
bwsFor: bwsFor,
|
||||
}, function(err) {
|
||||
if (err) console.log(err);
|
||||
|
||||
root.profile.credentials.push(JSON.parse(walletClient.export()));
|
||||
root.setWalletClients();
|
||||
|
||||
|
||||
var handleImport = function(cb) {
|
||||
var isImport = opts.mnemonic || opts.externalSource || opts.extendedPrivateKey;
|
||||
|
||||
if (!isImport)
|
||||
return cb();
|
||||
|
||||
$rootScope.$emit('Local/BackupDone', walletId);
|
||||
|
||||
if (!walletClient.isComplete())
|
||||
return cb();
|
||||
|
||||
storageService.setCleanAndScanAddresses(walletId, cb);
|
||||
};
|
||||
|
||||
handleImport(function() {
|
||||
saveBwsUrl(function() {
|
||||
handleImportedClient(function() {
|
||||
root.setAndStoreFocus(walletId, function() {
|
||||
storageService.storeProfile(root.profile, function(err) {
|
||||
|
||||
$rootScope.$emit('Local/ProfileCreated');
|
||||
var config = configService.getSync();
|
||||
if (config.pushNotifications.enabled)
|
||||
pushNotificationsService.enableNotifications(root.walletClients);
|
||||
return cb(err, walletId);
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
root.storeProfileIfDirty = function(cb) {
|
||||
if (root.profile.dirty) {
|
||||
storageService.storeProfile(root.profile, function(err) {
|
||||
$log.debug('Saved modified Profile');
|
||||
if (cb) return cb(err);
|
||||
});
|
||||
} else {
|
||||
if (cb) return cb();
|
||||
};
|
||||
};
|
||||
|
||||
root.importWallet = function(str, opts, cb) {
|
||||
|
||||
var walletClient = bwcService.getClient(null, opts);
|
||||
|
|
@ -491,7 +516,13 @@ angular.module('copayApp.services')
|
|||
var addressBook = str.addressBook || {};
|
||||
var historyCache = str.historyCache || [];
|
||||
|
||||
root._addWalletClient(walletClient, opts, function(err, walletId) {
|
||||
if (!walletClient.incorrectDerivation)
|
||||
root.profile.setChecked(platformInfo.ua, walletClient.credentials.walletId);
|
||||
|
||||
root.addAndBindWalletClient(walletClient, {
|
||||
bwsurl: opts.bwsurl,
|
||||
isImport: true
|
||||
}, function(err, walletId) {
|
||||
if (err) return cb(err);
|
||||
root.setMetaData(walletClient, addressBook, historyCache, function(error) {
|
||||
if (error) $log.warn(error);
|
||||
|
|
@ -508,7 +539,10 @@ angular.module('copayApp.services')
|
|||
if (err)
|
||||
return bwsError.cb(err, gettext('Could not import'), cb);
|
||||
|
||||
root._addWalletClient(walletClient, opts, cb);
|
||||
root.addAndBindWalletClient(walletClient, {
|
||||
bwsurl: opts.bwsurl,
|
||||
isImport: true
|
||||
}, cb);
|
||||
});
|
||||
};
|
||||
|
||||
|
|
@ -533,7 +567,10 @@ angular.module('copayApp.services')
|
|||
if (err)
|
||||
return bwsError.cb(err, gettext('Could not import'), cb);
|
||||
|
||||
root._addWalletClient(walletClient, opts, cb);
|
||||
root.addAndBindWalletClient(walletClient, {
|
||||
bwsurl: opts.bwsurl,
|
||||
isImport: true
|
||||
}, cb);
|
||||
});
|
||||
};
|
||||
|
||||
|
|
@ -554,16 +591,20 @@ angular.module('copayApp.services')
|
|||
return bwsError.cb(err, gettext('Could not import'), cb);
|
||||
}
|
||||
|
||||
root._addWalletClient(walletClient, opts, cb);
|
||||
root.addAndBindWalletClient(walletClient, {
|
||||
bwsurl: opts.bwsurl,
|
||||
isImport: true
|
||||
}, cb);
|
||||
});
|
||||
};
|
||||
|
||||
root.create = function(opts, cb) {
|
||||
$log.info('Creating profile');
|
||||
$log.info('Creating profile', opts);
|
||||
var defaults = configService.getDefaults();
|
||||
|
||||
configService.get(function(err) {
|
||||
root._createNewProfile(opts, function(err, p) {
|
||||
|
||||
root.createDefaultProfile(opts, function(err, p) {
|
||||
if (err) return cb(err);
|
||||
|
||||
root.bindProfile(p, function(err) {
|
||||
|
|
@ -605,59 +646,45 @@ angular.module('copayApp.services')
|
|||
walletClient.createWalletFromOldCopay(username, password, blob, function(err, existed) {
|
||||
if (err) return cb(gettext('Error importing wallet: ') + err);
|
||||
|
||||
if (root.walletClients[walletClient.credentials.walletId]) {
|
||||
if (root.profile.hasWallet(walletClient.credentials.walletId)) {
|
||||
$log.debug('Wallet:' + walletClient.credentials.walletName + ' already imported');
|
||||
return cb(gettext('Wallet Already Imported: ') + walletClient.credentials.walletName);
|
||||
};
|
||||
|
||||
$log.debug('Creating Wallet:', walletClient.credentials.walletName);
|
||||
root.profile.credentials.push(JSON.parse(walletClient.export()));
|
||||
root.setWalletClients();
|
||||
root.setAndStoreFocus(walletClient.credentials.walletId, function() {
|
||||
storageService.storeProfile(root.profile, function(err) {
|
||||
return cb(null, walletClient.credentials.walletId, walletClient.credentials.walletName, existed);
|
||||
});
|
||||
});
|
||||
root.addAndBindWalletClient(walletClient, {
|
||||
isImport: true
|
||||
}, cb);
|
||||
});
|
||||
};
|
||||
|
||||
root.updateCredentialsFC = function(cb) {
|
||||
var fc = root.focusedClient;
|
||||
|
||||
var newCredentials = lodash.reject(root.profile.credentials, {
|
||||
walletId: fc.credentials.walletId
|
||||
});
|
||||
newCredentials.push(JSON.parse(fc.export()));
|
||||
root.profile.credentials = newCredentials;
|
||||
|
||||
root.updateCredentials = function(credentials, cb) {
|
||||
root.profile.updateWallet(credentials);
|
||||
storageService.storeProfileThrottled(root.profile, cb);
|
||||
};
|
||||
|
||||
root.getClients = function() {
|
||||
return lodash.values(root.walletClients);
|
||||
};
|
||||
|
||||
root.setPrivateKeyEncryptionFC = function(password, cb) {
|
||||
var fc = root.focusedClient;
|
||||
$log.debug('Encrypting private key for', fc.credentials.walletName);
|
||||
root.needsBackup = function(client, cb) {
|
||||
|
||||
fc.setPrivateKeyEncryption(password);
|
||||
fc.lock();
|
||||
root.updateCredentialsFC(function() {
|
||||
$log.debug('Wallet encrypted');
|
||||
return cb();
|
||||
if (!walletService.needsBackup(client))
|
||||
return cb(false);
|
||||
|
||||
storageService.getBackupFlag(client.credentials.walletId, function(err, val) {
|
||||
if (err) $log.error(err);
|
||||
if (val) return cb(false);
|
||||
return cb(true);
|
||||
});
|
||||
};
|
||||
|
||||
root.isReady = function(client, cb) {
|
||||
if (!client.isComplete())
|
||||
return cb('WALLET_NOT_COMPLETE');
|
||||
|
||||
root.disablePrivateKeyEncryptionFC = function(cb) {
|
||||
var fc = root.focusedClient;
|
||||
$log.debug('Disabling private key encryption for', fc.credentials.walletName);
|
||||
|
||||
try {
|
||||
fc.disablePrivateKeyEncryption();
|
||||
} catch (e) {
|
||||
return cb(e);
|
||||
}
|
||||
root.updateCredentialsFC(function() {
|
||||
$log.debug('Wallet encryption disabled');
|
||||
root.needsBackup(client, function(needsBackup) {
|
||||
if (needsBackup)
|
||||
return cb('WALLET_NEEDS_BACKUP');
|
||||
return cb();
|
||||
});
|
||||
};
|
||||
|
|
|
|||
|
|
@ -24,21 +24,6 @@ angular.module('copayApp.services')
|
|||
}, cb);
|
||||
};
|
||||
|
||||
var encryptOnMobile = function(text, cb) {
|
||||
|
||||
// UUID encryption is disabled.
|
||||
return cb(null, text);
|
||||
//
|
||||
// getUUID(function(uuid) {
|
||||
// if (uuid) {
|
||||
// $log.debug('Encrypting profile');
|
||||
// text = sjcl.encrypt(uuid, text);
|
||||
// }
|
||||
// return cb(null, text);
|
||||
// });
|
||||
};
|
||||
|
||||
|
||||
var decryptOnMobile = function(text, cb) {
|
||||
var json;
|
||||
try {
|
||||
|
|
@ -107,18 +92,14 @@ angular.module('copayApp.services')
|
|||
};
|
||||
|
||||
root.storeNewProfile = function(profile, cb) {
|
||||
encryptOnMobile(profile.toObj(), function(err, x) {
|
||||
storage.create('profile', x, cb);
|
||||
});
|
||||
storage.create('profile', profile.toObj(), cb);
|
||||
};
|
||||
|
||||
root.storeProfile = function(profile, cb) {
|
||||
encryptOnMobile(profile.toObj(), function(err, x) {
|
||||
storage.set('profile', x, cb);
|
||||
});
|
||||
storage.set('profile', profile.toObj(), cb);
|
||||
};
|
||||
|
||||
root.storeProfileThrottled = lodash.throttle(root.storeProfile, 5000);
|
||||
root.storeProfileThrottled = lodash.throttle(root.storeProfile, 5000);
|
||||
|
||||
root.getProfile = function(cb) {
|
||||
storage.get('profile', function(err, str) {
|
||||
|
|
@ -200,6 +181,14 @@ angular.module('copayApp.services')
|
|||
storage.remove('config', cb);
|
||||
};
|
||||
|
||||
root.setHideBalanceFlag = function(walletId, val, cb) {
|
||||
storage.set('hideBalance-' + walletId, val, cb);
|
||||
};
|
||||
|
||||
root.getHideBalanceFlag = function(walletId, cb) {
|
||||
storage.get('hideBalance-' + walletId, cb);
|
||||
};
|
||||
|
||||
//for compatibility
|
||||
root.getCopayDisclaimerFlag = function(cb) {
|
||||
storage.get('agreeDisclaimer', cb);
|
||||
|
|
@ -285,5 +274,17 @@ angular.module('copayApp.services')
|
|||
storage.remove('coinbaseTxs-' + network, cb);
|
||||
};
|
||||
|
||||
root.removeAllWalletData = function(walletId, cb) {
|
||||
root.clearLastAddress(walletId, function(err) {
|
||||
if (err) return cb(err);
|
||||
root.removeTxHistory(walletId, function(err) {
|
||||
if (err) return cb(err);
|
||||
root.clearBackupFlag(walletId, function(err) {
|
||||
return cb(err);
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
return root;
|
||||
});
|
||||
|
|
|
|||
|
|
@ -3,6 +3,8 @@ angular.module('copayApp.services')
|
|||
.factory('uxLanguage', function languageService($log, lodash, gettextCatalog, amMoment, configService) {
|
||||
var root = {};
|
||||
|
||||
root.currentLanguage = null;
|
||||
|
||||
root.availableLanguages = [{
|
||||
name: 'English',
|
||||
isoCode: 'en',
|
||||
|
|
@ -33,7 +35,6 @@ angular.module('copayApp.services')
|
|||
isoCode: 'ru',
|
||||
}];
|
||||
|
||||
root.currentLanguage = null;
|
||||
|
||||
root._detect = function(cb) {
|
||||
|
||||
|
|
@ -99,20 +100,20 @@ angular.module('copayApp.services')
|
|||
var userLang = configService.getSync().wallet.settings.defaultLanguage;
|
||||
|
||||
if (!userLang) {
|
||||
|
||||
root._detect(function(lang) {
|
||||
userLang = lang;
|
||||
|
||||
if (userLang != root.currentLanguage) {
|
||||
root._set(lang);
|
||||
}
|
||||
return cb(userLang);
|
||||
if (cb) return cb(userLang);
|
||||
});
|
||||
} else {
|
||||
if (userLang != root.currentLanguage) {
|
||||
root._set(userLang);
|
||||
}
|
||||
return cb(userLang);
|
||||
|
||||
if (cb) return cb(userLang);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,9 @@
|
|||
'use strict';
|
||||
|
||||
angular.module('copayApp.services').factory('walletService', function($log, lodash, trezor, ledger, storageService) {
|
||||
// DO NOT INCLUDE STORAGE HERE \/ \/
|
||||
angular.module('copayApp.services').factory('walletService', function($log, lodash, trezor, ledger, storageService, configService, uxLanguage) {
|
||||
// DO NOT INCLUDE STORAGE HERE ^^
|
||||
|
||||
var root = {};
|
||||
|
||||
var _signWithLedger = function(client, txp, cb) {
|
||||
|
|
@ -31,27 +34,14 @@ angular.module('copayApp.services').factory('walletService', function($log, loda
|
|||
});
|
||||
};
|
||||
|
||||
root.isBackupNeeded = function(client, cb) {
|
||||
if (client.isPrivKeyExternal()) return cb(false);
|
||||
if (!client.credentials.mnemonic) return cb(false);
|
||||
if (client.credentials.network == 'testnet') return cb(false);
|
||||
root.needsBackup = function(client) {
|
||||
if (client.isPrivKeyExternal()) return false;
|
||||
if (!client.credentials.mnemonic) return false;
|
||||
if (client.credentials.network == 'testnet') return false;
|
||||
|
||||
storageService.getBackupFlag(client.credentials.walletId, function(err, val) {
|
||||
if (err) $log.error(err);
|
||||
if (val) return cb(false);
|
||||
return cb(true);
|
||||
});
|
||||
return true;
|
||||
};
|
||||
|
||||
root.isReady = function(client, cb) {
|
||||
if(!client.isComplete())
|
||||
return cb('WALLET_NOT_COMPLETE');
|
||||
root.isBackupNeeded(client, function(needsBackup) {
|
||||
if (needsBackup)
|
||||
return cb('WALLET_NEEDS_BACKUP');
|
||||
return cb();
|
||||
});
|
||||
};
|
||||
|
||||
root.isEncrypted = function(client) {
|
||||
if (lodash.isEmpty(client)) return;
|
||||
|
|
@ -82,7 +72,7 @@ angular.module('copayApp.services').factory('walletService', function($log, loda
|
|||
};
|
||||
|
||||
root.createTx = function(client, txp, cb) {
|
||||
if (lodash.isEmpty(txp) || lodash.isEmpty(client))
|
||||
if (lodash.isEmpty(txp) || lodash.isEmpty(client))
|
||||
return cb('MISSING_PARAMETER');
|
||||
|
||||
if (txp.sendMax) {
|
||||
|
|
@ -97,7 +87,7 @@ angular.module('copayApp.services').factory('walletService', function($log, loda
|
|||
var feeLevelValue = lodash.find(levels, {
|
||||
level: txp.feeLevel
|
||||
});
|
||||
|
||||
|
||||
if (!feeLevelValue || !feeLevelValue.feePerKB)
|
||||
return cb({
|
||||
message: 'Could not get dynamic fee for level: ' + feeLevel
|
||||
|
|
@ -118,10 +108,12 @@ angular.module('copayApp.services').factory('walletService', function($log, loda
|
|||
};
|
||||
|
||||
root.publishTx = function(client, txp, cb) {
|
||||
if (lodash.isEmpty(txp) || lodash.isEmpty(client))
|
||||
if (lodash.isEmpty(txp) || lodash.isEmpty(client))
|
||||
return cb('MISSING_PARAMETER');
|
||||
|
||||
client.publishTxProposal({txp: txp}, function(err, publishedTx) {
|
||||
client.publishTxProposal({
|
||||
txp: txp
|
||||
}, function(err, publishedTx) {
|
||||
if (err) return cb(err);
|
||||
else {
|
||||
$log.debug('Transaction published');
|
||||
|
|
@ -131,9 +123,9 @@ angular.module('copayApp.services').factory('walletService', function($log, loda
|
|||
};
|
||||
|
||||
root.signTx = function(client, txp, cb) {
|
||||
if (lodash.isEmpty(txp) || lodash.isEmpty(client))
|
||||
if (lodash.isEmpty(txp) || lodash.isEmpty(client))
|
||||
return cb('MISSING_PARAMETER');
|
||||
|
||||
|
||||
if (client.isPrivKeyExternal()) {
|
||||
switch (client.getPrivKeyExternalSourceName()) {
|
||||
case 'ledger':
|
||||
|
|
@ -160,14 +152,14 @@ angular.module('copayApp.services').factory('walletService', function($log, loda
|
|||
};
|
||||
|
||||
root.broadcastTx = function(client, txp, cb) {
|
||||
if (lodash.isEmpty(txp) || lodash.isEmpty(client))
|
||||
if (lodash.isEmpty(txp) || lodash.isEmpty(client))
|
||||
return cb('MISSING_PARAMETER');
|
||||
|
||||
|
||||
if (txp.status != 'accepted')
|
||||
return cb('TX_NOT_ACCEPTED');
|
||||
|
||||
client.broadcastTxProposal(txp, function(err, broadcastedTxp, memo) {
|
||||
if (err)
|
||||
if (err)
|
||||
return cb(err);
|
||||
|
||||
$log.debug('Transaction broadcasted');
|
||||
|
|
@ -178,9 +170,9 @@ angular.module('copayApp.services').factory('walletService', function($log, loda
|
|||
};
|
||||
|
||||
root.rejectTx = function(client, txp, cb) {
|
||||
if (lodash.isEmpty(txp) || lodash.isEmpty(client))
|
||||
if (lodash.isEmpty(txp) || lodash.isEmpty(client))
|
||||
return cb('MISSING_PARAMETER');
|
||||
|
||||
|
||||
client.rejectTxProposal(txp, null, function(err, rejectedTxp) {
|
||||
$log.debug('Transaction rejected');
|
||||
return cb(err, rejectedTxp);
|
||||
|
|
@ -188,14 +180,50 @@ angular.module('copayApp.services').factory('walletService', function($log, loda
|
|||
};
|
||||
|
||||
root.removeTx = function(client, txp, cb) {
|
||||
if (lodash.isEmpty(txp) || lodash.isEmpty(client))
|
||||
if (lodash.isEmpty(txp) || lodash.isEmpty(client))
|
||||
return cb('MISSING_PARAMETER');
|
||||
|
||||
|
||||
client.removeTxProposal(txp, function(err) {
|
||||
$log.debug('Transaction removed');
|
||||
return cb(err);
|
||||
});
|
||||
};
|
||||
|
||||
root.updateRemotePreferences = function(clients, prefs, cb) {
|
||||
prefs = prefs || {};
|
||||
|
||||
if (!lodash.isArray(clients))
|
||||
clients = [clients];
|
||||
|
||||
function updateRemotePreferencesFor(clients, prefs, cb) {
|
||||
var client = clients.shift();
|
||||
if (!client) return cb();
|
||||
$log.debug('Saving remote preferences', client.credentials.walletName, prefs);
|
||||
|
||||
client.savePreferences(prefs, function(err) {
|
||||
// we ignore errors here
|
||||
if (err) $log.warn(err);
|
||||
|
||||
updateRemotePreferencesFor(clients, prefs, cb);
|
||||
});
|
||||
};
|
||||
|
||||
// Update this JIC.
|
||||
var config = configService.getSync().wallet.settings;
|
||||
|
||||
//prefs.email (may come from arguments)
|
||||
prefs.language = uxLanguage.getCurrentLanguage();
|
||||
prefs.unit = config.unitCode;
|
||||
|
||||
updateRemotePreferencesFor(clients, prefs, function(err) {
|
||||
if (err) return cb(err);
|
||||
|
||||
lodash.each(clients, function(c) {
|
||||
c.preferences = lodash.assign(prefs, c.preferences);
|
||||
});
|
||||
return cb();
|
||||
});
|
||||
};
|
||||
|
||||
return root;
|
||||
});
|
||||
|
|
|
|||
|
|
@ -327,7 +327,7 @@ button.radius, .button.radius {
|
|||
left: 0;
|
||||
width: 100%;
|
||||
padding: 0.8rem;
|
||||
z-index: 9999;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.backup .button-box {
|
||||
|
|
|
|||
|
|
@ -89,7 +89,7 @@ h4.title a {
|
|||
}
|
||||
}
|
||||
|
||||
.modal-content h4, .glidera h4, .coinbase h4, .txModal h4 {
|
||||
.modal-content h4, .glidera h4, .coinbase h4 {
|
||||
background: #F6F7F9;
|
||||
padding: 25px 0px 5px 10px;
|
||||
text-transform: uppercase;
|
||||
|
|
@ -100,12 +100,12 @@ h4.title a {
|
|||
}
|
||||
|
||||
.walletHome h4.title {
|
||||
padding: 0 0 10px 15px;
|
||||
padding: 0px 0 10px 15px;
|
||||
margin: 5px 0 5px 0;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.preferences ul, .modal-content ul, .txModal ul {
|
||||
.preferences ul, .modal-content ul {
|
||||
font-size: 14px;
|
||||
background: white;
|
||||
}
|
||||
|
|
@ -255,6 +255,12 @@ a {
|
|||
position: relative;
|
||||
}
|
||||
|
||||
|
||||
.half-row {
|
||||
width: 50%;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
.content {
|
||||
width: 100%;
|
||||
position: absolute;
|
||||
|
|
@ -600,6 +606,10 @@ ul.manage li {
|
|||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.m10l {
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.m5l {
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
|
@ -693,6 +703,10 @@ ul.manage li {
|
|||
padding-top: 20px;
|
||||
}
|
||||
|
||||
.p50t {
|
||||
padding-top: 50px;
|
||||
}
|
||||
|
||||
.p10 {
|
||||
padding: 10px;
|
||||
}
|
||||
|
|
@ -825,10 +839,6 @@ ul.manage li {
|
|||
}
|
||||
}
|
||||
|
||||
.sidebar li.nav-item.selected .name-wallet {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.locked {
|
||||
font-size: 11px;
|
||||
color: #7A8C9E;
|
||||
|
|
@ -863,7 +873,8 @@ ul.manage li {
|
|||
.header-modal {
|
||||
background: #fff;
|
||||
width: 100%;
|
||||
padding: 0.8rem;
|
||||
padding-top: 60px;
|
||||
padding-bottom: 20px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
|
|
@ -1334,61 +1345,6 @@ input.ng-invalid-match {
|
|||
}
|
||||
}
|
||||
|
||||
/* Confirmation popup */
|
||||
|
||||
.confirmTxModal {
|
||||
background: white;
|
||||
border-radius: 5px;
|
||||
position: absolute;
|
||||
width: 90%;
|
||||
left: 0;
|
||||
right: 0;
|
||||
margin: 15% auto;
|
||||
z-index: 1100;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.confirmHead {
|
||||
padding: 10px;
|
||||
border-top-left-radius: 5px;
|
||||
border-top-right-radius: 5px;
|
||||
}
|
||||
|
||||
.txModal-content {
|
||||
overflow: auto;
|
||||
height: 100%;
|
||||
padding-bottom: 50px;
|
||||
}
|
||||
|
||||
.txModal {
|
||||
background: #f6f7f9;
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
overflow: hidden;
|
||||
z-index: 1100;
|
||||
}
|
||||
|
||||
.alertModal, .passModal {
|
||||
background: #FFFFFF;
|
||||
border-radius: 5px;
|
||||
position: absolute;
|
||||
width: 90%;
|
||||
left: 5%;
|
||||
top: 15%;
|
||||
z-index: 1100;
|
||||
}
|
||||
|
||||
.modalMask {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
z-index: 1099;
|
||||
opacity: 0.8;
|
||||
background: black;
|
||||
}
|
||||
|
||||
.tx-details-blockchain li {
|
||||
cursor: pointer !important;
|
||||
}
|
||||
|
|
@ -1591,14 +1547,6 @@ input.ng-invalid-match {
|
|||
left: 0;
|
||||
}
|
||||
|
||||
.right-off-canvas-menu {
|
||||
background-color: #213140;
|
||||
}
|
||||
|
||||
.off-canvas-wrap, .inner-wrap {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.walletHome {
|
||||
.avatar-wallet {
|
||||
padding: 0.5rem;
|
||||
|
|
@ -1664,6 +1612,11 @@ a.missing-copayers {
|
|||
margin-bottom: 50px;
|
||||
}
|
||||
|
||||
.sidebar ion-content {
|
||||
background: #2C3E50;
|
||||
top: 115px;
|
||||
}
|
||||
|
||||
.sidebar {
|
||||
background: #2C3E50;
|
||||
.icon {
|
||||
|
|
@ -1734,16 +1687,24 @@ a.missing-copayers {
|
|||
color: #fff;
|
||||
}
|
||||
}
|
||||
ul.off-canvas-list {
|
||||
margin-bottom: 30px;
|
||||
li a {
|
||||
font-size: 12px;
|
||||
font-weight: 300;
|
||||
border-bottom: transparent;
|
||||
color: #A5B2BF;
|
||||
padding: 1rem 0.7rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.sidebar li {
|
||||
overflow: hidden;
|
||||
border-bottom-style: solid;
|
||||
border-bottom-width: 1px;
|
||||
|
||||
padding: 1rem 0.7rem;
|
||||
font-size: 12px;
|
||||
font-weight: 300;
|
||||
}
|
||||
|
||||
.sidebar ul {
|
||||
margin: 0 0 30px 0;
|
||||
}
|
||||
|
||||
.sidebar a {
|
||||
color: #A5B2BF;
|
||||
}
|
||||
|
||||
.modal-content ul li a {
|
||||
|
|
@ -1767,11 +1728,6 @@ a.missing-copayers {
|
|||
}
|
||||
}
|
||||
|
||||
.sidebar .left-off-canvas-menu {
|
||||
background: #E4E8EC;
|
||||
line-height: 24px;
|
||||
}
|
||||
|
||||
/*
|
||||
* Remove all vendors hover / shadow / fade
|
||||
*/
|
||||
|
|
@ -1784,23 +1740,6 @@ a.missing-copayers {
|
|||
box-shadow: none;
|
||||
}
|
||||
|
||||
ul.off-canvas-list li a:hover {
|
||||
background: none;
|
||||
}
|
||||
|
||||
.move-right .close-menu {
|
||||
cursor: pointer;
|
||||
box-shadow: none;
|
||||
display: block;
|
||||
position: absolute;
|
||||
background: none;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
z-index: 1002;
|
||||
}
|
||||
|
||||
/* === */
|
||||
|
||||
/* Have to set height explicity on ui-view
|
||||
|
|
@ -1991,12 +1930,9 @@ to prevent collapsing during animation*/
|
|||
}
|
||||
|
||||
.modal-content {
|
||||
position: fixed;
|
||||
overflow-y: auto;
|
||||
position: relative;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
top: 45px;
|
||||
padding-bottom: 50px;
|
||||
-webkit-transform: translate3d(0, 0, 0);
|
||||
background: #f6f7f9;
|
||||
}
|
||||
|
|
@ -2021,13 +1957,6 @@ body.modal-open {
|
|||
}
|
||||
}
|
||||
|
||||
.txModal.animated {
|
||||
&.slideInRight, &.slideOutRight {
|
||||
-webkit-animation-duration: 0.3s;
|
||||
animation-duration: 0.3s;
|
||||
}
|
||||
}
|
||||
|
||||
.reveal-modal.animated {
|
||||
&.fadeOutUp, &.slideInUp, &.slideInDown {
|
||||
-webkit-animation-duration: 0.3s;
|
||||
|
|
@ -2073,7 +2002,7 @@ body.modal-open {
|
|||
|
||||
.payment-proposal-head {
|
||||
color: #fff;
|
||||
padding: 0 10px 20px 10px;
|
||||
padding: 40px 10px 20px 10px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
|
|
@ -2083,7 +2012,7 @@ body.modal-open {
|
|||
padding: 5px 15px;
|
||||
background-color: rgba(0, 0, 0, 0.1);
|
||||
i {
|
||||
position: absolute;
|
||||
position: inherit;
|
||||
left: 25px;
|
||||
padding-right: 10px;
|
||||
border-right: 1px solid;
|
||||
|
|
@ -2144,4 +2073,3 @@ body.modal-open {
|
|||
color: #2C3E50;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,9 @@
|
|||
describe('createController', function() {
|
||||
var fixtures = {
|
||||
|
||||
// Store prefs
|
||||
'1eda3e702196b8d5d82fae129249bc79f0d5be2f5309a4e39855e7eb4ad31428': {},
|
||||
'31f5deeef4cf7fd8fc67297179232e8e4590532960454ad958009132fef3daae': {},
|
||||
// createWallet 1-1
|
||||
'56db6f58f2c212591afb4d508d03e5fb40bb786f23dc56c43b98bde42dc513e5': {
|
||||
"walletId": "267bfa75-5575-4af7-8aa3-f5186bc99262"
|
||||
|
|
|
|||
79
test/controllers/disclaimer.test.js
Normal file
79
test/controllers/disclaimer.test.js
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
describe('disclaimerController', function() {
|
||||
var walletService;
|
||||
var storeProfile;
|
||||
|
||||
var fixtures = {
|
||||
'8dc332881e99c908c655147dc6bc605e102b0bd3cf2dbee02ed2a0f4daf2925a': {
|
||||
"walletId": "eddaef15-f412-462f-9d3e-a793a7f6f6ba"
|
||||
},
|
||||
'654145bc3f15f03a8b1ccf55aa1bdcd1cfd5bbe3de90e909fd4e7f9f69ec4d79': {
|
||||
"copayerId": "1a91ead1b6d13da882a25377a20e460df557e77008ea4f60eecbf984f786cf03",
|
||||
"wallet": {
|
||||
"version": "1.0.0",
|
||||
"createdOn": 1465152783,
|
||||
"id": "eddaef15-f412-462f-9d3e-a793a7f6f6ba",
|
||||
"name": "{\"iv\":\"BZQVWAP6d1e4G8Fq1rQKbA==\",\"v\":1,\"iter\":1,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"ct\":\"/gaG7FIkhCiwsWKZUR0sL/cxH+zHMK0=\"}",
|
||||
"m": 1,
|
||||
"n": 1,
|
||||
"status": "complete",
|
||||
"publicKeyRing": [{
|
||||
"xPubKey": "xpub6Cb7MYAX7mJR28MfFueCsoDVVHhoWkQxRC4viAeHanYwRNgDo5xMF42xmAeExzfyPXX3GaALNA8hWFMekVYvDF2BALommUhMgZ52szh88fd",
|
||||
"requestPubKey": "029a167eebe3ccd9987d41743477f8b75e1f3c30463187e1b106e0cc1155efa4dd"
|
||||
}],
|
||||
"copayers": [{
|
||||
"version": 2,
|
||||
"createdOn": 1465152783,
|
||||
"xPubKey": "xpub6Cb7MYAX7mJR28MfFueCsoDVVHhoWkQxRC4viAeHanYwRNgDo5xMF42xmAeExzfyPXX3GaALNA8hWFMekVYvDF2BALommUhMgZ52szh88fd",
|
||||
"id": "1a91ead1b6d13da882a25377a20e460df557e77008ea4f60eecbf984f786cf03",
|
||||
"name": "{\"iv\":\"BZQVWAP6d1e4G8Fq1rQKbA==\",\"v\":1,\"iter\":1,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"ct\":\"wwZd+2LQgYR6cA==\"}",
|
||||
"requestPubKey": "029a167eebe3ccd9987d41743477f8b75e1f3c30463187e1b106e0cc1155efa4dd",
|
||||
"signature": "3045022100ac3f31ef145eabde6a125958aa9d63c2bd4aa27717d7f6905c3e3ff1e733ee8e02206a43200b775ee5c8f7a85c4d3309d155240d5de46a7d9c5e60045bf49779f40b",
|
||||
"requestPubKeys": [{
|
||||
"key": "029a167eebe3ccd9987d41743477f8b75e1f3c30463187e1b106e0cc1155efa4dd",
|
||||
"signature": "3045022100ac3f31ef145eabde6a125958aa9d63c2bd4aa27717d7f6905c3e3ff1e733ee8e02206a43200b775ee5c8f7a85c4d3309d155240d5de46a7d9c5e60045bf49779f40b"
|
||||
}],
|
||||
"customData": "{\"iv\":\"BZQVWAP6d1e4G8Fq1rQKbA==\",\"v\":1,\"iter\":1,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"ct\":\"9l63hoVnA71LshCC5xbOTHA+ivBzux7u8SAci56p4aaVIF4qzXQhQKFX+sAFGfBjULm/E1st6awdXnxbAgjbF7D0zsbBFLFOSCw+ko5Xc6o=\"}"
|
||||
}],
|
||||
"pubKey": "026d95bb5cc2a30c19e22379ae78b4757aaa2dd0ccbd15a1db054fb50cb98ed361",
|
||||
"network": "livenet",
|
||||
"derivationStrategy": "BIP44",
|
||||
"addressType": "P2PKH",
|
||||
"addressManager": {
|
||||
"version": 2,
|
||||
"derivationStrategy": "BIP44",
|
||||
"receiveAddressIndex": 0,
|
||||
"changeAddressIndex": 0,
|
||||
"copayerIndex": 2147483647
|
||||
},
|
||||
"scanStatus": null
|
||||
}
|
||||
},
|
||||
}; // TODO: Read from file
|
||||
|
||||
beforeEach(function(done) {
|
||||
mocks.init(fixtures, 'disclaimerController', {
|
||||
initController: true,
|
||||
noDisclaimer: true,
|
||||
}, done);
|
||||
});
|
||||
|
||||
afterEach(function(done) {
|
||||
mocks.clear({}, done);
|
||||
});
|
||||
|
||||
it('should be defined', function() {
|
||||
should.exist(ctrl);
|
||||
});
|
||||
|
||||
it('should create the initial profile', function(done) {
|
||||
localStorage.clear();
|
||||
ctrl.init({
|
||||
walletPrivKey: 'Kz4CFSTgLzoYfMkt97BTBotUbZYXjMts6Ej9HbVfCf5oLmun1BXy',
|
||||
mnemonic: 'tunnel fork scare industry noble snow tank bullet over gesture nuclear next',
|
||||
});
|
||||
setTimeout(function() {
|
||||
ctrl.creatingProfile.should.equal(false);
|
||||
done();
|
||||
}, 100);
|
||||
});
|
||||
});
|
||||
|
|
@ -3,6 +3,7 @@ describe('importController', function() {
|
|||
var storeProfile;
|
||||
|
||||
var fixtures = {
|
||||
'31f5deeef4cf7fd8fc67297179232e8e4590532960454ad958009132fef3daae': {},
|
||||
'4599136eff6deb4c9c78043fa84113617a16d75c45920d662305f6227ae8f0a0': {
|
||||
"wallet": {
|
||||
"version": "1.0.0",
|
||||
|
|
|
|||
|
|
@ -60,7 +60,7 @@ describe('index', function() {
|
|||
});
|
||||
|
||||
|
||||
it('should updates remote preferences', function(done) {
|
||||
it.skip('should updates remote preferences', function(done) {
|
||||
ctrl.updateRemotePreferences({}, function() {
|
||||
done();
|
||||
});
|
||||
|
|
|
|||
31
test/controllers/preferencesHistory.test.js
Normal file
31
test/controllers/preferencesHistory.test.js
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
describe('Preferences History Controller', function() {
|
||||
|
||||
var walletService;
|
||||
|
||||
var txHistory = '[{"txid":"bf31ecaa8e10ce57f9a889fc4c893b40ff57b016dd763957d942e21ed55fc62c","action":"received","amount":120000,"fees":4862,"time":1464969291,"confirmations":8,"outputs":[{"amount":120000,"address":"2N4HgtF9cJSzxhVkj5gbKxwJSKWBmnb9FNJ","message":null}],"note":{"body":"just a comment","editedBy":"31a8c3c0be9ffbb9f257c95f3fd2f73a59cf81e40199ba5918417270db8c4cdb","editedByName":"2-2","editedOn":1464969101},"message":null,"creatorName":"","hasUnconfirmedInputs":false,"amountStr":"1,200 bits","alternativeAmountStr":"0.68 USD","feeStr":"49 bits","safeConfirmed":"6+"}]';
|
||||
|
||||
describe('Complete 1-1 wallet', function() {
|
||||
beforeEach(function(done) {
|
||||
mocks.init(FIXTURES, 'preferencesHistory', {
|
||||
loadProfile: PROFILE.testnet1of1,
|
||||
loadStorage: {
|
||||
'txsHistory-66d3afc9-7d76-4b25-850e-aa62fcc53a7d': txHistory,
|
||||
},
|
||||
}, done);
|
||||
});
|
||||
|
||||
afterEach(function(done) {
|
||||
mocks.clear({}, done);
|
||||
});
|
||||
|
||||
it('should export csv', function(done) {
|
||||
ctrl.csvHistory(function(err) {
|
||||
should.not.exist(err);
|
||||
ctrl.csvReady.should.equal(true);
|
||||
JSON.stringify(ctrl.csvContent).should.equal('[{"Date":"2016-06-03T15:54:51.000Z","Destination":"","Description":"","Amount":"0.00120000","Currency":"BTC","Txid":"bf31ecaa8e10ce57f9a889fc4c893b40ff57b016dd763957d942e21ed55fc62c","Creator":"","Copayers":"","Comment":"just a comment"}]');
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
|
@ -12,7 +12,6 @@ describe('walletHome', function() {
|
|||
mocks.clear({}, done);
|
||||
});
|
||||
|
||||
|
||||
it('should be defined', function() {
|
||||
should.exist(ctrl);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -52,6 +52,9 @@ var walletInfo = {
|
|||
|
||||
var FIXTURES = {
|
||||
|
||||
// store preferences
|
||||
'1eda3e702196b8d5d82fae129249bc79f0d5be2f5309a4e39855e7eb4ad31428': {},
|
||||
|
||||
// Incomplete wallet status
|
||||
'd05582c35aa545494e3f3be9713efa9df112d36a324350f6b7141996b824bce2': walletInfo,
|
||||
// ^ same thing, twostep=1
|
||||
|
|
|
|||
|
|
@ -80,8 +80,11 @@ mocks.init = function(fixtures, controllerName, opts, done) {
|
|||
angular.module('stateMock', []);
|
||||
angular.module('stateMock').service("$state", mocks.$state.bind());
|
||||
|
||||
module('ionic');
|
||||
module('ngLodash');
|
||||
module('angularMoment');
|
||||
module('gettext');
|
||||
module('stateMock');
|
||||
module('bwcModule', function($provide) {
|
||||
$provide.decorator('bwcService', function($delegate, lodash) {
|
||||
var getClient = $delegate.getClient;
|
||||
|
|
@ -104,7 +107,7 @@ mocks.init = function(fixtures, controllerName, opts, done) {
|
|||
var headers = JSON.stringify(bwc._getHeaders(method, url, args));
|
||||
|
||||
// Fixes BWC version... TODO
|
||||
headers=headers.replace(/bwc-\d\.\d\.\d/,'bwc-2.4.0')
|
||||
headers = headers.replace(/bwc-\d\.\d\.\d/, 'bwc-2.4.0')
|
||||
var x = method + url + JSON.stringify(args) + headers;
|
||||
var sjcl = $delegate.getSJCL();
|
||||
return sjcl.codec.hex.fromBits(sjcl.hash.sha256.hash(x));
|
||||
|
|
@ -153,8 +156,6 @@ mocks.init = function(fixtures, controllerName, opts, done) {
|
|||
});
|
||||
});
|
||||
|
||||
module('angularMoment');
|
||||
module('stateMock');
|
||||
module('copayApp.services', {
|
||||
$modal: mocks.modal,
|
||||
$timeout: mocks.$timeout,
|
||||
|
|
@ -162,7 +163,7 @@ mocks.init = function(fixtures, controllerName, opts, done) {
|
|||
});
|
||||
module('copayApp.controllers');
|
||||
|
||||
inject(function($rootScope, $controller, $injector, _configService_, _profileService_, _storageService_) {
|
||||
inject(function($rootScope, $controller, $injector, lodash, _configService_, _profileService_, _storageService_) {
|
||||
scope = $rootScope.$new();
|
||||
storageService = _storageService_;
|
||||
|
||||
|
|
@ -197,6 +198,13 @@ mocks.init = function(fixtures, controllerName, opts, done) {
|
|||
if (opts.initController)
|
||||
startController();
|
||||
|
||||
|
||||
if (opts.loadStorage) {
|
||||
lodash.each(opts.loadStorage, function(v, k) {
|
||||
localStorage.setItem(k, v);
|
||||
});
|
||||
}
|
||||
|
||||
if (opts.loadProfile) {
|
||||
|
||||
localStorage.setItem('profile', JSON.stringify(opts.loadProfile));
|
||||
|
|
@ -212,6 +220,9 @@ mocks.init = function(fixtures, controllerName, opts, done) {
|
|||
noWallet: true
|
||||
}, function(err) {
|
||||
should.not.exist(err, err);
|
||||
if (opts.noDisclaimer){
|
||||
return done();
|
||||
}
|
||||
_profileService_.setDisclaimerAccepted(function() {
|
||||
if (!opts.initController)
|
||||
startController();
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue