From 589200b455c8f66d9c51815534a649a16a04c261 Mon Sep 17 00:00:00 2001 From: Matias Alejo Garcia Date: Thu, 23 Apr 2015 12:27:43 -0300 Subject: [PATCH] one page --- public/views/history.html | 64 ---- public/views/includes/menu.html | 4 +- public/views/receive.html | 68 ---- public/views/walletHome.html | 287 +++++++++++++++- src/css/main.css | 98 ------ src/js/controllers/history.js | 72 ---- src/js/controllers/index.js | 2 + src/js/controllers/receive.js | 4 +- src/js/controllers/walletHome.js | 558 ++++++++++++++++++++++++++++++- src/js/routes.js | 57 ---- 10 files changed, 847 insertions(+), 367 deletions(-) diff --git a/public/views/history.html b/public/views/history.html index 65a3a3c0c..a9ea5e970 100644 --- a/public/views/history.html +++ b/public/views/history.html @@ -1,65 +1 @@ -
-
-
-
- Getting transactions... - - - Could not fetch transaction history -
[Tap to retry] -
- - No transactions yet - -
-
-
-
-
-
-
- Received - Sent - Moved -
- -
- - + - - - {{btx.amountStr}} - {{history.getUnitName()}} - -
-
-
- - - Unconfirmed - -
-
-
- {{btx.message || btx.addressTo}} -
-
-
-
- -
-
-
-
diff --git a/public/views/includes/menu.html b/public/views/includes/menu.html index 78208dc95..e9f651a10 100644 --- a/public/views/includes/menu.html +++ b/public/views/includes/menu.html @@ -1,8 +1,8 @@
- +
{{item.title|translate}} diff --git a/public/views/receive.html b/public/views/receive.html index 3fc6c72b0..1dc122042 100644 --- a/public/views/receive.html +++ b/public/views/receive.html @@ -1,70 +1,2 @@ -
- -
-
- - - WARNING: Backup needed - -
-
- Before receiving funds, it is highly recommended you backup your wallet keys. -
- -
- - SKIP BACKUP - -
-
- - Backup now - -
-
- -
- -
- - {{receive.error|translate}} - -
- -
- -
-

My Bitcoin address

-
- -
-

{{receive.addr}}

-
-
-
- - - Share address - -
-
- Share this with anyone to have them send you payments. To protect your privacy, new addresses are generated automatically once you use them. -
-
-
-
-
- -
-
-
- -
diff --git a/public/views/walletHome.html b/public/views/walletHome.html index dbce3f15d..572ff73a9 100644 --- a/public/views/walletHome.html +++ b/public/views/walletHome.html @@ -1,5 +1,7 @@
+ +
+ +
+ +
+
+ + + WARNING: Backup needed + +
+
+ Before receiving funds, it is highly recommended you backup your wallet keys. +
+ +
+ + SKIP BACKUP + +
+
+ + Backup now + +
+
+
+
+ + {{home.error|translate}} + +
+
+ +
+

My Bitcoin address

+
+ +
+

{{home.addr}}

+
+
+
+ + + Share address + +
+
+ Share this with anyone to have them send you payments. To protect your privacy, new addresses are generated automatically once you use them. +
+
+
+
+
+ +
+
+
+ +
+ + + +
+ + +
+
+
+
+ Getting transactions... + + + Could not fetch transaction history +
[Tap to retry] +
+ + No transactions yet + +
+
+
+
+
+
+
+ Received + Sent + Moved +
+ +
+ + + + - + {{btx.amountStr}} + {{home.getUnitName()}} + +
+
+
+ + + Unconfirmed + +
+
+
+ {{btx.message || btx.addressTo}} +
+
+
+
+ +
+
+
+
+ + + +
You do not have any wallet diff --git a/src/css/main.css b/src/css/main.css index 007adb6eb..b2b831fcd 100644 --- a/src/css/main.css +++ b/src/css/main.css @@ -1106,104 +1106,6 @@ input.ng-invalid-match, input.ng-invalid-match:focus { cursor: pointer !important; } - - - -.updating { - -webkit-animation-iteration-count: infinite; - -webkit-animation-name: up-animation; - -webkit-animation-duration: 1s; - -webkit-animation-iteration-count: infinite; - animation-iteration-count: infinite; - animation-name: up-animation; - animation-duration: 1s; - animation-iteration-count: infinite; -} - - - -/* Generated with Bounce.js. Edit at http://goo.gl/Vn2Euz */ - -@-webkit-keyframes up-animation { - 0% { -webkit-transform: matrix3d(0.5, 0, 0, 0, 0, 0.5, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); transform: matrix3d(0.5, 0, 0, 0, 0, 0.5, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); } - 3.4% { -webkit-transform: matrix3d(0.658, 0, 0, 0, 0, 0.703, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); transform: matrix3d(0.658, 0, 0, 0, 0, 0.703, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); } - 4.7% { -webkit-transform: matrix3d(0.725, 0, 0, 0, 0, 0.8, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); transform: matrix3d(0.725, 0, 0, 0, 0, 0.8, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); } - 6.81% { -webkit-transform: matrix3d(0.83, 0, 0, 0, 0, 0.946, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); transform: matrix3d(0.83, 0, 0, 0, 0, 0.946, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); } - 9.41% { -webkit-transform: matrix3d(0.942, 0, 0, 0, 0, 1.084, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); transform: matrix3d(0.942, 0, 0, 0, 0, 1.084, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); } - 10.21% { -webkit-transform: matrix3d(0.971, 0, 0, 0, 0, 1.113, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); transform: matrix3d(0.971, 0, 0, 0, 0, 1.113, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); } - 13.61% { -webkit-transform: matrix3d(1.062, 0, 0, 0, 0, 1.166, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); transform: matrix3d(1.062, 0, 0, 0, 0, 1.166, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); } - 14.11% { -webkit-transform: matrix3d(1.07, 0, 0, 0, 0, 1.165, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); transform: matrix3d(1.07, 0, 0, 0, 0, 1.165, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); } - 17.52% { -webkit-transform: matrix3d(1.104, 0, 0, 0, 0, 1.12, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); transform: matrix3d(1.104, 0, 0, 0, 0, 1.12, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); } - 18.72% { -webkit-transform: matrix3d(1.106, 0, 0, 0, 0, 1.094, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); transform: matrix3d(1.106, 0, 0, 0, 0, 1.094, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); } - 21.32% { -webkit-transform: matrix3d(1.098, 0, 0, 0, 0, 1.035, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); transform: matrix3d(1.098, 0, 0, 0, 0, 1.035, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); } - 24.32% { -webkit-transform: matrix3d(1.075, 0, 0, 0, 0, 0.98, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); transform: matrix3d(1.075, 0, 0, 0, 0, 0.98, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); } - 25.23% { -webkit-transform: matrix3d(1.067, 0, 0, 0, 0, 0.969, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); transform: matrix3d(1.067, 0, 0, 0, 0, 0.969, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); } - 29.03% { -webkit-transform: matrix3d(1.031, 0, 0, 0, 0, 0.948, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); transform: matrix3d(1.031, 0, 0, 0, 0, 0.948, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); } - 29.93% { -webkit-transform: matrix3d(1.024, 0, 0, 0, 0, 0.949, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); transform: matrix3d(1.024, 0, 0, 0, 0, 0.949, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); } - 35.54% { -webkit-transform: matrix3d(0.99, 0, 0, 0, 0, 0.981, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); transform: matrix3d(0.99, 0, 0, 0, 0, 0.981, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); } - 36.74% { -webkit-transform: matrix3d(0.986, 0, 0, 0, 0, 0.989, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); transform: matrix3d(0.986, 0, 0, 0, 0, 0.989, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); } - 41.04% { -webkit-transform: matrix3d(0.98, 0, 0, 0, 0, 1.011, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); transform: matrix3d(0.98, 0, 0, 0, 0, 1.011, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); } - 44.44% { -webkit-transform: matrix3d(0.983, 0, 0, 0, 0, 1.016, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); transform: matrix3d(0.983, 0, 0, 0, 0, 1.016, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); } - 52.15% { -webkit-transform: matrix3d(0.996, 0, 0, 0, 0, 1.003, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); transform: matrix3d(0.996, 0, 0, 0, 0, 1.003, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); } - 59.86% { -webkit-transform: matrix3d(1.003, 0, 0, 0, 0, 0.995, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); transform: matrix3d(1.003, 0, 0, 0, 0, 0.995, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); } - 63.26% { -webkit-transform: matrix3d(1.004, 0, 0, 0, 0, 0.996, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); transform: matrix3d(1.004, 0, 0, 0, 0, 0.996, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); } - 75.28% { -webkit-transform: matrix3d(1.001, 0, 0, 0, 0, 1.002, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); transform: matrix3d(1.001, 0, 0, 0, 0, 1.002, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); } - 85.49% { -webkit-transform: matrix3d(0.999, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); transform: matrix3d(0.999, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); } - 90.69% { -webkit-transform: matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); transform: matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); } - 100% { -webkit-transform: matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); transform: matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); } -} - - -@keyframes up-animation { - 0% { transform: matrix3d(0.5, 0, 0, 0, 0, 0.5, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); transform: matrix3d(0.5, 0, 0, 0, 0, 0.5, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); } - 3.4% { transform: matrix3d(0.658, 0, 0, 0, 0, 0.703, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); transform: matrix3d(0.658, 0, 0, 0, 0, 0.703, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); } - 4.7% { transform: matrix3d(0.725, 0, 0, 0, 0, 0.8, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); transform: matrix3d(0.725, 0, 0, 0, 0, 0.8, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); } - 6.81% { transform: matrix3d(0.83, 0, 0, 0, 0, 0.946, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); transform: matrix3d(0.83, 0, 0, 0, 0, 0.946, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); } - 9.41% { transform: matrix3d(0.942, 0, 0, 0, 0, 1.084, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); transform: matrix3d(0.942, 0, 0, 0, 0, 1.084, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); } - 10.21% { transform: matrix3d(0.971, 0, 0, 0, 0, 1.113, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); transform: matrix3d(0.971, 0, 0, 0, 0, 1.113, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); } - 13.61% { transform: matrix3d(1.062, 0, 0, 0, 0, 1.166, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); transform: matrix3d(1.062, 0, 0, 0, 0, 1.166, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); } - 14.11% { transform: matrix3d(1.07, 0, 0, 0, 0, 1.165, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); transform: matrix3d(1.07, 0, 0, 0, 0, 1.165, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); } - 17.52% { transform: matrix3d(1.104, 0, 0, 0, 0, 1.12, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); transform: matrix3d(1.104, 0, 0, 0, 0, 1.12, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); } - 18.72% { transform: matrix3d(1.106, 0, 0, 0, 0, 1.094, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); transform: matrix3d(1.106, 0, 0, 0, 0, 1.094, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); } - 21.32% { transform: matrix3d(1.098, 0, 0, 0, 0, 1.035, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); transform: matrix3d(1.098, 0, 0, 0, 0, 1.035, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); } - 24.32% { transform: matrix3d(1.075, 0, 0, 0, 0, 0.98, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); transform: matrix3d(1.075, 0, 0, 0, 0, 0.98, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); } - 25.23% { transform: matrix3d(1.067, 0, 0, 0, 0, 0.969, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); transform: matrix3d(1.067, 0, 0, 0, 0, 0.969, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); } - 29.03% { transform: matrix3d(1.031, 0, 0, 0, 0, 0.948, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); transform: matrix3d(1.031, 0, 0, 0, 0, 0.948, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); } - 29.93% { transform: matrix3d(1.024, 0, 0, 0, 0, 0.949, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); transform: matrix3d(1.024, 0, 0, 0, 0, 0.949, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); } - 35.54% { transform: matrix3d(0.99, 0, 0, 0, 0, 0.981, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); transform: matrix3d(0.99, 0, 0, 0, 0, 0.981, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); } - 36.74% { transform: matrix3d(0.986, 0, 0, 0, 0, 0.989, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); transform: matrix3d(0.986, 0, 0, 0, 0, 0.989, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); } - 41.04% { transform: matrix3d(0.98, 0, 0, 0, 0, 1.011, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); transform: matrix3d(0.98, 0, 0, 0, 0, 1.011, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); } - 44.44% { transform: matrix3d(0.983, 0, 0, 0, 0, 1.016, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); transform: matrix3d(0.983, 0, 0, 0, 0, 1.016, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); } - 52.15% { transform: matrix3d(0.996, 0, 0, 0, 0, 1.003, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); transform: matrix3d(0.996, 0, 0, 0, 0, 1.003, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); } - 59.86% { transform: matrix3d(1.003, 0, 0, 0, 0, 0.995, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); transform: matrix3d(1.003, 0, 0, 0, 0, 0.995, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); } - 63.26% { transform: matrix3d(1.004, 0, 0, 0, 0, 0.996, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); transform: matrix3d(1.004, 0, 0, 0, 0, 0.996, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); } - 75.28% { transform: matrix3d(1.001, 0, 0, 0, 0, 1.002, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); transform: matrix3d(1.001, 0, 0, 0, 0, 1.002, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); } - 85.49% { transform: matrix3d(0.999, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); transform: matrix3d(0.999, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); } - 90.69% { transform: matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); transform: matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); } - 100% { transform: matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); transform: matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); } -} - - -.opacityCycle { - -webkit-animation-iteration-count: infinite; - -webkit-animation-name: opCycle-animation; - -webkit-animation-duration: 20s; - -webkit-animation-iteration-count: infinite; -} - - - -@-webkit-keyframes opCycle-animation -{ -0% {opacity:1} -15% {opacity:0.7} -30% {opacity:0.85} -45% {opacity:0.5} -60% {opacity:0.8} -75% {opacity:0.7} -99% {opacity:1;} -} - .onGoingProcess { text-align: center; max-width: 14.5rem; diff --git a/src/js/controllers/history.js b/src/js/controllers/history.js index 7dc20d6ec..9f9275abe 100644 --- a/src/js/controllers/history.js +++ b/src/js/controllers/history.js @@ -3,76 +3,4 @@ angular.module('copayApp.controllers').controller('historyController', function($scope, $rootScope, $filter, $timeout, $modal, $log, profileService, notification, go, configService, rateService, lodash) { - function strip(number) { - return (parseFloat(number.toPrecision(12))); - } - - var fc = profileService.focusedClient; - var config = configService.getSync().wallet.settings; - this.unitToSatoshi = config.unitToSatoshi; - this.satToUnit = 1 / this.unitToSatoshi; - this.unitName = config.unitName; - this.alternativeIsoCode = config.alternativeIsoCode; - - this.getUnitName = function() { - return this.unitName; - }; - - this.getAlternativeIsoCode = function() { - return this.alternativeIsoCode; - }; - - this._addRates = function(txs, cb) { - if (!txs || txs.length == 0) return cb(); - var index = lodash.groupBy(txs, 'rateTs'); - - rateService.getHistoricRates(config.alternativeIsoCode, lodash.keys(index), function(err, res) { - if (err || !res) return cb(err); - lodash.each(res, function(r) { - lodash.each(index[r.ts], function(tx) { - var alternativeAmount = (r.rate != null ? tx.amount * rateService.SAT_TO_BTC * r.rate : null); - tx.alternativeAmount = alternativeAmount ? $filter('noFractionNumber')(alternativeAmount, 2) : null; - }); - }); - return cb(); - }); - }; - - this.openTxModal = function(btx) { - var self = this; - var fc = profileService.focusedClient; - var ModalInstanceCtrl = function($scope, $modalInstance) { - $scope.btx = btx; - $scope.settings = config; - $scope.color = fc.backgroundColor; - - $scope.getAmount = function(amount) { - return self.getAmount(amount); - }; - - $scope.getUnitName = function() { - return self.getUnitName(); - }; - - $scope.getShortNetworkName = function() { - var n = fc.credentials.network; - return n.substring(0, 4); - }; - - $scope.cancel = function() { - $modalInstance.dismiss('cancel'); - }; - }; - - $modal.open({ - templateUrl: 'views/modals/tx-details.html', - windowClass: 'full', - controller: ModalInstanceCtrl, - }); - }; - - this.hasAction = function(actions, action) { - return actions.hasOwnProperty('create'); - }; - }); diff --git a/src/js/controllers/index.js b/src/js/controllers/index.js index 78fc79ad4..310ae7388 100644 --- a/src/js/controllers/index.js +++ b/src/js/controllers/index.js @@ -28,6 +28,8 @@ angular.module('copayApp.controllers').controller('indexController', function($r 'link': 'history' }]; + self.tab = 'walletHome'; + self.availableLanguages = [{ name: 'English', isoCode: 'en', diff --git a/src/js/controllers/receive.js b/src/js/controllers/receive.js index bd5d47acc..dd82b60f6 100644 --- a/src/js/controllers/receive.js +++ b/src/js/controllers/receive.js @@ -3,8 +3,6 @@ angular.module('copayApp.controllers').controller('receiveController', function($rootScope, $scope, $timeout, $modal, $log, isCordova, isMobile, profileService, storageService) { var self = this; - var fc = profileService.focusedClient; - this.isCordova = isCordova; self.addresses = []; @@ -15,6 +13,7 @@ angular.module('copayApp.controllers').controller('receiveController', $scope.$on('$destroy', newAddrListener); this.newAddress = function() { + var fc = profileService.focusedClient; self.generatingAddress = true; self.error = null; fc.createAddress(function(err, addr) { @@ -32,6 +31,7 @@ angular.module('copayApp.controllers').controller('receiveController', }; this.getAddress = function() { + var fc = profileService.focusedClient; $timeout(function() { storageService.getLastAddress(fc.credentials.walletId, function(err, addr) { if (addr) { diff --git a/src/js/controllers/walletHome.js b/src/js/controllers/walletHome.js index 2450616cb..ff72de7f0 100644 --- a/src/js/controllers/walletHome.js +++ b/src/js/controllers/walletHome.js @@ -1,8 +1,57 @@ 'use strict'; -angular.module('copayApp.controllers').controller('walletHomeController', function($scope, $rootScope, $timeout, $filter, $modal, notification, txStatus, isCordova, profileService, lodash) { +angular.module('copayApp.controllers').controller('walletHomeController', function($scope, $rootScope, $timeout, $filter, $modal, notification, txStatus, isCordova, profileService, lodash, configService, rateService, storageService) { + var self = this; $rootScope.hideMenuBar = false; + $rootScope.wpInputFocused = false; + + // INIT + var config = configService.getSync().wallet.settings; + this.unitToSatoshi = config.unitToSatoshi; + this.satToUnit = 1 / this.unitToSatoshi; + this.unitName = config.unitName; + this.alternativeIsoCode = config.alternativeIsoCode; + this.alternativeName = config.alternativeName; + this.alternativeAmount = 0; + this.unitDecimals = config.unitDecimals; + this.isCordova = isCordova; + this.addresses = []; + this.isMobile = isMobile.any(); + this.isWindowsPhoneApp = isMobile.Windows() && isCordova; + this.blockUx = false; + this.isRateAvailable = false; + this.showScanner = false; + this.isMobile = isMobile.any(); + + var disableScannerListener = $rootScope.$on('dataScanned', function(event, data) { + self.setForm(data); + }); + + var disablePaymentUriListener = $rootScope.$on('paymentUri', function(event, uri) { + $timeout(function() { + self.setForm(uri); + }, 100); + }); + + var disableAddrListener = $rootScope.$on('Local/NeedNewAddress', function() { + self.getAddress(); + }); + + $scope.$on('$destroy', function() { + disableAddrListener(); + disableScannerListener(); + disablePaymentUriListener(); + $rootScope.hideMenuBar = false; + }); + + rateService.whenAvailable(function() { + self.isRateAvailable = true; + $rootScope.$digest(); + }); + + // walletHome + $scope.openCopayersModal = function(copayers, copayerId) { var fc = profileService.focusedClient; @@ -21,8 +70,6 @@ angular.module('copayApp.controllers').controller('walletHomeController', functi }); }; - - $scope.openTxModal = function(tx, copayers) { var fc = profileService.focusedClient; var ModalInstanceCtrl = function($scope, $modalInstance) { @@ -213,4 +260,509 @@ angular.module('copayApp.controllers').controller('walletHomeController', functi }; + // Receive + + this.newAddress = function() { + var fc = profileService.focusedClient; + self.generatingAddress = true; + self.error = null; + fc.createAddress(function(err, addr) { + self.generatingAddress = false; + if (err) { + $log.debug('Creating address ERROR:', err); + $scope.$emit('Local/ClientError', err); + self.error = 'Could not generate address'; + } else { + self.addr = addr.address; + storageService.storeLastAddress(fc.credentials.walletId, addr.address, function() {}); + } + $scope.$digest(); + }); + }; + + this.getAddress = function() { + var fc = profileService.focusedClient; + $timeout(function() { + storageService.getLastAddress(fc.credentials.walletId, function(err, addr) { + if (addr) { + self.addr = addr; + } else { + self.newAddress(); + } + }); + }); + }; + + this.copyAddress = function(addr) { + if (isCordova) { + window.cordova.plugins.clipboard.copy('bitcoin:' + addr); + window.plugins.toast.showShortCenter('Copied to clipboard'); + } + }; + + this.shareAddress = function(addr) { + if (isCordova) { + if (isMobile.Android() || isMobile.Windows()) { + window.ignoreMobilePause = true; + } + window.plugins.socialsharing.share('bitcoin:' + addr, null, null, null); + } + }; + + // Send + this.resetError = function() { + this.error = this.success = null; + }; + + + var hideMenuBar = lodash.debounce(function(hide) { + if (hide) { + $rootScope.hideMenuBar = true; + } else { + $rootScope.hideMenuBar = false; + } + $rootScope.$digest(); + }, 100); + + + this.formFocus = function(what) { + if (isCordova) { + hideMenuBar(what); + } + if (!this.isWindowsPhoneApp) return + + if (!what) { + this.hideAddress = false; + this.hideAmount = false; + + } else { + if (what == 'amount') { + this.hideAddress = true; + } else if (what == 'msg') { + this.hideAddress = true; + this.hideAmount = true; + } + } + $timeout(function() { + $rootScope.$digest(); + }, 1); + }; + + this.setInputs = function() { + var unitToSat = this.unitToSatoshi; + var satToUnit = 1 / unitToSat; + /** + * Setting the two related amounts as properties prevents an infinite + * recursion for watches while preserving the original angular updates + * + */ + Object.defineProperty($scope, + "_alternative", { + get: function() { + return $scope.__alternative; + }, + set: function(newValue) { + $scope.__alternative = newValue; + if (typeof(newValue) === 'number' && self.isRateAvailable) { + $scope._amount = parseFloat((rateService.fromFiat(newValue, self.alternativeIsoCode) * satToUnit).toFixed(self.unitDecimals), 10); + } + }, + enumerable: true, + configurable: true + }); + Object.defineProperty($scope, + "_amount", { + get: function() { + return $scope.__amount; + }, + set: function(newValue) { + $scope.__amount = newValue; + if (typeof(newValue) === 'number' && self.isRateAvailable) { + $scope.__alternative = parseFloat((rateService.toFiat(newValue * self.unitToSatoshi, self.alternativeIsoCode)).toFixed(2), 10); + } else { + $scope.__alternative = 0; + } + self.alternativeAmount = $scope.__alternative; + self.resetError(); + }, + enumerable: true, + configurable: true + }); + + Object.defineProperty($scope, + "_address", { + get: function() { + return $scope.__address; + }, + set: function(newValue) { + $scope.__address = self.onAddressChange(newValue); + }, + enumerable: true, + configurable: true + }); + }; + + this.setError = function(err) { + $log.warn(err); + var errMessage = 'The transaction' + (fc.credentials.m > 1 ? ' proposal' : '') + + + ' could not be created: ' + (err.message ? err.message : err); + + this.error = errMessage; + + $timeout(function() { + $scope.$digest(); + }, 1); + }; + + + this.setOngoingProcess = function(name) { + var self = this; + $timeout(function() { + self.onGoingProcess = name; + $rootScope.$apply(); + }) + }; + + this.submitForm = function(form) { + var unitToSat = this.unitToSatoshi; + + if (form.$invalid) { + this.error = 'Unable to send transaction proposal'; + return; + } + + if (fc.isPrivKeyEncrypted()) { + profileService.unlockFC(function(err) { + if (err) return self.setError(err); + return self.submitForm(form); + }); + return; + }; + + self.blockUx = true; + self.setOngoingProcess('Sending'); + + if (isCordova) { + window.plugins.spinnerDialog.show(null, 'Creating transaction...', true); + } + + $timeout(function() { + var comment = form.comment.$modelValue; + var paypro = self._paypro; + var address, amount; + + address = form.address.$modelValue; + amount = parseInt((form.amount.$modelValue * unitToSat).toFixed(0)); + + fc.sendTxProposal({ + toAddress: address, + amount: amount, + message: comment, + payProUrl: paypro ? paypro.url : null, + }, function(err, txp) { + self.setOngoingProcess(); + if (err) { + profileService.lockFC(); + if (isCordova) { + window.plugins.spinnerDialog.hide(); + } + self.blockUx = false; + return self.setError(err); + } + + self.signAndBroadcast(txp, function(err) { + self.setOngoingProcess(); + if (isCordova) { + window.plugins.spinnerDialog.hide(); + } + self.blockUx = false; + if (err) { + profileService.lockFC(); + return self.setError(err); + } + self.resetForm(form); + }); + }); + }, 100); + }; + + + this.signAndBroadcast = function(txp, cb) { + self.setOngoingProcess('Signing'); + fc.signTxProposal(txp, function(err, signedTx) { + profileService.lockFC(); + self.setOngoingProcess(); + + if (err) return cb(err); + + if (signedTx.status == 'accepted') { + self.setOngoingProcess('Broadcasting'); + fc.broadcastTxProposal(signedTx, function(err, btx) { + self.setOngoingProcess(); + if (err) { + $scope.error = 'Transaction not broadcasted. Please try again.'; + $scope.$digest(); + } else { + txStatus.notify(btx); + $scope.$emit('Local/TxProposalAction'); + } + return cb(); + }); + } else { + txStatus.notify(signedTx); + $scope.$emit('Local/TxProposalAction'); + return cb(); + } + }); + }; + + this.setTopAmount = function() { + throw new Error('todo: setTopAmount'); + var form = $scope.sendForm; + if (form) { + form.amount.$setViewValue(w.balanceInfo.topAmount); + form.amount.$render(); + form.amount.$isValid = true; + } + }; + + this.setForm = function(to, amount, comment) { + var form = $scope.sendForm; + if (to) { + form.address.$setViewValue(to); + form.address.$isValid = true; + form.address.$render(); + this.lockAddress = true; + } + + if (amount) { + form.amount.$setViewValue("" + amount); + form.amount.$isValid = true; + form.amount.$render(); + this.lockAmount = true; + } + + if (comment) { + form.comment.$setViewValue(comment); + form.comment.$isValid = true; + form.comment.$render(); + } + }; + + + + this.resetForm = function(form) { + this.resetError(); + this.fetchingURL = null; + this._paypro = null; + + this.lockAddress = false; + this.lockAmount = false; + + this._amount = this._address = null; + + if (form && form.amount) { + form.amount.$pristine = true; + form.amount.$setViewValue(''); + form.amount.$render(); + + form.comment.$setViewValue(''); + form.comment.$render(); + form.$setPristine(); + + if (form.address) { + form.address.$pristine = true; + form.address.$setViewValue(''); + form.address.$render(); + } + } + $timeout(function() { + $rootScope.$digest(); + }, 1); + }; + + this.openPPModal = function(paypro) { + var ModalInstanceCtrl = function($scope, $modalInstance) { + var satToUnit = 1 / self.unitToSatoshi; + $scope.paypro = paypro; + $scope.alternative = self.alternativeAmount; + $scope.alternativeIsoCode = self.alternativeIsoCode; + $scope.isRateAvailable = self.isRateAvailable; + $scope.unitTotal = (paypro.amount * satToUnit).toFixed(self.unitDecimals); + $scope.unitName = self.unitName; + $scope.color = fc.backgroundColor; + + $scope.cancel = function() { + $modalInstance.dismiss('cancel'); + }; + }; + $modal.open({ + templateUrl: 'views/modals/paypro.html', + windowClass: 'full', + controller: ModalInstanceCtrl, + }); + }; + + this.setFromPayPro = function(uri, form) { + if (isChromeApp) { + this.error = 'Payment Protocol not supported on Chrome App'; + return; + } + + var satToUnit = 1 / this.unitToSatoshi; + this.fetchingURL = uri; + this.blockUx = true; + var self = this; + + $log.debug('Fetch PayPro Request...', uri); + $timeout(function() { + fc.fetchPayPro({ + payProUrl: uri, + }, function(err, paypro) { + $log.debug(paypro); + self.blockUx = false; + self.fetchingURL = null; + + if (err) { + $log.warn(err); + self.resetForm(form); + var msg = err.toString(); + if (msg.match('HTTP')) { + msg = 'Could not fetch payment information'; + } + self.error = msg; + } else { + self._paypro = paypro; + self.setForm(paypro.toAddress, (paypro.amount * satToUnit).toFixed(self.unitDecimals), + paypro.memo); + } + }); + }, 1); + }; + + this.setFromUri = function(uri) { + function sanitizeUri(uri) { + // Fixes when a region uses comma to separate decimals + var regex = /[\?\&]amount=(\d+([\,\.]\d+)?)/i; + var match = regex.exec(uri); + if (!match || match.length === 0) { + return uri; + } + var value = match[0].replace(',', '.'); + var newUri = uri.replace(regex, value); + return newUri; + }; + + var satToUnit = 1 / this.unitToSatoshi; + + uri = sanitizeUri(uri); + + if (!bitcore.URI.isValid(uri)) { + return uri; + } + var parsed = new bitcore.URI(uri); + var addr = parsed.address.toString(); + var message = parsed.message; + if (parsed.r) + return this.setFromPayPro(parsed.r); + + var amount = parsed.amount ? + (parsed.amount.toFixed(0) * satToUnit).toFixed(this.unitDecimals) : 0; + + this.setForm(addr, amount, message); + return addr; + }; + + this.onAddressChange = function(value) { + this.resetError(); + if (!value) return ''; + + if (this._paypro) + return value; + + if (value.indexOf('bitcoin:') === 0) { + return this.setFromUri(value); + } else if (/^https?:\/\//.test(value)) { + return this.setFromPayPro(value); + } else { + return value; + } + }; + + + // History + + function strip(number) { + return (parseFloat(number.toPrecision(12))); + } + + this.getUnitName = function() { + return this.unitName; + }; + + this.getAlternativeIsoCode = function() { + return this.alternativeIsoCode; + }; + + this._addRates = function(txs, cb) { + if (!txs || txs.length == 0) return cb(); + var index = lodash.groupBy(txs, 'rateTs'); + + rateService.getHistoricRates(config.alternativeIsoCode, lodash.keys(index), function(err, res) { + if (err || !res) return cb(err); + lodash.each(res, function(r) { + lodash.each(index[r.ts], function(tx) { + var alternativeAmount = (r.rate != null ? tx.amount * rateService.SAT_TO_BTC * r.rate : null); + tx.alternativeAmount = alternativeAmount ? $filter('noFractionNumber')(alternativeAmount, 2) : null; + }); + }); + return cb(); + }); + }; + + this.openTxModal = function(btx) { + var self = this; + var fc = profileService.focusedClient; + var ModalInstanceCtrl = function($scope, $modalInstance) { + $scope.btx = btx; + $scope.settings = config; + $scope.color = fc.backgroundColor; + + $scope.getAmount = function(amount) { + return self.getAmount(amount); + }; + + $scope.getUnitName = function() { + return self.getUnitName(); + }; + + $scope.getShortNetworkName = function() { + var n = fc.credentials.network; + return n.substring(0, 4); + }; + + $scope.cancel = function() { + $modalInstance.dismiss('cancel'); + }; + }; + + $modal.open({ + templateUrl: 'views/modals/tx-details.html', + windowClass: 'full', + controller: ModalInstanceCtrl, + }); + }; + + this.hasAction = function(actions, action) { + return actions.hasOwnProperty('create'); + }; + + // ToDo a send... + this.resetError(); + this.setInputs(); + // Todo Receive + this.getAddress(); + + }); diff --git a/src/js/routes.js b/src/js/routes.js index 831c301a7..b8122ac63 100644 --- a/src/js/routes.js +++ b/src/js/routes.js @@ -179,63 +179,6 @@ angular templateUrl: 'views/profile.html', needProfile: true }) - .state('receive', { - url: '/receive', - walletShouldBeComplete: true, - needProfile: true, - views: { - 'main': { - templateUrl: 'views/receive.html' - }, - 'topbar': { - templateUrl: 'views/includes/topbar.html' - }, - 'menu': { - templateUrl: 'views/includes/menu.html', - controller: function($scope) { - $scope.activeMenu = 'receive'; - } - } - } - }) - .state('send', { - url: '/send', - walletShouldBeComplete: true, - needProfile: true, - views: { - 'main': { - templateUrl: 'views/send.html' - }, - 'topbar': { - templateUrl: 'views/includes/topbar.html' - }, - 'menu': { - templateUrl: 'views/includes/menu.html', - controller: function($scope) { - $scope.activeMenu = 'send'; - } - } - } - }) - .state('history', { - url: '/history', - walletShouldBeComplete: true, - needProfile: true, - views: { - 'main': { - templateUrl: 'views/history.html' - }, - 'topbar': { - templateUrl: 'views/includes/topbar.html' - }, - 'menu': { - templateUrl: 'views/includes/menu.html', - controller: function($scope) { - $scope.activeMenu = 'history'; - } - } - } - }) .state('preferences', { url: '/preferences', templateUrl: 'views/preferences.html',