send transaction layer 1

This commit is contained in:
Andrey Shovkoplyas 2017-09-27 13:40:50 +03:00 committed by Roman Volosovskyi
parent 62affda881
commit 9246d7df21
35 changed files with 1056 additions and 402 deletions

View File

@ -0,0 +1,3 @@
<svg width="16px" height="8px" viewBox="0 0 16 8">
<path fill="none" d="M0,0 L15.969209,0.0223819541 L9.39011025,6.60148066 C8.60906167,7.38252924 7.34273171,7.38252924 6.56168312,6.60148066 L6.55741249,6.59719708 L0,0 Z"/>
</svg>

After

Width:  |  Height:  |  Size: 234 B

View File

@ -55,3 +55,5 @@
(defn create-value-xy [x y] (defn create-value-xy [x y]
(js/ReactNative.Animated.ValueXY. (clj->js {:x x, :y y}))) (js/ReactNative.Animated.ValueXY. (clj->js {:x x, :y y})))
(def easing js/ReactNative.Easing)

View File

@ -67,6 +67,7 @@
:icons/dropdown (slurp-svg "./resources/icons/dropdown.svg") :icons/dropdown (slurp-svg "./resources/icons/dropdown.svg")
:icons/grab (slurp-svg "./resources/icons/grab.svg") :icons/grab (slurp-svg "./resources/icons/grab.svg")
:icons/share (slurp-svg "./resources/icons/share.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")}) :icons/network (slurp-svg "./resources/icons/network.svg")})
(defn normalize-property-name [n] (defn normalize-property-name [n]

View File

@ -106,3 +106,5 @@
(def toolbar-text-action-disabled {:color styles/color-gray7}) (def toolbar-text-action-disabled {:color styles/color-gray7})
(def item-text-white-background {:color styles/color-blue4}) (def item-text-white-background {:color styles/color-blue4})
(def ios-content-item {:position :absolute :right 90 :left 90})

View File

@ -7,7 +7,8 @@
[status-im.components.context-menu :as context-menu] [status-im.components.context-menu :as context-menu]
[status-im.components.toolbar-new.actions :as act] [status-im.components.toolbar-new.actions :as act]
[status-im.components.toolbar-new.styles :as tst] [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 ;; Navigation item
@ -103,10 +104,15 @@
action-items] action-items]
[rn/view {:style (merge (tst/toolbar-wrapper background-color flat?) style)} [rn/view {:style (merge (tst/toolbar-wrapper background-color flat?) style)}
[rn/view {:style tst/toolbar} [rn/view {:style tst/toolbar}
(when platform/ios?
[rn/view tst/ios-content-item
content-item])
(when nav-item (when nav-item
[rn/view {:style (tst/toolbar-nav-actions-container 0)} [rn/view {:style (tst/toolbar-nav-actions-container 0)}
nav-item]) nav-item])
content-item (if platform/ios?
[rn/view st/flex]
content-item)
action-items] action-items]
(when-not no-sync-bar? [sync-state-gradient-view/sync-state-gradient-view])])) (when-not no-sync-bar? [sync-state-gradient-view/sync-state-gradient-view])]))

View File

@ -127,8 +127,7 @@
(status/discard-transaction id))))) (status/discard-transaction id)))))
(register-handler ::transaction-queued (register-handler ::transaction-queued
(after #(dispatch [:navigate-to-modal :unsigned-transactions])) (fn [{:wallet/keys [send-transaction] :as db} [_ {:keys [id message_id args] :as transaction}]]
(fn [db [_ {:keys [id message_id args] :as transaction}]]
(let [{:keys [from to value data gas gasPrice]} args] (let [{:keys [from to value data gas gasPrice]} args]
(if (transaction-valid? transaction) (if (transaction-valid? transaction)
(let [transaction {:id id (let [transaction {:id id
@ -140,6 +139,9 @@
:gas-price (.toDecimal js/Web3.prototype gasPrice) :gas-price (.toDecimal js/Web3.prototype gasPrice)
:timestamp (time/now-ms) :timestamp (time/now-ms)
:message-id message_id}] :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)) (assoc-in db [:transactions-queue id] transaction))
db)))) db))))
@ -147,7 +149,7 @@
(u/side-effect! (u/side-effect!
(fn [{:keys [transactions modal]} [_ {:keys [id response]}]] (fn [{:keys [transactions modal]} [_ {:keys [id response]}]]
(let [{:keys [hash error]} response (let [{:keys [hash error]} response
{:keys [message-id]} (transactions id)] {:keys [message-id]} (get transactions id)]
(log/debug :parsed-response response) (log/debug :parsed-response response)
(when-not (and error (string? error) (not (s/blank? error))) (when-not (and error (string? error) (not (s/blank? error)))
(if (and message-id (not (s/blank? message-id))) (if (and message-id (not (s/blank? message-id)))

View File

@ -16,6 +16,8 @@
:error "Error" :error "Error"
:unknown-status-go-error "Unknown status-go error" :unknown-status-go-error "Unknown status-go error"
:node-unavailable "No ethereum node running" :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." :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." :photos-access-error "To grant the required photos permission, please, go to your system settings and make sure that Status > Photos is selected."
@ -275,6 +277,7 @@
:sign-in-to-status "Sign in to Status" :sign-in-to-status "Sign in to Status"
:sign-in "Sign in" :sign-in "Sign in"
:wrong-password "Wrong password" :wrong-password "Wrong password"
:enter-password "Enter password"
;;recover ;;recover
:recover-from-passphrase "Recover from passphrase" :recover-from-passphrase "Recover from passphrase"
@ -356,6 +359,7 @@
:wallet-add-asset "Add asset" :wallet-add-asset "Add asset"
:wallet-total-value "Total value" :wallet-total-value "Total value"
:wallet-settings "Wallet Settings" :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" :request-transaction "Request Transaction"
:send-request "Send request" :send-request "Send request"
:share "Share" :share "Share"
@ -364,6 +368,8 @@
:transaction-details "Transaction details" :transaction-details "Transaction details"
:transactions-sign "Sign" :transactions-sign "Sign"
:transactions-sign-all "Sign all" :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-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-sign-input-placeholder "Enter your password"
:transactions-history "History" :transactions-history "History"
@ -374,15 +380,26 @@
:transactions-filter-tokens "Tokens" :transactions-filter-tokens "Tokens"
:transactions-filter-type "Type" :transactions-filter-type "Type"
:transactions-filter-select-all "Select all" :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" :not-applicable "Not applicable for unsigned transactions"
;; Wallet Send ;; Wallet Send
:wallet-send-transaction "Send a Transaction" :wallet-send-transaction "Send Transaction"
:wallet-send-step-one "Step 1 of 3" :wallet-send-step "Step {{step}} of {{number}}"
:wallet-choose-recipient "Choose Recipient" :wallet-choose-recipient "Choose Recipient"
:wallet-choose-from-contacts "Choose From Contacts" :wallet-choose-from-contacts "Choose From Contacts"
:wallet-address-from-clipboard "Use Address From Clipboard" :wallet-address-from-clipboard "Use Address From Clipboard"
:wallet-browse-photos "Browse Photos" :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 ;network settings
:new-network "New network" :new-network "New network"

View File

@ -112,6 +112,7 @@
:my-profile/profile :my-profile/profile
:my-profile/default-name :my-profile/default-name
:wallet/request-transaction :wallet/request-transaction
:wallet/send-transaction
:networks/selected-network :networks/selected-network
:networks/networks :networks/networks
:node/after-start :node/after-start

View File

@ -11,6 +11,7 @@
status-im.ui.screens.wallet.subs status-im.ui.screens.wallet.subs
status-im.ui.screens.wallet.transactions.subs status-im.ui.screens.wallet.transactions.subs
status-im.ui.screens.wallet.send.subs status-im.ui.screens.wallet.send.subs
status-im.ui.screens.wallet.request.subs
status-im.ui.screens.network-settings.subs status-im.ui.screens.network-settings.subs
status-im.transactions.subs status-im.transactions.subs
status-im.bots.subs)) status-im.bots.subs))

View File

@ -43,9 +43,12 @@
[status-im.ui.screens.profile.qr-code.views :refer [qr-code-view]] [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.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.request.views :refer [request-transaction]]
[status-im.ui.screens.wallet.wallet-list.views :refer [wallet-list-screen]] [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.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.components.status-bar :as status-bar]
[status-im.ui.screens.network-settings.views :refer [network-settings]] [status-im.ui.screens.network-settings.views :refer [network-settings]]
[status-im.ui.screens.network-settings.add-rpc.views :refer [add-rpc-url]] [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 :chat-list :discover :contact-list) main-tabs
:wallet-list wallet-list-screen :wallet-list wallet-list-screen
:wallet-send-transaction send-transaction :wallet-send-transaction send-transaction
:wallet-transaction-sent transaction-sent
:choose-recipient choose-recipient
:wallet-request-transaction request-transaction :wallet-request-transaction request-transaction
:wallet-transactions wallet-transactions/transactions :wallet-transactions wallet-transactions/transactions
:wallet-transaction-details wallet-transactions/transaction-details :wallet-transaction-details wallet-transactions/transaction-details

View File

@ -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))))

View File

@ -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]]))

View File

@ -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})]))))

View File

@ -12,15 +12,23 @@
:height 52 :height 52
:letter-spacing -0.2}) :letter-spacing -0.2})
(def label (defstyle label
{:color :white {:color :white
:font-size 14 :ios {:font-size 14
:line-height 16 :line-height 16
:letter-spacing -0.2}) :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?] (defnstyle amount-container [active?]
{:margin-top 8 {:height 52
:height 52
:background-color (if active? :background-color (if active?
styles/color-white-transparent-4 styles/color-white-transparent-4
styles/color-white-transparent-3) styles/color-white-transparent-3)
@ -44,17 +52,36 @@
(defstyle currency-container (defstyle currency-container
{:margin-top 8 {:margin-top 8
:height 52 :height 52
:background-color styles/color-white-transparent-3 ;;TODO disabled
:border-width 1
:border-color styles/color-white-transparent-4
;:background-color styles/color-white-transparent-3
:justify-content :center :justify-content :center
:padding 14 :padding 14
:ios {:border-radius 8} :ios {:border-radius 8}
:android {:border-radius 4}}) :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
:padding-vertical 14
:padding-left 14
:padding-right 8
:ios {:border-radius 8}
:android {:border-radius 4}})
(defstyle wallet-container (defstyle wallet-container
{:flex-direction :row {:flex-direction :row
:margin-top 8 :margin-top 8
:height 52 :height 52
:background-color styles/color-white-transparent-3 ;;TODO disabled
:border-width 1
:border-color styles/color-white-transparent-4
;:background-color styles/color-white-transparent-3
:align-items :center :align-items :center
:padding 14 :padding 14
:ios {:border-radius 8} :ios {:border-radius 8}
@ -65,8 +92,58 @@
:font-size 15 :font-size 15
:letter-spacing -0.2}) :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 (def wallet-value
{:padding-left 6 {:padding-left 6
:color styles/color-white-transparent-5 :color styles/color-white-transparent-5
:font-size 15 :font-size 15
:letter-spacing -0.2}) :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})

View File

@ -1,16 +1,32 @@
(ns status-im.ui.screens.wallet.components.views (ns status-im.ui.screens.wallet.components.views
(:require-macros [status-im.utils.views :as views]) (:require-macros [status-im.utils.views :as views])
(:require [status-im.components.react :as react] (: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.ui.screens.wallet.components.styles :as styles]
[status-im.i18n :as i18n] [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]))
(defn amount-input [& [{:keys [input-options style]}]] (views/defview tooltip [label & [style]]
(let [active? (reagent/atom false) (views/letsubs [bottom-value (animation/create-value 16)
{:keys [on-focus on-blur]} input-options] opacity-value (animation/create-value 0)]
(fn [] {:component-did-mount (animations/animate-tooltip bottom-value opacity-value)}
[react/view {:flex 1} [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/text {:style styles/label} (i18n/label :t/amount)]
[react/view styles/amount-text-input-container
[react/view (merge (styles/amount-container @active?) style) [react/view (merge (styles/amount-container @active?) style)
[react/text-input [react/text-input
(merge (merge
@ -23,7 +39,10 @@
(when on-focus (on-focus))) (when on-focus (on-focus)))
:on-blur #(do (reset! active? false) :on-blur #(do (reset! active? false)
(when on-blur (on-blur)))} (when on-blur (on-blur)))}
(dissoc input-options :on-focus :on-blur))]]]))) (dissoc input-options :on-focus :on-blur))]]
(when-not (nil? error)
[tooltip error])]]))))
;;TODO (andrey) this should be choose component with the list of currencies ;;TODO (andrey) this should be choose component with the list of currencies
(defn choose-currency [& [style]] (defn choose-currency [& [style]]
@ -33,6 +52,25 @@
style) style)
[react/text {:style styles/wallet-name} "ETH"]]]) [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 ;;TODO (andrey) this should be choose component with the list of wallets
(views/defview choose-wallet [& [style]] (views/defview choose-wallet [& [style]]
(views/letsubs [eth-balance [:eth-balance]] (views/letsubs [eth-balance [:eth-balance]]
@ -41,10 +79,21 @@
[react/view (merge styles/wallet-container [react/view (merge styles/wallet-container
style) style)
[react/text {:style styles/wallet-name} "Main wallet"] [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 (defn network-label
([n] (network-label [{} n])) ([n] (network-label [{} n]))
([style n] [react/view (merge styles/network-container ([style n] [react/view (merge styles/network-container
style) style)
[react/text {:style styles/network} n]])) [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])

View File

@ -1,6 +1,10 @@
(ns status-im.ui.screens.wallet.db (ns status-im.ui.screens.wallet.db
(:require [cljs.spec.alpha :as spec] (: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 ;; (angusiguess) If we add more error types we can treat them as 'one-of' the following
(spec/def :wallet/error #{:error}) (spec/def :wallet/error #{:error})
@ -15,3 +19,20 @@
;; TODO(oskarth): spec for balance as BigNumber ;; 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} ;; 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)))

View File

@ -3,9 +3,6 @@
(:require [status-im.components.styles :as styles] (:require [status-im.components.styles :as styles]
[status-im.utils.platform :as platform])) [status-im.utils.platform :as platform]))
(def wallet-container
{:flex 1})
(def error-container (def error-container
{:align-self :center {:align-self :center
:justify-content :center :justify-content :center
@ -19,12 +16,9 @@
:padding-right 10 :padding-right 10
:font-size 13}) :font-size 13})
(def toolbar
{:background-color (if platform/ios? styles/color-blue4 styles/color-blue5)
:elevation 0})
(def toolbar-title-container (def toolbar-title-container
{:flex-direction :row}) {:flex-direction :row
:padding-left 24})
(def toolbar-title-text (def toolbar-title-text
{:color styles/color-white {:color styles/color-white

View File

@ -6,7 +6,6 @@
[status-im.components.drawer.view :as drawer] [status-im.components.drawer.view :as drawer]
[status-im.components.list.views :as list] [status-im.components.list.views :as list]
[status-im.components.react :as react] [status-im.components.react :as react]
[status-im.components.styles :as styles]
[status-im.components.icons.vector-icons :as vi] [status-im.components.icons.vector-icons :as vi]
[status-im.components.toolbar-new.view :as toolbar] [status-im.components.toolbar-new.view :as toolbar]
[status-im.components.toolbar-new.actions :as act] [status-im.components.toolbar-new.actions :as act]
@ -15,7 +14,9 @@
[status-im.utils.config :as config] [status-im.utils.config :as config]
[status-im.utils.utils :as utils] [status-im.utils.utils :as utils]
[status-im.utils.platform :as platform] [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])) [status-im.ui.screens.wallet.views :as wallet.views]))
(defn- show-not-implemented! [] (defn- show-not-implemented! []
@ -23,18 +24,18 @@
(defn toolbar-title [] (defn toolbar-title []
[react/touchable-highlight {:on-press #(rf/dispatch [:navigate-to :wallet-list])} [react/touchable-highlight {:on-press #(rf/dispatch [:navigate-to :wallet-list])}
[react/view {:style wallet.styles/toolbar-title-container} [react/view {:style styles/toolbar-title-container}
[react/text {:style wallet.styles/toolbar-title-text [react/text {:style styles/toolbar-title-text
:font :toolbar-title} :font :toolbar-title}
(i18n/label :t/main-wallet)] (i18n/label :t/main-wallet)]
[vi/icon [vi/icon
:icons/dropdown :icons/dropdown
{:container-style wallet.styles/toolbar-title-icon {:container-style styles/toolbar-title-icon
:color :white}]]]) :color :white}]]])
(def transaction-history-action (def transaction-history-action
{:icon :icons/transaction-history {: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])}) :handler #(rf/dispatch [:navigate-to :wallet-transactions])})
(defn toolbar-view [] (defn toolbar-view []
@ -49,27 +50,27 @@
(defn- change-display [change] (defn- change-display [change]
(let [pos-change? (or (pos? change) (zero? change))] (let [pos-change? (or (pos? change) (zero? change))]
[react/view {:style (if pos-change? [react/view {:style (if pos-change?
wallet.styles/today-variation-container-positive styles/today-variation-container-positive
wallet.styles/today-variation-container-negative)} styles/today-variation-container-negative)}
[react/text {:style (if pos-change? [react/text {:style (if pos-change?
wallet.styles/today-variation-positive styles/today-variation-positive
wallet.styles/today-variation-negative)} styles/today-variation-negative)}
(if change (if change
(str (when pos-change? "+") change "%") (str (when pos-change? "+") change "%")
"-%")]])) "-%")]]))
(defn main-section [usd-value change error-message] (defn main-section [usd-value change error-message]
[react/view {:style wallet.styles/main-section} [react/view {:style styles/main-section}
(when error-message [wallet.views/error-message-view wallet.styles/error-container wallet.styles/error-message]) (when error-message [wallet.views/error-message-view styles/error-container styles/error-message])
[react/view {:style wallet.styles/total-balance-container} [react/view {:style styles/total-balance-container}
[react/view {:style wallet.styles/total-balance} [react/view {:style styles/total-balance}
[react/text {:style wallet.styles/total-balance-value} usd-value] [react/text {:style styles/total-balance-value} usd-value]
[react/text {:style wallet.styles/total-balance-currency} "USD"]] [react/text {:style styles/total-balance-currency} "USD"]]
[react/view {:style wallet.styles/value-variation} [react/view {:style styles/value-variation}
[react/text {:style wallet.styles/value-variation-title} [react/text {:style styles/value-variation-title}
(i18n/label :t/wallet-total-value)] (i18n/label :t/wallet-total-value)]
[change-display change]] [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) [{:text (i18n/label :t/wallet-send)
:on-press #(do (rf/dispatch [:navigate-to :wallet-send-transaction]) :on-press #(do (rf/dispatch [:navigate-to :wallet-send-transaction])
(when platform/android? (when platform/android?
@ -83,15 +84,15 @@
(defn- token->image [id] (defn- token->image [id]
(case 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 [] (defn add-asset []
[list/touchable-item show-not-implemented! [list/touchable-item show-not-implemented!
[react/view [react/view
[list/item [list/item
[list/item-icon {:icon :icons/add :style wallet.styles/add-asset-icon :icon-opts {:color :blue}}] [list/item-icon {:icon :icons/add :style styles/add-asset-icon :icon-opts {:color :blue}}]
[react/view {:style wallet.styles/asset-item-value-container} [react/view {:style styles/asset-item-value-container}
[react/text {:style wallet.styles/add-asset-text} [react/text {:style styles/add-asset-text}
(i18n/label :t/wallet-add-asset)]]]]]) (i18n/label :t/wallet-add-asset)]]]]])
(defn render-asset [{:keys [id currency amount]}] (defn render-asset [{:keys [id currency amount]}]
@ -101,9 +102,9 @@
[react/view [react/view
[list/item [list/item
[list/item-image {:uri :launch_logo}] [list/item-image {:uri :launch_logo}]
[react/view {:style wallet.styles/asset-item-value-container} [react/view {:style styles/asset-item-value-container}
[react/text {:style wallet.styles/asset-item-value} (str amount)] [react/text {:style styles/asset-item-value} (str amount)]
[react/text {:style wallet.styles/asset-item-currency [react/text {:style styles/asset-item-currency
:uppercase? true} :uppercase? true}
id]] id]]
[list/item-icon {:icon :icons/forward}]]]] [list/item-icon {:icon :icons/forward}]]]]
@ -112,17 +113,17 @@
[list/item [list/item
(let [{:keys [source style]} (token->image id)] (let [{:keys [source style]} (token->image id)]
[list/item-image source style]) [list/item-image source style])
[react/view {:style wallet.styles/asset-item-value-container} [react/view {:style styles/asset-item-value-container}
[react/text {:style wallet.styles/asset-item-value} (str amount)] [react/text {:style styles/asset-item-value} (str amount)]
[react/text {:style wallet.styles/asset-item-currency [react/text {:style styles/asset-item-currency
:uppercase? true} :uppercase? true}
id]]]] id]]]]
[add-asset])) [add-asset]))
(defn asset-section [eth prices-loading? balance-loading?] (defn asset-section [eth prices-loading? balance-loading?]
(let [assets [{:id "eth" :currency :eth :amount eth}]] (let [assets [{:id "eth" :currency :eth :amount eth}]]
[react/view {:style wallet.styles/asset-section} [react/view {:style styles/asset-section}
[react/text {:style wallet.styles/asset-section-title} (i18n/label :t/wallet-assets)] [react/text {:style styles/asset-section-title} (i18n/label :t/wallet-assets)]
[list/flat-list [list/flat-list
{:data (conj assets {}) ;; Extra map triggers rendering for add-asset {:data (conj assets {}) ;; Extra map triggers rendering for add-asset
:render-fn render-asset :render-fn render-asset
@ -138,6 +139,6 @@
error-message [:wallet/error-message?]] error-message [:wallet/error-message?]]
[react/view {:style wallet.styles/wallet-container} [react/view {:style wallet.styles/wallet-container}
[toolbar-view] [toolbar-view]
[react/view {:style styles/flex} [react/view components.styles/flex
[main-section portfolio-value portfolio-change error-message] [main-section portfolio-value portfolio-change error-message]
[asset-section eth-balance prices-loading? balance-loading?]]])) [asset-section eth-balance prices-loading? balance-loading?]]]))

View File

@ -15,3 +15,9 @@
(defmethod navigation/preload-data! :wallet-request-transaction (defmethod navigation/preload-data! :wallet-request-transaction
[db _] [db _]
(dissoc db :wallet/request-transaction)) (dissoc db :wallet/request-transaction))
(defmethod navigation/preload-data! :wallet-send-transaction
[db [event]]
(if (= event :navigate-back)
db
(dissoc db :wallet/send-transaction)))

View File

@ -3,6 +3,7 @@
(:require [cljs.spec.alpha :as spec])) (:require [cljs.spec.alpha :as spec]))
(spec/def ::amount (spec/nilable string?)) (spec/def ::amount (spec/nilable string?))
(spec/def ::amount-error (spec/nilable string?))
(spec/def :wallet/request-transaction (allowed-keys (spec/def :wallet/request-transaction (allowed-keys
:opt-un [::amount])) :opt-un [::amount ::amount-error]))

View File

@ -1,7 +1,7 @@
(ns status-im.ui.screens.wallet.request.events (ns status-im.ui.screens.wallet.request.events
(:require (: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 (handlers/register-handler-fx
:wallet-send-request :wallet-send-request
@ -11,3 +11,10 @@
[:chat-with-command whisper-identity :request [:chat-with-command whisper-identity :request
{:contact contact {:contact contact
:amount (:amount request-transaction)}]]})) :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)})))

View File

@ -1,9 +1,6 @@
(ns status-im.ui.screens.wallet.request.styles (ns status-im.ui.screens.wallet.request.styles
(:require [status-im.components.styles :as styles])) (:require [status-im.components.styles :as styles]))
(def main-container
{:flex 1})
(def network-label (def network-label
{:margin-top 27}) {:margin-top 27})
@ -14,44 +11,5 @@
(def qr-container (def qr-container
{:margin-top 16}) {: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 (def share-icon-container
{:margin-right 8}) {: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})

View File

@ -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 ""))))

View File

@ -7,7 +7,7 @@
[status-im.components.toolbar-new.actions :as actions] [status-im.components.toolbar-new.actions :as actions]
[status-im.components.toolbar-new.view :as toolbar] [status-im.components.toolbar-new.view :as toolbar]
[status-im.components.status-bar :as status-bar] [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.components.icons.vector-icons :as vi]
[status-im.ui.screens.wallet.components.views :as components] [status-im.ui.screens.wallet.components.views :as components]
[status-im.ui.screens.wallet.request.styles :as styles] [status-im.ui.screens.wallet.request.styles :as styles]
@ -16,7 +16,7 @@
[status-im.utils.platform :as platform])) [status-im.utils.platform :as platform]))
(defn toolbar-view [] (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/nav-button (actions/back-white actions/default-handler)]
[toolbar/content-title {:color :white} (i18n/label :t/request-transaction)]]) [toolbar/content-title {:color :white} (i18n/label :t/request-transaction)]])
@ -36,41 +36,40 @@
:fgColor "#4360df" :fgColor "#4360df"
:size 256}])) :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 [] (views/defview request-transaction []
;;Because input field is in the end of view we will scroll to the end on input focus event ;;Because input field is in the end of view we will scroll to the end on input focus event
(views/letsubs [scroll (atom nil)] (views/letsubs [amount-error [:get-in [:wallet/request-transaction :amount-error]]
[react/keyboard-avoiding-view wallet-styles/wallet-modal-container request-enabled? [:wallet.request/request-enabled?]
scroll (atom nil)]
[react/keyboard-avoiding-view wallet.styles/wallet-modal-container
[status-bar/status-bar {:type :wallet}] [status-bar/status-bar {:type :wallet}]
[toolbar-view] [toolbar-view]
[react/scroll-view {:ref #(reset! scroll %)} [react/scroll-view {:ref #(reset! scroll %)}
[react/view styles/main-container [react/view components.styles/flex
[react/view styles/network-container [react/view styles/network-container
;;TODO (andrey) name of active network should be used ;;TODO (andrey) name of active network should be used
[components/network-label styles/network-label "Testnet"] [components/network-label styles/network-label "Testnet"]
[react/view styles/qr-container [react/view styles/qr-container
[qr-code]]] [qr-code]]]
[react/view styles/choose-wallet-container [react/view wallet.styles/choose-wallet-container
[components/choose-wallet]] [components/choose-wallet]]
[react/view styles/amount-container [react/view wallet.styles/amount-container
[components/amount-input [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 :on-change-text
#(re-frame/dispatch [:set-in [:wallet/request-transaction :amount] %])}}] #(do (re-frame/dispatch [:set-in [:wallet/request-transaction :amount] %])
[react/view styles/choose-currency-container (re-frame/dispatch [:wallet-validate-request-amount]))}}]
[components/choose-currency styles/choose-currency]]]]] [react/view wallet.styles/choose-currency-container
[react/view styles/separator] [components/choose-currency wallet.styles/choose-currency]]]]]
[react/view styles/buttons-container [components/separator]
[react/view wallet.styles/buttons-container
[react/touchable-highlight {:on-press #()} [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}] [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/view components.styles/flex]
[react/touchable-highlight {:on-press send-request} [react/touchable-highlight {:on-press (when request-enabled? send-request)}
[react/view styles/button-container [react/view (wallet.styles/button-container request-enabled?)
[button-text (i18n/label :t/send-request)] [components/button-text (i18n/label :t/send-request)]
[vi/icon :icons/forward {:color :white :container-style styles/forward-icon-container}]]]]])) [vi/icon :icons/forward {:color :white :container-style wallet.styles/forward-icon-container}]]]]]))

View File

@ -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})])))

View File

@ -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?]))

View File

@ -1,8 +1,123 @@
(ns status-im.ui.screens.wallet.send.events (ns status-im.ui.screens.wallet.send.events
(:require [re-frame.core :as re-frame] (: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 :choose-recipient
(fn [db [_ recipient]] (fn [db [_ recipient]]
(assoc-in db [:wallet :send :recipient] 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]]})))))

View File

@ -1,18 +1,9 @@
(ns status-im.ui.screens.wallet.send.styles (ns status-im.ui.screens.wallet.send.styles
(:require-macros [status-im.utils.styles :refer [defnstyle defstyle]]) (:require-macros [status-im.utils.styles :refer [defnstyle defstyle]])
(:require [status-im.components.styles :as styles] (: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})
(def toolbar (def toolbar
{:background-color styles/color-blue4 {:background-color styles/color-blue5
:elevation 0 :elevation 0
:padding-bottom 10}) :padding-bottom 10})
@ -33,119 +24,40 @@
(def toolbar-title-icon (def toolbar-title-icon
(merge toolbar-icon {:opacity 0.4})) (merge toolbar-icon {:opacity 0.4}))
(def toolbar-buttons-container (defn animated-sign-panel [bottom-value]
{: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 {:position :absolute
:width 40 :left 12
:height 40}) :right 12
:bottom bottom-value})
(defn corner-left-bottom [dimension] (defn sign-panel [opacity-value]
(let [viewport-offset 0.1666] {:opacity opacity-value
(merge corner-dimensions {:bottom (* viewport-offset dimension) :border-radius 8
:left (* viewport-offset dimension)}))) :background-color :white
:padding-top 12
:padding-horizontal 12})
(defn corner-right-bottom [dimension] (def signing-phrase-container
(let [viewport-offset 0.1666] {:border-radius 8
(merge corner-dimensions {:right (* viewport-offset dimension) :height 36
:bottom (* viewport-offset dimension)}))) :align-items :center
:justify-content :center
:background-color styles/color-light-gray})
(defn corner-left-top [dimension] (def signing-phrase
(let [viewport-offset 0.1666] {:font-size 15
(merge corner-dimensions {:top (* viewport-offset dimension) :letter-spacing -0.2
:left (* viewport-offset dimension)}))) :color :black})
(defn corner-right-top [dimension] (def signing-phrase-description
(let [viewport-offset 0.1666] {:padding-top 8})
(merge corner-dimensions {:top (* viewport-offset dimension)
:right (* viewport-offset dimension)})))
(def viewfinder-port {:position :absolute (def password-container
:left 0 {:flex 1
:top 0 :padding-vertical 20})
:bottom 0
:right 0
:flex 1})
(defn viewfinder-translucent [height width side] (def password
(let [viewport-offset 0.1666 {:padding 0
height-offset (* viewport-offset height) :font-size 15
width-offset (* viewport-offset width)] :letter-spacing -0.2
(cond-> {:position :absolute :height 20})
: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))))

View File

@ -4,3 +4,20 @@
(re-frame/reg-sub :camera-dimensions (re-frame/reg-sub :camera-dimensions
(fn [db] (fn [db]
(get-in db [:wallet :camera-dimensions]))) (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 ""))))

View File

@ -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})

View File

@ -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)]]]])

View File

@ -4,7 +4,6 @@
[status-im.components.react :as react] [status-im.components.react :as react]
[re-frame.core :as re-frame] [re-frame.core :as re-frame]
[status-im.components.button.view :as button] [status-im.components.button.view :as button]
[status-im.components.styles :as styles]
[status-im.components.status-bar :as status-bar] [status-im.components.status-bar :as status-bar]
[status-im.components.toolbar-new.actions :as act] [status-im.components.toolbar-new.actions :as act]
[status-im.components.toolbar-new.view :as toolbar] [status-im.components.toolbar-new.view :as toolbar]
@ -13,77 +12,113 @@
[status-im.i18n :as i18n] [status-im.i18n :as i18n]
[status-im.ui.screens.wallet.send.styles :as send.styles] [status-im.ui.screens.wallet.send.styles :as send.styles]
[status-im.components.icons.vector-icons :as vector-icons] [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! [] (defn toolbar-view [signing?]
(utils/show-popup "TODO" "Not implemented yet!")) [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 [] (defn sign-later []
[toolbar/toolbar2 {:style send.styles/toolbar (utils/show-question
:no-sync-bar? true} (i18n/label :t/sign-later-title)
[toolbar/nav-button (act/close-white act/default-handler)] (i18n/label :t/sign-later-text)
[toolbar/content-title {:color :white} (i18n/label :t/wallet-choose-recipient)] #(re-frame/dispatch [:wallet/sign-transaction true])))
[toolbar/actions [{:icon :icons/flash-active
:icon-opts {:color :white}
:handler show-not-implemented!}]]])
(defn recipient-buttons [] (defview sign-panel []
[react/view {:style send.styles/recipient-buttons} (letsubs [account [:get-current-account]
[react/touchable-highlight {:style (send.styles/recipient-touchable true)} ;;TODO (andrey) use send-transaction map after we remove old transactions ui
[react/view {:style send.styles/recipient-button} wrong-password? [:get :wrong-password?];[:get-in [:wallet/send-transaction :wrong-password?]]
[react/text {:style send.styles/recipient-button-text} signing-phrase (:signing-phrase @account)
(i18n/label :t/wallet-choose-from-contacts)] bottom-value (animation/create-value -250)
[vector-icons/icon :icons/qr {:color :white opacity-value (animation/create-value 0)]
:container-style send.styles/recipient-icon}]]] {:component-did-mount #(send.animations/animate-sign-panel opacity-value bottom-value)}
[react/touchable-highlight {:style (send.styles/recipient-touchable true) [react/animated-view {:style (styles/animated-sign-panel bottom-value)}
:on-press #(react/get-from-clipboard [react/animated-view {:style (styles/sign-panel opacity-value)}
(fn [clipboard] [react/view styles/signing-phrase-container
(re-frame/dispatch [:choose-recipient clipboard])))} [react/text {:style styles/signing-phrase} signing-phrase]]
[react/view {:style send.styles/recipient-button} [react/text {:style styles/signing-phrase-description} (i18n/label :t/signing-phrase-description)]
[react/text {:style send.styles/recipient-button-text} [react/view styles/password-container
(i18n/label :t/wallet-address-from-clipboard)] [react/text-input
[vector-icons/icon :icons/copy-from {:color :white {:auto-focus true
:container-style send.styles/recipient-icon}]]] :secure-text-entry true
[react/touchable-highlight {:style send.styles/recipient-touchable-disabled} :placeholder (i18n/label :t/enter-password)
[react/view {:style send.styles/recipient-button} :placeholder-text-color "#939ba1"
[react/text {:style send.styles/recipient-button-text-disabled} :on-change-text #(do
(i18n/label :t/wallet-browse-photos)] (re-frame/dispatch [:set-in [:wallet/send-transaction :password] %]))
[vector-icons/icon :icons/browse {:color :white :style styles/password}]]]
:container-style send.styles/recipient-icon-disabled}]]]]) (when wrong-password?
[components/tooltip (i18n/label :t/wrong-password)])]))
(defn viewfinder [{:keys [height width]}]
(let [min-dimension (min height width)] (defview signing-buttons []
[react/view {:style send.styles/viewfinder-port} (letsubs [sign-enabled? [:wallet.send/sign-password-enabled?]]
[react/view {:style (send.styles/viewfinder-translucent height width :top)}] [react/view wallet.styles/buttons-container
[react/view {:style (send.styles/viewfinder-translucent height width :right)}] [react/touchable-highlight {:on-press #(re-frame/dispatch [:wallet/discard-transaction])}
[react/view {:style (send.styles/viewfinder-translucent height width :bottom)}] [react/view (wallet.styles/button-container true)
[react/view {:style (send.styles/viewfinder-translucent height width :left)}] [components/button-text (i18n/label :t/cancel)]]]
[react/image {:source {:uri :corner_left_top} [react/view components.styles/flex]
:style (send.styles/corner-left-top min-dimension)}] [react/touchable-highlight {:on-press #(re-frame/dispatch [:wallet/sign-transaction])}
[react/image {:source {:uri :corner_right_top} [react/view (wallet.styles/button-container sign-enabled?)
:style (send.styles/corner-right-top min-dimension)}] [components/button-text (i18n/label :t/transactions-sign-transaction)]
[react/image {:source {:uri :corner_left_bottom} [vi/icon :icons/forward {:color :white :container-style wallet.styles/forward-icon-container}]]]]))
:style (send.styles/corner-left-bottom min-dimension)}]
[react/image {:source {:uri :corner_right_bottom} (defview sign-buttons []
:style (send.styles/corner-right-bottom min-dimension)}]])) (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 [] (defview send-transaction []
(letsubs [camera-dimensions [:camera-dimensions]] (letsubs [amount [:get-in [:wallet/send-transaction :amount]]
[react/view {:style send.styles/wallet-container} 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}] [status-bar/status-bar {:type :wallet}]
[toolbar-view] [toolbar-view signing?]
[react/view {:style send.styles/qr-container [react/scroll-view {:keyboardShouldPersistTaps :always}
:on-layout #(let [layout (.. % -nativeEvent -layout)] [react/view components.styles/flex
(re-frame/dispatch [:set-in [:wallet :camera-dimensions] [react/view wallet.styles/choose-participant-container
{:width (.-width layout) [components/choose-recipient {:address to-address
:height (.-height layout)}]))} :name to-name
[camera/camera {:style send.styles/preview :on-press #(re-frame/dispatch [:navigate-to :choose-recipient])}]]
:aspect :fill [react/view wallet.styles/choose-wallet-container
:captureAudio false [components/choose-wallet]]
:onBarCodeRead (fn [code] [react/view wallet.styles/amount-container
(let [data (-> code [components/amount-input
.-data {:error amount-error
(str/replace #"ethereum:" ""))] :input-options {:auto-focus true
(re-frame/dispatch [:choose-recipient data])))}] :default-value amount
[viewfinder camera-dimensions]] :on-change-text #(do
[recipient-buttons]])) (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])]]))

View File

@ -1,5 +1,15 @@
(ns status-im.ui.screens.wallet.styles (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 (def wallet-exclamation-container
{:background-color st/color-red-2 {:background-color st/color-red-2
@ -13,3 +23,42 @@
(def wallet-error-exclamation (def wallet-error-exclamation
{:width 16 {:width 16
:height 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})

View File

@ -23,6 +23,9 @@
(defn bignumber [n] (defn bignumber [n]
(dependencies/Web3.prototype.toBigNumber (str n))) (dependencies/Web3.prototype.toBigNumber (str n)))
(defn to-wei [str]
(dependencies/Web3.prototype.toWei str "ether"))
(def eth-units (def eth-units
{:wei (bignumber "1") {:wei (bignumber "1")
:kwei (bignumber "1000") :kwei (bignumber "1000")

View File

@ -24,6 +24,18 @@
(when on-cancel {:onPress on-cancel})) (when on-cancel {:onPress on-cancel}))
{:text (or s "OK") :onPress on-accept :style "destructive"}))))) {: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 (defn http-post
([action data on-success] ([action data on-success]
(http-post action data on-success nil)) (http-post action data on-success nil))