Compare commits

...

246 commits

Author SHA1 Message Date
A R Hansen
8e73e4c16d Merge branch 'develop' 2018-10-12 12:50:54 +02:00
A R Hansen
06f1f4df91 - ll - 2018-10-12 12:48:15 +02:00
A R Hansen
70f520ea43 adding instructions 2018-10-12 12:46:09 +02:00
A R Hansen
f1d2b00b2b set up my own bitcore server and changed the url. for some reason Bitcoin.com's server won't accept 0 fee txs. 2018-10-12 11:53:16 +02:00
A R Hansen
4e524a7500 Not needed. 2018-10-08 02:16:43 +02:00
A R Hansen
c0a14fe11c Merge ZeroFeeTxs 2018-10-08 02:01:15 +02:00
A R Hansen
d47393f160 .. 2018-10-08 02:00:36 +02:00
A R Hansen
898b7d03fe 0 fee transaction feature added. 2018-10-08 01:55:55 +02:00
Jean-Baptiste Dominguez
49a6711970
Merge branch 'wallet/prod' into master 2018-09-22 10:59:24 +02:00
Brendon Duncan
d72a71d920 RC version. 2018-09-21 17:21:59 -07:00
Brendon Duncan
4d623d51d7 Merged sprint 21. 2018-09-21 17:16:18 -07:00
Brendon Duncan
ff40317d87 Merged fix for wallet transaction history. 2018-09-21 16:45:46 -07:00
Brendon Duncan
2e1cc9fa87 Showing cached history first when entering Wallet Details screen. 2018-09-21 16:43:27 -07:00
Brendon Duncan
cecad4a072 Merged 575. 2018-09-21 14:58:39 -07:00
Brendon Duncan
61c91d518a Merge branch 'wallet/sprint/21'. 2018-09-21 14:50:47 -07:00
Brendon Duncan
a17c3518c3 Manual merge from Sprint 22 for 575. 2018-09-21 14:38:28 -07:00
Brendon Duncan
6554b385cb
Merge pull request #345 from Bitcoin-com/wallet/task/591
591 - Wallet balance directive fails to get fiat balance sometimes
2018-09-21 12:53:46 -07:00
Brendon Duncan
c0912c5f17 IIFE for reviewController to make merging easier. 2018-09-21 12:31:55 -07:00
Brendon Duncan
e7eac1ee16
Merge pull request #335 from Bitcoin-com/wallet/task/586
Bug - 586 - Reconnect WebSocket when connection is failing or lost.
2018-09-21 11:35:27 -07:00
Brendon Duncan
46413fa6aa
Merge pull request #346 from Bitcoin-com/wallet/task/593
Bug - 593 - Update confirmations on cached transactions
2018-09-21 11:29:30 -07:00
Brendon Duncan
fb2802f392
Merge pull request #347 from Bitcoin-com/wallet/task/546
546 - Fix modal & shapeshift
2018-09-21 11:26:08 -07:00
Brendon Duncan
ea51e035ab Fixed updating of confirmations of cached transactions. Cache now properly cleared on first fetch from Wallet Details screen. 2018-09-21 03:56:51 -07:00
Brendon Duncan
0c8d145f73 Check validity of cachedStatus. 2018-09-21 02:54:06 -07:00
Jean-Baptiste Dominguez
977d03429d
Merge pull request #343 from Bitcoin-com/wallet/task/590
590 - The Personal Note field is not cleared between transactions.
2018-09-21 11:47:28 +02:00
Jean-Baptiste Dominguez
a8e5c212f0 Fix : Check if the status could be set before checking the balance. 2018-09-21 11:41:20 +02:00
Jean-Baptiste Dominguez
4250a98718 walletStatus worst case, IIFE for both controllers. 2018-09-21 11:31:27 +02:00
Brendon Duncan
a3614566e3 Reconnecting socket based on CloseEvent.code. 2018-09-21 02:27:34 -07:00
Jean-Baptiste Dominguez
5cb3d7521d Fix modal 2018-09-21 10:23:02 +02:00
Sebastiaan Pasma
3cad7d7ad8
Update confirmations on cached transactions 2018-09-20 15:58:42 +02:00
Brendon Duncan
4d6aeb4f91 Renamed directive and replaced old variable references with new ones. 2018-09-18 15:26:30 -07:00
Sebastiaan Pasma
78e4942fd6
Merge pull request #339 from Bitcoin-com/wallet/task/589
589 - Fix mobile build scripts
2018-09-18 13:51:54 +02:00
Brendon Duncan
7b6efaa1db Clear memo before entering Review screen. 2018-09-17 21:26:21 -07:00
Brendon Duncan
ebc32a2d85 Added 'default' to mobile debug build configs. 2018-09-16 21:04:41 -07:00
Brendon Duncan
e02368ea3c Incremented version for release candidate. 2018-09-15 18:05:39 +12:00
Brendon Duncan
92c1b4b346 Merged translations from sprint. 2018-09-15 18:02:00 +12:00
Brendon Duncan
8bf31f97d8 Updated translations from Crowdin. 2018-09-15 18:00:34 +12:00
Brendon Duncan
f3902ba3f0 Merged sprint to get fix for Wallet Details transaction list. 2018-09-15 17:32:55 +12:00
Brendon Duncan
6aa7ee2bbb
Merge pull request #338 from Bitcoin-com/wallet/task/588
588 - Transaction list in Wallet Details disappearing and reappearing
2018-09-15 17:29:56 +12:00
Brendon Duncan
ac82b51848 Takes into account size of current transaction list when determining if overlap in transactions is enough to assume all have been retrieved. 2018-09-15 14:45:39 +12:00
Sebastiaan Pasma
e6d8ddb360
update wallets in the promise callback 2018-09-13 09:54:53 +02:00
Sebastiaan Pasma
eb3067d369
Use cachedStatus when available and a fix for the review controller 2018-09-13 09:47:45 +02:00
Sebastiaan Pasma
908dcbf968
Add the received wallet status to the wallet API object 2018-09-12 16:50:26 +02:00
Sebastiaan Pasma
6692812d55
Websocket reconnect (and close old connection on leave) 2018-09-12 15:09:21 +02:00
Brendon Duncan
9438960e24 Hotfix version. 2018-09-12 18:39:56 +12:00
Brendon Duncan
d55fc70e7d Remove Games link. 2018-09-12 18:13:50 +12:00
Sebastiaan Pasma
ad4aa76902
Empty origin wallet screen bug 2018-09-11 14:55:22 +02:00
Brendon Duncan
c1f6e56b56 Increment version. 2018-09-11 15:37:47 +12:00
Brendon Duncan
7d29490b0f Merged sprint. 2018-09-11 15:33:26 +12:00
Sebastiaan Pasma
a54b30f6ae
Merge pull request #334 from Bitcoin-com/wallet/task/583
583 - Remove Games Link from Google Play app
2018-09-10 10:45:56 +02:00
Brendon Duncan
319a5c86ec Merge language fix. 2018-09-10 20:28:18 +12:00
Brendon Duncan
8bc73353d5 Run command for Android emulator. 2018-09-10 20:26:33 +12:00
Brendon Duncan
101e33ec36 Remove Games link on Android. 2018-09-10 20:26:13 +12:00
Jean-Baptiste Dominguez
51c35101a6 appConfig beta release 2018-09-07 17:08:39 +09:00
Jean-Baptiste Dominguez
3d3fdd7425 Fix languages 2018-09-07 15:31:54 +09:00
Jean-Baptiste Dominguez
d361956290
Merge pull request #331 from Bitcoin-com/wallet/sprint/21
Wallet/sprint/21
2018-09-07 01:43:15 +09:00
Jean-Baptiste Dominguez
46b5317f5e
Merge pull request #307 from Bitcoin-com/wallet/task/518
518 - Add true pagination to the Wallet Detail screens
2018-09-07 01:41:39 +09:00
Jean-Baptiste Dominguez
230b6e2228 Merge 2018-09-07 01:41:13 +09:00
Jean-Baptiste Dominguez
81852836dd Fix : callback the newHistory 2018-09-07 01:18:54 +09:00
Jean-Baptiste Dominguez
9c0b54a7ce
Merge pull request #326 from Bitcoin-com/wallet/task/562
Bug - 562 - No notification when a new release version is available (Mac OSX github)
2018-09-06 22:12:45 +09:00
Jean-Baptiste Dominguez
47e1d0bdc7
Merge pull request #325 from Bitcoin-com/wallet/task/576
Bug - 576 - Android KitKat (4.4) interface fixes
2018-09-06 21:34:36 +09:00
Jean-Baptiste Dominguez
5386c5f7a6
Merge pull request #328 from Bitcoin-com/wallet/task/572
572 - Adhere to Shapeshift max amounts.
2018-09-06 20:30:43 +09:00
Jean-Baptiste Dominguez
6a8f8ca33b Fix to send the amount if shapeshift case 2018-09-06 20:29:50 +09:00
Sebastiaan Pasma
828cb927dc
refactor latestReleaseService to new format 2018-09-06 13:16:32 +02:00
Jean-Baptiste Dominguez
176f0c3141 Fix to provide an amount (canSendMax was never set to false when shapeshift) 2018-09-06 20:16:25 +09:00
Sebastiaan Pasma
c14820ea34
Shim for Array.includes (fixing receive notifications) + Android KitKat fixes (effect on slide + received payment notification) 2018-09-06 13:04:19 +02:00
Jean-Baptiste Dominguez
57ce93ccb8 fix parenthesis 2018-09-06 18:28:27 +09:00
Jean-Baptiste Dominguez
b6f63706f3 Resolution conflicts 2018-09-06 18:21:04 +09:00
Brendon Duncan
b0fba31040 Fix fee display on Android 4.4 KitKat. 2018-09-06 20:18:31 +12:00
Jean-Baptiste Dominguez
b0178c0673
Merge pull request #327 from Bitcoin-com/wallet/task/567
Improvement - 567 - Stuttering animation when scrolling transaction history.
2018-09-06 17:16:53 +09:00
Jean-Baptiste Dominguez
b2e22647e2
Merge pull request #293 from Bitcoin-com/wallet/task/555
555 - Include version number in Android apk filename.
2018-09-06 17:16:27 +09:00
Jean-Baptiste Dominguez
688f2b97bc
Merge pull request #324 from Bitcoin-com/wallet/task/546
546 - revamp shapeshift service
2018-09-06 17:13:05 +09:00
Brendon Duncan
c79ca6d1af More specific Shapeshift error handling. 2018-09-06 18:34:59 +12:00
Jean-Baptiste Dominguez
71e530f535 Unify the translation service 2018-09-06 15:31:22 +09:00
Jean-Baptiste Dominguez
a397e83cf0 error case without .message 2018-09-06 15:24:16 +09:00
Jean-Baptiste Dominguez
1905be6f14
Merge branch 'wallet/sprint/21' into wallet/task/555 2018-09-06 15:19:34 +09:00
Jean-Baptiste Dominguez
98e6e8ac75 Recommendation #brendon :) 2018-09-06 15:17:37 +09:00
Brendon Duncan
0d8812509d Updated readme. 2018-09-06 15:45:20 +12:00
Brendon Duncan
3de34dbf15 Renaming to follow new convention. 2018-09-06 11:34:00 +12:00
Brendon Duncan
394317bc46 Better default error message for Shapeshift error. 2018-09-06 11:27:48 +12:00
Sebastiaan Pasma
f52b2e7f09
Better fix for KitKat 4.4 2018-09-05 16:44:33 +02:00
Sebastiaan Pasma
525afac92e
scss var name change 2018-09-05 16:36:53 +02:00
Sebastiaan Pasma
22e1c397d0
Removed duplicate android header + refactored animation to use CSS animations instead of javascript calculated animations 2018-09-05 16:19:31 +02:00
Sebastiaan Pasma
74a9fe9691
Merge branch 'wallet/sprint/21' into wallet/task/567 2018-09-05 14:11:51 +02:00
Sebastiaan Pasma
70663492eb
Merge branch 'wallet/sprint/21' into wallet/task/562
# Conflicts:
#	src/js/controllers/tab-home.controller.js
2018-09-05 14:07:23 +02:00
Sebastiaan Pasma
477a9b6995
Android KitKat (4.4) fixes 2018-09-05 14:03:01 +02:00
Jean-Baptiste Dominguez
2d6a1528c1 handle errors properly 2018-09-05 17:23:09 +09:00
Jean-Baptiste Dominguez
3b6bb1fcdd ionicModal using for all devices 2018-09-05 16:10:21 +09:00
Jean-Baptiste Dominguez
2a607d245d using ionicModal for all devices 2018-09-05 16:10:05 +09:00
Jean-Baptiste Dominguez
fa8ce4779f async series with shapeshift & fee calculation 2018-09-05 15:32:47 +09:00
Brendon Duncan
d01baa6060 Better handling of Shapeshift error. 2018-09-05 18:16:01 +12:00
Brendon Duncan
ce02025991 Merge for building pkg. 2018-09-05 18:03:49 +12:00
Brendon Duncan
d2715016b5 Merge for building pkg. 2018-09-05 18:00:45 +12:00
Jean-Baptiste Dominguez
2c398bbe03 Fix amount check 2018-09-05 14:51:06 +09:00
Jean-Baptiste Dominguez
9128d3ebe2 Migrate to our new standard for services 2018-09-05 14:43:16 +09:00
Jean-Baptiste Dominguez
887270bfa9 clean ";" 2018-09-05 14:42:27 +09:00
Jean-Baptiste Dominguez
f7cde7a071 clean up, migrate to no-fix amount 2018-09-05 12:45:20 +09:00
Brendon Duncan
3afbdf73d3
Merge pull request #323 from Bitcoin-com/wallet/hotfix/578
578 - Shapeshift item on home tab now links to the Shapeshift website.
2018-09-05 15:30:09 +12:00
Brendon Duncan
00e4f8ac39 Hotfix version. 2018-09-05 15:28:10 +12:00
Jean-Baptiste Dominguez
5e034e7b8b clean up 2018-09-05 11:37:29 +09:00
Jean-Baptiste Dominguez
c553b51817 clean up 2018-09-05 11:02:10 +09:00
Jean-Baptiste Dominguez
c40eabf807 Clean shapeshift service and add the correct callback 2018-09-05 10:59:09 +09:00
Jean-Baptiste Dominguez
9ff9e2ea91 tab missing 2018-09-05 10:30:06 +09:00
Jean-Baptiste Dominguez
5c5558fbd4 remove final 2018-09-05 10:28:01 +09:00
Brendon Duncan
8c62bc445a Shapeshift item on home tab now goes to the Shapeshift website. 2018-09-05 12:37:39 +12:00
Brendon Duncan
ba2fa1f26e Fixed start:ios and put Grunt tasks in alphabetical order to make it easier to find definitions. 2018-09-05 10:33:47 +12:00
Brendon Duncan
8dedc04fe8 Edited obsolete comment. 2018-09-05 10:03:00 +12:00
Brendon Duncan
ea23b26795 Removed duplicate logging for Android, and created start:android-log. 2018-09-05 09:56:19 +12:00
Brendon Duncan
2121655249 Grouping scripts in alphabetical order so it is easier to find things, and know what is there. 2018-09-05 09:48:42 +12:00
Sebastiaan Pasma
001cd82afb
getMarketData can return an error 2018-09-04 17:52:53 +02:00
Sebastiaan Pasma
793bfee6a2
Changed wallet details transition behaviour 2018-09-04 16:36:05 +02:00
Brendon Duncan
6c8a1cfd5a Bugfix for using cached status in Enter Amount screen. 2018-09-04 20:48:16 +12:00
Brendon Duncan
3ab535a36b Improved handling of available funds below min amount. 2018-09-04 19:47:50 +12:00
Jean-Baptiste Dominguez
8b505141d7 renaming, clean up 2018-09-04 16:45:52 +09:00
Jean-Baptiste Dominguez
d934a9241a Organize the package.json and grunt file. Some optimizations and renaming. 2018-09-04 15:54:24 +09:00
Jean-Baptiste Dominguez
ce3fcacac3
Merge pull request #308 from Bitcoin-com/wallet/task/490
Bug - 490 - Korean Won isn't automatically selected when phone is in Korean (fiat currency)
2018-09-04 15:27:32 +09:00
Jean-Baptiste Dominguez
4c14bdcce0
Merge pull request #315 from Bitcoin-com/wallet/task/500
500 - detect addresses such as "bitcoin-cash:" and "bitcoincash://"
2018-09-04 14:39:12 +09:00
Jean-Baptiste Dominguez
e06613eaee
Merge pull request #322 from Bitcoin-com/wallet/task/546
Wallet/task/546
2018-09-04 14:37:51 +09:00
Jean-Baptiste Dominguez
2cec6a476b
Merge pull request #294 from Bitcoin-com/wallet/task/556
Improvement - 556 - Make "Share the Wallet App" translatable
2018-09-04 14:33:15 +09:00
Jean-Baptiste Dominguez
b766f2723c
Merge pull request #290 from Bitcoin-com/wallet/task/501
501 - Fix text when sweeping paper wallet
2018-09-04 14:31:40 +09:00
Jean-Baptiste Dominguez
911247e463
Merge branch 'wallet/sprint/21' into wallet/task/501 2018-09-04 14:31:22 +09:00
Jean-Baptiste Dominguez
8b38cb2d21
Merge pull request #305 from Bitcoin-com/wallet/task/335
Wallet/task/335
2018-09-04 14:17:50 +09:00
Jean-Baptiste Dominguez
57efbc3c63
Merge pull request #319 from Bitcoin-com/wallet/task/566
Bug - 566 - Fees hidden on iPhone X
2018-09-04 14:14:41 +09:00
Jean-Baptiste Dominguez
7353e0ff06
Merge pull request #320 from Bitcoin-com/wallet/task/492
492 - Fix typo in English original source
2018-09-04 14:08:42 +09:00
Jean-Baptiste Dominguez
7b1597e229
Merge pull request #321 from Bitcoin-com/wallet/task/568
Bug - 568 - Wrong display of multi-line texts in buttons
2018-09-04 14:08:24 +09:00
Jean-Baptiste Dominguez
38037f34fe
Merge pull request #292 from Bitcoin-com/wallet/task/554
554 - All Images for iOS App Icon
2018-09-04 14:03:19 +09:00
Jean-Baptiste Dominguez
1453a74302
Merge pull request #313 from Bitcoin-com/wallet/task/546
546 Enhancement "send flow" architecture
2018-09-04 13:55:41 +09:00
Jean-Baptiste Dominguez
2a017bc999 Remove duplicate data. 2018-09-04 13:37:31 +09:00
Jean-Baptiste Dominguez
05d73e3e14 init missing variables 2018-09-04 13:34:08 +09:00
Jean-Baptiste Dominguez
547b216edd migrate copayApp to bitcoincom. Clean up 2018-09-04 13:32:36 +09:00
Jean-Baptiste Dominguez
cf1dc66b59 renaming files 2018-09-04 11:56:13 +09:00
Jean-Baptiste Dominguez
23659b0cd4 Rename files reviewed 2018-09-04 11:35:01 +09:00
Jean-Baptiste Dominguez
8322453edf clean send flow state when transaction sent 2018-09-04 11:24:25 +09:00
Jean-Baptiste Dominguez
c715fdcb41 Rename incomingData to incomingDataService 2018-09-04 11:24:07 +09:00
Brendon Duncan
4315d16f73 The Enter Amount screen now correctly sets the amount when sendMax() is called when available funds exceed the max limit. 2018-09-04 12:52:54 +12:00
Brendon Duncan
4dc3e7c2e8 Enter Amount displaying ongoing progress indicator when contacting Shapeshift. Send max button now displays max limit amount when available funds are above the Shapeshift limit. 2018-09-04 12:21:57 +12:00
Brendon Duncan
b2178c84e3 When available funds are higher than Shapeshift max limit, the send max button changes to send max limit. 2018-09-04 11:02:52 +12:00
Brendon Duncan
52f18c3c9b Got the Enter Amount screen unit tests running. 2018-09-04 09:13:09 +12:00
Sebastiaan Pasma
383d811067
Multi-line buttons fix 2018-09-03 14:44:09 +02:00
Sebastiaan Pasma
274d221af0
Links changed, app store added and some logic added 2018-09-03 13:45:40 +02:00
Sebastiaan Pasma
b1bf269ca2
Review transaction screen: iPhone X fee summary display fix 2018-09-03 11:54:15 +02:00
Brendon Duncan
b7dda8b6ca Currency in send max button now updates with currency change. 2018-09-03 20:17:46 +12:00
Brendon Duncan
8908b5ef80 Send max button behaviour is now conditional based on min and max limits. 2018-09-03 20:12:20 +12:00
Brendon Duncan
1c2c381317 Can now scan good data after scanning bad data. 2018-09-03 16:56:30 +12:00
Jean-Baptiste Dominguez
6f28f6ba2b redir cb 2018-09-03 13:31:58 +09:00
Brendon Duncan
afa9fad9d2 Now parses wallet import data. 2018-09-03 14:12:12 +12:00
Jean-Baptiste Dominguez
e8b3476ea1
Merge pull request #318 from Bitcoin-com/wallet/task/500
Update bitcoin-uri-service.js
2018-09-03 10:37:25 +09:00
Brendon Duncan
f84b1e4f35 Changed name to amountInSatoshis. 2018-09-03 13:02:28 +12:00
Brendon Duncan
48b8bbf90a Added amountSatoshis. 2018-09-03 12:53:12 +12:00
Jean-Baptiste Dominguez
b4d42215d3 add copay invitation in incomingData 2018-09-03 09:08:12 +09:00
Jean-Baptiste Dominguez
6e52aaca2d 1st cleaning for incomingData 2018-09-03 08:28:22 +09:00
Jean-Baptiste Dominguez
1708eeef82 Fix address format 2018-08-31 18:48:16 +09:00
Jean-Baptiste Dominguez
ddd867092c Refactor incomingData 2018-08-31 18:37:04 +09:00
Jean-Baptiste Dominguez
fa15b26792
Update bitcoin-uri.service.js
Add the conversion to satoshi however a currency can be precised, in that case, the satoshi conversion should be not operated.
2018-08-31 18:36:40 +09:00
Jean-Baptiste Dominguez
2b8c2d1c61
Merge pull request #316 from Bitcoin-com/wallet/task/500
Changed testnet to isTestnet.
2018-08-31 16:45:40 +09:00
Jean-Baptiste Dominguez
7fa2ab7ff4 Merge 2018-08-31 16:11:58 +09:00
Jean-Baptiste Dominguez
dd59169d5f adapt for the request specific amount. Clean comments and code, and more. 2018-08-31 16:09:31 +09:00
Brendon Duncan
ef23746de6 Changed testnet to isTestnet. 2018-08-31 17:14:58 +12:00
Sebastiaan Pasma
d65e4401dd
Update popup 2018-08-30 14:10:46 +02:00
Brendon Duncan
c4d9b94a86 Merged latest changes for detecting addresses. 2018-08-30 20:49:03 +12:00
Brendon Duncan
421c4ca26a Bugfix for displaying error message. 2018-08-30 20:46:58 +12:00
Brendon Duncan
6a68d73e3a Now handles spaces after address prefix. 2018-08-30 20:20:51 +12:00
Brendon Duncan
918451f3da Scanning or pasting ordinary URL gives the option to open it in a browser. 2018-08-30 19:56:18 +12:00
Jean-Baptiste Dominguez
2c0432e292 3rd step enhancement send flow 2018-08-30 16:03:51 +09:00
Brendon Duncan
98f317dea7 Merged more permissive Bitcoin Cash URIs. 2018-08-30 18:16:26 +12:00
Jean-Baptiste Dominguez
480cfe43ca
Merge pull request #312 from Bitcoin-com/wallet/task/500
Upgrade the bitcoin uri service and its integration
2018-08-30 14:41:38 +09:00
Brendon Duncan
986f85e7aa The send tab paste button now recognises a wider variety of Bitcoin Cash prefixes. 2018-08-30 17:18:26 +12:00
Brendon Duncan
9877e13390 Corrected the addres for display in the Review screen cashAddr. 2018-08-30 16:34:21 +12:00
Brendon Duncan
5ad9c7bf49 "Testnet not supported" message when pasting. 2018-08-30 15:55:29 +12:00
Brendon Duncan
5747cbfb66 "Testnet not supported" message when scanning a testnet address. 2018-08-30 15:49:59 +12:00
Brendon Duncan
e6fba98af9 Not using data if it is for the testnet. 2018-08-30 09:04:01 +12:00
Jean-Baptiste Dominguez
162fd685e5 second step enhancement 2018-08-29 17:28:07 +09:00
Jean-Baptiste Dominguez
d864b35513 First enhancement in the send flow architecture 2018-08-29 15:38:23 +09:00
Brendon Duncan
529bdf3386 Alert and resume scanning if data is not recognised. 2018-08-29 18:28:37 +12:00
Brendon Duncan
6d48572f28 Handling all cash URI addresses formats at the same time. 2018-08-29 18:27:58 +12:00
Brendon Duncan
471a9ba4ae Treating BitPay address like cashAddr. 2018-08-29 17:39:04 +12:00
Jean-Baptiste Dominguez
b35ee316a8
Merge pull request #310 from Bitcoin-com/wallet/task/500
Upgrade bitcoin-uri.service.js
2018-08-29 14:27:23 +09:00
Brendon Duncan
8a1e3f2297 Bitcoin Cash URI in incoming data now more permissive with the prefix. 2018-08-29 15:55:49 +12:00
Brendon Duncan
89f4328f8e BIP70 in IncomingDataService uses new BitcoinUriService. 2018-08-29 15:42:43 +12:00
Brendon Duncan
5738bab13e Updated comments. 2018-08-29 15:41:13 +12:00
Brendon Duncan
fcfb039673 parse() now only returns the address in the format that was present in the data, to make it easier to find out about the data. 2018-08-29 15:21:43 +12:00
Brendon Duncan
3a3a525133 Bugfix for displaying payment amount. 2018-08-29 11:38:20 +12:00
Brendon Duncan
e6d438abf9 Test for a bare URL being invalid. 2018-08-29 11:18:09 +12:00
Brendon Duncan
32aa02a8c2 Fails gracefully when not passed a string. 2018-08-29 10:15:03 +12:00
Brendon Duncan
d30a076f4b Changed argument name to data, because it may not be a URI. 2018-08-29 10:05:23 +12:00
Brendon Duncan
217b02504a Added tests for single slash. Removed some unused code. 2018-08-29 10:04:02 +12:00
Brendon Duncan
3b74ab7d8c Tolerating extra dash and slashes. 2018-08-29 09:59:47 +12:00
Brendon Duncan
9bed4239da Grouped the output. 2018-08-29 09:54:04 +12:00
Brendon Duncan
6c85cffb20 Now handles encrypted private key. 2018-08-29 09:11:14 +12:00
Brendon Duncan
b95798d271 Tests for private keys on testnet. 2018-08-29 08:49:18 +12:00
Brendon Duncan
a9f2794d11 Testing uncompressed private keys on mainnet. 2018-08-28 20:56:10 +12:00
Brendon Duncan
61a29cf7ea Copay invitation and wifPrivateKey. 2018-08-28 18:42:03 +12:00
Brendon Duncan
c2cca3c080 Bitcoin cash uppercase. 2018-08-28 17:18:05 +12:00
Brendon Duncan
e8005c9ea6 Legacy addresses with bitcoincash: prefix. 2018-08-28 17:10:15 +12:00
Brendon Duncan
4d525c85d7 CashAddr on testnet and Bitpay format on mainnet. 2018-08-28 17:00:52 +12:00
Jean-Baptiste Dominguez
1d1fde21ea
Merge pull request #309 from Bitcoin-com/wallet/task/500
Get the interface for the sendFlowService
2018-08-28 11:22:39 +09:00
Brendon Duncan
a4ab20abba Tests for BIP72. 2018-08-28 14:17:43 +12:00
Jean-Baptiste Dominguez
4564040459 fixes for pkg mas 2018-08-28 10:50:53 +09:00
Sebastiaan Pasma
3cab114618
template.pot change 2018-08-27 14:09:06 +02:00
Sebastiaan Pasma
2c2c21bcdf
Update notifications now able to parse "rc"'s and also show release notes from GitHub. 2018-08-27 14:06:10 +02:00
Sebastiaan Pasma
8217351141
possible fix for selecting the right currency on new wallets 2018-08-27 10:59:14 +02:00
Brendon Duncan
1be9ce39c1 Starting to work on more comprehensive handling of cashAddr format. Basic handling of cashAddr on testnet. 2018-08-27 20:46:11 +12:00
Brendon Duncan
43592a1689
Fix package.json. 2018-08-27 18:04:55 +12:00
Jean-Baptiste Dominguez
d0bd3e1f61
Merge pull request #306 from Bitcoin-com/wallet/sprint/21
Wallet/sprint/21
2018-08-27 13:22:53 +09:00
Jean-Baptiste Dominguez
1c450418b8
Merge pull request #301 from Bitcoin-com/wallet/prod
5.0
2018-08-27 12:59:17 +09:00
Jean-Baptiste Dominguez
7718724958 pkg building script 2018-08-27 12:00:02 +09:00
Jean-Baptiste Dominguez
9a44a4b33b
Merge pull request #304 from Bitcoin-com/wallet/sprint/21
Wallet/sprint/21
2018-08-26 22:20:25 +09:00
Jean-Baptiste Dominguez
4deeb80a14
Merge pull request #303 from Bitcoin-com/wallet/dev
Wallet/dev
2018-08-26 22:16:46 +09:00
Jean-Baptiste Dominguez
0a4054707f
Merge pull request #302 from Bitcoin-com/wallet/prod
Wallet/prod
2018-08-26 22:07:20 +09:00
Jean-Baptiste Dominguez
1fa87fc9a0
Merge pull request #299 from Bitcoin-com/wallet/sprint/20
Wallet/sprint/20
2018-08-23 16:33:00 +09:00
Jean-Baptiste Dominguez
07ff264619
Merge pull request #297 from Bitcoin-com/wallet/dev
Wallet/dev
2018-08-23 14:40:54 +09:00
Jean-Baptiste Dominguez
dd4fcc9ed1
Update appConfig.json 2018-08-23 14:40:18 +09:00
Jean-Baptiste Dominguez
7e0998c919
Merge pull request #296 from Bitcoin-com/wallet/sprint/20
Wallet/sprint/20
2018-08-23 14:39:47 +09:00
Jean-Baptiste Dominguez
a2009a2f06
Merge pull request #295 from Bitcoin-com/wallet/task/503
Tweak strings to match translations.
2018-08-23 14:39:23 +09:00
Brendon Duncan
39bcb9daae Tweak strings to match translations. 2018-08-23 16:21:15 +12:00
Brendon Duncan
b9943c403f Testing addresses with Bitcore wallet client. 2018-08-23 14:37:02 +12:00
Brendon Duncan
1da9a79296 Parsing BTC testnet address. 2018-08-23 14:27:03 +12:00
Brendon Duncan
93d061c96a Returning addresses from cashAddr with bitcoincash: prefix. 2018-08-23 12:55:58 +12:00
Brendon Duncan
ab0b8b19b0 Returning legacy address for cashAddr. 2018-08-23 12:47:47 +12:00
Brendon Duncan
70f76baad0 bitcoinUriService passing first test. 2018-08-23 10:48:49 +12:00
Brendon Duncan
f16ef22436 amountController tests no longer fail. 2018-08-23 10:32:47 +12:00
Sebastiaan Pasma
ed998e9036
Make "Share the Wallet App" translatable 2018-08-22 16:27:30 +02:00
Sebastiaan Pasma
4cf268682b
removed comments 2018-08-22 16:21:57 +02:00
Brendon Duncan
feca8b5807 Checking every time we get earlier data, if all transactions have been fetched. 2018-08-22 20:01:30 +12:00
Brendon Duncan
e6beb6fed1 Displaying more cached data while waiting for first tx history fetch. 2018-08-22 19:51:10 +12:00
Brendon Duncan
745737ef73 Adding missing parameter. 2018-08-22 19:39:32 +12:00
Brendon Duncan
95de043d3d Merged. 2018-08-22 19:32:53 +12:00
Brendon Duncan
ad6a1fbe8d Adding latest transactions. 2018-08-22 19:28:24 +12:00
Brendon Duncan
ecb3b2391d Grunt task for signing. 2018-08-22 18:17:33 +12:00
Brendon Duncan
14f77cf03f Using generic *PACKAGENAME* in template. 2018-08-22 12:07:20 +12:00
Brendon Duncan
e5560bf63a Images with config file changes. 2018-08-22 11:48:19 +12:00
Sebastiaan Pasma
d5f01e9713
stop fetching the wallet history when everything is fetched 2018-08-21 21:36:55 +02:00
Brendon Duncan
78f0ff28cd Getting earlier transactions with pagination. 2018-08-21 12:43:03 +12:00
Brendon Duncan
0bd94601ae Controlling when infinite scroll is available. 2018-08-21 09:17:56 +12:00
Brendon Duncan
72cb94d212 Using infinite scroll on cached data. 2018-08-21 08:58:19 +12:00
Brendon Duncan
ba98fbe187 Merge sprint/21. 2018-08-20 17:23:01 +12:00
Brendon Duncan
03fa87adbc Displaying transactions from cache when first entering wallet details. 2018-08-20 17:21:30 +12:00
Brendon Duncan
bbe2fb20c4 Updated package.json attempt 4. 2018-08-20 12:02:11 +12:00
Brendon Duncan
2a5a21f7d7 Updated package.json for real. 2018-08-20 11:48:48 +12:00
Brendon Duncan
09871c9eba Updated package.json. 2018-08-20 11:46:34 +12:00
Brendon Duncan
bb92f5ec1b Merged updated sprint/21. 2018-08-20 11:31:49 +12:00
Brendon Duncan
33d780f2e5 The spinner in wallet details now spins when the infinite scroll spinner spins. 2018-08-16 15:44:59 +12:00
Brendon Duncan
93b6c3f1be Spinner same size as refresh button. 2018-08-16 15:00:51 +12:00
Sebastiaan Pasma
0ae6f043ed
loading new txs using pagination 2018-08-15 17:23:00 +02:00
Brendon Duncan
4e2c06f69d Changed "Bitcoin" to "Bitcoin Core" on the Sweep Paper Wallet screen and made other strings translatable. 2018-08-14 12:24:10 +12:00
Jean-Baptiste Dominguez
15b161d55d
Merge pull request #269 from Bitcoin-com/wallet/dev
Wallet/dev
2018-08-13 15:36:06 +09:00
Jean-Baptiste Dominguez
e127b81e7c
Merge pull request #242 from Bitcoin-com/wallet/prod
Wallet/prod
2018-08-07 23:01:10 +09:00
100 changed files with 6180 additions and 2802 deletions

14
.vscode/launch.json vendored Normal file
View file

@ -0,0 +1,14 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"type": "node",
"request": "launch",
"name": "Launch Program",
"program": "${workspaceFolder}/www/index.html"
}
]
}

View file

@ -8,26 +8,32 @@ module.exports = function(grunt) {
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
exec: {
get_nwjs_for_pkg: {
command: 'if [ ! -d ./cache/0.19.5-pkg/osx64/nwjs.app ]; then cd ./cache; curl https://dl.nwjs.io/v0.19.5-mas-beta/nwjs-mas-v0.19.5-osx-x64.zip --output nwjs.zip; unzip nwjs.zip; mkdir -p ./0.19.5-pkg/osx64; cp -R ./nwjs-mas-v0.19.5-osx-x64/nwjs.app ./0.19.5-pkg/osx64/; fi'
},
create_others_dist: {
command: 'sh webkitbuilds/create-others-dist.sh "<%= pkg.name %>" "<%= pkg.fullVersion %>" "<%= pkg.nameCaseNoSpace %>" "<%= pkg.title %>"'
},
create_dmg_dist: {
command: 'sh webkitbuilds/create-dmg-dist.sh "<%= pkg.name %>" "<%= pkg.fullVersion %>" "<%= pkg.nameCaseNoSpace %>" "<%= pkg.title %>"'
},
create_pkg_dist: {
command: 'sh webkitbuilds/create-pkg-dist.sh "<%= pkg.name %>" "<%= pkg.fullVersion %>" "<%= pkg.nameCaseNoSpace %>" "<%= pkg.title %>"'
},
sign_desktop_dist: {
command: 'sh webkitbuilds/sign-desktop-dist.sh "<%= pkg.name %>" "<%= pkg.fullVersion %>"'
},
appConfig: {
command: 'node ./util/buildAppConfig.js'
},
externalServices: {
command: 'node ./util/buildExternalServices.js'
android_studio: {
command: ' open -a open -a /Applications/Android\\ Studio.app platforms/android',
},
build_android_debug: {
command: 'cordova prepare android && cordova build android --debug',
},
build_android_release: {
command: 'cordova prepare android && cordova build android --release',
},
build_ios_debug: {
command: 'cordova prepare ios && cordova build ios --debug',
options: {
maxBuffer: 3200 * 1024
}
},
build_ios_release: {
command: 'cordova prepare ios && cordova build ios --release',
options: {
maxBuffer: 1600 * 1024
}
},
chrome: {
command: 'make -C chrome-app '
},
clean: {
command: 'rm -Rf bower_components node_modules'
@ -38,8 +44,38 @@ module.exports = function(grunt) {
coveralls: {
command: 'cat coverage/report-lcov/lcov.info |./node_modules/coveralls/bin/coveralls.js'
},
chrome: {
command: 'make -C chrome-app '
create_dmg_dist: {
command: 'sh webkitbuilds/create-dmg-dist.sh "<%= pkg.name %>" "<%= pkg.fullVersion %>" "<%= pkg.nameCaseNoSpace %>" "<%= pkg.title %>"'
},
create_others_dist: {
command: 'sh webkitbuilds/create-others-dist.sh "<%= pkg.name %>" "<%= pkg.fullVersion %>" "<%= pkg.nameCaseNoSpace %>" "<%= pkg.title %>"'
},
create_pkg_dist: {
command: 'sh webkitbuilds/create-pkg-dist.sh "<%= pkg.name %>" "<%= pkg.fullVersion %>" "<%= pkg.nameCaseNoSpace %>" "<%= pkg.title %>"'
},
externalServices: {
command: 'node ./util/buildExternalServices.js'
},
get_nwjs_for_pkg: {
command: 'if [ ! -d ./cache/0.19.5-pkg/osx64/nwjs.app ]; then cd ./cache; curl https://dl.nwjs.io/v0.19.5-mas-beta/nwjs-mas-v0.19.5-osx-x64.zip --output nwjs.zip; unzip nwjs.zip; mkdir -p ./0.19.5-pkg/osx64; cp -R ./nwjs-mas-v0.19.5-osx-x64/nwjs.app ./0.19.5-pkg/osx64/; fi'
},
log_android: {
command: 'adb logcat | grep chromium',
},
run_android: {
command: 'cordova run android --device',
},
run_android_emulator: {
command: 'cordova run android --emulator',
},
sign_android: {
// When the build log outputs "Built the following apk(s):", it seems to need the filename to start with "android-release".
// It looks like it simply lists all apk files starting with "android-release"
command: 'rm -f platforms/android/build/outputs/apk/android-release-signed-*.apk; jarsigner -verbose -sigalg SHA1withRSA -digestalg SHA1 -keystore ../bitcoin-com-release-key.jks -signedjar platforms/android/build/outputs/apk/android-release-signed.apk platforms/android/build/outputs/apk/android-release-unsigned.apk bitcoin-com && zipalign -v 4 platforms/android/build/outputs/apk/android-release-signed.apk platforms/android/build/outputs/apk/bitcoin-com-wallet-<%= pkg.fullVersion %>-android-signed-aligned.apk',
stdin: true,
},
sign_desktop_dist: {
command: 'sh webkitbuilds/sign-desktop-dist.sh "<%= pkg.name %>" "<%= pkg.fullVersion %>"'
},
wpinit: {
command: 'make -C cordova wp-init',
@ -47,40 +83,9 @@ module.exports = function(grunt) {
wpcopy: {
command: 'make -C cordova wp-copy',
},
iosdebug: {
command: 'npm run build:ios',
},
ios: {
command: 'npm run build:ios-release',
},
xcode: {
command: 'npm run open:ios',
},
androiddebug: {
command: 'npm run build:android',
},
android: {
command: 'npm run build:android-release',
},
androidrun: {
command: 'npm run run:android && npm run log:android',
},
androidbuild: {
command: 'cd cordova/project && cordova build android --release',
},
androidsign: {
command: 'rm -f cordova/project/platforms/android/build/outputs/apk/android-release-signed-aligned.apk; jarsigner -verbose -sigalg SHA1withRSA -digestalg SHA1 -keystore ../bitcoin-com-release-key.jks -signedjar cordova/project/platforms/android/build/outputs/apk/android-release-signed.apk cordova/project/platforms/android/build/outputs/apk/android-release-unsigned.apk bitcoin-com && ../android-sdk-macosx/build-tools/27.0.1/zipalign -v 4 cordova/project/platforms/android/build/outputs/apk/android-release-signed.apk cordova/project/platforms/android/build/outputs/apk/android-release-signed-aligned.apk ',
stdin: true,
},
desktopsign: {
cmd: 'gpg -u E0AE67E7 --output webkitbuilds/others/<%= pkg.title %>-linux.zip.sig --detach-sig webkitbuilds/others/<%= pkg.title %>-linux.zip ; gpg -u E0AE67E7 --output webkitbuilds/others/<%= pkg.title %>.exe.sig --detach-sig webkitbuilds/others/<%= pkg.title %>.exe'
},
desktopverify: {
cmd: 'gpg --verify webkitbuilds/<%= pkg.title %>-linux.zip.sig webkitbuilds/<%= pkg.title %>-linux.zip; gpg --verify webkitbuilds/<%= pkg.title %>.exe.sig webkitbuilds/<%= pkg.title %>.exe'
},
osxsign: {
cmd: 'gpg -u E0AE67E7 --output webkitbuilds/<%= pkg.title %>.dmg.sig --detach-sig webkitbuilds/<%= pkg.title %>.dmg'
},
command: 'open platforms/ios/*.xcodeproj',
}
},
watch: {
options: {
@ -296,10 +301,10 @@ module.exports = function(grunt) {
},
pkg: {
options: {
appName: '<%= pkg.nameCaseNoSpace %>',
appName: '<%= pkg.title %>',
platforms: ['osx64'],
buildDir: './webkitbuilds/pkg',
version: '0.19.5',
version: '0.19.4',
macIcns: './resources/<%= pkg.name %>/mac/pkg/app.icns',
exeIco: './www/img/app/logo.ico',
macPlist: {
@ -348,30 +353,49 @@ module.exports = function(grunt) {
grunt.registerTask('wp', ['prod', 'exec:wp']);
grunt.registerTask('wp-copy', ['default', 'exec:wpcopy']);
grunt.registerTask('wp-init', ['default', 'exec:wpinit']);
grunt.registerTask('ios', ['exec:ios']);
grunt.registerTask('ios-debug', ['exec:iosdebug']);
grunt.registerTask('ios-run', ['exec:xcode']);
grunt.registerTask('cordovaclean', ['exec:cordovaclean']);
grunt.registerTask('android-debug', ['exec:androiddebug', 'exec:androidrun']);
grunt.registerTask('android', ['exec:android']);
grunt.registerTask('android-release', ['prod', 'exec:android', 'exec:androidsign']);
grunt.registerTask('desktopsign', ['exec:desktopsign', 'exec:desktopverify']);
// Build all
grunt.registerTask('build-app-release', ['build-mobile-release', 'build-desktop-release']);
/**
* Mobile app
*/
// Build mobile app
grunt.registerTask('build-mobile-release', ['build-ios-release', 'build-android-release']);
// Build ios
grunt.registerTask('start-ios', ['default', 'exec:build_ios_debug', 'exec:xcode']);
grunt.registerTask('build-ios-debug', ['default', 'exec:build_ios_debug']);
grunt.registerTask('build-ios-release', ['prod', 'exec:build_ios_release']);
// Build android
grunt.registerTask('start-android', ['build-android-debug', 'exec:run_android']);
grunt.registerTask('build-android-debug', ['default', 'exec:build_android_debug']);
grunt.registerTask('start-android-emulator', ['build-android-debug', 'exec:run_android_emulator']);
grunt.registerTask('build-android-release', ['prod', 'exec:build_android_release', 'sign-android']);
grunt.registerTask('sign-android', ['exec:sign_android']);
/**
* Desktop app
*/
// Build desktop
grunt.registerTask('desktop-build', ['desktop-others', 'desktop-osx-dmg', 'desktop-osx-pkg']);
grunt.registerTask('build-desktop', ['build-desktop-others', 'build-desktop-osx-dmg', 'build-desktop-osx-pkg']);
// Build desktop win64 & linux64
grunt.registerTask('desktop-others', ['prod', 'nwjs:others', 'copy:linux', 'exec:create_others_dist']);
grunt.registerTask('build-desktop-others', ['prod', 'nwjs:others', 'copy:linux', 'exec:create_others_dist']);
// Build desktop osx pkg
grunt.registerTask('desktop-osx-pkg', ['prod', 'exec:get_nwjs_for_pkg', 'nwjs:pkg', 'exec:create_pkg_dist']);
grunt.registerTask('build-desktop-osx-pkg', ['prod', 'exec:get_nwjs_for_pkg', 'nwjs:pkg', 'exec:create_pkg_dist']);
// Build desktop osx dmg
grunt.registerTask('desktop-osx-dmg', ['prod', 'nwjs:dmg', 'exec:create_dmg_dist']);
grunt.registerTask('build-desktop-osx-dmg', ['prod', 'nwjs:dmg', 'exec:create_dmg_dist']);
// Sign desktop
grunt.registerTask('desktop-sign', ['exec:sign_desktop_dist']);
grunt.registerTask('sign-desktop', ['exec:sign_desktop_dist']);
// Release desktop
grunt.registerTask('desktop-release', ['desktop-build', 'desktop-sign']);
grunt.registerTask('build-desktop-release', ['build-desktop', 'sign-desktop']);
};

View file

@ -1,3 +1,24 @@
This is a fork of the Bitcoin.com wallet to add additional features.
Features included:
- Zero fee transactions (only works for Bitcoin Cash). You will be asked for, if you want to send a transaction as zero fee on the confirmation page.
## Zero fee transactions:
Because most network nodes on the Bitcoin Cash network don't relay zero fee txs, you will experience some strange issues, but don't worry: for me personally the Bitcoin.com pool has included all my zero fee transactions, but please beware that the receiver probably won't see your tx before it has been confirmed and please do also keep in mind, that the transactions coming after it won't confirm or be seen before the zero fee one has been confirmed.
If you do already have a Bitcoin.com wallet, you need to create a new one to use this feature or change the wallet URL to: https://bws.freepages.dk/bws/api
## Disclaimer
Please beware this is my personal experimental project. You are more than welcome to play with it, but I don't take any responsibility of loss of funds due to errors in the code, so please make sure you made a backup before running this software.
## Builds
You can build the software yourself using the instructions below or use prebuilt binaries which can be found here (currently Windows and Linux only): https://ipfs.io/ipfs/QmR1DaS3QsDS48SzAWKUWFfmtMfJc4tgMtkSk3JFmuzewe
##
The Bitcoin.com wallet is a fork of the Copay Wallet (https://github.com/bitpay/copay).
The Bitcoin.com wallet is a secure bitcoin wallet platform for both desktop and mobile devices. It uses [Bitcore Wallet Service](https://github.com/Bitcoin-com/bitcore-wallet-service) (our fork of the [Bitpay Bitcore Wallet Service](https://github.com/bitpay/bitcore-wallet-service)) (BWS) for peer synchronization and network interfacing.
@ -113,14 +134,14 @@ npm run start:desktop
Before building the release version for a platform, run the `clean-all` command to delete any untracked files in your current working directory. (Be sure to stash any uncommited changes you've made.) This guarantees consistency across builds for the current state of this repository.
The `final` commands build the production version of the app, and bundle it with the release version of the platform being built.
The `build:*-release` commands build the production version of the app, and bundle it with the release version of the platform being built.
### Android
```sh
npm run clean-all
npm run apply:bitcoincom
npm run final:android
npm run build:android-release
```
### iOS
@ -128,7 +149,7 @@ npm run final:android
```sh
npm run clean-all
npm run apply:bitcoincom
npm run final:ios
npm run build:ios-release
```
### Desktop (Linux, macOS, and Windows)
@ -136,7 +157,7 @@ npm run final:ios
```sh
npm run clean-all
npm run apply:bitcoincom
npm run final:desktop
npm run build:desktop-release
```
## About The Bitcoin.com Wallet

View file

@ -24,9 +24,9 @@
"windowsAppId": "804636ee-b017-4cad-8719-e58ac97ffa5c",
"pushSenderId": "1036948132229",
"description": "A Secure Bitcoin Wallet",
"version": "5.0.1",
"fullVersion": "5.0-rc2",
"androidVersion": "500100",
"version": "5.1.3",
"fullVersion": "5.1-rc2",
"androidVersion": "501003",
"_extraCSS": "",
"_enabledExtensions": {
"coinbase": false,

View file

@ -85,6 +85,12 @@
<config-file platform="ios" target="*-Info.plist" parent="UIStatusBarHidden"><true/></config-file>
<config-file platform="ios" target="*-Info.plist" parent="UIViewControllerBasedStatusBarAppearance"><false/></config-file>
<config-file target="*-Info.plist" parent="ITSAppUsesNonExemptEncryption"><false/></config-file>
<icon src="resources/*PACKAGENAME*/ios/icon/AppIcon24x24@2x.png" width="48" height="48" />
<icon src="resources/*PACKAGENAME*/ios/icon/AppIcon27.5x27.5@2x.png" width="55" height="55" />
<icon src="resources/*PACKAGENAME*/ios/icon/AppIcon44x44@2x.png" width="88" height="88" />
<icon src="resources/*PACKAGENAME*/ios/icon/AppIcon86x86@2x.png" width="172" height="172" />
<icon src="resources/*PACKAGENAME*/ios/icon/AppIcon98x98@2x.png" width="196" height="196" />
<icon src="resources/*PACKAGENAME*/ios/icon/icon-20.png" width="20" height="20" />
<icon src="resources/*PACKAGENAME*/ios/icon/icon-60@3x.png" width="180" height="180" />
<icon src="resources/*PACKAGENAME*/ios/icon/icon-60.png" width="60" height="60" />
<icon src="resources/*PACKAGENAME*/ios/icon/icon-60@2x.png" width="120" height="120" />
@ -102,6 +108,7 @@
<icon src="resources/*PACKAGENAME*/ios/icon/icon-small@3x.png" width="87" height="87" />
<icon src="resources/*PACKAGENAME*/ios/icon/icon-50.png" width="50" height="50" />
<icon src="resources/*PACKAGENAME*/ios/icon/icon-50@2x.png" width="100" height="100" />
<icon src="resources/*PACKAGENAME*/ios/icon/icon-1024.png" width="1024" height="1024" />
<splash src="resources/*PACKAGENAME*/ios/splash/Default~iphone.png" width="320" height="480"/>
<splash src="resources/*PACKAGENAME*/ios/splash/Default@2x~iphone.png" width="640" height="960"/>

View file

@ -24,8 +24,9 @@ ln -s ../resources/bitcoin.com/mac/pkg/build.cfg build.cfg
rm build_mas.py
ln -s ../resources/bitcoin.com/mac/pkg/build_mas.py build_mas.py
echo "Signing ${APP_NAME}"
export APP_PATH="pkg/${APP_NAME}/osx64/${APP_NAME}"
echo "Signing ${APP_FULLNAME}"
export CURRENT_PATH=`pwd`
export APP_PATH="pkg/${APP_FULLNAME}/osx64/${APP_FULLNAME}"
export TMP_PATH="tmp"
export DIST_PATH="dist"
@ -36,10 +37,16 @@ if [ ! -d $DIST_PATH ]; then
mkdir $DIST_PATH
fi
python build_mas.py -C build.cfg -O "${TMP_PATH}/${APP_NAME}.app" -I "${APP_PATH}.app" -P "$DIST_PATH/${APP_PACKAGE}-wallet-${APP_VERSION}-osx.pkg"
cd "${APP_PATH}.app/Contents/Versions"
ln -s "55.0.2883.87" "Current"
cd $CURRENT_PATH
chmod -vR 777 "${APP_PATH}.app/Contents"
python build_mas.py -C build.cfg -O "${TMP_PATH}/${APP_FULLNAME}.app" -I "${APP_PATH}.app" -P "$DIST_PATH/${APP_PACKAGE}-wallet-${APP_VERSION}-osx.pkg"
echo "Signing Done"
echo "Done."
exit
exit

View file

@ -87,46 +87,53 @@
"bitcoincashjs-fork": "^1.0.3"
},
"scripts": {
"postinstall": "bower install",
"start": "npm run build:www && ionic serve --nolivereload --nogulp -s --address 0.0.0.0",
"start:chrome": "npm run build:www && ionic serve --nolivereload --nogulp -s --address 0.0.0.0 --browser \"google chrome\"",
"start:ios": "npm run build:www && npm run build:ios && npm run open:ios",
"start:android": "npm run build:www && npm run build:android && npm run run:android",
"start:windows": "npm run build:www && npm run build:windows",
"start:desktop": "npm start",
"watch": "grunt watch",
"apply:bitcoincom": "npm i fs-extra && cd app-template && node apply.js bitcoincom && npm i && cordova prepare && cd ../ && ./fix-asn1.sh",
"build:app-release": "grunt build-app-release",
"build:mobile-release": "grunt build-mobile-release",
"build:desktop-release": "grunt build-desktop-release",
"build:android-debug": "grunt build-android-debug",
"build:android-release": "grunt build-android-release",
"build:ios-debug": "grunt build-ios-debug",
"build:ios-release": "grunt build-ios-release",
"build:desktop": "grunt build-desktop",
"build:osx-pkg": "grunt build-desktop-osx-pkg",
"build:osx-dmg": "grunt build-desktop-osx-dmg",
"build:others": "grunt build-desktop-others",
"build:windows": "cordova prepare windows && cordova build windows -- --arch=\"ARM\"",
"build:windows-release": "cordova prepare windows && cordova build windows --release --arch=\"ARM\"",
"build:www": "grunt",
"build:www-release": "grunt prod",
"build:ios": "cordova prepare ios && cordova build ios --debug",
"build:android": "cordova prepare android && cordova build android --debug",
"build:windows": "cordova prepare windows && cordova build windows -- --arch=\"ARM\"",
"build:ios-release": "cordova prepare ios && cordova build ios --release",
"build:android-release": "cordova prepare android && cordova build android --release",
"build:windows-release": "cordova prepare windows && cordova build windows --release --arch=\"ARM\"",
"build:desktop-release": "grunt desktop-release",
"build:desktop": "grunt desktop-build",
"build:osx-pkg": "grunt desktop-osx-pkg",
"build:osx-dmg": "grunt desktop-osx-dmg",
"build:others": "grunt desktop-others",
"sign:desktop": "grunt desktop-sign",
"open:ios": "open platforms/ios/*.xcodeproj",
"open:android": "open -a open -a /Applications/Android\\ Studio.app platforms/android",
"final:www": "npm run build:www-release",
"final:ios": "npm run final:www && npm run build:ios-release && npm run open:ios",
"final:android": "npm run final:www && npm run build:android-release && npm run sign:android && npm run run:android-release",
"final:windows": "npm run final:www && npm run build:windows-release",
"final:desktop": "npm run final:www && npm run build:desktop-release",
"run:android": "cordova run android --device",
"run:android-release": "cordova run android --device --release",
"log:android": "adb logcat | grep chromium",
"sign:android": "rm -f platforms/android/build/outputs/apk/android-release-signed-aligned.apk; jarsigner -verbose -sigalg SHA1withRSA -digestalg SHA1 -keystore ../bitcoin-com-release-key.jks -signedjar platforms/android/build/outputs/apk/android-release-signed.apk platforms/android/build/outputs/apk/android-release-unsigned.apk bitcoin-com && zipalign -v 4 platforms/android/build/outputs/apk/android-release-signed.apk platforms/android/build/outputs/apk/android-release-signed-aligned.apk",
"apply:copay": "npm i fs-extra && cd app-template && node apply.js copay && npm i && cordova prepare",
"apply:bitpay": "npm i fs-extra && cd app-template && node apply.js bitpay && npm i && cordova prepare",
"apply:bitcoincom": "npm i fs-extra && cd app-template && node apply.js bitcoincom && npm i && cordova prepare && cd ../ && ./fix-asn1.sh",
"test": "karma start test/karma.conf.js --single-run",
"clean": "trash platforms && trash plugins && cordova prepare",
"clean-all": "git clean -dfx",
"log:android": "adb logcat | grep chromium",
"open:android": "grunt exec:android_studio",
"open:ios": "grunt exec:xcode",
"postinstall": "bower install",
"sign:android": "grunt sign-android",
"sign:desktop": "grunt sign-desktop",
"start": "npm run build:www && ionic serve --nolivereload --nogulp -s --address 0.0.0.0",
"start:chrome": "npm run build:www && ionic serve --nolivereload --nogulp -s --address 0.0.0.0 --browser \"google chrome\"",
"start:android": "grunt start-android",
"start:android-emulator": "grunt start-android",
"start:android-log": "grunt start-android && npm run log:android",
"start:ios": "grunt start-ios",
"start:windows": "npm run build:www && npm run build:windows",
"test": "karma start test/karma.conf.js --single-run",
"unstage-package": "git reset package.json",
"clean-all": "git clean -dfx"
"watch": "grunt watch"
},
"devDependencies": {
"cordova": "^6.3.1",

View file

@ -11,7 +11,7 @@ msgstr ""
"Last-Translator: emilold\n"
"Language-Team: Catalan\n"
"Language: ca\n"
"PO-Revision-Date: 2018-08-21 03:39\n"
"PO-Revision-Date: 2018-09-15 05:56\n"
#: www/views/modals/paypro.html:34
msgid "(Trusted)"
@ -520,7 +520,7 @@ msgid "Cannot Create Wallet"
msgstr "No es pot crear la cartera"
#: src/js/services/profileService.js:442
msgid "Cannot join the same wallet more that once"
msgid "Cannot join the same wallet more than once"
msgstr "No us podeu unir a la mateixa cartera més d'un cop"
#: www/views/includes/bitpayCardsCard.html:2
@ -2925,10 +2925,15 @@ msgid "Sweep"
msgstr "Escombratge"
#: www/views/includes/incomingDataMenu.html:89
#: www/views/paperWallet.html:3
msgctxt "List item"
msgid "Sweep paper wallet"
msgstr "Escombra la cartera de paper"
#: www/views/paperWallet.html:3
msgctxt "Page title"
msgid "Sweep Paper Wallet"
msgstr "Escombra la cartera de paper"
#: src/js/services/onGoingProcess.js:33
msgid "Sweeping Wallet..."
msgstr "S'està escombrant la cartera..."
@ -3792,6 +3797,10 @@ msgstr "Explora Bitcoin.com"
msgid "Bitcoin Cash Games"
msgstr "Jocs de Bitcoin Cash"
#: www/views/includes/community.html:29
msgid "Share the Wallet App"
msgstr "Comparteix l'aplicació de la cartera"
#: src/js/services/bitcoincomService.js:28
msgid "News"
msgstr "Notícies"
@ -3860,3 +3869,75 @@ msgstr "Menys d'1 cèntim"
msgid "This invoice is no longer accepting payments"
msgstr "Aquesta factura ja no accepta pagaments"
#: www/views/amount.html.js:60
msgid "Send Maximum Amount"
msgstr "Envia la quantitat màxima"
#: src/js/controllers/amount.controller.js:239
msgid "Unknown error."
msgstr "Error desconegut."
#: www/views/paperWallet.html:48
msgid "No Bitcoin Cash wallet to transfer funds to found."
msgstr "No hi ha cap cartera de Bitcoin Cash per transferir els fons trobats."
#: www/views/paperWallet.html:54
msgid "No Bitcoin Cash found."
msgstr "No s'ha trobat cap Bitcoin Cash."
#: www/views/paperWallet.html:60
msgid "Bitcoin Core found:"
msgstr "S'ha trobat Bitcoin Core:"
#: www/views/paperWallet.html:98
msgid "No Bitcoin Core wallet to transfer funds to found."
msgstr "No hi ha cap cartera de Bitcoin Core per transferir els fons trobats."
#: www/views/paperWallet.html:104
msgid "No Bitcoin Core found."
msgstr "No s'ha trobat cap Bitcoin Core."
#: src/js/controllers/tab-scan.js:120
msgid "Scan Failed"
msgstr "L'escaneig ha fallat"
#: src/js/controllers/tab-scan.js:121
msgid "Data not recognised."
msgstr "No s'han reconegut les dades."
#: src/js/controllers/tab-scan.js:121
msgid "Unsupported"
msgstr "No compatible"
#: src/js/controllers/tab-scan.js:121
msgid "Testnet is not supported."
msgstr "Testnet no és compatible."
#: www/views/includes/incomingDataMenu.html:81
msgid "URL"
msgstr "URL"
#: www/views/includes/incomingDataMenu.html:90
msgid "Open in web browser"
msgstr "Obre a l'explorador web"
#: src/js/services/shapeshift.service.js.html:90
msgid "Invalid address"
msgstr "Adreça no vàlida"
#: src/js/services/shapeshift.service.js.html:90
msgid "Amount is not defined"
msgstr "No s'ha definit l'import"
#: src/js/services/shapeshift.service.js.html:90
msgid "Amount is below the minimun"
msgstr "L'import és inferior al mínim"
#: src/js/services/shapeshift.service.js.html:90
msgid "Amount is above the limit"
msgstr "L'import supera el límit"
#: src/js/services/shapeshift.service.js.html:90
msgid "Invalid response from Shapeshift"
msgstr "Resposta no vàlida de Shapeshift"

View file

@ -11,7 +11,7 @@ msgstr ""
"Last-Translator: emilold\n"
"Language-Team: Czech\n"
"Language: cs\n"
"PO-Revision-Date: 2018-08-21 03:39\n"
"PO-Revision-Date: 2018-09-15 05:56\n"
#: www/views/modals/paypro.html:34
msgid "(Trusted)"
@ -520,8 +520,8 @@ msgid "Cannot Create Wallet"
msgstr "Není možné vytvořit peněženku"
#: src/js/services/profileService.js:442
msgid "Cannot join the same wallet more that once"
msgstr "Není možné několikrát sloučit stejnou peněženku"
msgid "Cannot join the same wallet more than once"
msgstr "Nelze se připojit vícekrát ke stejné peněžence"
#: www/views/includes/bitpayCardsCard.html:2
msgid "Cards"
@ -1297,7 +1297,7 @@ msgstr "Nastavení filtru"
#: src/js/services/fingerprintService.js:43
#: src/js/services/fingerprintService.js:48
msgid "Finger Scan Failed"
msgstr "Scan otisku prstu se nezdařil"
msgstr "Sken otisku prstu se nezdařil"
#: src/js/controllers/feedback/send.js:34
#: www/views/feedback/complete.html:7
@ -1667,7 +1667,7 @@ msgstr "Neplatná odvozovací cesta"
#: src/js/controllers/copayers.js:90
msgid "Invitation to share a {{appName}} Wallet"
msgstr "Pozvání ke sdílení {{appName}} peněženky"
msgstr "Pozvánka ke sdílení {{appName}} peněženky"
#: www/views/mercadoLibreCards.html:20
#: www/views/modals/mercadolibre-card-details.html:48
@ -2925,10 +2925,15 @@ msgid "Sweep"
msgstr "Sweep"
#: www/views/includes/incomingDataMenu.html:89
#: www/views/paperWallet.html:3
msgctxt "List item"
msgid "Sweep paper wallet"
msgstr "Sweep papírové peněženky"
#: www/views/paperWallet.html:3
msgctxt "Page title"
msgid "Sweep Paper Wallet"
msgstr "Sweep papírové peněženky"
#: src/js/services/onGoingProcess.js:33
msgid "Sweeping Wallet..."
msgstr "Probíhá sweep peněženky..."
@ -3792,6 +3797,10 @@ msgstr "Prohlédnout Bitcoin.com"
msgid "Bitcoin Cash Games"
msgstr "Bitcoin Cash hry"
#: www/views/includes/community.html:29
msgid "Share the Wallet App"
msgstr "Sdílet aplikaci peněženky"
#: src/js/services/bitcoincomService.js:28
msgid "News"
msgstr "Novinky"
@ -3860,3 +3869,75 @@ msgstr "Méně než 1 koruna"
msgid "This invoice is no longer accepting payments"
msgstr "Tato faktura již nepřijímá platby"
#: www/views/amount.html.js:60
msgid "Send Maximum Amount"
msgstr "Odeslat maximální částku"
#: src/js/controllers/amount.controller.js:239
msgid "Unknown error."
msgstr "Neznámá chyba."
#: www/views/paperWallet.html:48
msgid "No Bitcoin Cash wallet to transfer funds to found."
msgstr "Žádná Bitcoin Cash peněženka pro transfer nalezených prostředků."
#: www/views/paperWallet.html:54
msgid "No Bitcoin Cash found."
msgstr "Žádný Bitcoin Cash nenalezen."
#: www/views/paperWallet.html:60
msgid "Bitcoin Core found:"
msgstr "Nalezen Bitcoin Core:"
#: www/views/paperWallet.html:98
msgid "No Bitcoin Core wallet to transfer funds to found."
msgstr "Žádná Bitcoin Core peněženka pro transfer nalezených prostředků."
#: www/views/paperWallet.html:104
msgid "No Bitcoin Core found."
msgstr "Žádný Bitcoin Core nenalezen."
#: src/js/controllers/tab-scan.js:120
msgid "Scan Failed"
msgstr "Sken selhal"
#: src/js/controllers/tab-scan.js:121
msgid "Data not recognised."
msgstr "Data nebyla rozpoznána."
#: src/js/controllers/tab-scan.js:121
msgid "Unsupported"
msgstr "Nepodporováno"
#: src/js/controllers/tab-scan.js:121
msgid "Testnet is not supported."
msgstr "Testnet není podporován."
#: www/views/includes/incomingDataMenu.html:81
msgid "URL"
msgstr "URL"
#: www/views/includes/incomingDataMenu.html:90
msgid "Open in web browser"
msgstr "Otevřít ve webovém prohlížeči"
#: src/js/services/shapeshift.service.js.html:90
msgid "Invalid address"
msgstr "Neplatná adresa"
#: src/js/services/shapeshift.service.js.html:90
msgid "Amount is not defined"
msgstr "Částka nedefinována"
#: src/js/services/shapeshift.service.js.html:90
msgid "Amount is below the minimun"
msgstr "Nižší než minimální částka"
#: src/js/services/shapeshift.service.js.html:90
msgid "Amount is above the limit"
msgstr "Nadlimitní částka"
#: src/js/services/shapeshift.service.js.html:90
msgid "Invalid response from Shapeshift"
msgstr "Neplatná odpověď z Shapeshift"

View file

@ -11,7 +11,7 @@ msgstr ""
"Last-Translator: emilold\n"
"Language-Team: German\n"
"Language: de\n"
"PO-Revision-Date: 2018-08-21 03:39\n"
"PO-Revision-Date: 2018-09-15 05:56\n"
#: www/views/modals/paypro.html:34
msgid "(Trusted)"
@ -520,7 +520,7 @@ msgid "Cannot Create Wallet"
msgstr "Wallet kann nicht erstellt werden"
#: src/js/services/profileService.js:442
msgid "Cannot join the same wallet more that once"
msgid "Cannot join the same wallet more than once"
msgstr "An einem Wallet kann nicht mehrfach teilgenommen werden"
#: www/views/includes/bitpayCardsCard.html:2
@ -2925,10 +2925,15 @@ msgid "Sweep"
msgstr "Leeren"
#: www/views/includes/incomingDataMenu.html:89
#: www/views/paperWallet.html:3
msgctxt "List item"
msgid "Sweep paper wallet"
msgstr "Paperwallet löschen"
#: www/views/paperWallet.html:3
msgctxt "Page title"
msgid "Sweep Paper Wallet"
msgstr "Paperwallet löschen"
#: src/js/services/onGoingProcess.js:33
msgid "Sweeping Wallet..."
msgstr "Leere Wallet..."
@ -3792,6 +3797,10 @@ msgstr "Erkunden Sie Bitcoin.com"
msgid "Bitcoin Cash Games"
msgstr "Bitcoin Cash Spiele"
#: www/views/includes/community.html:29
msgid "Share the Wallet App"
msgstr "Die Wallet-App teilen"
#: src/js/services/bitcoincomService.js:28
msgid "News"
msgstr "Neuigkeiten"
@ -3860,3 +3869,75 @@ msgstr "Weniger als 1 Cent"
msgid "This invoice is no longer accepting payments"
msgstr "Diese Rechnung akzeptiert keine Zahlungen mehr"
#: www/views/amount.html.js:60
msgid "Send Maximum Amount"
msgstr "Maximalen Betrag senden"
#: src/js/controllers/amount.controller.js:239
msgid "Unknown error."
msgstr "Unbekannter Fehler."
#: www/views/paperWallet.html:48
msgid "No Bitcoin Cash wallet to transfer funds to found."
msgstr "Keine Bitcoin Cash Wallet, zu welchem Geld verschickt werden kann, gefunden."
#: www/views/paperWallet.html:54
msgid "No Bitcoin Cash found."
msgstr "Kein Bitcoin Cash gefunden."
#: www/views/paperWallet.html:60
msgid "Bitcoin Core found:"
msgstr "Bitcoin Core gefunden:"
#: www/views/paperWallet.html:98
msgid "No Bitcoin Core wallet to transfer funds to found."
msgstr "Keine Bitcoin Core Wallet, zu welchem Geld verschickt werden kann, gefunden."
#: www/views/paperWallet.html:104
msgid "No Bitcoin Core found."
msgstr "Kein Bitcoin Core gefunden."
#: src/js/controllers/tab-scan.js:120
msgid "Scan Failed"
msgstr "Scan ist fehlgeschlagen"
#: src/js/controllers/tab-scan.js:121
msgid "Data not recognised."
msgstr "Daten nicht erkannt."
#: src/js/controllers/tab-scan.js:121
msgid "Unsupported"
msgstr "Nicht unterstützt"
#: src/js/controllers/tab-scan.js:121
msgid "Testnet is not supported."
msgstr "Testnet wird nicht unterstützt."
#: www/views/includes/incomingDataMenu.html:81
msgid "URL"
msgstr "URL"
#: www/views/includes/incomingDataMenu.html:90
msgid "Open in web browser"
msgstr "Im Webbrowser öffnen"
#: src/js/services/shapeshift.service.js.html:90
msgid "Invalid address"
msgstr "Ungültige Adresse"
#: src/js/services/shapeshift.service.js.html:90
msgid "Amount is not defined"
msgstr "Betrag ist nicht definiert"
#: src/js/services/shapeshift.service.js.html:90
msgid "Amount is below the minimun"
msgstr "Menge liegt unter dem Minimum"
#: src/js/services/shapeshift.service.js.html:90
msgid "Amount is above the limit"
msgstr "Betrag ist über dem Grenzwert"
#: src/js/services/shapeshift.service.js.html:90
msgid "Invalid response from Shapeshift"
msgstr "Ungültige Antwort von Shapeshift"

View file

@ -11,7 +11,7 @@ msgstr ""
"Last-Translator: emilold\n"
"Language-Team: Spanish\n"
"Language: es\n"
"PO-Revision-Date: 2018-08-21 03:40\n"
"PO-Revision-Date: 2018-09-15 05:56\n"
#: www/views/modals/paypro.html:34
msgid "(Trusted)"
@ -520,7 +520,7 @@ msgid "Cannot Create Wallet"
msgstr "No se pudo crear la billetera"
#: src/js/services/profileService.js:442
msgid "Cannot join the same wallet more that once"
msgid "Cannot join the same wallet more than once"
msgstr "No puede unirse a la misma billetera más de una vez"
#: www/views/includes/bitpayCardsCard.html:2
@ -2674,7 +2674,7 @@ msgstr "Guardar las direcciones que usas frecuentemente y envía Bitcoin en un c
#: www/views/tab-send.html:55
msgid "Add your first contact"
msgstr "Añadie tu primer contacto"
msgstr "Añade tu primer contacto"
#: www/views/tab-send.html:65
msgid "Your Bitcoin wallet is empty"
@ -2925,9 +2925,14 @@ msgid "Sweep"
msgstr "Importar"
#: www/views/includes/incomingDataMenu.html:89
#: www/views/paperWallet.html:3
msgctxt "List item"
msgid "Sweep paper wallet"
msgstr "Importar billetera en papel"
msgstr "Importar billetera de papel"
#: www/views/paperWallet.html:3
msgctxt "Page title"
msgid "Sweep Paper Wallet"
msgstr "Importar billetera de papel"
#: src/js/services/onGoingProcess.js:33
msgid "Sweeping Wallet..."
@ -3792,6 +3797,10 @@ msgstr "Explora Bitcoin.com"
msgid "Bitcoin Cash Games"
msgstr "Juegos de Bitcoin Cash"
#: www/views/includes/community.html:29
msgid "Share the Wallet App"
msgstr "Comparte esta app"
#: src/js/services/bitcoincomService.js:28
msgid "News"
msgstr "Noticias"
@ -3860,3 +3869,75 @@ msgstr "Menos de 1 centavo"
msgid "This invoice is no longer accepting payments"
msgstr "Esta factura ya no está aceptando pagos"
#: www/views/amount.html.js:60
msgid "Send Maximum Amount"
msgstr "Enviar cantidad máxima"
#: src/js/controllers/amount.controller.js:239
msgid "Unknown error."
msgstr "Error desconocido."
#: www/views/paperWallet.html:48
msgid "No Bitcoin Cash wallet to transfer funds to found."
msgstr "No se encontró billetera BCH para transferir estos fondos."
#: www/views/paperWallet.html:54
msgid "No Bitcoin Cash found."
msgstr "No se encontró Bitcoin Cash."
#: www/views/paperWallet.html:60
msgid "Bitcoin Core found:"
msgstr "Bitcoin Core encontrado:"
#: www/views/paperWallet.html:98
msgid "No Bitcoin Core wallet to transfer funds to found."
msgstr "No se encontró billetera BTC para transferir estos fondos."
#: www/views/paperWallet.html:104
msgid "No Bitcoin Core found."
msgstr "No se encontró Bitcoin Core."
#: src/js/controllers/tab-scan.js:120
msgid "Scan Failed"
msgstr "Falló el escaneado"
#: src/js/controllers/tab-scan.js:121
msgid "Data not recognised."
msgstr "Datos no reconocidos."
#: src/js/controllers/tab-scan.js:121
msgid "Unsupported"
msgstr "No compatible"
#: src/js/controllers/tab-scan.js:121
msgid "Testnet is not supported."
msgstr "Testnet no es compatible."
#: www/views/includes/incomingDataMenu.html:81
msgid "URL"
msgstr "URL"
#: www/views/includes/incomingDataMenu.html:90
msgid "Open in web browser"
msgstr "Abrir en navegador web"
#: src/js/services/shapeshift.service.js.html:90
msgid "Invalid address"
msgstr "Dirección inválida"
#: src/js/services/shapeshift.service.js.html:90
msgid "Amount is not defined"
msgstr "Cantidad no definida"
#: src/js/services/shapeshift.service.js.html:90
msgid "Amount is below the minimun"
msgstr "Cantidad por debajo del mínimo"
#: src/js/services/shapeshift.service.js.html:90
msgid "Amount is above the limit"
msgstr "Cantidad por encima del límite"
#: src/js/services/shapeshift.service.js.html:90
msgid "Invalid response from Shapeshift"
msgstr "Respuesta inválida de Shapeshift"

View file

@ -11,7 +11,7 @@ msgstr ""
"Last-Translator: emilold\n"
"Language-Team: Persian\n"
"Language: fa\n"
"PO-Revision-Date: 2018-08-21 03:39\n"
"PO-Revision-Date: 2018-09-15 05:56\n"
#: www/views/modals/paypro.html:34
msgid "(Trusted)"
@ -520,7 +520,7 @@ msgid "Cannot Create Wallet"
msgstr "قادر به ایجاد کیف پول نیست"
#: src/js/services/profileService.js:442
msgid "Cannot join the same wallet more that once"
msgid "Cannot join the same wallet more than once"
msgstr "امکان بیش از یک بار پیوستن به یک کیف پول نیست"
#: www/views/includes/bitpayCardsCard.html:2
@ -2925,9 +2925,14 @@ msgid "Sweep"
msgstr "وارد کردن و تغییر آدرس به جدید(sweep)"
#: www/views/includes/incomingDataMenu.html:89
#: www/views/paperWallet.html:3
msgctxt "List item"
msgid "Sweep paper wallet"
msgstr "وارد کردن والت کاغذی و تغییر آدرس به جدید(sweep)"
msgstr "جاروب کردن کیف پول کاغذی"
#: www/views/paperWallet.html:3
msgctxt "Page title"
msgid "Sweep Paper Wallet"
msgstr "جاروب کردن کیف پول کاغذی"
#: src/js/services/onGoingProcess.js:33
msgid "Sweeping Wallet..."
@ -3792,6 +3797,10 @@ msgstr "کاوش Bitcoin.com"
msgid "Bitcoin Cash Games"
msgstr "Bitcoin Cash Games"
#: www/views/includes/community.html:29
msgid "Share the Wallet App"
msgstr "اشتراک گذاری نرم افزار کیف پول"
#: src/js/services/bitcoincomService.js:28
msgid "News"
msgstr "اخبار"
@ -3860,3 +3869,75 @@ msgstr "کمتر از 1 سنت"
msgid "This invoice is no longer accepting payments"
msgstr "این صورت حساب دیگر پرداخت ها را نمی پذیرد"
#: www/views/amount.html.js:60
msgid "Send Maximum Amount"
msgstr "ارسال حداکثر مقدار"
#: src/js/controllers/amount.controller.js:239
msgid "Unknown error."
msgstr "خطای ناشناخته."
#: www/views/paperWallet.html:48
msgid "No Bitcoin Cash wallet to transfer funds to found."
msgstr "هیچ کیف پولی از نوع Bitcoin Cash برای انتقال وجه یافت نشد."
#: www/views/paperWallet.html:54
msgid "No Bitcoin Cash found."
msgstr "Bitcoin Cash یافت نشد."
#: www/views/paperWallet.html:60
msgid "Bitcoin Core found:"
msgstr "Bitcoin Core یافت شده:"
#: www/views/paperWallet.html:98
msgid "No Bitcoin Core wallet to transfer funds to found."
msgstr "هیچ کیف پولی از نوع Bitcoin Core برای انتقال وجه یافت نشد."
#: www/views/paperWallet.html:104
msgid "No Bitcoin Core found."
msgstr "Bitcoin Core یافت نشد."
#: src/js/controllers/tab-scan.js:120
msgid "Scan Failed"
msgstr "اسکن انجام نشد"
#: src/js/controllers/tab-scan.js:121
msgid "Data not recognised."
msgstr "داده ها شناسایی نشد."
#: src/js/controllers/tab-scan.js:121
msgid "Unsupported"
msgstr "پشتیبانی نشده"
#: src/js/controllers/tab-scan.js:121
msgid "Testnet is not supported."
msgstr "Testnet پشتیبانی نمی شود."
#: www/views/includes/incomingDataMenu.html:81
msgid "URL"
msgstr "آدرس"
#: www/views/includes/incomingDataMenu.html:90
msgid "Open in web browser"
msgstr "باز کردن در مرورگر"
#: src/js/services/shapeshift.service.js.html:90
msgid "Invalid address"
msgstr "آدرس نامعتبر"
#: src/js/services/shapeshift.service.js.html:90
msgid "Amount is not defined"
msgstr "مبلغ تعریف نشده است"
#: src/js/services/shapeshift.service.js.html:90
msgid "Amount is below the minimun"
msgstr "مقدار زیر مقدار حداقل است"
#: src/js/services/shapeshift.service.js.html:90
msgid "Amount is above the limit"
msgstr "مقدار بالاتر از حد است"
#: src/js/services/shapeshift.service.js.html:90
msgid "Invalid response from Shapeshift"
msgstr "پاسخ نامعتبر از Shapeshift"

View file

@ -11,7 +11,7 @@ msgstr ""
"Last-Translator: emilold\n"
"Language-Team: French\n"
"Language: fr\n"
"PO-Revision-Date: 2018-08-21 03:39\n"
"PO-Revision-Date: 2018-09-15 05:56\n"
#: www/views/modals/paypro.html:34
msgid "(Trusted)"
@ -520,8 +520,8 @@ msgid "Cannot Create Wallet"
msgstr "Impossible de créer le portefeuille"
#: src/js/services/profileService.js:442
msgid "Cannot join the same wallet more that once"
msgstr "Impossible de rejoindre le même portefeuille plus d'une fois"
msgid "Cannot join the same wallet more than once"
msgstr "Impossible de joindre le même portefeuille plus d'une fois"
#: www/views/includes/bitpayCardsCard.html:2
msgid "Cards"
@ -2925,9 +2925,14 @@ msgid "Sweep"
msgstr "Balayer"
#: www/views/includes/incomingDataMenu.html:89
#: www/views/paperWallet.html:3
msgctxt "List item"
msgid "Sweep paper wallet"
msgstr "Balayer un portefeuille de papier"
msgstr "Balayer un portefeuille papier"
#: www/views/paperWallet.html:3
msgctxt "Page title"
msgid "Sweep Paper Wallet"
msgstr "Balayer un portefeuille papier"
#: src/js/services/onGoingProcess.js:33
msgid "Sweeping Wallet..."
@ -3792,6 +3797,10 @@ msgstr "Explorez Bitcoin.com"
msgid "Bitcoin Cash Games"
msgstr "Jeux Bitcoin Cash"
#: www/views/includes/community.html:29
msgid "Share the Wallet App"
msgstr "Partager lapplication Wallet"
#: src/js/services/bitcoincomService.js:28
msgid "News"
msgstr "Nouvelles"
@ -3860,3 +3869,75 @@ msgstr "Moins de 1 centime"
msgid "This invoice is no longer accepting payments"
msgstr "Cette facture naccepte plus les paiements"
#: www/views/amount.html.js:60
msgid "Send Maximum Amount"
msgstr "Envoi du montant maximal"
#: src/js/controllers/amount.controller.js:239
msgid "Unknown error."
msgstr "Erreur inconnue."
#: www/views/paperWallet.html:48
msgid "No Bitcoin Cash wallet to transfer funds to found."
msgstr "Aucun portefeuille Bitcoin Cash auquel transférer des fonds trouvé."
#: www/views/paperWallet.html:54
msgid "No Bitcoin Cash found."
msgstr "Pas de Bitcoin Cash trouvé."
#: www/views/paperWallet.html:60
msgid "Bitcoin Core found:"
msgstr "Bitcoin Core trouvé :"
#: www/views/paperWallet.html:98
msgid "No Bitcoin Core wallet to transfer funds to found."
msgstr "Aucun portefeuille Bitcoin Core auquel transférer des fonds trouvé."
#: www/views/paperWallet.html:104
msgid "No Bitcoin Core found."
msgstr "Pas de Bitcoin Core trouvé."
#: src/js/controllers/tab-scan.js:120
msgid "Scan Failed"
msgstr "Échec de l'analyse"
#: src/js/controllers/tab-scan.js:121
msgid "Data not recognised."
msgstr "Données non reconnues."
#: src/js/controllers/tab-scan.js:121
msgid "Unsupported"
msgstr "Non pris en charge"
#: src/js/controllers/tab-scan.js:121
msgid "Testnet is not supported."
msgstr "Testnet nest pas pris en charge."
#: www/views/includes/incomingDataMenu.html:81
msgid "URL"
msgstr "URL"
#: www/views/includes/incomingDataMenu.html:90
msgid "Open in web browser"
msgstr "Ouvrir dans le navigateur web"
#: src/js/services/shapeshift.service.js.html:90
msgid "Invalid address"
msgstr "Adresse invalide"
#: src/js/services/shapeshift.service.js.html:90
msgid "Amount is not defined"
msgstr "Le montant nest pas défini"
#: src/js/services/shapeshift.service.js.html:90
msgid "Amount is below the minimun"
msgstr "Le montant est inférieur au minimum"
#: src/js/services/shapeshift.service.js.html:90
msgid "Amount is above the limit"
msgstr "Le montant est supérieur à la limite"
#: src/js/services/shapeshift.service.js.html:90
msgid "Invalid response from Shapeshift"
msgstr "Réponse de Shapeshift invalide"

View file

@ -11,7 +11,7 @@ msgstr ""
"Last-Translator: emilold\n"
"Language-Team: Italian\n"
"Language: it\n"
"PO-Revision-Date: 2018-08-21 03:39\n"
"PO-Revision-Date: 2018-09-15 05:56\n"
#: www/views/modals/paypro.html:34
msgid "(Trusted)"
@ -520,7 +520,7 @@ msgid "Cannot Create Wallet"
msgstr "Impossibile creare portafoglio"
#: src/js/services/profileService.js:442
msgid "Cannot join the same wallet more that once"
msgid "Cannot join the same wallet more than once"
msgstr "Non è possibile aggiungere un portafoglio più di una volta"
#: www/views/includes/bitpayCardsCard.html:2
@ -2925,10 +2925,15 @@ msgid "Sweep"
msgstr "Spazzola"
#: www/views/includes/incomingDataMenu.html:89
#: www/views/paperWallet.html:3
msgctxt "List item"
msgid "Sweep paper wallet"
msgstr "Spazzare il portafoglio di carta"
#: www/views/paperWallet.html:3
msgctxt "Page title"
msgid "Sweep Paper Wallet"
msgstr "Spazzare il portafoglio di carta"
#: src/js/services/onGoingProcess.js:33
msgid "Sweeping Wallet..."
msgstr "Spazzolamento Portafoglio..."
@ -3792,6 +3797,10 @@ msgstr "Esplora Bitcoin.com"
msgid "Bitcoin Cash Games"
msgstr "Giochi Bitcoin Cash"
#: www/views/includes/community.html:29
msgid "Share the Wallet App"
msgstr "Condividere l'app Portafoglio"
#: src/js/services/bitcoincomService.js:28
msgid "News"
msgstr "News"
@ -3860,3 +3869,75 @@ msgstr "Meno di 1 centesimo"
msgid "This invoice is no longer accepting payments"
msgstr "Questa fattura non accetta più pagamenti"
#: www/views/amount.html.js:60
msgid "Send Maximum Amount"
msgstr "Inviare importo massimo"
#: src/js/controllers/amount.controller.js:239
msgid "Unknown error."
msgstr "Errore sconosciuto."
#: www/views/paperWallet.html:48
msgid "No Bitcoin Cash wallet to transfer funds to found."
msgstr "Nessun portafoglio Bitcoin Cash trovato per il trasferimento di fondi."
#: www/views/paperWallet.html:54
msgid "No Bitcoin Cash found."
msgstr "Nessun Bitcoin Cash trovato."
#: www/views/paperWallet.html:60
msgid "Bitcoin Core found:"
msgstr "Bitcoin Core trovato:"
#: www/views/paperWallet.html:98
msgid "No Bitcoin Core wallet to transfer funds to found."
msgstr "Nessun portafoglio Bitcoin Core trovato per il trasferimento di fondi."
#: www/views/paperWallet.html:104
msgid "No Bitcoin Core found."
msgstr "Nessun Bitcoin Core trovato."
#: src/js/controllers/tab-scan.js:120
msgid "Scan Failed"
msgstr "Scansione non riuscita"
#: src/js/controllers/tab-scan.js:121
msgid "Data not recognised."
msgstr "Dati non riconosciuti."
#: src/js/controllers/tab-scan.js:121
msgid "Unsupported"
msgstr "Non supportato"
#: src/js/controllers/tab-scan.js:121
msgid "Testnet is not supported."
msgstr "Testnet non supportato."
#: www/views/includes/incomingDataMenu.html:81
msgid "URL"
msgstr "URL"
#: www/views/includes/incomingDataMenu.html:90
msgid "Open in web browser"
msgstr "Aprire nel browser web"
#: src/js/services/shapeshift.service.js.html:90
msgid "Invalid address"
msgstr "Indirizzo non valido"
#: src/js/services/shapeshift.service.js.html:90
msgid "Amount is not defined"
msgstr "Importo non definito"
#: src/js/services/shapeshift.service.js.html:90
msgid "Amount is below the minimun"
msgstr "Importo inferiore al minimo"
#: src/js/services/shapeshift.service.js.html:90
msgid "Amount is above the limit"
msgstr "Importo sopra il limite"
#: src/js/services/shapeshift.service.js.html:90
msgid "Invalid response from Shapeshift"
msgstr "Risposta non valida da Shapeshift"

View file

@ -11,7 +11,7 @@ msgstr ""
"Last-Translator: emilold\n"
"Language-Team: Japanese\n"
"Language: ja\n"
"PO-Revision-Date: 2018-08-21 03:39\n"
"PO-Revision-Date: 2018-09-15 05:56\n"
#: www/views/modals/paypro.html:34
msgid "(Trusted)"
@ -522,8 +522,8 @@ msgid "Cannot Create Wallet"
msgstr "ウォレットを作成できません。"
#: src/js/services/profileService.js:442
msgid "Cannot join the same wallet more that once"
msgstr "同じ端末で同じウォレットに複数回参加することできません。"
msgid "Cannot join the same wallet more than once"
msgstr "同じ端末で同じウォレットに複数回参加することできません。"
#: www/views/includes/bitpayCardsCard.html:2
msgid "Cards"
@ -2929,10 +2929,15 @@ msgid "Sweep"
msgstr "全残高インポート"
#: www/views/includes/incomingDataMenu.html:89
#: www/views/paperWallet.html:3
msgctxt "List item"
msgid "Sweep paper wallet"
msgstr "ペーパーウォレットの全残高インポート"
#: www/views/paperWallet.html:3
msgctxt "Page title"
msgid "Sweep Paper Wallet"
msgstr "ペーパーウォレットの全残高インポート"
#: src/js/services/onGoingProcess.js:33
msgid "Sweeping Wallet..."
msgstr "ビットコイン回収中…"
@ -3798,6 +3803,10 @@ msgstr "Bitcoin.com を参照"
msgid "Bitcoin Cash Games"
msgstr "Bitcoin Cash ゲーム"
#: www/views/includes/community.html:29
msgid "Share the Wallet App"
msgstr "ウォレットアプリを共有"
#: src/js/services/bitcoincomService.js:28
msgid "News"
msgstr "ニュース"
@ -3866,3 +3875,75 @@ msgstr "1セント以下"
msgid "This invoice is no longer accepting payments"
msgstr "この請求書はもう支払を受け付けていません"
#: www/views/amount.html.js:60
msgid "Send Maximum Amount"
msgstr "全残高を送金"
#: src/js/controllers/amount.controller.js:239
msgid "Unknown error."
msgstr "不明なエラーです。"
#: www/views/paperWallet.html:48
msgid "No Bitcoin Cash wallet to transfer funds to found."
msgstr "資金を送金できるビットコインキャッシュウォレットが見つかりません。"
#: www/views/paperWallet.html:54
msgid "No Bitcoin Cash found."
msgstr "ビットコインキャッシュが見つかりません。"
#: www/views/paperWallet.html:60
msgid "Bitcoin Core found:"
msgstr "ビットコインが見つかりました:"
#: www/views/paperWallet.html:98
msgid "No Bitcoin Core wallet to transfer funds to found."
msgstr "資金を送金できるビットコインウォレットが見つかりません。"
#: www/views/paperWallet.html:104
msgid "No Bitcoin Core found."
msgstr "ビットコインが見つかりません。"
#: src/js/controllers/tab-scan.js:120
msgid "Scan Failed"
msgstr "スキャンできませんでした"
#: src/js/controllers/tab-scan.js:121
msgid "Data not recognised."
msgstr "データが認識されていません。"
#: src/js/controllers/tab-scan.js:121
msgid "Unsupported"
msgstr "未対応"
#: src/js/controllers/tab-scan.js:121
msgid "Testnet is not supported."
msgstr "Testnet には対応していません。"
#: www/views/includes/incomingDataMenu.html:81
msgid "URL"
msgstr "URL"
#: www/views/includes/incomingDataMenu.html:90
msgid "Open in web browser"
msgstr "ウェブブラウザで開く"
#: src/js/services/shapeshift.service.js.html:90
msgid "Invalid address"
msgstr "無効なアドレス"
#: src/js/services/shapeshift.service.js.html:90
msgid "Amount is not defined"
msgstr "金額が定義されていません"
#: src/js/services/shapeshift.service.js.html:90
msgid "Amount is below the minimun"
msgstr "金額が最少額を下回っています"
#: src/js/services/shapeshift.service.js.html:90
msgid "Amount is above the limit"
msgstr "金額が上限を超えています"
#: src/js/services/shapeshift.service.js.html:90
msgid "Invalid response from Shapeshift"
msgstr "Shapeshift から無効な応答がありました"

View file

@ -11,7 +11,7 @@ msgstr ""
"Last-Translator: emilold\n"
"Language-Team: Korean\n"
"Language: ko\n"
"PO-Revision-Date: 2018-08-21 03:39\n"
"PO-Revision-Date: 2018-09-15 05:56\n"
#: www/views/modals/paypro.html:34
msgid "(Trusted)"
@ -233,12 +233,12 @@ msgstr "가격 표시"
#: src/js/controllers/tab-settings.js:19
#: www/views/preferencesPriceDisplay.html:12
msgid "Fiat"
msgstr "고정비"
msgstr "법정 화폐"
#: src/js/controllers/tab-settings.js:19
#: www/views/preferencesPriceDisplay.html:15
msgid "Cryptocurrency"
msgstr "가상화폐"
msgstr "가상 화폐"
#: src/js/controllers/buyAmazon.js:98
msgid "Amazon.com is not available at this moment. Please try back later."
@ -520,7 +520,7 @@ msgid "Cannot Create Wallet"
msgstr "지갑을 생성할 수 없습니다"
#: src/js/services/profileService.js:442
msgid "Cannot join the same wallet more that once"
msgid "Cannot join the same wallet more than once"
msgstr "같은 지갑에 한 번 이상 접속할 수 없습니다"
#: www/views/includes/bitpayCardsCard.html:2
@ -2658,7 +2658,7 @@ msgstr "주소 붙여넣기"
#: www/views/tab-send.html:27
msgid "Transfer between wallets"
msgstr "월릿 간 전송"
msgstr "지갑 간 전송"
#: www/views/tab-send.html:35
msgid "Scan QR Code"
@ -2925,10 +2925,15 @@ msgid "Sweep"
msgstr "스윕하기"
#: www/views/includes/incomingDataMenu.html:89
#: www/views/paperWallet.html:3
msgctxt "List item"
msgid "Sweep paper wallet"
msgstr "종이 지갑 스윕하기"
#: www/views/paperWallet.html:3
msgctxt "Page title"
msgid "Sweep Paper Wallet"
msgstr "종이 지갑 스윕하기"
#: src/js/services/onGoingProcess.js:33
msgid "Sweeping Wallet..."
msgstr "지갑 스윕 중..."
@ -3792,6 +3797,10 @@ msgstr "Bitcoin.com 탐색"
msgid "Bitcoin Cash Games"
msgstr "BCH 게임"
#: www/views/includes/community.html:29
msgid "Share the Wallet App"
msgstr "지갑 응용 프로그램 공유"
#: src/js/services/bitcoincomService.js:28
msgid "News"
msgstr "소식"
@ -3860,3 +3869,75 @@ msgstr "1센트 미만"
msgid "This invoice is no longer accepting payments"
msgstr "이 청구서는 더 이상 결제를 수락하지 않습니다"
#: www/views/amount.html.js:60
msgid "Send Maximum Amount"
msgstr "최대 수량 보내는 중"
#: src/js/controllers/amount.controller.js:239
msgid "Unknown error."
msgstr "알 수 없는 오류가 발생 했습니다."
#: www/views/paperWallet.html:48
msgid "No Bitcoin Cash wallet to transfer funds to found."
msgstr "자금 찾을 없습니다 Bitcoin Cash 현금 지갑."
#: www/views/paperWallet.html:54
msgid "No Bitcoin Cash found."
msgstr "찾을 수 없습니다 Bitcoin Cash 현금."
#: www/views/paperWallet.html:60
msgid "Bitcoin Core found:"
msgstr "Bitcoin Core 코어 발견:"
#: www/views/paperWallet.html:98
msgid "No Bitcoin Core wallet to transfer funds to found."
msgstr "자금 찾을 없습니다 Bitcoin Core 현금 지갑."
#: www/views/paperWallet.html:104
msgid "No Bitcoin Core found."
msgstr "찾을 수 없습니다 Bitcoin Core 현금."
#: src/js/controllers/tab-scan.js:120
msgid "Scan Failed"
msgstr "검색 실패"
#: src/js/controllers/tab-scan.js:121
msgid "Data not recognised."
msgstr "데이터 인식 되지입니다."
#: src/js/controllers/tab-scan.js:121
msgid "Unsupported"
msgstr "지원 되지 않는"
#: src/js/controllers/tab-scan.js:121
msgid "Testnet is not supported."
msgstr "Testnet는 지원 되지 않습니다."
#: www/views/includes/incomingDataMenu.html:81
msgid "URL"
msgstr "URL"
#: www/views/includes/incomingDataMenu.html:90
msgid "Open in web browser"
msgstr "웹 브라우저에서 열기"
#: src/js/services/shapeshift.service.js.html:90
msgid "Invalid address"
msgstr "주소 오류"
#: src/js/services/shapeshift.service.js.html:90
msgid "Amount is not defined"
msgstr "금액은 정의 되지 않은"
#: src/js/services/shapeshift.service.js.html:90
msgid "Amount is below the minimun"
msgstr "Minimun 아래 금액은"
#: src/js/services/shapeshift.service.js.html:90
msgid "Amount is above the limit"
msgstr "제한 위에 금액은"
#: src/js/services/shapeshift.service.js.html:90
msgid "Invalid response from Shapeshift"
msgstr "변신에서 잘못 된 응답"

View file

@ -11,7 +11,7 @@ msgstr ""
"Last-Translator: emilold\n"
"Language-Team: Dutch\n"
"Language: nl\n"
"PO-Revision-Date: 2018-08-21 03:39\n"
"PO-Revision-Date: 2018-09-15 05:56\n"
#: www/views/modals/paypro.html:34
msgid "(Trusted)"
@ -520,8 +520,8 @@ msgid "Cannot Create Wallet"
msgstr "Kan Portemonnee Niet Aanmaken"
#: src/js/services/profileService.js:442
msgid "Cannot join the same wallet more that once"
msgstr "Kan niet meerdere keren tegelijk deelnemen aan een portemonnee"
msgid "Cannot join the same wallet more than once"
msgstr "Je kan niet meerdere keren tegelijk aan een portemonnee deelnemen"
#: www/views/includes/bitpayCardsCard.html:2
msgid "Cards"
@ -2925,9 +2925,14 @@ msgid "Sweep"
msgstr "Saldo opnemen"
#: www/views/includes/incomingDataMenu.html:89
#: www/views/paperWallet.html:3
msgctxt "List item"
msgid "Sweep paper wallet"
msgstr "Saldo papieren portemonnee opnemen"
msgstr "Paper wallet leeghalen"
#: www/views/paperWallet.html:3
msgctxt "Page title"
msgid "Sweep Paper Wallet"
msgstr "Paper wallet leeghalen"
#: src/js/services/onGoingProcess.js:33
msgid "Sweeping Wallet..."
@ -3792,6 +3797,10 @@ msgstr "Bitcoin.com verkennen"
msgid "Bitcoin Cash Games"
msgstr "Bitcoin Cash spellen"
#: www/views/includes/community.html:29
msgid "Share the Wallet App"
msgstr "Wallet app delen"
#: src/js/services/bitcoincomService.js:28
msgid "News"
msgstr "Nieuws"
@ -3860,3 +3869,75 @@ msgstr "Minder dan 1 cent"
msgid "This invoice is no longer accepting payments"
msgstr "Deze factuur accepteert geen betalingen meer"
#: www/views/amount.html.js:60
msgid "Send Maximum Amount"
msgstr "Alles versturen"
#: src/js/controllers/amount.controller.js:239
msgid "Unknown error."
msgstr "Onbekende fout."
#: www/views/paperWallet.html:48
msgid "No Bitcoin Cash wallet to transfer funds to found."
msgstr "Geen Bitcoin Cash portemonnee gevonden om bedrag naartoe over te maken."
#: www/views/paperWallet.html:54
msgid "No Bitcoin Cash found."
msgstr "Geen Bitcoin Cash gevonden."
#: www/views/paperWallet.html:60
msgid "Bitcoin Core found:"
msgstr "Bitcoin Core gevonden:"
#: www/views/paperWallet.html:98
msgid "No Bitcoin Core wallet to transfer funds to found."
msgstr "Geen Bitcoin Core portemonnee gevonden om waarde naartoe te versturen."
#: www/views/paperWallet.html:104
msgid "No Bitcoin Core found."
msgstr "Geen Bitcoin Core gevonden."
#: src/js/controllers/tab-scan.js:120
msgid "Scan Failed"
msgstr "Scannen mislukt"
#: src/js/controllers/tab-scan.js:121
msgid "Data not recognised."
msgstr "Gegevens niet herkend."
#: src/js/controllers/tab-scan.js:121
msgid "Unsupported"
msgstr "Niet ondersteund"
#: src/js/controllers/tab-scan.js:121
msgid "Testnet is not supported."
msgstr "Testnet wordt niet ondersteund."
#: www/views/includes/incomingDataMenu.html:81
msgid "URL"
msgstr "URL"
#: www/views/includes/incomingDataMenu.html:90
msgid "Open in web browser"
msgstr "Open in webbrowser"
#: src/js/services/shapeshift.service.js.html:90
msgid "Invalid address"
msgstr "Ongeldig adres"
#: src/js/services/shapeshift.service.js.html:90
msgid "Amount is not defined"
msgstr "Bedrag is niet gespecificeerd"
#: src/js/services/shapeshift.service.js.html:90
msgid "Amount is below the minimun"
msgstr "Bedrag lager is dan het minimum"
#: src/js/services/shapeshift.service.js.html:90
msgid "Amount is above the limit"
msgstr "Bedrag is boven het limiet"
#: src/js/services/shapeshift.service.js.html:90
msgid "Invalid response from Shapeshift"
msgstr "Ongeldig respons van Shapeshift"

View file

@ -11,7 +11,7 @@ msgstr ""
"Last-Translator: emilold\n"
"Language-Team: Polish\n"
"Language: pl\n"
"PO-Revision-Date: 2018-08-21 03:40\n"
"PO-Revision-Date: 2018-09-15 05:56\n"
#: www/views/modals/paypro.html:34
msgid "(Trusted)"
@ -520,7 +520,7 @@ msgid "Cannot Create Wallet"
msgstr "Nie można utworzyć portfela"
#: src/js/services/profileService.js:442
msgid "Cannot join the same wallet more that once"
msgid "Cannot join the same wallet more than once"
msgstr "Nie można dołączyć tego samego portfela więcej niż raz"
#: www/views/includes/bitpayCardsCard.html:2
@ -2925,10 +2925,15 @@ msgid "Sweep"
msgstr "Opróżnij"
#: www/views/includes/incomingDataMenu.html:89
#: www/views/paperWallet.html:3
msgctxt "List item"
msgid "Sweep paper wallet"
msgstr "Wyczyść papierowy portfel"
#: www/views/paperWallet.html:3
msgctxt "Page title"
msgid "Sweep Paper Wallet"
msgstr "Wyczyść papierowy portfel"
#: src/js/services/onGoingProcess.js:33
msgid "Sweeping Wallet..."
msgstr "Sczytywanie portfela..."
@ -3792,6 +3797,10 @@ msgstr "Poznaj Bitcoin.com"
msgid "Bitcoin Cash Games"
msgstr "Gry Bitcoin Cash"
#: www/views/includes/community.html:29
msgid "Share the Wallet App"
msgstr "Udostępnij aplikację Portfel"
#: src/js/services/bitcoincomService.js:28
msgid "News"
msgstr "Aktualności"
@ -3860,3 +3869,75 @@ msgstr "Mniej niż 1 cent"
msgid "This invoice is no longer accepting payments"
msgstr "Nie można już opłacić tej faktury"
#: www/views/amount.html.js:60
msgid "Send Maximum Amount"
msgstr "Wyślij maksymalną kwotę"
#: src/js/controllers/amount.controller.js:239
msgid "Unknown error."
msgstr "Nieznany błąd."
#: www/views/paperWallet.html:48
msgid "No Bitcoin Cash wallet to transfer funds to found."
msgstr "Nie znaleziono portfela Bitcoin Cash do przekazania środków."
#: www/views/paperWallet.html:54
msgid "No Bitcoin Cash found."
msgstr "Nie znaleziono Bitcoin Cash."
#: www/views/paperWallet.html:60
msgid "Bitcoin Core found:"
msgstr "Znaleziono Bitcoin Core:"
#: www/views/paperWallet.html:98
msgid "No Bitcoin Core wallet to transfer funds to found."
msgstr "Nie znaleziono portfela Bitcoin Core do przekazania środków."
#: www/views/paperWallet.html:104
msgid "No Bitcoin Core found."
msgstr "Nie znaleziono Bitcoin Core."
#: src/js/controllers/tab-scan.js:120
msgid "Scan Failed"
msgstr "Skanowanie nie powiodło się"
#: src/js/controllers/tab-scan.js:121
msgid "Data not recognised."
msgstr "Nie rozpoznano danych."
#: src/js/controllers/tab-scan.js:121
msgid "Unsupported"
msgstr "Nieobsługiwane"
#: src/js/controllers/tab-scan.js:121
msgid "Testnet is not supported."
msgstr "Sieć testowa nie jest obsługiwana."
#: www/views/includes/incomingDataMenu.html:81
msgid "URL"
msgstr "Adres URL"
#: www/views/includes/incomingDataMenu.html:90
msgid "Open in web browser"
msgstr "Otwórz w przeglądarce internetowej"
#: src/js/services/shapeshift.service.js.html:90
msgid "Invalid address"
msgstr "Nieprawidłowy adres"
#: src/js/services/shapeshift.service.js.html:90
msgid "Amount is not defined"
msgstr "Kwota nie jest określona"
#: src/js/services/shapeshift.service.js.html:90
msgid "Amount is below the minimun"
msgstr "Kwota niższa od minimalnej"
#: src/js/services/shapeshift.service.js.html:90
msgid "Amount is above the limit"
msgstr "Kwota przekracza limit"
#: src/js/services/shapeshift.service.js.html:90
msgid "Invalid response from Shapeshift"
msgstr "Nieprawidłowa odpowiedź z Shapeshift"

View file

@ -11,7 +11,7 @@ msgstr ""
"Last-Translator: emilold\n"
"Language-Team: Portuguese, Brazilian\n"
"Language: pt\n"
"PO-Revision-Date: 2018-08-21 03:40\n"
"PO-Revision-Date: 2018-09-15 05:56\n"
#: www/views/modals/paypro.html:34
msgid "(Trusted)"
@ -520,8 +520,8 @@ msgid "Cannot Create Wallet"
msgstr "Não é possível criar a carteira"
#: src/js/services/profileService.js:442
msgid "Cannot join the same wallet more that once"
msgstr "Não pode juntar-se a mesma carteira mais que uma vez"
msgid "Cannot join the same wallet more than once"
msgstr "Não pode juntar-se à mesma carteira mais que uma vez"
#: www/views/includes/bitpayCardsCard.html:2
msgid "Cards"
@ -1623,7 +1623,7 @@ msgstr "Endereço de rede incorreto"
#: src/js/controllers/confirm.js:306
#: src/js/services/bwcError.js:44
msgid "Insufficient confirmed funds"
msgstr "Insuficiência de fundos confirmados"
msgstr "Insuficiência de fundos confirmada"
#: src/js/controllers/topup.js:165
#: src/js/controllers/topup.js:177
@ -2090,7 +2090,7 @@ msgstr "Abrir Projeto no GitHub"
#: src/js/controllers/bitpayCard.js:123
#: src/js/controllers/tx-details.js:192
msgid "Open Explorer"
msgstr "Abra o Explorer"
msgstr "Abrir o Explorer"
#: www/views/tab-scan.html:22
msgid "Open Settings"
@ -2222,7 +2222,7 @@ msgstr "Detalhes do pagamento"
#: www/views/modals/paypro.html:6
msgid "Payment Request"
msgstr "Pedido de pagamento"
msgstr "Solicitação de pagamento"
#: www/views/mercadoLibreCards.html:22
#: www/views/modals/mercadolibre-card-details.html:39
@ -2696,7 +2696,7 @@ msgstr "Para começar, você precisa criar uma carteira de bitcoins e obter algu
#: www/views/tab-send.html:74
msgid "Buy Bitcoin now"
msgstr "Compre Bitcoin agora"
msgstr "Comprar Bitcoin agora"
#: www/views/tab-send.html:76
msgid "Show my address"
@ -2925,10 +2925,15 @@ msgid "Sweep"
msgstr "Limpar"
#: www/views/includes/incomingDataMenu.html:89
#: www/views/paperWallet.html:3
msgctxt "List item"
msgid "Sweep paper wallet"
msgstr "Varrer a carteira de papel"
#: www/views/paperWallet.html:3
msgctxt "Page title"
msgid "Sweep Paper Wallet"
msgstr "Varrer a carteira de papel"
#: src/js/services/onGoingProcess.js:33
msgid "Sweeping Wallet..."
msgstr "Carteira de varredura..."
@ -3792,6 +3797,10 @@ msgstr "Explore Bitcoin.com"
msgid "Bitcoin Cash Games"
msgstr "Jogos Bitcoin Cash"
#: www/views/includes/community.html:29
msgid "Share the Wallet App"
msgstr "Compartilhar o app de carteira"
#: src/js/services/bitcoincomService.js:28
msgid "News"
msgstr "Notícias"
@ -3860,3 +3869,75 @@ msgstr "Menos de 1 centavo"
msgid "This invoice is no longer accepting payments"
msgstr "Essa fatura não aceita mais pagamentos"
#: www/views/amount.html.js:60
msgid "Send Maximum Amount"
msgstr "Enviar montante máximo"
#: src/js/controllers/amount.controller.js:239
msgid "Unknown error."
msgstr "Erro desconhecido."
#: www/views/paperWallet.html:48
msgid "No Bitcoin Cash wallet to transfer funds to found."
msgstr "Nenhuma carteira de Bitcoin Cash para transferir fundos encontrada."
#: www/views/paperWallet.html:54
msgid "No Bitcoin Cash found."
msgstr "Bitcoin Cash não encontrado."
#: www/views/paperWallet.html:60
msgid "Bitcoin Core found:"
msgstr "Bitcoin Core encontrado:"
#: www/views/paperWallet.html:98
msgid "No Bitcoin Core wallet to transfer funds to found."
msgstr "Nenhuma carteira de Bitcoin Core para transferir fundos encontrada."
#: www/views/paperWallet.html:104
msgid "No Bitcoin Core found."
msgstr "Nenhum Bitcoin Core encontrado."
#: src/js/controllers/tab-scan.js:120
msgid "Scan Failed"
msgstr "Falha de verificação"
#: src/js/controllers/tab-scan.js:121
msgid "Data not recognised."
msgstr "Dados não reconhecidos."
#: src/js/controllers/tab-scan.js:121
msgid "Unsupported"
msgstr "Não suportado"
#: src/js/controllers/tab-scan.js:121
msgid "Testnet is not supported."
msgstr "Testnet não suportado."
#: www/views/includes/incomingDataMenu.html:81
msgid "URL"
msgstr "URL"
#: www/views/includes/incomingDataMenu.html:90
msgid "Open in web browser"
msgstr "Abrir no navegador da Web"
#: src/js/services/shapeshift.service.js.html:90
msgid "Invalid address"
msgstr "Endereço inválido"
#: src/js/services/shapeshift.service.js.html:90
msgid "Amount is not defined"
msgstr "Montante não definido"
#: src/js/services/shapeshift.service.js.html:90
msgid "Amount is below the minimun"
msgstr "Montante abaixo do mínimo"
#: src/js/services/shapeshift.service.js.html:90
msgid "Amount is above the limit"
msgstr "Montante acima do limite"
#: src/js/services/shapeshift.service.js.html:90
msgid "Invalid response from Shapeshift"
msgstr "Resposta inválida do Shapeshift"

View file

@ -11,7 +11,7 @@ msgstr ""
"Last-Translator: emilold\n"
"Language-Team: Russian\n"
"Language: ru\n"
"PO-Revision-Date: 2018-08-21 03:40\n"
"PO-Revision-Date: 2018-09-15 05:56\n"
#: www/views/modals/paypro.html:34
msgid "(Trusted)"
@ -520,7 +520,7 @@ msgid "Cannot Create Wallet"
msgstr "Не удаётся создать кошелёк"
#: src/js/services/profileService.js:442
msgid "Cannot join the same wallet more that once"
msgid "Cannot join the same wallet more than once"
msgstr "Нельзя присоединиться к одному и тому же кошельку более одного раза"
#: www/views/includes/bitpayCardsCard.html:2
@ -2925,10 +2925,15 @@ msgid "Sweep"
msgstr "Считать"
#: www/views/includes/incomingDataMenu.html:89
#: www/views/paperWallet.html:3
msgctxt "List item"
msgid "Sweep paper wallet"
msgstr "Пополнить с бумажного кошелька"
#: www/views/paperWallet.html:3
msgctxt "Page title"
msgid "Sweep Paper Wallet"
msgstr "Пополнить с бумажного кошелька"
#: src/js/services/onGoingProcess.js:33
msgid "Sweeping Wallet..."
msgstr "Считывание кошелька..."
@ -3792,6 +3797,10 @@ msgstr "Обзор Bitcoin.com"
msgid "Bitcoin Cash Games"
msgstr "Игры Bitcoin Cash"
#: www/views/includes/community.html:29
msgid "Share the Wallet App"
msgstr "Поделиться программой «Кошелёк»"
#: src/js/services/bitcoincomService.js:28
msgid "News"
msgstr "Новости"
@ -3860,3 +3869,75 @@ msgstr "менее 1 копейки"
msgid "This invoice is no longer accepting payments"
msgstr "По этому инвойсу платежи больше не принимаются"
#: www/views/amount.html.js:60
msgid "Send Maximum Amount"
msgstr "Отправить максимальную сумму"
#: src/js/controllers/amount.controller.js:239
msgid "Unknown error."
msgstr "Неизвестная ошибка."
#: www/views/paperWallet.html:48
msgid "No Bitcoin Cash wallet to transfer funds to found."
msgstr "Не найден кошелек Bitcoin Cash для перевода средств."
#: www/views/paperWallet.html:54
msgid "No Bitcoin Cash found."
msgstr "Не найден Bitcoin Cash."
#: www/views/paperWallet.html:60
msgid "Bitcoin Core found:"
msgstr "Найден Bitcoin Core:"
#: www/views/paperWallet.html:98
msgid "No Bitcoin Core wallet to transfer funds to found."
msgstr "Не найден кошелек Bitcoin Core для перевода средств."
#: www/views/paperWallet.html:104
msgid "No Bitcoin Core found."
msgstr "Не найден Bitcoin Core."
#: src/js/controllers/tab-scan.js:120
msgid "Scan Failed"
msgstr "Ошибка сканирования"
#: src/js/controllers/tab-scan.js:121
msgid "Data not recognised."
msgstr "Данные не распознаны."
#: src/js/controllers/tab-scan.js:121
msgid "Unsupported"
msgstr "Не поддерживается"
#: src/js/controllers/tab-scan.js:121
msgid "Testnet is not supported."
msgstr "Testnet не поддерживается."
#: www/views/includes/incomingDataMenu.html:81
msgid "URL"
msgstr "URL-адрес"
#: www/views/includes/incomingDataMenu.html:90
msgid "Open in web browser"
msgstr "Открыть в веб-браузере"
#: src/js/services/shapeshift.service.js.html:90
msgid "Invalid address"
msgstr "Неверный адрес"
#: src/js/services/shapeshift.service.js.html:90
msgid "Amount is not defined"
msgstr "Сумма не задана"
#: src/js/services/shapeshift.service.js.html:90
msgid "Amount is below the minimun"
msgstr "Сумма ниже минимума"
#: src/js/services/shapeshift.service.js.html:90
msgid "Amount is above the limit"
msgstr "Сумма больше ограничения"
#: src/js/services/shapeshift.service.js.html:90
msgid "Invalid response from Shapeshift"
msgstr "Неправильный ответ от Shapeshift"

View file

@ -11,7 +11,7 @@ msgstr ""
"Last-Translator: emilold\n"
"Language-Team: Swedish\n"
"Language: sv\n"
"PO-Revision-Date: 2018-08-21 03:40\n"
"PO-Revision-Date: 2018-09-06 16:45\n"
#: www/views/modals/paypro.html:34
msgid "(Trusted)"
@ -520,7 +520,7 @@ msgid "Cannot Create Wallet"
msgstr "Kan inte skapa plånbok"
#: src/js/services/profileService.js:442
msgid "Cannot join the same wallet more that once"
msgid "Cannot join the same wallet more than once"
msgstr ""
#: www/views/includes/bitpayCardsCard.html:2
@ -2925,10 +2925,15 @@ msgid "Sweep"
msgstr ""
#: www/views/includes/incomingDataMenu.html:89
#: www/views/paperWallet.html:3
msgctxt "List item"
msgid "Sweep paper wallet"
msgstr ""
#: www/views/paperWallet.html:3
msgctxt "Page title"
msgid "Sweep Paper Wallet"
msgstr ""
#: src/js/services/onGoingProcess.js:33
msgid "Sweeping Wallet..."
msgstr ""
@ -3792,6 +3797,10 @@ msgstr ""
msgid "Bitcoin Cash Games"
msgstr ""
#: www/views/includes/community.html:29
msgid "Share the Wallet App"
msgstr ""
#: src/js/services/bitcoincomService.js:28
msgid "News"
msgstr ""
@ -3860,3 +3869,75 @@ msgstr ""
msgid "This invoice is no longer accepting payments"
msgstr ""
#: www/views/amount.html.js:60
msgid "Send Maximum Amount"
msgstr ""
#: src/js/controllers/amount.controller.js:239
msgid "Unknown error."
msgstr ""
#: www/views/paperWallet.html:48
msgid "No Bitcoin Cash wallet to transfer funds to found."
msgstr ""
#: www/views/paperWallet.html:54
msgid "No Bitcoin Cash found."
msgstr ""
#: www/views/paperWallet.html:60
msgid "Bitcoin Core found:"
msgstr ""
#: www/views/paperWallet.html:98
msgid "No Bitcoin Core wallet to transfer funds to found."
msgstr ""
#: www/views/paperWallet.html:104
msgid "No Bitcoin Core found."
msgstr ""
#: src/js/controllers/tab-scan.js:120
msgid "Scan Failed"
msgstr ""
#: src/js/controllers/tab-scan.js:121
msgid "Data not recognised."
msgstr ""
#: src/js/controllers/tab-scan.js:121
msgid "Unsupported"
msgstr ""
#: src/js/controllers/tab-scan.js:121
msgid "Testnet is not supported."
msgstr ""
#: www/views/includes/incomingDataMenu.html:81
msgid "URL"
msgstr ""
#: www/views/includes/incomingDataMenu.html:90
msgid "Open in web browser"
msgstr ""
#: src/js/services/shapeshift.service.js.html:90
msgid "Invalid address"
msgstr ""
#: src/js/services/shapeshift.service.js.html:90
msgid "Amount is not defined"
msgstr ""
#: src/js/services/shapeshift.service.js.html:90
msgid "Amount is below the minimun"
msgstr ""
#: src/js/services/shapeshift.service.js.html:90
msgid "Amount is above the limit"
msgstr ""
#: src/js/services/shapeshift.service.js.html:90
msgid "Invalid response from Shapeshift"
msgstr ""

View file

@ -511,7 +511,7 @@ msgid "Cannot Create Wallet"
msgstr ""
#: src/js/services/profileService.js:442
msgid "Cannot join the same wallet more that once"
msgid "Cannot join the same wallet more than once"
msgstr ""
#: www/views/includes/bitpayCardsCard.html:2
@ -2916,10 +2916,15 @@ msgid "Sweep"
msgstr ""
#: www/views/includes/incomingDataMenu.html:89
#: www/views/paperWallet.html:3
msgctxt "List item"
msgid "Sweep paper wallet"
msgstr ""
#: www/views/paperWallet.html:3
msgctxt "Page title"
msgid "Sweep Paper Wallet"
msgstr ""
#: src/js/services/onGoingProcess.js:33
msgid "Sweeping Wallet..."
msgstr ""
@ -3783,6 +3788,10 @@ msgstr ""
msgid "Bitcoin Cash Games"
msgstr ""
#: www/views/includes/community.html:29
msgid "Share the Wallet App"
msgstr ""
#: src/js/services/bitcoincomService.js:28
msgid "News"
msgstr ""
@ -3849,4 +3858,76 @@ msgstr ""
#: src/js/services/incomingData.js:129
msgid "This invoice is no longer accepting payments"
msgstr ""
#: www/views/amount.html.js:60
msgid "Send Maximum Amount"
msgstr ""
#: src/js/controllers/amount.controller.js:239
msgid "Unknown error."
msgstr ""
#: www/views/paperWallet.html:48
msgid "No Bitcoin Cash wallet to transfer funds to found."
msgstr ""
#: www/views/paperWallet.html:54
msgid "No Bitcoin Cash found."
msgstr ""
#: www/views/paperWallet.html:60
msgid "Bitcoin Core found:"
msgstr ""
#: www/views/paperWallet.html:98
msgid "No Bitcoin Core wallet to transfer funds to found."
msgstr ""
#: www/views/paperWallet.html:104
msgid "No Bitcoin Core found."
msgstr ""
#: src/js/controllers/tab-scan.js:120
msgid "Scan Failed"
msgstr ""
#: src/js/controllers/tab-scan.js:121
msgid "Data not recognised."
msgstr ""
#: src/js/controllers/tab-scan.js:121
msgid "Unsupported"
msgstr ""
#: src/js/controllers/tab-scan.js:121
msgid "Testnet is not supported."
msgstr ""
#: www/views/includes/incomingDataMenu.html:81
msgid "URL"
msgstr ""
#: www/views/includes/incomingDataMenu.html:90
msgid "Open in web browser"
msgstr ""
#: src/js/services/shapeshift.service.js.html:90
msgid "Invalid address"
msgstr ""
#: src/js/services/shapeshift.service.js.html:90
msgid "Amount is not defined"
msgstr ""
#: src/js/services/shapeshift.service.js.html:90
msgid "Amount is below the minimun"
msgstr ""
#: src/js/services/shapeshift.service.js.html:90
msgid "Amount is above the limit"
msgstr ""
#: src/js/services/shapeshift.service.js.html:90
msgid "Invalid response from Shapeshift"
msgstr ""

View file

@ -11,7 +11,7 @@ msgstr ""
"Last-Translator: emilold\n"
"Language-Team: Vietnamese\n"
"Language: vi\n"
"PO-Revision-Date: 2018-08-21 03:40\n"
"PO-Revision-Date: 2018-09-15 05:56\n"
#: www/views/modals/paypro.html:34
msgid "(Trusted)"
@ -36,7 +36,7 @@ msgstr "{{tx.feeRateStr}} của giao dịch"
#: www/views/feedback/rateApp.html:7
msgid "5-star ratings help us get {{appName}} into more hands, and more users means more resources can be committed to the app!"
msgstr "5-star ratings help us get {{appName}} into more hands, and more users means more resources can be committed to the app!"
msgstr "{{appName}} 5-star ratings help us get {{appName}} into more hands, and more users means more resources can be committed to the app!"
#: www/views/mercadoLibre.html:18
#: www/views/mercadoLibre.html:40
@ -56,8 +56,8 @@ msgstr "A total of {{amountAboveMaxSizeStr}} were excluded. The maximum size all
#: src/js/controllers/confirm.js:395
msgid "A total of {{amountBelowFeeStr}} were excluded. These funds come from UTXOs smaller than the network fee provided."
msgstr "Tổng cộng {{amountBelowFeeStr}} đã bị loại trừ. Số tiền này đến từ UTXOs nhỏ hơn chi phí mạng cung cấp.#\n"
"1"
msgstr "{{amountBelowFeeStr}} Tổng cộng {{amountBelowFeeStr}} đã bị loại trừ. Số tiền này đến từ UTXOs nhỏ hơn chi phí mạng cung cấp.#\n"
"1."
#: src/js/controllers/preferencesAbout.js:6
#: www/views/tab-settings.html:156
@ -67,7 +67,7 @@ msgstr "About"
#: src/js/controllers/modals/txpDetails.js:62
#: src/js/controllers/tx-details.js:79
msgid "Accepted"
msgstr "Accepted"
msgstr "Chấp nhận"
#: www/views/preferencesInformation.html:72
msgid "Account"
@ -78,7 +78,7 @@ msgstr "Account"
#: www/views/tab-create-shared.html:74
#: www/views/tab-import-hardware.html:19
msgid "Account Number"
msgstr "Account Number"
msgstr "Account Number0941162662"
#: www/views/tab-home.html:61
msgid "Instant transactions with low fees"
@ -241,7 +241,7 @@ msgstr "Fiat"
#: src/js/controllers/tab-settings.js:19
#: www/views/preferencesPriceDisplay.html:15
msgid "Cryptocurrency"
msgstr "Cryptocurrency"
msgstr "Cryptocurrencyg"
#: src/js/controllers/buyAmazon.js:98
msgid "Amazon.com is not available at this moment. Please try back later."
@ -523,8 +523,8 @@ msgid "Cannot Create Wallet"
msgstr "Cannot Create Wallet"
#: src/js/services/profileService.js:442
msgid "Cannot join the same wallet more that once"
msgstr "Cannot join the same wallet more that once"
msgid "Cannot join the same wallet more than once"
msgstr ""
#: www/views/includes/bitpayCardsCard.html:2
msgid "Cards"
@ -2928,9 +2928,14 @@ msgid "Sweep"
msgstr "Sweep"
#: www/views/includes/incomingDataMenu.html:89
#: www/views/paperWallet.html:3
msgctxt "List item"
msgid "Sweep paper wallet"
msgstr "Sweep paper wallet"
msgstr ""
#: www/views/paperWallet.html:3
msgctxt "Page title"
msgid "Sweep Paper Wallet"
msgstr "Sweep Paper Wallet"
#: src/js/services/onGoingProcess.js:33
msgid "Sweeping Wallet..."
@ -3795,6 +3800,10 @@ msgstr "Explore Bitcoin.com"
msgid "Bitcoin Cash Games"
msgstr "Bitcoin Cash Games"
#: www/views/includes/community.html:29
msgid "Share the Wallet App"
msgstr ""
#: src/js/services/bitcoincomService.js:28
msgid "News"
msgstr "News"
@ -3863,3 +3872,75 @@ msgstr "Less than 1 cent"
msgid "This invoice is no longer accepting payments"
msgstr "This invoice is no longer accepting payments"
#: www/views/amount.html.js:60
msgid "Send Maximum Amount"
msgstr ""
#: src/js/controllers/amount.controller.js:239
msgid "Unknown error."
msgstr ""
#: www/views/paperWallet.html:48
msgid "No Bitcoin Cash wallet to transfer funds to found."
msgstr ""
#: www/views/paperWallet.html:54
msgid "No Bitcoin Cash found."
msgstr ""
#: www/views/paperWallet.html:60
msgid "Bitcoin Core found:"
msgstr ""
#: www/views/paperWallet.html:98
msgid "No Bitcoin Core wallet to transfer funds to found."
msgstr ""
#: www/views/paperWallet.html:104
msgid "No Bitcoin Core found."
msgstr ""
#: src/js/controllers/tab-scan.js:120
msgid "Scan Failed"
msgstr ""
#: src/js/controllers/tab-scan.js:121
msgid "Data not recognised."
msgstr ""
#: src/js/controllers/tab-scan.js:121
msgid "Unsupported"
msgstr ""
#: src/js/controllers/tab-scan.js:121
msgid "Testnet is not supported."
msgstr ""
#: www/views/includes/incomingDataMenu.html:81
msgid "URL"
msgstr ""
#: www/views/includes/incomingDataMenu.html:90
msgid "Open in web browser"
msgstr ""
#: src/js/services/shapeshift.service.js.html:90
msgid "Invalid address"
msgstr ""
#: src/js/services/shapeshift.service.js.html:90
msgid "Amount is not defined"
msgstr ""
#: src/js/services/shapeshift.service.js.html:90
msgid "Amount is below the minimun"
msgstr ""
#: src/js/services/shapeshift.service.js.html:90
msgid "Amount is above the limit"
msgstr ""
#: src/js/services/shapeshift.service.js.html:90
msgid "Invalid response from Shapeshift"
msgstr ""

View file

@ -11,7 +11,7 @@ msgstr ""
"Last-Translator: emilold\n"
"Language-Team: Chinese Simplified\n"
"Language: zh\n"
"PO-Revision-Date: 2018-08-21 03:39\n"
"PO-Revision-Date: 2018-09-15 05:56\n"
#: www/views/modals/paypro.html:34
msgid "(Trusted)"
@ -520,7 +520,7 @@ msgid "Cannot Create Wallet"
msgstr "不能创建钱包"
#: src/js/services/profileService.js:442
msgid "Cannot join the same wallet more that once"
msgid "Cannot join the same wallet more than once"
msgstr "无法重复加入同一个钱包"
#: www/views/includes/bitpayCardsCard.html:2
@ -2925,9 +2925,14 @@ msgid "Sweep"
msgstr "扫描"
#: www/views/includes/incomingDataMenu.html:89
#: www/views/paperWallet.html:3
msgctxt "List item"
msgid "Sweep paper wallet"
msgstr "Sweep 纸钱包"
msgstr "清空纸钱包"
#: www/views/paperWallet.html:3
msgctxt "Page title"
msgid "Sweep Paper Wallet"
msgstr "清空纸钱包"
#: src/js/services/onGoingProcess.js:33
msgid "Sweeping Wallet..."
@ -3792,6 +3797,10 @@ msgstr "探索 Bitcoin.com"
msgid "Bitcoin Cash Games"
msgstr "Bitcoin Cash 游戏"
#: www/views/includes/community.html:29
msgid "Share the Wallet App"
msgstr "分享钱包应用"
#: src/js/services/bitcoincomService.js:28
msgid "News"
msgstr "新闻"
@ -3860,3 +3869,75 @@ msgstr "少于 1 美分"
msgid "This invoice is no longer accepting payments"
msgstr "此发票不再接受付款"
#: www/views/amount.html.js:60
msgid "Send Maximum Amount"
msgstr "发送最大金额"
#: src/js/controllers/amount.controller.js:239
msgid "Unknown error."
msgstr "未知错误。"
#: www/views/paperWallet.html:48
msgid "No Bitcoin Cash wallet to transfer funds to found."
msgstr "未找到可以转入资金的 Bitcoin Cash 钱包。"
#: www/views/paperWallet.html:54
msgid "No Bitcoin Cash found."
msgstr "未找到 Bitcoin Cash。"
#: www/views/paperWallet.html:60
msgid "Bitcoin Core found:"
msgstr "已找到 Bitcoin Core"
#: www/views/paperWallet.html:98
msgid "No Bitcoin Core wallet to transfer funds to found."
msgstr "未找到可以转入资金的 Bitcoin Core 钱包。"
#: www/views/paperWallet.html:104
msgid "No Bitcoin Core found."
msgstr "未找到 Bitcoin Core。"
#: src/js/controllers/tab-scan.js:120
msgid "Scan Failed"
msgstr "扫描失败"
#: src/js/controllers/tab-scan.js:121
msgid "Data not recognised."
msgstr "数据未被识别。"
#: src/js/controllers/tab-scan.js:121
msgid "Unsupported"
msgstr "不受支持"
#: src/js/controllers/tab-scan.js:121
msgid "Testnet is not supported."
msgstr "不支持测试网。"
#: www/views/includes/incomingDataMenu.html:81
msgid "URL"
msgstr "网址"
#: www/views/includes/incomingDataMenu.html:90
msgid "Open in web browser"
msgstr "在网络浏览器中打开"
#: src/js/services/shapeshift.service.js.html:90
msgid "Invalid address"
msgstr "地址无效"
#: src/js/services/shapeshift.service.js.html:90
msgid "Amount is not defined"
msgstr "未定义金额"
#: src/js/services/shapeshift.service.js.html:90
msgid "Amount is below the minimun"
msgstr "金额低于最低金额"
#: src/js/services/shapeshift.service.js.html:90
msgid "Amount is above the limit"
msgstr "金额高于限额"
#: src/js/services/shapeshift.service.js.html:90
msgid "Invalid response from Shapeshift"
msgstr "Shapeshift 的响应无效"

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 425 B

View file

@ -178,11 +178,17 @@ def codesign_app(config, args):
plistlib.writePlist(child_entitlements, tmp_child_entitlements)
info('Child entitlements: %s' % tmp_child_entitlements)
framework = glob(args.output, 'nwjs Framework.framework', returnOnFound=True)
system('codesign -f --verbose -s "%s" --entitlements %s --deep "%s"' % (identity, tmp_child_entitlements, framework))
helperApp = glob(args.output, 'nwjs Helper.app', returnOnFound=True)
system('codesign -f --verbose -s "%s" --entitlements %s --deep "%s"' % (identity, tmp_child_entitlements, helperApp))
libffmpeg = glob(os.path.join(args.output, 'Contents/Versions/55.0.2883.87/nwjs Framework.framework/Versions/A'), 'libffmpeg.dylib', returnOnFound=True)
system('codesign --deep --force --verbose --verify --sign "%s" --entitlements %s --deep "%s"' % (identity, tmp_child_entitlements, libffmpeg))
libnode = glob(os.path.join(args.output, 'Contents/Versions/55.0.2883.87/nwjs Framework.framework/Versions/A'), 'libnode.dylib', returnOnFound=True)
system('codesign --deep --force --verbose --verify --sign "%s" --entitlements %s --deep "%s"' % (identity, tmp_child_entitlements, libnode))
helperApp = glob(args.output, 'nwjs Helper.app', returnOnFound=True)
system('codesign --deep --force --verbose --verify --sign "%s" --entitlements %s --deep "%s"' % (identity, tmp_child_entitlements, helperApp))
framework = glob(args.output, 'nwjs Framework.framework', returnOnFound=True)
system('codesign --deep --force --verbose --verify --sign "%s" --entitlements %s --deep "%s"' % (identity, tmp_child_entitlements, framework))
## sign parent app
(_, tmp_parent_entitlements) = tempfile.mkstemp()
if config.has_option('Sign', 'ParentEntitlements'):

View file

@ -5,7 +5,9 @@
<key>com.apple.security.app-sandbox</key>
<true/>
<key>com.apple.security.application-groups</key>
<string>$GROUPID</string>
<array>
<string>299HJ3G3BP.com.bitcoin.mwallet.mac</string>
</array>
<key>com.apple.security.files.user-selected.read-only</key>
<true/>
<key>com.apple.security.network.client</key>

View file

@ -19,7 +19,9 @@ var modules = [
'copayApp.controllers',
'copayApp.directives',
'copayApp.addons',
'bitcoincom.directives'
'bitcoincom.controllers',
'bitcoincom.directives',
'bitcoincom.services'
];
var copayApp = window.copayApp = angular.module('copayApp', modules);
@ -29,4 +31,6 @@ angular.module('copayApp.services', []);
angular.module('copayApp.controllers', []);
angular.module('copayApp.directives', []);
angular.module('copayApp.addons', []);
angular.module('bitcoincom.controllers', []);
angular.module('bitcoincom.directives', []);
angular.module('bitcoincom.services', []);

View file

@ -21,28 +21,14 @@ angular.module('copayApp.controllers').controller('addressbookViewController', f
});
$scope.sendTo = function() {
$ionicHistory.removeBackView();
sendFlowService.clear();
$state.go('tabs.send');
$timeout(function() {
var to = '';
if ($scope.addressbookEntry.coin == 'bch') {
var a = 'bitcoincash:' + $scope.addressbookEntry.address;
to = bitcoinCashJsService.readAddress(a).legacy;
} else {
to = $scope.addressbookEntry.address;
}
var stateParams = {
data: $scope.addressbookEntry.address,
toName: $scope.addressbookEntry.name,
toEmail: $scope.addressbookEntry.email,
coin: $scope.addressbookEntry.coin
};
var stateParams = {
toAddress: to,
toName: $scope.addressbookEntry.name,
toEmail: $scope.addressbookEntry.email,
coin: $scope.addressbookEntry.coin
};
sendFlowService.pushState(stateParams);
$state.transitionTo('tabs.send.origin');
}, 100);
sendFlowService.start(stateParams);
};
$scope.remove = function(addressbookEntry) {

View file

@ -1,16 +1,23 @@
'use strict';
angular.module('copayApp.controllers').controller('amountController', amountController);
(function(){
function amountController(configService, $filter, gettextCatalog, $ionicHistory, $ionicModal, $ionicScrollDelegate, lodash, $log, nodeWebkitService, rateService, $scope, $state, $timeout, sendFlowService, shapeshiftService, txFormatService, platformInfo, profileService, walletService, $window) {
angular
.module('bitcoincom.controllers')
.controller('amountController', amountController);
function amountController(configService, $filter, gettextCatalog, $ionicHistory, $ionicModal, $ionicScrollDelegate, lodash, $log, nodeWebkitService, rateService, $scope, $state, $timeout, sendFlowService, shapeshiftService, txFormatService, platformInfo, ongoingProcess, popupService, profileService, walletService, $window) {
var vm = this;
// Variables
vm.allowSend = false;
vm.altCurrencyList = [];
vm.alternativeAmount = '';
vm.alternativeUnit = '';
vm.amount = '0';
vm.availableFunds = '';
vm.canSendAllAvailableFunds = true;
vm.errorMessage = '';
// Use insufficient for logic, as when the amount is invalid, funds being
// either sufficent or insufficient doesn't make sense.
vm.fundsAreInsufficient = false;
@ -20,9 +27,13 @@ function amountController(configService, $filter, gettextCatalog, $ionicHistory,
vm.lastUsedPopularList = [];
vm.maxAmount = 0;
vm.minAmount = 0;
vm.sendableFunds = '';
vm.showSendMaxButton = false;
vm.showSendLimitMaxButton = false;
vm.thirdParty = false;
vm.unit = '';
// Functions
vm.changeUnit = changeUnit;
vm.close = close;
vm.findCurrency = findCurrency;
@ -35,7 +46,7 @@ function amountController(configService, $filter, gettextCatalog, $ionicHistory,
vm.removeDigit = removeDigit;
vm.save = save;
vm.sendMax = sendMax;
vm.errorMessage = '';
$scope.$on('$ionicView.beforeEnter', onBeforeEnter);
$scope.$on('$ionicView.leave', onLeave);
@ -46,10 +57,8 @@ function amountController(configService, $filter, gettextCatalog, $ionicHistory,
var altCurrencyModal = null;
var altUnitIndex = 0;
var availableFundsInCrypto = '';
var availableFundsInFiat = '';
var availableSatoshis = null;
var availableUnits = [];
var canSendMax = true;
var fiatCode;
var isNW = platformInfo.isNW;
var isAndroid = platformInfo.isAndroid;
@ -57,10 +66,18 @@ function amountController(configService, $filter, gettextCatalog, $ionicHistory,
var lastUsedAltCurrencyList = [];
var passthroughParams = {};
var satToUnit;
var transactionSendableAmount = {
crypto: '',
satoshis: null
};
var unitDecimals;
var unitIndex = 0;
var unitToSatoshi;
var useSendMax = false;
var walletSpendableAmount = {
crypto: '',
satoshis: null
};
function onLeave() {
angular.element($window).off('keydown');
@ -68,42 +85,26 @@ function amountController(configService, $filter, gettextCatalog, $ionicHistory,
function onBeforeEnter(event, data) {
if (data.direction == "back") {
sendFlowService.popState();
sendFlowService.state.pop();
}
console.log('amount onBeforeEnter after back sendflow ', sendFlowService.state);
initCurrencies();
passthroughParams = sendFlowService.getStateClone();
passthroughParams = sendFlowService.state.getClone();
console.log('amount onBeforeEnter after back sendflow ', passthroughParams);
vm.fromWalletId = passthroughParams.fromWalletId;
vm.toWalletId = passthroughParams.toWalletId;
vm.minAmount = parseFloat(passthroughParams.minAmount);
vm.maxAmount = parseFloat(passthroughParams.maxAmount);
if (passthroughParams.thirdParty) {
vm.thirdParty = passthroughParams.thirdParty; // Parse stringified JSON-object
if (vm.thirdParty) {
if (vm.thirdParty.id === 'shapeshift') {
if (!vm.thirdParty.data) {
vm.thirdParty.data = {};
}
vm.thirdParty.data['fromWalletId'] = vm.fromWalletId;
vm.fromWallet = profileService.getWallet(vm.fromWalletId);
vm.toWallet = profileService.getWallet(vm.toWalletId);
shapeshiftService.getMarketData(vm.fromWallet.coin, vm.toWallet.coin, function(data) {
vm.thirdParty.data['minAmount'] = vm.minAmount = parseFloat(data.minimum);
vm.thirdParty.data['maxAmount'] = vm.maxAmount = parseFloat(data.maxLimit);
});
}
}
}
vm.isRequestingSpecificAmount = !passthroughParams.fromWalletId;
vm.showSendMaxButton = !vm.isRequestingSpecificAmount;
var config = configService.getSync().wallet.settings;
unitToSatoshi = config.unitToSatoshi;
satToUnit = 1 / unitToSatoshi;
unitDecimals = config.unitDecimals;
setAvailableUnits();
updateUnitUI();
@ -112,7 +113,7 @@ function amountController(configService, $filter, gettextCatalog, $ionicHistory,
var reOp = /^[\*\+\-\/]$/;
if (!isAndroid && !isIos) {
var disableKeys = angular.element($window).on('keydown', function(e) {
angular.element($window).on('keydown', function(e) {
if (!e.key) return;
if (e.which === 8) { // you can add others here inside brackets.
if (!altCurrencyModal) {
@ -135,10 +136,7 @@ function amountController(configService, $filter, gettextCatalog, $ionicHistory,
});
}
unitToSatoshi = config.unitToSatoshi;
satToUnit = 1 / unitToSatoshi;
unitDecimals = config.unitDecimals;
resetAmount();
processAmount();
@ -210,13 +208,48 @@ function amountController(configService, $filter, gettextCatalog, $ionicHistory,
var fromWallet = profileService.getWallet(passthroughParams.fromWalletId);
updateAvailableFundsFromWallet(fromWallet);
}
if (passthroughParams.thirdParty) {
vm.thirdParty = passthroughParams.thirdParty; // Parse stringified JSON-object
if (vm.thirdParty) {
initShapeshift();
}
}
}
}
function goBack() {
$ionicHistory.goBack();
sendFlowService.router.goBack();
}
function initShapeshift() {
if (vm.thirdParty.id === 'shapeshift') {
vm.thirdParty.data = vm.thirdParty.data || {};
vm.fromWallet = profileService.getWallet(vm.fromWalletId);
vm.toWallet = profileService.getWallet(vm.toWalletId);
vm.showSendMaxButton = false;
vm.showSendLimitMaxButton = false;
vm.canSendAllAvailableFunds = false;
ongoingProcess.set('connectingShapeshift', true);
shapeshiftService.getMarketData(vm.fromWallet.coin, vm.toWallet.coin, function onMarketData(err, data) {
ongoingProcess.set('connectingShapeshift', false);
if (err) {
// Error stop here
popupService.showAlert(gettextCatalog.getString('Shapeshift Error'), err.message, function () {
goBack();
});
} else {
vm.thirdParty.data.minAmount = vm.minAmount = parseFloat(data.minimum);
vm.thirdParty.data.maxAmount = vm.maxAmount = parseFloat(data.maxLimit);
setMaximumButtonFromWallet(vm.fromWallet);
}
});
}
}
function paste(value) {
vm.amount = value;
processAmount();
@ -232,8 +265,28 @@ function amountController(configService, $filter, gettextCatalog, $ionicHistory,
}
function sendMax() {
useSendMax = true;
finish();
if (canSendMax) {
useSendMax = true;
finish();
} else {
var transactionSendableAmountInUnits = transactionSendableAmount.satoshis * satToUnit;
if (vm.minAmount && transactionSendableAmountInUnits < vm.minAmount) {
popupService.showAlert(
gettextCatalog.getString('Insufficient funds'),
gettextCatalog.getString('Amount below minimum allowed')
);
} else {
// Need to be precise, so use crypto directly rather than fiat with exchange rate
if (availableUnits[unitIndex].isFiat) {
var tempIndex = altUnitIndex;
altUnitIndex = unitIndex;
unitIndex = tempIndex;
}
vm.amount = transactionSendableAmountInUnits.toFixed(LENGTH_AFTER_COMMA_EXPRESSION_LIMIT);
useSendMax = true;
finish();
}
}
}
function updateUnitUI() {
@ -353,8 +406,8 @@ function amountController(configService, $filter, gettextCatalog, $ionicHistory,
amountInCrypto = a;
var amountInSatoshis = a * unitToSatoshi;
vm.fundsAreInsufficient = !!passthroughParams.fromWalletId
&& availableSatoshis !== null
&& availableSatoshis < amountInSatoshis;
&& walletSpendableAmount.satoshis !== null
&& walletSpendableAmount.satoshis < amountInSatoshis;
vm.alternativeAmount = txFormatService.formatAmount(amountInSatoshis, true);
vm.allowSend = lodash.isNumber(a)
@ -374,8 +427,8 @@ function amountController(configService, $filter, gettextCatalog, $ionicHistory,
} else {
amountInCrypto = result;
vm.fundsAreInsufficient = passthroughParams.fromWalletId
&& availableSatoshis !== null
&& availableSatoshis < result * unitToSatoshi;
&& walletSpendableAmount.satoshis !== null
&& walletSpendableAmount.satoshis < result * unitToSatoshi;
vm.alternativeAmount = $filter('formatFiatAmount')(toFiat(result));
vm.allowSend = lodash.isNumber(result)
@ -449,13 +502,13 @@ function amountController(configService, $filter, gettextCatalog, $ionicHistory,
var satoshis = 0;
if (unit.isFiat) {
satoshis = (fromFiat(uiAmount) * unitToSatoshi).toFixed(0);
satoshis = Math.floor(fromFiat(uiAmount) * unitToSatoshi);
} else {
satoshis = (uiAmount * unitToSatoshi).toFixed(0);
satoshis = Math.floor(uiAmount * unitToSatoshi);
}
var confirmData = {
amount: useSendMax ? undefined : satoshis,
amount: (useSendMax && canSendMax) ? undefined : satoshis,
displayAddress: passthroughParams.displayAddress,
fromWalletId: passthroughParams.fromWalletId,
sendMax: useSendMax,
@ -467,12 +520,11 @@ function amountController(configService, $filter, gettextCatalog, $ionicHistory,
confirmData.thirdParty = vm.thirdParty;
}
sendFlowService.pushState(confirmData);
if (!confirmData.fromWalletId) {
$state.transitionTo('tabs.paymentRequest.confirm', confirmData);
} else {
$state.transitionTo('tabs.send.review', confirmData);
$scope.useSendMax = null;
sendFlowService.goNext(confirmData);
useSendMax = false;
}
}
@ -590,18 +642,73 @@ function amountController(configService, $filter, gettextCatalog, $ionicHistory,
}
function updateAvailableFundsStringIfNeeded() {
if (passthroughParams.fromWalletId && availableSatoshis !== null) {
availableFundsInFiat = '';
vm.availableFunds = availableFundsInCrypto;
if (passthroughParams.fromWalletId && walletSpendableAmount.satoshis !== null) {
vm.availableFunds = walletSpendableAmount.crypto;
if (availableUnits[unitIndex].isFiat) {
var coin = availableUnits[altUnitIndex].id;
txFormatService.formatAlternativeStr(coin, availableSatoshis, function formatCallback(formatted){
if (formatted) {
availableFundsInFiat = formatted;
txFormatService.formatAlternativeStr(coin, walletSpendableAmount.satoshis, function formatCallback(formatted){
if (formatted) {
$scope.$apply(function() {
vm.availableFunds = availableFundsInFiat;
vm.availableFunds = formatted;
});
}
});
}
updateMaximumButtonIfNeeded();
}
}
function updateAvailableFundsFromWallet(wallet) {
console.log('amount updateAvailableFundsFromWallet()');
var availableFundsInFiat = '';
if (wallet.status && wallet.status.isValid) {
walletSpendableAmount.crypto = wallet.status.spendableBalanceStr;
walletSpendableAmount.satoshis = wallet.status.spendableAmount;
if (wallet.status.alternativeBalanceAvailable) {
availableFundsInFiat = wallet.status.spendableBalanceAlternative + ' ' + wallet.status.alternativeIsoCode;
} else {
availableFundsInFiat = '';
}
} else if (wallet.cachedStatus && wallet.cachedStatus.isValid) {
if (wallet.cachedStatus.alternativeBalanceAvailable) {
availableFundsInFiat = wallet.cachedStatus.spendableBalanceAlternative + ' ' + wallet.cachedStatus.alternativeIsoCode;
} else {
availableFundsInFiat = '';
}
walletSpendableAmount.crypto = wallet.cachedStatus.spendableBalanceStr;
walletSpendableAmount.satoshis = wallet.cachedStatus.spendableAmount;
} else {
walletSpendableAmount.crypto = '';
walletSpendableAmount.satoshis = null;
}
if (availableUnits[unitIndex].isFiat) {
vm.availableFunds = availableFundsInFiat || walletSpendableAmount.crypto;
} else {
vm.availableFunds = walletSpendableAmount.crypto;
}
setMaximumButtonFromWallet(wallet);
}
function updateMaximumButtonIfNeeded() {
console.log('sendmax updateMaximumButtonIfNeeded()');
if (vm.showSendMaxButton || vm.showSendLimitMaxButton) {
transactionSendableAmount.fiat = '';
vm.sendableFunds = transactionSendableAmount.crypto;
if (availableUnits[unitIndex].isFiat) {
var coin = availableUnits[altUnitIndex].id;
txFormatService.formatAlternativeStr(coin, transactionSendableAmount.satoshis, function formatCallback(formatted){
if (formatted) {
$scope.$apply(function onApply() {
vm.sendableFunds = formatted;
});
}
});
@ -609,37 +716,59 @@ function amountController(configService, $filter, gettextCatalog, $ionicHistory,
}
}
function updateAvailableFundsFromWallet(wallet) {
if (wallet.status && wallet.status.isValid) {
availableFundsInCrypto = wallet.status.spendableBalanceStr;
availableSatoshis = wallet.status.spendableAmount;
if (wallet.status.alternativeBalanceAvailable) {
availableFundsInFiat = wallet.status.spendableBalanceAlternative + ' ' + wallet.status.alternativeIsoCode;
function setMaximumButtonFromWallet(wallet) {
console.log('sendmax setMaximumButtonFromWallet()');
var minSatoshis = vm.minAmount * unitToSatoshi;
var maxSatoshis = vm.maxAmount * unitToSatoshi;
if (minSatoshis > walletSpendableAmount.satoshis) {
console.log('sendmax Hiding max buttons as minimum is too high.');
canSendMax = false;
vm.showSendMaxButton = true;
vm.showSendLimitMaxButton = false;
transactionSendableAmount.satoshis = walletSpendableAmount.satoshis;
} else if (maxSatoshis) {
if (walletSpendableAmount.satoshis > maxSatoshis) {
console.log('sendmax Showing max limit button as available is greater than max limit.');
canSendMax = false;
vm.showSendMaxButton = false;
vm.showSendLimitMaxButton = true;
transactionSendableAmount.satoshis = maxSatoshis;
} else {
availableFundsInFiat = '';
console.log('sendmax Showing sendmax as all available as less than max limit.');
// Enabling send max here is a little dangerous, if they receive funds between pressing
// this and the calculation in the Review screen.
canSendMax = false;
vm.showSendMaxButton = true;
vm.showSendLimitMaxButton = false;
transactionSendableAmount.satoshis = walletSpendableAmount.satoshis;
}
} else if (wallet.cachedStatus && wallet.status.isValid) {
} else {
console.log('sendmax Showing sendmax as all available because no limits.');
canSendMax = true;
vm.showSendMaxButton = true;
vm.showSendLimitMaxButton = false;
transactionSendableAmount.satoshis = walletSpendableAmount.satoshis;
}
if (wallet.cachedStatus.alternativeBalanceAvailable) {
availableFundsInFiat = wallet.cachedStatus.spendableBalanceAlternative + ' ' + wallet.cachedStatus.alternativeIsoCode;
} else {
availableFundsInFiat = '';
if (vm.showSendMaxButton || vm.showSendLimitMaxButton) {
console.log('sendmax Setting max button text');
transactionSendableAmount.crypto = txFormatService.formatAmountStr(wallet.coin, transactionSendableAmount.satoshis);
vm.sendableFunds = transactionSendableAmount.crypto;
if (availableUnits[unitIndex].isFiat) {
txFormatService.formatAlternativeStr(wallet.coin, transactionSendableAmount.satoshis, function onFormat(formatted){
if (formatted) {
$scope.$apply(function onApply() {
vm.sendableFunds = formatted;
});
}
});
}
availableFundsInCrypto = wallet.cachedStatus.spendableBalanceStr;
availableSatoshis = wallet.cachedStatus.spendableAmount;
} else {
availableFundsInFiat = '';
availableFundsInCrypto = '';
availableSatoshis = null;
}
if (availableUnits[unitIndex].isFiat) {
vm.availableFunds = availableFundsInFiat || availableFundsInCrypto;
} else {
vm.availableFunds = availableFundsInCrypto;
}
}
}
})();

View file

@ -1,12 +1,20 @@
describe('amountController', function(){
var configCache,
configService,
configService,
gettextCatalog,
$controller,
$ionicHistory,
$rootScope,
ongoingProcess,
platformInfo,
popupService,
profileService,
rateService,
sendFlowService,
shapeshiftService,
txFormatService,
$scope,
$state,
$stateParams;
@ -18,7 +26,7 @@ describe('amountController', function(){
configCache = {
wallet: {
settings: {
unitToSatoshi: 100000000
}
}
};
@ -31,18 +39,42 @@ describe('amountController', function(){
});
configService.getSync.and.returnValue(configCache);
gettextCatalog = jasmine.createSpyObj(['getString']);
gettextCatalog.getString.and.callFake(function(str){ return str; });
$ionicHistory = jasmine.createSpyObj(['backView']);
ongoingProcess = jasmine.createSpyObj(['set']);
platformInfo = {
isChromeApp: false,
isAndroid: false,
isIos: true
};
profileService = jasmine.createSpyObj(['getWallets']);
popupService = jasmine.createSpyObj(['showAlert']);
profileService = jasmine.createSpyObj(['getWallet', 'getWallets']);
rateService = jasmine.createSpyObj(['fromFiat', 'whenAvailable']);
rateService = jasmine.createSpyObj(['fromFiat', 'listAlternatives', 'updateRates', 'whenAvailable']);
sendFlowService = jasmine.createSpyObj(['getStateClone', 'pushState']);
shapeshiftService = jasmine.createSpyObj(['getMarketData']);
txFormatService = jasmine.createSpyObj(['formatAlternativeStr', 'formatAmountStr']);
txFormatService.formatAlternativeStr.and.callFake(function(coin, satoshis, cb) {
if (typeof satoshis !== "number") {
throw "satoshis in formatAlternativeStr() is not a number."
}
var units = satoshis / 100000000;
var formatted = (units * 10000).toFixed(2) + ' USD';
cb(formatted);
});
txFormatService.formatAmountStr.and.callFake(function(coin, satoshis) {
if (typeof satoshis !== "number") {
throw "satoshis in formatAmountStr() is not a number."
}
return (satoshis * 100000000).toFixed(8) + ' ' + (coin || 'bch').toUpperCase();
});
$state = jasmine.createSpyObj(['transitionTo']);
$stateParams = {};
inject(function(_$controller_, _$rootScope_){
@ -61,6 +93,14 @@ describe('amountController', function(){
stateName: 'ignoreme'
};
$ionicHistory.backView.and.returnValue(backView);
var wallet = {
status: {
isValid: true,
spendableAmount: 123456
}
};
profileService.getWallet.and.returnValue(wallet);
profileService.getWallets.and.returnValue([{}]);
rateService.fromFiat.and.returnValue(12); // satoshis or coins?
@ -69,33 +109,496 @@ describe('amountController', function(){
var amountController = $controller('amountController', {
configService: configService,
gettextCatalog: {},
gettextCatalog: gettextCatalog,
$ionicHistory: $ionicHistory,
$ionicModal: {},
$ionicScrollDelegate: {},
nodeWebkitService: {},
ongoingProcess: {},
ongoingProcess: ongoingProcess,
platformInfo: platformInfo,
profileService: profileService,
popupService: {},
popupService: popupService,
rateService: rateService,
$scope: $scope,
sendFlowService: sendFlowService,
shapeshiftService: shapeshiftService,
$state: {},
$stateParams: $stateParams,
txFormatService: {},
txFormatService: txFormatService,
walletService: {}
});
var data = {
stateParams: {
fromWalletId: 'fd56c1e7-e3ac-4fd9-8afc-27b9c1b3718b',
toAddress: 'qrup46avn8t466xxwlzs4qelht7cnwvesv2e29wf7s'
}
var sendFlowState = {
fromWalletId: 'fd56c1e7-e3ac-4fd9-8afc-27b9c1b3718b',
toAddress: 'qrup46avn8t466xxwlzs4qelht7cnwvesv2e29wf7s'
};
$scope.$emit('$ionicView.beforeEnter', data);
expect($scope.fromWalletId).toBe('fd56c1e7-e3ac-4fd9-8afc-27b9c1b3718b');
expect($scope.toAddress).toBe('qrup46avn8t466xxwlzs4qelht7cnwvesv2e29wf7s');
sendFlowService.getStateClone.and.returnValue(sendFlowState);
$scope.$emit('$ionicView.beforeEnter', {});
//expect($scope.fromWalletId).toBe('fd56c1e7-e3ac-4fd9-8afc-27b9c1b3718b');
//expect($scope.toAddress).toBe('qrup46avn8t466xxwlzs4qelht7cnwvesv2e29wf7s');
});
describe('Shapeshift', function() {
var walletFrom;
var walletTo;
beforeEach(function(){
walletFrom = {};
walletTo = {};
profileService.getWallet.and.callFake(function(walletId){
if (walletId === '4cd7673e-7320-4dfa-86e5-d4edb51d460a') {
return walletFrom;
} else if (walletId === 'bf00af8f-0788-4b57-b30a-0390747407e9') {
return walletTo;
} else {
return null;
}
});
rateService.listAlternatives.and.returnValue([
{name: "Australian Dollar", isoCode: "AUD"},
{name: "United States Dollar", isoCode: "USD"}
]);
});
it ('with available balance below limit, shows sendMax for triggering alert', function() {
walletFrom.coin = 'btc';
walletFrom.status = {
isValid: true,
spendableAmount: 789
};
walletTo.coin = 'bch';
profileService.getWallets.and.returnValue([{}]);
rateService.fromFiat.and.returnValue(12);
var $scope = $rootScope.$new();
var amountController = $controller('amountController', {
configService: configService,
gettextCatalog: gettextCatalog,
$ionicHistory: $ionicHistory,
$ionicModal: {},
$ionicScrollDelegate: {},
nodeWebkitService: {},
ongoingProcess: ongoingProcess,
platformInfo: platformInfo,
profileService: profileService,
popupService: popupService,
rateService: rateService,
$scope: $scope,
sendFlowService: sendFlowService,
shapeshiftService: shapeshiftService,
$state: $state,
$stateParams: $stateParams,
txFormatService: txFormatService,
walletService: {}
});
rateService.whenAvailable.and.callFake(function(cb){
cb();
});
var sendFlowState = {
amount: '',
displayAddress: null,
fromWalletId: '4cd7673e-7320-4dfa-86e5-d4edb51d460a',
sendMax: false,
thirdParty: {
id: 'shapeshift',
data: {},
},
toAddress: '',
toWalletId: 'bf00af8f-0788-4b57-b30a-0390747407e9'
};
sendFlowService.getStateClone.and.returnValue(sendFlowState);
var reqCoinIn = '';
var reqCoinOut = '';
shapeshiftService.getMarketData.and.callFake(function(coinIn, coinOut, cb){
reqCoinIn = coinIn;
reqCoinOut = coinOut;
cb({
maxLimit: '0.6846239',
minimum: '0.00013692'
});
});
$scope.$emit('$ionicView.beforeEnter', {});
expect(rateService.updateRates.calls.any()).toEqual(true);
expect(reqCoinIn).toBe('btc');
expect(reqCoinOut).toBe('bch');
expect(amountController.maxAmount).toBe(0.68462390);
expect(amountController.minAmount).toBe(0.00013692);
expect(amountController.showSendMaxButton).toEqual(true);
expect(amountController.showSendLimitMaxButton).toEqual(false);
expect(amountController.sendableFunds).toEqual('0.08 USD');
// Now hit the Send Max button
amountController.sendMax();
expect(popupService.showAlert.calls.argsFor(0)[0]).toEqual('Insufficient funds');
expect(popupService.showAlert.calls.argsFor(0)[1]).toEqual('Amount below minimum allowed');
expect(sendFlowService.pushState.calls.any()).toEqual(false);
expect($state.transitionTo.calls.any()).toEqual(false);
});
it ('with available balance between limits, uses sendMax', function() {
walletFrom.coin = 'btc';
walletFrom.status = {
isValid: true,
spendableAmount: 456789
};
walletTo.coin = 'bch';
profileService.getWallets.and.returnValue([{}]);
rateService.fromFiat.and.returnValue(12);
var $scope = $rootScope.$new();
var amountController = $controller('amountController', {
configService: configService,
gettextCatalog: {},
$ionicHistory: $ionicHistory,
$ionicModal: {},
$ionicScrollDelegate: {},
nodeWebkitService: {},
ongoingProcess: ongoingProcess,
platformInfo: platformInfo,
profileService: profileService,
popupService: {},
rateService: rateService,
$scope: $scope,
sendFlowService: sendFlowService,
shapeshiftService: shapeshiftService,
$state: $state,
$stateParams: $stateParams,
txFormatService: txFormatService,
walletService: {}
});
rateService.whenAvailable.and.callFake(function(cb){
cb();
});
var sendFlowState = {
amount: '',
displayAddress: null,
fromWalletId: '4cd7673e-7320-4dfa-86e5-d4edb51d460a',
sendMax: false,
thirdParty: {
id: 'shapeshift',
data: {},
},
toAddress: '',
toWalletId: 'bf00af8f-0788-4b57-b30a-0390747407e9'
};
sendFlowService.getStateClone.and.returnValue(sendFlowState);
var reqCoinIn = '';
var reqCoinOut = '';
shapeshiftService.getMarketData.and.callFake(function(coinIn, coinOut, cb){
reqCoinIn = coinIn;
reqCoinOut = coinOut;
cb({
maxLimit: '0.6846239',
minimum: '0.00013692'
});
});
$scope.$emit('$ionicView.beforeEnter', {});
expect(rateService.updateRates.calls.any()).toEqual(true);
expect(reqCoinIn).toBe('btc');
expect(reqCoinOut).toBe('bch');
expect(amountController.maxAmount).toBe(0.68462390);
expect(amountController.minAmount).toBe(0.00013692);
expect(amountController.showSendMaxButton).toEqual(true);
expect(amountController.showSendLimitMaxButton).toEqual(false);
// Now hit the Send Max button
var pushedState = null;
sendFlowService.pushState.and.callFake(function (sendFlowState){
pushedState = sendFlowState;
});
amountController.sendMax();
expect(pushedState.amount).toBeUndefined();
expect(pushedState.fromWalletId).toEqual('4cd7673e-7320-4dfa-86e5-d4edb51d460a');
expect(pushedState.sendMax).toEqual(true);
expect(pushedState.toWalletId).toEqual('bf00af8f-0788-4b57-b30a-0390747407e9');
expect(pushedState.thirdParty.id).toEqual('shapeshift');
expect(pushedState.thirdParty.data.maxAmount).toEqual(0.6846239);
expect(pushedState.thirdParty.data.minAmount).toEqual(0.00013692);
expect($state.transitionTo.calls.count()).toEqual(1);
expect($state.transitionTo.calls.argsFor(0)[0]).toEqual('tabs.send.review');
});
it ('with available balance higher than max, uses send limit max instead of sendMax', function() {
walletFrom.coin = 'btc';
walletFrom.status = {
isValid: true,
spendableAmount: 123456789
};
walletTo.coin = 'bch';
profileService.getWallets.and.returnValue([{}]);
rateService.fromFiat.and.returnValue(12); // satoshis or coins?
var $scope = $rootScope.$new();
var amountController = $controller('amountController', {
configService: configService,
gettextCatalog: {},
$ionicHistory: $ionicHistory,
$ionicModal: {},
$ionicScrollDelegate: {},
nodeWebkitService: {},
ongoingProcess: ongoingProcess,
platformInfo: platformInfo,
profileService: profileService,
popupService: {},
rateService: rateService,
$scope: $scope,
sendFlowService: sendFlowService,
shapeshiftService: shapeshiftService,
$state: $state,
$stateParams: $stateParams,
txFormatService: txFormatService,
walletService: {}
});
rateService.whenAvailable.and.callFake(function(cb){
cb();
});
var sendFlowState = {
amount: '',
displayAddress: null,
fromWalletId: '4cd7673e-7320-4dfa-86e5-d4edb51d460a',
sendMax: false,
thirdParty: {
id: 'shapeshift',
data: {},
},
toAddress: '',
toWalletId: 'bf00af8f-0788-4b57-b30a-0390747407e9'
};
sendFlowService.getStateClone.and.returnValue(sendFlowState);
var reqCoinIn = '';
var reqCoinOut = '';
shapeshiftService.getMarketData.and.callFake(function(coinIn, coinOut, cb){
reqCoinIn = coinIn;
reqCoinOut = coinOut;
cb({
maxLimit: '0.6846239',
minimum: '0.00013692'
});
});
$scope.$emit('$ionicView.beforeEnter', {});
expect(rateService.updateRates.calls.any()).toEqual(true);
expect(reqCoinIn).toBe('btc');
expect(reqCoinOut).toBe('bch');
expect(amountController.maxAmount).toBe(0.6846239);
expect(amountController.minAmount).toBe(0.00013692);
expect(amountController.showSendMaxButton).toEqual(false);
expect(amountController.showSendLimitMaxButton).toEqual(true);
// Now hit the Send Max button
var pushedState = null;
sendFlowService.pushState.and.callFake(function (sendFlowState){
pushedState = sendFlowState;
});
amountController.sendMax();
expect(pushedState.amount).toEqual(68462390);
expect(pushedState.fromWalletId).toEqual('4cd7673e-7320-4dfa-86e5-d4edb51d460a');
expect(pushedState.sendMax).toEqual(false);
expect(pushedState.toWalletId).toEqual('bf00af8f-0788-4b57-b30a-0390747407e9');
expect(pushedState.thirdParty.id).toEqual('shapeshift');
expect(pushedState.thirdParty.data.maxAmount).toEqual(0.6846239);
expect(pushedState.thirdParty.data.minAmount).toEqual(0.00013692);
expect($state.transitionTo.calls.count()).toEqual(1);
expect($state.transitionTo.calls.argsFor(0)[0]).toEqual('tabs.send.review');
});
});
describe('Wallet transfer', function() {
var walletFrom;
var walletTo;
beforeEach(function(){
walletFrom = {};
walletTo = {};
profileService.getWallet.and.callFake(function(walletId){
if (walletId === '4cd7673e-7320-4dfa-86e5-d4edb51d460a') {
return walletFrom;
} else if (walletId === 'bf00af8f-0788-4b57-b30a-0390747407e9') {
return walletTo;
} else {
return null;
}
});
rateService.listAlternatives.and.returnValue([
{name: "Australian Dollar", isoCode: "AUD"},
{name: "United States Dollar", isoCode: "USD"}
]);
});
it('wallet transfer send max.', function() {
walletFrom.coin = 'btc';
walletFrom.status = {
isValid: true,
spendableAmount: 123456789
};
profileService.getWallets.and.returnValue([{}]);
var $scope = $rootScope.$new();
var amountController = $controller('amountController', {
configService: configService,
gettextCatalog: gettextCatalog,
$ionicHistory: $ionicHistory,
$ionicModal: {},
$ionicScrollDelegate: {},
nodeWebkitService: {},
ongoingProcess: ongoingProcess,
platformInfo: platformInfo,
profileService: profileService,
popupService: popupService,
rateService: rateService,
$scope: $scope,
sendFlowService: sendFlowService,
shapeshiftService: shapeshiftService,
$state: $state,
$stateParams: $stateParams,
txFormatService: txFormatService,
walletService: {}
});
var sendFlowState = {
fromWalletId: '4cd7673e-7320-4dfa-86e5-d4edb51d460a',
toWalletId: 'bf00af8f-0788-4b57-b30a-0390747407e9'
};
sendFlowService.getStateClone.and.returnValue(sendFlowState);
$scope.$emit('$ionicView.beforeEnter', {});
expect(amountController.showSendMaxButton).toEqual(true);
expect(amountController.showSendLimitMaxButton).toEqual(false);
expect(amountController.sendableFunds).toEqual('12345.68 USD');
// Now hit the Send Max button
var pushedState = null;
sendFlowService.pushState.and.callFake(function (sendFlowState){
pushedState = sendFlowState;
});
amountController.sendMax();
expect(pushedState.amount).toBeUndefined();
expect(pushedState.fromWalletId).toEqual('4cd7673e-7320-4dfa-86e5-d4edb51d460a');
expect(pushedState.sendMax).toEqual(true);
expect(pushedState.toWalletId).toEqual('bf00af8f-0788-4b57-b30a-0390747407e9');
expect($state.transitionTo.calls.count()).toEqual(1);
expect($state.transitionTo.calls.argsFor(0)[0]).toEqual('tabs.send.review');
});
// This situation was seen in real life
it('wallet transfer with valid cached status only.', function() {
walletFrom.coin = 'btc';
walletFrom.status = {
isValid: false,
};
walletFrom.cachedStatus = {
isValid: true,
spendableAmount: 5678
};
profileService.getWallets.and.returnValue([{}]);
var $scope = $rootScope.$new();
var amountController = $controller('amountController', {
configService: configService,
gettextCatalog: gettextCatalog,
$ionicHistory: $ionicHistory,
$ionicModal: {},
$ionicScrollDelegate: {},
nodeWebkitService: {},
ongoingProcess: ongoingProcess,
platformInfo: platformInfo,
profileService: profileService,
popupService: popupService,
rateService: rateService,
$scope: $scope,
sendFlowService: sendFlowService,
shapeshiftService: shapeshiftService,
$state: $state,
$stateParams: $stateParams,
txFormatService: txFormatService,
walletService: {}
});
var sendFlowState = {
fromWalletId: '4cd7673e-7320-4dfa-86e5-d4edb51d460a',
toWalletId: 'bf00af8f-0788-4b57-b30a-0390747407e9'
};
sendFlowService.getStateClone.and.returnValue(sendFlowState);
$scope.$emit('$ionicView.beforeEnter', {});
expect(amountController.showSendMaxButton).toEqual(true);
expect(amountController.showSendLimitMaxButton).toEqual(false);
expect(amountController.sendableFunds).toEqual('0.57 USD');
});
});
});

View file

@ -517,7 +517,8 @@ angular.module('copayApp.controllers').controller('confirmController', function(
if (!lodash.isEmpty(warningMsg))
msg += '\n' + warningMsg;
popupService.showAlert(null, msg, function() {});
popupService.showAlert(null, msg, function() {});
};
$scope.onWalletSelect = function(wallet) {

View file

@ -1,6 +1,6 @@
'use strict';
angular.module('copayApp.controllers').controller('tourController',
function($scope, $state, $log, $timeout, $filter, ongoingProcess, profileService, rateService, popupService, gettextCatalog, startupService, storageService, walletService, $q) {
function ($scope, $state, $log, $timeout, $filter, ongoingProcess, configService, profileService, rateService, popupService, gettextCatalog, lodash, startupService, storageService, uxLanguage, walletService, $q) {
$scope.data = {
index: 0
@ -46,62 +46,90 @@ angular.module('copayApp.controllers').controller('tourController',
creatingWallet = true;
ongoingProcess.set('creatingWallet', true);
$timeout(function() {
profileService.createDefaultWallet(function(err, walletClients) {
if (err) {
$log.warn(err);
uxLanguage.init(function(lang) {
var rateCode = uxLanguage.getRateCode(lang);
console.log("When Available: rateService");
rateService.whenAvailable(function() {
var alternatives = rateService.listAlternatives(true);
return $timeout(function() {
$log.warn('Retrying to create default wallet.....:' + ++retryCount);
if (retryCount > 3) {
ongoingProcess.set('creatingWallet', false);
popupService.showAlert(
gettextCatalog.getString('Cannot Create Wallet'), err,
function() {
retryCount = 0;
return $scope.createDefaultWallet();
}, gettextCatalog.getString('Retry'));
} else {
return $scope.createDefaultWallet();
}
}, 2000);
};
ongoingProcess.set('creatingWallet', false);
var bchWallet = walletClients[0];
var btcWallet = walletClients[1];
var bchWalletId = bchWallet.credentials.walletId;
var btcWalletId = btcWallet.credentials.walletId;
function createAddressPromise(wallet) {
return $q(function(resolve, reject) {
walletService.getAddress(wallet, true, function(e, addr) {
if (e) reject(e);
resolve(addr);
var newAltCurrency = lodash.find(alternatives, {
'isoCode': rateCode
});
configService.whenAvailable(function(config) {
var opts = {
wallet: {
settings: {
alternativeName: newAltCurrency.name,
alternativeIsoCode: newAltCurrency.isoCode,
}
}
};
configService.set(opts, function(err) {
if (err) $log.warn(err);
profileService.createDefaultWallet(function(err, walletClients) {
if (err) {
$log.warn(err);
return $timeout(function() {
$log.warn('Retrying to create default wallet.....:' + ++retryCount);
if (retryCount > 3) {
ongoingProcess.set('creatingWallet', false);
popupService.showAlert(
gettextCatalog.getString('Cannot Create Wallet'), err,
function() {
retryCount = 0;
return $scope.createDefaultWallet();
}, gettextCatalog.getString('Retry'));
} else {
return $scope.createDefaultWallet();
}
}, 2000);
}
;
ongoingProcess.set('creatingWallet', false);
var bchWallet = walletClients[0];
var btcWallet = walletClients[1];
var bchWalletId = bchWallet.credentials.walletId;
var btcWalletId = btcWallet.credentials.walletId;
function createAddressPromise(wallet) {
return $q(function (resolve, reject) {
walletService.getAddress(wallet, true, function (e, addr) {
if (e) reject(e);
resolve(addr);
});
});
}
function goToCollectEmail() {
$state.go('onboarding.collectEmail', {
bchWalletId: bchWalletId,
btcWalletId: btcWalletId
});
}
var bchAddressPromise = createAddressPromise(bchWallet);
var btcAddressPromise = createAddressPromise(btcWallet);
ongoingProcess.set('generatingNewAddress', true);
$q.all([bchAddressPromise, btcAddressPromise]).then(function (addresses) {
ongoingProcess.set('generatingNewAddress', false);
$state.go('tabs.home');
}, function (e) {
ongoingProcess.set('generatingNewAddress', false);
$log.warn(e);
popupService.showAlert(gettextCatalog.getString('Error'), e);
$state.go('tabs.home');
});
});
});
});
$log.debug('Setting default currency : ' + newAltCurrency);
});
}
function goToCollectEmail() {
$state.go('onboarding.collectEmail', {
bchWalletId: bchWalletId,
btcWalletId: btcWalletId
});
}
var bchAddressPromise = createAddressPromise(bchWallet);
var btcAddressPromise = createAddressPromise(btcWallet);
ongoingProcess.set('generatingNewAddress', true);
$q.all([bchAddressPromise, btcAddressPromise]).then(function(addresses) {
ongoingProcess.set('generatingNewAddress', false);
$state.go('tabs.home');
}, function(e) {
ongoingProcess.set('generatingNewAddress', false);
$log.warn(e);
popupService.showAlert(gettextCatalog.getString('Error'), e);
$state.go('tabs.home');
});
});
}, 300);
};
});
})
}, 300);
};
});

File diff suppressed because it is too large Load diff

View file

@ -1,6 +1,6 @@
'use strict';
angular.module('copayApp.controllers').controller('servicesController', function($scope, $ionicScrollDelegate, $timeout, servicesService, configService) {
angular.module('copayApp.controllers').controller('servicesController', function(externalLinkService, $scope, $ionicScrollDelegate, $timeout, servicesService, configService) {
$scope.hide = false;
configService.whenAvailable(function(config) {
@ -20,4 +20,8 @@ angular.module('copayApp.controllers').controller('servicesController', function
}, 10);
};
$scope.open = function(url) {
externalLinkService.open(url, false);
}
});

View file

@ -6,27 +6,11 @@ angular.module('copayApp.controllers').controller('shapeshiftController', functi
$scope.showMyAddress = showMyAddress;
function generateAddress(wallet, cb) {
if (!wallet) return;
walletService.getAddress(wallet, false, function(err, addr) {
if (err) {
popupService.showAlert(err);
}
return cb(addr);
});
}
function showToWallets() {
$scope.toWallets = $scope.fromWallet.coin === 'btc' ? walletsBch : walletsBtc;
$scope.onToWalletSelect($scope.toWallets[0]);
$scope.singleToWallet = $scope.toWallets.length === 1;
}
$scope.$on("$ionicView.beforeEnter", function(event, data) {
walletsBtc = profileService.getWallets({coin: 'btc'});
walletsBch = profileService.getWallets({coin: 'bch'});
$scope.fromWallets = lodash.filter(walletsBtc.concat(walletsBch), function(w) {
return w.status.balance.availableAmount > 0;
return (w.status && w.status.balance && w.status.balance.availableAmount > 0);
});
$scope.singleFromWallet = $scope.fromWallets.length === 1;
@ -62,18 +46,7 @@ angular.module('copayApp.controllers').controller('shapeshiftController', functi
id: 'shapeshift'
}
};
// Starting new send flow, so ensure everything is reset
sendFlowService.clear();
$state.go('tabs.home').then(function() {
$ionicHistory.clearHistory();
$state.go('tabs.send').then(function() {
$timeout(function () {
sendFlowService.pushState(stateParams);
$state.transitionTo('tabs.send.origin');
}, 60);
});
});
sendFlowService.start(stateParams);
}
function showMyAddress() {

View file

@ -43,21 +43,20 @@ angular.module('copayApp.controllers').controller('tabHomeController',
});
}
if ($scope.isNW) {
latestReleaseService.checkLatestRelease(function(err, newRelease) {
if (err) {
$log.warn(err);
return;
}
if (newRelease) {
$scope.newRelease = true;
$scope.updateText = gettextCatalog.getString('There is a new version of {{appName}} available', {
appName: $scope.name
});
}
});
}
};
latestReleaseService.checkLatestRelease(function(err, newReleaseData) {
if (err) {
$log.warn(err);
return;
}
if (newReleaseData) {
$scope.newRelease = true;
$scope.newReleaseText = gettextCatalog.getString('There is a new version of {{appName}} available', {
appName: $scope.name
});
$scope.newReleaseNotes = newReleaseData.releaseNotes;
}
});
}
function onEnter(event, data) {
$ionicNavBarDelegate.showBar(true);
@ -109,31 +108,24 @@ angular.module('copayApp.controllers').controller('tabHomeController',
$scope.$apply();
}, 10);
});
};
}
function onLeave (event, data) {
lodash.each(listeners, function(x) {
x();
});
};
}
$scope.createdWithinPastDay = function(time) {
return timeService.withinPastDay(time);
};
$scope.startFreshSend = function() {
sendFlowService.clear();
$state.go('tabs.send');
sendFlowService.start();
}
$scope.openExternalLink = function() {
var url = 'https://github.com/Bitcoin-com/Wallet/releases/latest';
var optIn = true;
var title = gettextCatalog.getString('Update Available');
var message = gettextCatalog.getString('An update to this app is available. For your security, please update to the latest version.');
var okText = gettextCatalog.getString('View Update');
var cancelText = gettextCatalog.getString('Go Back');
externalLinkService.open(url, optIn, title, message, okText, cancelText);
$scope.showUpdatePopup = function() {
latestReleaseService.showUpdatePopup();
};
$scope.openBannerUrl = function() {

View file

@ -2,6 +2,7 @@
angular.module('copayApp.controllers').controller('tabReceiveController', function($rootScope, $scope, $timeout, $log, $ionicModal, $state, $ionicHistory, $ionicPopover, storageService, platformInfo, walletService, profileService, configService, lodash, gettextCatalog, popupService, bwcError, bitcoinCashJsService, $ionicNavBarDelegate, sendFlowService, txFormatService, soundService, clipboardService) {
var CLOSE_NORMAL = 1000;
var listeners = [];
$scope.bchAddressType = { type: 'cashaddr' };
var bchAddresses = {};
@ -10,20 +11,63 @@ angular.module('copayApp.controllers').controller('tabReceiveController', functi
$scope.isCordova = platformInfo.isCordova;
$scope.isNW = platformInfo.isNW;
var currentAddressSocket = {};
var paymentSubscriptionObj = { op:"addr_sub" }
var config;
var currentAddressSocket = null;
var paymentSubscriptionObj = { op:'addr_sub' };
$scope.displayBalanceAsFiat = true;
$scope.$on('$ionicView.beforeLeave', onBeforeLeave);
$scope.requestSpecificAmount = function() {
sendFlowService.pushState({
toWalletId: $scope.wallet.credentials.walletId
sendFlowService.start({
toWalletId: $scope.wallet.credentials.walletId,
isRequestAmount: true
});
$state.go('tabs.paymentRequest.amount');
};
function connectSocket() {
// Close existing socket if not connected with current address
if (currentAddressSocket) {
currentAddressSocket.close([CLOSE_NORMAL]);
}
if ($scope.wallet.coin === 'bch') {
// listen to bch address
currentAddressSocket = new WebSocket('wss://ws.blockchain.info/bch/inv');
paymentSubscriptionObj.addr = $scope.addrBchLegacy;
} else {
// listen to btc address
currentAddressSocket = new WebSocket('wss://ws.blockchain.info/inv');
paymentSubscriptionObj.addr = $scope.addr;
}
// create subscription to address
var msg = JSON.stringify(paymentSubscriptionObj);
currentAddressSocket.onopen = function (event) {
currentAddressSocket.send(msg);
};
// listen for response
currentAddressSocket.onmessage = function (event) {
//console.log("message received:" + event.data);
receivedPayment(event.data);
};
currentAddressSocket.onclose = function(event) {
if (event.code !== CLOSE_NORMAL) {
$log.debug('Socket was closed abnormally. Reconnect will be attempted in 1 second.');
$timeout(function() {
connectSocket();
}, 1000);
}
};
currentAddressSocket.onerror = function(err) {
console.error('Socket encountered error: ', err, 'Closing socket');
currentAddressSocket.close();
};
}
$scope.setAddress = function(newAddr, copyAddress) {
$scope.addr = null;
if (!$scope.wallet || $scope.generatingAddress || !$scope.wallet.isComplete()) return;
@ -36,49 +80,24 @@ angular.module('copayApp.controllers').controller('tabReceiveController', functi
popupService.showAlert(err);
}
//close existing socket
if (currentAddressSocket.close === 'function') {
currentAddressSocket.close();
}
if ($scope.wallet.coin == 'bch') {
bchAddresses = bitcoinCashJsService.translateAddresses(addr);
$scope.addr = bchAddresses[$scope.bchAddressType.type];
$scope.addrBchLegacy = bchAddresses['legacy'];
// listen to bch address
currentAddressSocket = new WebSocket("wss://ws.blockchain.info/bch/inv");
paymentSubscriptionObj.addr = bchAddresses['legacy'];
if ($scope.wallet.coin === 'bch') {
bchAddresses = bitcoinCashJsService.translateAddresses(addr);
$scope.addr = bchAddresses[$scope.bchAddressType.type];
$scope.addrBchLegacy = bchAddresses['legacy'];
} else {
$scope.addr = addr;
// listen to btc address
currentAddressSocket = new WebSocket("wss://ws.blockchain.info/inv");
paymentSubscriptionObj.addr = $scope.addr
$scope.addr = addr;
}
connectSocket();
if (copyAddress === true) {
try {
clipboardService.copyToClipboard($scope.wallet.coin == 'bch' && $scope.bchAddressType.type == 'cashaddr' ? 'bitcoincash:' + $scope.addr : $scope.addr);
} catch (error) {
$log.debug("Error copying to clipboard:");
$log.debug('Error copying to clipboard:');
$log.debug(error);
}
}
// create subscription
var msg = JSON.stringify(paymentSubscriptionObj);
currentAddressSocket.onopen = function (event) {
//console.log("message sent: " + msg);
currentAddressSocket.send(msg);
}
// listen for response
currentAddressSocket.onmessage = function (event) {
//console.log("message received:" + event.data);
receivedPayment(event.data);
}
$timeout(function() {
$scope.$apply();
@ -164,7 +183,6 @@ angular.module('copayApp.controllers').controller('tabReceiveController', functi
// Notify new tx
$scope.$emit('bwsEvent', $scope.wallet.id);
$scope.$apply(function () {
$scope.showingPaymentReceived = true;
});
@ -233,6 +251,10 @@ angular.module('copayApp.controllers').controller('tabReceiveController', functi
}
};
function onBeforeLeave() {
currentAddressSocket.close([CLOSE_NORMAL]);
}
$scope.$on("$ionicView.beforeEnter", function(event, data) {
$scope.wallets = profileService.getWallets();
$scope.singleWallet = $scope.wallets.length == 1;
@ -258,7 +280,6 @@ angular.module('copayApp.controllers').controller('tabReceiveController', functi
configService.whenAvailable(function(_config) {
$scope.displayBalanceAsFiat = _config.wallet.settings.priceDisplay === 'fiat';
config = _config;
});
});

View file

@ -1,6 +1,6 @@
'use strict';
angular.module('copayApp.controllers').controller('tabScanController', function($scope, $log, $timeout, scannerService, incomingData, $state, $ionicHistory, $rootScope, $ionicNavBarDelegate) {
angular.module('copayApp.controllers').controller('tabScanController', function(gettextCatalog, popupService, $scope, $log, $timeout, scannerService, incomingDataService, $state, $ionicHistory, $rootScope, $ionicNavBarDelegate) {
var scannerStates = {
unauthorized: 'unauthorized',
@ -111,7 +111,18 @@ angular.module('copayApp.controllers').controller('tabScanController', function(
// Sometimes (testing in Chrome, when reading QR Code) data is an object
// that has a string data.result.
contents = contents.result || contents;
incomingData.redir(contents);
incomingDataService.redir(contents, function onError(err) {
if (err) {
var title = gettextCatalog.getString('Scan Failed');
popupService.showAlert(title, err.message, function onAlertShown() {
// Enable another scan since we won't receive incomingDataMenu.menuHidden
activate();
});
} else {
scannerService.resumePreview();
}
});
}
$rootScope.$on('incomingDataMenu.menuHidden', function() {

View file

@ -1,6 +1,6 @@
'use strict';
angular.module('copayApp.controllers').controller('tabSendController', function($scope, $rootScope, $log, $timeout, $ionicScrollDelegate, $ionicLoading, addressbookService, profileService, lodash, $state, walletService, incomingData, popupService, platformInfo, sendFlowService, bwcError, gettextCatalog, scannerService, configService, bitcoinCashJsService, $ionicPopup, $ionicNavBarDelegate, clipboardService) {
angular.module('copayApp.controllers').controller('tabSendController', function(bitcoinUriService, $scope, $log, $timeout, $ionicScrollDelegate, addressbookService, profileService, lodash, $state, walletService, platformInfo, sendFlowService, gettextCatalog, configService, $ionicPopup, $ionicNavBarDelegate, clipboardService, incomingDataService) {
var clipboardHasAddress = false;
var clipboardHasContent = false;
var originalList;
@ -29,7 +29,7 @@ angular.module('copayApp.controllers').controller('tabSendController', function(
$scope.$on("$ionicView.enter", function(event, data) {
var stateParams = sendFlowService.getStateClone();
var stateParams = sendFlowService.state.getClone();
$scope.fromWallet = profileService.getWallet(stateParams.fromWalletId);
clipboardService.readFromClipboard(function(text) {
@ -39,7 +39,9 @@ angular.module('copayApp.controllers').controller('tabSendController', function(
$scope.clipboardHasAddress = false;
$scope.clipboardHasContent = false;
if ((text.indexOf('bitcoincash:') === 0 || text[0] === 'C' || text[0] === 'H' || text[0] === 'p' || text[0] === 'q') && text.replace('bitcoincash:', '').length === 42) { // CashAddr
var parsed = bitcoinUriService.parse(text);
console.log('parsed', parsed);
if (parsed.isValid && parsed.publicAddress && parsed.coin === 'bch' && !parsed.testnet) { // CashAddr
$scope.clipboardHasAddress = true;
} else if ((text[0] === "1" || text[0] === "3" || text.substring(0, 3) === "bc1") && text.length >= 26 && text.length <= 35) { // Legacy Addresses
$scope.clipboardHasAddress = true;
@ -60,11 +62,6 @@ angular.module('copayApp.controllers').controller('tabSendController', function(
});
$scope.findContact = function(search) {
if (incomingData.redir(search)) {
return;
}
if (!search || search.length < 1) {
$scope.list = originalList;
$timeout(function() {
@ -73,12 +70,16 @@ angular.module('copayApp.controllers').controller('tabSendController', function(
return;
}
var result = lodash.filter(originalList, function(item) {
var val = item.name;
return lodash.startsWith(val.toLowerCase(), search.toLowerCase());
var params = sendFlowService.state.getClone();
params.data = search;
sendFlowService.start(params, function onError() {
var result = lodash.filter(originalList, function(item) {
var val = item.name;
return lodash.startsWith(val.toLowerCase(), search.toLowerCase());
});
$scope.list = result;
});
$scope.list = result;
};
var hasWallets = function() {
@ -184,27 +185,18 @@ angular.module('copayApp.controllers').controller('tabSendController', function(
$log.debug('Got toAddress:' + toAddress + ' | ' + item.name);
var stateParams = sendFlowService.getStateClone();
stateParams.toAddress = toAddress,
var stateParams = sendFlowService.state.getClone();
stateParams.toAddress = toAddress;
stateParams.coin = item.coin;
sendFlowService.pushState(stateParams);
if (!stateParams.fromWalletId) { // If we have no toAddress or fromWallet
$state.transitionTo('tabs.send.origin');
} else {
$state.transitionTo('tabs.send.amount');
}
sendFlowService.start(stateParams);
});
};
$scope.startWalletToWalletTransfer = function() {
console.log('startWalletToWalletTransfer()');
var params = sendFlowService.getStateClone();
sendFlowService.pushState(params);
$state.transitionTo('tabs.send.wallet-to-wallet', {
fromWalletId: sendFlowService.fromWalletId
});
var params = sendFlowService.state.getClone();
params.isWalletTransfer = true;
sendFlowService.start(params);
}
// This could probably be enhanced refactoring the routes abstract states
@ -238,7 +230,7 @@ angular.module('copayApp.controllers').controller('tabSendController', function(
});
if (data.direction == "back") {
sendFlowService.clear();
sendFlowService.state.clear();
}
});

View file

@ -1,11 +1,13 @@
'use strict';
angular.module('copayApp.controllers').controller('tabsController', function($rootScope, $log, $scope, $state, $stateParams, $timeout, platformInfo, incomingData, lodash, popupService, gettextCatalog, scannerService, sendFlowService) {
angular.module('copayApp.controllers').controller('tabsController', function($rootScope, $log, $scope, $state, $stateParams, $timeout, platformInfo, incomingDataService, lodash, popupService, gettextCatalog, scannerService, sendFlowService) {
$scope.onScan = function(data) {
if (!incomingData.redir(data)) {
popupService.showAlert(gettextCatalog.getString('Error'), gettextCatalog.getString('Invalid data'));
}
incomingDataService.redir(data, function onError(err) {
if (err) {
popupService.showAlert(gettextCatalog.getString('Error'), err.message);
}
});
};
$scope.setScanFn = function(scanFn) {
@ -16,8 +18,7 @@ angular.module('copayApp.controllers').controller('tabsController', function($ro
};
$scope.startFreshSend = function() {
sendFlowService.clear();
$state.go('tabs.send');
sendFlowService.start();
};
$scope.importInit = function() {
@ -28,7 +29,6 @@ angular.module('copayApp.controllers').controller('tabsController', function($ro
};
$scope.chooseScanner = function() {
sendFlowService.clear();
var isWindowsPhoneApp = platformInfo.isCordova && platformInfo.isWP;
if (!isWindowsPhoneApp) {
@ -38,10 +38,14 @@ angular.module('copayApp.controllers').controller('tabsController', function($ro
scannerService.useOldScanner(function(err, contents) {
if (err) {
popupService.showAlert(gettextCatalog.getString('Error'), err);
return;
popupService.showAlert(gettextCatalog.getString('Error'), err.message);
} else {
incomingDataService.redir(contents, function onError(err) {
if (err) {
popupService.showAlert(gettextCatalog.getString('Error'), err.message);
}
});
}
incomingData.redir(contents);
});
};

View file

@ -1,16 +1,52 @@
'use strict';
angular.module('copayApp.controllers').controller('walletDetailsController', function($scope, $rootScope, $interval, $timeout, $filter, $log, $ionicModal, $ionicPopover, $state, $stateParams, $ionicHistory, profileService, lodash, configService, platformInfo, walletService, txpModalService, externalLinkService, popupService, addressbookService, sendFlowService, storageService, $ionicScrollDelegate, $window, bwcError, gettextCatalog, timeService, feeService, appConfigService, rateService) {
var HISTORY_SHOW_LIMIT = 10;
var currentTxHistoryPage = 0;
angular.module('copayApp.controllers').controller('walletDetailsController', function($scope, $rootScope, $interval, $timeout, $filter, $log, $ionicModal, $ionicPopover, $state, $stateParams, $ionicHistory, profileService, lodash, configService, platformInfo, walletService, txpModalService, externalLinkService, popupService, addressbookService, sendFlowService, storageService, $ionicScrollDelegate, $window, bwcError, gettextCatalog, timeService, feeService, appConfigService, rateService, walletHistoryService) {
// Desktop can display 13 rows of transactions, bump it up to a nice round 15.
var DISPLAY_PAGE_SIZE = 15;
var currentTxHistoryDisplayPage = 0;
var completeTxHistory = []
var listeners = [];
$scope.txps = [];
$scope.completeTxHistory = [];
$scope.openTxpModal = txpModalService.open;
// For gradual migration for doing it properly
$scope.vm = {
allowInfiniteScroll: false,
gettingCachedHistory: true,
gettingInitialHistory: true,
updatingTxHistory: false,
fetchedAllTxHistory: false,
//updateTxHistoryError: false
updateTxHistoryFailed: false
};
// Need flag for when to allow infinite scroll at bottom
// - ie not when loading initial data and there is no more cached data
$scope.amountIsCollapsible = false;
$scope.color = '#888888';
$scope.filteredTxHistory = [];
$scope.isCordova = platformInfo.isCordova;
$scope.isAndroid = platformInfo.isAndroid;
$scope.isIOS = platformInfo.isIOS;
$scope.isSearching = false;
$scope.openTxpModal = txpModalService.open;
$scope.requiresMultipleSignatures = false;
$scope.showBalanceButton = false;
$scope.status = null;
// Displaying 50 transactions when entering the screen takes a while, so only display a subset
// of everything we have, not the complete history.
$scope.txHistory = []; // This is what is displayed
$scope.txHistorySearchResults = [];
$scope.txps = [];
$scope.updatingStatus = false;
$scope.updateStatusError = null;
$scope.updatingTxHistoryProgress = 0;
$scope.wallet = null;
$scope.walletId = '';
$scope.walletNotRegistered = false;
var channel = "ga";
if (platformInfo.isCordova) {
@ -19,34 +55,11 @@ angular.module('copayApp.controllers').controller('walletDetailsController', fun
var log = new window.BitAnalytics.LogEvent("wallet_details_open", [], [channel]);
window.BitAnalytics.LogEventHandlers.postEvent(log);
$scope.amountIsCollapsible = !$scope.isAndroid;
$scope.openExternalLink = function(url, target) {
externalLinkService.open(url, target);
};
var setPendingTxps = function(txps) {
/* Uncomment to test multiple outputs */
// var txp = {
// message: 'test multi-output',
// fee: 1000,
// createdOn: new Date() / 1000,
// outputs: [],
// wallet: $scope.wallet
// };
//
// function addOutput(n) {
// txp.outputs.push({
// amount: 600,
// toAddress: '2N8bhEwbKtMvR2jqMRcTCQqzHP6zXGToXcK',
// message: 'output #' + (Number(n) + 1)
// });
// };
// lodash.times(15, addOutput);
// txps.push(txp);
if (!txps) {
$scope.txps = [];
return;
@ -73,6 +86,7 @@ angular.module('copayApp.controllers').controller('walletDetailsController', fun
$scope.updatingStatus = true;
$scope.updateStatusError = null;
$scope.walletNotRegistered = false;
$scope.vm.fetchedAllTxHistory = false;
walletService.getStatus($scope.wallet, {
force: !!force,
@ -156,68 +170,97 @@ angular.module('copayApp.controllers').controller('walletDetailsController', fun
if (err) return;
$timeout(function() {
walletService.startScan($scope.wallet, function() {
$scope.updateAll();
$scope.updateAll(true, true);
$scope.$apply();
});
});
});
};
var updateTxHistory = function(cb) {
if (!cb) cb = function() {};
$scope.updateTxHistoryError = false;
$scope.updatingTxHistoryProgress = 0;
feeService.getFeeLevels($scope.wallet.coin, function(err, levels) {
walletService.getTxHistory($scope.wallet, {
feeLevels: levels
}, function(err, txHistory) {
$scope.updatingTxHistory = false;
if (err) {
$scope.txHistory = null;
$scope.updateTxHistoryError = true;
return;
}
applyCurrencyAliases(txHistory);
var config = configService.getSync();
var fiatCode = config.wallet.settings.alternativeIsoCode;
lodash.each(txHistory, function(t) {
var r = rateService.toFiat(t.amount, fiatCode, $scope.wallet.coin);
t.alternativeAmountStr = r.toFixed(2) + ' ' + fiatCode;
});
$scope.completeTxHistory = txHistory;
$scope.showHistory();
$timeout(function() {
$scope.$apply();
});
return cb();
});
});
};
function applyCurrencyAliases(txHistory) {
var defaults = configService.getDefaults();
var configCache = configService.getSync();
lodash.each(txHistory, function(t) {
t.amountUnitStr = $scope.wallet.coin == 'btc'
lodash.each(txHistory, function onTx(tx) {
tx.amountUnitStr = $scope.wallet.coin == 'btc'
? (configCache.bitcoinAlias || defaults.bitcoinAlias)
: (configCache.bitcoinCashAlias || defaults.bitcoinCashAlias);
t.amountUnitStr = t.amountUnitStr.toUpperCase();
tx.amountUnitStr = tx.amountUnitStr.toUpperCase();
});
}
$scope.showHistory = function() {
if ($scope.completeTxHistory) {
$scope.txHistory = $scope.completeTxHistory.slice(0, (currentTxHistoryPage + 1) * HISTORY_SHOW_LIMIT);
$scope.txHistoryShowMore = $scope.completeTxHistory.length > $scope.txHistory.length;
function formatTxHistoryForDisplay(txHistory) {
applyCurrencyAliases(txHistory);
var config = configService.getSync();
var fiatCode = config.wallet.settings.alternativeIsoCode;
lodash.each(txHistory, function(t) {
var r = rateService.toFiat(t.amount, fiatCode, $scope.wallet.coin);
t.alternativeAmountStr = r.toFixed(2) + ' ' + fiatCode;
});
}
function updateTxHistoryFromCachedData() {
$scope.vm.gettingCachedHistory = true;
walletHistoryService.getCachedTxHistory($scope.wallet.id, function onGetCachedTxHistory(err, txHistory){
$scope.vm.gettingCachedHistory = false;
if (err) {
// Don't display an error because we are also requesting the history.
$log.error('Error getting cached tx history.', err);
return;
}
if (!txHistory) {
$log.debug('No cached tx history.');
return;
}
formatTxHistoryForDisplay(txHistory);
completeTxHistory = txHistory;
showHistory(false);
$scope.$apply();
});
}
function fetchAndShowTxHistory(getLatest, flushCacheOnNew) {
$scope.vm.updatingTxHistory = true;
walletHistoryService.updateLocalTxHistoryByPage($scope.wallet, getLatest, flushCacheOnNew, function onUpdateLocalTxHistoryByPage(err, txHistory, fetchedAllTransactions) {
$scope.vm.gettingInitialHistory = false;
$scope.vm.updatingTxHistory = false;
$scope.$broadcast('scroll.infiniteScrollComplete');
if (err) {
console.error('pagination Failed to get history.', err);
$scope.vm.updateTxHistoryFailed = true;
return;
}
if (fetchedAllTransactions) {
$scope.vm.fetchedAllTxHistory = true;
}
formatTxHistoryForDisplay(txHistory);
completeTxHistory = txHistory;
showHistory(true);
$scope.$apply();
});
}
function showHistory(showAll) {
if (completeTxHistory) {
$scope.txHistory = showAll ? completeTxHistory : completeTxHistory.slice(0, (currentTxHistoryDisplayPage + 1) * DISPLAY_PAGE_SIZE);
$scope.vm.allowInfiniteScroll = !$scope.vm.fetchedAllTxHistory && !(completeTxHistory.length === $scope.txHistory.length && $scope.vm.gettingInitialHistory);
} else {
$scope.vm.allowInfiniteScroll = false;
}
};
}
$scope.getDate = function(txCreated) {
var date = new Date(txCreated * 1000);
@ -256,24 +299,35 @@ angular.module('copayApp.controllers').controller('walletDetailsController', fun
return !tx.confirmations || tx.confirmations === 0;
};
// on-infinite="showMore()"
$scope.showMore = function() {
$timeout(function() {
currentTxHistoryPage++;
$scope.showHistory();
// Check if we have more than we are displaying
if (completeTxHistory.length > $scope.txHistory.length) {
currentTxHistoryDisplayPage++;
showHistory(false);
$scope.$broadcast('scroll.infiniteScrollComplete');
}, 100);
return;
}
if ($scope.vm.updatingTxHistory) {
return;
}
fetchAndShowTxHistory(false, false);
};
// on-refresh="onRefresh()"
$scope.onRefresh = function() {
$timeout(function() {
$scope.$broadcast('scroll.refreshComplete');
}, 300);
$scope.updateAll(true);
$scope.updateAll(true, false);
};
$scope.updateAll = function(force, cb)  {
updateStatus(force);
updateTxHistory(cb);
$scope.updateAll = function(forceStatusUpdate, flushTxCacheOnNew)  {
updateStatus(forceStatusUpdate);
//updateTxHistory(cb);
fetchAndShowTxHistory(true, flushTxCacheOnNew);
};
$scope.hideToggle = function() {
@ -283,103 +337,36 @@ angular.module('copayApp.controllers').controller('walletDetailsController', fun
};
var prevPos;
$scope.txHistoryPaddingBottom = 0;
function getScrollPosition() {
var scrollPosition = $ionicScrollDelegate.getScrollPosition();
$timeout(function() {
getScrollPosition();
}, 200);
if (!scrollPosition) {
$window.requestAnimationFrame(function() {
getScrollPosition();
});
return;
}
var pos = scrollPosition.top;
if (pos > 0) {
$scope.txHistoryPaddingBottom = "200px";
}
if (pos === prevPos) {
$window.requestAnimationFrame(function() {
getScrollPosition();
});
return;
}
prevPos = pos;
refreshAmountSection(pos);
};
function refreshAmountSection(scrollPos) {
var AMOUNT_HEIGHT_BASE = 210;
$scope.showBalanceButton = false;
if ($scope.status) {
$scope.showBalanceButton = ($scope.status.totalBalanceSat != $scope.status.spendableAmount);
if ($scope.showBalanceButton) {
AMOUNT_HEIGHT_BASE = 270;
}
}
if (!$scope.amountIsCollapsible) {
var t = ($scope.showBalanceButton ? 15 : 45);
$scope.amountScale = 'translateY(' + t + 'px)';
return;
}
scrollPos = scrollPos || 0;
var amountHeight = AMOUNT_HEIGHT_BASE - scrollPos;
if (amountHeight < 80) {
amountHeight = 80;
}
var contentMargin = amountHeight;
if (contentMargin > AMOUNT_HEIGHT_BASE) {
contentMargin = AMOUNT_HEIGHT_BASE;
}
var amountScale = (amountHeight / AMOUNT_HEIGHT_BASE);
if (amountScale < 0.5) {
amountScale = 0.5;
}
if (amountScale > 1.1) {
amountScale = 1.1;
}
var s = amountScale;
// Make space for the balance button when it needs to display.
var TOP_NO_BALANCE_BUTTON = 115;
var TOP_BALANCE_BUTTON = 30;
var top = TOP_NO_BALANCE_BUTTON;
if ($scope.showBalanceButton) {
top = TOP_BALANCE_BUTTON;
}
var amountTop = ((amountScale - 0.80) / 0.80) * top;
if (amountTop < -2) {
amountTop = -2;
}
if (amountTop > top) {
amountTop = top;
}
var t = amountTop;
$scope.altAmountOpacity = (amountHeight - 100) / 80;
$scope.buttonsOpacity = (amountHeight - 140) / 70;
$window.requestAnimationFrame(function() {
$scope.amountHeight = amountHeight + 'px';
$scope.contentMargin = contentMargin + 'px';
$scope.amountScale = 'scale3d(' + s + ',' + s + ',' + s + ') translateY(' + t + 'px)';
$scope.$digest();
getScrollPosition();
});
$scope.scrollPosition = pos;
}
var scrollWatcherInitialized;
$scope.$on("$ionicView.enter", function(event, data) {
if ($scope.isCordova && $scope.isAndroid) setAndroidStatusBarColor();
if (scrollWatcherInitialized || !$scope.amountIsCollapsible) {
return;
}
scrollWatcherInitialized = true;
});
$scope.$on("$ionicView.beforeEnter", function(event, data) {
sendFlowService.clear();
configService.whenAvailable(function (config) {
$scope.selectedPriceDisplay = config.wallet.settings.priceDisplay;
@ -393,7 +380,7 @@ angular.module('copayApp.controllers').controller('walletDetailsController', fun
if (!$scope.wallet) return;
$scope.requiresMultipleSignatures = $scope.wallet.credentials.m > 1;
$scope.updatingTxHistory = true;
$scope.vm.gettingInitialHistory = true;
addressbookService.list(function(err, ab) {
if (err) $log.error(err);
@ -403,21 +390,25 @@ angular.module('copayApp.controllers').controller('walletDetailsController', fun
listeners = [
$rootScope.$on('bwsEvent', function(e, walletId) {
if (walletId == $scope.wallet.id && e.type != 'NewAddress')
$scope.updateAll();
$scope.updateAll(false, false);
}),
$rootScope.$on('Local/TxAction', function(e, walletId) {
if (walletId == $scope.wallet.id)
$scope.updateAll();
$scope.updateAll(false, false);
}),
];
});
var refreshInterval;
$scope.$on("$ionicView.afterEnter", function(event, data) {
$scope.updateAll();
refreshAmountSection();
$scope.$on("$ionicView.afterEnter", function onAfterEnter(event, data) {
updateTxHistoryFromCachedData();
$scope.updateAll(true, true);
// refreshAmountSection();
refreshInterval = $interval($scope.onRefresh, 10 * 1000);
$timeout(function() {
getScrollPosition();
}, 1000);
});
$scope.$on("$ionicView.afterLeave", function(event, data) {
@ -477,16 +468,10 @@ angular.module('copayApp.controllers').controller('walletDetailsController', fun
}
$scope.goToSend = function() {
sendFlowService.startSend({
sendFlowService.start({
fromWalletId: $scope.wallet.id
});
// Go home first so that the Home tab works properly
$state.go('tabs.home').then(function () {
$ionicHistory.clearHistory();
$state.go('tabs.send');
});
};
$scope.goToReceive = function() {
$state.go('tabs.home', {

View file

@ -0,0 +1,204 @@
'use strict';
(function () {
angular
.module('copayApp.controllers')
.controller('walletSelectorController', walletSelectorController);
function walletSelectorController ($scope, $state, sendFlowService, configService, gettextCatalog, ongoingProcess, profileService, walletService, txFormatService) {
var fromWalletId = '';
var priceDisplayAsFiat = false;
var unitDecimals = 0;
var unitsFromSatoshis = 0;
$scope.$on("$ionicView.beforeEnter", onBeforeEnter);
$scope.$on("$ionicView.enter", onEnter);
function onBeforeEnter(event, data) {
if (data.direction == "back") {
sendFlowService.state.pop();
}
$scope.params = sendFlowService.state.getClone();
console.log('walletSelector onBeforeEnter after back sendflow', $scope.params);
var config = configService.getSync().wallet.settings;
priceDisplayAsFiat = config.priceDisplay === 'fiat';
unitDecimals = config.unitDecimals;
unitsFromSatoshis = 1 / config.unitToSatoshi;
if ($scope.params.isWalletTransfer) {
$scope.sendFlowTitle = gettextCatalog.getString('Transfer between wallets');
} else if (!$scope.params.thirdParty) {
$scope.sendFlowTitle = gettextCatalog.getString('Send');
}
$scope.coin = false; // Wallets to show (for destination screen or contacts)
$scope.type = $scope.params['fromWalletId'] ? 'destination' : 'origin'; // origin || destination
fromWalletId = $scope.params['fromWalletId'];
if ($scope.type === 'destination' && $scope.params.toAddress) {
$state.transitionTo(getNextStep($scope.params));
}
if ($scope.params.coin) {
$scope.coin = $scope.params.coin; // Contacts have a coin embedded
}
if ($scope.params.amount) { // There is an amount, so presume that it is a payment request
$scope.sendFlowTitle = gettextCatalog.getString('Payment Request');
$scope.specificAmount = $scope.specificAlternativeAmount = '';
$scope.isPaymentRequest = true;
}
if ($scope.params.thirdParty) {
$scope.thirdParty = $scope.params.thirdParty;
}
};
function onEnter (event, data) {
configService.whenAvailable(function(config) {
$scope.selectedPriceDisplay = config.wallet.settings.priceDisplay;
});
if ($scope.thirdParty) {
// Third party services specific logic
handleThirdPartyIfShapeshift();
}
prepareWalletLists();
formatRequestedAmount();
};
function formatRequestedAmount() {
if ($scope.params.amount) {
var cryptoAmount = (unitsFromSatoshis * $scope.params.amount).toFixed(unitDecimals);
var cryptoCoin = $scope.coin.toUpperCase();
txFormatService.formatAlternativeStr($scope.coin, $scope.params.amount, function onFormatAlternativeStr(formatted){
if (formatted) {
var fiatParts = formatted.split(' ');
var fiatAmount = fiatParts[0];
var fiatCurrrency = fiatParts.length > 1 ? fiatParts[1] : '';
if (priceDisplayAsFiat) {
$scope.requestAmount = fiatAmount;
$scope.requestCurrency = fiatCurrrency;
$scope.requestAmountSecondary = cryptoAmount;
$scope.requestCurrencySecondary = cryptoCoin;
} else {
$scope.requestAmount = cryptoAmount;
$scope.requestCurrency = cryptoCoin;
$scope.requestAmountSecondary = fiatAmount;
$scope.requestCurrencySecondary = fiatCurrrency;
}
$scope.$apply();
}
});
}
}
function handleThirdPartyIfShapeshift() {
console.log($scope.thirdParty, $scope.coin);
if ($scope.thirdParty.id === 'shapeshift' && $scope.type === 'destination') { // Shapeshift wants to know the
$scope.coin = profileService.getWallet(fromWalletId).coin;
if ($scope.coin === 'bch') {
$scope.coin = 'btc';
} else {
$scope.coin = 'bch';
}
}
}
function prepareWalletLists() {
var walletsAll = [];
var walletsSufficientFunds = [];
$scope.walletsInsufficientFunds = []; // For origin screen
if ($scope.type === 'origin') {
$scope.headerTitle = gettextCatalog.getString('Choose a wallet to send from');
if ($scope.params.amount || $scope.coin) {
walletsAll = profileService.getWallets({coin: $scope.coin});
ongoingProcess.set('scanning', true);
walletsAll.forEach(function forWallet(wallet) {
if (!wallet.status && !wallet.cachedStatus) {
walletService.getStatus(wallet, {}, function(err, status) {
wallet.status = status;
if (status.availableBalanceSat > ($scope.params.amount ? $scope.params.amount : 0)) {
walletsSufficientFunds.push(wallet);
} else {
$scope.walletsInsufficientFunds.push(wallet);
}
if ($scope.coin === 'btc') { // As this is a promise
$scope.walletsBtc = walletsSufficientFunds;
} else {
$scope.walletsBch = walletsSufficientFunds;
}
ongoingProcess.set('scanning', false);
});
} else {
var walletStatus = null;
if (wallet.status && wallet.status.isValid) {
walletStatus = wallet.status;
} else if (wallet.cachedStatus && wallet.status.isValid) {
walletStatus = wallet.cachedStatus;
}
if (walletStatus && walletStatus.availableBalanceSat > ($scope.params.amount ? $scope.params.amount : 0)) {
walletsSufficientFunds.push(wallet);
} else {
$scope.walletsInsufficientFunds.push(wallet);
}
ongoingProcess.set('scanning', false);
}
});
if ($scope.coin === 'btc') {
$scope.walletsBtc = walletsSufficientFunds;
} else {
$scope.walletsBch = walletsSufficientFunds;
}
} else {
$scope.walletsBch = profileService.getWallets({coin: 'bch', hasFunds: true});
$scope.walletsBtc = profileService.getWallets({coin: 'btc', hasFunds: true});
$scope.walletsInsufficientFunds = profileService.getWallets({coin: $scope.coin, hasNoFunds: true});
}
} else if ($scope.type === 'destination') {
if (!$scope.coin) { // Allow for the coin to be set by a third party
$scope.fromWallet = profileService.getWallet(fromWalletId);
$scope.coin = $scope.fromWallet.coin; // Only show wallets with the select origin wallet coin
}
$scope.headerTitle = gettextCatalog.getString('Choose a wallet to send to');
if ($scope.coin === 'btc') { // if no specific coin is set or coin is set btc
$scope.walletsBtc = profileService.getWallets({coin: $scope.coin});
} else {
$scope.walletsBch = profileService.getWallets({coin: $scope.coin});
}
}
}
$scope.useWallet = function(wallet) {
var params = sendFlowService.state.getClone();
if ($scope.type === 'origin') { // we're on the origin screen, set wallet to send from
params.fromWalletId = wallet.id;
} else { // we're on the destination screen, set wallet to send to
params.toWalletId = wallet.id;
}
sendFlowService.goNext(params);
};
$scope.goBack = function() {
sendFlowService.router.goBack();
}
}
})();

View file

@ -1,210 +0,0 @@
'use strict';
angular.module('copayApp.controllers').controller('walletSelectorController', function($scope, $rootScope, $state, $log, $ionicHistory, sendFlowService, configService, gettextCatalog, profileService, txFormatService) {
var fromWalletId = '';
var priceDisplayAsFiat = false;
var unitDecimals = 0;
var unitsFromSatoshis = 0;
$scope.$on("$ionicView.beforeEnter", onBeforeEnter);
$scope.$on("$ionicView.enter", onEnter);
function onBeforeEnter(event, data) {
if (data.direction == "back") {
sendFlowService.popState();
}
console.log('walletSelector onBeforeEnter after back sendflow', sendFlowService.state);
$scope.params = sendFlowService.getStateClone();
var config = configService.getSync().wallet.settings;
priceDisplayAsFiat = config.priceDisplay === 'fiat';
unitDecimals = config.unitDecimals;
unitsFromSatoshis = 1 / config.unitToSatoshi;
switch($state.current.name) {
case 'tabs.send.wallet-to-wallet':
$scope.sendFlowTitle = gettextCatalog.getString('Transfer between wallets');
break;
case 'tabs.send.destination':
if ($scope.params.fromWalletId && !$scope.params.thirdParty) {
$scope.sendFlowTitle = gettextCatalog.getString('Transfer between wallets');
}
break;
default:
if (!$scope.params.thirdParty) {
$scope.sendFlowTitle = gettextCatalog.getString('Send');
}
// nop
}
$scope.coin = false; // Wallets to show (for destination screen or contacts)
$scope.type = $scope.params['fromWalletId'] ? 'destination' : 'origin'; // origin || destination
fromWalletId = $scope.params['fromWalletId'];
if ($scope.type === 'destination' && $scope.params.toAddress) {
$state.transitionTo(getNextStep($scope.params));
}
if ($scope.params.coin) {
$scope.coin = $scope.params.coin; // Contacts have a coin embedded
}
if ($scope.params.amount) { // There is an amount, so presume that it is a payment request
$scope.sendFlowTitle = gettextCatalog.getString('Payment Request');
$scope.specificAmount = $scope.specificAlternativeAmount = '';
$scope.isPaymentRequest = true;
}
if ($scope.params.thirdParty) {
$scope.thirdParty = $scope.params.thirdParty;
}
};
function onEnter (event, data) {
configService.whenAvailable(function(config) {
$scope.selectedPriceDisplay = config.wallet.settings.priceDisplay;
});
if ($scope.thirdParty) {
// Third party services specific logic
handleThirdPartyIfShapeshift();
}
prepareWalletLists();
formatRequestedAmount();
};
function formatRequestedAmount() {
if ($scope.params.amount) {
var cryptoAmount = (unitsFromSatoshis * $scope.params.amount).toFixed(unitDecimals);
var cryptoCoin = $scope.coin.toUpperCase();
txFormatService.formatAlternativeStr($scope.coin, $scope.params.amount, function onFormatAlternativeStr(formatted){
if (formatted) {
var fiatParts = formatted.split(' ');
var fiatAmount = fiatParts[0];
var fiatCurrrency = fiatParts.length > 1 ? fiatParts[1] : '';
if (priceDisplayAsFiat) {
$scope.requestAmount = fiatAmount;
$scope.requestCurrency = fiatCurrrency;
$scope.requestAmountSecondary = cryptoAmount;
$scope.requestCurrencySecondary = cryptoCoin;
} else {
$scope.requestAmount = cryptoAmount;
$scope.requestCurrency = cryptoCoin;
$scope.requestAmountSecondary = fiatAmount;
$scope.requestCurrencySecondary = fiatCurrrency;
}
}
});
}
}
function getNextStep(params) {
if (!params.toWalletId && !params.toAddress) { // If we have no toAddress or fromWallet
return 'tabs.send.destination';
} else if (!params.amount) { // If we have no amount
return 'tabs.send.amount';
} else { // If we do have them
return 'tabs.send.review';
}
}
function handleThirdPartyIfShapeshift() {
console.log($scope.thirdParty, $scope.coin);
if ($scope.thirdParty.id === 'shapeshift' && $scope.type === 'destination') { // Shapeshift wants to know the
$scope.coin = profileService.getWallet(fromWalletId).coin;
if ($scope.coin === 'bch') {
$scope.coin = 'btc';
} else {
$scope.coin = 'bch';
}
}
}
function prepareWalletLists() {
var walletsAll = [];
var walletsSufficientFunds = [];
$scope.walletsInsufficientFunds = []; // For origin screen
if ($scope.type === 'origin') {
$scope.headerTitle = gettextCatalog.getString('Choose a wallet to send from');
if ($scope.params.amount) {
walletsAll = profileService.getWallets({coin: $scope.coin});
walletsAll.forEach(function forWallet(wallet){
if (wallet.status.availableBalanceSat > $scope.params.amount) {
walletsSufficientFunds.push(wallet);
} else {
$scope.walletsInsufficientFunds.push(wallet);
}
});
if ($scope.coin === 'btc') {
$scope.walletsBtc = walletsSufficientFunds;
} else {
$scope.walletsBch = walletsSufficientFunds;
}
} else if ($scope.coin) {
walletsAll = profileService.getWallets({coin: $scope.coin});
walletsAll.forEach(function forWallet(wallet){
if (wallet.status.availableBalanceSat > 0) {
walletsSufficientFunds.push(wallet);
} else {
$scope.walletsInsufficientFunds.push(wallet);
}
});
if ($scope.coin === 'btc') {
$scope.walletsBtc = walletsSufficientFunds;
} else {
$scope.walletsBch = walletsSufficientFunds;
}
} else {
$scope.walletsBch = profileService.getWallets({coin: 'bch', hasFunds: true});
$scope.walletsBtc = profileService.getWallets({coin: 'btc', hasFunds: true});
$scope.walletsInsufficientFunds = profileService.getWallets({coin: $scope.coin, hasNoFunds: true});
}
} else if ($scope.type === 'destination') {
if (!$scope.coin) { // Allow for the coin to be set by a third party
$scope.fromWallet = profileService.getWallet(fromWalletId);
$scope.coin = $scope.fromWallet.coin; // Only show wallets with the select origin wallet coin
}
$scope.headerTitle = gettextCatalog.getString('Choose a wallet to send to');
if ($scope.coin === 'btc') { // if no specific coin is set or coin is set btc
$scope.walletsBtc = profileService.getWallets({coin: $scope.coin});
} else {
$scope.walletsBch = profileService.getWallets({coin: $scope.coin});
}
}
}
$scope.useWallet = function(wallet) {
var params = sendFlowService.getStateClone();
if ($scope.type === 'origin') { // we're on the origin screen, set wallet to send from
params.fromWalletId = wallet.id;
} else { // we're on the destination screen, set wallet to send to
params.toWalletId = wallet.id;
}
sendFlowService.pushState(params);
var nextStep = getNextStep(params);
console.log('walletSelector nextStep', nextStep);
$state.transitionTo(nextStep, $scope.params);
};
$scope.goBack = function() {
$ionicHistory.goBack();
}
});

View file

@ -1,23 +1,28 @@
'use strict';
angular.module('copayApp.directives')
.directive('incomingDataMenu', function($timeout, $rootScope, $state, externalLinkService) {
.directive('incomingDataMenu', function($timeout, $rootScope, $state, externalLinkService, sendFlowService, bitcoinCashJsService) {
return {
restrict: 'E',
templateUrl: 'views/includes/incomingDataMenu.html',
link: function(scope, element, attrs) {
$rootScope.$on('incomingDataMenu.showMenu', function(event, data) {
$timeout(function() {
scope.data = data.data;
scope.type = data.type;
scope.showMenu = true;
scope.https = false;
scope.data = data;
if (scope.type === 'url') {
if (scope.data.indexOf('https://') === 0) {
scope.https = true;
}
if (scope.data.parsed.privateKey) {
scope.type = "privateKey";
} else if (scope.data.parsed.url) {
scope.type = "url";
} else if (scope.data.parsed.publicAddress) {
scope.type = "bitcoinAddress";
var prefix = scope.data.parsed.isTestnet ? 'bchtest:' : 'bitcoincash:';
scope.data.toAddress = (prefix + scope.data.parsed.publicAddress.cashAddr) || scope.data.parsed.publicAddress.legacy || scope.data.parsed.publicAddress.bitpay;
} else {
scope.type = "text";
}
scope.showMenu = true;
});
});
scope.hide = function() {
@ -28,18 +33,9 @@ angular.module('copayApp.directives')
externalLinkService.open(url);
};
scope.sendPaymentToAddress = function(bitcoinAddress) {
var noPrefixInAddress = 0;
if (bitcoinAddress.toLowerCase().indexOf('bitcoin') < 0) {
noPrefixInAddress = 1;
}
scope.showMenu = false;
$state.go('tabs.send').then(function() {
$timeout(function() {
$state.transitionTo('tabs.send.amount', {
toAddress: bitcoinAddress,
noPrefix: noPrefixInAddress
});
}, 50);
sendFlowService.start({
data: bitcoinAddress
});
};
scope.addToAddressBook = function(bitcoinAddress) {

View file

@ -1,6 +1,6 @@
'use strict';
angular.module('copayApp.directives').directive('shapeshiftCoinTrader', function($interval, shapeshiftApiService, profileService, incomingData, ongoingProcess) {
angular.module('copayApp.directives').directive('shapeshiftCoinTrader', function($interval, shapeshiftApiService, profileService, incomingDataService, ongoingProcess) {
return {
restrict: 'E',
transclude: true,
@ -111,7 +111,8 @@ angular.module('copayApp.directives').directive('shapeshiftCoinTrader', function
orderId: $scope.depositInfo.orderId
};
if (incomingData.redir(sendAddress, 'shapeshift', shapeshiftData)) {
// How to handle this
if (incomingDataService.redir(sendAddress, 'shapeshift', shapeshiftData)) {
ongoingProcess.set('connectingShapeshift', false);
return;
}

View file

@ -116,7 +116,8 @@ angular.module('copayApp.directives')
function getTransformStyle(translatePct) {
return {
'transform': 'translateX(' + translatePct + '%)'
'transform': 'translateX(' + translatePct + '%)',
'-webkit-transform': 'translateX(' + translatePct + '%)'
};
}

View file

@ -12,6 +12,7 @@
totalBalanceSat: '@',
// The Wallet object is sometimes not stringify()-able, so not interpolatable,
// so can't be passed to a directive.
walletCoin: '@',
walletStatus: '@',
walletCachedBalance: '@',
walletCachedBalanceUpdatedOn: '@',
@ -31,7 +32,6 @@
});
function displayCryptoBalance(walletStatus, walletCachedBalance, walletCachedBalanceUpdatedOn, walletCachedStatus) {
console.log('displayCryptoBalance()');
if (walletStatus && walletStatus.isValid && walletStatus.totalBalanceStr) {
setDisplay(walletStatus.totalBalanceStr, '');
@ -52,7 +52,7 @@
setDisplay('', '');
}
function displayFiatBalance(walletStatus, walletCachedStatus) {
function displayFiatBalance(walletStatus, walletCachedStatus, walletCoin) {
var displayAmount = '';
if (walletStatus && walletStatus.isValid && walletStatus.alternativeBalanceAvailable) {
displayAmount = walletStatus.totalBalanceAlternative + ' ' + walletStatus.alternativeIsoCode;
@ -66,7 +66,7 @@
return;
}
getFiatBalance(wallet);
getFiatBalance(walletStatus, walletCachedStatus, walletCoin);
}
function formatBalance() {
@ -94,19 +94,30 @@
}
if (displayAsFiat) {
displayFiatBalance(walletStatusObj, walletCachedStatusObj);
displayFiatBalance(walletStatusObj, walletCachedStatusObj, $scope.walletCoin);
}
}
function getFiatBalance(wallet) {
if (!(wallet.status && wallet.status.isValid)) {
$log.warn('Abandoning call to get fiat balance, because no valid wallet status.');
function getFiatBalance(walletStatus, walletCachedStatus, walletCoin) {
var totalBalanceSat = null;
if (walletStatus && walletStatus.isValid) {
totalBalanceSat = walletStatus.totalBalanceSat
} else if (walletCachedStatus && walletCachedStatus.isValid) {
totalBalanceSat = walletCachedStatus.totalBalanceSat
}
// 0 is valid
if (totalBalanceSat === null) {
$log.warn('Abandoning call to get fiat balance, because no valid wallet status (cached or otherwise).');
return;
}
txFormatService.formatAlternativeStr(wallet.coin, wallet.status.totalBalanceSat, function onFormatAlernativeStr(formatted) {
txFormatService.formatAlternativeStr(walletCoin, totalBalanceSat, function onFormatAlernativeStr(formatted) {
if (formatted) {
setDisplay(formatted, '');
} else {
$log.error('Failed to format fiat balance of wallet.');
}
});
}

View file

@ -0,0 +1,416 @@
'use strict';
// https://github.com/bitcoin/bips/blob/master/bip-0021.mediawiki
// https://github.com/bitcoin/bips/blob/master/bip-0072.mediawiki
(function(){
angular
.module('bitcoincom.services')
.factory('bitcoinUriService', bitcoinUriService);
function bitcoinUriService(bitcoinCashJsService, bwcService, $log) {
var bch = bitcoinCashJsService.getBitcoinCashJs();
var bitcore = bwcService.getBitcore();
var service = {
parse: parse
};
return service;
function bitpayAddrOnMainnet(address) {
var Address = bch.Address;
var BitpayFormat = Address.BitpayFormat;
var mainnet = bch.Networks.mainnet;
var result = null;
if (address[0] == 'C') {
try {
result = Address.fromString(address, mainnet, 'pubkeyhash', BitpayFormat);
} catch (e) {};
} else if (address[0] == 'H') {
try {
result = Address.fromString(address, mainnet, 'scripthash', BitpayFormat);
} catch (e) {};
}
return result;
}
function cashAddrOnMainnet(address) {
var Address = bch.Address;
var CashAddrFormat = Address.CashAddrFormat;
var mainnet = bch.Networks.mainnet;
var prefixed = 'bitcoincash:' + address;
var result = null;
if (address[0] == 'q') {
try {
result = Address.fromString(prefixed, mainnet, 'pubkeyhash', CashAddrFormat);
} catch (e) {};
} else if (address[0] == 'p') {
try {
result = Address.fromString(prefixed, mainnet, 'scripthash', CashAddrFormat);
} catch (e) {};
}
return result;
}
function cashAddrOnTestnet(address) {
var Address = bch.Address;
var CashAddrFormat = Address.CashAddrFormat;
var testnet = bch.Networks.testnet;
var prefixed = 'bchtest:' + address;
var result = null;
if (address[0] == 'q') {
try {
result = Address.fromString(prefixed, testnet, 'pubkeyhash', CashAddrFormat);
} catch (e) {};
} else if (address[0] == 'p') {
try {
result = Address.fromString(prefixed, testnet, 'scripthash', CashAddrFormat);
} catch (e) {};
}
return result;
}
function infoFromWalletImportText(data) {
var split = data.split('|');
// Copay seems to use extra parameter for coin.
if (split.length < 5 || split.length > 6) {
return null;
}
var type = parseInt(split[0], 10);
if (isNaN(type)) {
return null;
}
var data = split[1];
var network = split[2];
if (!(network === 'livenet' || network === 'testnet')) {
return null;
}
var isTestnet = network === 'testnet';
var derivationPath = split[3];
if (!/^m\/\d+'\/\d+'\/\d+'$/.test(derivationPath)) {
return null;
}
var hasPassphraseText = split[4];
if (!(hasPassphraseText === 'true' || hasPassphraseText === 'false')) {
return null;
}
var hasPassphrase = hasPassphraseText === 'true';
var coin; // Intentionally undefined as may not be present
if (split.length > 5) {
var coinText = split[5];
if (!(coinText === 'bch' || coinText === 'btc')) {
return null;
}
coin = coinText;
}
return {
type: type,
data: data,
isTestnet: isTestnet,
derivationPath: derivationPath,
hasPassphrase: hasPassphrase,
coin: coin
};
}
/*
For parsing:
BIP21
BIP72
returns:
{
amount: '',
amountInSatoshis: 0,
bareUrl: '',
coin: '',
copayInvitation: '',
import: { // testnet info in root, coin info in root if available
data: '',
derivationPath: '',
hasPassphrase: false,
type: 1,
},
isValid: false,
label: '',
message: '',
other: {
somethingIDontUnderstand: 'Its value'
},
privateKey: {
encrypted: '',
wif: ''
}'',
publicAddress: {
bitpay: '',
cashAddr: '',
legacy: '',
},
req: {
"req-param0": '',
"req-param1": ''
},
testnet: false,
url: '' // For BIP70
}
Only fields that are present in the data are defined in the returned object. Both privateKey and publicAddress only have 1 field defined, if they exist at all.
The exception to this is the coin property, which is determined from other data, such as the prefix or address type.
*/
function parse(data) {
var parsed = {
isValid: false
};
if (typeof data !== 'string') {
return parsed;
}
// Identify prefix
var trimmed = data.trim();
var colonSplit = /^([\w-]*):?(.*)$/.exec(trimmed);
if (!colonSplit) {
return parsed;
}
var addressAndParams = '';
var preColonLower = colonSplit[1].toLowerCase();
if (preColonLower === 'bitcoin') {
parsed.coin = 'btc';
addressAndParams = colonSplit[2].trim();
console.log('Is btc');
} else if (/^(?:bitcoincash)|(?:bitcoin-cash)$/.test(preColonLower)) {
parsed.coin = 'bch';
parsed.test = false;
addressAndParams = colonSplit[2].trim();
console.log('Is bch');
} else if (/^(?:bchtest)$/.test(preColonLower)) {
parsed.coin = 'bch';
parsed.isTestnet = true;
addressAndParams = colonSplit[2].trim();
console.log('Is bch');
} else if (colonSplit[2] === '') {
// No colon and no coin specifier.
addressAndParams = colonSplit[1].trim();
console.log('No prefix.');
} else if (/^https?$/.test(colonSplit[1])) { // Plain URL
addressAndParams = trimmed;
} else if (colonSplit[2].indexOf('|') == 0) { // Import
addressAndParams = trimmed
} else {
// Something we don't recognise
return parsed;
}
// Remove erroneous leading slashes
//var leadingSlashes = /^\/*([^\/]+(?:.*))$/.exec(addressAndParams);
var leadingSlashes = /^\/*(.*)$/.exec(addressAndParams);
if (!leadingSlashes) {
return parsed;
}
addressAndParams = leadingSlashes[1];
var questionMarkSplit = /^([^\?]*)\??([^\?]*)$/.exec(addressAndParams);
if (!questionMarkSplit) {
return parsed;
}
var address = questionMarkSplit[1];
var params = questionMarkSplit[2];
if (params.length > 0) {
var paramsSplit = params.split('&');
var others;
var req;
var paramCount = paramsSplit.length;
for(var i = 0; i < paramCount; i++) {
var param = paramsSplit[i];
var valueSplit = param.split('=');
if (valueSplit.length !== 2) {
return parsed;
}
var key = valueSplit[0];
var value = valueSplit[1];
var decodedValue = decodeURIComponent(value);
switch(key) {
case 'amount':
var amount = parseFloat(decodedValue);
if (amount) { // Checking for NaN, or no numbers at all etc. & convert to satoshi
parsed.amount = decodedValue; // Need to check if a currency is precised
parsed.amountInSatoshis = amount * 100000000
} else {
return parsed;
}
break;
case 'label':
parsed.label = decodedValue;
break;
case 'message':
parsed.message = decodedValue;
break;
case 'r':
// Could use a more comprehesive regex to test URL validity, but then how would we know
// which part of the validation it failed?
if (decodedValue.startsWith('https://')) {
parsed.url = decodedValue;
} else {
return parsed;
}
break;
default:
if (key.startsWith('req-')) {
req = req || {};
req[key] = decodedValue;
} else {
others = others || {};
others[key] = decodedValue;
}
}
};
}
parsed.others = others;
parsed.req = req;
if (address) {
var addressLowerCase = address.toLowerCase();
var copayInvitationRe = /^[0-9A-HJ-NP-Za-km-z]{70,80}$/;
//var legacyRe = /^[13][a-km-zA-HJ-NP-Z1-9]{25,34}$/;
//var legacyTestnetRe = /^[mn][a-km-zA-HJ-NP-Z1-9]{25,34}$/;
var privateKeyEncryptedRe = /^6P[1-9A-HJ-NP-Za-km-z]{56}$/;
var privateKeyForUncompressedPublicKeyRe = /^5[1-9A-HJ-NP-Za-km-z]{50}$/;
var privateKeyForUncompressedPublicKeyTestnetRe = /^9[1-9A-HJ-NP-Za-km-z]{50}$/;
var privateKeyForCompressedPublicKeyRe = /^[KL][1-9A-HJ-NP-Za-km-z]{51}$/;
var privateKeyForCompressedPublicKeyTestnetRe = /^[c][1-9A-HJ-NP-Za-km-z]{51}$/;
var urlRe = /^https?:\/\/.+/;
var bitpayAddrMainnet = bitpayAddrOnMainnet(address);
var cashAddrTestnet = cashAddrOnTestnet(addressLowerCase);
var cashAddrMainnet = cashAddrOnMainnet(addressLowerCase);
var importInfo = infoFromWalletImportText(address);
var privateKey = '';
if (parsed.isTestnet && cashAddrTestnet) {
parsed.address = addressLowerCase;
parsed.coin = 'bch';
parsed.publicAddress = {
cashAddr: addressLowerCase
};
parsed.isValid = true;
} else if (cashAddrMainnet) {
parsed.coin = 'bch';
parsed.publicAddress = {
cashAddr: addressLowerCase
};
parsed.isTestnet = false;
parsed.isValid = true;
} else if (bitcore.Address.isValid(address, 'livenet')) {
parsed.publicAddress = {
legacy: address
};
parsed.isTestnet = false;
parsed.isValid = true;
} else if (bitcore.Address.isValid(address, 'testnet')) {
parsed.publicAddress = {
legacy: address
};
parsed.isTestnet = true;
parsed.isValid = true;
} else if (bitpayAddrMainnet) {
parsed.coin = 'bch';
parsed.publicAddress = {
bitpay: address
};
parsed.isTestnet = false;
parsed.isValid = true;
} else if (copayInvitationRe.test(address) ) {
parsed.copayInvitation = address;
parsed.isValid = true;
} else if (privateKeyForUncompressedPublicKeyRe.test(address) || privateKeyForCompressedPublicKeyRe.test(address)) {
privateKey = address;
try {
new bitcore.PrivateKey(privateKey, 'livenet');
parsed.privateKey = { wif: privateKey };
parsed.isTestnet = false;
parsed.isValid = true;
} catch (e) {}
} else if (privateKeyForUncompressedPublicKeyTestnetRe.test(address) || privateKeyForCompressedPublicKeyTestnetRe.test(address)) {
privateKey = address;
try {
new bitcore.PrivateKey(privateKey, 'testnet');
parsed.privateKey = { wif: privateKey };
parsed.isTestnet = true;
parsed.isValid = true;
} catch (e) {}
} else if (privateKeyEncryptedRe.test(address)) {
parsed.privateKey = { encrypted: address };
parsed.isValid = true;
} else if (urlRe.test(address)) {
parsed.bareUrl = trimmed;
parsed.isValid = true;
} else if (importInfo) {
parsed.import = {
type: importInfo.type,
data: importInfo.data,
derivationPath: importInfo.derivationPath,
hasPassphrase: importInfo.hasPassphrase
};
parsed.coin = importInfo.coin;
parsed.isTestnet = importInfo.isTestnet;
parsed.isValid = true;
}
} else {
parsed.isValid = !!parsed.url; // BIP72
}
return parsed;
}
}
})();

View file

@ -0,0 +1,429 @@
describe('bitcoinUriService', function() {
var bitcoinUriService;
beforeEach(function() {
module('bitcoinCashJsModule');
module('bitcoincom.services');
module('bwcModule');
inject(function($injector){
bitcoinUriService = $injector.get('bitcoinUriService');
});
});
it('Bitcoin BIP72', function() {
var parsed = bitcoinUriService.parse('bitcoin:?r=https://bitpay.com/i/CwzbKP3k3JNgXJBfuoerDr');
expect(parsed.isValid).toBe(true);
expect(parsed.coin).toBe('btc');
expect(parsed.isTestnet).toBeUndefined();
expect(parsed.publicAddress).toBeUndefined();
expect(parsed.url).toBe('https://bitpay.com/i/CwzbKP3k3JNgXJBfuoerDr');
});
it('Bitcoin Cash BIP72', function() {
var parsed = bitcoinUriService.parse('bitcoincash:?r=https://bitpay.com/i/SmHdie5dvBnG5kouZzEPzu');
expect(parsed.isValid).toBe(true);
expect(parsed.coin).toBe('bch');
expect(parsed.publicAddress).toBeUndefined();
expect(parsed.isTestnet).toBeUndefined();
expect(parsed.url).toBe('https://bitpay.com/i/SmHdie5dvBnG5kouZzEPzu');
});
it('Bitcoin Cash prefix with legacy address', function() {
var parsed = bitcoinUriService.parse('bitcoincash:1G9FA9fFnHfTYxvmXeAbBD9FwzPAVMbd3j');
expect(parsed.isValid).toBe(true);
expect(parsed.coin).toBe('bch');
expect(parsed.publicAddress.legacy).toBe('1G9FA9fFnHfTYxvmXeAbBD9FwzPAVMbd3j');
expect(parsed.isTestnet).toBe(false);
});
it('Bitcoin Cash prefix with legacy address on testnet', function() {
var parsed = bitcoinUriService.parse('bitcoincash:mkDQrKfSFD441JxrD1iPBsJFExgkvrPGQn');
expect(parsed.isValid).toBe(true);
expect(parsed.coin).toBe('bch');
expect(parsed.publicAddress.legacy).toBe('mkDQrKfSFD441JxrD1iPBsJFExgkvrPGQn');
expect(parsed.isTestnet).toBe(true);
});
it('Bitcoin Cash uri with extended params', function() {
var parsed = bitcoinUriService.parse('bitcoincash:qr8v2vqnzntykakht43rqmxq8cdjzjp795fc3vsjgc?unknown=something&mystery=Melton%20probang&req-one=ichi&req-beta=Ni%20san');
expect(parsed.isValid).toBe(true);
expect(parsed.coin).toBe('bch');
expect(parsed.others.mystery).toBe('Melton probang');
expect(parsed.others.unknown).toBe('something');
expect(parsed.publicAddress.cashAddr).toBe('qr8v2vqnzntykakht43rqmxq8cdjzjp795fc3vsjgc');
expect(parsed.req['req-beta']).toBe('Ni san');
expect(parsed.req['req-one']).toBe('ichi');
expect(parsed.isTestnet).toBe(false);
});
it('Bitcoin Cash uri with invalid amount', function() {
var parsed = bitcoinUriService.parse('bitcoincash:qq0knhwj4d5zy3kdph24w6etq58vwzua6sm7lhcmuk?amount=three');
expect(parsed.isValid).toBe(false);
});
it('Bitcoin testnet address', function() {
var parsed = bitcoinUriService.parse('mtWcoToWhbtPoCby5fvs8xdBujT5GGenD4');
expect(parsed.isValid).toBe(true);
expect(parsed.coin).toBeUndefined();
expect(parsed.publicAddress.legacy).toBe('mtWcoToWhbtPoCby5fvs8xdBujT5GGenD4');
expect(parsed.isTestnet).toBe(true);
});
it('Bitcoin uri', function() {
var parsed = bitcoinUriService.parse('bitcoin:15yCdKWVKRvfXMJpPYZBqMhiGKwjKzZdLN');
expect(parsed.isValid).toBe(true);
expect(parsed.coin).toBe('btc');
expect(parsed.publicAddress.legacy).toBe('15yCdKWVKRvfXMJpPYZBqMhiGKwjKzZdLN');
expect(parsed.isTestnet).toBe(false);
});
it('Bitcoin uri with encoded label', function() {
var parsed = bitcoinUriService.parse('bitcoin:1MxudKDEBWZ1yjizUSf6htacenNtb3DWbT?label=Mr.%20Smith');
expect(parsed.isValid).toBe(true);
expect(parsed.coin).toBe('btc');
expect(parsed.label).toBe('Mr. Smith');
expect(parsed.publicAddress.legacy).toBe('1MxudKDEBWZ1yjizUSf6htacenNtb3DWbT');
expect(parsed.isTestnet).toBe(false);
});
it('Bitcoin uri with params', function() {
var parsed = bitcoinUriService.parse('bitcoin:12nCRhMDfxVnuF3uYMXv2fNxBohNmacfWu?amount=20.3&label=Luke-Jr&message=Donation%20for%20project%20xyz');
expect(parsed.isValid).toBe(true);
expect(parsed.amount).toBe('20.3');
expect(parsed.amountInSatoshis).toBe(2030000000);
expect(parsed.coin).toBe('btc');
expect(parsed.label).toBe('Luke-Jr');
expect(parsed.publicAddress.legacy).toBe('12nCRhMDfxVnuF3uYMXv2fNxBohNmacfWu');
expect(parsed.message).toBe('Donation for project xyz');
expect(parsed.isTestnet).toBe(false);
});
it('Bitcoin uri with slash', function() {
var parsed = bitcoinUriService.parse('bitcoin:/1GhpYmbRaf73AZRxDwAGr6653iZBGzdgeA');
expect(parsed.isValid).toBe(true);
expect(parsed.coin).toBe('btc');
expect(parsed.publicAddress.legacy).toBe('1GhpYmbRaf73AZRxDwAGr6653iZBGzdgeA');
expect(parsed.isTestnet).toBe(false);
});
it('Bitcoin uri with slashes', function() {
var parsed = bitcoinUriService.parse('bitcoin://18PCPhgZJjLxe9g3Q1BXLpL5aVut1fW3aX');
expect(parsed.isValid).toBe(true);
expect(parsed.coin).toBe('btc');
expect(parsed.publicAddress.legacy).toBe('18PCPhgZJjLxe9g3Q1BXLpL5aVut1fW3aX');
expect(parsed.isTestnet).toBe(false);
});
it('Bitcoin uri with space', function() {
var parsed = bitcoinUriService.parse('bitcoin: 19cPoKU5ZazY8NkLEsxK7drBqJnpGkax3d');
expect(parsed.isValid).toBe(true);
expect(parsed.coin).toBe('btc');
expect(parsed.publicAddress.legacy).toBe('19cPoKU5ZazY8NkLEsxK7drBqJnpGkax3d');
expect(parsed.isTestnet).toBe(false);
});
it('Bitpay without prefix', function() {
var parsed = bitcoinUriService.parse('CJoRov8TirekvajiimQpb5Hk95evA7H2Yz');
expect(parsed.isValid).toBe(true);
expect(parsed.coin).toBe('bch');
expect(parsed.publicAddress.bitpay).toBe('CJoRov8TirekvajiimQpb5Hk95evA7H2Yz');
expect(parsed.isTestnet).toBe(false);
});
it('legacy address', function() {
var parsed = bitcoinUriService.parse('1JXeGEu7bNEAYu6URT6dU6g1Ys6ffSAWYW');
expect(parsed.isValid).toBe(true);
expect(parsed.coin).toBeUndefined();
expect(parsed.publicAddress.legacy).toBe('1JXeGEu7bNEAYu6URT6dU6g1Ys6ffSAWYW');
expect(parsed.isTestnet).toBe(false);
});
it('cashAddr testnet with prefix', function() {
var parsed = bitcoinUriService.parse('bchtest:qpcz6pmurq9ctg5848trzz9zmuuygj4q5qam7ph3gt');
expect(parsed.isValid).toBe(true);
expect(parsed.coin).toBe('bch');
expect(parsed.publicAddress.cashAddr).toBe('qpcz6pmurq9ctg5848trzz9zmuuygj4q5qam7ph3gt');
expect(parsed.isTestnet).toBe(true);
});
it('cashAddr uppercase', function() {
var parsed = bitcoinUriService.parse('BITCOINCASH:QZZG9NMC5VX8GAP6XFATX3TWNSDN2YRMCSSULSMY44');
expect(parsed.isValid).toBe(true);
expect(parsed.coin).toBe('bch');
expect(parsed.publicAddress.cashAddr).toBe('qzzg9nmc5vx8gap6xfatx3twnsdn2yrmcssulsmy44');
expect(parsed.isTestnet).toBe(false);
});
it('cashAddr with dash', function() {
var parsed = bitcoinUriService.parse('bitcoin-cash:qpshfu3dk5s3e7zdcgdcun6xgxtra6uyxs7g580js0');
expect(parsed.isValid).toBe(true);
expect(parsed.coin).toBe('bch');
expect(parsed.publicAddress.cashAddr).toBe('qpshfu3dk5s3e7zdcgdcun6xgxtra6uyxs7g580js0');
expect(parsed.isTestnet).toBe(false);
});
it('cashAddr with prefix', function() {
var parsed = bitcoinUriService.parse('bitcoincash:qrq9p82a247lecv08ldk5p5h6ahtnjzpqcnh8yhq92');
expect(parsed.isValid).toBe(true);
expect(parsed.coin).toBe('bch');
expect(parsed.publicAddress.cashAddr).toBe('qrq9p82a247lecv08ldk5p5h6ahtnjzpqcnh8yhq92');
expect(parsed.isTestnet).toBe(false);
});
it('cashAddr with slash', function() {
var parsed = bitcoinUriService.parse('bitcoincash:/qzdectfmuw0xxztfx7mh045830dqcshj85hr44l35a');
expect(parsed.isValid).toBe(true);
expect(parsed.coin).toBe('bch');
expect(parsed.publicAddress.cashAddr).toBe('qzdectfmuw0xxztfx7mh045830dqcshj85hr44l35a');
expect(parsed.isTestnet).toBe(false);
});
it('cashAddr with slashes', function() {
var parsed = bitcoinUriService.parse('bitcoincash://qpj966w8utue75lqqq3rlgh20zkz3rmydqpq8syv9c');
expect(parsed.isValid).toBe(true);
expect(parsed.coin).toBe('bch');
expect(parsed.publicAddress.cashAddr).toBe('qpj966w8utue75lqqq3rlgh20zkz3rmydqpq8syv9c');
expect(parsed.isTestnet).toBe(false);
});
it('cashAddr with space', function() {
var parsed = bitcoinUriService.parse('bitcoincash: qpar9ldle8z6alcwgclejdhc24ha2xrg0szs5802ce');
expect(parsed.isValid).toBe(true);
expect(parsed.coin).toBe('bch');
expect(parsed.publicAddress.cashAddr).toBe('qpar9ldle8z6alcwgclejdhc24ha2xrg0szs5802ce');
expect(parsed.isTestnet).toBe(false);
});
it('cashAddr with space on testnet', function() {
var parsed = bitcoinUriService.parse('bchtest: qqjxkmtaxk4nv6w9h5ht2fjcj9c7ruh0fu7cnxsx5j');
expect(parsed.isValid).toBe(true);
expect(parsed.coin).toBe('bch');
expect(parsed.publicAddress.cashAddr).toBe('qqjxkmtaxk4nv6w9h5ht2fjcj9c7ruh0fu7cnxsx5j');
expect(parsed.isTestnet).toBe(true);
});
it('cashAddr without prefix', function() {
var parsed = bitcoinUriService.parse('qqen2y3l28dpk0dzsag8w027ds96u7z4pc0uxtl0nq');
expect(parsed.isValid).toBe(true);
expect(parsed.coin).toBe('bch');
expect(parsed.publicAddress.cashAddr).toBe('qqen2y3l28dpk0dzsag8w027ds96u7z4pc0uxtl0nq');
expect(parsed.isTestnet).toBe(false);
});
it('copay invitation', function() {
var parsed = bitcoinUriService.parse('PD5B7rEEj72st9d5nFszyuKxJP6FAGS7idVC2SMqiMxUcWVd8JifZDJw1UgjUctxefUFE3Sz6qLbch');
expect(parsed.isValid).toBe(true);
expect(parsed.copayInvitation).toBe('PD5B7rEEj72st9d5nFszyuKxJP6FAGS7idVC2SMqiMxUcWVd8JifZDJw1UgjUctxefUFE3Sz6qLbch');
});
it ('import BCH wallet no password', function() {
var parsed = bitcoinUriService.parse("1|suggest route obvious broccoli good position hidden tone history around final lobster|livenet|m/44'/0'/0'|false");
expect(parsed.isValid).toBe(true);
expect(parsed.import.type).toBe(1);
expect(parsed.import.data).toBe('suggest route obvious broccoli good position hidden tone history around final lobster');
expect(parsed.isTestnet).toBe(false);
expect(parsed.import.derivationPath).toBe("m/44'/0'/0'");
expect(parsed.import.hasPassphrase).toBe(false);
});
it ('import BCH wallet with passphrase', function() {
var parsed = bitcoinUriService.parse("1|fringe hazard all hobby trap myth fire stand sock empty soon east|livenet|m/44'/0'/0'|true");
expect(parsed.isValid).toBe(true);
expect(parsed.import.type).toBe(1);
expect(parsed.import.data).toBe('fringe hazard all hobby trap myth fire stand sock empty soon east');
expect(parsed.isTestnet).toBe(false);
expect(parsed.import.derivationPath).toBe("m/44'/0'/0'");
expect(parsed.import.hasPassphrase).toBe(true);
});
it ('import BTC wallet testnet', function() {
// From copay
var parsed = bitcoinUriService.parse("1|cat wealth column firm wet sauce tornado era feature monster click eyebrow|testnet|m/44'/1'/0'|false|btc");
expect(parsed.isValid).toBe(true);
expect(parsed.import.type).toBe(1);
expect(parsed.import.data).toBe('cat wealth column firm wet sauce tornado era feature monster click eyebrow');
expect(parsed.isTestnet).toBe(true);
expect(parsed.import.derivationPath).toBe("m/44'/1'/0'");
expect(parsed.import.hasPassphrase).toBe(false);
});
// Invalid addresses from https://github.com/bitcoincashorg/bitcoincash.org/blob/master/spec/cashaddr.md
it('invalid cashAddr style 1', function() {
var parsed = bitcoinUriService.parse('prefix:x64nx6hz');
expect(parsed.isValid).toBe(false);
});
it('invalid cashAddr style 2', function() {
var parsed = bitcoinUriService.parse('p:gpf8m4h7');
expect(parsed.isValid).toBe(false);
});
it('invalid cashAddr style 3', function() {
var parsed = bitcoinUriService.parse('bitcoincash:qpzry9x8gf2tvdw0s3jn54khce6mua7lcw20ayyn');
expect(parsed.isValid).toBe(false);
});
it('invalid cashAddr style 4', function() {
var parsed = bitcoinUriService.parse('bchtest:testnetaddress4d6njnut');
expect(parsed.isValid).toBe(false);
});
it('invalid cashAddr style 5', function() {
var parsed = bitcoinUriService.parse('bchreg:555555555555555555555555555555555555555555555udxmlmrz');
expect(parsed.isValid).toBe(false);
});
it('non-string', function() {
var parsed = bitcoinUriService.parse([1, 2, 3, 4]);
expect(parsed.isValid).toBe(false);
});
it('private key encrypted with BIP38', function() {
var parsed = bitcoinUriService.parse('6PRN5nEDmX842gsBzJryPu8Tw5kcsaQq1GPLcjVQPcEStvbFAtz11JX9pX');
expect(parsed.isValid).toBe(true);
expect(parsed.privateKey.encrypted).toBe('6PRN5nEDmX842gsBzJryPu8Tw5kcsaQq1GPLcjVQPcEStvbFAtz11JX9pX');
});
it('private key for compressed pubkey mainnet', function() {
var parsed = bitcoinUriService.parse('5HueCGU8rMjxEXxiPuD5BDku4MkFqeZyd4dZ1jvhTVqvbTLvyTJ');
expect(parsed.isValid).toBe(true);
expect(parsed.privateKey.wif).toBe('5HueCGU8rMjxEXxiPuD5BDku4MkFqeZyd4dZ1jvhTVqvbTLvyTJ');
expect(parsed.isTestnet).toBe(false);
});
it('private key for compressed pubkey mainnet with wrong checksum', function() {
var parsed = bitcoinUriService.parse('5HueCGU8rMjxEXxiPuD5BDku4MkFqeZyd4dZ1jvhTVqvbTLvyTu');
expect(parsed.isValid).toBe(false);
});
it('private key for compressed pubkey testnet', function() {
var parsed = bitcoinUriService.parse('cNJFgo1driFnPcBdBX8BrJrpxchBWXwXCvNH5SoSkdcF6JXXwHMm');
expect(parsed.isValid).toBe(true);
expect(parsed.privateKey.wif).toBe('cNJFgo1driFnPcBdBX8BrJrpxchBWXwXCvNH5SoSkdcF6JXXwHMm');
expect(parsed.isTestnet).toBe(true);
});
it('private key for compressed pubkey testnet with wrong checksum', function() {
var parsed = bitcoinUriService.parse('cNJFgo1driFnPcBdBX8BrJrpxchBWXwXCvNH5SoSkdcF6JXXwHMM');
expect(parsed.isValid).toBe(false);
});
it('private key for uncompressed pubkey mainnet', function() {
var parsed = bitcoinUriService.parse('L18V3rAhCKEioPnJ4BHLCCsaYa8eSNFrMjNQ2EdwgeAdmBSnTMwx');
expect(parsed.isValid).toBe(true);
expect(parsed.privateKey.wif).toBe('L18V3rAhCKEioPnJ4BHLCCsaYa8eSNFrMjNQ2EdwgeAdmBSnTMwx');
expect(parsed.isTestnet).toBe(false);
});
it('private key for uncompressed pubkey mainnet with wrong checksum', function() {
var parsed = bitcoinUriService.parse('L18V3rAhCKEioPnJ4BHLCCsaYa8eSNFrMjNQ2EdwgeAdmBSnTTwx');
expect(parsed.isValid).toBe(false);
});
it('private key for uncompressed pubkey testnet', function() {
var parsed = bitcoinUriService.parse('92Pg46rUhgTT7romnV7iGW6W1gbGdeezqdbJCzShkCsYNzyyNcc');
expect(parsed.isValid).toBe(true);
expect(parsed.privateKey.wif).toBe('92Pg46rUhgTT7romnV7iGW6W1gbGdeezqdbJCzShkCsYNzyyNcc');
expect(parsed.isTestnet).toBe(true);
});
it('private key for uncompressed pubkey testnet with wrong checksum', function() {
var parsed = bitcoinUriService.parse('92Pg46rUhgTT7romnV7iGW6W1gbGdeezqdbJCzShkCsYNzyyNcC');
expect(parsed.isValid).toBe(false);
});
it('URL only, http', function() {
var parsed = bitcoinUriService.parse('http://paperwallet.bitcoin.com');
expect(parsed.isValid).toBe(true);
expect(parsed.bareUrl).toBe('http://paperwallet.bitcoin.com');
});
it('URL only, https with query', function() {
var parsed = bitcoinUriService.parse('https://purse.io/?one=two&three=four');
expect(parsed.isValid).toBe(true);
expect(parsed.bareUrl).toBe('https://purse.io/?one=two&three=four');
});
});

View file

@ -85,7 +85,9 @@ angular.module('copayApp.services').factory('bitcoincomService', function(gettex
};
var register = function() {
nextStepsService.register(cashGamesItem);
if (!platformInfo.isAndroid) { // To comply with Google Play policies
nextStepsService.register(cashGamesItem);
}
nextStepsService.register(newsItem);
nextStepsService.register(poolItem);
nextStepsService.register(toolsItem);

View file

@ -18,7 +18,7 @@ angular.module('copayApp.services').factory('configService', function(storageSer
},
bwscash: {
url: 'https://bwscash.bitcoin.com/bws/api'
url: 'https://bws.freepages.dk/bws/api'
},
download: {

View file

@ -12,6 +12,7 @@ angular.module('copayApp.services').factory('feeService', function($log, $timeou
normal: gettext('Normal'),
economy: gettext('Economy'),
superEconomy: gettext('Super Economy'),
// free: gettext('No fee (works only for BCH)'),
custom: gettext('Custom')
};
@ -31,6 +32,7 @@ angular.module('copayApp.services').factory('feeService', function($log, $timeou
root.getFeeRate = function(coin, network, feeLevel, cb) {
if (feeLevel == 'custom') return cb();
// if (feeLevel == 'free') return cb(null, 0);
network = network || 'livenet';
@ -48,9 +50,9 @@ angular.module('copayApp.services').factory('feeService', function($log, $timeou
})
});
}
var feeRate = feeLevelRate.feePerKb;
if (!fromCache) $log.debug('Dynamic fee: ' + feeLevel + '/' + network + ' ' + (feeLevelRate.feePerKb / 1000).toFixed() + ' SAT/B');
return cb(null, feeRate);

View file

@ -0,0 +1,79 @@
'use strict';
/**
* incomingDataService is an intermediate to redirect either to the sendFlow
* or to import/join a wallet.
*/
angular.module('copayApp.services').factory('incomingDataService', function(bitcoinUriService, $log, $state, $rootScope, scannerService, sendFlowService, gettextCatalog) {
var root = {};
root.showMenu = function(data) {
$rootScope.$broadcast('incomingDataMenu.showMenu', data);
};
root.redir = function(data, cbError) {
var parsed = bitcoinUriService.parse(data);
console.log(parsed);
$log.debug(parsed);
if (parsed.isValid) {
if (parsed.isTestnet) {
if (cbError) {
var errorMessage = gettextCatalog.getString('Testnet is not supported.');
cbError(new Error(errorMessage));
}
} else {
scannerService.pausePreview();
/**
* Strategy for the action
*/
if (parsed.copayInvitation) {
$state.go('tabs.home').then(function() {
$state.transitionTo('tabs.add.join', {
url: data
});
});
} else if (parsed.import) {
$state.go('tabs.home').then(function() {
$state.transitionTo('tabs.add.import', {
code: data
});
});
} else if (
!parsed.isValid
|| parsed.privateKey
|| (sendFlowService.state.isEmpty() && !parsed.url && !parsed.amount)
) {
root.showMenu({
original: data,
parsed: parsed
});
} else {
var state = sendFlowService.state.getClone();
state.data = data;
sendFlowService.start(state, function onError(err) {
/**
* OnError, open the menu (link not validated)
*/
root.showMenu({
original: data,
parsed: parsed
});
});
}
}
} else {
if (cbError) {
var errorMessage = gettextCatalog.getString('Data not recognised.');
cbError(new Error(errorMessage));
}
}
};
return root;
});

View file

@ -1,475 +0,0 @@
'use strict';
angular.module('copayApp.services').factory('incomingData', function($log, $state, $timeout, $ionicHistory, bitcore, bitcoreCash, $rootScope, payproService, scannerService, sendFlowService, appConfigService, popupService, gettextCatalog, bitcoinCashJsService) {
var root = {};
root.showMenu = function(data) {
$rootScope.$broadcast('incomingDataMenu.showMenu', data);
};
root.redir = function(data, serviceId, serviceData) {
var originalAddress = null;
var noPrefixInAddress = 0;
if (data.toLowerCase().indexOf('bitcoin') < 0) {
noPrefixInAddress = 1;
}
if (typeof(data) == 'string' && !(/^bitcoin(cash)?:\?r=[\w+]/).exec(data) && (data.toLowerCase().indexOf('bitcoincash:') >= 0 || data[0] == 'q' || data[0] == 'p' || data[0] == 'C' || data[0] == 'H')) {
try {
noPrefixInAddress = 0;
if (data[0] == 'p' || data[0] == 'q') {
data = 'bitcoincash:' + data;
}
var paramString = '';
if (data.indexOf('?') >= 0) {
paramString = data.substring(data.indexOf('?'));
data = data.substring(0, data.indexOf('?'));
}
if (data.indexOf('BITCOINCASH:') >= 0) {
data = data.toLowerCase();
}
originalAddress = data.replace('bitcoincash:', '');
var legacyAddress = bitcoinCashJsService.readAddress(data).legacy;
data = 'bitcoincash:' + legacyAddress + paramString;
} catch (ex) {}
}
$log.debug('Processing incoming data: ' + data);
function sanitizeUri(data) {
// Fixes when a region uses comma to separate decimals
var regex = /[\?\&]amount=(\d+([\,\.]\d+)?)/i;
var match = regex.exec(data);
if (!match || match.length === 0) {
return data;
}
var value = match[0].replace(',', '.');
var newUri = data.replace(regex, value);
// mobile devices, uris like copay://glidera
newUri.replace('://', ':');
return newUri;
}
function getParameterByName(name, url) {
if (!url) return;
name = name.replace(/[\[\]]/g, "\\$&");
var regex = new RegExp("[?&]" + name + "(=([^&#]*)|&|#|$)"),
results = regex.exec(url);
if (!results) return null;
if (!results[2]) return '';
return decodeURIComponent(results[2].replace(/\+/g, " "));
}
function checkPrivateKey(privateKey) {
try {
new bitcore.PrivateKey(privateKey, 'livenet');
} catch (err) {
return false;
}
return true;
}
function goSend(addr, amount, message, coin, serviceId, serviceData) {
$state.go('tabs.send', {}, {
'reload': true,
'notify': $state.current.name == 'tabs.send' ? false : true
});
// Timeout is required to enable the "Back" button
$timeout(function() {
var params = sendFlowService.getStateClone();
if (amount) {
params.amount = amount;
}
if (addr) {
params.toAddress = addr;
params.displayAddress = originalAddress ? originalAddress : addr;
}
if (coin) {
params.coin = coin;
}
if (noPrefixInAddress) {
params.noPrefixInAddress = noPrefixInAddress;
}
if (serviceId) {
params.thirdParty = [];
params.thirdParty.id = serviceId;
params.thirdParty.data = serviceData;
sendFlowService.pushState(params);
$state.transitionTo('tabs.send.amount');
} else {
sendFlowService.pushState(params);
$state.transitionTo('tabs.send.origin');
}
}, 100);
}
// data extensions for Payment Protocol with non-backwards-compatible request
if ((/^bitcoin(cash)?:\?r=[\w+]/).exec(data)) {
var coin = data.indexOf('bitcoincash') >= 0 ? 'bch' : 'btc';
data = decodeURIComponent(data.replace(/bitcoin(cash)?:\?r=/, ''));
if (coin == 'bch') {
payproService.getPayProDetailsViaHttp(data, function onGetPayProDetailsViaHttp(err, details) {
if (err) {
var message = err.toString();
if (typeof err.data === 'string') {
// i.e. 'This invoice is no longer accepting payments'
message = gettextCatalog.getString(err.data);
}
popupService.showAlert(gettextCatalog.getString('Error'), message)
} else {
handlePayPro(details, coin);
}
});
} else {
payproService.getPayProDetails(data, coin, function onGetPayProDetails(err, details) {
if (err) {
popupService.showAlert(gettextCatalog.getString('Error'), err);
} else {
handlePayPro(details, coin);
}
});
}
return true;
}
data = sanitizeUri(data);
// Bitcoin URL
if (bitcore.URI.isValid(data)) {
var coin = 'btc';
var parsed = new bitcore.URI(data);
var addr = parsed.address ? parsed.address.toString() : '';
var message = parsed.message;
var amount = parsed.amount ? parsed.amount : '';
if (parsed.r) {
payproService.getPayProDetails(parsed.r, coin, function(err, details) {
if (err) {
if (addr && amount) goSend(addr, amount, message, coin, serviceId, serviceData);
else popupService.showAlert(gettextCatalog.getString('Error'), err);
} else handlePayPro(details, coin);
});
} else {
goSend(addr, amount, message, coin, serviceId, serviceData);
}
return true;
// Cash URI
} else if (bitcoreCash.URI.isValid(data)) {
var coin = 'bch';
var parsed = new bitcoreCash.URI(data);
var addr = parsed.address ? parsed.address.toString() : '';
var message = parsed.message;
var amount = parsed.amount ? parsed.amount : '';
// paypro not yet supported on cash
if (parsed.r) {
payproService.getPayProDetails(parsed.r, coin, function(err, details) {
if (err) {
if (addr && amount)
goSend(addr, amount, message, coin, serviceId, serviceData);
else
popupService.showAlert(gettextCatalog.getString('Error'), err);
}
handlePayPro(details, coin);
});
} else {
goSend(addr, amount, message, coin, serviceId, serviceData);
}
return true;
// Cash URI with bitcoin (btc) address version number?
} else if (bitcore.URI.isValid(data.replace(/^bitcoincash:/,'bitcoin:'))) {
$log.debug('Handling bitcoincash URI with legacy address');
var coin = 'bch';
var parsed = new bitcore.URI(data.replace(/^bitcoincash:/,'bitcoin:'));
var oldAddr = parsed.address ? parsed.address.toString() : '';
if (!oldAddr) return false;
var addr = '';
var a = bitcore.Address(oldAddr).toObject();
addr = bitcoreCash.Address.fromObject(a).toString();
// Translate address
$log.debug('address transalated to:' + addr);
popupService.showConfirm(
gettextCatalog.getString('Bitcoin cash Payment'),
gettextCatalog.getString('Payment address was translated to new Bitcoin Cash address format: ' + addr),
gettextCatalog.getString('OK'),
gettextCatalog.getString('Cancel'),
function(ret) {
if (!ret) return false;
var message = parsed.message;
var amount = parsed.amount ? parsed.amount : '';
// paypro not yet supported on cash
if (parsed.r) {
payproService.getPayProDetails(parsed.r, coin, function(err, details) {
if (err) {
if (addr && amount)
goSend(addr, amount, message, coin, serviceId, serviceData);
else
popupService.showAlert(gettextCatalog.getString('Error'), err);
}
handlePayPro(details, coin);
});
} else {
goSend(addr, amount, message, coin, serviceId, serviceData);
}
}
);
return true;
// Plain URL
} else if (/^https?:\/\//.test(data)) {
payproService.getPayProDetails(data, coin, function(err, details) {
if (err) {
if ($state.includes('tabs.scan')) {
root.showMenu({
data: data,
type: 'url'
});
}
return;
}
handlePayPro(details);
return true;
});
// Plain Address
} else if (bitcore.Address.isValid(data, 'livenet') || bitcore.Address.isValid(data, 'testnet')) {
if ($state.includes('tabs.scan')) {
root.showMenu({
data: data,
type: 'bitcoinAddress'
});
} else {
goToAmountPage(data);
}
} else if (bitcoreCash.Address.isValid(data, 'livenet')) {
if ($state.includes('tabs.scan')) {
root.showMenu({
data: data,
type: 'bitcoinAddress',
coin: 'bch',
});
} else {
goToAmountPage(data, 'bch');
}
} else if (data && data.indexOf(appConfigService.name + '://glidera') === 0) {
var code = getParameterByName('code', data);
$ionicHistory.nextViewOptions({
disableAnimate: true
});
$state.go('tabs.home', {}, {
'reload': true,
'notify': $state.current.name == 'tabs.home' ? false : true
}).then(function() {
$ionicHistory.nextViewOptions({
disableAnimate: true
});
$state.transitionTo('tabs.buyandsell.glidera', {
code: code
});
});
return true;
} else if (data && data.indexOf(appConfigService.name + '://coinbase') === 0) {
var code = getParameterByName('code', data);
$ionicHistory.nextViewOptions({
disableAnimate: true
});
$state.go('tabs.home', {}, {
'reload': true,
'notify': $state.current.name == 'tabs.home' ? false : true
}).then(function() {
$ionicHistory.nextViewOptions({
disableAnimate: true
});
$state.transitionTo('tabs.buyandsell.coinbase', {
code: code
});
});
return true;
// BitPayCard Authentication
} else if (data && data.indexOf(appConfigService.name + '://') === 0) {
// Disable BitPay Card
if (!appConfigService._enabledExtensions.debitcard) return false;
var secret = getParameterByName('secret', data);
var email = getParameterByName('email', data);
var otp = getParameterByName('otp', data);
var reason = getParameterByName('r', data);
$state.go('tabs.home', {}, {
'reload': true,
'notify': $state.current.name == 'tabs.home' ? false : true
}).then(function() {
switch (reason) {
default:
case '0':
/* For BitPay card binding */
$state.transitionTo('tabs.bitpayCardIntro', {
secret: secret,
email: email,
otp: otp
});
break;
}
});
return true;
// Join
} else if (data && data.match(/^copay:[0-9A-HJ-NP-Za-km-z]{70,80}$/)) {
$state.go('tabs.home', {}, {
'reload': true,
'notify': $state.current.name == 'tabs.home' ? false : true
}).then(function() {
$state.transitionTo('tabs.add.join', {
url: data
});
});
return true;
// Old join
} else if (data && data.match(/^[0-9A-HJ-NP-Za-km-z]{70,80}$/)) {
$state.go('tabs.home', {}, {
'reload': true,
'notify': $state.current.name == 'tabs.home' ? false : true
}).then(function() {
$state.transitionTo('tabs.add.join', {
url: data
});
});
return true;
} else if (data && (data.substring(0, 2) == '6P' || checkPrivateKey(data))) {
root.showMenu({
data: data,
type: 'privateKey'
});
} else if (data && ((data.substring(0, 2) == '1|') || (data.substring(0, 2) == '2|') || (data.substring(0, 2) == '3|'))) {
$state.go('tabs.home').then(function() {
$state.transitionTo('tabs.add.import', {
code: data
});
});
return true;
} else {
if ($state.includes('tabs.scan')) {
root.showMenu({
data: data,
type: 'text'
});
}
}
return false;
};
function goToAmountPage(toAddress, coin) {
$state.go('tabs.send', {}, {
'reload': true,
'notify': $state.current.name == 'tabs.send' ? false : true
});
$timeout(function() {
var stateParams = {
toAddress: toAddress,
displayAddress: toAddress,
coin: coin,
noPrefix: 1
};
sendFlowService.pushState(stateParams);
$state.transitionTo('tabs.send.origin');
}, 100);
}
function handlePayPro(payProData, coin) {
console.log(payProData);
var toAddr = payProData.toAddress;
var amount = payProData.amount;
var paymentUrl = payProData.url;
var expires = payProData.expires;
var time = payProData.time;
if (coin === 'bch') {
var displayAddr = payProData.outputs[0].address;
toAddr = bitcoinCashJsService.readAddress('bitcoincash:' + displayAddr).legacy;
amount = payProData.outputs[0].amount;
paymentUrl = payProData.paymentUrl;
expires = Math.floor(new Date(expires).getTime() / 1000)
time = Math.ceil(new Date(time).getTime() / 1000)
}
var name = payProData.domain;
if (payProData.memo.indexOf('eGifter') > -1) {
name = 'eGifter'
} else if (paymentUrl.indexOf('https://bitpay.com') > -1) {
name = 'BitPay';
}
var thirdPartyData = {
id: 'bip70',
amount: amount,
caTrusted: true,
name: name,
domain: payProData.domain,
expires: expires,
memo: payProData.memo,
network: 'livenet',
requiredFeeRate: payProData.requiredFeeRate,
selfSigned: 0,
time: time,
displayAddress: displayAddr,
toAddress: toAddr,
url: paymentUrl,
verified: true
};
var stateParams = {
amount: thirdPartyData.amount,
toAddress: thirdPartyData.toAddress,
coin: coin,
thirdParty: thirdPartyData
};
// fee
if (thirdPartyData.requiredFeeRate) {
stateParams.requiredFeeRate = thirdPartyData.requiredFeeRate * 1024;
}
// This does not make sense, thirdPartyData gets added by stateParams below
//sendFlowService.pushState(thirdPartyData);
scannerService.pausePreview();
$state.go('tabs.send', {}, {
'reload': true,
'notify': $state.current.name == 'tabs.send' ? false : true
}).then(function() {
$timeout(function() {
sendFlowService.pushState(stateParams); // Need to do more here
$state.transitionTo('tabs.send.origin');
});
});
}
return root;
});

View file

@ -0,0 +1,180 @@
'use strict';
(function() {
angular
.module('bitcoincom.services')
.factory('latestReleaseService', latestReleaseService);
function latestReleaseService($log, $http, $ionicPopup, configService, externalLinkService, gettextCatalog, platformInfo) {
var service = {
// Functions
checkLatestRelease: checkLatestRelease,
requestLatestRelease: requestLatestRelease,
showUpdatePopup: showUpdatePopup
};
return service;
function checkLatestRelease(cb) {
var releaseURL = configService.getDefaults().release.url;
requestLatestRelease(releaseURL, function (err, releaseData) {
if (err) return cb(err);
var currentVersion = window.version;
var latestVersion = releaseData.tag_name;
if (!verifyTagFormat(currentVersion))
return cb('Cannot verify the format of version tag: ' + currentVersion);
if (!verifyTagFormat(latestVersion))
return cb('Cannot verify the format of latest release tag: ' + latestVersion);
var current = formatTagNumber(currentVersion);
var latest = formatTagNumber(latestVersion);
if (latest.major < current.major || (latest.major === current.major && latest.minor <= current.minor)) {
return cb(null, false);
}
var releaseSearchTerm = "";
if (platformInfo.isNW) { // XX SP: DESKTOP: Check if the latest release is already available for current OS
var platform = process.platform;
if (platform === "darwin") {
releaseSearchTerm = "osx";
} else if (platform === "win32") {
releaseSearchTerm = "win";
} else if (platform === "linux") {
releaseSearchTerm = "linux";
}
var foundNewVersion = false;
for (var i in releaseData.assets) {
if (releaseData.assets[i].name.indexOf(releaseSearchTerm) !== -1) {
foundNewVersion = true;
break;
}
}
}
$log.debug('A new version is available: ' + latestVersion);
var releaseNotes = false;
if (releaseData.body) {
var releaseLines = releaseData.body.split('\n');
for (var lineNum in releaseLines) {
if (releaseLines[lineNum].substring(0, 2) === "# ") {
releaseLines[lineNum] = "<strong>" + releaseLines[lineNum].substring(2) + "</strong>";
} else if (releaseLines[lineNum].substring(0, 2) === "- ") {
releaseLines[lineNum] = "&bull; " + releaseLines[lineNum].substring(2);
}
}
releaseNotes = releaseLines.join('\n');
}
return cb(null, {latestVersion: latestVersion, releaseNotes: releaseNotes});
});
function verifyTagFormat(tag) {
var regex = /^v?\d+\.\d+(\.\d+)?(-rc\d)?$/i;
return regex.exec(tag);
}
function formatTagNumber(tag) {
var label = false;
if (tag.split("-")[1]) { // Move postfixes like "-rc2" to a variable
label = tag.split("-")[1];
tag = tag.split("-")[0];
}
var formattedNumber = tag.replace(/^v/i, '').split('.');
return {
major: +(formattedNumber[0] ? +formattedNumber[0] : 0),
minor: +(formattedNumber[1] ? +formattedNumber[1] : 0),
patch: +(formattedNumber[2] ? +formattedNumber[2] : 0),
label: label /* XX SP: Maybe we can use this in a later stage (with for example 1.0.0-rc2 the value will be "rc2" and false if there is no label) */
};
}
}
function requestLatestRelease(releaseURL, cb) {
$log.debug('Retrieving latest release information...');
var request = {
url: releaseURL,
method: 'GET',
json: true
};
$http(request).then(function (release) {
$log.debug('Latest release: ' + release.data.name);
return cb(null, release.data);
}, function (err) {
return cb('Cannot get the release information: ' + err);
});
}
function showUpdatePopup() {
var buttons = [];
if (!platformInfo.isIOS) { // There is no GitHub-release for iPhone
buttons.push({
text: "GitHub",
type: 'button-positive',
onTap: function () {
var url = 'https://github.com/Bitcoin-com/Wallet/releases/latest';
externalLinkService.open(url, false);
}
});
}
if (platformInfo.isAndroid) {
buttons.unshift({
text: "Google Play Store",
type: 'button-positive',
onTap: function () {
var url = 'https://play.google.com/store/apps/details?id=com.bitcoin.mwallet';
externalLinkService.open(url, false);
}
});
}
if (platformInfo.isIOS) {
buttons.unshift({
text: "App Store",
type: 'button-positive',
onTap: function () {
var url = 'https://itunes.apple.com/app/id1252903728';
externalLinkService.open(url, false);
}
});
} else if (platformInfo.isNW) {
if (process.platform === 'darwin') {
buttons.unshift({
text: "Mac App Store",
type: 'button-positive',
onTap: function () {
var url = 'https://itunes.apple.com/app/bitcoin-com-wallet/id1383072453';
externalLinkService.open(url, false);
}
});
}
}
if (buttons.length === 1) { // There is only one source to download (probably on desktop, so open GitHub release page..)
buttons[0].onTap();
} else {
buttons.push({
text: gettextCatalog.getString('Go Back'),
type: 'button-positive',
onTap: function () {
return true;
}
});
$ionicPopup.show({
title: gettextCatalog.getString('Update Available'),
subTitle: gettextCatalog.getString('An update to this app is available. For your security, please update to the latest version.'),
cssClass: 'popup-update',
buttons: buttons
});
}
}
}
})();

View file

@ -1,63 +0,0 @@
'use strict';
angular.module('copayApp.services')
.factory('latestReleaseService', function latestReleaseServiceFactory($log, $http, configService) {
var root = {};
root.checkLatestRelease = function(cb) {
var releaseURL = configService.getDefaults().release.url;
requestLatestRelease(releaseURL, function(err, release) {
if (err) return cb(err);
var currentVersion = window.version;
var latestVersion = release.data.tag_name;
if (!verifyTagFormat(currentVersion))
return cb('Cannot verify the format of version tag: ' + currentVersion);
if (!verifyTagFormat(latestVersion))
return cb('Cannot verify the format of latest release tag: ' + latestVersion);
var current = formatTagNumber(currentVersion);
var latest = formatTagNumber(latestVersion);
if (latest.major < current.major || (latest.major == current.major && latest.minor <= current.minor))
return cb(null, false);
$log.debug('A new version is available: ' + latestVersion);
return cb(null, true);
});
function verifyTagFormat(tag) {
var regex = /^v?\d+\.\d+\.\d+$/i;
return regex.exec(tag);
};
function formatTagNumber(tag) {
var formattedNumber = tag.replace(/^v/i, '').split('.');
return {
major: +formattedNumber[0],
minor: +formattedNumber[1],
patch: +formattedNumber[2]
};
};
};
function requestLatestRelease(releaseURL, cb) {
$log.debug('Retrieving latest relsease information...');
var request = {
url: releaseURL,
method: 'GET',
json: true
};
$http(request).then(function(release) {
$log.debug('Latest release: ' + release.data.name);
return cb(null, release);
}, function(err) {
return cb('Cannot get the release information: ' + err);
});
};
return root;
});

View file

@ -52,11 +52,7 @@ angular.module('copayApp.services').factory('ongoingProcess', function($log, $ti
root.clear = function() {
ongoingProcess = {};
if (isCordova && !isWindowsPhoneApp) {
window.plugins.spinnerDialog.hide();
} else {
$ionicLoading.hide();
}
$ionicLoading.hide();
};
root.get = function(processName) {
@ -78,28 +74,21 @@ angular.module('copayApp.services').factory('ongoingProcess', function($log, $ti
root.onGoingProcessName = name;
var showName = $filter('translate')(processNames[name] || name);
if (root.onGoingProcessName) {
var tmpl;
if (isWindowsPhoneApp) tmpl = '<div>' + showName + '</div>';
else tmpl = '<div class="item-icon-left">' + showName + '<ion-spinner class="spinner-stable" icon="lines"></ion-spinner></div>';
$ionicLoading.show({
template: tmpl,
});
} else {
$ionicLoading.hide();
}
if (customHandler) {
customHandler(processName, showName, isOn);
} else if (root.onGoingProcessName) {
if (isCordova && !isWindowsPhoneApp) {
window.plugins.spinnerDialog.show(null, showName, root.clear);
} else {
var tmpl;
if (isWindowsPhoneApp) tmpl = '<div>' + showName + '</div>';
else tmpl = '<div class="item-icon-left">' + showName + '<ion-spinner class="spinner-stable" icon="lines"></ion-spinner></div>';
$ionicLoading.show({
template: tmpl
});
}
} else {
if (isCordova && !isWindowsPhoneApp) {
window.plugins.spinnerDialog.hide();
} else {
$ionicLoading.hide();
}
}
}
};
return root;

View file

@ -1,6 +1,6 @@
'use strict';
angular.module('copayApp.services').factory('openURLService', function($rootScope, $ionicHistory, $document, $log, $state, platformInfo, lodash, profileService, incomingData, appConfigService) {
angular.module('copayApp.services').factory('openURLService', function($rootScope, $ionicHistory, $document, $log, $state, platformInfo, lodash, profileService, incomingDataService, appConfigService) {
var root = {};
var handleOpenURL = function(args) {
@ -23,9 +23,12 @@ angular.module('copayApp.services').factory('openURLService', function($rootScop
document.addEventListener('handleopenurl', handleOpenURL, false);
if (!incomingData.redir(url)) {
$log.warn('Unknown URL! : ' + url);
}
incomingDataService.redir(url, function onError(err) {
if (err) {
$log.warn('Unknown URL! : ' + url);
popupService.showAlert(gettextCatalog.getString('Error'), err.message);
}
});
};
var handleResume = function() {

View file

@ -0,0 +1,85 @@
'use strict';
(function(){
angular
.module('bitcoincom.services')
.factory('sendFlowRouterService', sendFlowRouterService);
function sendFlowRouterService(
sendFlowStateService
, $state, $ionicHistory, $timeout
) {
var service = {
// Functions
start: start,
goNext: goNext,
goBack: goBack,
};
return service;
/**
* Start new send flow
*/
function start() {
var state = sendFlowStateService.state;
if (state.isRequestAmount) {
$state.go('tabs.paymentRequest.amount');
} else {
if ($state.current.name != 'tabs.send') {
$state.go('tabs.home').then(function () {
$ionicHistory.clearHistory();
$state.go('tabs.send').then(function () {
$timeout(function () {
goNext();
}, 60);
});
});
} else {
goNext();
}
}
}
/**
* Go to the next page
* Routing strategy : https://bitcoindotcom.atlassian.net/wiki/x/BQDWKQ
*/
function goNext() {
var state = sendFlowStateService.state;
var needsDestination = !state.toWalletId && !state.toAddress;
var needsOrigin = !state.fromWalletId;
var needsAmount = !state.amount && !state.sendMax;
if (needsDestination) {
if (!state.isWalletTransfer && !state.thirdParty) {
$state.go('tabs.send');
return;
} else if (!needsOrigin) {
$state.go('tabs.send.destination');
return;
}
}
if (needsOrigin) {
$state.go('tabs.send.origin');
} else if (needsAmount) {
$state.go('tabs.send.amount');
} else {
$state.go('tabs.send.review');
}
}
/**
* Go to the previous page
*/
function goBack() {
$ionicHistory.goBack();
}
};
})();

View file

@ -0,0 +1,142 @@
'use strict';
(function(){
angular
.module('bitcoincom.services')
.factory('sendFlowStateService', sendFlowStateService);
function sendFlowStateService($log) {
var service = {
// Variables
state: {
amount: 0,
displayAddress: null,
fromWalletId: '',
sendMax: false,
thirdParty: null,
toAddress: '',
toWalletId: '',
coin: '',
isRequestAmount: false,
isWalletTransfer: false
},
previousStates: [],
// Functions
init: init,
clear: clear,
getClone: getClone,
map: map,
pop: pop,
push: push,
isEmpty: isEmpty
};
return service;
/**
* Init state & stack
* @param {Object} params
*/
function init(params) {
$log.debug("send-flow-state init()");
clear();
if (params) {
push(params);
}
}
/**
* Clear a state & stack
*/
function clear() {
$log.debug("send-flow-state clear()");
clearCurrent();
service.previousStates = [];
}
/**
* Clear current state only
*/
function clearCurrent() {
$log.debug("send-flow-state clearCurrent()");
service.state = {
amount: 0,
displayAddress: null,
fromWalletId: '',
sendMax: false,
thirdParty: null,
toAddress: '',
toWalletId: '',
coin: '',
isRequestAmount: false,
isWalletTransfer: false
}
}
/**
* Get a clone of the current state
*/
function getClone() {
var currentState = {};
Object.keys(service.state).forEach(function forCurrentParam(key) {
if (typeof service.state[key] !== 'function' && key !== 'previousStates') {
currentState[key] = service.state[key];
}
});
return currentState;
}
/**
* Fill in the current state from the params
* @param {Object} params
*/
function map(params) {
Object.keys(params).forEach(function forNewParam(key) {
service.state[key] = params[key];
});
}
/**
* Pop state
*/
function pop() {
$log.debug('send-flow-state pop');
if (service.previousStates.length) {
var params = service.previousStates.pop();
clearCurrent();
map(params);
} else {
clear();
}
}
/**
* Push state
* @param {Object} params
*/
function push(params) {
$log.debug('send-flow-state push');
var currentParams = getClone();
service.previousStates.push(currentParams);
clearCurrent();
map(params);
}
/**
* Is empty stack
*/
function isEmpty() {
return service.previousStates.length == 0;
}
};
})();

View file

@ -0,0 +1,148 @@
'use strict';
(function(){
angular
.module('bitcoincom.services')
.factory('sendFlowService', sendFlowService);
function sendFlowService(
sendFlowStateService, sendFlowRouterService
, bitcoinUriService, payproService, bitcoinCashJsService
, popupService, gettextCatalog
, $state, $log
) {
var service = {
// Variables
state: sendFlowStateService,
router: sendFlowRouterService,
// Functions
start: start,
goNext: goNext,
goBack: goBack
};
return service;
/**
* Start a new send flow
* @param {Object} params
* @param {Function} onError
*/
function start(params, onError) {
$log.debug('send-flow start()');
if (params && params.data) {
var res = bitcoinUriService.parse(params.data);
if (res.isValid) {
// If BIP70 (url)
if (res.url) {
var url = res.url;
var coin = res.coin || '';
payproService.getPayProDetails(url, coin, function onGetPayProDetails(err, payProData) {
if (err) {
popupService.showAlert(gettextCatalog.getString('Error'), err);
} else {
var name = payProData.domain;
// Detect some merchant that we know
if (payProData.memo.indexOf('eGifter') > -1) {
name = 'eGifter'
} else if (paymentUrl.indexOf('https://bitpay.com') > -1) {
name = 'BitPay';
}
// Init thirdParty
var thirdPartyData = {
id: 'bip70',
caTrusted: true,
name: name,
domain: payProData.domain,
expires: payProData.expires,
memo: payProData.memo,
network: 'livenet',
requiredFeeRate: payProData.requiredFeeRate,
selfSigned: 0,
time: payProData.time,
url: payProData.url,
verified: true
};
// Fill in params
params.amount = payProData.amount,
params.toAddress = payProData.toAddress,
params.coin = coin,
params.thirdParty = thirdPartyData
}
// Resolve
_next();
});
} else {
if (res.coin) {
params.coin = res.coin;
}
if (res.amountInSatoshis) {
params.amount = res.amountInSatoshis;
}
if (res.publicAddress) {
var prefix = res.isTestnet ? 'bchtest:' : 'bitcoincash:';
params.displayAddress = res.publicAddress.cashAddr || res.publicAddress.legacy || res.publicAddress.bitpay;
var formatAddress = res.publicAddress.cashAddr ? prefix + params.displayAddress : params.displayAddress;
params.toAddress = bitcoinCashJsService.readAddress(formatAddress).legacy;
}
_next();
}
} else {
if (onError) {
onError();
}
}
} else {
_next();
}
// Next used for sync the async task
function _next() {
sendFlowStateService.init(params);
// Routing strategy to -> send-flow-router.service
sendFlowRouterService.start();
}
}
/**
* Go to the next step
* @param {Object} state
*/
function goNext(state) {
$log.debug('send-flow goNext()');
// Save the current route before leaving
state.route = $state.current.name;
// Save the state and redirect the user
sendFlowStateService.push(state);
sendFlowRouterService.goNext();
}
/**
* Go to the previous step
*/
function goBack() {
$log.debug('send-flow goBack()');
// Remove the state on top and redirect the user
sendFlowStateService.pop();
sendFlowRouterService.goBack();
}
}
})();

View file

@ -1,103 +0,0 @@
'use strict';
(function(){
angular
.module('copayApp.services')
.factory('sendFlowService', sendFlowService);
function sendFlowService($log) {
var service = {
// A separate state variable so we can ensure it is cleared of everything,
// even other properties added that this service does not know about. (such as "coin")
state: {
amount: '',
displayAddress: null,
fromWalletId: '',
sendMax: false,
thirdParty: null,
toAddress: '',
toWalletId: ''
},
previousStates: [],
// Functions
clear: clear,
getStateClone: getStateClone,
map: map,
popState: popState,
pushState: pushState,
startSend: startSend
};
return service;
function clear() {
console.log("sendFlow clear()");
clearCurrent();
service.previousStates = [];
}
function clearCurrent() {
console.log("sendFlow clearCurrent()");
service.state = {
amount: '',
displayAddress: null,
fromWalletId: '',
sendMax: false,
thirdParty: null,
toAddress: '',
toWalletId: ''
}
}
/**
* Handy for debugging
*/
function getStateClone() {
var currentState = {};
Object.keys(service.state).forEach(function forCurrentParam(key) {
if (typeof service.state[key] !== 'function' && key !== 'previousStates') {
currentState[key] = service.state[key];
}
});
return currentState;
}
/**
* Clears all previous state
*/
function startSend(params) {
console.log('startSend()');
clear();
map(params);
}
function map(params) {
Object.keys(params).forEach(function forNewParam(key) {
service.state[key] = params[key];
});
};
function popState() {
console.log('sendFlow pop');
if (service.previousStates.length) {
var params = service.previousStates.pop();
clearCurrent();
map(params);
} else {
clear();
}
};
function pushState(params) {
console.log('sendFlow push');
var currentParams = getStateClone();
service.previousStates.push(currentParams);
clearCurrent();
map(params);
};
};
})();

View file

@ -5,7 +5,7 @@ angular.module('copayApp.services').factory('servicesService', function(configSe
name: 'shapeshift',
title: 'Shapeshift',
icon: 'icon-shapeshift',
sref: 'tabs.shapeshift',
href: 'https://shapeshift.io/'
}];
root.register = function(serviceInfo) {

View file

@ -328,18 +328,23 @@ angular.module('copayApp.services').factory('shapeshiftApiService', function($q)
$scope.amount, $scope.withdrawalAddress,
$scope.coinIn, $scope.coinOut
);
console.log('shapeshiftApiService.FixedAmountTx()');
console.log(fixedTx);
SSA.FixedAmountTx(fixedTx, function (data) {
console.log(data)
return promise.resolve({ fixedTxData : data.success });
console.log(data);
promise.resolve(data);
});
return promise.promise;
},
NormalTx : function($scope){
var promise = $q.defer();
var normalTx = SSA.CreateNormalTx($scope.withdrawalAddress, $scope.coinIn, $scope.coinOut);
console.log('shapeshiftApiService.NormalTx()');
console.log(normalTx);
SSA.NormalTx(normalTx, function (data) {
promise.resolve({ normalTxData : data });
console.log(data);
promise.resolve(data);
});
return promise.promise;
},
@ -360,11 +365,12 @@ angular.module('copayApp.services').factory('shapeshiftApiService', function($q)
return promise.promise;
},
ValidateAddress : function(address, coin) {
var promise = $q.defer();
SSA.ValidateAdddress(address, coin, function(data){
promise.resolve(data);
});
return promise.promise;
var promise = $q.defer();
SSA.ValidateAdddress(address, coin, function onRequest(data){
console.log(data);
promise.resolve(data);
});
return promise.promise;
}
};
});

View file

@ -0,0 +1,112 @@
'use strict';
(function(){
angular
.module('bitcoincom.services')
.factory('shapeshiftService', shapeshiftService);
function shapeshiftService(shapeshiftApiService, gettextCatalog) {
var service = {
// Variables
coinIn: '',
coinOut: '',
withdrawalAddress: '',
returnAddress: '',
amount: '',
marketData: {},
coins: {
'BTC': {name: 'Bitcoin', symbol: 'BTC'},
'BCH': {name: 'Bitcoin Cash', symbol: 'BCH'}
},
// Functions
getMarketData: getMarketData,
shiftIt: shiftIt
};
return service;
function handleError(response, defaultMessage, cb) {
if (response && typeof response.error === "string") {
cb(new Error(response.error));
} else if (response && response.error && response.error.message) {
cb(new Error(response.error.message));
} else {
cb(new Error(defaultMessage));
}
}
function getMarketData(coinIn, coinOut, cb) {
service.coinIn = coinIn;
service.coinOut = coinOut;
shapeshiftApiService
.marketInfo(service.coinIn, service.coinOut)
.then(function (response) {
if (!response || response.error) {
handleError(response, 'Invalid response from Shapeshift', cb);
} else {
service.marketData = response;
service.rateString = service.marketData.rate.toString() + ' ' + coinOut.toUpperCase() + '/' + coinIn.toUpperCase();
cb(null, response);
}
});
}
function shiftIt(coinIn, coinOut, withdrawalAddress, returnAddress, amount, cb) {
// Test if the amount is correct depending on the min and max
if (!amount || typeof amount !== 'number') {
cb(new Error(gettextCatalog.getString('Amount is not defined')));
} else if (amount < service.marketData.minimum) {
cb(new Error(gettextCatalog.getString('Amount is below the minimun')));
} else if (amount > service.marketData.maxLimit) {
cb(new Error(gettextCatalog.getString('Amount is above the limit')));
} else {
// Init service data
service.withdrawalAddress = withdrawalAddress;
service.returnAddress = returnAddress;
service.coinIn = coinIn;
service.coinOut = coinOut;
service.amount = amount;
// Check the address
shapeshiftApiService
.ValidateAddress(withdrawalAddress, coinOut)
.then(function onSuccess(response) {
if (response && response.isvalid) {
// Prepare the transaction shapeshift side
shapeshiftApiService.NormalTx(service).then(function onResponse(response) {
// If error, return it
if (!response || response.error) {
handleError(response, gettextCatalog.getString('Invalid response from Shapeshift'), cb);
} else {
var txData = response;
// If the content is not that it was expected, get back an error
if (!txData || !txData.orderId || !txData.deposit) {
cb(new Error(gettextCatalog.getString('Invalid response from Shapeshift')));
} else {
// Get back the data
service.depositInfo = txData;
var shapeshiftData = {
coinIn: coinIn,
coinOut: coinOut,
toWalletId: service.toWalletId,
minAmount: service.marketData.minimum,
maxAmount: service.marketData.maxLimit,
orderId: txData.orderId,
toAddress: txData.deposit
};
cb(null, shapeshiftData);
}
}
});
} else {
cb(new Error(gettextCatalog.getString('Invalid address')));
}
});
}
}
}
})();

View file

@ -1,141 +0,0 @@
'use strict';
angular.module('copayApp.services').factory('shapeshiftService', function ($http, $interval, $log, lodash, moment, ongoingProcess, shapeshiftApiService, storageService, configService, incomingData, platformInfo, servicesService) {
var root = {};
root.ShiftState = 'Shift';
root.coinIn = '';
root.coinOut = '';
root.withdrawalAddress = '';
root.returnAddress = '';
root.amount = '';
root.marketData = {};
root.getMarketDataIn = function (coin) {
if (coin === root.coinOut) return root.getMarketData(root.coinOut, root.coinIn);
return root.getMarketData(coin, root.coinOut);
};
root.getMarketDataOut = function (coin) {
if (coin === root.coinIn) return root.getMarketData(root.coinOut, root.coinIn);
return root.getMarketData(root.coinIn, coin);
};
root.getMarketData = function (coinIn, coinOut, cb) {
root.coinIn = coinIn;
root.coinOut = coinOut;
if (root.coinIn === undefined || root.coinOut === undefined) return;
shapeshiftApiService
.marketInfo(root.coinIn, root.coinOut)
.then(function (marketData) {
root.marketData = marketData;
root.rateString = root.marketData.rate.toString() + ' ' + coinOut.toUpperCase() + '/' + coinIn.toUpperCase();
if (cb) {
cb(marketData);
}
});
};
/*shapeshiftApiService.coins().then(function(coins){
root.coins = coins;
root.coinIn = coins['BTC'].symbol;
root.coinOut = coins['BCH'].symbol;
root.getMarketData(root.coinIn, root.coinOut);
});*/
root.coins = {
'BTC': {name: 'Bitcoin', symbol: 'BTC'},
'BCH': {name: 'Bitcoin Cash', symbol: 'BCH'}
};
function checkForError(data) {
if (data.err) return true;
return false;
}
root.shiftIt = function (coinIn, coinOut, withdrawalAddress, returnAddress, cb) {
ongoingProcess.set('connectingShapeshift', true);
root.withdrawalAddress = withdrawalAddress;
root.returnAddress = returnAddress;
root.coinIn = coinIn;
root.coinOut = coinOut;
shapeshiftApiService.ValidateAddress(withdrawalAddress, coinOut).then(function (valid) {
var tx = ShapeShift();
var coin;
console.log("Starting");
tx.then(function (txData) {
console.log("Got txData", txData);
if (txData['fixedTxData']) {
txData = txData.fixedTxData;
if (checkForError(txData)) return cb(txData.err);
//console.log(txData)
var coinPair = txData.pair.split('_');
txData.depositType = coinPair[0].toUpperCase();
txData.withdrawalType = coinPair[1].toUpperCase();
coin = root.coins[txData.depositType].name.toLowerCase();
txData.depositQR = coin + ":" + txData.deposit + "?amount=" + txData.depositAmount;
root.txFixedPending = true;
} else if (txData['normalTxData']) {
txData = txData.normalTxData;
if (checkForError(txData)) return cb(txData.err);
coin = root.coins[txData.depositType.toUpperCase()].name.toLowerCase();
txData.depositQR = coin + ":" + txData.deposit;
} else if (txData['cancelTxData']) {
txData = txData.cancelTxData;
if (checkForError(txData)) return cb(txData.err);
if (root.txFixedPending) {
root.txFixedPending = false;
}
root.ShiftState = 'Shift';
}
root.depositInfo = txData;
//console.log(root.marketData);
//console.log(root.depositInfo);
var sendAddress = txData.depositQR;
if (sendAddress && sendAddress.indexOf('bitcoin cash') >= 0)
sendAddress = sendAddress.replace('bitcoin cash', 'bitcoincash');
ongoingProcess.set('connectingShapeshift', false);
root.ShiftState = 'Cancel';
//root.GetStatus();
//root.txInterval=$interval(root.GetStatus, 8000);
var shapeshiftData = {
coinIn: coinIn,
coinOut: coinOut,
toWalletId: root.toWalletId,
minAmount: root.marketData.minimum,
maxAmount: root.marketData.maxLimit,
orderId: root.depositInfo.orderId,
toAddress: txData.deposit
};
//
// if (incomingData.redir(sendAddress, 'shapeshift', shapeshiftData)) {
ongoingProcess.set('connectingShapeshift', false);
// return;
// }
cb(null, shapeshiftData);
});
})
};
function ShapeShift() {
if (parseFloat(root.amount) > 0) return shapeshiftApiService.FixedAmountTx(root);
return shapeshiftApiService.NormalTx(root);
}
root.GetStatus = function () {
var address = root.depositInfo.deposit
shapeshiftApiService.GetStatusOfDepositToAddress(address).then(function (data) {
root.DepositStatus = data;
if (root.DepositStatus.status === 'complete') {
$interval.cancel(root.txInterval);
root.depositInfo = null;
root.ShiftState = 'Shift'
}
});
};
return root;
});

View file

@ -10,7 +10,7 @@ angular.module('copayApp.services')
isoCode: 'en',
rateCode: 'USD'
}, {
name: 'català',
name: 'Català',
isoCode: 'ca',
rateCode: 'EUR'
},{
@ -59,10 +59,6 @@ angular.module('copayApp.services')
name: 'Português',
isoCode: 'pt',
rateCode: 'EUR'
}, {
name: 'русский язык',
isoCode: 'ru',
rateCode: 'RUB'
}, {
name: '한국어',
isoCode: 'ko',

View file

@ -0,0 +1,275 @@
'use strict';
(function(){
angular
.module('bitcoincom.services')
.factory('walletHistoryService', walletHistoryService);
function walletHistoryService(configService, storageService, lodash, $log, txFormatService) {
//var PAGE_SIZE = 50;
var PAGE_SIZE = 20; // For dev only
// How much to overlap on each end of the page, for mitigating inconsistent sort order.
var PAGE_OVERLAP_FRACTION = 0.2;
var PAGE_OVERLAP = Math.floor(PAGE_SIZE * PAGE_OVERLAP_FRACTION);
// The fraction of transactions in the new overlapping resultset that we already know about.
// If we know about at least this many, then there are probably no gaps.
var MIN_KNOWN_TX_OVERLAP_FRACTION = 0.5;
var SAFE_CONFIRMATIONS = 6;
var allTransactionsFetched = false;
var service = {
getCachedTxHistory: getCachedTxHistory,
updateLocalTxHistoryByPage: updateLocalTxHistoryByPage,
};
return service;
function addEarlyTransactions(walletId, cachedTxs, newTxs) {
var cachedTxIndexFromId = {};
cachedTxs.forEach(function forCachedTx(tx){
cachedTxIndexFromId[tx.txid] = true;
});
var confirmationsUpdated = false;
var someTransactionsWereNew = false;
var overlappingTxsCount = 0;
newTxs.forEach(function forNewTx(tx){
if (typeof cachedTxIndexFromId[tx.txid] === "undefined") {
someTransactionsWereNew = true;
cachedTxs.push(tx);
} else {
var txUpdated = updateCachedTx(cachedTxs, cachedTxIndexFromId, tx);
confirmationsUpdated = confirmationsUpdated || txUpdated;
overlappingTxsCount++;
}
});
var overlappingTxFraction = overlappingTxsCount / Math.min(cachedTxs.length, PAGE_OVERLAP);
console.log('overlappingTxFraction:', overlappingTxFraction);
if (overlappingTxFraction >= MIN_KNOWN_TX_OVERLAP_FRACTION) { // We are good
if (someTransactionsWereNew) {
saveTxHistory(walletId, cachedTxs);
} else if (confirmationsUpdated) {
saveTxHistory(walletId, cachedTxs);
} else if (overlappingTxsCount === newTxs.length) {
allTransactionsFetched = true;
}
return cachedTxs;
} else {
// We might be missing some txs.
console.error('We might be missing some txs in the history.');
// Our history is wrong, so remove it - we could instead, try to fetch data that was not so early.
storageService.removeTxHistory(walletId, function onRemoveTxHistory(){});
return [];
}
}
function addLatestTransactions(walletId, cachedTxs, newTxs) {
var cachedTxIndexFromId = {};
cachedTxs.forEach(function forCachedTx(tx, txIndex){
cachedTxIndexFromId[tx.txid] = txIndex;
});
var someTransactionsWereNew = false;
var confirmationsUpdated = false;
var overlappingTxsCount = 0;
var uniqueNewTxs = [];
newTxs.forEach(function forNewTx(tx){
if (typeof cachedTxIndexFromId[tx.txid] === "undefined") {
someTransactionsWereNew = true;
uniqueNewTxs.push(tx);
} else {
var txUpdated = updateCachedTx(cachedTxs, cachedTxIndexFromId, tx);
confirmationsUpdated = confirmationsUpdated || txUpdated;
overlappingTxsCount++;
}
});
var overlappingTxFraction = overlappingTxsCount / Math.min(cachedTxs.length, PAGE_OVERLAP);
if (overlappingTxFraction >= MIN_KNOWN_TX_OVERLAP_FRACTION) { // We are good
if (someTransactionsWereNew) {
var allTxs = uniqueNewTxs.concat(cachedTxs);
saveTxHistory(walletId, allTxs);
return allTxs;
} else {
if (confirmationsUpdated) {
saveTxHistory(walletId, cachedTxs);
}
return cachedTxs;
}
} else {
// We might be missing some txs.
// Our history is wrong, so just include the latest ones
saveTxHistory(walletId, newTxs);
return newTxs;
}
}
// Only clear the cache once we have received new transactions from the server.
/**
* @param wallet
* @param start
* @param {function(err, txs)} cb - transactions is always an array, may be empty
*/
function fetchTxHistoryByPage(wallet, start, cb) {
var skip = Math.max(0, start - PAGE_OVERLAP);
var limit = PAGE_SIZE;
var opts = {
skip: skip,
limit: limit
};
wallet.getTxHistory(opts, function onTxHistory(err, txsFromServer) {
if (err) {
return cb(err, []);
}
if (txsFromServer.length === 0) {
return cb(null, []);
}
var processedTxs = processNewTxs(wallet, txsFromServer);
return cb(null, processedTxs);
});
}
/**
* @param {string} walletId
* @param {function(error, txs)} cb - txs is always an array, may be empty
*/
function getCachedTxHistory(walletId, cb) {
console.log('txhistory updateLocalTxHistoryByPage()');
storageService.getTxHistory(walletId, function onGetTxHistory(err, txHistoryString){
if (err) {
return cb(err, []);
}
if (!txHistoryString) {
return cb(null, []);
}
try {
var txHistory = JSON.parse(txHistoryString);
return cb(null, txHistory);
} catch (e) {
$log.error('Failed to parse tx history.', e);
return cb(e, []);
}
});
}
function processNewTxs(wallet, txs) {
var now = Math.floor(Date.now() / 1000);
var txHistoryUnique = {};
var processedTxs = [];
wallet.hasUnsafeConfirmed = false;
lodash.each(txs, function(tx) {
tx = txFormatService.processTx(wallet.coin, tx);
// no future transactions...
if (tx.time > now)
tx.time = now;
if (tx.confirmations >= SAFE_CONFIRMATIONS) {
tx.safeConfirmed = SAFE_CONFIRMATIONS + '+';
} else {
tx.safeConfirmed = false;
wallet.hasUnsafeConfirmed = true;
}
if (tx.note) {
delete tx.note.encryptedEditedByName;
delete tx.note.encryptedBody;
}
if (!txHistoryUnique[tx.txid]) {
processedTxs.push(tx);
txHistoryUnique[tx.txid] = true;
} else {
$log.debug('Ignoring duplicate TX in history: ' + tx.txid)
}
});
return processedTxs;
}
function saveTxHistory(walletId, processedTxs) {
storageService.setTxHistory(processedTxs, walletId, function onSetTxHistory(error){
if (error) {
$log.error('pagination Failed to save tx history.', error);
}
});
}
/**
* Returns true if the cached tx was updated
* @param {*} cachedTxs
* @param {*} cachedTxIndexFromId - Indices for cachedTxs, based on txid
* @param {*} tx - The most recent tx info
*/
function updateCachedTx(cachedTxs, cachedTxIndexFromId, tx) {
var updated = false;
var txIndex = cachedTxIndexFromId[tx.txid];
var cachedTx = cachedTxs[txIndex];
if (cachedTx.confirmations < SAFE_CONFIRMATIONS && tx.confirmations > cachedTx.confirmations) {
cachedTxs[txIndex].confirmations = tx.confirmations;
updated = true;
}
return updated;
}
function updateLocalTxHistoryByPage(wallet, getLatest, flushCacheOnNew, cb) {
console.log('txhistory updaetLocalTxHistoryByPage()');
if (flushCacheOnNew) {
fetchTxHistoryByPage(wallet, 0, function onFetchTxHistory(err, txs){
if (err) {
return cb(err, txs);
}
saveTxHistory(wallet.id, txs);
return cb(null, txs);
});
} else {
getCachedTxHistory(wallet.id, function onCachedHistory(err, cachedTxs){
if (err) {
$log.error('Failed to get cached tx history.', err);
return cb(err, []);
}
var start = getLatest ? 0 : cachedTxs.length;
fetchTxHistoryByPage(wallet, start, function onFetchHistory(err, fetchedTxs){
if (err) {
return cb(err);
}
if (fetchedTxs.length === 0) {
return cb(null, cachedTxs, true /*fetchedAllTransactions*/);
}
var txs = [];
if (getLatest) {
txs = addLatestTransactions(wallet.id, cachedTxs, fetchedTxs);
} else {
allTransactionsFetched = false;
txs = addEarlyTransactions(wallet.id, cachedTxs, fetchedTxs);
return cb(null, txs, allTransactionsFetched);
}
return cb(null, txs);
});
});
}
}
}
})();

View file

@ -396,6 +396,23 @@ angular.module('copayApp.services').factory('walletService', function($log, $tim
return ret;
};
var skipped = 0;
function fixTxsUnit(txs) {
if (!txs || !txs[0] || !txs[0].amountStr) return;
var cacheCoin = txs[0].amountStr.split(' ')[1];
if (cacheCoin == 'bits') {
$log.debug('Fixing Tx Cache Unit to: ' + wallet.coin)
lodash.each(txs, function(tx) {
tx.amountStr = txFormatService.formatAmountStr(wallet.coin, tx.amount);
tx.feeStr = txFormatService.formatAmountStr(wallet.coin, tx.fees);
});
}
};
var updateLocalTxHistory = function(wallet, opts, cb) {
var FIRST_LIMIT = 5;
var LIMIT = 50;
@ -406,25 +423,10 @@ angular.module('copayApp.services').factory('walletService', function($log, $tim
var progressFn = opts.progressFn || function() {};
var foundLimitTx = false;
if (opts.feeLevels) {
opts.lowAmount = root.getLowAmount(wallet, opts.feeLevels);
}
var fixTxsUnit = function(txs) {
if (!txs || !txs[0] || !txs[0].amountStr) return;
var cacheCoin = txs[0].amountStr.split(' ')[1];
if (cacheCoin == 'bits') {
$log.debug('Fixing Tx Cache Unit to: ' + wallet.coin)
lodash.each(txs, function(tx) {
tx.amountStr = txFormatService.formatAmountStr(wallet.coin, tx.amount);
tx.feeStr = txFormatService.formatAmountStr(wallet.coin, tx.fees);
});
}
};
getSavedTxs(walletId, function(err, txsFromLocal) {
if (err) return cb(err);
@ -435,13 +437,14 @@ angular.module('copayApp.services').factory('walletService', function($log, $tim
var endingTxid = confirmedTxs[0] ? confirmedTxs[0].txid : null;
var endingTs = confirmedTxs[0] ? confirmedTxs[0].time : null;
$log.debug('Confirmed TXs. Got:' + confirmedTxs.length + '/' + txsFromLocal.length);
console.log('pagination Hard confirmed TXs. Got:' + confirmedTxs.length + '/' + txsFromLocal.length);
// First update
progressFn(txsFromLocal, 0);
wallet.completeHistory = txsFromLocal;
function getNewTxs(newTxs, skip, next) {
console.log('pagination getNewTxs skip: ' + skip);
getTxsFromServer(wallet, skip, endingTxid, requestLimit, function(err, res) {
if (err) {
$log.warn(bwcError.msg(err, 'Server Error')); //TODO
@ -454,6 +457,7 @@ angular.module('copayApp.services').factory('walletService', function($log, $tim
return next(err);
}
console.log('pagination Result count: ' + res.length);
// Check if new txs are founds, if yes, lets investigate in the 50 next
// To be sure we are not missing txs by sorting (maybe a new tx is after the "endingTxid"
var newDiscoveredTxs = res.filter(function (x) {
@ -462,7 +466,7 @@ angular.module('copayApp.services').factory('walletService', function($log, $tim
}).length == 0;
});
$log.debug('Discovering TXs. Got:' + newDiscoveredTxs.length);
console.log('pagination Discovering new TXs. Got:' + newDiscoveredTxs.length);
var shouldContinue = newDiscoveredTxs.length > 0;
@ -475,7 +479,7 @@ angular.module('copayApp.services').factory('walletService', function($log, $tim
skip = skip + requestLimit;
$log.debug('Syncing TXs. Got:' + newTxs.length + ' Skip:' + skip, ' EndingTxid:', endingTxid, ' Continue:', shouldContinue);
console.log('pagination Syncing TXs. Got:' + newTxs.length + ' Skip:' + skip, ' EndingTxid:', endingTxid, ' Continue:', shouldContinue);
// TODO Dirty <HACK>
// do not sync all history, just looking for a single TX.
@ -492,14 +496,16 @@ angular.module('copayApp.services').factory('walletService', function($log, $tim
}
// </HACK>
shouldContinue = false;
skipped = skip;
if (!shouldContinue) {
$log.debug('Finished Sync: New / soft confirmed Txs: ' + newTxs.length);
console.log('pagination Finished Sync: New / soft confirmed Txs: ' + newTxs.length);
return next(null, newTxs);
}
requestLimit = LIMIT;
getNewTxs(newTxs, skip, next);
});
};
@ -535,6 +541,87 @@ angular.module('copayApp.services').factory('walletService', function($log, $tim
});
}
if (opts.getMoreTxs) {
var requestLimit = LIMIT;
getNewTxs([], skipped, function(err, txs) {
if (err) return cb(err);
console.log();
createReceivedEvents(txs);
var newHistory = lodash.uniq(lodash.compact(txs.concat(confirmedTxs)), function(x) {
return x.txid;
});
function updateNotes(cb2) {
if (!endingTs) return cb2();
$log.debug('Syncing notes from: ' + endingTs);
wallet.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();
});
}
function updateLowAmount(txs) {
if (!opts.lowAmount) return;
lodash.each(txs, function(tx) {
tx.lowAmount = tx.amount < opts.lowAmount;
});
};
updateLowAmount(txs);
updateNotes(function() {
// <HACK>
if (foundLimitTx) {
$log.debug('Tx history read until limitTx: ' + opts.limitTx);
return cb(null, newHistory);
}
// </HACK>
var historyToSave = JSON.stringify(newHistory);
lodash.each(txs, function(tx) {
tx.recent = true;
})
$log.debug('Tx History synced. Total Txs: ' + newHistory.length);
// Final update
if (walletId == wallet.credentials.walletId) {
wallet.completeHistory = newHistory;
}
return storageService.setTxHistory(historyToSave, walletId, function() {
$log.debug('Tx History saved.');
return cb(null, newHistory);
});
});
});
return;
}
skipped = 0;
getNewTxs([], 0, function(err, txs) {
if (err) return cb(err);
@ -603,14 +690,19 @@ angular.module('copayApp.services').factory('walletService', function($log, $tim
return storageService.setTxHistory(historyToSave, walletId, function() {
$log.debug('Tx History saved.');
return cb();
return cb(null, newHistory);
});
});
});
});
};
root.getMoreTxs = function(wallet, cb) {
var opts = {};
opts.getMoreTxs = true;
updateLocalTxHistory(wallet, opts, cb);
};
root.getTxNote = function(wallet, txid, cb) {
wallet.getTxNote({
txid: txid
@ -634,7 +726,6 @@ angular.module('copayApp.services').factory('walletService', function($log, $tim
};
root.getTx = function(wallet, txid, cb) {
function finish(list) {
var tx = lodash.find(list, {
txid: txid
@ -651,7 +742,6 @@ angular.module('copayApp.services').factory('walletService', function($log, $tim
limitTx: txid
}, function(err, txHistory) {
if (err) return cb(err);
finish(txHistory);
});
}
@ -675,16 +765,10 @@ angular.module('copayApp.services').factory('walletService', function($log, $tim
root.getTxHistory = function(wallet, opts, cb) {
opts = opts || {};
var walletId = wallet.credentials.walletId;
if (!wallet.isComplete()) return cb();
function isHistoryCached() {
return wallet.completeHistory && wallet.completeHistory.isValid;
};
// disable caching
//if (isHistoryCached() && !opts.force) return cb(null, wallet.completeHistory);
// var historyIsCached = wallet.completeHistory && wallet.completeHistory.isValid;
// if (historyIsCached && !opts.force) return cb(null, wallet.completeHistory);
$log.debug('Updating Transaction History');

View file

@ -1,5 +1,5 @@
%button-standard {
width: 85%;
width: 90%;
max-width: 300px;
margin-left: auto;
margin-right: auto;

View file

@ -1,11 +1,11 @@
.fee-summary {
position: relative;
background-color: #F2F2F2;
box-sizing: border-box;
display: flex;
flex-direction: column;
width: 100%;
padding: 5px 12px 15px;
box-sizing: border-box;
background-color: #F2F2F2;
position: relative;
width: 100%;
&:before {
content: '';
@ -18,12 +18,11 @@
}
.amount {
display: flex;
flex-direction: row;
justify-content: space-between;
width: 100%;
.fee-fiat {
display: inline;
&.positive {
color: #70955F;
}
@ -35,6 +34,7 @@
.fee-crypto {
color: #A7A7A7;
float: right;
}
}
}

View file

@ -27,6 +27,7 @@
left: 13px;
top: 50%;
padding: 0;
-webkit-transform: translate(0,-50%);
transform: translate(0,-50%);
}
}

View file

@ -59,6 +59,8 @@
.button {
font-weight: bold;
font-size: 19px;
line-height: 26px;
padding: 8px 6px;
}
}
.button-first-contact img {

View file

@ -350,6 +350,7 @@
.primary-amount-display {
margin-right: 5px;
word-break: break-all;
width: 100%;
}
}

View file

@ -6,8 +6,10 @@
}
.fee-summary {
position: absolute;
bottom: 92px;
bottom: calc(92px + constant(safe-area-inset-bottom)); /* iOS 11.0 */
bottom: calc(92px + env(safe-area-inset-bottom)); /* iOS 11.2 */
position: absolute;
}
.shapeshift-banner, .bitpay-banner, .egifter-banner {
@ -17,4 +19,5 @@
.warning {
color: $v-warning-color-2;
}
}

View file

@ -83,14 +83,14 @@
.button {
border: 2px solid;
border-radius: 47px;
padding: 0 15px 0 15px;
padding: 8px 2px 8px 2px;
text-align: center;
width: 100%;
max-width: 300px;
font-size: 19px;
font-weight: bolder;
min-height: auto;
line-height: 36px;
min-height: 0;
line-height: 19px;
}
}
.wallet-coin-logo {
@ -173,6 +173,11 @@
font-weight: 700;
color: #444;
}
.release-notes {
white-space: pre;
white-space: pre-line;
text-align: left;
}
.button {
width: 100%;
border: none;
@ -190,3 +195,13 @@
top:11px;
}
}
.popup-update {
.popup-buttons {
display: block;
}
.popup-buttons .button{
display:block;
min-width: 100% !important;
margin-top: 4px;
}
}

View file

@ -84,6 +84,9 @@
width: 100%;
}
.payment-received-container {
svg {
max-height: 400px;
}
margin: 0 20px;
.payment-received-amount {
font-size: 1.8em;

View file

@ -105,7 +105,7 @@
width: auto;
margin: 2px 0 4px;
}
height: 60px;
min-height: 65px;
line-height: 16px;
margin-right: 0px;
width: 95%;

View file

@ -1,3 +1,4 @@
$wallet-details-collapse-transition: all 0.25s ease-in-out;
.wallet-details {
&__tx-amount {
font-size: 16px;
@ -137,6 +138,20 @@
margin-top: 20px;
margin-top: env(safe-area-inset-top);
}
&.collapse {
ion-content {
margin-top: 40px;
}
.amount {
&__scale, &__error {
-webkit-transform: scale3d(0.5, 0.5, 0.5) translateY(0px);
transform: scale3d(0.5, 0.5, 0.5) translateY(0px);
}
}
.amount-alternative, .send-receive-buttons, .wallet-details-wallet-info {
opacity: 0;
}
}
}
.bar-header {
border: 0;
@ -152,13 +167,14 @@
background-color: inherit !important;
}
ion-content {
&.collapsible {
margin-top: 230px;
}
padding-top: 0;
top: 0;
transition: $wallet-details-collapse-transition;
margin-top: 185px;
@media only screen and (max-height:500px) {
margin-top: 165px;
}
margin-bottom: 16px;
.scroll {
@ -199,7 +215,7 @@
width: 100%;
position: absolute;
bottom: 20px;
transition: $wallet-details-collapse-transition;
>.col {
padding: 5px 10px;
margin-bottom: 0;
@ -207,30 +223,37 @@
.button {
border: 2px solid;
border-radius: 47px;
padding: 0 15px 0 15px;
padding: 6px 2px 6px 2px;
text-align: center;
width: 100%;
max-width: 300px;
font-size: 19px;
font-weight: bolder;
min-height: auto;
line-height: 36px;
min-height: 0;
line-height: 19px;
}
}
}
.amount {
width: 100%;
text-align: center;
color: #fff;
height: 230px;
padding-top: 40px;
display: block;
align-items: center;
color: #fff;
display: block;
height: 230px;
@media only screen and (max-height:500px) {
height: 210px;
}
justify-content: center;
padding-top: 40px;
text-align: center;
transition: $wallet-details-collapse-transition;
width: 100%;
&__balance {
-webkit-transform: scale3d(1, 1, 1) translateY(45px);
transform: scale3d(1, 1, 1) translateY(45px);
transition: $wallet-details-collapse-transition;
}
&__updating {
@ -240,6 +263,7 @@
&-alternative {
line-height: 36px;
transition: $wallet-details-collapse-transition;
}
&__button-balance {
@ -255,6 +279,7 @@
&__error {
font-size: 14px;
padding: 35px 20px;
opacity: 1;
}
}
@ -341,4 +366,6 @@ a.item {
.loading-wallet svg {
margin-top: 0;
width: 16px;
height: 16px;
}

View file

@ -8,4 +8,56 @@ if (!ArrayBuffer['isView']) {
ArrayBuffer.isView = function(a) {
return a !== null && typeof(a) === "object" && a['buffer'] instanceof ArrayBuffer;
};
}
// https://tc39.github.io/ecma262/#sec-array.prototype.includes
if (!Array.prototype.includes) {
Object.defineProperty(Array.prototype, 'includes', {
value: function(searchElement, fromIndex) {
if (this == null) {
throw new TypeError('"this" is null or not defined');
}
// 1. Let O be ? ToObject(this value).
var o = Object(this);
// 2. Let len be ? ToLength(? Get(O, "length")).
var len = o.length >>> 0;
// 3. If len is 0, return false.
if (len === 0) {
return false;
}
// 4. Let n be ? ToInteger(fromIndex).
// (If fromIndex is undefined, this step produces the value 0.)
var n = fromIndex | 0;
// 5. If n ≥ 0, then
// a. Let k be n.
// 6. Else n < 0,
// a. Let k be len + n.
// b. If k < 0, let k be 0.
var k = Math.max(n >= 0 ? n : len - Math.abs(n), 0);
function sameValueZero(x, y) {
return x === y || (typeof x === 'number' && typeof y === 'number' && isNaN(x) && isNaN(y));
}
// 7. Repeat, while k < len
while (k < len) {
// a. Let elementK be the result of ? Get(O, ! ToString(k)).
// b. If SameValueZero(searchElement, elementK) is true, return true.
if (sameValueZero(o[k], searchElement)) {
return true;
}
// c. Increase k by 1.
k++;
}
// 8. Return false
return false;
}
});
}

View file

@ -17,7 +17,7 @@ module.exports = function(config) {
files: [
'node_modules/angular/angular.js',
'bitanalytics/bitanalytics-0.1.0.js',
'bitanalytics/bitanalytics.js',
// From Gruntfile.js
'bower_components/qrcode-generator/js/qrcode.js',

View file

@ -10083,7 +10083,7 @@ ion-view.deflash-blue:before, ion-view#view-amount:before, ion-view#view-confirm
.onboarding .button.button-white.button-standard,
.onboarding .button.button-green.button-standard,
.onboarding .button.button-assertive.button-standard, #shapeshift .button-shapeshift {
width: 85%;
width: 90%;
max-width: 300px;
margin-left: auto;
margin-right: auto;
@ -10195,6 +10195,7 @@ ion-view.deflash-blue:before, ion-view#view-amount:before, ion-view#view-confirm
.button {
border-radius: 6px; }
.button.button-full {
border-radius: 0;
display: block; }
.button-green {
border-color: #FFF;
@ -10263,8 +10264,8 @@ ion-view.deflash-blue:before, ion-view#view-amount:before, ion-view#view-confirm
color: #FFFFFF;
text-decoration: none; }
.button-white-outline.active, .button-white-outline.activated {
border-color: #FFF;
background-color: #FAFAFA; }
border-color: #FFFFFF;
background-color: #FFFFFF; }
.button-white-outline.button-outline {
border-color: #FFFFFF;
background: transparent;
@ -10632,7 +10633,7 @@ qrcode {
#view-amount .scroll-content .send-amount .send-amount-tool .send-amount-tool-input .primary-amount.long input, #view-amount .scroll-content .send-amount .send-amount-tool .send-amount-tool-input .primary-amount.long .unit, #view-amount .scroll-content .send-amount .send-amount-tool .send-amount-tool-input .primary-amount.long .primary-amount-display {
font-size: 2em; } }
#view-amount .scroll-content .send-amount .send-amount-tool .send-amount-tool-input .primary-amount.very-long input, #view-amount .scroll-content .send-amount .send-amount-tool .send-amount-tool-input .primary-amount.very-long .unit, #view-amount .scroll-content .send-amount .send-amount-tool .send-amount-tool-input .primary-amount.very-long .primary-amount-display {
font-size: 0.9em; }
font-size: 1.2em; }
@media (min-width: 375px) {
#view-amount .scroll-content .send-amount .send-amount-tool .send-amount-tool-input .primary-amount.very-long input, #view-amount .scroll-content .send-amount .send-amount-tool .send-amount-tool-input .primary-amount.very-long .unit, #view-amount .scroll-content .send-amount .send-amount-tool .send-amount-tool-input .primary-amount.very-long .primary-amount-display {
font-size: 1.3em; } }
@ -10659,7 +10660,8 @@ qrcode {
line-height: 1em; }
#view-amount .scroll-content .send-amount .send-amount-tool .send-amount-tool-input .primary-amount .primary-amount-display {
margin-right: 5px;
word-break: break-all; }
word-break: break-all;
width: 100%; }
#view-amount .scroll-content .send-amount .send-amount-tool .send-amount-tool-input .alternative-amount {
color: #6F6F70; }
#view-amount .scroll-content .send-amount .send-amount-tool .send-amount-tool-input .switch-currencies {
@ -10680,30 +10682,33 @@ qrcode {
justify-content: space-between;
margin: 0 14px; }
#view-amount .scroll-content .send-amount-extras .available-funds {
color: #6F6F70; }
color: #6F6F70;
text-align: left; }
#view-amount .scroll-content .send-amount-extras .change-currency {
text-align: right; }
#view-amount .scroll-content .send-amount-extras .warning {
color: #b7664d; }
#view-amount .scroll-content .send-amount-extras .extra,
#view-amount .scroll-content .send-amount-extras button.extra {
/*display: flex;*/
flex: 0 1 auto; }
#view-amount .scroll-content .send-amount-extras button.extra {
background: none;
border: none;
color: #000;
font-family: 'ProximaNova';
font-size: 14px;
line-height: normal;
min-height: auto;
min-width: auto;
padding: 0; }
#view-amount .scroll-content .send-amount-extras .button .icon:before {
font-size: 14px;
#view-amount .scroll-content .send-amount-extras .extra {
flex: 1;
line-height: normal; }
#view-amount .scroll-content .send-amount-extras .button span {
display: flex;
align-items: center;
justify-content: center; }
#view-amount .scroll-content .send-amount-extras .extra .button {
background: none;
border: none;
border-radius: 0;
color: #000;
font-family: 'ProximaNova';
font-size: 14px;
line-height: normal;
min-height: auto;
min-width: auto;
padding: 0; }
#view-amount .scroll-content .send-amount-extras .extra .button .icon:before {
font-size: 14px;
line-height: normal; }
#view-amount .scroll-content .send-amount-extras .extra .button span {
display: flex;
align-items: center;
justify-content: center; }
#view-amount .scroll-content .button.no-margin {
margin: 0; }
#view-amount .scroll-content .notification-warning {
@ -10939,14 +10944,14 @@ qrcode {
#tab-home .buttons .button {
border: 2px solid;
border-radius: 47px;
padding: 0 15px 0 15px;
padding: 8px 2px 8px 2px;
text-align: center;
width: 100%;
max-width: 300px;
font-size: 19px;
font-weight: bolder;
min-height: auto;
line-height: 36px; }
min-height: 0;
line-height: 19px; }
#tab-home .wallet-coin-logo {
vertical-align: middle;
@ -11013,6 +11018,10 @@ qrcode {
#tab-home .release .title {
font-weight: 700;
color: #444; }
#tab-home .release .release-notes {
white-space: pre;
white-space: pre-line;
text-align: left; }
#tab-home .release .button {
width: 100%;
border: none; }
@ -11025,6 +11034,14 @@ qrcode {
#tab-home .badge {
top: 11px; }
.popup-update .popup-buttons {
display: block; }
.popup-update .popup-buttons .button {
display: block;
min-width: 100% !important;
margin-top: 4px; }
#tab-receive .button-share {
color: #fff;
box-shadow: none;
@ -11101,6 +11118,8 @@ qrcode {
#tab-receive .payment-received-container {
margin: 0 20px; }
#tab-receive .payment-received-container svg {
max-height: 400px; }
#tab-receive .payment-received-container .payment-received-amount {
font-size: 1.8em;
display: block;
@ -11207,7 +11226,7 @@ qrcode {
margin: auto;
margin-top: 18px; }
#tab-send .send-wrapper .buttons .button {
height: 60px;
min-height: 65px;
line-height: 16px;
margin-right: 0px;
width: 95%;
@ -11284,7 +11303,9 @@ qrcode {
margin-top: 18px; }
#tab-send .sendTip .buttons .button {
font-weight: bold;
font-size: 19px; }
font-size: 19px;
line-height: 26px;
padding: 8px 6px; }
#tab-send .sendTip .button-first-contact img {
height: 19px;
width: 19px;
@ -11920,6 +11941,13 @@ qrcode {
#walletDetails .bp-content.status-bar {
margin-top: 20px;
margin-top: env(safe-area-inset-top); }
#walletDetails .bp-content.collapse ion-content {
margin-top: 40px; }
#walletDetails .bp-content.collapse .amount__scale, #walletDetails .bp-content.collapse .amount__error {
-webkit-transform: scale3d(0.5, 0.5, 0.5) translateY(0px);
transform: scale3d(0.5, 0.5, 0.5) translateY(0px); }
#walletDetails .bp-content.collapse .amount-alternative, #walletDetails .bp-content.collapse .send-receive-buttons, #walletDetails .bp-content.collapse .wallet-details-wallet-info {
opacity: 0; }
#walletDetails .bar-header {
border: 0;
background: #eeb640; }
@ -11932,9 +11960,12 @@ qrcode {
#walletDetails ion-content {
padding-top: 0;
top: 0;
transition: all 0.25s ease-in-out;
margin-top: 185px;
margin-bottom: 16px; }
#walletDetails ion-content.collapsible {
margin-top: 230px; }
@media only screen and (max-height: 500px) {
#walletDetails ion-content {
margin-top: 165px; } }
#walletDetails ion-content .scroll {
background: #f8f8f9;
min-height: 300px; }
@ -11965,38 +11996,45 @@ qrcode {
justify-content: space-evenly;
width: 100%;
position: absolute;
bottom: 20px; }
bottom: 20px;
transition: all 0.25s ease-in-out; }
#walletDetails .amount-wrapper .send-receive-buttons > .col {
padding: 5px 10px;
margin-bottom: 0; }
#walletDetails .amount-wrapper .send-receive-buttons .button {
border: 2px solid;
border-radius: 47px;
padding: 0 15px 0 15px;
padding: 6px 2px 6px 2px;
text-align: center;
width: 100%;
max-width: 300px;
font-size: 19px;
font-weight: bolder;
min-height: auto;
line-height: 36px; }
min-height: 0;
line-height: 19px; }
#walletDetails .amount {
width: 100%;
text-align: center;
color: #fff;
height: 230px;
padding-top: 40px;
display: block;
align-items: center;
justify-content: center; }
color: #fff;
display: block;
height: 230px;
justify-content: center;
padding-top: 40px;
text-align: center;
transition: all 0.25s ease-in-out;
width: 100%; }
@media only screen and (max-height: 500px) {
#walletDetails .amount {
height: 210px; } }
#walletDetails .amount__balance {
-webkit-transform: scale3d(1, 1, 1) translateY(45px);
transform: scale3d(1, 1, 1) translateY(45px); }
transform: scale3d(1, 1, 1) translateY(45px);
transition: all 0.25s ease-in-out; }
#walletDetails .amount__updating {
z-index: 999;
margin-top: -2.1rem; }
#walletDetails .amount-alternative {
line-height: 36px; }
line-height: 36px;
transition: all 0.25s ease-in-out; }
#walletDetails .amount__button-balance {
background-color: transparent;
border: 1px solid rgba(255, 255, 255, 0.25);
@ -12006,7 +12044,8 @@ qrcode {
vertical-align: middle; }
#walletDetails .amount__error {
font-size: 14px;
padding: 35px 20px; }
padding: 35px 20px;
opacity: 1; }
#walletDetails .no-alternative {
padding-top: 45px; }
#walletDetails .item.item-footer {
@ -12071,7 +12110,9 @@ a.item {
font-size: 0.9em; }
.loading-wallet svg {
margin-top: 0; }
margin-top: 0;
width: 16px;
height: 16px; }
#advanced-settings .list .item:before {
display: block;
@ -12125,7 +12166,9 @@ a.item {
margin-top: 18px; }
#shapeshift .empty-case .buttons .button {
font-weight: bold;
font-size: 19px; }
font-size: 19px;
line-height: 26px;
padding: 8px 6px; }
#shapeshift .empty-case .button-first-contact img {
height: 19px;
width: 19px;
@ -13812,6 +13855,7 @@ click-to-accept {
height: 92px;
width: 100%; }
click-to-accept .click-to-accept__button.button.button-primary.button-standard {
border-radius: 0;
height: 100%;
max-width: 9999px;
width: 100%; }
@ -13918,6 +13962,8 @@ slide-to-accept {
height: 92px;
width: 100%;
background: #494949; }
slide-to-accept .slide .button {
border-radius: 0; }
slide-to-accept .slide__listener {
height: 100%;
width: 100%;
@ -15349,20 +15395,25 @@ log-options #check-bar .checkbox-icon {
#cash-scan a {
cursor: pointer; }
#view-review {
background-color: #494949; }
#view-review slide-to-accept, #view-review slide-to-accept-success {
margin-bottom: constant(safe-area-inset-bottom);
/* iOS 11.0 */
margin-bottom: env(safe-area-inset-bottom);
/* iOS 11.2 */ }
#view-review .fee-summary {
position: absolute;
bottom: 92px; }
#view-review .shapeshift-banner, #view-review .bitpay-banner, #view-review .egifter-banner {
box-shadow: none; }
#view-review .warning {
color: #b7664d; }
#view-review slide-to-accept, #view-review slide-to-accept-success {
margin-bottom: constant(safe-area-inset-bottom);
/* iOS 11.0 */
margin-bottom: env(safe-area-inset-bottom);
/* iOS 11.2 */ }
#view-review .fee-summary {
bottom: 92px;
bottom: calc(92px + constant(safe-area-inset-bottom));
/* iOS 11.0 */
bottom: calc(92px + env(safe-area-inset-bottom));
/* iOS 11.2 */
position: absolute; }
#view-review .shapeshift-banner, #view-review .bitpay-banner, #view-review .egifter-banner {
box-shadow: none; }
#view-review .warning {
color: #b7664d; }
.gravatar {
border-radius: 3px;
@ -15395,6 +15446,7 @@ log-options #check-bar .checkbox-icon {
left: 13px;
top: 50%;
padding: 0;
-webkit-transform: translate(0, -50%);
transform: translate(0, -50%); }
.item .item-content.item-content-compact {
@ -15463,8 +15515,8 @@ ion-content.padded-bottom-cta-with-summary {
overflow: hidden;
text-overflow: ellipsis; }
.address-frame.expanded {
white-space: pre-wrap;
word-break: break-all; }
white-space: normal;
text-overflow: clip; }
.address-frame .prefix {
color: #000000; }
.address-frame .mid {
@ -15507,13 +15559,13 @@ ion-content.padded-bottom-cta-with-summary {
transform: scale(1, 1); }
.fee-summary {
position: relative;
background-color: #F2F2F2;
box-sizing: border-box;
display: flex;
flex-direction: column;
width: 100%;
padding: 5px 12px 15px;
box-sizing: border-box;
background-color: #F2F2F2; }
position: relative;
width: 100%; }
.fee-summary:before {
content: '';
position: absolute;
@ -15523,16 +15575,16 @@ ion-content.padded-bottom-cta-with-summary {
height: 15px;
background: linear-gradient(to bottom, rgba(242, 242, 242, 0) 0%, #f2f2f2 100%); }
.fee-summary .amount {
display: flex;
flex-direction: row;
justify-content: space-between;
width: 100%; }
.fee-summary .amount .fee-fiat.positive {
color: #70955F; }
.fee-summary .amount .fee-fiat.negative {
color: #C24633; }
.fee-summary .amount .fee-fiat {
display: inline; }
.fee-summary .amount .fee-fiat.positive {
color: #70955F; }
.fee-summary .amount .fee-fiat.negative {
color: #C24633; }
.fee-summary .amount .fee-crypto {
color: #A7A7A7; }
color: #A7A7A7;
float: right; }
.formatted-amount {
display: inline-block; }
@ -15557,6 +15609,9 @@ ion-content.padded-bottom-cta-with-summary {
margin-left: 5px;
text-transform: uppercase; }
.wallet-balance-directive {
display: inline-block; }
/* This is for rules that don't yet have a home.
* Our goal is to delete this file. Search the regex: /class=".*CLASS.*?"/
*/

View file

@ -53,11 +53,12 @@
</div>
<div class="keypad-container" style="background: #fff; position: absolute; bottom: 0; margin-bottom: 57px; width: 100%;">
<div class="sendmax" ng-if="vm.availableFunds && !vm.isRequestingSpecificAmount">
<div class="sendmax" ng-if="vm.showSendMaxButton || vm.showSendLimitMaxButton">
<button class="button button-sendmax" ng-click="vm.sendMax()">
<span>
<span translate>Use All Available Funds</span>&ensp;
<span class="available-funds-amount">(<formatted-amount value="{{vm.availableFunds}}"></formatted-amount>)</span>
<span ng-if="vm.showSendMaxButton" translate>Use All Available Funds</span>
<span ng-if="vm.showSendLimitMaxButton" translate>Send Maximum Amount</span>&ensp;
<span class="available-funds-amount">(<formatted-amount value="{{vm.sendableFunds}}"></formatted-amount>)</span>
</span>
</button>
</div>

View file

@ -26,7 +26,7 @@
<i class="icon big-icon-svg theme-circle theme-circle-community">
<div class="bg icon-share"></div>
</i>
<span>Share the Wallet App</span>
<span translate>Share the Wallet App</span>
<i class="icon bp-arrow-right"></i>
</a>
</div>

View file

@ -9,21 +9,21 @@
<img src="img/icon-bitcoin-small.svg">
</div>
<div class="incoming-data-menu__url__text">
{{data}}
{{data.original}}
</div>
</div>
</div>
<a class="incoming-data-menu__item item item-icon-right" ng-click="addToAddressBook(data)">
<a class="incoming-data-menu__item item item-icon-right" ng-click="addToAddressBook(data.toAddress)">
<img src="img/icon-contacts.svg">
<div class="incoming-data-menu__item__text" translate>Add as a contact</div>
<i class="icon bp-arrow-right"></i>
</a>
<a class="incoming-data-menu__item item item-icon-right" ng-click="sendPaymentToAddress(data)">
<a class="incoming-data-menu__item item item-icon-right" ng-click="sendPaymentToAddress(data.toAddress)">
<img src="img/icon-send-alt.svg">
<div class="incoming-data-menu__item__text" translate>Send payment to this address</div>
<i class="icon bp-arrow-right"></i>
</a>
<a class="incoming-data-menu__item item item-icon-right" copy-to-clipboard="data">
<a class="incoming-data-menu__item item item-icon-right" copy-to-clipboard="data.original">
<img src="img/icon-paperclip.svg">
<div class="incoming-data-menu__item__text" translate>Copy to clipboard</div>
<i class="icon bp-arrow-right"></i>
@ -38,11 +38,11 @@
<div class="incoming-data-menu__header" translate>Text</div>
<div class="incoming-data-menu__url">
<div class="incoming-data-menu__url__text" style="border: 0;">
{{data}}
{{data.original}}
</div>
</div>
</div>
<a class="incoming-data-menu__item item item-icon-right" copy-to-clipboard="data">
<a class="incoming-data-menu__item item item-icon-right" copy-to-clipboard="data.original">
<img src="img/icon-paperclip.svg">
<div class="incoming-data-menu__item__text" translate>Copy to clipboard</div>
<i class="icon bp-arrow-right"></i>
@ -57,16 +57,40 @@
<div class="incoming-data-menu__header" translate>Private Key</div>
<div class="incoming-data-menu__url">
<div class="incoming-data-menu__url__text" style="border: 0;">
{{data}}
{{data.original}}
</div>
</div>
</div>
<a class="incoming-data-menu__item item item-icon-right" ng-click="scanPaperWallet(data)">
<a class="incoming-data-menu__item item item-icon-right" ng-click="scanPaperWallet(data.original)">
<img src="img/icon-import.svg">
<div class="incoming-data-menu__item__text" translate>Sweep paper wallet</div>
<i class="icon bp-arrow-right"></i>
</a>
<a class="incoming-data-menu__item item item-icon-right" copy-to-clipboard="data">
<a class="incoming-data-menu__item item item-icon-right" copy-to-clipboard="data.original">
<img src="img/icon-paperclip.svg">
<div class="incoming-data-menu__item__text" translate>Copy to clipboard</div>
<i class="icon bp-arrow-right"></i>
</a>
<a class="incoming-data-menu__cancel item" ng-click="hide()" translate>
Cancel
</a>
</div>
<div ng-if="type === 'url'">
<div class="incoming-data-menu__item head">
<div class="incoming-data-menu__header" translate>URL</div>
<div class="incoming-data-menu__url">
<div class="incoming-data-menu__url__text" style="border: 0;">
{{data.original}}
</div>
</div>
</div>
<a class="incoming-data-menu__item item item-icon-right" ng-click="goToUrl(data.original)">
<img src="img/icon-link-external.svg">
<div class="incoming-data-menu__item__text" translate>Open in web browser</div>
<i class="icon bp-arrow-right"></i>
</a>
<a class="incoming-data-menu__item item item-icon-right" copy-to-clipboard="data.original">
<img src="img/icon-paperclip.svg">
<div class="incoming-data-menu__item__text" translate>Copy to clipboard</div>
<i class="icon bp-arrow-right"></i>

View file

@ -6,7 +6,14 @@
</div>
<div ng-show="!hide">
<div ng-repeat="service in services track by $index">
<a ui-sref="{{service.sref}}" id="home_{{service.name}}" title="{{service.title || service.name}}" class="track_link_click_out item item-sub item-icon-left item-big-icon-left item-icon-right next-step">
<a ng-if="service.sref" ui-sref="{{service.sref}}" id="home_{{service.name}}" title="{{service.title || service.name}}" class="track_link_click_out item item-sub item-icon-left item-big-icon-left item-icon-right next-step">
<i class="icon big-icon-svg theme-circle theme-circle-services">
<div class="bg {{service.icon}}"></div>
</i>
<span>{{service.title || service.name}}</span>
<i class="icon bp-arrow-right"></i>
</a>
<a ng-if="!service.sref" ng-click="open('{{service.href}}')" id="home_{{service.name}}" title="{{service.title || service.name}}" class="track_link_click_out item item-sub item-icon-left item-big-icon-left item-icon-right next-step">
<i class="icon big-icon-svg theme-circle theme-circle-services">
<div class="bg {{service.icon}}"></div>
</i>

View file

@ -3,9 +3,9 @@
<a ng-show="walletNotRegistered" ng-click="recreate()" translate>Tap to recreate</a>
<a ng-show="updateStatusError" ng-click="updateAll(true)" translate>Tap to retry</a>
</div>
<span ng-click="onRefresh()" class="right" ng-show="(!updatingStatus && !updatingTxHistory)">&#8635;</span>
<ion-spinner class="spinner-dark recent right loading-wallet" icon="crescent" ng-show="(updatingStatus || updatingTxHistory) &&
!walletNotRegistered && !updateStatusError && !updateTxHistoryError"></ion-spinner>
<span ng-click="onRefresh()" class="right" ng-show="(!updatingStatus && !vm.updatingTxHistory && !vm.gettingInitialHistory)">&#8635;</span>
<ion-spinner class="spinner-dark recent right loading-wallet" icon="crescent" ng-show="(updatingStatus || vm.updatingTxHistory || vm.gettingInitialHistory) &&
!walletNotRegistered && !updateStatusError && !vm.updateTxHistoryFailed"></ion-spinner>
<div>
<span ng-show="wallet.status.wallet.singleAddress" class="size-12"><span translate>Auditable</span></span>

View file

@ -1,6 +1,6 @@
<ion-view hide-tabs>
<ion-nav-bar class="bar-royal">
<ion-nav-title>{{'Sweep paper wallet' | translate}}</ion-nav-title>
<ion-nav-title>{{'Sweep Paper Wallet' | translate}}</ion-nav-title>
<ion-nav-back-button>
</ion-nav-back-button>
</ion-nav-bar>
@ -45,19 +45,19 @@
</div>
<div ng-class="ng-hide" ng-show="noMatchingBchWallet">
<div class="text-center size-12 text-gray">
<span>No Bitcoin Cash wallet to transfer funds to found.</span>
<span translate>No Bitcoin Cash wallet to transfer funds to found.</span>
</div>
</div>
</div>
<div ng-class="ng-hide" ng-show="!bchBalance && readyToShow">
<div class="text-center">
<h4 class="text-bold" translate>No Bitcoin Cash found</h4>
<h4 class="text-bold" translate>No Bitcoin Cash found.</h4>
</div>
</div>
<div ng-class="ng-hide" ng-show="btcBalance">
<div class="row">
<div class="col text-center">
<h4 class="text-bold" translate>Bitcoin found:</h4>
<h4 class="text-bold" translate>Bitcoin Core found:</h4>
<div class="size-24">
<span>{{btcBalanceText}}</span>
<div class="size-14 amount-alternative">
@ -95,13 +95,13 @@
</div>
<div ng-class="ng-hide" ng-show="noMatchingBtcWallet">
<div class="text-center size-12 text-gray">
<span>No Bitcoin wallet to transfer funds to found.</span>
<span translate>No Bitcoin Core wallet to transfer funds to found.</span>
</div>
</div>
</div>
<div ng-class="ng-hide" ng-show="!btcBalance && readyToShow">
<div class="text-center">
<h4 class="text-bold" translate>No Bitcoin found</h4>
<h4 class="text-bold" translate>No Bitcoin Core found.</h4>
</div>
</div>
<slide-to-accept-success

View file

@ -15,9 +15,11 @@
<img src="img/icon-update.svg" class="bg"/>
</i>
<div class="item title">{{updateText}}</div>
<div class="item title">{{newReleaseText}}</div>
<div class="item release-notes" ng-if="newReleaseNotes"><span ng-bind-html="newReleaseNotes"></span></div>
<div class="button" ng-click="openExternalLink()">
<div class="button" ng-click="showUpdatePopup()">
<span translate>Download</span>
</div>
</div>

View file

@ -31,7 +31,7 @@
<div ng-show="!showingPaymentReceived">
<div>
<button ng-show="addr" class="button button-standard button-small" ng-click="requestSpecificAmount()">
<span translate>Request Specific Amount</span>
<span translate>Request Specific amount</span>
</button><br/><br/>
</div>
<div copy-to-clipboard="wallet.coin == 'bch' && bchAddressType.type == 'cashaddr' ? 'bitcoincash:' + addr : addr">
@ -69,8 +69,8 @@
<!-- end of animation -->
<div ng-show="addr && wallet.coin == 'bch' && !showingPaymentReceived">
<label class="item-input item-select">
<div class="input-label" translate>
Address Type:
<div class="input-label">
<span translate>Address Type</span>:
</div>
<select ng-model="bchAddressType.type" ng-change="displayAddress(bchAddressType.type)">
<option value="cashaddr">Cash Address</option>

View file

@ -17,14 +17,13 @@
</i>
<h2>{{fromWallet.name}}</h2>
<wallet-balance
display-as-fiat="{{displayBalanceAsFiat}}"
display-as-fiat="{{displayBalanceAsFiat}}"
wallet-coin="{{fromWallet.coin}}"
wallet-status="{{fromWallet.status}}"
wallet-cached-balance="{{fromWallet.cachedBalance}}"
wallet-cached-balance-updated-on="{{fromWallet.cachedBalanceUpdatedOn}}"
wallet-cached-status="{{fromWallet.cachedStatus}}"
total-balance-sat="{{fromWallet.status.totalBalanceSat}}"></wallet-balance>
<!--<p ng-show="vm.origin.balanceAmount">{{vm.origin.balanceAmount}} {{vm.origin.balanceCurrency}}</p>-->
<!--<formatted-amount value="{{fromWallet.status.totalBalanceStr ? fromWallet.status.totalBalanceStr : ( fromWallet.cachedBalance ? fromWallet.cachedBalance + (fromWallet.cachedBalanceUpdatedOn ? ' &middot; ' + ( fromWallet.cachedBalanceUpdatedOn * 1000 | amTimeAgo) : '') : '' ) }}"></formatted-amount>-->
</div>
</div>
</div>

View file

@ -16,79 +16,74 @@
}
</style>
<div class="bp-content" ng-class="{'status-bar': isCordova}">
<div class="bp-content" ng-class="{'status-bar': isCordova, 'collapse': scrollPosition > 50}">
<div class="amount-wrapper" ng-show="wallet && wallet.isComplete() && amountIsCollapsible" ng-class="{'wallet-background-color-default': !wallet.color}" ng-style="{'background-color':wallet.color}">
<div class="amount-wrapper" ng-show="wallet && wallet.isComplete()" ng-class="{'wallet-background-color-default': !wallet.color}" ng-style="{'background-color':wallet.color}">
<div
ng-style="{'background-color':wallet.color, 'height': amountHeight}"
class="amount"
ng-class="{collapsible: amountIsCollapsible, 'wallet-background-color-default': !wallet.color, 'no-alternative': wallet.network != 'livenet'}"
ng-style="{'background-color':wallet.color}"
class="amount collapsible"
ng-class="{'wallet-background-color-default': !wallet.color, 'no-alternative': wallet.network != 'livenet'}"
>
<div class="amount__error" ng-style="{opacity: altAmountOpacity}" ng-show="updateStatusError">
<div class="amount__error" ng-show="updateStatusError">
<span>{{updateStatusError}}</span>
</div>
<div class="amount__error" ng-style="{opacity: altAmountOpacity}" ng-show="walletNotRegistered">
<div class="amount__error" ng-show="walletNotRegistered">
<span translate>This wallet is not registered at the given Bitcore Wallet Service (BWS). You can recreate it from the local information.</span>
</div>
<div
ng-show="selectedPriceDisplay=='fiat' && !updateStatusError && !wallet.balanceHidden && !wallet.scanning"
ng-if="selectedPriceDisplay=='fiat' && !updateStatusError && !wallet.balanceHidden && !wallet.scanning"
on-hold="hideToggle()"
ng-style="{'transform': amountScale}"
ng-class="{amount__balance: amountIsCollapsible}">
class="amount__balance amount__scale">
<strong class="size-36" ng-show="status.totalBalanceAlternative">
<formatted-amount value="{{status.totalBalanceAlternative}}" currency="{{status.alternativeIsoCode}}"></formatted-amount>
</strong>
<div
class="size-14 amount-alternative"
ng-if="status.totalBalanceStr && wallet.network == 'livenet'"
ng-style="{opacity: altAmountOpacity}">
ng-if="status.totalBalanceStr && wallet.network == 'livenet'">
<formatted-amount value="{{status.totalBalanceStr}}" size-equal="true"></formatted-amount>
</div>
</div>
<div
ng-show="selectedPriceDisplay=='crypto' && !updateStatusError && !wallet.balanceHidden && !wallet.scanning"
on-hold="hideToggle()"
ng-style="{'transform': amountScale}"
ng-if="status.totalBalanceStr"
ng-class="{amount__balance: amountIsCollapsible}">
ng-if="status.totalBalanceStr && selectedPriceDisplay=='crypto' && !updateStatusError && !wallet.balanceHidden && !wallet.scanning"
class="amount__balance amount__scale">
<strong class="size-36">
<formatted-amount value="{{status.totalBalanceStr}}"></formatted-amount>
</strong>
<div
class="size-14 amount-alternative"
ng-if="status.totalBalanceAlternative && wallet.network == 'livenet'"
ng-style="{opacity: altAmountOpacity}">
ng-if="status.totalBalanceAlternative && wallet.network == 'livenet'">
<formatted-amount value="{{status.totalBalanceAlternative}}" currency="{{status.alternativeIsoCode}}"></formatted-amount>
</div>
</div>
<div ng-style="{'transform': amountScale}"
class="amount__balance"
ng-show="!updateStatusError && wallet.balanceHidden && !wallet.scanning"
<div
class="amount__balance amount__scale"
ng-if="!updateStatusError && wallet.balanceHidden && !wallet.scanning"
on-hold="hideToggle()">
<strong class="size-24" translate>[Balance Hidden]</strong>
<div ng-style="{opacity: altAmountOpacity}" class="size-14 amount-alternative" translate>
<div class="size-14 amount-alternative" translate>
Tap and hold to show
</div>
</div>
<div ng-style="{'transform': amountScale}"
class="amount__balance"
ng-show="!updateStatusError && wallet.scanning">
<div
class="amount__balance amount__scale"
ng-if="!updateStatusError && wallet.scanning">
<strong class="size-24" translate>[Scanning Funds]</strong>
<div ng-style="{opacity: altAmountOpacity}" class="size-14 amount-alternative" translate>
<div class="size-14 amount-alternative" translate>
Please wait
</div>
</div>
<div ng-if="!wallet.balanceHidden && !wallet.scanning && showBalanceButton" ng-style="{'opacity': altAmountOpacity, 'transform': amountScale}">
<div ng-if="!wallet.balanceHidden && !wallet.scanning && showBalanceButton" class="amount__scale">
<button class="button button-standard button-primary amount__button-balance size-14" ng-click="openBalanceModal()">
<i class="icon ion-ios-checkmark-outline"></i>
<strong>
@ -101,7 +96,7 @@
</button>
</div>
<div class="send-receive-buttons row" ng-if="(status.availableBalanceSat || status.availableBalanceSat === 0) && (buttonsOpacity > 0 || isAndroid)" ng-style="{opacity: buttonsOpacity}">
<div class="send-receive-buttons row" ng-if="(status.availableBalanceSat || status.availableBalanceSat === 0)">
<div class="col">
<div class="button button-outline button-white-outline" ng-click="goToReceive()">
<span translate>Receive</span>
@ -118,136 +113,18 @@
</div>
</div>
<div class="wallet-details-wallet-info" ng-style="{opacity: altAmountOpacity}">
<div class="wallet-details-wallet-info">
<span ng-include="'views/includes/walletInfo.html'"></span>
</div>
</div>
<ion-content ng-style="{'margin-top': contentMargin}" ng-class="{collapsible: amountIsCollapsible}">
<ion-content class="amount__balance">
<ion-refresher
pulling-icon="ion-ios-refresh"
spinner="ios-small"
on-refresh="onRefresh()">
</ion-refresher>
<!-- Start Balance view duplicate (for Android compatibility) -->
<div class="amount-wrapper" ng-show="wallet && wallet.isComplete() && !amountIsCollapsible" ng-class="{'wallet-background-color-default': !wallet.color}" ng-style="{'background-color':wallet.color}">
<div
ng-style="{'background-color':wallet.color, 'height': amountHeight}"
class="amount"
ng-class="{collapsible: amountIsCollapsible, 'wallet-background-color-default': !wallet.color, 'no-alternative': wallet.network != 'livenet'}"
>
<div class="amount__error" ng-style="{opacity: altAmountOpacity}" ng-show="updateStatusError">
<span>{{updateStatusError}}</span>
</div>
<div class="amount__error" ng-style="{opacity: altAmountOpacity}" ng-show="walletNotRegistered">
<span translate>This wallet is not registered at the given Bitcore Wallet Service (BWS). You can recreate it from the local information.</span>
</div>
<div
ng-click='updateAll(true)'
ng-show="selectedPriceDisplay=='crypto' && !updateStatusError && !wallet.balanceHidden && !wallet.scanning"
on-hold="hideToggle()"
ng-style="{'transform': amountScale}"
ng-class="{amount__balance: amountIsCollapsible}">
<strong ng-if="status.totalBalanceStr" class="size-36"><formatted-amount value="{{status.totalBalanceStr}}"></formatted-amount></strong>
<div
class="size-14 amount-alternative"
ng-if="status.totalBalanceAlternative && wallet.network == 'livenet'"
ng-style="{opacity: altAmountOpacity}">
<formatted-amount value="{{status.totalBalanceAlternative}}" currency="{{status.alternativeIsoCode}}"></formatted-amount>
</div>
</div>
<div
ng-click='updateAll(true)'
ng-show="selectedPriceDisplay=='fiat' && !updateStatusError && !wallet.balanceHidden && !wallet.scanning"
on-hold="hideToggle()"
ng-style="{'transform': amountScale}"
ng-class="{amount__balance: amountIsCollapsible}">
<strong class="size-36"><formatted-amount value="{{status.totalBalanceAlternative}}" currency="{{status.alternativeIsoCode}}"></formatted-amount></strong>
<div
class="size-14 amount-alternative"
ng-if="status.totalBalanceStr && wallet.network == 'livenet'"
ng-style="{opacity: altAmountOpacity}">
<formatted-amount value="{{status.totalBalanceStr}}"></formatted-amount>
</div>
</div>
<div ng-style="{'transform': amountScale}"
class="amount__balance"
ng-show="!updateStatusError && wallet.balanceHidden && !wallet.scanning"
on-hold="hideToggle()">
<strong class="size-24" translate>[Balance Hidden]</strong>
<div ng-style="{opacity: altAmountOpacity}" class="size-16 amount-alternative" translate>
Tap and hold to show
</div>
</div>
<div ng-style="{'transform': amountScale}"
class="amount__balance"
ng-show="!updateStatusError && wallet.scanning">
<strong class="size-24" translate>[Scanning Funds]</strong>
<div ng-style="{opacity: altAmountOpacity}" class="size-16 amount-alternative" translate>
Please wait
</div>
</div>
<div ng-if="selectedPriceDisplay=='crypto' && !wallet.balanceHidden && !wallet.scanning && showBalanceButton" ng-style="{'opacity': altAmountOpacity, 'transform': amountScale}">
<button class="button button-standard button-primary amount__button-balance size-14" ng-click="openBalanceModal()">
<i class="icon ion-ios-checkmark-outline"></i>
<strong>
{{status.spendableBalanceStr}}
</strong>
&nbsp;
<span>
<formatted-amount value="{{status.spendableBalanceAlternative}}" currency="{{status.alternativeIsoCode}}"></formatted-amount>
</span>
</button>
</div>
<div ng-if="selectedPriceDisplay=='fiat' && !wallet.balanceHidden && !wallet.scanning && showBalanceButton" ng-style="{'opacity': altAmountOpacity, 'transform': amountScale}">
<button class="button button-standard button-primary amount__button-balance size-14" ng-click="openBalanceModal()">
<i class="icon ion-ios-checkmark-outline"></i>
<strong>
<formatted-amount value="{{status.spendableBalanceAlternative}}" currency="{{status.alternativeIsoCode}}"></formatted-amount>
</strong>
&nbsp;
<span>
{{status.spendableBalanceStr}}
</span>
</button>
</div>
<div class="send-receive-buttons row" ng-if="(status.availableBalanceSat || status.availableBalanceSat === 0) && (buttonsOpacity > 0 || isAndroid)" ng-style="{opacity: buttonsOpacity}">
<div class="col">
<div class="button button-outline button-white-outline" ng-click="goToReceive()">
<span translate>Receive</span>
</div>
</div>
<div class="col">
<div class="button button-outline button-white-outline" ng-if="!status.availableBalanceSat" ng-click="goToBuy()">
<span translate>Buy Bitcoin</span>
</div>
<div class="button button-outline button-white-outline" ng-if="status.availableBalanceSat>0" ng-click="goToSend()">
<span translate>Send</span>
</div>
</div>
</div>
</div>
<div class="wallet-details-wallet-info" ng-style="{opacity: altAmountOpacity}">
<span ng-include="'views/includes/walletInfo.html'"></span>
</div>
</div>
<!-- End Balance view duplicate (for Android compatibility) -->
<a class="wallet-not-backed-up-warning" ng-if="wallet.needsBackup" ui-sref="tabs.wallet.backupWarning({from: 'tabs.wallet'})" translate>
Wallet not backed up
</a>
@ -284,29 +161,29 @@
<!-- Transactions -->
<div class="wallet-details__no-transaction"
ng-show="!txHistory[0] && !updatingTxHistory && !updateTxHistoryError && !updateStatusError" translate>
ng-show="!txHistory[0] && !vm.gettingInitialHistory && !vm.updateTxHistoryFailed && !updateStatusError" translate>
No transactions yet
</div>
<div class="wallet-details__no-update-history"
ng-show="!txHistory[0] && !updatingTxHistory && updateTxHistoryError" translate>
ng-show="!txHistory[0] && !vm.gettingInitialHistory && vm.updateTxHistoryFailed" translate>
Could not update transaction history
</div>
<div ng-show="updatingTxHistory && updatingTxHistoryProgress>5" class="wallet-details__updating-history">
<div ng-show="vm.gettingInitialHistory && updatingTxHistoryProgress>5" class="wallet-details__updating-history">
<span translate>Updating transaction history. Please stand by.</span><br>
<span translate>{{updatingTxHistoryProgress}} transactions downloaded</span>
</div>
<div class="wallet-details__list" ng-show="txHistory[0] && !updatingTxHistory">
<div ng-style="{'padding-bottom': txHistoryPaddingBottom}" class="wallet-details__list" ng-show="txHistory[0] && !updatingTxHistory">
<div ng-repeat="btx in txHistory track by $index" ng-click="openTxModal(btx)">
<span ng-include="'views/includes/walletHistory.html'"></span>
</div>
</div>
<ion-infinite-scroll
ng-if="txHistory[0] && !updatingTxHistory && txHistoryShowMore"
ng-if="txHistory[0] && vm.allowInfiniteScroll"
on-infinite="showMore()"
distance="1%">
</ion-infinite-scroll>

View file

@ -13,7 +13,7 @@
<div class="header--request__amount-alt" ng-show="requestAmountSecondary" translate>{{requestAmountSecondary}} {{requestCurrencySecondary}}</div>
</div>
<div class="wallets-header">
<div class="title">
<div class="title" ng-if="walletsBch.length > 0 || walletsBtc.length > 0 || walletsInsufficientFunds.length > 0">
{{headerTitle}}
</div>
</div>