send transaction GUI

Signed-off-by: Andrey Shovkoplyas <motor4ik@gmail.com>
This commit is contained in:
Andrey Shovkoplyas 2019-11-16 10:56:09 +01:00
parent 182bfef295
commit 507cc5cf39
No known key found for this signature in database
GPG Key ID: EAAB7C8622D860A4
71 changed files with 794 additions and 1395 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 198 B

After

Width:  |  Height:  |  Size: 283 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 339 B

After

Width:  |  Height:  |  Size: 444 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 465 B

After

Width:  |  Height:  |  Size: 721 B

View File

@ -302,7 +302,13 @@
(defn create-main-screen-view [current-view] (defn create-main-screen-view [current-view]
(fn [props & children] (fn [props & children]
(apply vector (adapt-class (object/get js-dependencies/safe-area-context "SafeAreaView")) props children))) (apply
vector
(adapt-class (object/get js-dependencies/safe-area-context "SafeAreaView"))
(cond-> props
(= current-view :qr-scanner)
(assoc :background-color :black))
children)))
(defn main-screen-modal-view [current-view & components] (defn main-screen-modal-view [current-view & components]
;; NOTE on Android we use Modal component and it manages statusbar area by itself ;; NOTE on Android we use Modal component and it manages statusbar area by itself

Binary file not shown.

Before

Width:  |  Height:  |  Size: 198 B

After

Width:  |  Height:  |  Size: 283 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 339 B

After

Width:  |  Height:  |  Size: 444 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 464 B

After

Width:  |  Height:  |  Size: 721 B

View File

@ -26,8 +26,7 @@
(cond (cond
(= permission constants/dapp-permission-qr-code) (= permission constants/dapp-permission-qr-code)
(fx/merge (assoc-in cofx [:db :browser/options :yielding-control?] true) (fx/merge (assoc-in cofx [:db :browser/options :yielding-control?] true)
(qr-scanner/scan-qr-code {} (qr-scanner/scan-qr-code {:handler :browser.bridge.callback/qr-code-scanned
{:handler :browser.bridge.callback/qr-code-scanned
:cancel-handler :browser.bridge.callback/qr-code-canceled :cancel-handler :browser.bridge.callback/qr-code-canceled
:data {:dapp-name dapp-name :data {:dapp-name dapp-name
:permission permission :permission permission

View File

@ -12,10 +12,9 @@
(if js/goog.DEBUG (if js/goog.DEBUG
(.ignoreWarnings (.-YellowBox js-dependencies/react-native) (.ignoreWarnings (.-YellowBox js-dependencies/react-native)
#js #js ["re-frame: overwriting"
["re-frame: overwriting" "Warning: componentWillMount is deprecated and will be removed in the next major version. Use componentDidMount instead. As a temporary workaround, you can rename to UNSAFE_componentWillMount."
"Warning: componentWillMount is deprecated and will be removed in the next major version. Use componentDidMount instead. As a temporary workaround, you can rename to UNSAFE_componentWillMount." "Warning: componentWillUpdate is deprecated and will be removed in the next major version. Use componentDidUpdate instead. As a temporary workaround, you can rename to UNSAFE_componentWillUpdate."])
"Warning: componentWillUpdate is deprecated and will be removed in the next major version. Use componentDidUpdate instead. As a temporary workaround, you can rename to UNSAFE_componentWillUpdate."])
(aset js/console "disableYellowBox" true)) (aset js/console "disableYellowBox" true))
(defn init [app-root] (defn init [app-root]

View File

@ -34,6 +34,10 @@
(or (some #(when (= i (:id (val %))) (key %)) chains) (or (some #(when (= i (:id (val %))) (key %)) chains)
:custom)) :custom))
(defn chain-id->chain-name [i]
(or (some #(when (= i (:id (val %))) (:name (val %))) chains)
:custom))
(defn chain-keyword->chain-id [k] (defn chain-keyword->chain-id [k]
(get-in chains [k :id])) (get-in chains [k :id]))
@ -91,6 +95,9 @@
(defn network->chain-keyword [network] (defn network->chain-keyword [network]
(chain-id->chain-keyword (network->chain-id network))) (chain-id->chain-keyword (network->chain-id network)))
(defn network->network-name [network]
(chain-id->chain-name (network->chain-id network)))
(defn network->chain-name [network] (defn network->chain-name [network]
(-> network (-> network
network->chain-keyword network->chain-keyword

View File

@ -222,7 +222,7 @@
(handlers/register-handler-fx (handlers/register-handler-fx
:mailserver.callback/qr-code-scanned :mailserver.callback/qr-code-scanned
(fn [cofx [_ _ url]] (fn [cofx [_ url _]]
(mailserver/set-url-from-qr cofx url))) (mailserver/set-url-from-qr cofx url)))
(handlers/register-handler-fx (handlers/register-handler-fx
@ -327,7 +327,7 @@
(handlers/register-handler-fx (handlers/register-handler-fx
:bootnodes.callback/qr-code-scanned :bootnodes.callback/qr-code-scanned
(fn [cofx [_ _ url]] (fn [cofx [_ url _]]
(bootnodes/set-bootnodes-from-qr cofx url))) (bootnodes/set-bootnodes-from-qr cofx url)))
(handlers/register-handler-fx (handlers/register-handler-fx
@ -386,30 +386,32 @@
(handlers/register-handler-fx (handlers/register-handler-fx
:browser.bridge.callback/qr-code-scanned :browser.bridge.callback/qr-code-scanned
(fn [cofx [_ _ data qr-code-data]] (fn [cofx [_ data qr-code-data]]
(browser/handle-scanned-qr-code cofx data (:data qr-code-data)))) (browser/handle-scanned-qr-code cofx data (:data qr-code-data))))
(handlers/register-handler-fx (handlers/register-handler-fx
:browser.bridge.callback/qr-code-canceled :browser.bridge.callback/qr-code-canceled
(fn [cofx [_ _ qr-code-data]] (fn [cofx [_ qr-code-data _]]
(browser/handle-canceled-qr-code cofx (:data qr-code-data)))) (browser/handle-canceled-qr-code cofx (:data qr-code-data))))
;; qr-scanner module ;; qr-scanner module
(handlers/register-handler-fx (handlers/register-handler-fx
:qr-scanner.ui/scan-qr-code-pressed :qr-scanner.ui/scan-qr-code-pressed
(fn [cofx [_ identifier handler & [opts]]] (fn [cofx [_ opts]]
(qr-scanner/scan-qr-code cofx identifier (merge {:handler handler} opts)))) (qr-scanner/scan-qr-code cofx opts)))
(handlers/register-handler-fx (handlers/register-handler-fx
:qr-scanner.callback/scan-qr-code-success :qr-scanner.callback/scan-qr-code-success
(fn [cofx [_ context data]] (fn [cofx [_ opts data]]
(qr-scanner/set-qr-code cofx context data))) (qr-scanner/set-qr-code cofx opts data)))
(handlers/register-handler-fx (handlers/register-handler-fx
:qr-scanner.callback/scan-qr-code-cancel :qr-scanner.callback/scan-qr-code-cancel
(fn [cofx [_ context]] (fn [cofx [_ opts]]
(qr-scanner/set-qr-code-cancel cofx context))) (fx/merge cofx
(qr-scanner/set-qr-code-cancel opts)
(navigation/navigate-back))))
;; privacy-policy module ;; privacy-policy module
@ -1268,7 +1270,7 @@
(handlers/register-handler-fx (handlers/register-handler-fx
:contact/qr-code-scanned :contact/qr-code-scanned
[(re-frame/inject-cofx :random-id-generator)] [(re-frame/inject-cofx :random-id-generator)]
(fn [{:keys [db] :as cofx} [_ _ contact-identity]] (fn [{:keys [db] :as cofx} [_ contact-identity _]]
(let [current-multiaccount (:multiaccount db) (let [current-multiaccount (:multiaccount db)
fx {:db (assoc db :contacts/new-identity contact-identity)} fx {:db (assoc db :contacts/new-identity contact-identity)}
validation-result (new-chat.db/validate-pub-key db contact-identity)] validation-result (new-chat.db/validate-pub-key db contact-identity)]
@ -1574,6 +1576,11 @@
(fn [] (fn []
(react/dismiss-keyboard!))) (react/dismiss-keyboard!)))
(handlers/register-handler-fx
:dismiss-keyboard
(fn [_]
{:dismiss-keyboard nil}))
(handlers/register-handler-fx (handlers/register-handler-fx
:wallet-send-request :wallet-send-request
(fn [{:keys [db] :as cofx} [_ public-key amount symbol decimals]] (fn [{:keys [db] :as cofx} [_ public-key amount symbol decimals]]

View File

@ -5,33 +5,22 @@
[status-im.utils.fx :as fx])) [status-im.utils.fx :as fx]))
(fx/defn scan-qr-code (fx/defn scan-qr-code
[{:keys [db]} {:keys [deny-handler] :as identifier} qr-codes] [_ opts]
{:db (assoc-in db [:qr-codes identifier] qr-codes) {:request-permissions-fx
:request-permissions-fx {:permissions [:camera] {:permissions [:camera]
:on-allowed #(re-frame/dispatch :on-allowed #(re-frame/dispatch [:navigate-to :qr-scanner opts])
[:navigate-to :qr-scanner :on-denied (fn []
{:current-qr-context identifier}]) (utils/set-timeout
:on-denied (if (nil? deny-handler) #(utils/show-popup (i18n/label :t/error)
(fn [] (i18n/label :t/camera-access-error))
(utils/set-timeout 50))}})
#(utils/show-popup (i18n/label :t/error)
(i18n/label :t/camera-access-error))
50))
#(re-frame/dispatch [deny-handler qr-codes]))}})
(fx/defn set-qr-code (fx/defn set-qr-code
[{:keys [db]} context data] [{:keys [db]} opts data]
(merge {:db (-> db (when-let [handler (:handler opts)]
(update :qr-codes dissoc context) {:dispatch [handler data opts]}))
(dissoc :current-qr-context))}
(when-let [qr-codes (get-in db [:qr-codes context])]
{:dispatch [(:handler qr-codes) context data (dissoc qr-codes :handler)]})))
(fx/defn set-qr-code-cancel (fx/defn set-qr-code-cancel
[{:keys [db]} context] [_ opts]
(merge {:db (-> db (when-let [handler (:cancel-handler opts)]
(update :qr-codes dissoc context) {:dispatch [handler opts]}))
(dissoc :current-qr-context))}
(when-let [qr-codes (get-in db [:qr-codes context])]
(when-let [handler (:cancel-handler qr-codes)]
{:dispatch [handler context qr-codes]}))))

View File

@ -166,6 +166,7 @@
(reg-root-key-sub :prices-loading? :prices-loading?) (reg-root-key-sub :prices-loading? :prices-loading?)
(reg-root-key-sub :wallet.transactions :wallet.transactions) (reg-root-key-sub :wallet.transactions :wallet.transactions)
(reg-root-key-sub :wallet/custom-token-screen :wallet/custom-token-screen) (reg-root-key-sub :wallet/custom-token-screen :wallet/custom-token-screen)
(reg-root-key-sub :wallet/prepare-transaction :wallet/prepare-transaction)
;;ethereum ;;ethereum
(reg-root-key-sub :ethereum/current-block :ethereum/current-block) (reg-root-key-sub :ethereum/current-block :ethereum/current-block)
@ -284,6 +285,12 @@
(fn [network] (fn [network]
(ethereum/network->chain-name network))) (ethereum/network->chain-name network)))
(re-frame/reg-sub
:network-name
:<- [:current-network]
(fn [network]
(ethereum/network->network-name network)))
(re-frame/reg-sub (re-frame/reg-sub
:chain-id :chain-id
:<- [:current-network] :<- [:current-network]
@ -488,6 +495,12 @@
(fn [[macc acc]] (fn [[macc acc]]
(some #(when (= (:address %) (:address acc)) %) (:accounts macc)))) (some #(when (= (:address %) (:address acc)) %) (:accounts macc))))
(re-frame/reg-sub
:account-by-address
:<- [:multiaccount]
(fn [macc [_ address]]
(some #(when (= (:address %) address) %) (:accounts macc))))
(re-frame/reg-sub (re-frame/reg-sub
:multiple-multiaccounts? :multiple-multiaccounts?
:<- [:multiaccounts/multiaccounts] :<- [:multiaccounts/multiaccounts]
@ -539,7 +552,7 @@
:<- [:chats/current-chat-ui-prop :input-height] :<- [:chats/current-chat-ui-prop :input-height]
:<- [:chats/current-chat-ui-prop :input-focused?] :<- [:chats/current-chat-ui-prop :input-focused?]
:<- [:keyboard-height] :<- [:keyboard-height]
:<- [:chats/current-chat-ui-prop :show-stickers?] :<- [:chats/current-chat-ui-prop :input-bottom-sheet]
(fn [[home-content-layout-height input-height input-focused? kheight stickers?]] (fn [[home-content-layout-height input-height input-focused? kheight stickers?]]
(- (+ home-content-layout-height tabs.styles/tabs-height) (- (+ home-content-layout-height tabs.styles/tabs-height)
(if platform/iphone-x? (if platform/iphone-x?
@ -2070,21 +2083,27 @@
(get-sufficient-gas-error balance nil nil gas gasPrice)))) (get-sufficient-gas-error balance nil nil gas gasPrice))))
(re-frame/reg-sub (re-frame/reg-sub
:wallet.send/transaction :wallet.send/prepare-transaction-with-balance
:<- [::send-transaction] :<- [:wallet/prepare-transaction]
:<- [:wallet] :<- [:wallet]
:<- [:offline?] :<- [:offline?]
:<- [:wallet/all-tokens] :<- [:wallet/all-tokens]
:<- [:ethereum/chain-keyword] :<- [:ethereum/chain-keyword]
(fn [[{:keys [amount symbol from to amount-error] :as transaction} (fn [[{:keys [symbol from to amount-text] :as transaction}
wallet offline? all-tokens chain]] wallet offline? all-tokens chain]]
(let [balance (get-in wallet [:accounts from :balance]) (let [balance (get-in wallet [:accounts (:address from) :balance])
token (tokens/asset-for all-tokens chain symbol)] {:keys [decimals] :as token} (tokens/asset-for all-tokens chain symbol)
(assoc (merge transaction {:keys [value error]} (wallet.db/parse-amount amount-text decimals)
(when amount amount (money/formatted->internal value symbol decimals)
(get-sufficient-funds-error balance symbol amount))) {:keys [amount-error] :as transaction-new}
(merge transaction
{:amount-error error}
(when amount
(get-sufficient-funds-error balance symbol amount)))]
(assoc transaction-new
:amount amount
:balance balance :balance balance
:token token :token (assoc token :amount (get balance (:symbol token)))
:sign-enabled? (and to :sign-enabled? (and to
(nil? amount-error) (nil? amount-error)
(not (nil? amount)) (not (nil? amount))

View File

@ -0,0 +1,67 @@
(ns status-im.ui.components.bottom-panel.views
(:require-macros [status-im.utils.views :as views])
(:require [status-im.ui.components.react :as react]
[status-im.ui.components.animation :as anim]
[reagent.core :as reagent]))
(defn hide-panel-anim
[bottom-anim-value alpha-value window-height]
(anim/start
(anim/parallel
[(anim/spring bottom-anim-value {:toValue (- window-height)
:useNativeDriver true})
(anim/timing alpha-value {:toValue 0
:duration 500
:useNativeDriver true})])))
(defn show-panel-anim
[bottom-anim-value alpha-value]
(anim/start
(anim/parallel
[(anim/spring bottom-anim-value {:toValue 40
:useNativeDriver true})
(anim/timing alpha-value {:toValue 0.4
:duration 500
:useNativeDriver true})])))
(defn bottom-panel [obj render window-height]
(let [bottom-anim-value (anim/create-value window-height)
alpha-value (anim/create-value 0)
clear-timeout (atom nil)
update? (atom nil)
current-obj (reagent/atom nil)]
(reagent/create-class
{:component-will-update (fn [_ [_ obj _ _]]
(when @clear-timeout (js/clearTimeout @clear-timeout))
(when (or (not= obj @current-obj) @update?)
(cond
@update?
(do (reset! update? false)
(show-panel-anim bottom-anim-value alpha-value))
(and @current-obj obj)
(do (reset! update? true)
(js/setTimeout #(reset! current-obj obj) 600)
(hide-panel-anim bottom-anim-value alpha-value (- window-height)))
obj
(do (reset! current-obj obj)
(show-panel-anim bottom-anim-value alpha-value))
:else
(do (reset! clear-timeout (js/setTimeout #(reset! current-obj nil) 600))
(hide-panel-anim bottom-anim-value alpha-value (- window-height))))))
:reagent-render (fn []
(when @current-obj
[react/keyboard-avoiding-view {:style {:position :absolute :top 0 :bottom 0 :left 0 :right 0}}
[react/view {:flex 1}
[react/animated-view {:flex 1 :background-color :black :opacity alpha-value}]
[react/animated-view {:style {:position :absolute
:transform [{:translateY bottom-anim-value}]
:bottom 0 :left 0 :right 0}}
[react/view {:flex 1}
[render @current-obj]]]]]))})))
(views/defview animated-bottom-panel [val signing-view]
(views/letsubs [{window-height :height} [:dimensions/window]]
[bottom-panel (when val (select-keys val [:from :contact :amount :token :approve? :message])) signing-view window-height]))

View File

@ -16,7 +16,7 @@
(= :previous type) (= :previous type)
{:padding-right 20 :padding-left 12} {:padding-right 20 :padding-left 12}
:else nil) :else nil)
{:padding-vertical 11 :border-radius 8 {:height 44 :border-radius 8
:align-items :center :justify-content :center :align-items :center :justify-content :center
:background-color (cond :background-color (cond
(#{:secondary :next :previous} type) (#{:secondary :next :previous} type)
@ -51,7 +51,7 @@
Spec: https://www.figma.com/file/cb4p8AxLtTF3q1L6JYDnKN15/Index?node-id=858%3A0" Spec: https://www.figma.com/file/cb4p8AxLtTF3q1L6JYDnKN15/Index?node-id=858%3A0"
[{:keys [label type theme disabled? on-press accessibility-label style] :or {type :main theme :blue}}] [{:keys [label type theme disabled? on-press accessibility-label style container-style] :or {type :main theme :blue}}]
(let [label (utils.label/stringify label)] (let [label (utils.label/stringify label)]
[react/touchable-opacity (cond-> {:on-press on-press [react/touchable-opacity (cond-> {:on-press on-press
:active-opacity 0.5 :active-opacity 0.5
@ -61,7 +61,7 @@
(assoc :disabled (boolean disabled?)) (assoc :disabled (boolean disabled?))
accessibility-label accessibility-label
(assoc :accessibility-label accessibility-label)) (assoc :accessibility-label accessibility-label))
[react/view {:style (style-container type disabled? theme)} [react/view {:style (merge (style-container type disabled? theme) container-style)}
[react/view {:flex-direction :row :align-items :center} [react/view {:flex-direction :row :align-items :center}
(when (= type :previous) (when (= type :previous)
[vector-icons/icon :main-icons/back {:container-style {:width 24 :height 24 :margin-right 4} [vector-icons/icon :main-icons/back {:container-style {:width 24 :height 24 :margin-right 4}

View File

@ -49,11 +49,14 @@
(def green "#44d058") ;; icon for successful inboud transaction (def green "#44d058") ;; icon for successful inboud transaction
(def green-transparent-10 (alpha green 0.1)) ;; icon for successful inboud transaction (def green-transparent-10 (alpha green 0.1)) ;; icon for successful inboud transaction
(def purple "#887af9")
(def orange "#FE8F59")
(def chat-colors ["#fa6565" (def chat-colors ["#fa6565"
"#7cda00" "#7cda00"
"#887af9" purple
"#51d0f0" "#51d0f0"
"#fe8f59" orange
"#d37ef4"]) "#d37ef4"])
(def account-colors ["#9B832F" (def account-colors ["#9B832F"
@ -61,7 +64,7 @@
"#1D806F" "#1D806F"
"#FA6565" "#FA6565"
"#7CDA00" "#7CDA00"
"#887AF9" purple
"#8B3131"]) "#8B3131"])
(def text black) (def text black)

View File

@ -196,5 +196,4 @@
(def error (def error
{:bottom-value 0 {:bottom-value 0
:color colors/red-light
:font-size 12}) :font-size 12})

View File

@ -14,6 +14,6 @@
:android (create-status-bar-style {:translucent? true :android (create-status-bar-style {:translucent? true
:bar-style "dark-content"})}) :bar-style "dark-content"})})
(styles/def status-bar-transparent (styles/def status-bar-black
{:ios (create-status-bar-style {:background-color colors/transparent}) {:ios (create-status-bar-style {:background-color colors/transparent})
:android (create-status-bar-style {:translucent? true})}) :android (create-status-bar-style {:background-color colors/black})})

View File

@ -4,7 +4,7 @@
[status-im.utils.platform :as platform])) [status-im.utils.platform :as platform]))
(defn get-config [view-id] (defn get-config [view-id]
(or (get {:recipient-qr-code {:type :transparent}} (or (get {:qr-scanner {:type :black}}
view-id) view-id)
{:type :main})) {:type :main}))
@ -26,12 +26,12 @@
network-activity-indicator-visible network-activity-indicator-visible
translucent]} translucent]}
(case type (case type
:transparent styles/status-bar-transparent :black styles/status-bar-black
styles/status-bar-default)] styles/status-bar-default)]
(when bar-style
(.setBarStyle react/status-bar-class (clj->js bar-style)) true)
(when (and background-color platform/android?) (when (and background-color platform/android?)
(.setBackgroundColor react/status-bar-class (clj->js background-color))) (.setBackgroundColor react/status-bar-class (clj->js background-color)))
(when bar-style
(.setBarStyle react/status-bar-class (clj->js bar-style)))
(when hidden (when hidden
(.setHidden react/status-bar-class (clj->js hidden))) (.setHidden react/status-bar-class (clj->js hidden)))
(when network-activity-indicator-visible (when network-activity-indicator-visible

View File

@ -31,7 +31,7 @@
(def tabs-list-data (def tabs-list-data
(->> (->>
[{:nav-stack :chat-stack [{:nav-stack :chat-stack
:content {:title (i18n/label :t/chats) :content {:title (i18n/label :t/chat)
:icon :main-icons/message} :icon :main-icons/message}
:count-subscription :chats/unread-messages-number :count-subscription :chats/unread-messages-number
:accessibility-label :home-tab-button} :accessibility-label :home-tab-button}

View File

@ -33,10 +33,10 @@
(defn nav-text (defn nav-text
([text] (nav-text nil text)) ([text] (nav-text nil text))
([{:keys [handler] :as props} text] ([{:keys [handler] :as props} text]
[react/text (utils/deep-merge {:style styles/item-text [react/touchable-highlight {:on-press (or handler #(re-frame/dispatch [:navigate-back]))}
:on-press (or handler #(re-frame/dispatch [:navigate-back]))} [react/text (utils/deep-merge {:style styles/item-text}
props) props)
text])) text]]))
(defn nav-clear-text (defn nav-clear-text
([text] (nav-clear-text nil text)) ([text] (nav-clear-text nil text))

View File

@ -3,11 +3,12 @@
[status-im.utils.styles :as styles])) [status-im.utils.styles :as styles]))
(def tooltip-container (def tooltip-container
{:position :absolute {:position :absolute
:align-items :center :align-items :center
:left 0 :pointer-events :none
:right 0 :left 0
:top 0}) :right 0
:top 0})
(styles/def bottom-tooltip-container (styles/def bottom-tooltip-container
{:position :absolute {:position :absolute
@ -28,10 +29,14 @@
(defn tooltip-text-container [color] (defn tooltip-text-container [color]
{:padding-horizontal 16 {:padding-horizontal 16
:padding-vertical 9 :padding-vertical 6
:elevation 3
:background-color color :background-color color
:elevation 2 :border-radius 8
:border-radius 8}) :shadow-radius 12
:shadow-offset {:width 0 :height 4}
:shadow-opacity 0.16
:shadow-color "rgba(0, 34, 51)"})
(def bottom-tooltip-text-container (def bottom-tooltip-text-container
{:flex-direction :row {:flex-direction :row
@ -43,8 +48,9 @@
:border-radius 8}) :border-radius 8})
(defn tooltip-text [font-size] (defn tooltip-text [font-size]
{:color colors/red {:color colors/red
:font-size font-size}) :line-height 15
:font-size font-size})
(def bottom-tooltip-text (def bottom-tooltip-text
{:color colors/white}) {:color colors/white})

View File

@ -18,9 +18,9 @@
(when label (when label
[react/view (styles/tooltip-text-container color) [react/view (styles/tooltip-text-container color)
[react/text {:style (styles/tooltip-text font-size)} label]]) [react/text {:style (styles/tooltip-text font-size)} label]])
[vector-icons/icon :icons/tooltip-triangle (assoc #_[vector-icons/icon :icons/tooltip-triangle (assoc
styles/tooltip-triangle styles/tooltip-triangle
:color color)]]])) :color color)]]]))
(views/defview bottom-tooltip-info [label on-close] (views/defview bottom-tooltip-info [label on-close]
(views/letsubs [bottom-anim-value (animation/create-value 150) (views/letsubs [bottom-anim-value (animation/create-value 150)

View File

@ -5,6 +5,5 @@
(handlers/register-handler-fx (handlers/register-handler-fx
:handle-qr-code :handle-qr-code
(fn [cofx [_ _ data]] (fn [cofx [_ data _]]
(log/debug "qr code scanned with data " data)
(models/handle-qr-code cofx data))) (models/handle-qr-code cofx data)))

View File

@ -10,7 +10,6 @@
[status-im.ui.screens.add-new.styles :as add-new.styles] [status-im.ui.screens.add-new.styles :as add-new.styles]
[status-im.ui.screens.add-new.new-chat.styles :as styles] [status-im.ui.screens.add-new.new-chat.styles :as styles]
[status-im.utils.platform :as platform] [status-im.utils.platform :as platform]
[reagent.core :as reagent]
[status-im.ui.components.list-item.views :as list-item] [status-im.ui.components.list-item.views :as list-item]
[status-im.ui.components.chat-icon.screen :as chat-icon] [status-im.ui.components.chat-icon.screen :as chat-icon]
[status-im.multiaccounts.core :as multiaccounts])) [status-im.multiaccounts.core :as multiaccounts]))
@ -21,9 +20,6 @@
:accessories [:chevron] :accessories [:chevron]
:on-press #(re-frame/dispatch [:chat.ui/start-chat (:public-key row) {:navigation-reset? true}])}]) :on-press #(re-frame/dispatch [:chat.ui/start-chat (:public-key row) {:navigation-reset? true}])}])
;;TODO workaround for https://github.com/facebook/react-native/issues/23653 (https://github.com/status-im/status-react/issues/8548)
(def tw (reagent/atom "95%"))
(views/defview new-chat [] (views/defview new-chat []
(views/letsubs [contacts [:contacts/active] (views/letsubs [contacts [:contacts/active]
new-identity [:contacts/new-identity] new-identity [:contacts/new-identity]
@ -32,12 +28,11 @@
[toolbar.view/simple-toolbar (i18n/label :t/new-chat) true] [toolbar.view/simple-toolbar (i18n/label :t/new-chat) true]
[react/view add-new.styles/new-chat-container [react/view add-new.styles/new-chat-container
[react/view add-new.styles/new-chat-input-container [react/view add-new.styles/new-chat-input-container
[react/text-input {:ref (fn [v] (js/setTimeout #(reset! tw (if v "100%" "95%")) 100)) [react/text-input {:on-change-text #(re-frame/dispatch [:new-chat/set-new-identity %])
:on-change-text #(re-frame/dispatch [:new-chat/set-new-identity %])
:on-submit-editing #(when (and new-identity (not error-message)) :on-submit-editing #(when (and new-identity (not error-message))
(re-frame/dispatch [:contact.ui/contact-code-submitted])) (re-frame/dispatch [:contact.ui/contact-code-submitted]))
:placeholder (i18n/label :t/enter-contact-code) :placeholder (i18n/label :t/enter-contact-code)
:style (add-new.styles/input @tw) :style add-new.styles/input
;; This input is fine to preserve inputs ;; This input is fine to preserve inputs
;; so its contents will not be erased ;; so its contents will not be erased
;; in onWillBlur navigation event handler ;; in onWillBlur navigation event handler
@ -46,8 +41,8 @@
:return-key-type :go}]] :return-key-type :go}]]
(when-not platform/desktop? (when-not platform/desktop?
[react/touchable-highlight {:on-press #(re-frame/dispatch [:qr-scanner.ui/scan-qr-code-pressed [react/touchable-highlight {:on-press #(re-frame/dispatch [:qr-scanner.ui/scan-qr-code-pressed
{:toolbar-title (i18n/label :t/new-contact)} {:title (i18n/label :t/new-contact)
:contact/qr-code-scanned]) :handler :contact/qr-code-scanned}])
:style add-new.styles/button-container :style add-new.styles/button-container
:accessibility-label :scan-contact-code-button} :accessibility-label :scan-contact-code-button}
[react/view [react/view

View File

@ -26,9 +26,8 @@
:padding-horizontal 16 :padding-horizontal 16
:height 52}) :height 52})
(styles/defn input [w] (styles/def input
{:padding-horizontal 14 {:padding-horizontal 14
:width w
:desktop {:height 30 :desktop {:height 30
:width "100%"} :width "100%"}
:android {:padding 0}}) :android {:padding 0}})

View File

@ -45,8 +45,8 @@
:accessibility-label :scan-qr-code-button :accessibility-label :scan-qr-code-button
:icon :main-icons/qr :icon :main-icons/qr
:on-press #(re-frame/dispatch [:qr-scanner.ui/scan-qr-code-pressed :on-press #(re-frame/dispatch [:qr-scanner.ui/scan-qr-code-pressed
{:toolbar-title (i18n/label :t/scan-qr)} {:title (i18n/label :t/scan-qr)
:handle-qr-code])}])]) :handler :handle-qr-code}])}])])
(defn add-new [] (defn add-new []
[react/view {:flex 1 :background-color :white} [react/view {:flex 1 :background-color :white}

View File

@ -26,8 +26,8 @@
(def qr-code (def qr-code
[react/touchable-highlight {:on-press #(re-frame/dispatch [:qr-scanner.ui/scan-qr-code-pressed [react/touchable-highlight {:on-press #(re-frame/dispatch [:qr-scanner.ui/scan-qr-code-pressed
{:toolbar-title (i18n/label :t/add-bootnode)} {:title (i18n/label :t/add-bootnode)
:bootnodes.callback/qr-code-scanned]) :handler :bootnodes.callback/qr-code-scanned}])
:style styles/qr-code} :style styles/qr-code}
[react/view [react/view
[vector-icons/icon :main-icons/qr {:color colors/blue}]]]) [vector-icons/icon :main-icons/qr {:color colors/blue}]]])

View File

@ -0,0 +1,59 @@
(ns status-im.ui.screens.chat.extensions.views
(:require-macros [status-im.utils.views :as views])
(:require [status-im.ui.components.react :as react]
[re-frame.core :as re-frame]
[status-im.utils.platform :as platform]
[status-im.ui.components.colors :as colors]
[status-im.ui.components.icons.vector-icons :as icons]
[status-im.ui.components.animation :as anim]
[status-im.i18n :as i18n]))
(def panel-height 264)
(defn button [showing?]
[react/touchable-highlight
{:on-press (fn [_]
(re-frame/dispatch [:chat.ui/set-chat-ui-props {:input-bottom-sheet (when-not showing? :extensions)}])
(when-not platform/desktop? (js/setTimeout #(react/dismiss-keyboard!) 100)))
:accessibility-label :show-extensions-icon}
[icons/icon :main-icons/commands {:container-style {:margin 14 :margin-right 10}
:color (if showing? colors/blue colors/gray)}]])
(defn show-panel-anim
[bottom-anim-value alpha-value]
(anim/start
(anim/parallel
[(anim/spring bottom-anim-value {:toValue 0
:useNativeDriver true})
(anim/timing alpha-value {:toValue 1
:duration 500
:useNativeDriver true})])))
(views/defview extensions-view []
(views/letsubs [bottom-anim-value (anim/create-value panel-height)
alpha-value (anim/create-value 0)]
{:component-did-mount #(show-panel-anim bottom-anim-value alpha-value)}
[react/animated-view {:style {:background-color :white
:height panel-height
:transform [{:translateY bottom-anim-value}]
:opacity alpha-value}}
[react/view
[react/touchable-highlight
{:on-press #(re-frame/dispatch [:wallet/prepare-transaction-from-chat])}
[react/view {:width 128 :height 128 :justify-content :space-between
:padding-horizontal 10 :padding-vertical 12
:background-color (colors/alpha colors/purple 0.2) :border-radius 16 :margin-left 8}
[react/view {:background-color colors/purple :width 40 :height 40 :border-radius 20 :align-items :center
:justify-content :center}
[icons/icon :main-icons/send {:color colors/white}]]
[react/text {:typography :medium} (i18n/label :t/send-transaction)]]]
;;TODO not implemented yet
#_[react/touchable-highlight
{:on-press #(re-frame/dispatch [:wallet/prepare-transaction-from-chat])}
[react/view {:width 128 :height 128 :justify-content :space-between
:padding-horizontal 10 :padding-vertical 12 :margin-top 8
:background-color (colors/alpha colors/orange 0.2) :border-radius 16 :margin-left 8}
[react/view {:background-color colors/orange :width 40 :height 40 :border-radius 20 :align-items :center
:justify-content :center}
[icons/icon :main-icons/receive {:color colors/white}]]
[react/text {:typography :medium} (i18n/label :t/request-transaction)]]]]]))

View File

@ -21,7 +21,8 @@
[status-im.utils.utils :as utils] [status-im.utils.utils :as utils]
[status-im.utils.config :as config] [status-im.utils.config :as config]
[taoensso.timbre :as log] [taoensso.timbre :as log]
[status-im.ui.screens.chat.stickers.views :as stickers])) [status-im.ui.screens.chat.stickers.views :as stickers]
[status-im.ui.screens.chat.extensions.views :as extensions]))
(defview basic-text-input [{:keys [set-container-width-fn height single-line-input?]}] (defview basic-text-input [{:keys [set-container-width-fn height single-line-input?]}]
(letsubs [input-text [:chats/current-chat-input-text] (letsubs [input-text [:chats/current-chat-input-text]
@ -35,7 +36,7 @@
:editable (not cooldown-enabled?) :editable (not cooldown-enabled?)
:blur-on-submit false :blur-on-submit false
:on-focus #(re-frame/dispatch [:chat.ui/set-chat-ui-props {:input-focused? true :on-focus #(re-frame/dispatch [:chat.ui/set-chat-ui-props {:input-focused? true
:show-stickers? false :input-bottom-sheet nil
:messages-focused? false}]) :messages-focused? false}])
:on-blur #(re-frame/dispatch [:chat.ui/set-chat-ui-props {:input-focused? false}]) :on-blur #(re-frame/dispatch [:chat.ui/set-chat-ui-props {:input-focused? false}])
:on-submit-editing #(when single-line-input? :on-submit-editing #(when single-line-input?
@ -66,7 +67,7 @@
:editable (not cooldown-enabled?) :editable (not cooldown-enabled?)
:blur-on-submit false :blur-on-submit false
:on-focus #(re-frame/dispatch [:chat.ui/set-chat-ui-props {:input-focused? true :on-focus #(re-frame/dispatch [:chat.ui/set-chat-ui-props {:input-focused? true
:show-stickers? false :input-bottom-sheet nil
:messages-focused? false}]) :messages-focused? false}])
:on-blur #(re-frame/dispatch [:chat.ui/set-chat-ui-props {:input-focused? false}]) :on-blur #(re-frame/dispatch [:chat.ui/set-chat-ui-props {:input-focused? false}])
:submit-shortcut {:key "Enter"} :submit-shortcut {:key "Enter"}
@ -187,14 +188,13 @@
mainnet? [:mainnet?] mainnet? [:mainnet?]
input-text [:chats/current-chat-input-text] input-text [:chats/current-chat-input-text]
result-box [:chats/current-chat-ui-prop :result-box] result-box [:chats/current-chat-ui-prop :result-box]
show-stickers? [:chats/current-chat-ui-prop :show-stickers?] input-bottom-sheet [:chats/current-chat-ui-prop :input-bottom-sheet]
state-text (reagent/atom "")] state-text (reagent/atom "")]
{:component-will-unmount #(when platform/desktop? {:component-will-unmount #(when platform/desktop?
(re-frame/dispatch [:chat.ui/set-chat-input-text @state-text])) (re-frame/dispatch [:chat.ui/set-chat-input-text @state-text]))
:component-did-mount #(when-not (string/blank? input-text) (reset! state-text input-text))} :component-did-mount #(when-not (string/blank? input-text) (reset! state-text input-text))}
(let [single-line-input? (:singleLineInput result-box) (let [single-line-input? (:singleLineInput result-box)
component (reagent/current-component)
set-text #(reset! state-text %) set-text #(reset! state-text %)
input-text-empty? (if platform/desktop? input-text-empty? (if platform/desktop?
(string/blank? state-text) (string/blank? state-text)
@ -209,9 +209,10 @@
[react/view {:style style/input-container} [react/view {:style style/input-container}
[input-view {:single-line-input? single-line-input? :set-text set-text :state-text state-text}] [input-view {:single-line-input? single-line-input? :set-text set-text :state-text state-text}]
(when (and input-text-empty? mainnet?) (when (and input-text-empty? mainnet?)
[stickers/button show-stickers?]) [stickers/button (= :stickers input-bottom-sheet)])
(if input-text-empty? (when (and input-text-empty?) ;;TODO show only for 1-1 chats?
[commands-button] [extensions/button (= :extensions input-bottom-sheet)])
(when-not input-text-empty?
(if platform/desktop? (if platform/desktop?
[send-button/send-button-view {:input-text @state-text} [send-button/send-button-view {:input-text @state-text}
#(do #(do

View File

@ -15,7 +15,7 @@
{:on-press (fn [_] {:on-press (fn [_]
(re-frame/dispatch (re-frame/dispatch
[:chat.ui/set-chat-ui-props {:messages-focused? true [:chat.ui/set-chat-ui-props {:messages-focused? true
:show-stickers? false}]) :input-bottom-sheet nil}])
(react/dismiss-keyboard!))} (react/dismiss-keyboard!))}
[react/view style/datemark-mobile [react/view style/datemark-mobile
[react/text {:style style/datemark-text} [react/text {:style style/datemark-text}

View File

@ -296,7 +296,7 @@
(when (and (= content-type constants/content-type-sticker) (:pack content)) (when (and (= content-type constants/content-type-sticker) (:pack content))
(re-frame/dispatch [:stickers/open-sticker-pack (:pack content)])) (re-frame/dispatch [:stickers/open-sticker-pack (:pack content)]))
(re-frame/dispatch [:chat.ui/set-chat-ui-props {:messages-focused? true (re-frame/dispatch [:chat.ui/set-chat-ui-props {:messages-focused? true
:show-stickers? false}]) :input-bottom-sheet nil}])
(when-not platform/desktop? (when-not platform/desktop?
(react/dismiss-keyboard!))))) (react/dismiss-keyboard!)))))
:on-long-press #(when (or (= content-type constants/content-type-text) :on-long-press #(when (or (= content-type constants/content-type-text)

View File

@ -19,14 +19,14 @@
(def icon-container (+ (* icon-horizontal-margin 2) icon-size)) (def icon-container (+ (* icon-horizontal-margin 2) icon-size))
(def scroll-x (reagent/atom 0)) (def scroll-x (reagent/atom 0))
(defn button [show-stickers?] (defn button [stickers-showing?]
[react/touchable-highlight [react/touchable-highlight
{:on-press (fn [_] {:on-press (fn [_]
(re-frame/dispatch [:chat.ui/set-chat-ui-props {:show-stickers? (not show-stickers?)}]) (re-frame/dispatch [:chat.ui/set-chat-ui-props {:input-bottom-sheet (when-not stickers-showing? :stickers)}])
(when-not platform/desktop? (js/setTimeout #(react/dismiss-keyboard!) 100))) (when-not platform/desktop? (js/setTimeout #(react/dismiss-keyboard!) 100)))
:accessibility-label :show-stickers-icon} :accessibility-label :show-stickers-icon}
[vector-icons/icon :main-icons/stickers {:container-style {:margin 14 :margin-right 6} [vector-icons/icon :main-icons/stickers {:container-style {:margin 14 :margin-right 6}
:color (if show-stickers? colors/blue colors/gray)}]]) :color (if stickers-showing? colors/blue colors/gray)}]])
(defn- no-stickers-yet-panel [] (defn- no-stickers-yet-panel []
[react/view {:style {:flex 1 :align-items :center :justify-content :center}} [react/view {:style {:flex 1 :align-items :center :justify-content :center}}

View File

@ -26,7 +26,8 @@
[status-im.ui.screens.profile.tribute-to-talk.views [status-im.ui.screens.profile.tribute-to-talk.views
:as :as
tribute-to-talk.views] tribute-to-talk.views]
[status-im.utils.platform :as platform]) [status-im.utils.platform :as platform]
[status-im.ui.screens.chat.extensions.views :as extensions])
(:require-macros [status-im.utils.views :refer [defview letsubs]])) (:require-macros [status-im.utils.views :refer [defview letsubs]]))
(defn add-contact-bar (defn add-contact-bar
@ -287,7 +288,7 @@
:on-press (fn [_] :on-press (fn [_]
(re-frame/dispatch (re-frame/dispatch
[:chat.ui/set-chat-ui-props {:messages-focused? true [:chat.ui/set-chat-ui-props {:messages-focused? true
:show-stickers? false}]) :input-bottom-sheet nil}])
(react/dismiss-keyboard!))} (react/dismiss-keyboard!))}
[react/view (style/intro-header-container height intro-status no-messages) [react/view (style/intro-header-container height intro-status no-messages)
;; Icon section ;; Icon section
@ -396,7 +397,7 @@
(letsubs [{:keys [public? chat-id chat-name show-input? group-chat contact] :as current-chat} (letsubs [{:keys [public? chat-id chat-name show-input? group-chat contact] :as current-chat}
[:chats/current-chat] [:chats/current-chat]
current-chat-id [:chats/current-chat-id] current-chat-id [:chats/current-chat-id]
show-stickers? [:chats/current-chat-ui-prop :show-stickers?] input-bottom-sheet [:chats/current-chat-ui-prop :input-bottom-sheet]
two-pane-ui-enabled? [:two-pane-ui-enabled?] two-pane-ui-enabled? [:two-pane-ui-enabled?]
anim-translate-y (animation/create-value anim-translate-y (animation/create-value
(if two-pane-ui-enabled? 0 connectivity/neg-connectivity-bar-height))] (if two-pane-ui-enabled? 0 connectivity/neg-connectivity-bar-height))]
@ -432,8 +433,12 @@
[messages-view current-chat modal?])]] [messages-view current-chat modal?])]]
(when show-input? (when show-input?
[input/container]) [input/container])
(when show-stickers? (case input-bottom-sheet
[stickers/stickers-view])])) :stickers
[stickers/stickers-view]
:extensions
[extensions/extensions-view]
nil)]))
(defview chat [] (defview chat []
[chat-root false]) [chat-root false])

View File

@ -6,7 +6,6 @@
status-im.transport.db status-im.transport.db
status-im.multiaccounts.db status-im.multiaccounts.db
status-im.contact.db status-im.contact.db
status-im.ui.screens.qr-scanner.db
status-im.ui.screens.group.db status-im.ui.screens.group.db
status-im.chat.specs status-im.chat.specs
status-im.ui.screens.profile.db status-im.ui.screens.profile.db
@ -183,6 +182,8 @@
(spec/def :popover/popover (spec/nilable map?)) (spec/def :popover/popover (spec/nilable map?))
(spec/def :wallet/prepare-transaction (spec/nilable map?))
(spec/def ::db (spec/keys :opt [:contacts/contacts (spec/def ::db (spec/keys :opt [:contacts/contacts
:contacts/new-identity :contacts/new-identity
:contacts/new-identity-error :contacts/new-identity-error
@ -248,6 +249,7 @@
:bottom-sheet/view :bottom-sheet/view
:bottom-sheet/options :bottom-sheet/options
:wallet/custom-token-screen :wallet/custom-token-screen
:wallet/prepare-transaction
:signing/in-progress? :signing/in-progress?
:signing/queue :signing/queue
:signing/sign :signing/sign
@ -282,9 +284,6 @@
:navigation/navigation-stack :navigation/navigation-stack
:navigation/prev-tab-view-id :navigation/prev-tab-view-id
:navigation/prev-view-id :navigation/prev-view-id
:qr/qr-codes
:qr/qr-modal
:qr/current-qr-context
:chat/chats :chat/chats
:chat/current-chat-id :chat/current-chat-id
:chat/chat-id :chat/chat-id

View File

@ -27,7 +27,7 @@
:on-change-text #(re-frame/dispatch [:set :new-chat-name %]) :on-change-text #(re-frame/dispatch [:set :new-chat-name %])
:default-value new-group-name :default-value new-group-name
:placeholder (i18n/label :t/set-a-topic) :placeholder (i18n/label :t/set-a-topic)
:style (add-new.styles/input "100%") :style add-new.styles/input
:accessibility-label :chat-name-input}]]) :accessibility-label :chat-name-input}]])
(defn- render-contact [contact] (defn- render-contact [contact]

View File

@ -40,8 +40,8 @@
:accessibility-label :scan-qr-code-button :accessibility-label :scan-qr-code-button
:icon :main-icons/qr :icon :main-icons/qr
:on-press #(hide-sheet-and-dispatch [:qr-scanner.ui/scan-qr-code-pressed :on-press #(hide-sheet-and-dispatch [:qr-scanner.ui/scan-qr-code-pressed
{:toolbar-title (i18n/label :t/scan-qr)} {:title (i18n/label :t/scan-qr)
:handle-qr-code])}] :handler :handle-qr-code}])}]
[list-item/list-item [list-item/list-item
{:theme :action {:theme :action
:title :t/invite-friends :title :t/invite-friends

View File

@ -34,8 +34,8 @@
(def qr-code (def qr-code
[react/touchable-highlight {:on-press #(re-frame/dispatch [:qr-scanner.ui/scan-qr-code-pressed [react/touchable-highlight {:on-press #(re-frame/dispatch [:qr-scanner.ui/scan-qr-code-pressed
{:toolbar-title (i18n/label :t/add-mailserver)} {:title (i18n/label :t/add-mailserver)
:mailserver.callback/qr-code-scanned]) :handler :mailserver.callback/qr-code-scanned}])
:style styles/qr-code} :style styles/qr-code}
[react/view [react/view
[vector-icons/icon :main-icons/qr {:color colors/blue}]]]) [vector-icons/icon :main-icons/qr {:color colors/blue}]]])

View File

@ -1,8 +0,0 @@
(ns status-im.ui.screens.qr-scanner.db
(:require [cljs.spec.alpha :as s]))
;;on scan qr
(s/def :qr/qr-codes (s/nilable map?))
;;used in qr modal screen
(s/def :qr/qr-modal (s/nilable map?))
(s/def :qr/current-qr-context (s/nilable map?))

View File

@ -1,77 +1,11 @@
(ns status-im.ui.screens.qr-scanner.styles (ns status-im.ui.screens.qr-scanner.styles)
(:require-macros [status-im.utils.styles :as styles])
(:require [status-im.ui.components.colors :as colors]
[status-im.ui.components.toolbar.styles :as toolbar.styles]))
(def barcode-scanner-container (def viewfinder-port
{:flex 1 {:position :absolute
:background-color :white}) :left 0
:top 0
(styles/def barcode-scanner :bottom 0
{:flex 1 :right 0
:elevation -10 :align-items :center
:android {:marginTop 10}}) :justify-content :center
:flex 1})
(def rectangle-container
{:position :absolute
:left 0
:top toolbar.styles/toolbar-height
:bottom 0
:right 0
:flex 1
:align-items :center
:justify-content :center
:background-color :transparent})
(def rectangle
{:height 250
:width 250
:background-color :transparent})
(def corner-left-top
{:position :absolute
:left 0
:top 0
:width 56
:height 56})
(def corner-right-top
{:position :absolute
:right 0
:top 0
:width 56
:height 56})
(def corner-right-bottom
{:position :absolute
:right 0
:bottom 0
:width 56
:height 56})
(def corner-left-bottom
{:position :absolute
:left 0
:bottom 0
:width 56
:height 56})
(def import-button
{:position :absolute
:right 16
:flex 1
:height 50
:align-items :center})
(def import-button-content
{:flex 1
:flex-direction :row
:height 50
:align-items :center
:align-self :center})
(def import-text
{:flex 1
:flex-direction :column
:color colors/white
:margin-left 8})

View File

@ -1,51 +1,58 @@
(ns status-im.ui.screens.qr-scanner.views (ns status-im.ui.screens.qr-scanner.views
(:require-macros [status-im.utils.views :refer [defview letsubs]]) (:require-macros [status-im.utils.views :refer [defview letsubs]])
(:require [reagent.core :as reagent] (:require [re-frame.core :as re-frame]
[re-frame.core :as re-frame]
[status-im.i18n :as i18n] [status-im.i18n :as i18n]
[status-im.ui.components.react :as react]
[status-im.ui.components.camera :as camera] [status-im.ui.components.camera :as camera]
[status-im.ui.components.toolbar.view :as toolbar] [status-im.ui.components.react :as react]
[status-im.ui.components.toolbar.view :as topbar]
[status-im.ui.screens.qr-scanner.styles :as styles] [status-im.ui.screens.qr-scanner.styles :as styles]
[status-im.ui.components.toolbar.actions :as actions])) [status-im.ui.components.colors :as colors]))
(defview qr-scanner-toolbar [title identifier] (defn- topbar [camera-flashlight {:keys [title] :as opts}]
[react/view [topbar/toolbar
[toolbar/toolbar {:style {:background-color :white}} {:transparent? true}
[toolbar/nav-button (actions/back [topbar/nav-text
#(do {:style {:color colors/white :margin-left 16}
(re-frame/dispatch [:qr-scanner.callback/scan-qr-code-cancel identifier]) :handler #(re-frame/dispatch [:qr-scanner.callback/scan-qr-code-cancel opts])}
(re-frame/dispatch [:navigate-back])))] (i18n/label :t/cancel)]
[toolbar/content-title title]]]) [topbar/content-title {:color :white}
(or title (i18n/label :t/scan-qr))]
#_[topbar/actions [{:icon (if (= :on camera-flashlight)
:main-icons/flash-active
:main-icons/flash-inactive)
:icon-opts {:color :white}
:handler #(re-frame/dispatch [:wallet/toggle-flashlight])}]]])
(defn on-barcode-read [identifier data] (defn corner [border1 border2 corner]
(re-frame/dispatch [:qr-scanner.callback/scan-qr-code-success identifier (camera/get-qr-code-data data)])) [react/view (assoc {:border-color :white :width 60 :height 60} border1 5 border2 5 corner 32)])
;; identifier is passed via navigation params instead of subs in order to ensure (defn- viewfinder [size]
;; that two separate instances of `qr-scanner` screen can work simultaneously [react/view {:style styles/viewfinder-port}
(defview qr-scanner [{identifier :current-qr-context} screen-focused?] [react/view {:width size :height size :justify-content :space-between}
(letsubs [camera-initialized? (reagent/atom false) [react/view {:flex-direction :row :justify-content :space-between}
barcode-read? (reagent/atom false)] [corner :border-top-width :border-left-width :border-top-left-radius]
[react/view styles/barcode-scanner-container [corner :border-top-width :border-right-width :border-top-right-radius]]
[qr-scanner-toolbar (or (:toolbar-title identifier) (i18n/label :t/scan-qr)) identifier] [react/view {:flex-direction :row :justify-content :space-between}
;; camera component should be hidden if screen is not shown [corner :border-bottom-width :border-left-width :border-bottom-left-radius]
;; otherwise another screen with camera from a different stack [corner :border-bottom-width :border-right-width :border-bottom-right-radius]]]])
;; will not work properly
(when @screen-focused? (defn on-barcode-read [opts data]
[camera/camera {:onBarCodeRead #(if (:multiple? identifier) (re-frame/dispatch [:qr-scanner.callback/scan-qr-code-success opts (camera/get-qr-code-data data)]))
(on-barcode-read identifier %)
(when-not @barcode-read? (defview qr-scanner []
(do (reset! barcode-read? true) (letsubs [read-once? (atom false)
(on-barcode-read identifier %)))) {:keys [height width]} [:dimensions/window]
:ref #(reset! camera-initialized? true) camera-flashlight [:wallet.send/camera-flashlight]
:style styles/barcode-scanner}]) opts [:get-screen-params]]
[react/view styles/rectangle-container [react/view {:style {:flex 1 :background-color colors/black}}
[react/view styles/rectangle [topbar camera-flashlight opts]
[react/image {:source {:uri :corner_left_top} [react/with-activity-indicator
:style styles/corner-left-top}] {}
[react/image {:source {:uri :corner_right_top} [camera/camera
:style styles/corner-right-top}] {:style {:flex 1}
[react/image {:source {:uri :corner_right_bottom} ;:torchMode (camera/set-torch camera-flashlight)
:style styles/corner-right-bottom}] :captureAudio false
[react/image {:source {:uri :corner_left_bottom} :onBarCodeRead #(when-not @read-once?
:style styles/corner-left-bottom}]]]])) (reset! read-once? true)
(on-barcode-read opts %))}]]
[viewfinder (int (* 2 (/ (min height width) 3)))]]))

View File

@ -3,6 +3,5 @@
(def browser-stack (def browser-stack
{:name :browser-stack {:name :browser-stack
:screens [:open-dapp :screens [:open-dapp
:browser :browser]
:qr-scanner]
:config {:initialRouteName :open-dapp}}) :config {:initialRouteName :open-dapp}})

View File

@ -7,7 +7,6 @@
:select-chat :select-chat
:profile :profile
:new :new
:qr-scanner
:take-picture :take-picture
:new-group :new-group
:add-participants-toggle-list :add-participants-toggle-list

View File

@ -11,4 +11,6 @@
:welcome :welcome
:keycard-welcome :keycard-welcome
:new-chat :new-chat
:new-public-chat]) :new-public-chat
:contact-code
:qr-scanner])

View File

@ -35,7 +35,6 @@
:mobile-network-settings :mobile-network-settings
:backup-seed :backup-seed
:tribute-to-talk :tribute-to-talk
:qr-scanner
:my-profile-ext-settings] :my-profile-ext-settings]
config/hardwallet-enabled? config/hardwallet-enabled?

View File

@ -1,7 +1,6 @@
(ns status-im.ui.screens.routing.screens (ns status-im.ui.screens.routing.screens
(:require [status-im.ui.screens.about-app.views :as about-app] (:require [status-im.ui.screens.about-app.views :as about-app]
[status-im.ui.screens.multiaccounts.login.views :as login] [status-im.ui.screens.multiaccounts.login.views :as login]
[status-im.ui.screens.multiaccounts.recover.views :as multiaccounts.recover]
[status-im.ui.screens.multiaccounts.views :as multiaccounts] [status-im.ui.screens.multiaccounts.views :as multiaccounts]
[status-im.ui.screens.add-new.new-chat.views :as new-chat] [status-im.ui.screens.add-new.new-chat.views :as new-chat]
[status-im.ui.screens.add-new.new-public-chat.view :as new-public-chat] [status-im.ui.screens.add-new.new-public-chat.view :as new-public-chat]
@ -59,8 +58,6 @@
[status-im.ui.screens.stickers.views :as stickers] [status-im.ui.screens.stickers.views :as stickers]
[status-im.ui.screens.wallet.collectibles.views :as collectibles] [status-im.ui.screens.wallet.collectibles.views :as collectibles]
[status-im.ui.screens.wallet.components.views :as wallet.components] [status-im.ui.screens.wallet.components.views :as wallet.components]
[status-im.ui.screens.wallet.request.views :as request]
[status-im.ui.screens.wallet.send.views :as send]
[status-im.ui.screens.wallet.settings.views :as wallet-settings] [status-im.ui.screens.wallet.settings.views :as wallet-settings]
[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.custom-tokens.views :as custom-tokens] [status-im.ui.screens.wallet.custom-tokens.views :as custom-tokens]
@ -124,7 +121,7 @@
:profile profile.contact/profile :profile profile.contact/profile
:new add-new/add-new :new add-new/add-new
:new-chat [:modal new-chat/new-chat] :new-chat [:modal new-chat/new-chat]
:qr-scanner qr-scanner/qr-scanner :qr-scanner [:modal qr-scanner/qr-scanner]
:new-group group/new-group :new-group group/new-group
:add-participants-toggle-list group/add-participants-toggle-list :add-participants-toggle-list group/add-participants-toggle-list
:contact-toggle-list group/contact-toggle-list :contact-toggle-list group/contact-toggle-list
@ -140,14 +137,7 @@
:wallet wallet.accounts/accounts-overview :wallet wallet.accounts/accounts-overview
:wallet-account wallet.account/account :wallet-account wallet.account/account
:collectibles-list collectibles/collectibles-list :collectibles-list collectibles/collectibles-list
:contact-code wallet.components/contact-code :contact-code [:modal wallet.components/contact-code]
:wallet-send-transaction send/send-transaction
:recent-recipients wallet.components/recent-recipients
:select-account wallet.components/accounts
:recipient-qr-code wallet.components/recipient-qr-code
:wallet-send-assets wallet.components/send-assets
:wallet-send-transaction-request request/send-transaction-request
:wallet-request-assets wallet.components/request-assets
:wallet-transaction-details wallet-transactions/transaction-details :wallet-transaction-details wallet-transactions/transaction-details
:wallet-settings-hook wallet-settings/settings-hook :wallet-settings-hook wallet-settings/settings-hook
:wallet-settings-assets wallet-settings/manage-assets :wallet-settings-assets wallet-settings/manage-assets

View File

@ -10,19 +10,6 @@
:account-settings :account-settings
:collectibles-list :collectibles-list
:wallet-onboarding-setup :wallet-onboarding-setup
:contact-code
{:name :send-transaction-stack
:screens [:wallet-send-transaction
:recent-recipients
:select-account
:enter-pin-sign
:hardwallet-connect-sign
:recipient-qr-code
:wallet-send-assets]}
{:name :request-transaction-stack
:screens [:wallet-send-transaction-request
:wallet-request-assets
:recent-recipients]}
:wallet-transaction-details :wallet-transaction-details
:wallet-settings-hook :wallet-settings-hook
:wallet-settings-assets :wallet-settings-assets

View File

@ -7,7 +7,6 @@
:justify-content :space-between :justify-content :space-between
:padding-top 16 :padding-top 16
:padding-left 16 :padding-left 16
:padding-right 24
:margin-bottom 11}) :margin-bottom 11})
(def message-header (def message-header

View File

@ -4,8 +4,6 @@
[re-frame.core :as re-frame] [re-frame.core :as re-frame]
[status-im.multiaccounts.core :as multiaccounts] [status-im.multiaccounts.core :as multiaccounts]
[status-im.ui.components.colors :as colors] [status-im.ui.components.colors :as colors]
[status-im.ui.components.animation :as anim]
[reagent.core :as reagent]
[status-im.ui.components.list-item.views :as list-item] [status-im.ui.components.list-item.views :as list-item]
[status-im.ui.components.button :as button] [status-im.ui.components.button :as button]
[status-im.ui.components.copyable-text :as copyable-text] [status-im.ui.components.copyable-text :as copyable-text]
@ -22,55 +20,31 @@
[status-im.ui.screens.signing.styles :as styles] [status-im.ui.screens.signing.styles :as styles]
[status-im.react-native.resources :as resources] [status-im.react-native.resources :as resources]
[status-im.ui.screens.hardwallet.pin.views :as pin.views] [status-im.ui.screens.hardwallet.pin.views :as pin.views]
[status-im.ui.components.bottom-panel.views :as bottom-panel]
[status-im.utils.utils :as utils])) [status-im.utils.utils :as utils]))
(defn hide-panel-anim
[bottom-anim-value alpha-value window-height]
(anim/start
(anim/parallel
[(anim/spring bottom-anim-value {:toValue (- window-height)
:useNativeDriver true})
(anim/timing alpha-value {:toValue 0
:duration 500
:useNativeDriver true})])))
(defn show-panel-anim
[bottom-anim-value alpha-value]
(anim/start
(anim/parallel
[(anim/spring bottom-anim-value {:toValue 40
:useNativeDriver true})
(anim/timing alpha-value {:toValue 0.4
:duration 500
:useNativeDriver true})])))
(defn separator [] (defn separator []
[react/view {:height 1 :background-color colors/gray-lighter}]) [react/view {:height 1 :background-color colors/gray-lighter}])
(defn displayed-name [contact] (defn displayed-name [contact]
(if (or (:preferred-name contact) (:name contact)) (if (or (:preferred-name contact) (:name contact))
(multiaccounts/displayed-name contact) (multiaccounts/displayed-name contact)
(:address contact))) (utils/get-shortened-checksum-address (:address contact))))
(defn contact-item [title contact] (defn contact-item [title contact]
[list-item/list-item [list-item/list-item
{:title-prefix title {:title title
:title-prefix-width 45 :title-prefix-width 45
:type :small :type :small
:title :accessories
[copyable-text/copyable-text-view [[copyable-text/copyable-text-view
{:copied-text (displayed-name contact)} {:copied-text (displayed-name contact)}
[react/text [react/text
{:ellipsize-mode :middle {:ellipsize-mode :middle
:number-of-lines 1 :number-of-lines 1
:style {:color colors/gray :style {:font-family "monospace"
:font-family "monospace" :line-height 22}}
;; since this goes in list-item title (displayed-name contact)]]]}])
;; which has design constraints
;; specified in figma spec,
;; better to do this
:line-height 22}}
(displayed-name contact)]]}])
(defn token-item [{:keys [icon color] :as token} display-symbol] (defn token-item [{:keys [icon color] :as token} display-symbol]
(when token (when token
@ -107,9 +81,10 @@
[{:style {:color colors/black}} (displayed-name contact)]] [{:style {:color colors/black}} (displayed-name contact)]]
[react/text {:style {:margin-top 6 :color colors/gray}} [react/text {:style {:margin-top 6 :color colors/gray}}
(str fee " " fee-display-symbol " " (string/lower-case (i18n/label :t/network-fee)))])] (str fee " " fee-display-symbol " " (string/lower-case (i18n/label :t/network-fee)))])]
[react/touchable-highlight (when-not in-progress? {:on-press #(re-frame/dispatch [:signing.ui/cancel-is-pressed])}) [button/button (merge {:type :secondary
[react/view {:padding 6} :container-style {:padding-horizontal 24}
[react/text {:style {:color colors/blue}} (i18n/label :t/cancel)]]]]) :label (i18n/label :t/cancel)}
(when-not in-progress? {:on-press #(re-frame/dispatch [:signing.ui/cancel-is-pressed])}))]])
(views/defview keycard-pin-view [] (views/defview keycard-pin-view []
(views/letsubs [pin [:hardwallet/pin] (views/letsubs [pin [:hardwallet/pin]
@ -268,6 +243,13 @@
{:content (fn [] [sheets/fee-bottom-sheet fee-display-symbol]) {:content (fn [] [sheets/fee-bottom-sheet fee-display-symbol])
:content-height 270}])}])) :content-height 270}])}]))
(views/defview network-item []
(views/letsubs [network-name [:network-name]]
[list-item/list-item
{:title :t/network
:type :small
:accessories [[react/text network-name]]}]))
(views/defview sheet [{:keys [from contact amount token approve?] :as tx}] (views/defview sheet [{:keys [from contact amount token approve?] :as tx}]
(views/letsubs [fee [:signing/fee] (views/letsubs [fee [:signing/fee]
sign [:signing/sign] sign [:signing/sign]
@ -275,7 +257,8 @@
{:keys [amount-error gas-error]} [:signing/amount-errors (:address from)] {:keys [amount-error gas-error]} [:signing/amount-errors (:address from)]
keycard-multiaccount? [:keycard-multiaccount?] keycard-multiaccount? [:keycard-multiaccount?]
prices [:prices] prices [:prices]
wallet-currency [:wallet/currency]] wallet-currency [:wallet/currency]
mainnet? [:mainnet?]]
(let [display-symbol (wallet.utils/display-symbol token) (let [display-symbol (wallet.utils/display-symbol token)
fee-display-symbol (wallet.utils/display-symbol (tokens/native-currency chain))] fee-display-symbol (wallet.utils/display-symbol (tokens/native-currency chain))]
[react/view styles/sheet [react/view styles/sheet
@ -285,6 +268,10 @@
[react/view {:padding-top 20} [react/view {:padding-top 20}
[password-view sign]] [password-view sign]]
[react/view [react/view
(when-not mainnet?
[react/view
[network-item]
[separator]])
[contact-item (i18n/label :t/from) from] [contact-item (i18n/label :t/from) from]
[separator] [separator]
[contact-item (i18n/label :t/to) contact] [contact-item (i18n/label :t/to) contact]
@ -300,47 +287,11 @@
:disabled? (or amount-error gas-error) :disabled? (or amount-error gas-error)
:label :t/sign-with-password}])]])]))) :label :t/sign-with-password}])]])])))
(defn signing-view [tx window-height]
(let [bottom-anim-value (anim/create-value window-height)
alpha-value (anim/create-value 0)
clear-timeout (atom nil)
current-tx (reagent/atom nil)
update? (reagent/atom nil)]
(reagent/create-class
{:component-will-update (fn [_ [_ tx _]]
(when @clear-timeout (js/clearTimeout @clear-timeout))
(cond
@update?
(do (reset! update? false)
(show-panel-anim bottom-anim-value alpha-value))
(and @current-tx tx)
(do (reset! update? true)
(js/setTimeout #(reset! current-tx tx) 600)
(hide-panel-anim bottom-anim-value alpha-value (- window-height)))
tx
(do (reset! current-tx tx)
(show-panel-anim bottom-anim-value alpha-value))
:else
(do (reset! clear-timeout (js/setTimeout #(reset! current-tx nil) 500))
(hide-panel-anim bottom-anim-value alpha-value (- window-height)))))
:reagent-render (fn []
(when @current-tx
[react/keyboard-avoiding-view {:style {:position :absolute :top 0 :bottom 0 :left 0 :right 0}}
[react/view {:flex 1}
[react/animated-view {:flex 1 :background-color :black :opacity alpha-value}]
[react/animated-view {:style {:position :absolute
:transform [{:translateY bottom-anim-value}]
:bottom 0 :left 0 :right 0}}
[react/view {:flex 1}
(if (:message @current-tx)
[message-sheet]
[sheet @current-tx])]]]]))})))
(views/defview signing [] (views/defview signing []
(views/letsubs [tx [:signing/tx] (views/letsubs [tx [:signing/tx]]
{window-height :height} [:dimensions/window]] [bottom-panel/animated-bottom-panel
;;we use select-keys here because we don't want to update view if other keys in map is changed ;;we use select-keys here because we don't want to update view if other keys in map are changed
[signing-view (when tx (select-keys tx [:from :contact :amount :token :approve? :message])) window-height])) (when tx (select-keys tx [:from :contact :amount :token :approve? :message]))
#(if (:message %)
[message-sheet]
[sheet %])]))

View File

@ -20,6 +20,7 @@
[status-im.ui.screens.popover.views :as popover] [status-im.ui.screens.popover.views :as popover]
[status-im.ui.screens.multiaccounts.recover.views :as recover.views] [status-im.ui.screens.multiaccounts.recover.views :as recover.views]
[status-im.utils.dimensions :as dimensions] [status-im.utils.dimensions :as dimensions]
[status-im.ui.screens.wallet.send.views :as wallet]
status-im.ui.screens.wallet.collectibles.etheremon.views status-im.ui.screens.wallet.collectibles.etheremon.views
status-im.ui.screens.wallet.collectibles.cryptostrikers.views status-im.ui.screens.wallet.collectibles.cryptostrikers.views
status-im.ui.screens.wallet.collectibles.cryptokitties.views status-im.ui.screens.wallet.collectibles.cryptokitties.views
@ -167,6 +168,7 @@
;; see https://reactnavigation.org/docs/en/state-persistence.html#development-mode ;; see https://reactnavigation.org/docs/en/state-persistence.html#development-mode
:persistNavigationState (when js/goog.DEBUG persist-state) :persistNavigationState (when js/goog.DEBUG persist-state)
:loadNavigationState (when js/goog.DEBUG load-state)}] :loadNavigationState (when js/goog.DEBUG load-state)}]
[wallet/prepare-transaction]
[signing/signing] [signing/signing]
[bottom-sheet] [bottom-sheet]
[popover/popover]]]))}))) [popover/popover]]]))})))

View File

@ -39,7 +39,7 @@
[icons/icon icon {:color colors/white}] [icons/icon icon {:color colors/white}]
[react/text {:style {:margin-left 8 :color colors/white}} label]]]]) [react/text {:style {:margin-left 8 :color colors/white}} label]]]])
(views/defview account-card [{:keys [address color]}] (views/defview account-card [{:keys [address color] :as account}]
(views/letsubs [currency [:wallet/currency] (views/letsubs [currency [:wallet/currency]
portfolio-value [:account-portfolio-value address] portfolio-value [:account-portfolio-value address]
window-width [:dimensions/window-width]] window-width [:dimensions/window-width]]
@ -62,9 +62,15 @@
:accessibility-label :share-wallet-address-icon}]]] :accessibility-label :share-wallet-address-icon}]]]
[react/view {:height 52 :background-color colors/black-transparent-20 [react/view {:height 52 :background-color colors/black-transparent-20
:border-bottom-right-radius 8 :border-bottom-left-radius 8 :flex-direction :row} :border-bottom-right-radius 8 :border-bottom-left-radius 8 :flex-direction :row}
[button (i18n/label :t/wallet-send) :main-icons/send #(re-frame/dispatch [:navigate-to :wallet-send-transaction address])] [button
(i18n/label :t/wallet-send)
:main-icons/send
#(re-frame/dispatch [:wallet/prepare-transaction-from-wallet account])]
[react/view {:style styles/divider}] [react/view {:style styles/divider}]
[button (i18n/label :t/receive) :main-icons/receive #(re-frame/dispatch [:show-popover {:view :share-account :address address}])]]])) [button
(i18n/label :t/receive)
:main-icons/receive
#(re-frame/dispatch [:show-popover {:view :share-account :address address}])]]]))
(defn render-collectible [address] (defn render-collectible [address]
(fn [{:keys [name icon amount] :as collectible}] (fn [{:keys [name icon amount] :as collectible}]

View File

@ -35,20 +35,22 @@
:accessibility-label :wallet-backup-recovery-title :accessibility-label :wallet-backup-recovery-title
:on-press #(hide-sheet-and-dispatch [:navigate-to :backup-seed])}])])) :on-press #(hide-sheet-and-dispatch [:navigate-to :backup-seed])}])]))
(defn send-receive [address] (defn send-receive [account]
[react/view [react/view
[list-item/list-item [list-item/list-item
{:theme :action {:theme :action
:title :t/wallet-send :title :t/wallet-send
:icon :main-icons/send :icon :main-icons/send
:accessibility-label :send-transaction-button :accessibility-label :send-transaction-button
:on-press #(hide-sheet-and-dispatch [:navigate-to :wallet-send-transaction address])}] :on-press #(hide-sheet-and-dispatch [:wallet/prepare-transaction-from-wallet account])}]
[list-item/list-item [list-item/list-item
{:theme :action {:theme :action
:title :t/receive :title :t/receive
:icon :main-icons/receive :icon :main-icons/receive
:accessibility-label :receive-transaction-button :accessibility-label :receive-transaction-button
:on-press #(hide-sheet-and-dispatch [:show-popover {:view :share-account :address address}])}]]) :on-press #(hide-sheet-and-dispatch
[:show-popover {:view :share-account
:address (:address account)}])}]])
(defn add-account [] (defn add-account []
[react/view [react/view

View File

@ -22,7 +22,7 @@
portfolio-value [:account-portfolio-value address]] portfolio-value [:account-portfolio-value address]]
[react/touchable-highlight {:on-press #(re-frame/dispatch [:navigate-to :wallet-account account]) [react/touchable-highlight {:on-press #(re-frame/dispatch [:navigate-to :wallet-account account])
:on-long-press #(re-frame/dispatch [:bottom-sheet/show-sheet :on-long-press #(re-frame/dispatch [:bottom-sheet/show-sheet
{:content (fn [] [sheets/send-receive address]) {:content (fn [] [sheets/send-receive account])
:content-height 130}])} :content-height 130}])}
[react/view {:style (styles/card color)} [react/view {:style (styles/card color)}
[react/view {:flex-direction :row :align-items :center :justify-content :space-between} [react/view {:flex-direction :row :align-items :center :justify-content :space-between}
@ -61,16 +61,18 @@
(when active? (when active?
[react/view {:width 24 :height 3 :border-radius 4 :background-color colors/blue}])]) [react/view {:width 24 :height 3 :border-radius 4 :background-color colors/blue}])])
(defn render-asset [currency] (defn render-asset [currency & [on-press]]
(fn [{:keys [icon decimals amount color value] :as token}] (fn [{:keys [icon decimals amount color value] :as token}]
[list-item/list-item [list-item/list-item
{:title-prefix (wallet.utils/format-amount amount decimals) (cond-> {:title-prefix (wallet.utils/format-amount amount decimals)
:title (wallet.utils/display-symbol token) :title (wallet.utils/display-symbol token)
:title-color-override colors/gray :title-color-override colors/gray
:subtitle (str (if value value 0) " " currency) :subtitle (str (if value value 0) " " currency)
:icon (if icon :icon (if icon
[list/item-image icon] [list/item-image icon]
[chat-icon/custom-icon-view-list (:name token) color])}])) [chat-icon/custom-icon-view-list (:name token) color])}
on-press
(assoc :on-press #(on-press token)))]))
(views/defview assets [] (views/defview assets []
(views/letsubs [{:keys [tokens nfts]} [:wallet/all-visible-assets-with-values] (views/letsubs [{:keys [tokens nfts]} [:wallet/all-visible-assets-with-values]

View File

@ -1,90 +0,0 @@
(ns status-im.ui.screens.wallet.choose-recipient.styles
(:require [status-im.ui.components.colors :as colors]))
(def wallet-container
{:flex 1
:background-color colors/blue})
(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 qr-container
{:position :absolute
:top 0
:left 0
:right 0
:bottom 0})
(def preview
{:flex 1})
(def corner-dimensions
{:position :absolute
:width 40
:height 40})
(defn corner-left-bottom [height width size]
(merge corner-dimensions {:bottom (int (/ (- height size) 2))
:left (int (/ (- width size) 2))}))
(defn corner-right-bottom [height width size]
(merge corner-dimensions {:right (int (/ (- width size) 2))
:bottom (int (/ (- height size) 2))}))
(defn corner-left-top [height width size]
(merge corner-dimensions {:top (int (/ (- height size) 2))
:left (int (/ (- width size) 2))}))
(defn corner-right-top [height width size]
(merge corner-dimensions {:top (int (/ (- height size) 2))
:right (int (/ (- width size) 2))}))
(def viewfinder-port {:position :absolute
:left 0
:top 0
:bottom 0
:right 0
:flex 1})
(defn viewfinder-translucent [height width size side]
(let [top-bottom-width width
top-bottom-height (int (/ (- height size) 2))
left-right-width (int (/ (- width size) 2))
left-right-height (- height (* 2 top-bottom-height))]
(cond-> {:position :absolute
:background-color :black
:opacity 0.7}
(= :top side) (assoc :height top-bottom-height
:width top-bottom-width)
(= :right side) (assoc :height left-right-height
:width left-right-width
:top top-bottom-height
:right 0)
(= :bottom side) (assoc :height top-bottom-height
:width top-bottom-width
:bottom 0
:left 0)
(= :left side) (assoc :height left-right-height
:width left-right-width
:top top-bottom-height
:left 0))))
(def qr-code
{:flex 1
:background-color colors/white-transparent
:align-items :stretch})
(defn qr-code-text [dimensions]
{:zIndex 1
:padding-top 16
:color :white
:text-align :center
:padding-vertical 16})

View File

@ -1,69 +0,0 @@
(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.i18n :as i18n]
[status-im.ui.components.toolbar :as toolbar]
[status-im.ui.components.camera :as camera]
[status-im.ui.components.react :as react]
[status-im.ui.components.toolbar.actions :as actions]
[status-im.ui.components.toolbar.view :as topbar]
[status-im.ui.screens.wallet.choose-recipient.styles :as styles]
[status-im.utils.platform :as platform]))
(defn- topbar [camera-flashlight]
[topbar/toolbar
{:transparent? true}
[topbar/nav-button (actions/back-white actions/default-handler)]
[topbar/content-title {:color :white}
(i18n/label :t/wallet-choose-recipient)]
[topbar/actions [{:icon (if (= :on camera-flashlight)
:main-icons/flash-active
:main-icons/flash-inactive)
:icon-opts {:color :white}
:handler #(re-frame/dispatch [:wallet/toggle-flashlight])}]]])
(defn- viewfinder [{:keys [height width]} size]
(let [height (cond-> height
platform/iphone-x? (- 78))]
[react/view {:style styles/viewfinder-port}
[react/view {:style (styles/viewfinder-translucent height width size :top)}]
[react/view {:style (styles/viewfinder-translucent height width size :right)}]
[react/view {:style (styles/viewfinder-translucent height width size :bottom)}]
[react/view {:style (styles/viewfinder-translucent height width size :left)}]
[react/image {:source {:uri :corner_left_top}
:style (styles/corner-left-top height width size)}]
[react/image {:source {:uri :corner_right_top}
:style (styles/corner-right-top height width size)}]
[react/image {:source {:uri :corner_left_bottom}
:style (styles/corner-left-bottom height width size)}]
[react/image {:source {:uri :corner_right_bottom}
:style (styles/corner-right-bottom height width size)}]]))
(defn- size [{:keys [height width]}]
(int (* 2 (/ (min height width) 3))))
(defview choose-recipient []
(letsubs [read-once? (atom false)
dimensions [:dimensions/window]
camera-flashlight [:wallet.send/camera-flashlight]]
[react/view {:style styles/qr-code}
[topbar camera-flashlight]
[react/text {:style (styles/qr-code-text dimensions)
:accessibility-label :scan-qr-code-with-wallet-address-text}
(i18n/label :t/scan-qr-code)]
[react/view {:style styles/qr-container
:pointer-events :none}
[react/with-activity-indicator
{}
[camera/camera {:style styles/preview
;:torchMode (camera/set-torch camera-flashlight)
:onBarCodeRead #(when-not @read-once?
(reset! read-once? true)
(re-frame/dispatch [:wallet/fill-request-from-url (camera/get-qr-code-data %) :qr]))}]]
[viewfinder dimensions (size dimensions)]]
[toolbar/toolbar
{:center {:type :secondary
:disabled? false
:on-press #(re-frame/dispatch [:navigate-back])
:accessibility-label :cancel-button
:label :t/cancel}}]]))

View File

@ -1,178 +1,10 @@
(ns status-im.ui.screens.wallet.components.styles (ns status-im.ui.screens.wallet.components.styles
(:require [status-im.ui.components.colors :as colors] (:require [status-im.ui.components.colors :as colors]))
[status-im.ui.components.styles :as components.styles]))
(def cartouche-container
{:flex 1
:margin-top 16
:margin-horizontal 16})
(defn cartouche-content-wrapper [disabled?]
(merge
{:flex-direction :row
:margin-top 8
:border-radius components.styles/border-radius
:padding-left 14
:padding-right 8}
(if disabled?
{:border-width 1
:border-color colors/gray-lighter}
{:background-color colors/gray-lighter})))
(def cartouche-icon-wrapper
{:flex 1
:flex-direction :row
:justify-content :space-between
:align-items :center})
(def text-content
{:color colors/black})
(def text-secondary-content
{:color colors/gray})
(def text
{:margin-right 10})
(def text-input
(merge text-content
{:flex 1
:padding-bottom 0
:padding-top 0
:height 52}))
(defn contact-code-text-input [w]
{:text-align-vertical :top
:padding-top 16
:padding-left 2
:padding-right 8
:width w
:height 72})
(def label
{:color colors/white})
(def network
{:color colors/white
:font-size 13})
(def network-container
{:flex-direction :row
:padding-horizontal 13
:padding-vertical 11
:align-items :center})
(def asset-container-read-only
{:margin-top 8
:height 52
:border-color colors/white-transparent-10
:border-width 1
:justify-content :center
:padding-left 14
:padding-vertical 14
:padding-right 8
:border-radius 8})
(def asset-content-container
{:flex-direction :row
:align-items :center
:margin-vertical 11})
(def asset-icon
{:background-color colors/gray-lighter
:border-radius 50})
(def asset-text-content
{:flex 1
:flex-direction :row
:justify-content :space-between
:align-items :center
:flex-wrap :wrap
:margin-left 10})
(def asset-label-content
{:flex-direction :row
:margin-right 10})
(def asset-label
{:margin-right 10})
(def asset-text
{:color colors/white})
(def container-disabled
{:border-width 1
:border-color colors/white-transparent-10
:border-radius 8})
(def wallet-container
{:flex-direction :row
:margin-top 8
:height 52
:border-width 1
:border-color colors/white-transparent-10
:align-items :center
:padding 14
:border-radius 8})
(def wallet-name
{:color colors/white})
(defn participant [address?]
{:color (if address? colors/black colors/gray)
:flex-shrink 1})
(def recipient-container
{:flex-direction :row})
(def recipient-icon
{:margin-top 11})
(def recipient-name
{:flex 1
:flex-direction :column
:margin-horizontal 12
:margin-vertical 16})
(def recipient-address
{:margin-vertical 17})
(def recipient-no-address
{:color colors/gray})
(def recent-recipients (def recent-recipients
{:flex 1 {:flex 1
:background-color colors/white}) :background-color colors/white})
(def wallet-value-container
{:flex 1
:flex-direction :row})
(def wallet-value
{:padding-left 6
:color colors/white-transparent})
(def wallet-value-amount
{:flex -1})
(def separator (def separator
{:height 1 {:height 1
:margin-horizontal 15 :background-color colors/gray-lighter})
:background-color colors/white-transparent-10})
(def button-text
{:color colors/white})
(def network-text
{:flex 1
:color colors/black
:font-size 14
:margin-left 16})
(def network-icon
{:width 40
:height 40
:border-radius (/ 40 2)
:background-color colors/green
:align-items :center
:justify-content :center})

View File

@ -2,341 +2,45 @@
(:require [clojure.string :as string] (:require [clojure.string :as string]
[re-frame.core :as re-frame] [re-frame.core :as re-frame]
[reagent.core :as reagent] [reagent.core :as reagent]
[status-im.ethereum.core :as ethereum]
[status-im.ethereum.eip55 :as eip55]
[status-im.ethereum.tokens :as tokens]
[status-im.i18n :as i18n] [status-im.i18n :as i18n]
[status-im.multiaccounts.core :as multiaccounts]
[status-im.ui.components.toolbar :as toolbar]
[status-im.ui.components.chat-icon.screen :as chat-icon]
[status-im.ui.components.colors :as colors]
[status-im.ui.components.icons.vector-icons :as vector-icons]
[status-im.ui.components.list-selection :as list-selection]
[status-im.ui.components.list.styles :as list.styles]
[status-im.ui.components.list.views :as list]
[status-im.ui.components.react :as react] [status-im.ui.components.react :as react]
[status-im.ui.components.styles :as components.styles] [status-im.ui.components.styles :as components.styles]
[status-im.ui.components.toolbar.actions :as actions]
[status-im.ui.components.toolbar.view :as topbar] [status-im.ui.components.toolbar.view :as topbar]
[status-im.ui.components.tooltip.views :as tooltip]
[status-im.ui.screens.chat.photos :as photos]
[status-im.ui.screens.wallet.choose-recipient.views
:as
choose-recipient]
[status-im.ui.screens.wallet.components.styles :as styles] [status-im.ui.screens.wallet.components.styles :as styles]
[status-im.wallet.utils :as wallet.utils] [status-im.ui.components.text-input.view :as text-input]
[status-im.utils.core :as utils.core] [status-im.ui.components.colors :as colors])
[status-im.utils.money :as money]
[status-im.utils.utils :as utils.utils])
(:require-macros [status-im.utils.views :as views])) (:require-macros [status-im.utils.views :as views]))
;; Wallet tab has a different coloring scheme (dark) that forces color changes (background, text)
;; It might be replaced by some theme mechanism
(defn text-input [props text]
[react/text-input (utils.core/deep-merge {:placeholder-text-color colors/gray
:selection-color colors/black
:style {:color colors/black
:height 52}}
props)
text])
(def default-action (actions/back-white actions/default-handler))
(defn topbar [title]
[topbar/simple-toolbar title])
(defn- top-view [avoid-keyboard?]
(if avoid-keyboard?
react/keyboard-avoiding-view
react/view))
(defn simple-screen
([toolbar content] (simple-screen nil toolbar content))
([{:keys [avoid-keyboard?]} toolbar content]
[(top-view avoid-keyboard?) {:flex 1}
toolbar
content]))
(defn- cartouche-content [{:keys [disabled?]} content]
[react/view {:style (styles/cartouche-content-wrapper disabled?)}
[react/view {:flex 1}
content]])
(defn cartouche [{:keys [disabled? on-press icon icon-opts] :or {icon :main-icons/next} :as m} header content]
[react/view {:style styles/cartouche-container}
[react/text
header]
(if (or disabled? (nil? on-press))
[cartouche-content m content]
[react/touchable-highlight {:on-press on-press}
[react/view
[cartouche-content m
(if-not (true? disabled?)
[react/view styles/cartouche-icon-wrapper
[react/view {:flex 1} ;; Let content shrink if needed
content]
[vector-icons/icon icon icon-opts]]
content)]]])])
(defn view-asset [symbol]
[react/view
[react/i18n-text {:style styles/label :key :wallet-asset}]
[react/view styles/asset-container-read-only
[react/text {:style styles/asset-text}
(name symbol)]]])
(defn- type->handler [k]
(case k
:send :wallet.send/set-symbol
:request :wallet.request/set-symbol
(throw (str "Unknown type: " k))))
(defn- render-token [{:keys [symbol name icon decimals amount color] :as token} type]
[list/touchable-item #(do (re-frame/dispatch [(type->handler type) symbol])
(re-frame/dispatch [:navigate-back]))
[react/view
[list/item
(if icon
[list/item-image icon]
[chat-icon/custom-icon-view-list name color])
[list/item-content
[react/view {:flex-direction :row}
[react/text {:style styles/text}
name]
[react/text {:style {:text-transform :uppercase}}
(wallet.utils/display-symbol token)]]
[list/item-secondary (wallet.utils/format-amount amount decimals)]]]]])
(views/defview assets [type address]
(views/letsubs [assets [:wallet/transferrable-assets-with-amount address]]
[simple-screen
[topbar (i18n/label :t/wallet-assets)]
[react/view {:style (assoc components.styles/flex :background-color :white)}
[list/flat-list {:default-separator? true
:data assets
:key-fn (comp str :symbol)
:render-fn #(render-token % type)}]]]))
(views/defview send-assets []
(views/letsubs [address [:get-screen-params]]
[assets :send address]))
(views/defview request-assets []
(views/letsubs [address [:get-screen-params]]
[assets :request address]))
(defn- type->view [k]
(case k
:send :wallet-send-assets
:request :wallet-request-assets
(throw (str "Unknown type: " k))))
(views/defview asset-selector [{:keys [disabled? type symbol error address]}]
(views/letsubs [balance [:balance address]
chain [:ethereum/chain-keyword]
all-tokens [:wallet/all-tokens]]
(let [{:keys [name icon decimals color] :as token} (tokens/asset-for all-tokens chain symbol)]
(when name
[react/view
[cartouche {:disabled? disabled? :on-press #(re-frame/dispatch [:navigate-to (type->view type) address])}
(i18n/label :t/wallet-asset)
[react/view {:style styles/asset-content-container
:accessibility-label :choose-asset-button}
(if icon
[list/item-image (assoc icon :style styles/asset-icon :image-style {:width 32 :height 32})]
[chat-icon/custom-icon-view-list name color 32])
[react/view styles/asset-text-content
[react/view styles/asset-label-content
[react/text {:style (merge styles/text-content styles/asset-label)}
name]
[react/text {:style styles/text-secondary-content}
(wallet.utils/display-symbol token)]]
[react/text {:style (merge styles/text-secondary-content styles/asset-label)}
(str (wallet.utils/format-amount (get balance symbol) decimals))]]]]
(when error
[tooltip/tooltip error {}])]))))
(defn- recipient-address [address modal?]
[react/text {:style (merge styles/recipient-address (when-not address styles/recipient-no-address))
:accessibility-label :recipient-address-text}
(or (eip55/address->checksum (ethereum/normalized-address address))
(if modal?
(i18n/label :t/new-contract)
(i18n/label :t/specify-recipient)))])
(views/defview recipient-contact [address name request?]
(views/letsubs [contact [:contacts/contact-by-address address]]
(let [address? (and (not (nil? address)) (not= address ""))]
[react/view styles/recipient-container
[react/view styles/recipient-icon
(when contact
[photos/photo
;;TODO this should be done in a subscription
(multiaccounts/displayed-photo contact)
{:size list.styles/image-size}])]
[react/view {:style styles/recipient-name}
[react/text {:style (styles/participant true)
:accessibility-label (if request? :contact-name-text :recipient-name-text)
:number-of-lines 1}
name]
[react/text {:style (styles/participant (and (not name) address?))
:accessibility-label (if request? :contact-address-text :recipient-address-text)}
(eip55/address->checksum (ethereum/normalized-address address))]]])))
(defn render-contact [contact request?]
[list/touchable-item #(re-frame/dispatch [:wallet/fill-request-from-contact contact request?])
[list/item
[photos/photo
;;TODO this should be done in a subscription
(multiaccounts/displayed-photo contact)
{:size list.styles/image-size}]
[list/item-content
[list/item-primary {:accessibility-label :contact-name-text}
(multiaccounts/displayed-name contact)]
[react/text {:style list.styles/secondary-text
:accessibility-label :contact-address-text}
(eip55/address->checksum (ethereum/normalized-address (:address contact)))]]]])
(defn render-account [account]
[list/touchable-item #(re-frame/dispatch [:wallet/fill-request-from-contact account false])
[list/item
[chat-icon/custom-icon-view-list (:name account) (:color account)]
[list/item-content
[list/item-primary {:accessibility-label :contact-name-text}
(:name account)]
[react/text {:style list.styles/secondary-text
:accessibility-label :contact-address-text}
(eip55/address->checksum (ethereum/normalized-address (:address account)))]]]])
(views/defview recent-recipients []
(views/letsubs [contacts [:contacts/active]
{:keys [request?]} [:get-screen-params :recent-recipients]]
[simple-screen
[topbar (i18n/label :t/recipient)]
[react/view styles/recent-recipients
[list/flat-list {:data contacts
:key-fn :address
:render-fn #(render-contact % request?)}]]]))
(views/defview accounts []
(views/letsubs [{:keys [accounts]} [:multiaccount]]
[simple-screen
[topbar (i18n/label :t/accounts)]
[react/view styles/recent-recipients
[list/flat-list {:data accounts
:key-fn :address
:render-fn render-account}]]]))
;;TODO workaround for https://github.com/facebook/react-native/issues/23653 (https://github.com/status-im/status-react/issues/8548)
(def tw (reagent/atom "95%"))
(views/defview contact-code []
(views/letsubs [content (reagent/atom nil)]
[simple-screen {:avoid-keyboard? true}
[topbar (i18n/label :t/recipient)]
[react/view components.styles/flex
[cartouche {}
(i18n/label :t/recipient)
[text-input {:ref (fn [v] (js/setTimeout #(reset! tw (if v "100%" "95%")) 100))
:multiline true
:style (styles/contact-code-text-input @tw)
:placeholder (i18n/label :t/recipient-code)
:on-change-text #(reset! content %)
:accessibility-label :recipient-address-input}]]
[toolbar/toolbar {:center {:type :secondary
:disabled? (string/blank? @content)
:on-press #(re-frame/dispatch [:wallet.send/set-recipient @content])
:label :t/done}}]]]))
(defn recipient-qr-code []
[choose-recipient/choose-recipient])
(defn- request-camera-permissions []
(re-frame/dispatch [:request-permissions {:permissions [:camera]
:on-allowed #(re-frame/dispatch [:navigate-to :recipient-qr-code])
:on-denied #(utils.utils/set-timeout
(fn []
(utils.utils/show-popup (i18n/label :t/error)
(i18n/label :t/camera-access-error)))
50)}]))
(defn- on-choose-recipient [contact-only? request?]
(list-selection/show {:title (i18n/label :t/wallet-choose-recipient)
;;TODO temporary hide for v1
#_(concat
[{:label (i18n/label :t/recent-recipients)
:action #(re-frame/dispatch [:navigate-to :recent-recipients {:request? request?}])}]
(when-not contact-only?))
:options [{:label (i18n/label :t/accounts)
:action #(re-frame/dispatch [:navigate-to :select-account])}
{:label (i18n/label :t/scan-qr)
:action request-camera-permissions}
{:label (i18n/label :t/recipient-code)
:action #(re-frame/dispatch [:navigate-to :contact-code])}]}))
(defn recipient-selector [{:keys [name address disabled? contact-only? request? modal?]}]
[cartouche {:on-press #(on-choose-recipient contact-only? request?)
:disabled? disabled?
:icon :main-icons/more
:icon-opts {:accessibility-label :choose-contact-button}}
(i18n/label :t/wallet-choose-recipient)
[react/view {:accessibility-label :choose-recipient-button}
(if name
[recipient-contact address name request?]
[recipient-address address modal?])]])
(defn amount-input [{:keys [input-options amount amount-text disabled?]}
{:keys [symbol decimals]}]
[react/view {:style components.styles/flex
:accessibility-label :specify-amount-button}
[text-input
(merge
input-options
;; We only auto-correct and prettify user's input when it is valid and positive.
;; Otherwise, user might want to fix his input and autocorrection will give more harm than good.
;; Positive check is because we don't want to replace unfinished 0.000 with just plain 0, that is annoying and
;; potentially dangerous on this screen (e.g. sending 7 ETH instead of 0.0007)
{:default-value (if (empty? amount-text)
(str (money/to-fixed (money/internal->formatted amount symbol decimals)))
amount-text)}
(if disabled?
{:editable false
:placeholder ""}
{:keyboard-type :numeric
:placeholder (i18n/label :t/amount-placeholder)
:style components.styles/flex
:accessibility-label :amount-input}))]])
(defn amount-selector [{:keys [error disabled?] :as m} token]
[react/view
[cartouche {:disabled? disabled?}
(i18n/label :t/amount)
[amount-input m token]]
(when error
[tooltip/tooltip error])])
(defn separator [] (defn separator []
[react/view styles/separator]) [react/view styles/separator])
(defn button-text [label] (defn- recipient-topbar [content]
[react/text {:style styles/button-text} [topbar/toolbar {:transparent? true}
label]) [topbar/nav-text
{:style {:margin-left 16}
:handler #(do
(re-frame/dispatch [:set-in [:wallet/prepare-transaction :modal-opened?] false])
(re-frame/dispatch [:navigate-back]))}
(i18n/label :t/cancel)]
[topbar/content-title {}
(i18n/label :t/recipient)]
[topbar/text-action
{:disabled? (string/blank? content)
:style {:margin-right 16}
:handler #(re-frame/dispatch [:wallet.send/set-recipient content])}
(i18n/label :t/done)]])
(views/defview network-info [] (views/defview contact-code []
(views/letsubs [network-id [:chain-id]] (views/letsubs [content (reagent/atom nil)]
[react/view [react/view {:flex 1}
[react/view styles/network-container [recipient-topbar @content]
[react/view styles/network-icon [react/view
[vector-icons/icon :main-icons/network {:color :white}]] [text-input/text-input-with-label
[react/text {:multiline true
{:style {:flex 1 :container {:margin 16 :padding-vertical 16 :height 72}
:padding-left 16}} :style {:text-align-vertical :top :height 42}
(cond (ethereum/testnet? network-id) :placeholder "0x... or username.domain.eth" ;(i18n/label :t/recipient-code)
(i18n/label :t/testnet-text {:testnet (get-in ethereum/chains [(ethereum/chain-id->chain-keyword network-id) :name] "Unknown")}) :on-change-text #(reset! content %)
:accessibility-label :recipient-address-input}]
(ethereum/sidechain? network-id) [react/text {:style {:color colors/gray :margin-horizontal 16}}
(i18n/label :t/sidechain-text {:sidechain (get-in ethereum/chains [(ethereum/chain-id->chain-keyword network-id) :name] "Unknown")}) "Enter address or username of the recepient"]]]))
:else
(i18n/label :t/mainnet-text))]]]))

View File

@ -1,13 +1,7 @@
(ns status-im.ui.screens.wallet.navigation (ns status-im.ui.screens.wallet.navigation
(:require [status-im.constants :as constants] (:require [status-im.ui.screens.navigation :as navigation]
[status-im.ui.screens.navigation :as navigation]
[status-im.ui.screens.wallet.signing-phrase.views :as signing-phrase])) [status-im.ui.screens.wallet.signing-phrase.views :as signing-phrase]))
(defn transaction-send-default [address]
{:method constants/web3-send-transaction
:from address
:symbol :ETH})
(defmethod navigation/preload-data! :wallet-stack (defmethod navigation/preload-data! :wallet-stack
[db [event]] [db [event]]
(let [wallet-set-up-passed? (get-in db [:multiaccount :wallet-set-up-passed?])] (let [wallet-set-up-passed? (get-in db [:multiaccount :wallet-set-up-passed?])]
@ -15,20 +9,6 @@
db db
(assoc db :popover/popover {:view [signing-phrase/signing-phrase]})))) (assoc db :popover/popover {:view [signing-phrase/signing-phrase]}))))
(defmethod navigation/preload-data! :wallet-send-transaction-request
[db [event _ address]]
(if (= event :navigate-back)
db
(-> db
(assoc-in [:wallet :request-transaction] {:symbol :ETH})
(assoc-in [:wallet :send-transaction] (transaction-send-default address)))))
(defmethod navigation/preload-data! :wallet-send-transaction
[db [event _ address]]
(if (= event :navigate-back)
db
(assoc-in db [:wallet :send-transaction] (transaction-send-default address))))
(defmethod navigation/preload-data! :wallet-add-custom-token (defmethod navigation/preload-data! :wallet-add-custom-token
[db [event]] [db [event]]
(dissoc db :wallet/custom-token-screen)) (dissoc db :wallet/custom-token-screen))

View File

@ -1,10 +0,0 @@
(ns status-im.ui.screens.wallet.request.db
(:require [cljs.spec.alpha :as spec]
[status-im.utils.money :as money]))
(spec/def ::amount (spec/nilable money/valid?))
(spec/def ::amount-error (spec/nilable string?))
(spec/def ::amount-text (spec/nilable string?))
(spec/def ::symbol (spec/nilable keyword?))
(spec/def :wallet/request-transaction (spec/keys :opt-un [::amount ::amount-error ::amount-text ::symbol]))

View File

@ -1,28 +0,0 @@
(ns status-im.ui.screens.wallet.request.styles
(:require [status-im.ui.components.colors :as colors]))
(def hint
{:color colors/white-transparent})
(def footer
{:color colors/white
:border-color colors/white-transparent-10})
(def bottom-buttons
{:background-color colors/blue})
(def request-wrapper
{:flex 1
:flex-direction :column})
;; Request panel
(def request-details-wrapper
{:padding-bottom 60})
(def send-request
{:background-color colors/black-transparent
:margin-top 12
:margin-bottom 16
:margin-horizontal 16
:height 42})

View File

@ -3,61 +3,12 @@
[reagent.core :as reagent] [reagent.core :as reagent]
[status-im.ethereum.eip55 :as eip55] [status-im.ethereum.eip55 :as eip55]
[status-im.ethereum.eip681 :as eip681] [status-im.ethereum.eip681 :as eip681]
[status-im.ethereum.tokens :as tokens]
[status-im.i18n :as i18n]
[status-im.ui.components.button :as button] [status-im.ui.components.button :as button]
[status-im.ui.components.copyable-text :as copyable-text] [status-im.ui.components.copyable-text :as copyable-text]
[status-im.ui.components.qr-code-viewer.views :as qr-code-viewer] [status-im.ui.components.qr-code-viewer.views :as qr-code-viewer]
[status-im.ui.components.react :as react] [status-im.ui.components.react :as react])
[status-im.ui.components.styles :as components.styles]
[status-im.ui.components.toolbar :as toolbar]
[status-im.ui.screens.wallet.components.views :as wallet.components]
[status-im.ui.screens.wallet.request.styles :as styles]
[status-im.utils.utils :as utils]
[status-im.wallet.utils :as wallet.utils])
(:require-macros [status-im.utils.views :as views])) (:require-macros [status-im.utils.views :as views]))
;;TODO DEPRECATED
(views/defview send-transaction-request []
;; TODO(jeluard) both send and request flows should be merged
(views/letsubs [chain [:ethereum/chain-keyword]
{:keys [amount amount-error amount-text symbol to
to-name public-key]} [:wallet.request/transaction]
network-status [:network-status]
all-tokens [:wallet/all-tokens]
scroll (atom nil)]
(let [{:keys [decimals] :as token} (tokens/asset-for all-tokens chain symbol)]
[wallet.components/simple-screen {:avoid-keyboard? true}
[wallet.components/topbar (i18n/label :t/new-request)]
[react/view components.styles/flex
[wallet.components/network-info]
[react/scroll-view {:ref #(reset! scroll %) :keyboardShouldPersistTaps :always}
[react/view styles/request-details-wrapper
[wallet.components/recipient-selector
{:contact-only? true
:address to
:name to-name
:request? true}]
[wallet.components/asset-selector
{:disabled? false
:type :request
:symbol symbol}]
[wallet.components/amount-selector
{:error amount-error
:disabled? (= :offline network-status)
:amount amount
:amount-text amount-text
:input-options {:on-focus (fn [] (when @scroll (utils/set-timeout #(.scrollToEnd @scroll) 100)))
:on-change-text #(re-frame/dispatch [:wallet.request/set-and-validate-amount % symbol decimals])}}
token]]]
[toolbar/toolbar
{:right {:type :next
:disabled? (or amount-error (not (and to amount)))
:on-press #(re-frame/dispatch [:wallet-send-request public-key amount
(wallet.utils/display-symbol token) decimals])
:accessibility-label :sent-request-button
:label :t/send-request}}]]])))
(views/defview share-address [] (views/defview share-address []
(views/letsubs [{:keys [address]} [:popover/popover] (views/letsubs [{:keys [address]} [:popover/popover]
chain-id [:chain-id] chain-id [:chain-id]

View File

@ -1,42 +0,0 @@
(ns status-im.ui.screens.wallet.send.db
(:require [cljs.spec.alpha :as spec]
[status-im.utils.money :as money]
[status-im.utils.security :as security]))
; transaction
(spec/def ::from (spec/nilable string?))
(spec/def ::to (spec/nilable string?))
(spec/def ::amount (spec/nilable money/valid?))
(spec/def ::gas (spec/nilable money/valid?))
(spec/def ::original-gas (spec/nilable money/valid?))
(spec/def ::gas-price (spec/nilable money/valid?))
; dapp transaction
(spec/def ::data (spec/nilable string?))
(spec/def ::nonce (spec/nilable string?))
(spec/def ::to-name (spec/nilable string?))
(spec/def ::amount-error (spec/nilable string?))
(spec/def ::asset-error (spec/nilable string?))
(spec/def ::amount-text (spec/nilable string?))
(spec/def ::password (spec/nilable #(instance? security/MaskedData %)))
(spec/def ::wrong-password? (spec/nilable boolean?))
(spec/def ::id (spec/nilable string?))
(spec/def ::show-password-input? (spec/nilable boolean?))
(spec/def ::height double?)
(spec/def ::width double?)
(spec/def ::camera-flashlight #{:on :off})
(spec/def ::in-progress? boolean?)
(spec/def ::from-chat? (spec/nilable boolean?))
(spec/def ::symbol (spec/nilable keyword?))
(spec/def ::advanced? boolean?)
(spec/def ::public-key (spec/nilable string?))
(spec/def ::method (spec/nilable string?))
(spec/def ::tx-hash (spec/nilable string?))
(spec/def ::on-result (spec/nilable any?))
(spec/def ::on-error (spec/nilable any?))
(spec/def :wallet/send-transaction (spec/keys :opt-un [::amount ::to ::to-name ::amount-error ::asset-error ::amount-text
::password ::show-password-input? ::id ::from ::data ::nonce
::camera-flashlight ::in-progress? ::on-result ::on-error
::wrong-password? ::from-chat? ::symbol ::advanced?
::gas ::gas-price ::original-gas ::public-key ::method ::tx-hash]))

View File

@ -0,0 +1,70 @@
(ns status-im.ui.screens.wallet.send.sheets
(:require-macros [status-im.utils.views :refer [defview letsubs] :as views])
(:require [re-frame.core :as re-frame]
[status-im.i18n :as i18n]
[status-im.ui.components.react :as react]
[status-im.ui.components.list-item.views :as list-item]
[status-im.ui.components.chat-icon.screen :as chat-icon]
[status-im.ui.components.list.views :as list]
[status-im.ui.screens.wallet.accounts.views :as wallet.accounts]
[status-im.utils.utils :as utils.utils]))
(views/defview assets [address]
(views/letsubs [{:keys [tokens]} [:wallet/visible-assets-with-values address]
currency [:wallet/currency]]
[list/flat-list
{:data tokens
:key-fn (comp str :symbol)
:render-fn (wallet.accounts/render-asset
(:code currency)
#(re-frame/dispatch [:wallet.send/set-symbol (:symbol %)]))}]))
(defn render-account [field]
(fn [account]
[list-item/list-item
{:icon [chat-icon/custom-icon-view-list (:name account) (:color account)]
:title (:name account)
:on-press #(re-frame/dispatch [:wallet.send/set-field field account])}]))
(views/defview accounts-list [field]
(views/letsubs [{:keys [accounts]} [:multiaccount]]
[list/flat-list {:data accounts
:key-fn :address
:render-fn (render-account field)}]))
(defn- request-camera-permissions []
(let [options {:handler :wallet.send/qr-scanner-result
:cancel-handler :wallet.send/qr-scanner-cancel}]
(re-frame/dispatch
[:request-permissions
{:permissions [:camera]
:on-allowed #(re-frame/dispatch [:wallet.send/qr-scanner-allowed options])
:on-denied
#(utils.utils/set-timeout
(fn []
(utils.utils/show-popup (i18n/label :t/error)
(i18n/label :t/camera-access-error)))
50)}])))
(defn show-accounts-list []
(re-frame/dispatch [:bottom-sheet/hide-sheet])
(js/setTimeout #(re-frame/dispatch [:bottom-sheet/show-sheet
{:content (fn [] [accounts-list :to])
:content-height 300}]) 400))
(defn choose-recipient []
[react/view
(for [item [{:title (i18n/label :t/accounts)
:icon :main-icons/profile
:theme :action
:on-press show-accounts-list}
{:title (i18n/label :t/scan-qr)
:icon :main-icons/qr
:theme :action
:on-press request-camera-permissions}
{:title (i18n/label :t/recipient-code)
:icon :main-icons/address
:theme :action
:on-press #(re-frame/dispatch [:wallet.send/navigate-to-recipient-code])}]]
^{:key item}
[list-item/list-item item])])

View File

@ -1,56 +1,14 @@
(ns status-im.ui.screens.wallet.send.styles (ns status-im.ui.screens.wallet.send.styles)
(:require [status-im.ui.components.colors :as colors]
[status-im.ui.screens.wallet.components.styles :as wallet.components.styles]))
(def send-transaction-form (defn sheet [small-screen?]
{:flex 1 {:background-color :white
:padding-bottom 60}) :border-top-right-radius 16
:border-top-left-radius 16
:padding-bottom (if small-screen? 40 60)})
(def signing-phrase-description (defn header [small-screen?]
{:padding-top 8}) {:flex-direction :row
:align-items :center
(def password-container :justify-content :space-between
{:flex 1 :padding-top (when-not small-screen? 16)
:padding-vertical 20}) :padding-left 16})
(def password
{:padding 0
:height 20})
(def processing-view
{:position :absolute
:top 0
:bottom 0
:right 0
:left 0
:align-items :center
:justify-content :center
:background-color (str colors/black "1A")})
(def empty-text
{:text-align :center
:margin-top 22
:margin-horizontal 92})
(def advanced-button
{:flex-direction :row
:background-color colors/black-transparent
:border-radius 50
:padding 8
:align-items :center})
(def advanced-label
{:text-align-vertical :center
:margin-left 4})
(def transaction-fee-info
{:flex-direction :row
:margin 15})
(def transaction-fee-input
{:keyboard-type :numeric
:auto-capitalize "none"
:placeholder "0.000"
:placeholder-text-color colors/white-transparent
:selection-color colors/white
:style wallet.components.styles/text-input})

View File

@ -1,67 +1,122 @@
(ns status-im.ui.screens.wallet.send.views (ns status-im.ui.screens.wallet.send.views
(:require-macros [status-im.utils.views :refer [defview letsubs]]) (:require-macros [status-im.utils.views :refer [defview letsubs] :as views])
(:require [re-frame.core :as re-frame] (:require [re-frame.core :as re-frame]
[reagent.core :as reagent]
[status-im.i18n :as i18n] [status-im.i18n :as i18n]
[status-im.ui.components.react :as react] [status-im.ui.components.react :as react]
[status-im.ui.components.styles :as components.styles]
[status-im.ui.components.toolbar.view :as topbar]
[status-im.ui.components.toolbar :as toolbar] [status-im.ui.components.toolbar :as toolbar]
[status-im.ui.screens.wallet.components.views :as wallet.components] [status-im.ui.screens.wallet.send.styles :as styles]
[status-im.ui.screens.wallet.send.styles :as styles])) [status-im.ui.components.bottom-panel.views :as bottom-panel]
[status-im.ui.components.colors :as colors]
[status-im.ui.components.list-item.views :as list-item]
[status-im.ui.components.tooltip.views :as tooltip]
[status-im.ui.components.chat-icon.screen :as chat-icon]
[status-im.ui.components.list.views :as list]
[status-im.wallet.utils :as wallet.utils]
[status-im.ui.components.icons.vector-icons :as icons]
[status-im.multiaccounts.core :as multiaccounts]
[status-im.ui.screens.wallet.send.sheets :as sheets]
[status-im.ui.screens.wallet.components.views :as components]
[status-im.utils.utils :as utils]
[status-im.ui.components.button :as button]))
(defn- sign-transaction-button [sign-enabled?] (defn header [small-screen?]
[toolbar/toolbar [react/view (styles/header small-screen?)
{:right {:type :next [react/view {:flex 1}
:disabled? (not sign-enabled?) [react/text {:style (merge {:typography :title-bold} (when small-screen? {:font-size 15}))}
:on-press #(re-frame/dispatch [:wallet.ui/sign-transaction-button-clicked]) (i18n/label :t/send-transaction)]]
:accessibility-label :sign-transaction-button [button/button {:type :secondary
:label :t/transactions-sign-transaction}}]) :container-style {:padding-horizontal 24}
:label (i18n/label :t/cancel)
:on-press #(re-frame/dispatch [:set :wallet/prepare-transaction nil])}]])
;;TODO DEPRECATED (defn asset-selector [{:keys [token from]}]
(defn- render-send-transaction-view (let [{:keys [name icon color]} token]
[{:keys [transaction scroll amount-input]}] [react/touchable-highlight
(let [{:keys [from amount amount-text amount-error token sign-enabled? {:on-press #(do
asset-error to to-name symbol]} transaction] (re-frame/dispatch [:dismiss-keyboard])
[wallet.components/simple-screen {:avoid-keyboard? true} (re-frame/dispatch [:bottom-sheet/show-sheet
[topbar/simple-toolbar (i18n/label :t/send-transaction)] {:content (fn [] [sheets/assets (:address from)])
[react/view components.styles/flex :content-height 300}]))}
[wallet.components/network-info] [react/view {:style {:flex-direction :row
[react/scroll-view {:keyboard-should-persist-taps :never :align-items :center
:keyboard-dismiss-mode :on-drag :margin-left 16}
:ref #(reset! scroll %) :accessibility-label :choose-asset-button}
:on-content-size-change #(when (and scroll @scroll) (if icon
(.scrollToEnd @scroll))} [list/item-image (assoc icon :style {:background-color colors/gray-lighter
[react/view styles/send-transaction-form :border-radius 50} :image-style {:width 32 :height 32})]
[wallet.components/recipient-selector [chat-icon/custom-icon-view-list name color 32])
{:address to [react/text {:style {:margin-left 8}}
:name to-name}] (wallet.utils/display-symbol token)]
[wallet.components/asset-selector [icons/icon :main-icons/dropdown {:color colors/gray}]]]))
{:error asset-error
:address from
:type :send
:symbol symbol}]
[wallet.components/amount-selector
{:error amount-error
:amount amount
:amount-text amount-text
:input-options {:on-change-text #(re-frame/dispatch [:wallet.send/set-and-validate-amount %])
:ref (partial reset! amount-input)}} token]]]
[sign-transaction-button sign-enabled?]]]))
(defn- send-transaction-view [{:keys [scroll]}] (defn render-account [account {:keys [amount decimals] :as token}]
(let [amount-input (atom nil) [list-item/list-item
handler #(when (and scroll @scroll @amount-input (.isFocused @amount-input)) (.scrollToEnd @scroll))] {:icon [chat-icon/custom-icon-view-list (:name account) (:color account)]
(reagent/create-class :title (:name account)
{:component-did-mount (fn [_] :subtitle (str (wallet.utils/format-amount amount decimals)
;;NOTE(goranjovic): keyboardDidShow is for android and keyboardWillShow for ios " "
(.addListener react/keyboard "keyboardDidShow" handler) (wallet.utils/display-symbol token))
(.addListener react/keyboard "keyboardWillShow" handler)) :accessories [:chevron]
:reagent-render (fn [opts] (render-send-transaction-view :on-press #(do
(assoc opts :amount-input amount-input)))}))) (re-frame/dispatch [:dismiss-keyboard])
(re-frame/dispatch [:bottom-sheet/show-sheet
{:content (fn [] [sheets/accounts-list :from])
:content-height 300}]))}])
(defview send-transaction [] (defn render-contact [contact from-chat?]
(letsubs [transaction [:wallet.send/transaction] (if from-chat?
scroll (atom nil)] [list-item/list-item {:title (multiaccounts/displayed-name contact)
[send-transaction-view {:transaction transaction :icon (multiaccounts/displayed-photo contact)}]
:scroll scroll}])) [list-item/list-item
{:title (utils/get-shortened-checksum-address
(if (string? contact) contact (:address contact)))
:subtitle (when-not contact (i18n/label :t/wallet-choose-recipient))
:on-press #(do
(re-frame/dispatch [:dismiss-keyboard])
(re-frame/dispatch [:bottom-sheet/show-sheet
{:content sheets/choose-recipient
:content-height 200}]))
:accessories [:chevron]}]))
(views/defview sheet [_]
(views/letsubs [{:keys [amount-error amount-text from token to sign-enabled? from-chat?] :as tx}
[:wallet.send/prepare-transaction-with-balance]
small-screen? [:dimensions/small-screen?]]
[react/view (styles/sheet small-screen?)
[header small-screen?]
[react/view (merge {:flex-direction :row :padding-horizontal 24 :align-items :center}
(when-not small-screen?
{:margin-top 16 :margin-bottom 16}))
[react/text-input
{:style {:font-size (if small-screen? 24 38)
:color (when amount-error colors/red)
:flex-shrink 1}
:keyboard-type :numeric
:accessibility-label :amount-input
:default-value amount-text
:auto-focus true
:on-change-text #(re-frame/dispatch [:wallet.send/set-amount-text %])
:placeholder "0.0 "}]
[asset-selector tx]
(when amount-error
[tooltip/tooltip amount-error {:bottom-value 2
:font-size 12}])]
[components/separator]
[list-item/list-item {:type :section-header :title :t/from}]
[render-account from token]
[list-item/list-item {:type :section-header :title :t/to}]
[render-contact to from-chat?]
[toolbar/toolbar
{:center {:label :t/wallet-send
:disabled? (not sign-enabled?)
:on-press #(re-frame/dispatch [:wallet.ui/sign-transaction-button-clicked tx])}}]]))
(defview prepare-transaction []
(letsubs [tx [:wallet/prepare-transaction]]
[bottom-panel/animated-bottom-panel
;;we use select-keys here because we don't want to update view if other keys in map are changed
;; and because modal screen (qr code scanner) can't be opened over bottom sheet we have to use :modal-opened?
;; to hide our transaction panel
(when (and tx (not (:modal-opened? tx)))
(select-keys tx [:from-chat?]))
sheet]))

View File

@ -81,8 +81,8 @@
(navigation/navigate-to-cofx (assoc-in cofx [:db :contacts/identity] public-key) :profile nil)))) (navigation/navigate-to-cofx (assoc-in cofx [:db :contacts/identity] public-key) :profile nil))))
(fx/defn handle-eip681 [cofx url] (fx/defn handle-eip681 [cofx url]
{:dispatch-n [[:navigate-to :wallet-send-transaction] {:dispatch-n [[:navigate-to :wallet]
[:wallet/fill-request-from-url url :deep-link]]}) [:wallet/fill-request-from-url url]]})
(defn handle-not-found [full-url] (defn handle-not-found [full-url]
(log/info "universal-links: no handler for " full-url)) (log/info "universal-links: no handler for " full-url))

View File

@ -8,7 +8,8 @@
[status-im.ethereum.ens :as ens] [status-im.ethereum.ens :as ens]
[status-im.i18n :as i18n] [status-im.i18n :as i18n]
[status-im.utils.money :as money] [status-im.utils.money :as money]
[status-im.utils.fx :as fx])) [status-im.utils.fx :as fx]
[status-im.ui.screens.navigation :as navigation]))
(fx/defn toggle-flashlight (fx/defn toggle-flashlight
{:events [:wallet/toggle-flashlight]} {:events [:wallet/toggle-flashlight]}
@ -23,21 +24,27 @@
(defn- fill-request-details [db {:keys [address name value symbol gas gasPrice public-key from-chat?]} request?] (defn- fill-request-details [db {:keys [address name value symbol gas gasPrice public-key from-chat?]} request?]
{:pre [(not (nil? address))]} {:pre [(not (nil? address))]}
(let [name (or name (find-address-name db address)) (let [name (or name (find-address-name db address))
;;TODO request isn't implemented
data-path (if request? data-path (if request?
[:wallet :request-transaction] :wallet/prepare-transaction
[:wallet :send-transaction])] :wallet/prepare-transaction)]
(update-in db data-path (update db data-path
(fn [{old-symbol :symbol :as old-transaction}] (fn [{old-symbol :symbol :as old-transaction}]
(let [symbol-changed? (not= old-symbol symbol)] (let [symbol-changed? (not= old-symbol symbol)]
(cond-> (assoc old-transaction :to address :to-name name :public-key public-key) (cond-> (assoc old-transaction
value (assoc :amount value) :to address :to-name name :public-key public-key
symbol (assoc :symbol symbol) :gas (money/bignumber gas)
(and gas symbol-changed?) (assoc :gas (money/bignumber gas)) :gas-price (money/bignumber gasPrice))
from-chat? (assoc :from-chat? from-chat?) value (assoc :amount value)
(and gasPrice symbol-changed?) symbol (assoc :symbol symbol)
(assoc :gas-price (money/bignumber gasPrice)) from-chat? (assoc :from-chat? from-chat?)))))))
(and symbol (not gasPrice) symbol-changed?)
(assoc :gas-price (ethereum/estimate-gas symbol)))))))) ;;TODO request isn't implemented
(fx/defn fill-request-from-contact
{:events [:wallet/fill-request-from-contact]}
[{db :db} {:keys [address name public-key]} request?]
{:db (fill-request-details db {:address address :name name :public-key public-key} request?)
:dispatch [:navigate-back]})
(defn- extract-details (defn- extract-details
"First try to parse as EIP681 URI, if not assume this is an address directly. "First try to parse as EIP681 URI, if not assume this is an address directly.
@ -51,16 +58,13 @@
;; NOTE(janherich) - whenever changing assets, we want to clear the previusly set amount/amount-text ;; NOTE(janherich) - whenever changing assets, we want to clear the previusly set amount/amount-text
(defn changed-asset [{:keys [db] :as fx} old-symbol new-symbol] (defn changed-asset [{:keys [db] :as fx} old-symbol new-symbol]
(-> fx (-> fx
(merge {:wallet/update-gas-price (assoc-in [:db :wallet/prepare-transaction :amount] nil)
{:success-event :wallet/update-gas-price-success (assoc-in [:db :wallet/prepare-transaction :amount-text] nil)
:edit? false}}) (assoc-in [:db :wallet/prepare-transaction :asset-error]
(assoc-in [:db :wallet :send-transaction :amount] nil)
(assoc-in [:db :wallet :send-transaction :amount-text] nil)
(assoc-in [:db :wallet :send-transaction :asset-error]
(i18n/label :t/changed-asset-warning {:old old-symbol :new new-symbol})))) (i18n/label :t/changed-asset-warning {:old old-symbol :new new-symbol}))))
(defn changed-amount-warning [fx old-amount new-amount] (defn changed-amount-warning [fx old-amount new-amount]
(assoc-in fx [:db :wallet :send-transaction :amount-error] (assoc-in fx [:db :wallet/prepare-transaction :amount-error]
(i18n/label :t/changed-amount-warning {:old old-amount :new new-amount}))) (i18n/label :t/changed-amount-warning {:old old-amount :new new-amount})))
(defn use-default-eth-gas [fx] (defn use-default-eth-gas [fx]
@ -84,41 +88,48 @@
(let [checksum (eip55/address->checksum recipient)] (let [checksum (eip55/address->checksum recipient)]
(if (eip55/valid-address-checksum? checksum) (if (eip55/valid-address-checksum? checksum)
{:db (-> db {:db (-> db
(assoc-in [:wallet :send-transaction :to] checksum) (assoc-in [:wallet/prepare-transaction :to] checksum)
(assoc-in [:wallet :send-transaction :to-name] nil)) (assoc-in [:wallet/prepare-transaction :modal-opened?] false))
:dispatch [:navigate-back]} :dispatch [:navigate-back]}
{:ui/show-error (i18n/label :t/wallet-invalid-address-checksum {:data recipient})})) {:ui/show-error (i18n/label :t/wallet-invalid-address-checksum {:data recipient})}))
{:ui/show-error (i18n/label :t/wallet-invalid-address {:data recipient})})))) {:ui/show-error (i18n/label :t/wallet-invalid-address {:data recipient})}))))
(fx/defn fill-request-from-url (fx/defn fill-request-from-url
{:events [:wallet/fill-request-from-url]} {:events [:wallet/fill-request-from-url]}
[{{:networks/keys [current-network] :wallet/keys [all-tokens] :as db} :db} data origin] [{{:networks/keys [current-network] :wallet/keys [all-tokens] :as db} :db} data]
(let [current-chain-id (get-in constants/default-networks [current-network :config :NetworkId]) (let [current-chain-id (get-in constants/default-networks [current-network :config :NetworkId])
{:keys [address chain-id] :as details} (extract-details data current-chain-id all-tokens) {:keys [address chain-id] :as details} (extract-details data current-chain-id all-tokens)
valid-network? (boolean (= current-chain-id chain-id)) valid-network? (boolean (= current-chain-id chain-id))
previous-state (get-in db [:wallet :send-transaction]) previous-state (get db :wallet/prepare-transaction)
old-symbol (:symbol previous-state) old-symbol (:symbol previous-state)
new-symbol (:symbol details) new-symbol (:symbol details)
old-amount (:amount previous-state) old-amount (:amount previous-state)
new-amount (:value details) new-amount (:value details)
new-gas (:gas details) symbol-changed? (and old-symbol new-symbol (not= old-symbol new-symbol))
symbol-changed? (and old-symbol new-symbol (not= old-symbol new-symbol))] amount-changed? (and old-amount new-amount (not= old-amount new-amount))]
(cond-> {:db db} (cond-> {:db db}
(not= :deep-link origin) (assoc :dispatch [:navigate-back]) ;; Only navigate-back when called from within wallet
(and address valid-network?) (update :db #(fill-request-details % details false)) (and address valid-network?) (update :db #(fill-request-details % details false))
symbol-changed? (changed-asset old-symbol new-symbol) symbol-changed? (changed-asset old-symbol new-symbol)
(and old-amount new-amount (not= old-amount new-amount)) (changed-amount-warning old-amount new-amount) amount-changed? (changed-amount-warning old-amount new-amount)
;; NOTE(goranjovic) - the next line is there is because QR code scanning switches the amount to ETH (not (:from previous-state))
;; automatically, so we need to update the gas limit accordingly. The check for origin screen is there (update :db assoc-in [:wallet/prepare-transaction :from]
;; so that we wouldn't also switch gas limit to ETH specific if the user pastes address as text. (ethereum/get-default-account (get-in db [:multiaccount :accounts])))
;; We need to check if address is defined so that we wouldn't trigger this behavior when invalid QR is scanned (not old-symbol)
;; (e.g. public-key) (update :db assoc-in [:wallet/prepare-transaction :symbol] (or new-symbol :ETH))
(and address (= origin :qr) (not new-gas) symbol-changed?) (use-default-eth-gas)
(not address) (assoc :ui/show-error (i18n/label :t/wallet-invalid-address {:data data})) (not address) (assoc :ui/show-error (i18n/label :t/wallet-invalid-address {:data data}))
(and address (not valid-network?)) (assoc :ui/show-error (i18n/label :t/wallet-invalid-chain-id {:data data :chain current-chain-id}))))) (and address (not valid-network?))
(assoc :ui/show-error (i18n/label :t/wallet-invalid-chain-id
{:data data :chain current-chain-id})))))
(fx/defn fill-request-from-contact (fx/defn qr-scanner-result
{:events [:wallet/fill-request-from-contact]} {:events [:wallet.send/qr-scanner-result]}
[{db :db} {:keys [address name public-key]} request?] [{db :db :as cofx} data opts]
{:db (fill-request-details db {:address address :name name :public-key public-key} request?) (fx/merge cofx
:dispatch [:navigate-back]}) {:db (assoc-in db [:wallet/prepare-transaction :modal-opened?] false)}
(navigation/navigate-back)
(fill-request-from-url data)))
(fx/defn qr-scanner-cancel
{:events [:wallet.send/qr-scanner-cancel]}
[{db :db} _]
{:db (assoc-in db [:wallet/prepare-transaction :modal-opened?] false)})

View File

@ -20,7 +20,9 @@
[status-im.wallet.db :as wallet.db] [status-im.wallet.db :as wallet.db]
[status-im.ethereum.abi-spec :as abi-spec] [status-im.ethereum.abi-spec :as abi-spec]
[status-im.signing.core :as signing] [status-im.signing.core :as signing]
[clojure.string :as string])) [clojure.string :as string]
[status-im.contact.db :as contact.db]
[status-im.ui.components.bottom-sheet.core :as bottom-sheet]))
(re-frame/reg-fx (re-frame/reg-fx
:wallet/get-balance :wallet/get-balance
@ -342,17 +344,9 @@
(multiaccounts.update/update-settings cofx new-settings {}))) (multiaccounts.update/update-settings cofx new-settings {})))
(fx/defn set-and-validate-amount (fx/defn set-and-validate-amount
{:events [:wallet.send/set-and-validate-amount]} {:events [:wallet.send/set-amount-text]}
[{:keys [db]} amount] [{:keys [db]} amount]
(let [chain (ethereum/chain-keyword db) {:db (assoc-in db [:wallet/prepare-transaction :amount-text] amount)})
all-tokens (:wallet/all-tokens db)
symbol (get-in db [:wallet :send-transaction :symbol])
{:keys [decimals]} (tokens/asset-for all-tokens chain symbol)
{:keys [value error]} (wallet.db/parse-amount amount decimals)]
{:db (-> db
(assoc-in [:wallet :send-transaction :amount] (money/formatted->internal value symbol decimals))
(assoc-in [:wallet :send-transaction :amount-text] amount)
(assoc-in [:wallet :send-transaction :amount-error] error))}))
(fx/defn set-symbol (fx/defn set-symbol
{:events [:wallet.send/set-symbol]} {:events [:wallet.send/set-symbol]}
@ -365,21 +359,27 @@
(fx/defn sign-transaction-button-clicked (fx/defn sign-transaction-button-clicked
{:events [:wallet.ui/sign-transaction-button-clicked]} {:events [:wallet.ui/sign-transaction-button-clicked]}
[{:keys [db] :as cofx}] [{:keys [db] :as cofx} {:keys [to amount from token from-chat? gas gasPrice]}]
(let [{:keys [to symbol amount from]} (get-in cofx [:db :wallet :send-transaction]) (let [{:keys [symbol address]} token
{:keys [symbol address]} (tokens/asset-for (:wallet/all-tokens db)
(ethereum/chain-keyword db)
symbol)
amount-hex (str "0x" (abi-spec/number-to-hex amount)) amount-hex (str "0x" (abi-spec/number-to-hex amount))
to-norm (ethereum/normalized-address to)] to-norm (ethereum/normalized-address (if (string? to) to (:address to)))
(signing/sign cofx {:tx-obj (if (= symbol :ETH) from-address (:address from)]
{:to to-norm (fx/merge cofx
:from from {:db (dissoc db :wallet/prepare-transaction)}
:value amount-hex} #(if from-chat?
{:to (ethereum/normalized-address address) nil;;TODO from chat, send request message or if ens name sign tx and send tx message
:from from (signing/sign % {:tx-obj (if (= symbol :ETH)
:data (abi-spec/encode "transfer(address,uint256)" [to-norm amount-hex])}) {:to to-norm
:on-result [:navigate-back]}))) :from from-address
:value amount-hex}
{:to (ethereum/normalized-address address)
:from from-address
:data (abi-spec/encode
"transfer(address,uint256)"
[to-norm amount-hex])
;;Note: data from qr (eip681)
:gas gas
:gasPrice gasPrice})})))))
(fx/defn set-and-validate-amount-request (fx/defn set-and-validate-amount-request
{:events [:wallet.request/set-and-validate-amount]} {:events [:wallet.request/set-and-validate-amount]}
@ -390,8 +390,60 @@
(assoc-in [:wallet :request-transaction :amount-text] amount) (assoc-in [:wallet :request-transaction :amount-text] amount)
(assoc-in [:wallet :request-transaction :amount-error] error))})) (assoc-in [:wallet :request-transaction :amount-error] error))}))
;;TODO request isn't implemented
(fx/defn set-symbol-request (fx/defn set-symbol-request
{:events [:wallet.request/set-symbol]} {:events [:wallet.request/set-symbol]}
[{:keys [db]} symbol] [{:keys [db]} symbol]
{:db (-> db {:db (assoc-in db [:wallet :request-transaction :symbol] symbol)})
(assoc-in [:wallet :request-transaction :symbol] symbol))})
(fx/defn prepare-transaction-from-chat
{:events [:wallet/prepare-transaction-from-chat]}
[{:keys [db]}]
(let [identity (:current-chat-id db)]
{:db (assoc db :wallet/prepare-transaction
{:from (ethereum/get-default-account (get-in db [:multiaccount :accounts]))
:to (or (get-in db [:contacts/contacts identity])
(-> identity
contact.db/public-key->new-contact
contact.db/enrich-contact))
:symbol :ETH
:from-chat? true})}))
(fx/defn prepare-transaction-from-wallet
{:events [:wallet/prepare-transaction-from-wallet]}
[{:keys [db]} account]
{:db (assoc db :wallet/prepare-transaction
{:from account
:to nil
:symbol :ETH
:from-chat? false})})
(fx/defn qr-scanner-allowed
{:events [:wallet.send/qr-scanner-allowed]}
[{:keys [db] :as cofx} options]
(fx/merge cofx
{:db (assoc-in db [:wallet/prepare-transaction :modal-opened?] true)}
(bottom-sheet/hide-bottom-sheet)
(navigation/navigate-to-cofx :qr-scanner options)))
(fx/defn wallet-send-set-symbol
{:events [:wallet.send/set-symbol]}
[{:keys [db] :as cofx} symbol]
(fx/merge cofx
{:db (assoc-in db [:wallet/prepare-transaction :symbol] symbol)}
(bottom-sheet/hide-bottom-sheet)))
(fx/defn wallet-send-set-field
{:events [:wallet.send/set-field]}
[{:keys [db] :as cofx} field value]
(fx/merge cofx
{:db (assoc-in db [:wallet/prepare-transaction field] value)}
(bottom-sheet/hide-bottom-sheet)))
(fx/defn navigate-to-recipient-code
{:events [:wallet.send/navigate-to-recipient-code]}
[{:keys [db] :as cofx}]
(fx/merge cofx
{:db (assoc-in db [:wallet/prepare-transaction :modal-opened?] true)}
(bottom-sheet/hide-bottom-sheet)
(navigation/navigate-to-cofx :contact-code nil)))

View File

@ -1,8 +1,6 @@
(ns status-im.wallet.db (ns status-im.wallet.db
(:require [cljs.spec.alpha :as spec] (:require [cljs.spec.alpha :as spec]
[status-im.i18n :as i18n] [status-im.i18n :as i18n]
status-im.ui.screens.wallet.request.db
status-im.ui.screens.wallet.send.db
[status-im.utils.money :as money] [status-im.utils.money :as money]
[status-im.utils.priority-map :refer [empty-transaction-map]])) [status-im.utils.priority-map :refer [empty-transaction-map]]))
@ -25,9 +23,7 @@
(spec/def :wallet/balance any?) (spec/def :wallet/balance any?)
(spec/def :wallet/filters set?) (spec/def :wallet/filters set?)
(spec/def :wallet/wallet (spec/keys :opt-un [:wallet/send-transaction (spec/def :wallet/wallet (spec/keys :opt-un [:wallet/transactions-queue
:wallet/request-transaction
:wallet/transactions-queue
:wallet/balance-loading? :wallet/balance-loading?
:wallet/errors :wallet/errors
:wallet/transactions :wallet/transactions

View File

@ -9,9 +9,9 @@
;;NOTE(goranjovic) - we are internally using symbol ETH for native currencies of any ethereum network ;;NOTE(goranjovic) - we are internally using symbol ETH for native currencies of any ethereum network
;; some sidechains have different names for this native currency, which we handle with `symbol-display` override. ;; some sidechains have different names for this native currency, which we handle with `symbol-display` override.
(defn display-symbol [{:keys [symbol-display symbol] :as token}] (defn display-symbol [{:keys [symbol-display symbol]}]
(when token (when-let [name (or symbol-display symbol)]
(clojure.core/name (or symbol-display symbol)))) (clojure.core/name name)))
;;NOTE(goranjovic) - in addition to custom symbol display, some sidechain native currencies are listed under a different ;;NOTE(goranjovic) - in addition to custom symbol display, some sidechain native currencies are listed under a different
;; ticker on exchange networks. We handle that with `symbol-exchange` override. ;; ticker on exchange networks. We handle that with `symbol-exchange` override.

View File

@ -463,7 +463,7 @@
"finishing-card-setup-steps": "> Loading keys to the card\n> Generating multiaccount", "finishing-card-setup-steps": "> Loading keys to the card\n> Generating multiaccount",
"fleet": "Fleet", "fleet": "Fleet",
"fleet-settings": "Fleet settings", "fleet-settings": "Fleet settings",
"follow-your-interests": "Follow your interests in one of the many Public Chats.", "follow-your-interests": "Follow your interests in one of the many Public Chat.",
"free": "↓ Free", "free": "↓ Free",
"from": "From", "from": "From",
"gas-limit": "Gas limit", "gas-limit": "Gas limit",