diff --git a/bower.json b/bower.json index c0bb256ef..d9ad490ab 100644 --- a/bower.json +++ b/bower.json @@ -17,7 +17,7 @@ "ng-lodash": "~0.2.0", "angular-moment": "0.10.1", "moment": "2.10.3", - "angular-bitcore-wallet-client": "^0.1.2", + "angular-bitcore-wallet-client": "0.1.2", "angular-ui-router": "~0.2.13", "qrcode-decoder-js": "*", "fastclick": "*", diff --git a/public/font/icomoon.eot b/public/font/icomoon.eot index d295df6f4..ea89aefc8 100755 Binary files a/public/font/icomoon.eot and b/public/font/icomoon.eot differ diff --git a/public/font/icomoon.svg b/public/font/icomoon.svg index 330024bef..f606d1228 100755 --- a/public/font/icomoon.svg +++ b/public/font/icomoon.svg @@ -6,53 +6,56 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/public/font/icomoon.ttf b/public/font/icomoon.ttf index 41c42fcd7..a51f9534f 100755 Binary files a/public/font/icomoon.ttf and b/public/font/icomoon.ttf differ diff --git a/public/font/icomoon.woff b/public/font/icomoon.woff index 6e6e8af19..245368ecb 100755 Binary files a/public/font/icomoon.woff and b/public/font/icomoon.woff differ diff --git a/public/img/bank.svg b/public/img/bank.svg new file mode 100644 index 000000000..343af7a9c --- /dev/null +++ b/public/img/bank.svg @@ -0,0 +1,14 @@ + + + + Shape + Shape Copy + Created with Sketch. + + + + + + + + + \ No newline at end of file diff --git a/public/img/glidera-logo.png b/public/img/glidera-logo.png new file mode 100644 index 000000000..930cdf4a9 Binary files /dev/null and b/public/img/glidera-logo.png differ diff --git a/public/views/buyGlidera.html b/public/views/buyGlidera.html new file mode 100644 index 000000000..202d9219f --- /dev/null +++ b/public/views/buyGlidera.html @@ -0,0 +1,124 @@ +
+
+ + +
+ +
+
+
+
+
+
+
+
+
+ {{buy.loading|translate}} +
+
+ +
+

+
+ +
+
+ Daily buy limit: + ${{index.glideraLimits.dailyBuy}} + (remaining ${{index.glideraLimits.dailyBuyRemaining}}) +
+ Monthly buy limit: + ${{index.glideraLimits.monthlyBuy}} + (remaining ${{index.glideraLimits.monthlyBuyRemaining}}) +
+

+
+ +
+
+ +
+ + This operation was disabled because you have a pending first transaction + +
+ +
+
+ +
+ + + + + BTC + USD + +
+ Buy + {{buy.buyPrice.currency}} {{buy.buyPrice.subtotal}} in Bitcoin + {{buy.buyPrice.qty}} BTC + at {{buy.buyPrice.price}} {{buy.buyPrice.currency}}/BTC +
+
+ (Enter the amount to get the exchange rate) +
+ + + +
+
+
+
+
+ {{buy.buyPrice.currency}} {{buy.buyPrice.subtotal}} → {{buy.buyPrice.qty}} BTC +

+ A SMS containing a confirmation code was sent to your phone.
+ Please, enter the code below +

+
+ + +
+

+ Fiat will be immediately withdrawn from your bank account. The bitcoins will be purchased and deposited to your wallet ({{index.walletName}}) in 2-4 business days. +

+
+
+
+ + {{buy.error|translate}} + +
+
+

Purchase initiated

+

+ A transfer has been initiated from your bank account. Your bitcoins should arrive to your wallet in 4-6 business days. +

+ + +
+
+
+
+
diff --git a/public/views/glidera.html b/public/views/glidera.html new file mode 100644 index 000000000..c222eef6b --- /dev/null +++ b/public/views/glidera.html @@ -0,0 +1,189 @@ + +
+
+ + +
+ +
+
+
+
+
+
+
+
+
+ Connecting to Glidera... + {{index.glideraLoading|translate}} +
+
+ +
+
+
+ +
+ +
+
+ {{index.glideraError}} +
+
+
+ +
+ + +
+
+
+
+ +
+
+
+

You can buy and sell Bitcoin with a US bank account directly in Copay.

+
Connect your Glidera account to get started
+ +
+ + Testnet wallets only work with Glidera Sandbox Accounts +
+ + + Connect to Glidera + +
+ + Do you already have the Oauth Code? + +
+
+
+
+ {{glidera.error}} +
+
+ + + +
+
+ Go back +
+
+
+
+ +
+ +
+
+ {{index.glideraEmail}} +
+ +
+ {{index.glideraPersonalInfo.firstName}} {{index.glideraPersonalInfo.lastName}} +
+ +
+ Your Glidera account is not ready to transact. Please, verify it at + Glidera.io +
+
+ + + +
+

Activity

+
+ No activity in your account +
+
+
+ Bought + Sold +
+ +
+ + {{tx.subtotal}} {{tx.currency}} + +
+
+
+ +
+
+
+ +
+
+ Completed + Processing + Error +
+
+
+ +
+
+
diff --git a/public/views/glideraUri.html b/public/views/glideraUri.html new file mode 100644 index 000000000..5e5412b52 --- /dev/null +++ b/public/views/glideraUri.html @@ -0,0 +1,35 @@ +
+
+ +
+ +
+
+
+
+
+
+
+
+
+ Connecting to Glidera... +
+
+ +
+
+
+ +
+ +
+
{{glidera.error}}
+ +
+
+
+
diff --git a/public/views/includes/menu-item.html b/public/views/includes/menu-item.html index 5f85ce3c5..fbd813303 100644 --- a/public/views/includes/menu-item.html +++ b/public/views/includes/menu-item.html @@ -1,4 +1,4 @@ - +
{{ item.title|translate }} @@ -6,4 +6,4 @@ {{ index.pendingTxProposalsCountForUs }}
-
\ No newline at end of file + diff --git a/public/views/includes/topbar.html b/public/views/includes/topbar.html index b47a18b04..734154b79 100644 --- a/public/views/includes/topbar.html +++ b/public/views/includes/topbar.html @@ -15,7 +15,7 @@
- +
diff --git a/public/views/includes/transaction.html b/public/views/includes/transaction.html index 0a0be0be0..95944e441 100644 --- a/public/views/includes/transaction.html +++ b/public/views/includes/transaction.html @@ -1,6 +1,6 @@
+ ng-click="home.openTxpModal(tx, index.copayers, !!index.glideraStatus)">
  diff --git a/public/views/modals/glidera-confirmation.html b/public/views/modals/glidera-confirmation.html new file mode 100644 index 000000000..11f007613 --- /dev/null +++ b/public/views/modals/glidera-confirmation.html @@ -0,0 +1,15 @@ +
+
+

Revoke Glidera Token

+
+ +
+
+ +
+
+
diff --git a/public/views/modals/glidera-tx-details.html b/public/views/modals/glidera-tx-details.html new file mode 100644 index 000000000..ca7396496 --- /dev/null +++ b/public/views/modals/glidera-tx-details.html @@ -0,0 +1,60 @@ + + + diff --git a/public/views/modals/txp-details.html b/public/views/modals/txp-details.html index 934073ef9..6597ffb35 100644 --- a/public/views/modals/txp-details.html +++ b/public/views/modals/txp-details.html @@ -133,31 +133,31 @@
-
- Payment accepted... -
-
-
But not broadcasted. Try to send manually
+
+
Payment accepted, but not yet broadcasted
+
+
+
Payment accepted. It will be broadcasted by Glidera. It case there is a problem, it can be deleted after 6 hours it was created.
+
- Payment sent! + Payment sent
- Payment finally rejected + Payment rejected
-
+
* A payment proposal can be deleted if 1) you are the creator, and no other copayer has signed, or 2) 24 hours have passed since the proposal was created.
+ +
+ +
    +

    Permissions

    +
  • + Email + + {{index.glideraPermissions.view_email_address}} + +
  • +
  • + Personal Information + + {{index.glideraPermissions.personal_info}} + +
  • +
  • + Buy/Sell + + {{index.glideraPermissions.transact}} + +
  • +
  • + Transaction History + + {{index.glideraPermissions.transaction_history}} + +
  • +
+ +
    +

    Email

    +
  • + Email + + {{glidera.email.email}} + +
  • +
  • + Active + + {{glidera.email.userEmailIsSetup}} + +
  • +
+ +
    +

    Personal Information

    + +
  • + First Name + + {{glidera.personalInfo.firstName}} + +
  • +
  • + Middle Name + + {{glidera.personalInfo.middleName}} + +
  • +
  • + Last Name + + {{glidera.personalInfo.lastName}} + +
  • +
  • + Birth Date + + {{glidera.personalInfo.birthDate}} + +
  • +
  • + Address 1 + + {{glidera.personalInfo.address1}} + +
  • +
  • + Address 2 + + {{glidera.personalInfo.address2}} + +
  • +
  • + City + + {{glidera.personalInfo.city}} + +
  • +
  • + State + + {{glidera.personalInfo.state}} + +
  • +
  • + ZIP Code + + {{glidera.personalInfo.zipCode}} + +
  • +
  • + Country + + {{glidera.personalInfo.countryCode}} + +
  • +
  • + Occupation + + {{glidera.personalInfo.occupation}} + +
  • +
  • + Basic Information State + + {{glidera.personalInfo.basicInfoState}} + +
  • +
+ +
    +

    Status

    + +
  • + Buy/Sell + + {{glidera.status.userCanTransact}} + +
  • + +
  • + Buy + + {{glidera.status.userCanBuy}} + +
  • +
  • + Sell + + {{glidera.status.userCanSell}} + +
  • +
  • + Email Is Setup + + {{glidera.status.userEmailIsSetup}} + +
  • +
  • + Phone Is Setup + + {{glidera.status.userPhoneIsSetup}} + +
  • +
  • + Bank Account Is Setup + + {{glidera.status.userBankAccountIsSetup}} + +
  • +
  • + Personal Information State + + {{glidera.status.personalInfoState}} + +
  • +
  • + Bank Account State + + {{glidera.status.bankAccountState}} + +
  • +
  • + Country + + {{glidera.status.country}} + +
  • +
+ +
    +

    Limits

    + +
  • + Daily Buy + + {{glidera.limits.dailyBuy}} {{glidera.limits.currency}} + +
  • +
  • + Daily Sell + + {{glidera.limits.dailySell}} {{glidera.limits.currency}} + +
  • +
  • + Monthly Buy + + {{glidera.limits.monthlyBuy}} {{glidera.limits.currency}} + +
  • +
  • + Monthly Sell + + {{glidera.limits.monthlySell}} {{glidera.limits.currency}} + +
  • +
  • + Daily Buy Remaining + + {{glidera.limits.dailyBuyRemaining}} {{glidera.limits.currency}} + +
  • +
  • + Daily Sell Remaining + + {{glidera.limits.dailySellRemaining}} {{glidera.limits.currency}} + +
  • +
  • + Monthly Buy Remaining + + {{glidera.limits.monthlyBuyRemaining}} {{glidera.limits.currency}} + +
  • +
  • + Monthly Sell Remaining + + {{glidera.limits.monthlySellRemaining}} {{glidera.limits.currency}} + +
  • +
  • + Buy/Sell Disabled (pending first transaction) + + {{glidera.limits.transactDisabledPendingFirstTransaction}} + +
  • +
+ +
    +

    Account

    + +
  • + + Revoke Token +
  • +
+ +
+
+ diff --git a/public/views/sellGlidera.html b/public/views/sellGlidera.html new file mode 100644 index 000000000..19b08a333 --- /dev/null +++ b/public/views/sellGlidera.html @@ -0,0 +1,120 @@ +
+
+ + +
+ +
+
+
+
+
+
+
+
+
+ {{sell.loading|translate}} +
+
+ +
+

+
+ +
+
+ Daily sell limit: + ${{index.glideraLimits.dailySell}} + (${{index.glideraLimits.dailySellRemaining}} remaining) +
+ Monthly sell limit: + ${{index.glideraLimits.monthlySell}} + (${{index.glideraLimits.monthlySellRemaining}} remaining) +
+

+
+ +
+
+ +
+ + This operation was disabled because you have a pending first transaction + +
+ +
+
+ +
+ + + + + BTC + USD + +
+ Sell + {{sell.sellPrice.currency}} {{sell.sellPrice.subtotal}} in Bitcoin + {{sell.sellPrice.qty}} BTC + at {{sell.sellPrice.price}} {{sell.sellPrice.currency}}/BTC + +
+
+ (Enter the amount to get the exchange rate) +
+ +
+
+
+
+
+{{sell.sellPrice.qty}} BTC → {{sell.sellPrice.currency}} {{sell.sellPrice.subtotal}} +

+ A SMS containing a confirmation code was sent to your phone.
+ Please, enter the code below +

+
+ + +
+

+ Bitcoins will be immediately sent from your wallet to Glidera. Fiat will be deposited in your bank account in 4-6 business days. +

+
+
+
+ + {{sell.error|translate}} + +
+
+

Sale initiated

+

+ A transfer has been initiated to your bank account and should arrive in 4-6 business days. +

+ + +
+
+
+
diff --git a/public/views/walletHome.html b/public/views/walletHome.html index 65e1ab513..c921fcacb 100644 --- a/public/views/walletHome.html +++ b/public/views/walletHome.html @@ -96,7 +96,7 @@ Pending Confirmation: {{index.pendingAmountStr}}
- +
@@ -112,6 +112,13 @@ ng-style="{'background-color':index.backgroundColor}">{{ (index.alias || index.walletName) | limitTo: 1}}
+ + + + Glidera + + Preferences @@ -161,26 +168,18 @@
-
-

Payment Proposals

-
-
- -
- Total Locked Balance: - {{index.lockedBalanceStr}} - {{index.lockedBalanceAlternative}} - {{index.alternativeIsoCode}} -
+

Payment Proposals

+

Unsent transactions

+
-
-

Unsent transactions

-
-
+
+ Total Locked Balance: + {{index.lockedBalanceStr}} + {{index.lockedBalanceAlternative}} + {{index.alternativeIsoCode}}
@@ -275,7 +274,7 @@
@@ -411,7 +410,7 @@
  • {{index.feeOpts[fee.level]|translate}} -
  • @@ -522,7 +521,7 @@ diff --git a/src/css/icons.css b/src/css/icons.css index 68111592b..96c803836 100644 --- a/src/css/icons.css +++ b/src/css/icons.css @@ -24,214 +24,159 @@ -moz-osx-font-smoothing: grayscale; } -.icon-minus-circle:before { - content: "\e62c"; +.icon-sell-btc:before { + content: "\e632"; } - -.icon-plus-circle:before { - content: "\e62d"; +.icon-buy-btc:before { + content: "\e631"; } - -.icon-close-circle:before { - content: "\e62e"; -} - -.icon-checkmark-circle:before { - content: "\e62b"; -} - -.icon-circle:before { - content: "\e629"; -} - -.icon-circle-active:before { - content: "\e627"; -} - -.icon-scan:before { - content: "\e62a"; -} - -.icon-trash:before { - content: "\e626"; -} - -.icon-wallet:before { - content: "\e622"; -} - -.icon-history:before { - content: "\e623"; -} - -.icon-reference:before { - content: "\e621"; -} - -.icon-bell:before { - content: "\e61c"; -} - -.icon-trash:before { - content: "\e626"; -} - -.icon-wallet:before { - content: "\e622"; -} - -.icon-history:before { - content: "\e623"; -} - -.icon-reference:before { - content: "\e621"; -} - -.icon-bell:before { - content: "\e61c"; -} - -.icon-receive:before { - content: "\e625"; -} - -.icon-wrench:before { - content: "\e61d"; -} - -.icon-download:before { - content: "\e61e"; -} - -.icon-upload:before { - content: "\e61f"; -} - -.icon-power:before { - content: "\e620"; -} - -.icon-forward:before { - content: "\e624"; -} - -.icon-compose:before { - content: "\e610"; -} - -.icon-contact:before { - content: "\e611"; -} - -.icon-email:before { - content: "\e612"; -} - -.icon-gear:before { - content: "\e613"; -} - -.icon-home:before { - content: "\e614"; -} - -.icon-locked:before { - content: "\e615"; -} - -.icon-paperplane:before { - content: "\e617"; -} - -.icon-people:before { - content: "\e618"; -} - -.icon-person:before { - content: "\e619"; -} - -.icon-pricetag:before { - content: "\e61a"; -} - -.icon-pricetags:before { - content: "\e61b"; -} - -.icon-bitcoin:before { - content: "\e60f"; -} - -.icon-usd:before { - content: "\e616"; -} - .icon-erase:before { content: "\e628"; } - .icon-receive2:before { content: "\e62f"; } - .icon-arrow-left:before { content: "\e600"; } - .icon-arrow-down:before { content: "\e601"; } - .icon-arrow-up:before { content: "\e602"; } - .icon-arrow-right:before { content: "\e603"; } - .icon-arrow-left2:before { content: "\e604"; } - .icon-arrow-down2:before { content: "\e605"; } - .icon-arrow-up2:before { content: "\e606"; } - .icon-arrow-right2:before { content: "\e607"; } - .icon-arrow-left3:before { content: "\e608"; } - .icon-arrow-down3:before { content: "\e609"; } - .icon-arrow-up3:before { content: "\e60a"; } - .icon-arrow-right3:before { content: "\e60b"; } - .icon-arrow-left4:before { content: "\e60c"; } - .icon-arrow-down4:before { content: "\e60d"; } - .icon-arrow-up4:before { content: "\e60e"; } +.icon-bank:before { + content: "\e630"; +} +.icon-minus-circle:before { + content: "\e62c"; +} +.icon-plus-circle:before { + content: "\e62d"; +} +.icon-close-circle:before { + content: "\e62e"; +} +.icon-checkmark-circle:before { + content: "\e62b"; +} +.icon-circle:before { + content: "\e629"; +} +.icon-circle-active:before { + content: "\e627"; +} +.icon-circle-active:before { + content: "\e627"; +} +.icon-scan:before { + content: "\e62a"; +} +.icon-trash:before { + content: "\e626"; +} +.icon-wallet:before { + content: "\e622"; +} +.icon-history:before { + content: "\e623"; +} +.icon-reference:before { + content: "\e621"; +} +.icon-bell:before { + content: "\e61c"; +} +.icon-receive:before { + content: "\e625"; +} +.icon-wrench:before { + content: "\e61d"; +} +.icon-download:before { + content: "\e61e"; +} +.icon-upload:before { + content: "\e61f"; +} +.icon-power:before { + content: "\e620"; +} +.icon-forward:before { + content: "\e624"; +} +.icon-compose:before { + content: "\e610"; +} +.icon-contact:before { + content: "\e611"; +} +.icon-email:before { + content: "\e612"; +} +.icon-gear:before { + content: "\e613"; +} +.icon-home:before { + content: "\e614"; +} +.icon-locked:before { + content: "\e615"; +} +.icon-paperplane:before { + content: "\e617"; +} +.icon-people:before { + content: "\e618"; +} +.icon-person:before { + content: "\e619"; +} +.icon-pricetag:before { + content: "\e61a"; +} +.icon-pricetags:before { + content: "\e61b"; +} +.icon-bitcoin:before { + content: "\e60f"; +} +.icon-usd:before { + content: "\e616"; +} diff --git a/src/css/main.css b/src/css/main.css index a140d1787..f71619032 100644 --- a/src/css/main.css +++ b/src/css/main.css @@ -410,7 +410,6 @@ ul.manage li { .line-t { border-top: 1px solid #E9E9EC; - padding-top: 0.5rem; } .line-b { @@ -1098,7 +1097,16 @@ input.ng-invalid-match, input.ng-invalid-match:focus { } .preferences-icon { - padding: 0.6rem 0.8rem !important; + width: auto; + height: 40px; +} + +@media only screen and (max-width: 40em) { + .preferences-icon { + width: 40px; + height: 40px; + padding: 0.55rem !important; + } } .alertModal { diff --git a/src/js/controllers/buyGlidera.js b/src/js/controllers/buyGlidera.js new file mode 100644 index 000000000..eac8576a7 --- /dev/null +++ b/src/js/controllers/buyGlidera.js @@ -0,0 +1,80 @@ +'use strict'; + +angular.module('copayApp.controllers').controller('buyGlideraController', + function($scope, $timeout, profileService, addressService, glideraService, gettext, gettextCatalog, bwsError) { + + this.addr = {}; + this.show2faCodeInput = null; + this.error = null; + this.success = null; + this.loading = null; + + this.getBuyPrice = function(token, price) { + var self = this; + if (!price || (price && !price.qty && !price.fiat)) { + this.buyPrice = null; + return; + } + glideraService.buyPrice(token, price, function(err, buyPrice) { + if (err) { + self.error = gettext('Could not get exchange information. Please, try again.'); + } + else { + self.buyPrice = buyPrice; + } + }); + }; + + this.get2faCode = function(token) { + var self = this; + this.loading = gettext('Sending 2FA code...'); + $timeout(function() { + glideraService.get2faCode(token, function(err, sent) { + self.loading = null; + if (err) { + self.error = gettext('Could not send confirmation code to your phone'); + } + else { + self.error = null; + self.show2faCodeInput = sent; + } + }); + }, 100); + }; + + this.sendRequest = function(token, permissions, twoFaCode) { + var fc = profileService.focusedClient; + if (!fc) return; + var self = this; + self.error = null; + addressService.getAddress(fc.credentials.walletId, null, function(err, addr) { + if (!addr) { + self.error = bwsError.msg(err); + $scope.$apply(); + } + else { + self.loading = gettext('Buying bitcoin...'); + var data = { + destinationAddress: addr, + qty: self.buyPrice.qty, + priceUuid: self.buyPrice.priceUuid, + useCurrentPrice: false, + ip: null + }; + $timeout(function() { + glideraService.buy(token, twoFaCode, data, function(err, data) { + self.loading = null; + if (err) { + self.error = err; + } + else { + self.success = data; + $scope.$emit('Local/GlideraTx'); + } + }); + }, 100); + } + }); + }; + + }); diff --git a/src/js/controllers/glidera.js b/src/js/controllers/glidera.js new file mode 100644 index 000000000..4d54c61cb --- /dev/null +++ b/src/js/controllers/glidera.js @@ -0,0 +1,76 @@ +'use strict'; + +angular.module('copayApp.controllers').controller('glideraController', + function($scope, $timeout, $modal, profileService, configService, storageService, glideraService, isChromeApp) { + + var config = configService.getSync().wallet.settings; + + this.getAuthenticateUrl = function() { + return glideraService.getOauthCodeUrl(); + }; + + this.submitOauthCode = function(code) { + var fc = profileService.focusedClient; + var self = this; + this.loading = true; + this.error = null; + $timeout(function() { + glideraService.getToken(code, function(err, data) { + self.loading = null; + if (err) { + self.error = err; + $timeout(function() { + $scope.$apply(); + }, 100); + } + else if (data && data.access_token) { + storageService.setGlideraToken(fc.credentials.network, data.access_token, function() { + $scope.$emit('Local/GlideraTokenUpdated', data.access_token); + $timeout(function() { + $scope.$apply(); + }, 100); + }); + } + }); + }, 100); + }; + + // DISABLE ANIMATION ON CHROMEAPP + if (isChromeApp) { + var animatedSlideRight = 'full'; + } + else { + var animatedSlideRight = 'full animated slideInRight'; + } + + this.openTxModal = function(token, tx) { + var self = this; + var fc = profileService.focusedClient; + var ModalInstanceCtrl = function($scope, $modalInstance) { + $scope.tx = tx; + $scope.settings = config; + $scope.color = fc.backgroundColor; + + glideraService.getTransaction(token, tx.transactionUuid, function(error, tx) { + $scope.tx = tx; + }); + + $scope.cancel = function() { + $modalInstance.dismiss('cancel'); + }; + + }; + + var modalInstance = $modal.open({ + templateUrl: 'views/modals/glidera-tx-details.html', + windowClass: animatedSlideRight, + controller: ModalInstanceCtrl, + }); + + modalInstance.result.finally(function() { + var m = angular.element(document.getElementsByClassName('reveal-modal')); + m.addClass('slideOutRight'); + }); + }; + + }); diff --git a/src/js/controllers/glideraUri.js b/src/js/controllers/glideraUri.js new file mode 100644 index 000000000..56e031d03 --- /dev/null +++ b/src/js/controllers/glideraUri.js @@ -0,0 +1,37 @@ +'use strict'; +angular.module('copayApp.controllers').controller('glideraUriController', + function($scope, $stateParams, $timeout, profileService, glideraService, storageService, go) { + + this.submitOauthCode = function(code) { + var fc = profileService.focusedClient; + var self = this; + this.loading = true; + this.error = null; + $timeout(function() { + glideraService.getToken(code, function(err, data) { + self.loading = null; + if (err) { + self.error = err; + $timeout(function() { + $scope.$apply(); + }, 100); + } + else if (data && data.access_token) { + storageService.setGlideraToken(fc.credentials.network, data.access_token, function() { + $scope.$emit('Local/GlideraTokenUpdated', data.access_token); + $timeout(function() { + go.path('glidera'); + $scope.$apply(); + }, 100); + }); + } + }); + }, 100); + }; + + this.checkCode = function() { + this.code = $stateParams.code; + this.submitOauthCode(this.code); + }; + + }); diff --git a/src/js/controllers/index.js b/src/js/controllers/index.js index 55c0fda22..a3de513ad 100644 --- a/src/js/controllers/index.js +++ b/src/js/controllers/index.js @@ -1,6 +1,6 @@ 'use strict'; -angular.module('copayApp.controllers').controller('indexController', function($rootScope, $scope, $log, $filter, $timeout, lodash, go, profileService, configService, isCordova, rateService, storageService, addressService, gettextCatalog, gettext, amMoment, nodeWebkit, addonManager, feeService, isChromeApp, bwsError, utilService, $state) { +angular.module('copayApp.controllers').controller('indexController', function($rootScope, $scope, $log, $filter, $timeout, lodash, go, profileService, configService, isCordova, rateService, storageService, addressService, gettextCatalog, gettext, amMoment, nodeWebkit, addonManager, feeService, isChromeApp, bwsError, utilService, $state, glideraService) { var self = this; self.isCordova = isCordova; self.onGoingProcess = {}; @@ -113,6 +113,13 @@ angular.module('copayApp.controllers').controller('indexController', function($r self.pendingTxProposalsCountForUs = null; self.setSpendUnconfirmed(); + self.glideraToken = null; + self.glideraError = null; + self.glideraPermissions = null; + self.glideraEmail = null; + self.glideraPersonalInfo = null; + self.glideraTxs = null; + $timeout(function() { self.hasProfile = true; self.noFocusedWallet = false; @@ -134,6 +141,7 @@ angular.module('copayApp.controllers').controller('indexController', function($r self.copayers = []; self.updateColor(); self.updateAlias(); + self.initGlidera(); storageService.getBackupFlag(self.walletId, function(err, val) { self.needsBackup = self.network == 'testnet' ? false : !val; @@ -145,18 +153,6 @@ angular.module('copayApp.controllers').controller('indexController', function($r self.setTab = function(tab, reset, tries) { tries = tries || 0; - // check if the whole menu item passed - if (typeof tab == 'object') { - if (tab.open) { - if (tab.link) { - self.tab = tab.link; - } - tab.open(); - return; - } else { - return self.setTab(tab.link); - } - } if (self.tab === tab && !reset) return; @@ -166,30 +162,27 @@ angular.module('copayApp.controllers').controller('indexController', function($r }, 300); } - if (!self.tab || !$state.is('walletHome')) + if (!self.tab) self.tab = 'walletHome'; - go.path('walletHome', function() { - - if (document.getElementById(self.tab)) { - document.getElementById(self.tab).className = 'tab-out tab-view ' + self.tab; - var old = document.getElementById('menu-' + self.tab); - if (old) { - old.className = ''; - } + if (document.getElementById(self.tab)) { + document.getElementById(self.tab).className = 'tab-out tab-view ' + self.tab; + var old = document.getElementById('menu-' + self.tab); + if (old) { + old.className = ''; } + } - if (document.getElementById(tab)) { - document.getElementById(tab).className = 'tab-in tab-view ' + tab; - var newe = document.getElementById('menu-' + tab); - if (newe) { - newe.className = 'active'; - } + if (document.getElementById(tab)) { + document.getElementById(tab).className = 'tab-in tab-view ' + tab; + var newe = document.getElementById('menu-' + tab); + if (newe) { + newe.className = 'active'; } + } - self.tab = tab; - $rootScope.$emit('Local/TabChanged', tab); - }); + self.tab = tab; + $rootScope.$emit('Local/TabChanged', tab); }; @@ -400,6 +393,7 @@ angular.module('copayApp.controllers').controller('indexController', function($r self.setOngoingProcess('updatingPendingTxps', true); $log.debug('Updating PendingTxps'); fc.getTxProposals({}, function(err, txps) { +console.log('[index.js:395]',txps); //TODO self.setOngoingProcess('updatingPendingTxps', false); if (err) { self.handleError(err); @@ -843,6 +837,79 @@ angular.module('copayApp.controllers').controller('indexController', function($r }), 'name'); }; + self.initGlidera = function(accessToken) { + if (self.isShared) return; + self.glideraStatus = null; + + glideraService.setCredentials(self.network); + + var getToken = function(cb) { + if (accessToken) { + cb(null, accessToken); + } else { + storageService.getGlideraToken(self.network, cb); + } + }; + + getToken(function(err, accessToken) { + if (err || !accessToken) return; + else { + self.glideraLoading = gettext('Connecting to Glidera...'); + glideraService.getAccessTokenPermissions(accessToken, function(err, p) { + self.glideraLoading = null; + if (err) { + self.glideraError = err; + } + else { + self.glideraToken = accessToken; + self.glideraPermissions = p; + self.updateGlidera({ fullUpdate: true}); + } + }); + } + }); + }; + + self.updateGlidera = function(opts) { + if (!self.glideraToken || !self.glideraPermissions) return; + var accessToken = self.glideraToken; + var permissions = self.glideraPermissions; + + opts = opts || {}; + + glideraService.getStatus(accessToken, function(err, data) { + self.glideraStatus = data; + }); + + glideraService.getLimits(accessToken, function(err, limits) { + self.glideraLimits = limits; + }); + + if (permissions.transaction_history) { + self.glideraLoadingHistory = gettext('Getting Glidera transactions...'); + glideraService.getTransactions(accessToken, function(err, data) { + self.glideraLoadingHistory = null; + self.glideraTxs = data; + }); + } + + if (permissions.view_email_address && opts.fullUpdate) { + self.glideraLoadingEmail = gettext('Getting Glidera Email...'); + glideraService.getEmail(accessToken, function(err, data) { + self.glideraLoadingEmail = null; + self.glideraEmail = data.email; + }); + } + if (permissions.personal_info && opts.fullUpdate) { + self.glideraLoadingPersonalInfo = gettext('Getting Glidera Personal Information...'); + glideraService.getPersonalInfo(accessToken, function(err, data) { + self.glideraLoadingPersonalInfo = null; + self.glideraPersonalInfo = data; + }); + } + + }; + // UX event handlers $rootScope.$on('Local/ColorUpdated', function(event) { self.updateColor(); @@ -892,6 +959,18 @@ angular.module('copayApp.controllers').controller('indexController', function($r }); }); + $rootScope.$on('Local/GlideraTokenUpdated', function(event, accessToken) { + self.initGlidera(accessToken); + }); + + $rootScope.$on('Local/GlideraTx', function(event, accessToken, permissions) { + self.updateGlidera(); + }); + + $rootScope.$on('Local/GlideraError', function(event) { + self.debouncedUpdate(); + }); + $rootScope.$on('Local/UnitSettingUpdated', function(event) { self.updateAll(); self.updateTxHistory(); @@ -989,8 +1068,8 @@ angular.module('copayApp.controllers').controller('indexController', function($r }); }); - lodash.each(['NewTxProposal', 'TxProposalFinallyRejected', 'TxProposalRemoved', - 'Local/NewTxProposal', 'Local/TxProposalAction', 'ScanFinished' + lodash.each(['NewTxProposal', 'TxProposalFinallyRejected', 'TxProposalRemoved', 'NewOutgoingTxByThirdParty', + 'Local/NewTxProposal', 'Local/TxProposalAction', 'ScanFinished', 'Local/GlideraTx' ], function(eventName) { $rootScope.$on(eventName, function(event, untilItChanges) { self.updateAll({ diff --git a/src/js/controllers/preferencesGlidera.js b/src/js/controllers/preferencesGlidera.js new file mode 100644 index 000000000..43d270f31 --- /dev/null +++ b/src/js/controllers/preferencesGlidera.js @@ -0,0 +1,61 @@ +'use strict'; + +angular.module('copayApp.controllers').controller('preferencesGlideraController', + function($scope, $modal, $timeout, profileService, applicationService, glideraService, storageService) { + + this.getEmail = function(token) { + var self = this; + glideraService.getEmail(token, function(error, data) { + self.email = data; + }); + }; + + this.getPersonalInfo = function(token) { + var self = this; + glideraService.getPersonalInfo(token, function(error, info) { + self.personalInfo = info; + }); + }; + + this.getStatus = function(token) { + var self = this; + glideraService.getStatus(token, function(error, data) { + self.status = data; + }); + }; + + this.getLimits = function(token) { + var self = this; + glideraService.getLimits(token, function(error, limits) { + self.limits = limits; + }); + }; + + this.revokeToken = function() { + var fc = profileService.focusedClient; + var ModalInstanceCtrl = function($scope, $modalInstance) { + $scope.ok = function() { + $modalInstance.close(true); + }; + $scope.cancel = function() { + $modalInstance.dismiss(); + }; + }; + + var modalInstance = $modal.open({ + templateUrl: 'views/modals/glidera-confirmation.html', + windowClass: 'full', + controller: ModalInstanceCtrl + }); + modalInstance.result.then(function(ok) { + if (ok) { + storageService.removeGlideraToken(fc.credentials.network, function() { + $timeout(function() { + applicationService.restart(); + }, 100); + }); + } + }); + }; + + }); diff --git a/src/js/controllers/sellGlidera.js b/src/js/controllers/sellGlidera.js new file mode 100644 index 000000000..998c57b32 --- /dev/null +++ b/src/js/controllers/sellGlidera.js @@ -0,0 +1,158 @@ +'use strict'; + +angular.module('copayApp.controllers').controller('sellGlideraController', + function($scope, $timeout, $log, gettext, gettextCatalog, configService, profileService, addressService, feeService, glideraService, bwsError) { + + var config = configService.getSync(); + this.data = {}; + this.show2faCodeInput = null; + this.success = null; + this.error = null; + this.loading = null; + this.currentSpendUnconfirmed = config.wallet.spendUnconfirmed; + this.currentFeeLevel = config.wallet.settings.feeLevel || 'normal'; + + this.getSellPrice = function(token, price) { + var self = this; + if (!price || (price && !price.qty && !price.fiat)) { + this.error = null; + this.sellPrice = null; + return; + } + glideraService.sellPrice(token, price, function(err, sellPrice) { + if (err) { + self.error = gettext('Could not get exchange information. Please, try again.'); + } + else { + self.error = null; + self.sellPrice = sellPrice; + } + }); + }; + + this.get2faCode = function(token) { + var self = this; + this.loading = gettext('Sending 2FA code...'); + $timeout(function() { + glideraService.get2faCode(token, function(err, sent) { + self.loading = null; + if (err) { + self.error = gettext('Could not send confirmation code to your phone'); + } + else { + self.show2faCodeInput = sent; + } + }); + }, 100); + }; + + this.createTx = function(token, permissions, twoFaCode) { + var self = this; + var fc = profileService.focusedClient; + self.error = null; + + this.loading = gettext('Selling Bitcoin...'); + $timeout(function() { + addressService.getAddress(fc.credentials.walletId, null, function(err, refundAddress) { + if (!refundAddress) { + self.loading = null; + self.error = bwsError.msg(err); + return; + } + glideraService.getSellAddress(token, function(error, sellAddress) { + if (!sellAddress) { + self.loading = null; + self.error = gettext('Could not get the destination bitcoin address'); + return; + } + var amount = parseInt((self.sellPrice.qty * 100000000).toFixed(0)); + + feeService.getCurrentFeeValue(self.currentFeeLevel, function(err, feePerKb) { + if (err) $log.debug(err); + fc.sendTxProposal({ + toAddress: sellAddress, + amount: amount, + message: 'Glidera transaction', + customData: {'glideraToken': token}, + payProUrl: null, + feePerKb: feePerKb, + excludeUnconfirmedUtxos: self.currentSpendUnconfirmed ? false : true + }, function(err, txp) { + if (err) { + profileService.lockFC(); + $log.error(err); + $timeout(function() { + self.loading = null; + self.error = bwsError.msg(err, gettextCatalog.getString('Error')); + }, 1); + return; + } + + if (!fc.canSign()) { + self.loading = null; + $log.info('No signing proposal: No private key'); + return; + } + + _signTx(txp, function(err, txp, rawTx) { + profileService.lockFC(); + if (err) { + self.loading = null; + self.error = err; + $scope.$apply(); + } + else { + var data = { + refundAddress: refundAddress, + signedTransaction: rawTx, + priceUuid: self.sellPrice.priceUuid, + useCurrentPrice: self.sellPrice.priceUuid ? false : true, + ip: null + }; + glideraService.sell(token, twoFaCode, data, function(err, data) { + self.loading = null; + if (err) { + self.error = err; + fc.removeTxProposal(txp, function(err, txpb) { + $timeout(function() { + $scope.$emit('Local/GlideraError'); + }, 100); + }); + } + else { + self.success = data; + $scope.$emit('Local/GlideraTx'); + } + }); + } + }); + }); + }); + }); + }); + + }, 100); + + }; + + var _signTx = function(txp, cb) { + var self = this; + var fc = profileService.focusedClient; + fc.signTxProposal(txp, function(err, signedTx) { + profileService.lockFC(); + if (err) { + err = bwsError.msg(err, gettextCatalog.getString('Could not accept payment')); + return cb(err); + } + else { + if (signedTx.status == 'accepted') { + return cb(null, txp, signedTx.raw); + + } else { + return cb(gettext('The transaction could not be signed')); + } + } + }); + }; + + }); diff --git a/src/js/controllers/walletHome.js b/src/js/controllers/walletHome.js index a307c188a..cc868c659 100644 --- a/src/js/controllers/walletHome.js +++ b/src/js/controllers/walletHome.js @@ -30,8 +30,7 @@ angular.module('copayApp.controllers').controller('walletHomeController', functi if (isChromeApp) { var animatedSlideUp = 'full'; var animatedSlideRight = 'full'; - } - else { + } else { var animatedSlideUp = 'full animated slideInUp'; var animatedSlideRight = 'full animated slideInRight'; } @@ -174,18 +173,30 @@ angular.module('copayApp.controllers').controller('walletHomeController', functi }); }; - this.openTxpModal = function(tx, copayers) { + var GLIDERA_LOCK_TIME = 6 * 60 * 60 ; + // isGlidera flag is a security mesure so glidera status is not + // only determined by the tx.message + this.openTxpModal = function(tx, copayers, isGlidera) { var fc = profileService.focusedClient; var refreshUntilItChanges = false; var currentSpendUnconfirmed = $scope.currentSpendUnconfirmed; var ModalInstanceCtrl = function($scope, $modalInstance) { $scope.error = null; - $scope.tx = tx; $scope.copayers = copayers $scope.copayerId = fc.credentials.copayerId; $scope.canSign = fc.canSign(); $scope.loading = null; $scope.color = fc.backgroundColor; + + // ToDo: use tx.customData instead of tx.message + if (tx.message === 'Glidera transaction' && isGlidera) { + tx.isGlidera = true; + if (tx.canBeRemoved) { + tx.canBeRemoved = (Date.now()/1000 - (tx.ts || tx.createdOn)) > GLIDERA_LOCK_TIME; + } + } + $scope.tx = tx; + refreshUntilItChanges = false; $scope.currentSpendUnconfirmed = currentSpendUnconfirmed; diff --git a/src/js/init.js b/src/js/init.js index 81673661e..069440edc 100644 --- a/src/js/init.js +++ b/src/js/init.js @@ -9,8 +9,14 @@ angular.element(document).ready(function() { var handleBitcoinURI = function(url) { if (!url) return; + if (url.indexOf('glidera') != -1) { + url = '#/uri-glidera' + url.replace('bitcoin://glidera', ''); + } + else { + url = '#/uri-payment/' + url; + } setTimeout(function() { - window.location = '#/uri-payment/' + url; + window.location = url; }, 1000); }; diff --git a/src/js/routes.js b/src/js/routes.js index 3a36c10eb..28e8e0937 100644 --- a/src/js/routes.js +++ b/src/js/routes.js @@ -288,6 +288,56 @@ angular }, } }) + .state('uriglidera', { + url: '/uri-glidera?code', + needProfile: true, + views: { + 'main': { + templateUrl: 'views/glideraUri.html' + }, + } + }) + .state('glidera', { + url: '/glidera', + walletShouldBeComplete: true, + needProfile: true, + views: { + 'main': { + templateUrl: 'views/glidera.html' + }, + } + }) + .state('buyGlidera', { + url: '/buy', + walletShouldBeComplete: true, + needProfile: true, + views: { + 'main': { + templateUrl: 'views/buyGlidera.html' + }, + } + }) + .state('sellGlidera', { + url: '/sell', + walletShouldBeComplete: true, + needProfile: true, + views: { + 'main': { + templateUrl: 'views/sellGlidera.html' + }, + } + }) + + .state('preferencesGlidera', { + url: '/preferencesGlidera', + walletShouldBeComplete: true, + needProfile: true, + views: { + 'main': { + templateUrl: 'views/preferencesGlidera.html' + }, + } + }) .state('preferencesAdvanced', { url: '/preferencesAdvanced', @@ -486,11 +536,16 @@ angular copayers: -1, cordova: -1, payment: -1, + uriglidera: -1, preferences: 11, + glidera: 11, preferencesColor: 12, backup: 12, preferencesAdvanced: 12, + buyGlidera: 12, + sellGlidera: 12, + preferencesGlidera: 12, delete: 13, preferencesLanguage: 12, preferencesUnit: 12, diff --git a/src/js/services/glideraService.js b/src/js/services/glideraService.js new file mode 100644 index 000000000..b57a1c0ab --- /dev/null +++ b/src/js/services/glideraService.js @@ -0,0 +1,253 @@ +'use strict'; + +angular.module('copayApp.services').factory('glideraService', function($http, $log, isCordova) { + var root = {}; + var credentials = {}; + + root.setCredentials = function(network) { + if (network == 'testnet') { + credentials.HOST = 'https://sandbox.glidera.io'; + if (isCordova) { + credentials.REDIRECT_URI = 'bitcoin://glidera'; + credentials.CLIENT_ID = 'dfc56e4336e32bb8ba46dde34f3d7d6d'; + credentials.CLIENT_SECRET = '5eb679058f6c7eb81123162323d4fba5'; + } + else { + credentials.REDIRECT_URI = 'urn:ietf:wg:oauth:2.0:oob'; + credentials.CLIENT_ID = '9915b6ffa6dc3baffb87135ed3873d49'; + credentials.CLIENT_SECRET = 'd74eda05b9c6a228fd5c85cfbd0eb7eb'; + } + } + else { + credentials.HOST = 'https://glidera.io'; + credentials.REDIRECT_URI = 'urn:ietf:wg:oauth:2.0:oob'; + credentials.CLIENT_ID = ''; + credentials.CLIENT_SECRET = ''; + }; + }; + + root.getOauthCodeUrl = function() { + return credentials.HOST + + '/oauth2/auth?response_type=code&client_id=' + + credentials.CLIENT_ID + + '&redirect_uri=' + + credentials.REDIRECT_URI; + }; + + root.getToken = function(code, cb) { + var req = { + method: 'POST', + url: credentials.HOST + '/api/v1/oauth/token', + headers: { + 'Content-Type': 'application/json', + 'Accept': 'application/json' + }, + data: { + grant_type : 'authorization_code', + code: code, + client_id : credentials.CLIENT_ID, + client_secret: credentials.CLIENT_SECRET, + redirect_uri: credentials.REDIRECT_URI + } + }; + + $http(req).then(function(data) { + $log.info('Glidera Authorization Access Token: SUCCESS'); + return cb(null, data.data); + }, function(data) { + $log.error('Glidera Authorization Access Token: ERROR ' + data.statusText); + return cb('Glidera Authorization Access Token: ERROR ' + data.statusText); + }); + }; + + var _get = function(endpoint, token) { + return { + method: 'GET', + url: credentials.HOST + '/api/v1' + endpoint, + headers: { + 'Content-Type': 'application/json', + 'Accept': 'application/json', + 'Authorization': 'Bearer ' + token + } + }; + }; + + root.getAccessTokenPermissions = function(token, cb) { + if (!token) return cb('Invalid Token'); + $http(_get('/oauth/token', token)).then(function(data) { + $log.info('Glidera Access Token Permissions: SUCCESS'); + return cb(null, data.data); + }, function(data) { + $log.error('Glidera Access Token Permissions: ERROR ' + data.statusText); + return cb('Glidera Access Token Permissions: ERROR ' + data.statusText); + }); + }; + + root.getEmail = function(token, cb) { + if (!token) return cb('Invalid Token'); + $http(_get('/user/email', token)).then(function(data) { + $log.info('Glidera Get Email: SUCCESS'); + return cb(null, data.data); + }, function(data) { + $log.error('Glidera Get Email: ERROR ' + data.statusText); + return cb('Glidera Get Email: ERROR ' + data.statusText); + }); + }; + + root.getPersonalInfo = function(token, cb) { + if (!token) return cb('Invalid Token'); + $http(_get('/user/personalinfo', token)).then(function(data) { + $log.info('Glidera Get Personal Info: SUCCESS'); + return cb(null, data.data); + }, function(data) { + $log.error('Glidera Get Personal Info: ERROR ' + data.statusText); + return cb('Glidera Get Personal Info: ERROR ' + data.statusText); + }); + }; + + root.getStatus = function(token, cb) { + if (!token) return cb('Invalid Token'); + $http(_get('/user/status', token)).then(function(data) { + $log.info('Glidera User Status: SUCCESS'); + return cb(null, data.data); + }, function(data) { + $log.error('Glidera User Status: ERROR ' + data.statusText); + return cb('Glidera User Status: ERROR ' + data.statusText); + }); + }; + + root.getLimits = function(token, cb) { + if (!token) return cb('Invalid Token'); + $http(_get('/user/limits', token)).then(function(data) { + $log.info('Glidera Transaction Limits: SUCCESS'); + return cb(null, data.data); + }, function(data) { + $log.error('Glidera Transaction Limits: ERROR ' + data.statusText); + return cb('Glidera Transaction Limits: ERROR ' + data.statusText); + }); + }; + + root.getTransactions = function(token, cb) { + if (!token) return cb('Invalid Token'); + $http(_get('/transaction', token)).then(function(data) { + $log.info('Glidera Transactions: SUCCESS'); + return cb(null, data.data.transactions); + }, function(data) { + $log.error('Glidera Transactions: ERROR ' + data.statusText); + return cb('Glidera Transactions: ERROR ' + data.statusText); + }); + }; + + root.getTransaction = function(token, txid, cb) { + if (!token) return cb('Invalid Token'); + if (!txid) return cb('TxId required'); + $http(_get('/transaction/' + txid, token)).then(function(data) { + $log.info('Glidera Transaction: SUCCESS'); + return cb(null, data.data); + }, function(data) { + $log.error('Glidera Transaction: ERROR ' + data.statusText); + return cb('Glidera Transaction: ERROR ' + data.statusText); + }); + }; + + root.getSellAddress = function(token, cb) { + if (!token) return cb('Invalid Token'); + $http(_get('/user/create_sell_address', token)).then(function(data) { + $log.info('Glidera Create Sell Address: SUCCESS'); + return cb(null, data.data.sellAddress); + }, function(data) { + $log.error('Glidera Create Sell Address: ERROR ' + data.statusText); + return cb('Glidera Create Sell Address: ERROR ' + data.statusText); + }); + }; + + root.get2faCode = function(token, cb) { + if (!token) return cb('Invalid Token'); + $http(_get('/authentication/get2faCode', token)).then(function(data) { + $log.info('Glidera Sent 2FA code by SMS: SUCCESS'); + return cb(null, data.status == 200 ? true : false); + }, function(data) { + $log.error('Glidera Sent 2FA code by SMS: ERROR ' + data.statusText); + return cb('Glidera Sent 2FA code by SMS: ERROR ' + data.statusText); + }); + }; + + var _post = function(endpoint, token, twoFaCode, data) { + return { + method: 'POST', + url: credentials.HOST + '/api/v1' + endpoint, + headers: { + 'Content-Type': 'application/json', + 'Accept': 'application/json', + 'Authorization': 'Bearer ' + token, + '2FA_CODE': twoFaCode + }, + data: data + }; + }; + + root.sellPrice = function(token, price, cb) { + var data = { + qty: price.qty, + fiat: price.fiat + }; + $http(_post('/prices/sell', token, null, data)).then(function(data) { + $log.info('Glidera Sell Price: SUCCESS'); + return cb(null, data.data); + }, function(data) { + $log.error('Glidera Sell Price: ERROR ' + data.statusText); + return cb('Glidera Sell Price: ERROR ' + data.statusText); + }); + }; + + root.sell = function(token, twoFaCode, data, cb) { + var data = { + refundAddress: data.refundAddress, + signedTransaction: data.signedTransaction, + priceUuid: data.priceUuid, + useCurrentPrice: data.useCurrentPrice, + ip: data.ip + }; + $http(_post('/sell', token, twoFaCode, data)).then(function(data) { + $log.info('Glidera Sell: SUCCESS'); + return cb(null, data.data); + }, function(data) { + $log.error('Glidera Sell Request: ERROR ' + data.statusText); + return cb('Glidera Sell Request: ERROR ' + data.statusText); + }); + }; + + root.buyPrice = function(token, price, cb) { + var data = { + qty: price.qty, + fiat: price.fiat + }; + $http(_post('/prices/buy', token, null, data)).then(function(data) { + $log.info('Glidera Buy Price: SUCCESS'); + return cb(null, data.data); + }, function(data) { + $log.error('Glidera Buy Price: ERROR ' + data.statusText); + return cb('Glidera Buy Price: ERROR ' + data.statusText); + }); + }; + + root.buy = function(token, twoFaCode, data, cb) { + var data = { + destinationAddress: data.destinationAddress, + qty: data.qty, + priceUuid: data.priceUuid, + useCurrentPrice: data.useCurrentPrice, + ip: data.ip + }; + $http(_post('/buy', token, twoFaCode, data)).then(function(data) { + $log.info('Glidera Buy: SUCCESS'); + return cb(null, data.data); + }, function(data) { + $log.error('Glidera Buy Request: ERROR ' + data.statusText); + return cb('Glidera Buy Request: ERROR ' + data.statusText); + }); + }; + + return root; + +}); diff --git a/src/js/services/go.js b/src/js/services/go.js index 3e55e2939..782683e7f 100644 --- a/src/js/services/go.js +++ b/src/js/services/go.js @@ -29,12 +29,13 @@ angular.module('copayApp.services').factory('go', function($window, $rootScope, } }; - root.openExternalLink = function(url) { + root.openExternalLink = function(url, target) { if (nodeWebkit.isDefined()) { nodeWebkit.openExternalLink(url); } else { - var ref = window.open(url, '_blank', 'location=no'); + target = target || '_blank'; + var ref = window.open(url, target, 'location=no'); } }; @@ -89,8 +90,8 @@ angular.module('copayApp.services').factory('go', function($window, $rootScope, root.path(path); }; - $rootScope.openExternalLink = function(url) { - root.openExternalLink(url); + $rootScope.openExternalLink = function(url, target) { + root.openExternalLink(url, target); }; diff --git a/src/js/services/storageService.js b/src/js/services/storageService.js index 1916ec032..b7208ea7a 100644 --- a/src/js/services/storageService.js +++ b/src/js/services/storageService.js @@ -192,5 +192,17 @@ angular.module('copayApp.services') storage.get('remotePrefStored', cb); }; + root.setGlideraToken = function(network, token, cb) { + storage.set('glideraToken-' + network, token, cb); + }; + + root.getGlideraToken = function(network, cb) { + storage.get('glideraToken-' + network, cb); + }; + + root.removeGlideraToken = function(network, cb) { + storage.remove('glideraToken-' + network, cb); + }; + return root; });