From 9246d7df21e761f53dd4bf0d910ebb8e3827d215 Mon Sep 17 00:00:00 2001 From: Andrey Shovkoplyas Date: Wed, 27 Sep 2017 13:40:50 +0300 Subject: [PATCH] send transaction layer 1 --- resources/icons/tooltip-triangle.svg | 3 + src/status_im/components/animation.cljs | 2 + .../components/icons/vector_icons.cljs | 1 + .../components/toolbar_new/styles.cljs | 2 + .../components/toolbar_new/view.cljs | 10 +- src/status_im/transactions/handlers.cljs | 8 +- src/status_im/translations/en.cljs | 99 +++++----- src/status_im/ui/screens/db.cljs | 1 + src/status_im/ui/screens/subs.cljs | 1 + src/status_im/ui/screens/views.cljs | 5 + .../wallet/choose_recipient/styles.cljs | 124 +++++++++++++ .../wallet/choose_recipient/views.cljs | 95 ++++++++++ .../screens/wallet/components/animations.cljs | 12 ++ .../ui/screens/wallet/components/styles.cljs | 113 ++++++++++-- .../ui/screens/wallet/components/views.cljs | 93 +++++++--- src/status_im/ui/screens/wallet/db.cljs | 23 ++- .../ui/screens/wallet/main/styles.cljs | 34 ++-- .../ui/screens/wallet/main/views.cljs | 65 +++---- .../ui/screens/wallet/navigation.cljs | 6 + .../ui/screens/wallet/request/db.cljs | 3 +- .../ui/screens/wallet/request/events.cljs | 13 +- .../ui/screens/wallet/request/styles.cljs | 42 ----- .../ui/screens/wallet/request/subs.cljs | 11 ++ .../ui/screens/wallet/request/views.cljs | 47 +++-- .../ui/screens/wallet/send/animations.cljs | 11 ++ src/status_im/ui/screens/wallet/send/db.cljs | 17 ++ .../ui/screens/wallet/send/events.cljs | 119 +++++++++++- .../ui/screens/wallet/send/styles.cljs | 154 ++++------------ .../ui/screens/wallet/send/subs.cljs | 17 ++ .../wallet/send/transaction_sent/styles.cljs | 50 +++++ .../wallet/send/transaction_sent/views.cljs | 38 ++++ .../ui/screens/wallet/send/views.cljs | 173 +++++++++++------- src/status_im/ui/screens/wallet/styles.cljs | 51 +++++- src/status_im/utils/money.cljs | 3 + src/status_im/utils/utils.cljs | 12 ++ 35 files changed, 1056 insertions(+), 402 deletions(-) create mode 100644 resources/icons/tooltip-triangle.svg create mode 100644 src/status_im/ui/screens/wallet/choose_recipient/styles.cljs create mode 100644 src/status_im/ui/screens/wallet/choose_recipient/views.cljs create mode 100644 src/status_im/ui/screens/wallet/components/animations.cljs create mode 100644 src/status_im/ui/screens/wallet/request/subs.cljs create mode 100644 src/status_im/ui/screens/wallet/send/animations.cljs create mode 100644 src/status_im/ui/screens/wallet/send/db.cljs create mode 100644 src/status_im/ui/screens/wallet/send/transaction_sent/styles.cljs create mode 100644 src/status_im/ui/screens/wallet/send/transaction_sent/views.cljs diff --git a/resources/icons/tooltip-triangle.svg b/resources/icons/tooltip-triangle.svg new file mode 100644 index 0000000000..d60503c3e4 --- /dev/null +++ b/resources/icons/tooltip-triangle.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/status_im/components/animation.cljs b/src/status_im/components/animation.cljs index 21a6cac5c1..42fdfdc2b9 100644 --- a/src/status_im/components/animation.cljs +++ b/src/status_im/components/animation.cljs @@ -55,3 +55,5 @@ (defn create-value-xy [x y] (js/ReactNative.Animated.ValueXY. (clj->js {:x x, :y y}))) + +(def easing js/ReactNative.Easing) \ No newline at end of file diff --git a/src/status_im/components/icons/vector_icons.cljs b/src/status_im/components/icons/vector_icons.cljs index 730744c447..3e2e051376 100644 --- a/src/status_im/components/icons/vector_icons.cljs +++ b/src/status_im/components/icons/vector_icons.cljs @@ -67,6 +67,7 @@ :icons/dropdown (slurp-svg "./resources/icons/dropdown.svg") :icons/grab (slurp-svg "./resources/icons/grab.svg") :icons/share (slurp-svg "./resources/icons/share.svg") + :icons/tooltip-triangle (slurp-svg "./resources/icons/tooltip-triangle.svg") :icons/network (slurp-svg "./resources/icons/network.svg")}) (defn normalize-property-name [n] diff --git a/src/status_im/components/toolbar_new/styles.cljs b/src/status_im/components/toolbar_new/styles.cljs index b2bc42de16..e904b0d8b9 100644 --- a/src/status_im/components/toolbar_new/styles.cljs +++ b/src/status_im/components/toolbar_new/styles.cljs @@ -106,3 +106,5 @@ (def toolbar-text-action-disabled {:color styles/color-gray7}) (def item-text-white-background {:color styles/color-blue4}) + +(def ios-content-item {:position :absolute :right 90 :left 90}) diff --git a/src/status_im/components/toolbar_new/view.cljs b/src/status_im/components/toolbar_new/view.cljs index cb54bb9ae1..d7a31ab6ab 100644 --- a/src/status_im/components/toolbar_new/view.cljs +++ b/src/status_im/components/toolbar_new/view.cljs @@ -7,7 +7,8 @@ [status-im.components.context-menu :as context-menu] [status-im.components.toolbar-new.actions :as act] [status-im.components.toolbar-new.styles :as tst] - [status-im.components.icons.vector-icons :as vi])) + [status-im.components.icons.vector-icons :as vi] + [status-im.utils.platform :as platform])) ;; Navigation item @@ -103,10 +104,15 @@ action-items] [rn/view {:style (merge (tst/toolbar-wrapper background-color flat?) style)} [rn/view {:style tst/toolbar} + (when platform/ios? + [rn/view tst/ios-content-item + content-item]) (when nav-item [rn/view {:style (tst/toolbar-nav-actions-container 0)} nav-item]) - content-item + (if platform/ios? + [rn/view st/flex] + content-item) action-items] (when-not no-sync-bar? [sync-state-gradient-view/sync-state-gradient-view])])) diff --git a/src/status_im/transactions/handlers.cljs b/src/status_im/transactions/handlers.cljs index d4a7764866..d858fbc446 100644 --- a/src/status_im/transactions/handlers.cljs +++ b/src/status_im/transactions/handlers.cljs @@ -127,8 +127,7 @@ (status/discard-transaction id))))) (register-handler ::transaction-queued - (after #(dispatch [:navigate-to-modal :unsigned-transactions])) - (fn [db [_ {:keys [id message_id args] :as transaction}]] + (fn [{:wallet/keys [send-transaction] :as db} [_ {:keys [id message_id args] :as transaction}]] (let [{:keys [from to value data gas gasPrice]} args] (if (transaction-valid? transaction) (let [transaction {:id id @@ -140,6 +139,9 @@ :gas-price (.toDecimal js/Web3.prototype gasPrice) :timestamp (time/now-ms) :message-id message_id}] + (if (:waiting-signal? send-transaction) + (dispatch [:wallet/transaction-queued id]) + (dispatch [:navigate-to-modal :unsigned-transactions])) (assoc-in db [:transactions-queue id] transaction)) db)))) @@ -147,7 +149,7 @@ (u/side-effect! (fn [{:keys [transactions modal]} [_ {:keys [id response]}]] (let [{:keys [hash error]} response - {:keys [message-id]} (transactions id)] + {:keys [message-id]} (get transactions id)] (log/debug :parsed-response response) (when-not (and error (string? error) (not (s/blank? error))) (if (and message-id (not (s/blank? message-id))) diff --git a/src/status_im/translations/en.cljs b/src/status_im/translations/en.cljs index 2514465a25..f27bcdb75b 100644 --- a/src/status_im/translations/en.cljs +++ b/src/status_im/translations/en.cljs @@ -3,50 +3,52 @@ (def translations { ;;common - :members-title "Members" - :not-implemented "!not implemented" - :chat-name "Chat name" - :notifications-title "Notifications and sounds" - :offline "Offline" - :search-for "Search for..." - :cancel "Cancel" - :next "Next" - :type-a-message "Type a message..." - :type-a-command "Start typing a command..." - :error "Error" - :unknown-status-go-error "Unknown status-go error" - :node-unavailable "No ethereum node running" + :members-title "Members" + :not-implemented "!not implemented" + :chat-name "Chat name" + :notifications-title "Notifications and sounds" + :offline "Offline" + :search-for "Search for..." + :cancel "Cancel" + :next "Next" + :type-a-message "Type a message..." + :type-a-command "Start typing a command..." + :error "Error" + :unknown-status-go-error "Unknown status-go error" + :node-unavailable "No ethereum node running" + :yes "Yes" + :no "No" - :camera-access-error "To grant the required camera permission, please, go to your system settings and make sure that Status > Camera is selected." - :photos-access-error "To grant the required photos permission, please, go to your system settings and make sure that Status > Photos is selected." + :camera-access-error "To grant the required camera permission, please, go to your system settings and make sure that Status > Camera is selected." + :photos-access-error "To grant the required photos permission, please, go to your system settings and make sure that Status > Photos is selected." ;;drawer - :invite-friends "Invite friends" - :faq "FAQ" - :switch-users "Switch users" - :feedback "Got feedback?\nShake your phone!" - :view-all "View all" - :current-network "Current network" + :invite-friends "Invite friends" + :faq "FAQ" + :switch-users "Switch users" + :feedback "Got feedback?\nShake your phone!" + :view-all "View all" + :current-network "Current network" ;;chat - :is-typing "is typing" - :and-you "and you" - :search-chat "Search chat" - :members {:one "1 member" - :other "{{count}} members" - :zero "no members"} - :members-active {:one "1 member" - :other "{{count}} members" - :zero "no members"} - :public-group-status "Public" - :active-online "Online" - :active-unknown "Unknown" - :available "Available" - :no-messages "No messages" - :suggestions-requests "Requests" - :suggestions-commands "Commands" - :faucet-success "Faucet request has been received" - :faucet-error "Faucet request error" + :is-typing "is typing" + :and-you "and you" + :search-chat "Search chat" + :members {:one "1 member" + :other "{{count}} members" + :zero "no members"} + :members-active {:one "1 member" + :other "{{count}} members" + :zero "no members"} + :public-group-status "Public" + :active-online "Online" + :active-unknown "Unknown" + :available "Available" + :no-messages "No messages" + :suggestions-requests "Requests" + :suggestions-commands "Commands" + :faucet-success "Faucet request has been received" + :faucet-error "Faucet request error" ;;sync :sync-in-progress "Syncing..." @@ -275,6 +277,7 @@ :sign-in-to-status "Sign in to Status" :sign-in "Sign in" :wrong-password "Wrong password" + :enter-password "Enter password" ;;recover :recover-from-passphrase "Recover from passphrase" @@ -356,6 +359,7 @@ :wallet-add-asset "Add asset" :wallet-total-value "Total value" :wallet-settings "Wallet Settings" + :signing-phrase-description "Sign the transaction by entering your password. Make sure that the words above match your secret signing phrase" :request-transaction "Request Transaction" :send-request "Send request" :share "Share" @@ -364,6 +368,8 @@ :transaction-details "Transaction details" :transactions-sign "Sign" :transactions-sign-all "Sign all" + :transactions-sign-transaction "Sign Transaction" + :transactions-sign-later "Sign Later" :transactions-sign-all-text "Sign the transaction by entering your password.\nMake sure that the words above match your secret signing phrase" :transactions-sign-input-placeholder "Enter your password" :transactions-history "History" @@ -374,15 +380,26 @@ :transactions-filter-tokens "Tokens" :transactions-filter-type "Type" :transactions-filter-select-all "Select all" + :view-transaction-details "View Transaction Details" + :transaction-description "If you want to be sure you transaction will not be compromised wait until it gets at least 10 block confirmations" + :transaction-sent "Transaction Sent" + :transaction-moved-text "The transaction has ben successfully moved to “Unsigned”" + :transaction-moved-title "Transaction Moved" + :sign-later-title "Sign Transaction Later?" + :sign-later-text "You will be able to sign in in the transaction history" :not-applicable "Not applicable for unsigned transactions" ;; Wallet Send - :wallet-send-transaction "Send a Transaction" - :wallet-send-step-one "Step 1 of 3" + :wallet-send-transaction "Send Transaction" + :wallet-send-step "Step {{step}} of {{number}}" :wallet-choose-recipient "Choose Recipient" :wallet-choose-from-contacts "Choose From Contacts" :wallet-address-from-clipboard "Use Address From Clipboard" :wallet-browse-photos "Browse Photos" + :validation-amount-invalid-number "Amount is not valid number" + :validation-amount-is-too-precise "Amount is too precise. The smallest unit you can send is 1 Wei (1x10^-18 ETH)" + + ;network settings :new-network "New network" diff --git a/src/status_im/ui/screens/db.cljs b/src/status_im/ui/screens/db.cljs index ce51ede946..677398e307 100644 --- a/src/status_im/ui/screens/db.cljs +++ b/src/status_im/ui/screens/db.cljs @@ -112,6 +112,7 @@ :my-profile/profile :my-profile/default-name :wallet/request-transaction + :wallet/send-transaction :networks/selected-network :networks/networks :node/after-start diff --git a/src/status_im/ui/screens/subs.cljs b/src/status_im/ui/screens/subs.cljs index 07595530b4..055794bd1c 100644 --- a/src/status_im/ui/screens/subs.cljs +++ b/src/status_im/ui/screens/subs.cljs @@ -11,6 +11,7 @@ status-im.ui.screens.wallet.subs status-im.ui.screens.wallet.transactions.subs status-im.ui.screens.wallet.send.subs + status-im.ui.screens.wallet.request.subs status-im.ui.screens.network-settings.subs status-im.transactions.subs status-im.bots.subs)) diff --git a/src/status_im/ui/screens/views.cljs b/src/status_im/ui/screens/views.cljs index 5f3f9e7aba..5400beffe7 100644 --- a/src/status_im/ui/screens/views.cljs +++ b/src/status_im/ui/screens/views.cljs @@ -43,9 +43,12 @@ [status-im.ui.screens.profile.qr-code.views :refer [qr-code-view]] [status-im.ui.screens.wallet.send.views :refer [send-transaction]] + [status-im.ui.screens.wallet.choose-recipient.views :refer [choose-recipient]] [status-im.ui.screens.wallet.request.views :refer [request-transaction]] [status-im.ui.screens.wallet.wallet-list.views :refer [wallet-list-screen]] [status-im.ui.screens.wallet.transactions.views :as wallet-transactions] + [status-im.ui.screens.wallet.send.transaction-sent.views :refer [transaction-sent]] + [status-im.components.status-bar :as status-bar] [status-im.ui.screens.network-settings.views :refer [network-settings]] [status-im.ui.screens.network-settings.add-rpc.views :refer [add-rpc-url]] @@ -69,6 +72,8 @@ (:wallet :chat-list :discover :contact-list) main-tabs :wallet-list wallet-list-screen :wallet-send-transaction send-transaction + :wallet-transaction-sent transaction-sent + :choose-recipient choose-recipient :wallet-request-transaction request-transaction :wallet-transactions wallet-transactions/transactions :wallet-transaction-details wallet-transactions/transaction-details diff --git a/src/status_im/ui/screens/wallet/choose_recipient/styles.cljs b/src/status_im/ui/screens/wallet/choose_recipient/styles.cljs new file mode 100644 index 0000000000..dac1b418df --- /dev/null +++ b/src/status_im/ui/screens/wallet/choose_recipient/styles.cljs @@ -0,0 +1,124 @@ +(ns status-im.ui.screens.wallet.choose-recipient.styles + (:require-macros [status-im.utils.styles :refer [defnstyle defstyle]]) + (:require [status-im.components.styles :as styles])) + +(def wallet-container + {:flex 1 + :background-color styles/color-blue4}) + +(def toolbar-buttons-container + {:flex-direction :row + :flex-shrink 1 + :justify-content :space-between + :width 68 + :margin-right 12}) + +(def choose-recipient-container + {:flex-direction :row + :padding-top 20 + :padding-bottom 20 + :justify-content :center}) + +(def choose-recipient-label + {:color :white}) + +(defstyle recipient-buttons + {:flex-direction :column + :margin-horizontal 28 + :margin-vertical 20 + :border-radius 8 + :ios {:background-color styles/color-blue6}}) + +(def recipient-icon {:margin-right 20}) + +(def recipient-icon-disabled {:margin-right 20 + :opacity 0.3}) + +(def recipient-button + {:flex-direction :row + :justify-content :space-between + :margin-vertical 10 + :margin-left 20}) + +(def recipient-button-text + {:color :white + :align-self :center + :font-size 14}) + +(def recipient-button-text-disabled + (merge recipient-button-text {:color "rgba(255, 255, 255, 0.3)"})) + +(defnstyle recipient-touchable [divider?] + (cond-> {:border-color styles/color-gray-transparent-light} + divider? (assoc :ios {:border-bottom-width 1}))) + +(def recipient-touchable-disabled + {:background-color styles/color-blue4 + :border-bottom-left-radius 8 + :border-bottom-right-radius 8 + :border-left-width 1 + :border-bottom-width 1 + :border-right-width 1 + :border-color "rgba(255, 255, 255, 0.3)"}) + +(def qr-container + {:flex 1}) + +(def preview + {:flex 1 + :justify-content :flex-end + :align-items :center}) + +(def corner-dimensions + {:position :absolute + :width 40 + :height 40}) + +(defn corner-left-bottom [dimension] + (let [viewport-offset 0.1666] + (merge corner-dimensions {:bottom (* viewport-offset dimension) + :left (* viewport-offset dimension)}))) + +(defn corner-right-bottom [dimension] + (let [viewport-offset 0.1666] + (merge corner-dimensions {:right (* viewport-offset dimension) + :bottom (* viewport-offset dimension)}))) + +(defn corner-left-top [dimension] + (let [viewport-offset 0.1666] + (merge corner-dimensions {:top (* viewport-offset dimension) + :left (* viewport-offset dimension)}))) + +(defn corner-right-top [dimension] + (let [viewport-offset 0.1666] + (merge corner-dimensions {:top (* viewport-offset dimension) + :right (* viewport-offset dimension)}))) + +(def viewfinder-port {:position :absolute + :left 0 + :top 0 + :bottom 0 + :right 0 + :flex 1}) + +(defn viewfinder-translucent [height width side] + (let [viewport-offset 0.1666 + height-offset (* viewport-offset height) + width-offset (* viewport-offset width)] + (cond-> {:position :absolute + :background-color :black + :opacity 0.7} + (= :top side) (assoc :height height-offset + :width width) + (= :right side) (assoc :height (- height height-offset) + :width width-offset + :bottom 0 + :right 0) + (= :bottom side) (assoc :height height-offset + :width (- width width-offset) + :bottom 0 + :left 0) + (= :left side) (assoc :height (- height (* 2 height-offset)) + :width width-offset + :top height-offset + :left 0)))) \ No newline at end of file diff --git a/src/status_im/ui/screens/wallet/choose_recipient/views.cljs b/src/status_im/ui/screens/wallet/choose_recipient/views.cljs new file mode 100644 index 0000000000..b130eae856 --- /dev/null +++ b/src/status_im/ui/screens/wallet/choose_recipient/views.cljs @@ -0,0 +1,95 @@ +(ns status-im.ui.screens.wallet.choose-recipient.views + (:require-macros [status-im.utils.views :refer [defview letsubs]]) + (:require [re-frame.core :as re-frame] + [status-im.utils.utils :as utils] + [status-im.components.toolbar-new.view :as toolbar] + [status-im.components.toolbar-new.actions :as act] + [status-im.i18n :as i18n] + [status-im.ui.screens.wallet.styles :as wallet.styles] + [status-im.components.react :as react] + [status-im.components.icons.vector-icons :as vector-icons] + [status-im.ui.screens.wallet.choose-recipient.styles :as styles] + [status-im.components.status-bar :as status-bar] + [status-im.components.camera :as camera] + [clojure.string :as string])) + +(defn- show-not-implemented! [] + (utils/show-popup "TODO" "Not implemented yet!")) + +(defn choose-from-contacts [] + (re-frame/dispatch [:navigate-to-modal + :contact-list-modal + {:handler #(re-frame/dispatch [:wallet-open-send-transaction (:address %1) (:name %1)]) + :action :send + :params {:hide-actions? true}}])) + +(defn toolbar-view [] + [toolbar/toolbar2 {:style wallet.styles/toolbar + :no-sync-bar? true} + [toolbar/nav-button (act/back-white act/default-handler)] + [toolbar/content-title {:color :white} (i18n/label :t/wallet-choose-recipient)] + [toolbar/actions [{:icon :icons/flash-active + :icon-opts {:color :white} + :handler show-not-implemented!}]]]) + +(defn recipient-buttons [] + [react/view {:style styles/recipient-buttons} + [react/touchable-highlight {:style (styles/recipient-touchable true) + :on-press choose-from-contacts} + [react/view {:style styles/recipient-button} + [react/text {:style styles/recipient-button-text} + (i18n/label :t/wallet-choose-from-contacts)] + [vector-icons/icon :icons/qr {:color :white + :container-style styles/recipient-icon}]]] + [react/touchable-highlight {:style (styles/recipient-touchable true) + :on-press #(react/get-from-clipboard + (fn [clipboard] + (re-frame/dispatch [:choose-recipient clipboard])))} + [react/view {:style styles/recipient-button} + [react/text {:style styles/recipient-button-text} + (i18n/label :t/wallet-address-from-clipboard)] + [vector-icons/icon :icons/copy-from {:color :white + :container-style styles/recipient-icon}]]] + [react/touchable-highlight {:style styles/recipient-touchable-disabled} + [react/view {:style styles/recipient-button} + [react/text {:style styles/recipient-button-text-disabled} + (i18n/label :t/wallet-browse-photos)] + [vector-icons/icon :icons/browse {:color :white + :container-style styles/recipient-icon-disabled}]]]]) + +(defn viewfinder [{:keys [height width]}] + (let [min-dimension (min height width)] + [react/view {:style styles/viewfinder-port} + [react/view {:style (styles/viewfinder-translucent height width :top)}] + [react/view {:style (styles/viewfinder-translucent height width :right)}] + [react/view {:style (styles/viewfinder-translucent height width :bottom)}] + [react/view {:style (styles/viewfinder-translucent height width :left)}] + [react/image {:source {:uri :corner_left_top} + :style (styles/corner-left-top min-dimension)}] + [react/image {:source {:uri :corner_right_top} + :style (styles/corner-right-top min-dimension)}] + [react/image {:source {:uri :corner_left_bottom} + :style (styles/corner-left-bottom min-dimension)}] + [react/image {:source {:uri :corner_right_bottom} + :style (styles/corner-right-bottom min-dimension)}]])) + +(defview choose-recipient [] + (letsubs [camera-dimensions [:camera-dimensions]] + [react/view {:style styles/wallet-container} + [status-bar/status-bar {:type :wallet}] + [toolbar-view] + [react/view {:style styles/qr-container + :on-layout #(let [layout (.. % -nativeEvent -layout)] + (re-frame/dispatch [:set-in [:wallet :camera-dimensions] + {:width (.-width layout) + :height (.-height layout)}]))} + [camera/camera {:style styles/preview + :aspect :fill + :captureAudio false + :onBarCodeRead (fn [code] + (let [data (-> code + .-data + (string/replace #"ethereum:" ""))] + (re-frame/dispatch [:choose-recipient data])))}] + [viewfinder camera-dimensions]] + [recipient-buttons]])) \ No newline at end of file diff --git a/src/status_im/ui/screens/wallet/components/animations.cljs b/src/status_im/ui/screens/wallet/components/animations.cljs new file mode 100644 index 0000000000..66923ecb46 --- /dev/null +++ b/src/status_im/ui/screens/wallet/components/animations.cljs @@ -0,0 +1,12 @@ +(ns status-im.ui.screens.wallet.components.animations + (:require [status-im.components.animation :as animation])) + +(defn animate-tooltip [bottom-value opacity-value] + (fn [] + (animation/start + (animation/parallel + [(animation/timing opacity-value {:toValue 1 + :duration 500}) + (animation/timing bottom-value {:toValue 8.5 + :easing (.bezier animation/easing 0.685, 0.000, 0.025, 1.185) + :duration 500})])))) \ No newline at end of file diff --git a/src/status_im/ui/screens/wallet/components/styles.cljs b/src/status_im/ui/screens/wallet/components/styles.cljs index 5489b2b501..30b722d9e8 100644 --- a/src/status_im/ui/screens/wallet/components/styles.cljs +++ b/src/status_im/ui/screens/wallet/components/styles.cljs @@ -12,15 +12,23 @@ :height 52 :letter-spacing -0.2}) -(def label - {:color :white - :font-size 14 - :line-height 16 - :letter-spacing -0.2}) +(defstyle label + {:color :white + :ios {:font-size 14 + :line-height 16 + :letter-spacing -0.2} + :android {:font-size 12 + :line-height 12}}) + +(def amount-text-input-container + {:margin-top 8}) + +(def label-transparent + (merge label + {:color styles/color-white-transparent})) (defnstyle amount-container [active?] - {:margin-top 8 - :height 52 + {:height 52 :background-color (if active? styles/color-white-transparent-4 styles/color-white-transparent-3) @@ -42,31 +50,100 @@ :justify-content :center}) (defstyle currency-container - {:margin-top 8 + {:margin-top 8 + :height 52 + ;;TODO disabled + :border-width 1 + :border-color styles/color-white-transparent-4 + ;:background-color styles/color-white-transparent-3 + :justify-content :center + :padding 14 + :ios {:border-radius 8} + :android {:border-radius 4}}) + +(defstyle recipient-container + {:flex-direction :row + :flex 1 + :margin-top 8 :height 52 + :align-items :center :background-color styles/color-white-transparent-3 - :justify-content :center - :padding 14 + :padding-vertical 14 + :padding-left 14 + :padding-right 8 :ios {:border-radius 8} :android {:border-radius 4}}) (defstyle wallet-container - {:flex-direction :row - :margin-top 8 - :height 52 - :background-color styles/color-white-transparent-3 - :align-items :center - :padding 14 - :ios {:border-radius 8} - :android {:border-radius 4}}) + {:flex-direction :row + :margin-top 8 + :height 52 + ;;TODO disabled + :border-width 1 + :border-color styles/color-white-transparent-4 + ;:background-color styles/color-white-transparent-3 + :align-items :center + :padding 14 + :ios {:border-radius 8} + :android {:border-radius 4}}) (def wallet-name {:color :white :font-size 15 :letter-spacing -0.2}) +(defn participant [address?] + {:color (if address? :white styles/color-white-transparent) + :flex-shrink 1 + :font-size 15 + :letter-spacing -0.2}) + (def wallet-value {:padding-left 6 :color styles/color-white-transparent-5 :font-size 15 :letter-spacing -0.2}) + +(def separator + {:height 1 + :margin-horizontal 15 + :background-color styles/color-white-transparent-1 + :margin-top 16}) + +(def button-text + {:color :white + :font-size 15 + :letter-spacing -0.2}) + +(def tooltip-container + {:position :absolute + :align-items :center + :left 0 + :right 0 + :top 0}) + +(defn tooltip-animated [bottom-value opacity-value] + {:position :absolute + :align-items :center + :left 0 + :right 0 + :bottom bottom-value + :opacity opacity-value}) + +(def tooltip-text-container + {:padding-horizontal 16 + :padding-vertical 9 + :background-color :white + :border-radius 8}) + +(def tooltip-text + {:color styles/color-red-2 + :font-size 15}) + +(def tooltip-triangle + {:width 16 + :height 8 + :viewBox "0 0 16 8"}) + +(def recipient-name-container + {:padding-right 6}) \ No newline at end of file diff --git a/src/status_im/ui/screens/wallet/components/views.cljs b/src/status_im/ui/screens/wallet/components/views.cljs index 7eb6a5bdc3..ac642995ed 100644 --- a/src/status_im/ui/screens/wallet/components/views.cljs +++ b/src/status_im/ui/screens/wallet/components/views.cljs @@ -1,29 +1,48 @@ (ns status-im.ui.screens.wallet.components.views (:require-macros [status-im.utils.views :as views]) (:require [status-im.components.react :as react] + [status-im.components.styles :as components.styles] [status-im.ui.screens.wallet.components.styles :as styles] [status-im.i18n :as i18n] - [reagent.core :as reagent])) + [reagent.core :as reagent] + [status-im.components.icons.vector-icons :as vector-icons] + [status-im.components.animation :as animation] + [status-im.utils.platform :as platform] + [status-im.ui.screens.wallet.components.animations :as animations])) + +(views/defview tooltip [label & [style]] + (views/letsubs [bottom-value (animation/create-value 16) + opacity-value (animation/create-value 0)] + {:component-did-mount (animations/animate-tooltip bottom-value opacity-value)} + [react/view styles/tooltip-container + [react/animated-view {:style (merge (styles/tooltip-animated bottom-value opacity-value) style)} + [react/view styles/tooltip-text-container + [react/text {:style styles/tooltip-text} label]] + [vector-icons/icon :icons/tooltip-triangle {:color :white :style styles/tooltip-triangle}]]])) + +(defn amount-input [] + (let [active? (reagent/atom false)] + (fn [& [{:keys [input-options style error]}]] + (let [{:keys [on-focus on-blur]} input-options] + [react/view components.styles/flex + [react/text {:style styles/label} (i18n/label :t/amount)] + [react/view styles/amount-text-input-container + [react/view (merge (styles/amount-container @active?) style) + [react/text-input + (merge + {:keyboard-type :numeric + :placeholder "0.000" + :placeholder-text-color "#ffffff66" + :selection-color :white + :style styles/text-input + :on-focus #(do (reset! active? true) + (when on-focus (on-focus))) + :on-blur #(do (reset! active? false) + (when on-blur (on-blur)))} + (dissoc input-options :on-focus :on-blur))]] + (when-not (nil? error) + [tooltip error])]])))) -(defn amount-input [& [{:keys [input-options style]}]] - (let [active? (reagent/atom false) - {:keys [on-focus on-blur]} input-options] - (fn [] - [react/view {:flex 1} - [react/text {:style styles/label} (i18n/label :t/amount)] - [react/view (merge (styles/amount-container @active?) style) - [react/text-input - (merge - {:keyboard-type :numeric - :placeholder "0.000" - :placeholder-text-color "#ffffff66" - :selection-color :white - :style styles/text-input - :on-focus #(do (reset! active? true) - (when on-focus (on-focus))) - :on-blur #(do (reset! active? false) - (when on-blur (on-blur)))} - (dissoc input-options :on-focus :on-blur))]]]))) ;;TODO (andrey) this should be choose component with the list of currencies (defn choose-currency [& [style]] @@ -33,6 +52,25 @@ style) [react/text {:style styles/wallet-name} "ETH"]]]) +(defn choose-recipient [{:keys [address name on-press style]}] + (let [address? (and (not (nil? address)) (not= address ""))] + [react/touchable-highlight {:on-press on-press} + [react/view + [react/text {:style styles/label} (i18n/label :t/recipient)] + [react/view (merge styles/recipient-container + style) + (when name + [react/view styles/recipient-name-container + [react/text {:style (styles/participant true) + :number-of-lines 1} + name]]) + [react/view components.styles/flex + [react/text {:style (styles/participant (and (not name) address?)) + :number-of-lines 1 + :ellipsizeMode :middle} + (if address? address "Choose recipient...")]] + [vector-icons/icon :icons/forward {:color :white}]]]])) + ;;TODO (andrey) this should be choose component with the list of wallets (views/defview choose-wallet [& [style]] (views/letsubs [eth-balance [:eth-balance]] @@ -41,10 +79,21 @@ [react/view (merge styles/wallet-container style) [react/text {:style styles/wallet-name} "Main wallet"] - [react/text {:style styles/wallet-value} (str eth-balance " ETH")]]])) + [react/text {:style styles/wallet-value + :number-of-lines 1 + :ellipsizeMode :middle} + (str eth-balance " ETH")]]])) (defn network-label ([n] (network-label [{} n])) ([style n] [react/view (merge styles/network-container style) - [react/text {:style styles/network} n]])) \ No newline at end of file + [react/text {:style styles/network} n]])) + +(defn separator [] + [react/view styles/separator]) + +(defn button-text [label] + [react/text {:style styles/button-text + :font (if platform/android? :medium :default) + :uppercase? (get-in platform/platform-specific [:uppercase?])} label]) \ No newline at end of file diff --git a/src/status_im/ui/screens/wallet/db.cljs b/src/status_im/ui/screens/wallet/db.cljs index 7639b9e0f9..3e663509df 100644 --- a/src/status_im/ui/screens/wallet/db.cljs +++ b/src/status_im/ui/screens/wallet/db.cljs @@ -1,6 +1,10 @@ (ns status-im.ui.screens.wallet.db (:require [cljs.spec.alpha :as spec] - status-im.ui.screens.wallet.request.db)) + status-im.ui.screens.wallet.request.db + status-im.ui.screens.wallet.send.db + [status-im.i18n :as i18n] + [clojure.string :as string])) + ;; (angusiguess) If we add more error types we can treat them as 'one-of' the following (spec/def :wallet/error #{:error}) @@ -15,3 +19,20 @@ ;; TODO(oskarth): spec for balance as BigNumber ;; TODO(oskarth): Spec for prices as as: {:from ETH, :to USD, :price 290.11, :last-day 304.17} + +(defn get-amount-validation-error [amount web3] + (let [amount' (string/replace amount #"," ".") + amount-splited (string/split amount' #"[.]")] + (cond + (or (nil? amount) (= amount "") (= amount "0") (re-matches #"0[,.]0*$" amount)) + nil + + (or (js/isNaN (js/parseFloat amount')) + (try (when (<= (.toWei web3 amount' "ether") 0) true) + (catch :default err true))) + (i18n/label :t/validation-amount-invalid-number) + + (and (= (count amount-splited) 2) (> (count (last amount-splited)) 18)) + (i18n/label :t/validation-amount-is-too-precise) + + :else nil))) \ No newline at end of file diff --git a/src/status_im/ui/screens/wallet/main/styles.cljs b/src/status_im/ui/screens/wallet/main/styles.cljs index 30c3024275..f44f491d81 100644 --- a/src/status_im/ui/screens/wallet/main/styles.cljs +++ b/src/status_im/ui/screens/wallet/main/styles.cljs @@ -3,9 +3,6 @@ (:require [status-im.components.styles :as styles] [status-im.utils.platform :as platform])) -(def wallet-container - {:flex 1}) - (def error-container {:align-self :center :justify-content :center @@ -19,12 +16,9 @@ :padding-right 10 :font-size 13}) -(def toolbar - {:background-color (if platform/ios? styles/color-blue4 styles/color-blue5) - :elevation 0}) - (def toolbar-title-container - {:flex-direction :row}) + {:flex-direction :row + :padding-left 24}) (def toolbar-title-text {:color styles/color-white @@ -59,21 +53,21 @@ :color styles/color-white}) (defstyle total-balance-currency - {:font-size 37 - :margin-left 9 - :color styles/color-white-transparent-5 - :android {:letter-spacing 1.5} - :ios {:letter-spacing 1.16}}) + {:font-size 37 + :margin-left 9 + :color styles/color-white-transparent-5 + :android {:letter-spacing 1.5} + :ios {:letter-spacing 1.16}}) (def value-variation {:flex-direction :row :align-items :center}) (defstyle value-variation-title - {:font-size 14 - :color styles/color-white-transparent-6 - :android {:letter-spacing -0.18} - :ios {:letter-spacing -0.2}}) + {:font-size 14 + :color styles/color-white-transparent-6 + :android {:letter-spacing -0.18} + :ios {:letter-spacing -0.2}}) (def today-variation-container {:border-radius 100 @@ -101,9 +95,9 @@ {:color styles/color-red-4})) (defstyle buttons - {:margin-top 34 - :android {:margin-horizontal 21} - :ios {:margin-horizontal 29}}) + {:margin-top 34 + :android {:margin-horizontal 21} + :ios {:margin-horizontal 29}}) (defstyle main-button-text {:padding-vertical 13 diff --git a/src/status_im/ui/screens/wallet/main/views.cljs b/src/status_im/ui/screens/wallet/main/views.cljs index 6adb06301d..8f398f34fa 100644 --- a/src/status_im/ui/screens/wallet/main/views.cljs +++ b/src/status_im/ui/screens/wallet/main/views.cljs @@ -6,7 +6,6 @@ [status-im.components.drawer.view :as drawer] [status-im.components.list.views :as list] [status-im.components.react :as react] - [status-im.components.styles :as styles] [status-im.components.icons.vector-icons :as vi] [status-im.components.toolbar-new.view :as toolbar] [status-im.components.toolbar-new.actions :as act] @@ -15,7 +14,9 @@ [status-im.utils.config :as config] [status-im.utils.utils :as utils] [status-im.utils.platform :as platform] - [status-im.ui.screens.wallet.main.styles :as wallet.styles] + [status-im.ui.screens.wallet.main.styles :as styles] + [status-im.ui.screens.wallet.styles :as wallet.styles] + [status-im.components.styles :as components.styles] [status-im.ui.screens.wallet.views :as wallet.views])) (defn- show-not-implemented! [] @@ -23,18 +24,18 @@ (defn toolbar-title [] [react/touchable-highlight {:on-press #(rf/dispatch [:navigate-to :wallet-list])} - [react/view {:style wallet.styles/toolbar-title-container} - [react/text {:style wallet.styles/toolbar-title-text + [react/view {:style styles/toolbar-title-container} + [react/text {:style styles/toolbar-title-text :font :toolbar-title} (i18n/label :t/main-wallet)] [vi/icon :icons/dropdown - {:container-style wallet.styles/toolbar-title-icon + {:container-style styles/toolbar-title-icon :color :white}]]]) (def transaction-history-action {:icon :icons/transaction-history - :icon-opts (merge {:color :white :style {:viewBox "-108 65.9 24 24"}} wallet.styles/toolbar-icon) + :icon-opts (merge {:color :white :style {:viewBox "-108 65.9 24 24"}} styles/toolbar-icon) :handler #(rf/dispatch [:navigate-to :wallet-transactions])}) (defn toolbar-view [] @@ -49,27 +50,27 @@ (defn- change-display [change] (let [pos-change? (or (pos? change) (zero? change))] [react/view {:style (if pos-change? - wallet.styles/today-variation-container-positive - wallet.styles/today-variation-container-negative)} + styles/today-variation-container-positive + styles/today-variation-container-negative)} [react/text {:style (if pos-change? - wallet.styles/today-variation-positive - wallet.styles/today-variation-negative)} + styles/today-variation-positive + styles/today-variation-negative)} (if change (str (when pos-change? "+") change "%") "-%")]])) (defn main-section [usd-value change error-message] - [react/view {:style wallet.styles/main-section} - (when error-message [wallet.views/error-message-view wallet.styles/error-container wallet.styles/error-message]) - [react/view {:style wallet.styles/total-balance-container} - [react/view {:style wallet.styles/total-balance} - [react/text {:style wallet.styles/total-balance-value} usd-value] - [react/text {:style wallet.styles/total-balance-currency} "USD"]] - [react/view {:style wallet.styles/value-variation} - [react/text {:style wallet.styles/value-variation-title} + [react/view {:style styles/main-section} + (when error-message [wallet.views/error-message-view styles/error-container styles/error-message]) + [react/view {:style styles/total-balance-container} + [react/view {:style styles/total-balance} + [react/text {:style styles/total-balance-value} usd-value] + [react/text {:style styles/total-balance-currency} "USD"]] + [react/view {:style styles/value-variation} + [react/text {:style styles/value-variation-title} (i18n/label :t/wallet-total-value)] [change-display change]] - [btn/buttons {:style wallet.styles/buttons :button-text-style wallet.styles/main-button-text} + [btn/buttons {:style styles/buttons :button-text-style styles/main-button-text} [{:text (i18n/label :t/wallet-send) :on-press #(do (rf/dispatch [:navigate-to :wallet-send-transaction]) (when platform/android? @@ -83,15 +84,15 @@ (defn- token->image [id] (case id - "eth" {:source (:ethereum resources/assets) :style (wallet.styles/asset-border styles/color-gray-transparent-light)})) + "eth" {:source (:ethereum resources/assets) :style (styles/asset-border components.styles/color-gray-transparent-light)})) (defn add-asset [] [list/touchable-item show-not-implemented! [react/view [list/item - [list/item-icon {:icon :icons/add :style wallet.styles/add-asset-icon :icon-opts {:color :blue}}] - [react/view {:style wallet.styles/asset-item-value-container} - [react/text {:style wallet.styles/add-asset-text} + [list/item-icon {:icon :icons/add :style styles/add-asset-icon :icon-opts {:color :blue}}] + [react/view {:style styles/asset-item-value-container} + [react/text {:style styles/add-asset-text} (i18n/label :t/wallet-add-asset)]]]]]) (defn render-asset [{:keys [id currency amount]}] @@ -101,9 +102,9 @@ [react/view [list/item [list/item-image {:uri :launch_logo}] - [react/view {:style wallet.styles/asset-item-value-container} - [react/text {:style wallet.styles/asset-item-value} (str amount)] - [react/text {:style wallet.styles/asset-item-currency + [react/view {:style styles/asset-item-value-container} + [react/text {:style styles/asset-item-value} (str amount)] + [react/text {:style styles/asset-item-currency :uppercase? true} id]] [list/item-icon {:icon :icons/forward}]]]] @@ -112,17 +113,17 @@ [list/item (let [{:keys [source style]} (token->image id)] [list/item-image source style]) - [react/view {:style wallet.styles/asset-item-value-container} - [react/text {:style wallet.styles/asset-item-value} (str amount)] - [react/text {:style wallet.styles/asset-item-currency + [react/view {:style styles/asset-item-value-container} + [react/text {:style styles/asset-item-value} (str amount)] + [react/text {:style styles/asset-item-currency :uppercase? true} id]]]] [add-asset])) (defn asset-section [eth prices-loading? balance-loading?] (let [assets [{:id "eth" :currency :eth :amount eth}]] - [react/view {:style wallet.styles/asset-section} - [react/text {:style wallet.styles/asset-section-title} (i18n/label :t/wallet-assets)] + [react/view {:style styles/asset-section} + [react/text {:style styles/asset-section-title} (i18n/label :t/wallet-assets)] [list/flat-list {:data (conj assets {}) ;; Extra map triggers rendering for add-asset :render-fn render-asset @@ -138,6 +139,6 @@ error-message [:wallet/error-message?]] [react/view {:style wallet.styles/wallet-container} [toolbar-view] - [react/view {:style styles/flex} + [react/view components.styles/flex [main-section portfolio-value portfolio-change error-message] [asset-section eth-balance prices-loading? balance-loading?]]])) diff --git a/src/status_im/ui/screens/wallet/navigation.cljs b/src/status_im/ui/screens/wallet/navigation.cljs index 8388a02b23..26685e2b51 100644 --- a/src/status_im/ui/screens/wallet/navigation.cljs +++ b/src/status_im/ui/screens/wallet/navigation.cljs @@ -15,3 +15,9 @@ (defmethod navigation/preload-data! :wallet-request-transaction [db _] (dissoc db :wallet/request-transaction)) + +(defmethod navigation/preload-data! :wallet-send-transaction + [db [event]] + (if (= event :navigate-back) + db + (dissoc db :wallet/send-transaction))) \ No newline at end of file diff --git a/src/status_im/ui/screens/wallet/request/db.cljs b/src/status_im/ui/screens/wallet/request/db.cljs index 368274149c..18da9d292c 100644 --- a/src/status_im/ui/screens/wallet/request/db.cljs +++ b/src/status_im/ui/screens/wallet/request/db.cljs @@ -3,6 +3,7 @@ (:require [cljs.spec.alpha :as spec])) (spec/def ::amount (spec/nilable string?)) +(spec/def ::amount-error (spec/nilable string?)) (spec/def :wallet/request-transaction (allowed-keys - :opt-un [::amount])) \ No newline at end of file + :opt-un [::amount ::amount-error])) \ No newline at end of file diff --git a/src/status_im/ui/screens/wallet/request/events.cljs b/src/status_im/ui/screens/wallet/request/events.cljs index 10f41f0753..a2414c643c 100644 --- a/src/status_im/ui/screens/wallet/request/events.cljs +++ b/src/status_im/ui/screens/wallet/request/events.cljs @@ -1,7 +1,7 @@ (ns status-im.ui.screens.wallet.request.events (:require - [re-frame.core :as re-frame :refer [dispatch reg-fx]] - [status-im.utils.handlers :as handlers])) + [status-im.utils.handlers :as handlers] + [status-im.ui.screens.wallet.db :as wallet.db])) (handlers/register-handler-fx :wallet-send-request @@ -10,4 +10,11 @@ [:navigate-to-clean :chat-list] [:chat-with-command whisper-identity :request {:contact contact - :amount (:amount request-transaction)}]]})) \ No newline at end of file + :amount (:amount request-transaction)}]]})) + +(handlers/register-handler-fx + :wallet-validate-request-amount + (fn [{{:keys [web3] :wallet/keys [request-transaction] :as db} :db} _] + (let [amount (:amount request-transaction) + error (wallet.db/get-amount-validation-error amount web3)] + {:db (assoc-in db [:wallet/request-transaction :amount-error] error)}))) \ No newline at end of file diff --git a/src/status_im/ui/screens/wallet/request/styles.cljs b/src/status_im/ui/screens/wallet/request/styles.cljs index e3f16e33f1..030f9aaf30 100644 --- a/src/status_im/ui/screens/wallet/request/styles.cljs +++ b/src/status_im/ui/screens/wallet/request/styles.cljs @@ -1,9 +1,6 @@ (ns status-im.ui.screens.wallet.request.styles (:require [status-im.components.styles :as styles])) -(def main-container - {:flex 1}) - (def network-label {:margin-top 27}) @@ -14,44 +11,5 @@ (def qr-container {:margin-top 16}) -(def choose-wallet-container - {:margin-top 27 - :margin-horizontal 15}) - -(def amount-container - {:margin-top 16 - :margin-horizontal 15 - :flex-direction :row}) - -(def choose-currency-container - {:margin-left 8}) - -(def choose-currency - {:width 116}) - -(def separator - {:height 1 - :margin-horizontal 15 - :background-color styles/color-white-transparent-1 - :margin-top 16}) - -(def buttons-container - {:margin-vertical 15 - :padding-horizontal 12 - :flex-direction :row - :align-items :center}) - (def share-icon-container {:margin-right 8}) - -(def forward-icon-container - {:margin-left 8}) - -(def button-text - {:color :white - :font-size 15 - :letter-spacing -0.2}) - -(def button-container - {:flex-direction :row - :align-items :center}) \ No newline at end of file diff --git a/src/status_im/ui/screens/wallet/request/subs.cljs b/src/status_im/ui/screens/wallet/request/subs.cljs new file mode 100644 index 0000000000..996d9dbb95 --- /dev/null +++ b/src/status_im/ui/screens/wallet/request/subs.cljs @@ -0,0 +1,11 @@ +(ns status-im.ui.screens.wallet.request.subs + (:require [re-frame.core :as re-frame])) + +(re-frame/reg-sub + :wallet.request/request-enabled? + :<- [:get-in [:wallet/request-transaction :amount]] + :<- [:get-in [:wallet/request-transaction :amount-error]] + (fn [[amount amount-error]] + (and + (nil? amount-error) + (not (nil? amount)) (not= amount "")))) \ No newline at end of file diff --git a/src/status_im/ui/screens/wallet/request/views.cljs b/src/status_im/ui/screens/wallet/request/views.cljs index 4db557d677..cd22b72dc1 100644 --- a/src/status_im/ui/screens/wallet/request/views.cljs +++ b/src/status_im/ui/screens/wallet/request/views.cljs @@ -7,7 +7,7 @@ [status-im.components.toolbar-new.actions :as actions] [status-im.components.toolbar-new.view :as toolbar] [status-im.components.status-bar :as status-bar] - [status-im.ui.screens.wallet.send.styles :as wallet-styles] + [status-im.ui.screens.wallet.styles :as wallet.styles] [status-im.components.icons.vector-icons :as vi] [status-im.ui.screens.wallet.components.views :as components] [status-im.ui.screens.wallet.request.styles :as styles] @@ -16,7 +16,7 @@ [status-im.utils.platform :as platform])) (defn toolbar-view [] - [toolbar/toolbar2 {:style wallet-styles/toolbar :hide-border? true} + [toolbar/toolbar2 {:style wallet.styles/toolbar :hide-border? true} [toolbar/nav-button (actions/back-white actions/default-handler)] [toolbar/content-title {:color :white} (i18n/label :t/request-transaction)]]) @@ -36,41 +36,40 @@ :fgColor "#4360df" :size 256}])) -(defn button-text [label] - [react/text {:style styles/button-text - :font (if platform/android? :medium :default) - :uppercase? (get-in platform/platform-specific [:uppercase?])} label]) - (views/defview request-transaction [] ;;Because input field is in the end of view we will scroll to the end on input focus event - (views/letsubs [scroll (atom nil)] - [react/keyboard-avoiding-view wallet-styles/wallet-modal-container + (views/letsubs [amount-error [:get-in [:wallet/request-transaction :amount-error]] + request-enabled? [:wallet.request/request-enabled?] + scroll (atom nil)] + [react/keyboard-avoiding-view wallet.styles/wallet-modal-container [status-bar/status-bar {:type :wallet}] [toolbar-view] [react/scroll-view {:ref #(reset! scroll %)} - [react/view styles/main-container + [react/view components.styles/flex [react/view styles/network-container ;;TODO (andrey) name of active network should be used [components/network-label styles/network-label "Testnet"] [react/view styles/qr-container [qr-code]]] - [react/view styles/choose-wallet-container + [react/view wallet.styles/choose-wallet-container [components/choose-wallet]] - [react/view styles/amount-container + [react/view wallet.styles/amount-container [components/amount-input - {:input-options {:on-focus (fn [] (when @scroll (js/setTimeout #(.scrollToEnd @scroll) 100))) + {:error amount-error + :input-options {:on-focus (fn [] (when @scroll (js/setTimeout #(.scrollToEnd @scroll) 100))) :on-change-text - #(re-frame/dispatch [:set-in [:wallet/request-transaction :amount] %])}}] - [react/view styles/choose-currency-container - [components/choose-currency styles/choose-currency]]]]] - [react/view styles/separator] - [react/view styles/buttons-container + #(do (re-frame/dispatch [:set-in [:wallet/request-transaction :amount] %]) + (re-frame/dispatch [:wallet-validate-request-amount]))}}] + [react/view wallet.styles/choose-currency-container + [components/choose-currency wallet.styles/choose-currency]]]]] + [components/separator] + [react/view wallet.styles/buttons-container [react/touchable-highlight {:on-press #()} - [react/view styles/button-container + [react/view (wallet.styles/button-container false) [vi/icon :icons/share {:color :white :container-style styles/share-icon-container}] - [button-text (i18n/label :t/share)]]] + [components/button-text (i18n/label :t/share)]]] [react/view components.styles/flex] - [react/touchable-highlight {:on-press send-request} - [react/view styles/button-container - [button-text (i18n/label :t/send-request)] - [vi/icon :icons/forward {:color :white :container-style styles/forward-icon-container}]]]]])) \ No newline at end of file + [react/touchable-highlight {:on-press (when request-enabled? send-request)} + [react/view (wallet.styles/button-container request-enabled?) + [components/button-text (i18n/label :t/send-request)] + [vi/icon :icons/forward {:color :white :container-style wallet.styles/forward-icon-container}]]]]])) \ No newline at end of file diff --git a/src/status_im/ui/screens/wallet/send/animations.cljs b/src/status_im/ui/screens/wallet/send/animations.cljs new file mode 100644 index 0000000000..2484501583 --- /dev/null +++ b/src/status_im/ui/screens/wallet/send/animations.cljs @@ -0,0 +1,11 @@ +(ns status-im.ui.screens.wallet.send.animations + (:require [status-im.components.animation :as animation])) + +(defn animate-sign-panel [opacity-value bottom-value] + (animation/start + (animation/parallel + [(animation/timing opacity-value {:toValue 1 + :duration 500}) + (animation/timing bottom-value {:toValue 53 + :easing (.bezier animation/easing 0.685, 0.000, 0.025, 1.185) + :duration 500})]))) \ No newline at end of file diff --git a/src/status_im/ui/screens/wallet/send/db.cljs b/src/status_im/ui/screens/wallet/send/db.cljs new file mode 100644 index 0000000000..5b559ff2d4 --- /dev/null +++ b/src/status_im/ui/screens/wallet/send/db.cljs @@ -0,0 +1,17 @@ +(ns status-im.ui.screens.wallet.send.db + (:require-macros [status-im.utils.db :refer [allowed-keys]]) + (:require [cljs.spec.alpha :as spec])) + +(spec/def ::amount (spec/nilable string?)) +(spec/def ::to-address (spec/nilable string?)) +(spec/def ::to-name (spec/nilable string?)) +(spec/def ::amount-error (spec/nilable string?)) +(spec/def ::password (spec/nilable string?)) +(spec/def ::transaction-id (spec/nilable string?)) +(spec/def ::waiting-signal? (spec/nilable boolean?)) +(spec/def ::signing? (spec/nilable boolean?)) +(spec/def ::later? (spec/nilable boolean?)) + +(spec/def :wallet/send-transaction (allowed-keys + :opt-un [::amount ::to-address ::to-name ::amount-error ::password + ::waiting-signal? ::signing? ::transaction-id ::later?])) \ No newline at end of file diff --git a/src/status_im/ui/screens/wallet/send/events.cljs b/src/status_im/ui/screens/wallet/send/events.cljs index 085d32f666..582412bfa2 100644 --- a/src/status_im/ui/screens/wallet/send/events.cljs +++ b/src/status_im/ui/screens/wallet/send/events.cljs @@ -1,8 +1,123 @@ (ns status-im.ui.screens.wallet.send.events (:require [re-frame.core :as re-frame] - [status-im.ui.screens.wallet.db :as wallet.db])) + [status-im.utils.handlers :as handlers] + [status-im.ui.screens.wallet.db :as wallet.db] + [status-im.native-module.core :as status] + [status-im.utils.types :as types] + [clojure.string :as string] + [status-im.utils.money :as money] + [status-im.utils.utils :as utils] + [status-im.i18n :as i18n])) -(re-frame/reg-event-db +;;;; FX + +(re-frame/reg-fx + ::accept-transaction + (fn [{:keys [password id on-completed]}] + (status/complete-transactions (list id) password on-completed))) + +(re-frame/reg-fx + ::send-transaction + (fn [{:keys [web3] :as params}] + (when web3 + (.sendTransaction + (.-eth web3) + (clj->js (select-keys params [:from :to :value])) + #())))) + +(re-frame/reg-fx + ::show-transaction-moved + (fn [] + (utils/show-popup (i18n/label :t/transaction-moved-title) (i18n/label :t/transaction-moved-text)))) + +(re-frame/reg-fx + ::discard-transaction + (fn [id] + (status/discard-transaction id))) + +;;;; Handlers + +(handlers/register-handler-db :choose-recipient (fn [db [_ recipient]] (assoc-in db [:wallet :send :recipient] recipient))) + +(handlers/register-handler-fx + :wallet-open-send-transaction + (fn [{db :db} [_ address name]] + {:db (update db :wallet/send-transaction + #(assoc % :to-address address + :to-name name)) + :dispatch-n [[:navigate-back] + [:navigate-back]]})) + +(handlers/register-handler-fx + :wallet-validate-amount + (fn [{{:keys [web3] :wallet/keys [send-transaction] :as db} :db} _] + (let [amount (:amount send-transaction) + error (wallet.db/get-amount-validation-error amount web3)] + {:db (assoc-in db [:wallet/send-transaction :amount-error] error)}))) + +(handlers/register-handler-fx + ::transaction-completed + (fn [{db :db} [_ {:keys [id response]}]] + (let [{:keys [hash error]} response] + (when-not (and error (string? error) (not (string/blank? error))) + {:db (-> db + (assoc-in [:wallet/send-transaction :transaction-id] nil) + (assoc :wrong-password? false)) + :dispatch [:navigate-to :wallet-transaction-sent]})))) + +(defn on-transactions-completed [raw-results] + (let [results (:results (types/json->clj raw-results))] + (doseq [result results] + ;;TODO (andrey) legacy, should be removed with old transactions screens + (re-frame/dispatch [:transaction-completed {:id (name (key result)) :response (second result)}]) + (re-frame/dispatch [::transaction-completed {:id (name (key result)) :response (second result)}])))) + +(handlers/register-handler-fx + :wallet/transaction-queued + (fn [{{:wallet/keys [send-transaction] :as db} :db} [_ transaction-id]] + (let [{:keys [later? password]} send-transaction] + (if later? + {:db (-> db + (assoc-in [:wallet/send-transaction :waiting-signal?] false) + (assoc :transactions (:transactions-queue db))) + :dispatch [:navigate-back] + ::show-transaction-moved nil} + {:db (assoc-in db [:wallet/send-transaction :transaction-id] transaction-id) + ::accept-transaction {:id transaction-id + :password password + :on-completed on-transactions-completed}})))) + +(handlers/register-handler-fx + :wallet/sign-transaction + (fn [{{:keys [web3] + :wallet/keys [send-transaction] + :accounts/keys [accounts current-account-id] :as db} :db} [_ later?]] + (let [{:keys [amount transaction-id password]} send-transaction + amount' (money/to-wei (string/replace amount #"," "."))] + (if transaction-id + {::accept-transaction {:id transaction-id + :password password + :on-completed on-transactions-completed}} + {:db (update-in db [:wallet/send-transaction] + #(assoc % :waiting-signal? true + :later? later?)) + ::send-transaction {:web3 web3 + :from (get-in accounts [current-account-id :address]) + :to (:to-address send-transaction) + :value amount'}})))) + +(handlers/register-handler-fx + :wallet/discard-transaction + (fn [{{:wallet/keys [send-transaction] :as db} :db} _] + (let [{:keys [transaction-id]} send-transaction] + (merge {:db (-> db + (update-in [:wallet/send-transaction] + #(assoc % :signing? false :transaction-id nil)) + (assoc :wrong-password? false))} + (when transaction-id + ;;TODO (andrey) use ::discard-transaction fx instead + {:dispatch-n [[:deny-transaction transaction-id] + [:status-im.transactions.handlers/remove-transaction transaction-id]]}))))) \ No newline at end of file diff --git a/src/status_im/ui/screens/wallet/send/styles.cljs b/src/status_im/ui/screens/wallet/send/styles.cljs index 2bb7920d22..71f9e172a0 100644 --- a/src/status_im/ui/screens/wallet/send/styles.cljs +++ b/src/status_im/ui/screens/wallet/send/styles.cljs @@ -1,18 +1,9 @@ (ns status-im.ui.screens.wallet.send.styles (:require-macros [status-im.utils.styles :refer [defnstyle defstyle]]) - (:require [status-im.components.styles :as styles] - [status-im.utils.platform :as platform])) - -(def wallet-container - {:flex 1 - :background-color styles/color-blue4}) - -(def wallet-modal-container - {:flex 1 - :background-color styles/color-blue3}) + (:require [status-im.components.styles :as styles])) (def toolbar - {:background-color styles/color-blue4 + {:background-color styles/color-blue5 :elevation 0 :padding-bottom 10}) @@ -33,119 +24,40 @@ (def toolbar-title-icon (merge toolbar-icon {:opacity 0.4})) -(def toolbar-buttons-container - {:flex-direction :row - :flex-shrink 1 - :justify-content :space-between - :width 68 - :margin-right 12}) +(defn animated-sign-panel [bottom-value] + {:position :absolute + :left 12 + :right 12 + :bottom bottom-value}) -(def choose-recipient-container - {:flex-direction :row - :padding-top 20 - :padding-bottom 20 - :justify-content :center}) +(defn sign-panel [opacity-value] + {:opacity opacity-value + :border-radius 8 + :background-color :white + :padding-top 12 + :padding-horizontal 12}) -(def choose-recipient-label - {:color :white}) +(def signing-phrase-container + {:border-radius 8 + :height 36 + :align-items :center + :justify-content :center + :background-color styles/color-light-gray}) -(defstyle recipient-buttons - {:flex-direction :column - :margin-horizontal 28 - :margin-vertical 20 - :border-radius 8 - :ios {:background-color styles/color-blue6}}) +(def signing-phrase + {:font-size 15 + :letter-spacing -0.2 + :color :black}) -(def recipient-icon {:margin-right 20}) +(def signing-phrase-description + {:padding-top 8}) -(def recipient-icon-disabled {:margin-right 20 - :opacity 0.3}) +(def password-container + {:flex 1 + :padding-vertical 20}) -(def recipient-button - {:flex-direction :row - :justify-content :space-between - :margin-vertical 10 - :margin-left 20}) - -(def recipient-button-text - {:color :white - :align-self :center - :font-size 14}) - -(def recipient-button-text-disabled - (merge recipient-button-text {:color "rgba(255, 255, 255, 0.3)"})) - -(defnstyle recipient-touchable [divider?] - (cond-> {:border-color styles/color-gray-transparent-light} - divider? (assoc :ios {:border-bottom-width 1}))) - -(def recipient-touchable-disabled - {:background-color styles/color-blue4 - :border-bottom-left-radius 8 - :border-bottom-right-radius 8 - :border-left-width 1 - :border-bottom-width 1 - :border-right-width 1 - :border-color "rgba(255, 255, 255, 0.3)"}) - -(def qr-container - {:flex 1}) - -(def preview - {:flex 1 - :justify-content :flex-end - :align-items :center}) - -(def corner-dimensions - {:position :absolute - :width 40 - :height 40}) - -(defn corner-left-bottom [dimension] - (let [viewport-offset 0.1666] - (merge corner-dimensions {:bottom (* viewport-offset dimension) - :left (* viewport-offset dimension)}))) - -(defn corner-right-bottom [dimension] - (let [viewport-offset 0.1666] - (merge corner-dimensions {:right (* viewport-offset dimension) - :bottom (* viewport-offset dimension)}))) - -(defn corner-left-top [dimension] - (let [viewport-offset 0.1666] - (merge corner-dimensions {:top (* viewport-offset dimension) - :left (* viewport-offset dimension)}))) - -(defn corner-right-top [dimension] - (let [viewport-offset 0.1666] - (merge corner-dimensions {:top (* viewport-offset dimension) - :right (* viewport-offset dimension)}))) - -(def viewfinder-port {:position :absolute - :left 0 - :top 0 - :bottom 0 - :right 0 - :flex 1}) - -(defn viewfinder-translucent [height width side] - (let [viewport-offset 0.1666 - height-offset (* viewport-offset height) - width-offset (* viewport-offset width)] - (cond-> {:position :absolute - :background-color :black - :opacity 0.7} - (= :top side) (assoc :height height-offset - :width width) - (= :right side) (assoc :height (- height height-offset) - :width width-offset - :bottom 0 - :right 0) - (= :bottom side) (assoc :height height-offset - :width (- width width-offset) - :bottom 0 - :left 0) - (= :left side) (assoc :height (- height (* 2 height-offset)) - :width width-offset - :top height-offset - :left 0)))) +(def password + {:padding 0 + :font-size 15 + :letter-spacing -0.2 + :height 20}) \ No newline at end of file diff --git a/src/status_im/ui/screens/wallet/send/subs.cljs b/src/status_im/ui/screens/wallet/send/subs.cljs index bef5f27b80..0879f20b4d 100644 --- a/src/status_im/ui/screens/wallet/send/subs.cljs +++ b/src/status_im/ui/screens/wallet/send/subs.cljs @@ -4,3 +4,20 @@ (re-frame/reg-sub :camera-dimensions (fn [db] (get-in db [:wallet :camera-dimensions]))) + +(re-frame/reg-sub + :wallet.send/sign-enabled? + :<- [:get-in [:wallet/send-transaction :amount]] + :<- [:get-in [:wallet/send-transaction :to-address]] + :<- [:get-in [:wallet/send-transaction :amount-error]] + (fn [[amount to-address amount-error]] + (and + (nil? amount-error) + (not (nil? to-address)) (not= to-address "") + (not (nil? amount)) (not= amount "")))) + +(re-frame/reg-sub + :wallet.send/sign-password-enabled? + :<- [:get-in [:wallet/send-transaction :password]] + (fn [password] + (and (not (nil? password)) (not= password "")))) \ No newline at end of file diff --git a/src/status_im/ui/screens/wallet/send/transaction_sent/styles.cljs b/src/status_im/ui/screens/wallet/send/transaction_sent/styles.cljs new file mode 100644 index 0000000000..e5c3e41fd4 --- /dev/null +++ b/src/status_im/ui/screens/wallet/send/transaction_sent/styles.cljs @@ -0,0 +1,50 @@ +(ns status-im.ui.screens.wallet.send.transaction-sent.styles + (:require-macros [status-im.utils.styles :refer [defnstyle defstyle]])) + +(def transaction-sent-container + {:align-items :center}) + +(def ok-icon-container + {:width 56 + :height 56 + :border-radius 28 + :background-color :white + :align-items :center + :justify-content :center + :margin-top 57 + :margin-bottom 16}) + +(def transaction-sent + {:color :white + :font-size 17}) + +(def gap + {:height 8}) + +(def transaction-sent-description + {:color :white + :opacity 0.6 + :font-size 14 + :text-align :center + :padding-horizontal 16}) + +(def transaction-details-container + {:height 42 + :background-color "#00000033" + :margin-horizontal 16 + :opacity 0.2 + :align-items :center + :justify-content :center + :border-radius 8}) + +(def transaction-details + {:color :white + :font-size 15}) + +(def got-it-container + {:align-items :center + :padding-vertical 18}) + +(def got-it + {:color :white + :font-size 15}) \ No newline at end of file diff --git a/src/status_im/ui/screens/wallet/send/transaction_sent/views.cljs b/src/status_im/ui/screens/wallet/send/transaction_sent/views.cljs new file mode 100644 index 0000000000..5f22df5482 --- /dev/null +++ b/src/status_im/ui/screens/wallet/send/transaction_sent/views.cljs @@ -0,0 +1,38 @@ +(ns status-im.ui.screens.wallet.send.transaction-sent.views + (:require-macros [status-im.utils.views :refer [defview letsubs]]) + (:require [status-im.components.react :as react] + [status-im.components.status-bar :as status-bar] + [status-im.components.icons.vector-icons :as vi] + [status-im.ui.screens.wallet.styles :as wallet.styles] + [status-im.ui.screens.wallet.send.transaction-sent.styles :as styles] + [status-im.components.styles :as components.styles] + [re-frame.core :as re-frame] + [status-im.ui.screens.wallet.components.views :as components] + [status-im.i18n :as i18n] + [status-im.utils.platform :as platform])) + +(defview transaction-sent [] + [react/view wallet.styles/wallet-modal-container + [status-bar/status-bar {:type :transparent}] + [react/view styles/transaction-sent-container + [react/view styles/ok-icon-container + [vi/icon :icons/ok {:color components.styles/color-blue4}]] + [react/text {:style styles/transaction-sent + :font (if platform/android? :medium :default)} + (i18n/label :t/transaction-sent)] + [react/view styles/gap] + [react/text {:style styles/transaction-sent-description} (i18n/label :t/transaction-description)]] + [react/view components.styles/flex] + [react/touchable-highlight {:on-press #()}; TODO (andrey) #(re-frame/dispatch [:navigate-to-clean :wallet-transaction-details])} + [react/view styles/transaction-details-container + [react/text {:style styles/transaction-details + :font (if platform/android? :medium :default) + :uppercase? (get-in platform/platform-specific [:uppercase?])} + (i18n/label :t/view-transaction-details)]]] + [components/separator] + [react/touchable-highlight {:on-press #(re-frame/dispatch [:navigate-to-clean :wallet])} + [react/view styles/got-it-container + [react/text {:style styles/got-it + :font (if platform/android? :medium :default) + :uppercase? (get-in platform/platform-specific [:uppercase?])} + (i18n/label :t/got-it)]]]]) \ No newline at end of file diff --git a/src/status_im/ui/screens/wallet/send/views.cljs b/src/status_im/ui/screens/wallet/send/views.cljs index 7571852ce0..9c2dbf90e8 100644 --- a/src/status_im/ui/screens/wallet/send/views.cljs +++ b/src/status_im/ui/screens/wallet/send/views.cljs @@ -4,7 +4,6 @@ [status-im.components.react :as react] [re-frame.core :as re-frame] [status-im.components.button.view :as button] - [status-im.components.styles :as styles] [status-im.components.status-bar :as status-bar] [status-im.components.toolbar-new.actions :as act] [status-im.components.toolbar-new.view :as toolbar] @@ -13,77 +12,113 @@ [status-im.i18n :as i18n] [status-im.ui.screens.wallet.send.styles :as send.styles] [status-im.components.icons.vector-icons :as vector-icons] - [reagent.core :as r])) + [reagent.core :as r] + [status-im.utils.platform :as platform] + [status-im.ui.screens.wallet.request.styles :as request.styles] + [status-im.ui.screens.wallet.send.styles :as styles] + [status-im.ui.screens.wallet.components.views :as components] + [status-im.components.styles :as components.styles] + [status-im.components.icons.vector-icons :as vi] + [status-im.components.animation :as animation] + [status-im.ui.screens.wallet.styles :as wallet.styles] + [status-im.ui.screens.wallet.send.animations :as send.animations])) -(defn- show-not-implemented! [] - (utils/show-popup "TODO" "Not implemented yet!")) +(defn toolbar-view [signing?] + [toolbar/toolbar2 {:style wallet.styles/toolbar} + [toolbar/nav-button (act/back-white (if signing? + #(do (re-frame/dispatch [:wallet/discard-transaction]) + (act/default-handler)) + act/default-handler))] + [toolbar/content-title {:color :white} (i18n/label :t/send-transaction)]]) -(defn toolbar-view [] - [toolbar/toolbar2 {:style send.styles/toolbar - :no-sync-bar? true} - [toolbar/nav-button (act/close-white act/default-handler)] - [toolbar/content-title {:color :white} (i18n/label :t/wallet-choose-recipient)] - [toolbar/actions [{:icon :icons/flash-active - :icon-opts {:color :white} - :handler show-not-implemented!}]]]) +(defn sign-later [] + (utils/show-question + (i18n/label :t/sign-later-title) + (i18n/label :t/sign-later-text) + #(re-frame/dispatch [:wallet/sign-transaction true]))) -(defn recipient-buttons [] - [react/view {:style send.styles/recipient-buttons} - [react/touchable-highlight {:style (send.styles/recipient-touchable true)} - [react/view {:style send.styles/recipient-button} - [react/text {:style send.styles/recipient-button-text} - (i18n/label :t/wallet-choose-from-contacts)] - [vector-icons/icon :icons/qr {:color :white - :container-style send.styles/recipient-icon}]]] - [react/touchable-highlight {:style (send.styles/recipient-touchable true) - :on-press #(react/get-from-clipboard - (fn [clipboard] - (re-frame/dispatch [:choose-recipient clipboard])))} - [react/view {:style send.styles/recipient-button} - [react/text {:style send.styles/recipient-button-text} - (i18n/label :t/wallet-address-from-clipboard)] - [vector-icons/icon :icons/copy-from {:color :white - :container-style send.styles/recipient-icon}]]] - [react/touchable-highlight {:style send.styles/recipient-touchable-disabled} - [react/view {:style send.styles/recipient-button} - [react/text {:style send.styles/recipient-button-text-disabled} - (i18n/label :t/wallet-browse-photos)] - [vector-icons/icon :icons/browse {:color :white - :container-style send.styles/recipient-icon-disabled}]]]]) +(defview sign-panel [] + (letsubs [account [:get-current-account] + ;;TODO (andrey) use send-transaction map after we remove old transactions ui + wrong-password? [:get :wrong-password?];[:get-in [:wallet/send-transaction :wrong-password?]] + signing-phrase (:signing-phrase @account) + bottom-value (animation/create-value -250) + opacity-value (animation/create-value 0)] + {:component-did-mount #(send.animations/animate-sign-panel opacity-value bottom-value)} + [react/animated-view {:style (styles/animated-sign-panel bottom-value)} + [react/animated-view {:style (styles/sign-panel opacity-value)} + [react/view styles/signing-phrase-container + [react/text {:style styles/signing-phrase} signing-phrase]] + [react/text {:style styles/signing-phrase-description} (i18n/label :t/signing-phrase-description)] + [react/view styles/password-container + [react/text-input + {:auto-focus true + :secure-text-entry true + :placeholder (i18n/label :t/enter-password) + :placeholder-text-color "#939ba1" + :on-change-text #(do + (re-frame/dispatch [:set-in [:wallet/send-transaction :password] %])) + :style styles/password}]]] + (when wrong-password? + [components/tooltip (i18n/label :t/wrong-password)])])) -(defn viewfinder [{:keys [height width]}] - (let [min-dimension (min height width)] - [react/view {:style send.styles/viewfinder-port} - [react/view {:style (send.styles/viewfinder-translucent height width :top)}] - [react/view {:style (send.styles/viewfinder-translucent height width :right)}] - [react/view {:style (send.styles/viewfinder-translucent height width :bottom)}] - [react/view {:style (send.styles/viewfinder-translucent height width :left)}] - [react/image {:source {:uri :corner_left_top} - :style (send.styles/corner-left-top min-dimension)}] - [react/image {:source {:uri :corner_right_top} - :style (send.styles/corner-right-top min-dimension)}] - [react/image {:source {:uri :corner_left_bottom} - :style (send.styles/corner-left-bottom min-dimension)}] - [react/image {:source {:uri :corner_right_bottom} - :style (send.styles/corner-right-bottom min-dimension)}]])) + +(defview signing-buttons [] + (letsubs [sign-enabled? [:wallet.send/sign-password-enabled?]] + [react/view wallet.styles/buttons-container + [react/touchable-highlight {:on-press #(re-frame/dispatch [:wallet/discard-transaction])} + [react/view (wallet.styles/button-container true) + [components/button-text (i18n/label :t/cancel)]]] + [react/view components.styles/flex] + [react/touchable-highlight {:on-press #(re-frame/dispatch [:wallet/sign-transaction])} + [react/view (wallet.styles/button-container sign-enabled?) + [components/button-text (i18n/label :t/transactions-sign-transaction)] + [vi/icon :icons/forward {:color :white :container-style wallet.styles/forward-icon-container}]]]])) + +(defview sign-buttons [] + (letsubs [sign-enabled? [:wallet.send/sign-enabled?]] + [react/view wallet.styles/buttons-container + (when sign-enabled? + [react/touchable-highlight {:on-press sign-later} + [react/view (wallet.styles/button-container sign-enabled?) + [components/button-text (i18n/label :t/transactions-sign-later)]]]) + [react/view components.styles/flex] + [react/touchable-highlight {:on-press (when sign-enabled? #(re-frame/dispatch [:set-in [:wallet/send-transaction :signing?] true]))} + [react/view (wallet.styles/button-container sign-enabled?) + [components/button-text (i18n/label :t/transactions-sign-transaction)] + [vi/icon :icons/forward {:color :white :container-style wallet.styles/forward-icon-container}]]]])) (defview send-transaction [] - (letsubs [camera-dimensions [:camera-dimensions]] - [react/view {:style send.styles/wallet-container} - [status-bar/status-bar {:type :wallet}] - [toolbar-view] - [react/view {:style send.styles/qr-container - :on-layout #(let [layout (.. % -nativeEvent -layout)] - (re-frame/dispatch [:set-in [:wallet :camera-dimensions] - {:width (.-width layout) - :height (.-height layout)}]))} - [camera/camera {:style send.styles/preview - :aspect :fill - :captureAudio false - :onBarCodeRead (fn [code] - (let [data (-> code - .-data - (str/replace #"ethereum:" ""))] - (re-frame/dispatch [:choose-recipient data])))}] - [viewfinder camera-dimensions]] - [recipient-buttons]])) + (letsubs [amount [:get-in [:wallet/send-transaction :amount]] + amount-error [:get-in [:wallet/send-transaction :amount-error]] + signing? [:get-in [:wallet/send-transaction :signing?]] + to-address [:get-in [:wallet/send-transaction :to-address]] + to-name [:get-in [:wallet/send-transaction :to-name]]] + [react/keyboard-avoiding-view wallet.styles/wallet-modal-container + [react/view components.styles/flex + [status-bar/status-bar {:type :wallet}] + [toolbar-view signing?] + [react/scroll-view {:keyboardShouldPersistTaps :always} + [react/view components.styles/flex + [react/view wallet.styles/choose-participant-container + [components/choose-recipient {:address to-address + :name to-name + :on-press #(re-frame/dispatch [:navigate-to :choose-recipient])}]] + [react/view wallet.styles/choose-wallet-container + [components/choose-wallet]] + [react/view wallet.styles/amount-container + [components/amount-input + {:error amount-error + :input-options {:auto-focus true + :default-value amount + :on-change-text #(do + (re-frame/dispatch [:set-in [:wallet/send-transaction :amount] %]) + (re-frame/dispatch [:wallet-validate-amount]))}}] + [react/view wallet.styles/choose-currency-container + [components/choose-currency wallet.styles/choose-currency]]]]] + [components/separator] + (if signing? + [signing-buttons] + [sign-buttons]) + (when signing? + [sign-panel])]])) \ No newline at end of file diff --git a/src/status_im/ui/screens/wallet/styles.cljs b/src/status_im/ui/screens/wallet/styles.cljs index 55e2496d53..72d88eec7d 100644 --- a/src/status_im/ui/screens/wallet/styles.cljs +++ b/src/status_im/ui/screens/wallet/styles.cljs @@ -1,5 +1,15 @@ (ns status-im.ui.screens.wallet.styles - (:require [status-im.components.styles :as st])) + (:require-macros [status-im.utils.styles :refer [defstyle]]) + (:require [status-im.components.styles :as st] + [status-im.components.styles :as styles])) + +(def wallet-container + {:flex 1}) + +(defstyle toolbar + {:ios {:background-color styles/color-blue4} + :android {:background-color styles/color-blue5 + :elevation 0}}) (def wallet-exclamation-container {:background-color st/color-red-2 @@ -13,3 +23,42 @@ (def wallet-error-exclamation {:width 16 :height 16}) + +(def buttons-container + {:margin-vertical 15 + :padding-horizontal 12 + :flex-direction :row + :align-items :center}) + +(def forward-icon-container + {:margin-left 8}) + +(defn button-container [enabled?] + (merge + {:flex-direction :row + :align-items :center} + (when-not enabled? + {:opacity 0.4}))) + +(def wallet-modal-container + {:flex 1 + :background-color styles/color-blue4}) + +(def choose-participant-container + {:margin-top 16 + :margin-horizontal 15}) + +(def choose-wallet-container + {:margin-top 16 + :margin-horizontal 15}) + +(def amount-container + {:margin-top 16 + :margin-horizontal 15 + :flex-direction :row}) + +(def choose-currency-container + {:margin-left 8}) + +(def choose-currency + {:width 116}) \ No newline at end of file diff --git a/src/status_im/utils/money.cljs b/src/status_im/utils/money.cljs index 901bd42b43..280c2a3adb 100644 --- a/src/status_im/utils/money.cljs +++ b/src/status_im/utils/money.cljs @@ -23,6 +23,9 @@ (defn bignumber [n] (dependencies/Web3.prototype.toBigNumber (str n))) +(defn to-wei [str] + (dependencies/Web3.prototype.toWei str "ether")) + (def eth-units {:wei (bignumber "1") :kwei (bignumber "1000") diff --git a/src/status_im/utils/utils.cljs b/src/status_im/utils/utils.cljs index 7cb2015ba6..3733c93aed 100644 --- a/src/status_im/utils/utils.cljs +++ b/src/status_im/utils/utils.cljs @@ -24,6 +24,18 @@ (when on-cancel {:onPress on-cancel})) {:text (or s "OK") :onPress on-accept :style "destructive"}))))) +(defn show-question + ([title content on-accept] + (show-question title content on-accept nil)) + ([title content on-accept on-cancel] + (.alert (.-Alert rn-dependencies/react-native) + title + content + (clj->js + (vector (merge {:text (i18n/label :t/no)} + (when on-cancel {:onPress on-cancel})) + {:text (i18n/label :t/yes) :onPress on-accept}))))) + (defn http-post ([action data on-success] (http-post action data on-success nil))