send transaction GUI
Signed-off-by: Andrey Shovkoplyas <motor4ik@gmail.com>
Before Width: | Height: | Size: 198 B After Width: | Height: | Size: 283 B |
Before Width: | Height: | Size: 339 B After Width: | Height: | Size: 444 B |
Before Width: | Height: | Size: 465 B After Width: | Height: | Size: 721 B |
|
@ -302,7 +302,13 @@
|
|||
|
||||
(defn create-main-screen-view [current-view]
|
||||
(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]
|
||||
;; NOTE on Android we use Modal component and it manages statusbar area by itself
|
||||
|
|
Before Width: | Height: | Size: 198 B After Width: | Height: | Size: 283 B |
Before Width: | Height: | Size: 339 B After Width: | Height: | Size: 444 B |
Before Width: | Height: | Size: 464 B After Width: | Height: | Size: 721 B |
|
@ -26,8 +26,7 @@
|
|||
(cond
|
||||
(= permission constants/dapp-permission-qr-code)
|
||||
(fx/merge (assoc-in cofx [:db :browser/options :yielding-control?] true)
|
||||
(qr-scanner/scan-qr-code {}
|
||||
{:handler :browser.bridge.callback/qr-code-scanned
|
||||
(qr-scanner/scan-qr-code {:handler :browser.bridge.callback/qr-code-scanned
|
||||
:cancel-handler :browser.bridge.callback/qr-code-canceled
|
||||
:data {:dapp-name dapp-name
|
||||
:permission permission
|
||||
|
|
|
@ -12,10 +12,9 @@
|
|||
|
||||
(if js/goog.DEBUG
|
||||
(.ignoreWarnings (.-YellowBox js-dependencies/react-native)
|
||||
#js
|
||||
["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: 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."])
|
||||
#js ["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: 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))
|
||||
|
||||
(defn init [app-root]
|
||||
|
|
|
@ -34,6 +34,10 @@
|
|||
(or (some #(when (= i (:id (val %))) (key %)) chains)
|
||||
:custom))
|
||||
|
||||
(defn chain-id->chain-name [i]
|
||||
(or (some #(when (= i (:id (val %))) (:name (val %))) chains)
|
||||
:custom))
|
||||
|
||||
(defn chain-keyword->chain-id [k]
|
||||
(get-in chains [k :id]))
|
||||
|
||||
|
@ -91,6 +95,9 @@
|
|||
(defn network->chain-keyword [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]
|
||||
(-> network
|
||||
network->chain-keyword
|
||||
|
|
|
@ -222,7 +222,7 @@
|
|||
|
||||
(handlers/register-handler-fx
|
||||
:mailserver.callback/qr-code-scanned
|
||||
(fn [cofx [_ _ url]]
|
||||
(fn [cofx [_ url _]]
|
||||
(mailserver/set-url-from-qr cofx url)))
|
||||
|
||||
(handlers/register-handler-fx
|
||||
|
@ -327,7 +327,7 @@
|
|||
|
||||
(handlers/register-handler-fx
|
||||
:bootnodes.callback/qr-code-scanned
|
||||
(fn [cofx [_ _ url]]
|
||||
(fn [cofx [_ url _]]
|
||||
(bootnodes/set-bootnodes-from-qr cofx url)))
|
||||
|
||||
(handlers/register-handler-fx
|
||||
|
@ -386,30 +386,32 @@
|
|||
|
||||
(handlers/register-handler-fx
|
||||
: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))))
|
||||
|
||||
(handlers/register-handler-fx
|
||||
: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))))
|
||||
|
||||
;; qr-scanner module
|
||||
|
||||
(handlers/register-handler-fx
|
||||
:qr-scanner.ui/scan-qr-code-pressed
|
||||
(fn [cofx [_ identifier handler & [opts]]]
|
||||
(qr-scanner/scan-qr-code cofx identifier (merge {:handler handler} opts))))
|
||||
(fn [cofx [_ opts]]
|
||||
(qr-scanner/scan-qr-code cofx opts)))
|
||||
|
||||
(handlers/register-handler-fx
|
||||
:qr-scanner.callback/scan-qr-code-success
|
||||
(fn [cofx [_ context data]]
|
||||
(qr-scanner/set-qr-code cofx context data)))
|
||||
(fn [cofx [_ opts data]]
|
||||
(qr-scanner/set-qr-code cofx opts data)))
|
||||
|
||||
(handlers/register-handler-fx
|
||||
:qr-scanner.callback/scan-qr-code-cancel
|
||||
(fn [cofx [_ context]]
|
||||
(qr-scanner/set-qr-code-cancel cofx context)))
|
||||
(fn [cofx [_ opts]]
|
||||
(fx/merge cofx
|
||||
(qr-scanner/set-qr-code-cancel opts)
|
||||
(navigation/navigate-back))))
|
||||
|
||||
;; privacy-policy module
|
||||
|
||||
|
@ -1268,7 +1270,7 @@
|
|||
(handlers/register-handler-fx
|
||||
:contact/qr-code-scanned
|
||||
[(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)
|
||||
fx {:db (assoc db :contacts/new-identity contact-identity)}
|
||||
validation-result (new-chat.db/validate-pub-key db contact-identity)]
|
||||
|
@ -1574,6 +1576,11 @@
|
|||
(fn []
|
||||
(react/dismiss-keyboard!)))
|
||||
|
||||
(handlers/register-handler-fx
|
||||
:dismiss-keyboard
|
||||
(fn [_]
|
||||
{:dismiss-keyboard nil}))
|
||||
|
||||
(handlers/register-handler-fx
|
||||
:wallet-send-request
|
||||
(fn [{:keys [db] :as cofx} [_ public-key amount symbol decimals]]
|
||||
|
|
|
@ -5,33 +5,22 @@
|
|||
[status-im.utils.fx :as fx]))
|
||||
|
||||
(fx/defn scan-qr-code
|
||||
[{:keys [db]} {:keys [deny-handler] :as identifier} qr-codes]
|
||||
{:db (assoc-in db [:qr-codes identifier] qr-codes)
|
||||
:request-permissions-fx {:permissions [:camera]
|
||||
:on-allowed #(re-frame/dispatch
|
||||
[:navigate-to :qr-scanner
|
||||
{:current-qr-context identifier}])
|
||||
:on-denied (if (nil? deny-handler)
|
||||
(fn []
|
||||
(utils/set-timeout
|
||||
#(utils/show-popup (i18n/label :t/error)
|
||||
(i18n/label :t/camera-access-error))
|
||||
50))
|
||||
#(re-frame/dispatch [deny-handler qr-codes]))}})
|
||||
[_ opts]
|
||||
{:request-permissions-fx
|
||||
{:permissions [:camera]
|
||||
:on-allowed #(re-frame/dispatch [:navigate-to :qr-scanner opts])
|
||||
:on-denied (fn []
|
||||
(utils/set-timeout
|
||||
#(utils/show-popup (i18n/label :t/error)
|
||||
(i18n/label :t/camera-access-error))
|
||||
50))}})
|
||||
|
||||
(fx/defn set-qr-code
|
||||
[{:keys [db]} context data]
|
||||
(merge {:db (-> db
|
||||
(update :qr-codes dissoc context)
|
||||
(dissoc :current-qr-context))}
|
||||
(when-let [qr-codes (get-in db [:qr-codes context])]
|
||||
{:dispatch [(:handler qr-codes) context data (dissoc qr-codes :handler)]})))
|
||||
[{:keys [db]} opts data]
|
||||
(when-let [handler (:handler opts)]
|
||||
{:dispatch [handler data opts]}))
|
||||
|
||||
(fx/defn set-qr-code-cancel
|
||||
[{:keys [db]} context]
|
||||
(merge {:db (-> db
|
||||
(update :qr-codes dissoc context)
|
||||
(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]}))))
|
||||
[_ opts]
|
||||
(when-let [handler (:cancel-handler opts)]
|
||||
{:dispatch [handler opts]}))
|
||||
|
|
|
@ -166,6 +166,7 @@
|
|||
(reg-root-key-sub :prices-loading? :prices-loading?)
|
||||
(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/prepare-transaction :wallet/prepare-transaction)
|
||||
|
||||
;;ethereum
|
||||
(reg-root-key-sub :ethereum/current-block :ethereum/current-block)
|
||||
|
@ -284,6 +285,12 @@
|
|||
(fn [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
|
||||
:chain-id
|
||||
:<- [:current-network]
|
||||
|
@ -488,6 +495,12 @@
|
|||
(fn [[macc acc]]
|
||||
(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
|
||||
:multiple-multiaccounts?
|
||||
:<- [:multiaccounts/multiaccounts]
|
||||
|
@ -539,7 +552,7 @@
|
|||
:<- [:chats/current-chat-ui-prop :input-height]
|
||||
:<- [:chats/current-chat-ui-prop :input-focused?]
|
||||
:<- [: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?]]
|
||||
(- (+ home-content-layout-height tabs.styles/tabs-height)
|
||||
(if platform/iphone-x?
|
||||
|
@ -2070,21 +2083,27 @@
|
|||
(get-sufficient-gas-error balance nil nil gas gasPrice))))
|
||||
|
||||
(re-frame/reg-sub
|
||||
:wallet.send/transaction
|
||||
:<- [::send-transaction]
|
||||
:wallet.send/prepare-transaction-with-balance
|
||||
:<- [:wallet/prepare-transaction]
|
||||
:<- [:wallet]
|
||||
:<- [:offline?]
|
||||
:<- [:wallet/all-tokens]
|
||||
:<- [: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]]
|
||||
(let [balance (get-in wallet [:accounts from :balance])
|
||||
token (tokens/asset-for all-tokens chain symbol)]
|
||||
(assoc (merge transaction
|
||||
(when amount
|
||||
(get-sufficient-funds-error balance symbol amount)))
|
||||
(let [balance (get-in wallet [:accounts (:address from) :balance])
|
||||
{:keys [decimals] :as token} (tokens/asset-for all-tokens chain symbol)
|
||||
{:keys [value error]} (wallet.db/parse-amount amount-text decimals)
|
||||
amount (money/formatted->internal value symbol decimals)
|
||||
{: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
|
||||
:token token
|
||||
:token (assoc token :amount (get balance (:symbol token)))
|
||||
:sign-enabled? (and to
|
||||
(nil? amount-error)
|
||||
(not (nil? amount))
|
||||
|
|
|
@ -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]))
|
|
@ -16,7 +16,7 @@
|
|||
(= :previous type)
|
||||
{:padding-right 20 :padding-left 12}
|
||||
:else nil)
|
||||
{:padding-vertical 11 :border-radius 8
|
||||
{:height 44 :border-radius 8
|
||||
:align-items :center :justify-content :center
|
||||
:background-color (cond
|
||||
(#{:secondary :next :previous} type)
|
||||
|
@ -51,7 +51,7 @@
|
|||
|
||||
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)]
|
||||
[react/touchable-opacity (cond-> {:on-press on-press
|
||||
:active-opacity 0.5
|
||||
|
@ -61,7 +61,7 @@
|
|||
(assoc :disabled (boolean disabled?))
|
||||
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}
|
||||
(when (= type :previous)
|
||||
[vector-icons/icon :main-icons/back {:container-style {:width 24 :height 24 :margin-right 4}
|
||||
|
|
|
@ -49,11 +49,14 @@
|
|||
(def green "#44d058") ;; 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"
|
||||
"#7cda00"
|
||||
"#887af9"
|
||||
purple
|
||||
"#51d0f0"
|
||||
"#fe8f59"
|
||||
orange
|
||||
"#d37ef4"])
|
||||
|
||||
(def account-colors ["#9B832F"
|
||||
|
@ -61,7 +64,7 @@
|
|||
"#1D806F"
|
||||
"#FA6565"
|
||||
"#7CDA00"
|
||||
"#887AF9"
|
||||
purple
|
||||
"#8B3131"])
|
||||
|
||||
(def text black)
|
||||
|
|
|
@ -196,5 +196,4 @@
|
|||
|
||||
(def error
|
||||
{:bottom-value 0
|
||||
:color colors/red-light
|
||||
:font-size 12})
|
||||
|
|
|
@ -14,6 +14,6 @@
|
|||
:android (create-status-bar-style {:translucent? true
|
||||
:bar-style "dark-content"})})
|
||||
|
||||
(styles/def status-bar-transparent
|
||||
(styles/def status-bar-black
|
||||
{: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})})
|
|
@ -4,7 +4,7 @@
|
|||
[status-im.utils.platform :as platform]))
|
||||
|
||||
(defn get-config [view-id]
|
||||
(or (get {:recipient-qr-code {:type :transparent}}
|
||||
(or (get {:qr-scanner {:type :black}}
|
||||
view-id)
|
||||
{:type :main}))
|
||||
|
||||
|
@ -26,12 +26,12 @@
|
|||
network-activity-indicator-visible
|
||||
translucent]}
|
||||
(case type
|
||||
:transparent styles/status-bar-transparent
|
||||
:black styles/status-bar-black
|
||||
styles/status-bar-default)]
|
||||
(when bar-style
|
||||
(.setBarStyle react/status-bar-class (clj->js bar-style)) true)
|
||||
(when (and background-color platform/android?)
|
||||
(.setBackgroundColor react/status-bar-class (clj->js background-color)))
|
||||
(when bar-style
|
||||
(.setBarStyle react/status-bar-class (clj->js bar-style)))
|
||||
(when hidden
|
||||
(.setHidden react/status-bar-class (clj->js hidden)))
|
||||
(when network-activity-indicator-visible
|
||||
|
|
|
@ -31,7 +31,7 @@
|
|||
(def tabs-list-data
|
||||
(->>
|
||||
[{:nav-stack :chat-stack
|
||||
:content {:title (i18n/label :t/chats)
|
||||
:content {:title (i18n/label :t/chat)
|
||||
:icon :main-icons/message}
|
||||
:count-subscription :chats/unread-messages-number
|
||||
:accessibility-label :home-tab-button}
|
||||
|
|
|
@ -33,10 +33,10 @@
|
|||
(defn nav-text
|
||||
([text] (nav-text nil text))
|
||||
([{:keys [handler] :as props} text]
|
||||
[react/text (utils/deep-merge {:style styles/item-text
|
||||
:on-press (or handler #(re-frame/dispatch [:navigate-back]))}
|
||||
props)
|
||||
text]))
|
||||
[react/touchable-highlight {:on-press (or handler #(re-frame/dispatch [:navigate-back]))}
|
||||
[react/text (utils/deep-merge {:style styles/item-text}
|
||||
props)
|
||||
text]]))
|
||||
|
||||
(defn nav-clear-text
|
||||
([text] (nav-clear-text nil text))
|
||||
|
|
|
@ -3,11 +3,12 @@
|
|||
[status-im.utils.styles :as styles]))
|
||||
|
||||
(def tooltip-container
|
||||
{:position :absolute
|
||||
:align-items :center
|
||||
:left 0
|
||||
:right 0
|
||||
:top 0})
|
||||
{:position :absolute
|
||||
:align-items :center
|
||||
:pointer-events :none
|
||||
:left 0
|
||||
:right 0
|
||||
:top 0})
|
||||
|
||||
(styles/def bottom-tooltip-container
|
||||
{:position :absolute
|
||||
|
@ -28,10 +29,14 @@
|
|||
|
||||
(defn tooltip-text-container [color]
|
||||
{:padding-horizontal 16
|
||||
:padding-vertical 9
|
||||
:padding-vertical 6
|
||||
:elevation 3
|
||||
: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
|
||||
{:flex-direction :row
|
||||
|
@ -43,8 +48,9 @@
|
|||
:border-radius 8})
|
||||
|
||||
(defn tooltip-text [font-size]
|
||||
{:color colors/red
|
||||
:font-size font-size})
|
||||
{:color colors/red
|
||||
:line-height 15
|
||||
:font-size font-size})
|
||||
|
||||
(def bottom-tooltip-text
|
||||
{:color colors/white})
|
||||
|
|
|
@ -18,9 +18,9 @@
|
|||
(when label
|
||||
[react/view (styles/tooltip-text-container color)
|
||||
[react/text {:style (styles/tooltip-text font-size)} label]])
|
||||
[vector-icons/icon :icons/tooltip-triangle (assoc
|
||||
styles/tooltip-triangle
|
||||
:color color)]]]))
|
||||
#_[vector-icons/icon :icons/tooltip-triangle (assoc
|
||||
styles/tooltip-triangle
|
||||
:color color)]]]))
|
||||
|
||||
(views/defview bottom-tooltip-info [label on-close]
|
||||
(views/letsubs [bottom-anim-value (animation/create-value 150)
|
||||
|
|
|
@ -5,6 +5,5 @@
|
|||
|
||||
(handlers/register-handler-fx
|
||||
:handle-qr-code
|
||||
(fn [cofx [_ _ data]]
|
||||
(log/debug "qr code scanned with data " data)
|
||||
(fn [cofx [_ data _]]
|
||||
(models/handle-qr-code cofx data)))
|
||||
|
|
|
@ -10,7 +10,6 @@
|
|||
[status-im.ui.screens.add-new.styles :as add-new.styles]
|
||||
[status-im.ui.screens.add-new.new-chat.styles :as styles]
|
||||
[status-im.utils.platform :as platform]
|
||||
[reagent.core :as reagent]
|
||||
[status-im.ui.components.list-item.views :as list-item]
|
||||
[status-im.ui.components.chat-icon.screen :as chat-icon]
|
||||
[status-im.multiaccounts.core :as multiaccounts]))
|
||||
|
@ -21,9 +20,6 @@
|
|||
:accessories [:chevron]
|
||||
: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/letsubs [contacts [:contacts/active]
|
||||
new-identity [:contacts/new-identity]
|
||||
|
@ -32,12 +28,11 @@
|
|||
[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-input-container
|
||||
[react/text-input {:ref (fn [v] (js/setTimeout #(reset! tw (if v "100%" "95%")) 100))
|
||||
:on-change-text #(re-frame/dispatch [:new-chat/set-new-identity %])
|
||||
[react/text-input {:on-change-text #(re-frame/dispatch [:new-chat/set-new-identity %])
|
||||
:on-submit-editing #(when (and new-identity (not error-message))
|
||||
(re-frame/dispatch [:contact.ui/contact-code-submitted]))
|
||||
: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
|
||||
;; so its contents will not be erased
|
||||
;; in onWillBlur navigation event handler
|
||||
|
@ -46,8 +41,8 @@
|
|||
:return-key-type :go}]]
|
||||
(when-not platform/desktop?
|
||||
[react/touchable-highlight {:on-press #(re-frame/dispatch [:qr-scanner.ui/scan-qr-code-pressed
|
||||
{:toolbar-title (i18n/label :t/new-contact)}
|
||||
:contact/qr-code-scanned])
|
||||
{:title (i18n/label :t/new-contact)
|
||||
:handler :contact/qr-code-scanned}])
|
||||
:style add-new.styles/button-container
|
||||
:accessibility-label :scan-contact-code-button}
|
||||
[react/view
|
||||
|
|
|
@ -26,9 +26,8 @@
|
|||
:padding-horizontal 16
|
||||
:height 52})
|
||||
|
||||
(styles/defn input [w]
|
||||
(styles/def input
|
||||
{:padding-horizontal 14
|
||||
:width w
|
||||
:desktop {:height 30
|
||||
:width "100%"}
|
||||
:android {:padding 0}})
|
||||
|
|
|
@ -45,8 +45,8 @@
|
|||
:accessibility-label :scan-qr-code-button
|
||||
:icon :main-icons/qr
|
||||
:on-press #(re-frame/dispatch [:qr-scanner.ui/scan-qr-code-pressed
|
||||
{:toolbar-title (i18n/label :t/scan-qr)}
|
||||
:handle-qr-code])}])])
|
||||
{:title (i18n/label :t/scan-qr)
|
||||
:handler :handle-qr-code}])}])])
|
||||
|
||||
(defn add-new []
|
||||
[react/view {:flex 1 :background-color :white}
|
||||
|
|
|
@ -26,8 +26,8 @@
|
|||
|
||||
(def qr-code
|
||||
[react/touchable-highlight {:on-press #(re-frame/dispatch [:qr-scanner.ui/scan-qr-code-pressed
|
||||
{:toolbar-title (i18n/label :t/add-bootnode)}
|
||||
:bootnodes.callback/qr-code-scanned])
|
||||
{:title (i18n/label :t/add-bootnode)
|
||||
:handler :bootnodes.callback/qr-code-scanned}])
|
||||
:style styles/qr-code}
|
||||
[react/view
|
||||
[vector-icons/icon :main-icons/qr {:color colors/blue}]]])
|
||||
|
|
|
@ -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)]]]]]))
|
|
@ -21,7 +21,8 @@
|
|||
[status-im.utils.utils :as utils]
|
||||
[status-im.utils.config :as config]
|
||||
[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?]}]
|
||||
(letsubs [input-text [:chats/current-chat-input-text]
|
||||
|
@ -35,7 +36,7 @@
|
|||
:editable (not cooldown-enabled?)
|
||||
:blur-on-submit false
|
||||
:on-focus #(re-frame/dispatch [:chat.ui/set-chat-ui-props {:input-focused? true
|
||||
:show-stickers? false
|
||||
:input-bottom-sheet nil
|
||||
:messages-focused? false}])
|
||||
:on-blur #(re-frame/dispatch [:chat.ui/set-chat-ui-props {:input-focused? false}])
|
||||
:on-submit-editing #(when single-line-input?
|
||||
|
@ -66,7 +67,7 @@
|
|||
:editable (not cooldown-enabled?)
|
||||
:blur-on-submit false
|
||||
:on-focus #(re-frame/dispatch [:chat.ui/set-chat-ui-props {:input-focused? true
|
||||
:show-stickers? false
|
||||
:input-bottom-sheet nil
|
||||
:messages-focused? false}])
|
||||
:on-blur #(re-frame/dispatch [:chat.ui/set-chat-ui-props {:input-focused? false}])
|
||||
:submit-shortcut {:key "Enter"}
|
||||
|
@ -187,14 +188,13 @@
|
|||
mainnet? [:mainnet?]
|
||||
input-text [:chats/current-chat-input-text]
|
||||
result-box [:chats/current-chat-ui-prop :result-box]
|
||||
show-stickers? [:chats/current-chat-ui-prop :show-stickers?]
|
||||
state-text (reagent/atom "")]
|
||||
input-bottom-sheet [:chats/current-chat-ui-prop :input-bottom-sheet]
|
||||
state-text (reagent/atom "")]
|
||||
{:component-will-unmount #(when platform/desktop?
|
||||
(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))}
|
||||
(let [single-line-input? (:singleLineInput result-box)
|
||||
component (reagent/current-component)
|
||||
set-text #(reset! state-text %)
|
||||
input-text-empty? (if platform/desktop?
|
||||
(string/blank? state-text)
|
||||
|
@ -209,9 +209,10 @@
|
|||
[react/view {:style style/input-container}
|
||||
[input-view {:single-line-input? single-line-input? :set-text set-text :state-text state-text}]
|
||||
(when (and input-text-empty? mainnet?)
|
||||
[stickers/button show-stickers?])
|
||||
(if input-text-empty?
|
||||
[commands-button]
|
||||
[stickers/button (= :stickers input-bottom-sheet)])
|
||||
(when (and input-text-empty?) ;;TODO show only for 1-1 chats?
|
||||
[extensions/button (= :extensions input-bottom-sheet)])
|
||||
(when-not input-text-empty?
|
||||
(if platform/desktop?
|
||||
[send-button/send-button-view {:input-text @state-text}
|
||||
#(do
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
{:on-press (fn [_]
|
||||
(re-frame/dispatch
|
||||
[:chat.ui/set-chat-ui-props {:messages-focused? true
|
||||
:show-stickers? false}])
|
||||
:input-bottom-sheet nil}])
|
||||
(react/dismiss-keyboard!))}
|
||||
[react/view style/datemark-mobile
|
||||
[react/text {:style style/datemark-text}
|
||||
|
|
|
@ -296,7 +296,7 @@
|
|||
(when (and (= content-type constants/content-type-sticker) (:pack content))
|
||||
(re-frame/dispatch [:stickers/open-sticker-pack (:pack content)]))
|
||||
(re-frame/dispatch [:chat.ui/set-chat-ui-props {:messages-focused? true
|
||||
:show-stickers? false}])
|
||||
:input-bottom-sheet nil}])
|
||||
(when-not platform/desktop?
|
||||
(react/dismiss-keyboard!)))))
|
||||
:on-long-press #(when (or (= content-type constants/content-type-text)
|
||||
|
|
|
@ -19,14 +19,14 @@
|
|||
(def icon-container (+ (* icon-horizontal-margin 2) icon-size))
|
||||
(def scroll-x (reagent/atom 0))
|
||||
|
||||
(defn button [show-stickers?]
|
||||
(defn button [stickers-showing?]
|
||||
[react/touchable-highlight
|
||||
{: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)))
|
||||
:accessibility-label :show-stickers-icon}
|
||||
[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 []
|
||||
[react/view {:style {:flex 1 :align-items :center :justify-content :center}}
|
||||
|
|
|
@ -26,7 +26,8 @@
|
|||
[status-im.ui.screens.profile.tribute-to-talk.views
|
||||
:as
|
||||
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]]))
|
||||
|
||||
(defn add-contact-bar
|
||||
|
@ -287,7 +288,7 @@
|
|||
:on-press (fn [_]
|
||||
(re-frame/dispatch
|
||||
[:chat.ui/set-chat-ui-props {:messages-focused? true
|
||||
:show-stickers? false}])
|
||||
:input-bottom-sheet nil}])
|
||||
(react/dismiss-keyboard!))}
|
||||
[react/view (style/intro-header-container height intro-status no-messages)
|
||||
;; Icon section
|
||||
|
@ -396,7 +397,7 @@
|
|||
(letsubs [{:keys [public? chat-id chat-name show-input? group-chat contact] :as current-chat}
|
||||
[:chats/current-chat]
|
||||
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?]
|
||||
anim-translate-y (animation/create-value
|
||||
(if two-pane-ui-enabled? 0 connectivity/neg-connectivity-bar-height))]
|
||||
|
@ -432,8 +433,12 @@
|
|||
[messages-view current-chat modal?])]]
|
||||
(when show-input?
|
||||
[input/container])
|
||||
(when show-stickers?
|
||||
[stickers/stickers-view])]))
|
||||
(case input-bottom-sheet
|
||||
:stickers
|
||||
[stickers/stickers-view]
|
||||
:extensions
|
||||
[extensions/extensions-view]
|
||||
nil)]))
|
||||
|
||||
(defview chat []
|
||||
[chat-root false])
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
status-im.transport.db
|
||||
status-im.multiaccounts.db
|
||||
status-im.contact.db
|
||||
status-im.ui.screens.qr-scanner.db
|
||||
status-im.ui.screens.group.db
|
||||
status-im.chat.specs
|
||||
status-im.ui.screens.profile.db
|
||||
|
@ -183,6 +182,8 @@
|
|||
|
||||
(spec/def :popover/popover (spec/nilable map?))
|
||||
|
||||
(spec/def :wallet/prepare-transaction (spec/nilable map?))
|
||||
|
||||
(spec/def ::db (spec/keys :opt [:contacts/contacts
|
||||
:contacts/new-identity
|
||||
:contacts/new-identity-error
|
||||
|
@ -248,6 +249,7 @@
|
|||
:bottom-sheet/view
|
||||
:bottom-sheet/options
|
||||
:wallet/custom-token-screen
|
||||
:wallet/prepare-transaction
|
||||
:signing/in-progress?
|
||||
:signing/queue
|
||||
:signing/sign
|
||||
|
@ -282,9 +284,6 @@
|
|||
:navigation/navigation-stack
|
||||
:navigation/prev-tab-view-id
|
||||
:navigation/prev-view-id
|
||||
:qr/qr-codes
|
||||
:qr/qr-modal
|
||||
:qr/current-qr-context
|
||||
:chat/chats
|
||||
:chat/current-chat-id
|
||||
:chat/chat-id
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
:on-change-text #(re-frame/dispatch [:set :new-chat-name %])
|
||||
:default-value new-group-name
|
||||
:placeholder (i18n/label :t/set-a-topic)
|
||||
:style (add-new.styles/input "100%")
|
||||
:style add-new.styles/input
|
||||
:accessibility-label :chat-name-input}]])
|
||||
|
||||
(defn- render-contact [contact]
|
||||
|
|
|
@ -40,8 +40,8 @@
|
|||
:accessibility-label :scan-qr-code-button
|
||||
:icon :main-icons/qr
|
||||
:on-press #(hide-sheet-and-dispatch [:qr-scanner.ui/scan-qr-code-pressed
|
||||
{:toolbar-title (i18n/label :t/scan-qr)}
|
||||
:handle-qr-code])}]
|
||||
{:title (i18n/label :t/scan-qr)
|
||||
:handler :handle-qr-code}])}]
|
||||
[list-item/list-item
|
||||
{:theme :action
|
||||
:title :t/invite-friends
|
||||
|
|
|
@ -34,8 +34,8 @@
|
|||
|
||||
(def qr-code
|
||||
[react/touchable-highlight {:on-press #(re-frame/dispatch [:qr-scanner.ui/scan-qr-code-pressed
|
||||
{:toolbar-title (i18n/label :t/add-mailserver)}
|
||||
:mailserver.callback/qr-code-scanned])
|
||||
{:title (i18n/label :t/add-mailserver)
|
||||
:handler :mailserver.callback/qr-code-scanned}])
|
||||
:style styles/qr-code}
|
||||
[react/view
|
||||
[vector-icons/icon :main-icons/qr {:color colors/blue}]]])
|
||||
|
|
|
@ -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?))
|
|
@ -1,77 +1,11 @@
|
|||
(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]))
|
||||
(ns status-im.ui.screens.qr-scanner.styles)
|
||||
|
||||
(def barcode-scanner-container
|
||||
{:flex 1
|
||||
:background-color :white})
|
||||
|
||||
(styles/def barcode-scanner
|
||||
{:flex 1
|
||||
:elevation -10
|
||||
:android {:marginTop 10}})
|
||||
|
||||
(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})
|
||||
(def viewfinder-port
|
||||
{:position :absolute
|
||||
:left 0
|
||||
:top 0
|
||||
:bottom 0
|
||||
:right 0
|
||||
:align-items :center
|
||||
:justify-content :center
|
||||
:flex 1})
|
|
@ -1,51 +1,58 @@
|
|||
(ns status-im.ui.screens.qr-scanner.views
|
||||
(:require-macros [status-im.utils.views :refer [defview letsubs]])
|
||||
(:require [reagent.core :as reagent]
|
||||
[re-frame.core :as re-frame]
|
||||
(:require [re-frame.core :as re-frame]
|
||||
[status-im.i18n :as i18n]
|
||||
[status-im.ui.components.react :as react]
|
||||
[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.components.toolbar.actions :as actions]))
|
||||
[status-im.ui.components.colors :as colors]))
|
||||
|
||||
(defview qr-scanner-toolbar [title identifier]
|
||||
[react/view
|
||||
[toolbar/toolbar {:style {:background-color :white}}
|
||||
[toolbar/nav-button (actions/back
|
||||
#(do
|
||||
(re-frame/dispatch [:qr-scanner.callback/scan-qr-code-cancel identifier])
|
||||
(re-frame/dispatch [:navigate-back])))]
|
||||
[toolbar/content-title title]]])
|
||||
(defn- topbar [camera-flashlight {:keys [title] :as opts}]
|
||||
[topbar/toolbar
|
||||
{:transparent? true}
|
||||
[topbar/nav-text
|
||||
{:style {:color colors/white :margin-left 16}
|
||||
:handler #(re-frame/dispatch [:qr-scanner.callback/scan-qr-code-cancel opts])}
|
||||
(i18n/label :t/cancel)]
|
||||
[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]
|
||||
(re-frame/dispatch [:qr-scanner.callback/scan-qr-code-success identifier (camera/get-qr-code-data data)]))
|
||||
(defn corner [border1 border2 corner]
|
||||
[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
|
||||
;; that two separate instances of `qr-scanner` screen can work simultaneously
|
||||
(defview qr-scanner [{identifier :current-qr-context} screen-focused?]
|
||||
(letsubs [camera-initialized? (reagent/atom false)
|
||||
barcode-read? (reagent/atom false)]
|
||||
[react/view styles/barcode-scanner-container
|
||||
[qr-scanner-toolbar (or (:toolbar-title identifier) (i18n/label :t/scan-qr)) identifier]
|
||||
;; camera component should be hidden if screen is not shown
|
||||
;; otherwise another screen with camera from a different stack
|
||||
;; will not work properly
|
||||
(when @screen-focused?
|
||||
[camera/camera {:onBarCodeRead #(if (:multiple? identifier)
|
||||
(on-barcode-read identifier %)
|
||||
(when-not @barcode-read?
|
||||
(do (reset! barcode-read? true)
|
||||
(on-barcode-read identifier %))))
|
||||
:ref #(reset! camera-initialized? true)
|
||||
:style styles/barcode-scanner}])
|
||||
[react/view styles/rectangle-container
|
||||
[react/view styles/rectangle
|
||||
[react/image {:source {:uri :corner_left_top}
|
||||
:style styles/corner-left-top}]
|
||||
[react/image {:source {:uri :corner_right_top}
|
||||
:style styles/corner-right-top}]
|
||||
[react/image {:source {:uri :corner_right_bottom}
|
||||
:style styles/corner-right-bottom}]
|
||||
[react/image {:source {:uri :corner_left_bottom}
|
||||
:style styles/corner-left-bottom}]]]]))
|
||||
(defn- viewfinder [size]
|
||||
[react/view {:style styles/viewfinder-port}
|
||||
[react/view {:width size :height size :justify-content :space-between}
|
||||
[react/view {:flex-direction :row :justify-content :space-between}
|
||||
[corner :border-top-width :border-left-width :border-top-left-radius]
|
||||
[corner :border-top-width :border-right-width :border-top-right-radius]]
|
||||
[react/view {:flex-direction :row :justify-content :space-between}
|
||||
[corner :border-bottom-width :border-left-width :border-bottom-left-radius]
|
||||
[corner :border-bottom-width :border-right-width :border-bottom-right-radius]]]])
|
||||
|
||||
(defn on-barcode-read [opts data]
|
||||
(re-frame/dispatch [:qr-scanner.callback/scan-qr-code-success opts (camera/get-qr-code-data data)]))
|
||||
|
||||
(defview qr-scanner []
|
||||
(letsubs [read-once? (atom false)
|
||||
{:keys [height width]} [:dimensions/window]
|
||||
camera-flashlight [:wallet.send/camera-flashlight]
|
||||
opts [:get-screen-params]]
|
||||
[react/view {:style {:flex 1 :background-color colors/black}}
|
||||
[topbar camera-flashlight opts]
|
||||
[react/with-activity-indicator
|
||||
{}
|
||||
[camera/camera
|
||||
{:style {:flex 1}
|
||||
;:torchMode (camera/set-torch camera-flashlight)
|
||||
:captureAudio false
|
||||
:onBarCodeRead #(when-not @read-once?
|
||||
(reset! read-once? true)
|
||||
(on-barcode-read opts %))}]]
|
||||
[viewfinder (int (* 2 (/ (min height width) 3)))]]))
|
|
@ -3,6 +3,5 @@
|
|||
(def browser-stack
|
||||
{:name :browser-stack
|
||||
:screens [:open-dapp
|
||||
:browser
|
||||
:qr-scanner]
|
||||
:browser]
|
||||
:config {:initialRouteName :open-dapp}})
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
:select-chat
|
||||
:profile
|
||||
:new
|
||||
:qr-scanner
|
||||
:take-picture
|
||||
:new-group
|
||||
:add-participants-toggle-list
|
||||
|
|
|
@ -11,4 +11,6 @@
|
|||
:welcome
|
||||
:keycard-welcome
|
||||
:new-chat
|
||||
:new-public-chat])
|
||||
:new-public-chat
|
||||
:contact-code
|
||||
:qr-scanner])
|
||||
|
|
|
@ -35,7 +35,6 @@
|
|||
:mobile-network-settings
|
||||
:backup-seed
|
||||
:tribute-to-talk
|
||||
:qr-scanner
|
||||
:my-profile-ext-settings]
|
||||
|
||||
config/hardwallet-enabled?
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
(ns status-im.ui.screens.routing.screens
|
||||
(: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.recover.views :as multiaccounts.recover]
|
||||
[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-public-chat.view :as new-public-chat]
|
||||
|
@ -59,8 +58,6 @@
|
|||
[status-im.ui.screens.stickers.views :as stickers]
|
||||
[status-im.ui.screens.wallet.collectibles.views :as collectibles]
|
||||
[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.transactions.views :as wallet-transactions]
|
||||
[status-im.ui.screens.wallet.custom-tokens.views :as custom-tokens]
|
||||
|
@ -124,7 +121,7 @@
|
|||
:profile profile.contact/profile
|
||||
:new add-new/add-new
|
||||
: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
|
||||
:add-participants-toggle-list group/add-participants-toggle-list
|
||||
:contact-toggle-list group/contact-toggle-list
|
||||
|
@ -140,14 +137,7 @@
|
|||
:wallet wallet.accounts/accounts-overview
|
||||
:wallet-account wallet.account/account
|
||||
:collectibles-list collectibles/collectibles-list
|
||||
:contact-code 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
|
||||
:contact-code [:modal wallet.components/contact-code]
|
||||
:wallet-transaction-details wallet-transactions/transaction-details
|
||||
:wallet-settings-hook wallet-settings/settings-hook
|
||||
:wallet-settings-assets wallet-settings/manage-assets
|
||||
|
|
|
@ -10,19 +10,6 @@
|
|||
:account-settings
|
||||
:collectibles-list
|
||||
: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-settings-hook
|
||||
:wallet-settings-assets
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
:justify-content :space-between
|
||||
:padding-top 16
|
||||
:padding-left 16
|
||||
:padding-right 24
|
||||
:margin-bottom 11})
|
||||
|
||||
(def message-header
|
||||
|
|
|
@ -4,8 +4,6 @@
|
|||
[re-frame.core :as re-frame]
|
||||
[status-im.multiaccounts.core :as multiaccounts]
|
||||
[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.button :as button]
|
||||
[status-im.ui.components.copyable-text :as copyable-text]
|
||||
|
@ -22,55 +20,31 @@
|
|||
[status-im.ui.screens.signing.styles :as styles]
|
||||
[status-im.react-native.resources :as resources]
|
||||
[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]))
|
||||
|
||||
(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 []
|
||||
[react/view {:height 1 :background-color colors/gray-lighter}])
|
||||
|
||||
(defn displayed-name [contact]
|
||||
(if (or (:preferred-name contact) (:name contact))
|
||||
(multiaccounts/displayed-name contact)
|
||||
(:address contact)))
|
||||
(utils/get-shortened-checksum-address (:address contact))))
|
||||
|
||||
(defn contact-item [title contact]
|
||||
[list-item/list-item
|
||||
{:title-prefix title
|
||||
{:title title
|
||||
:title-prefix-width 45
|
||||
:type :small
|
||||
:title
|
||||
[copyable-text/copyable-text-view
|
||||
{:copied-text (displayed-name contact)}
|
||||
[react/text
|
||||
{:ellipsize-mode :middle
|
||||
:number-of-lines 1
|
||||
:style {:color colors/gray
|
||||
:font-family "monospace"
|
||||
;; since this goes in list-item title
|
||||
;; which has design constraints
|
||||
;; specified in figma spec,
|
||||
;; better to do this
|
||||
:line-height 22}}
|
||||
(displayed-name contact)]]}])
|
||||
:accessories
|
||||
[[copyable-text/copyable-text-view
|
||||
{:copied-text (displayed-name contact)}
|
||||
[react/text
|
||||
{:ellipsize-mode :middle
|
||||
:number-of-lines 1
|
||||
:style {:font-family "monospace"
|
||||
:line-height 22}}
|
||||
(displayed-name contact)]]]}])
|
||||
|
||||
(defn token-item [{:keys [icon color] :as token} display-symbol]
|
||||
(when token
|
||||
|
@ -107,9 +81,10 @@
|
|||
[{:style {:color colors/black}} (displayed-name contact)]]
|
||||
[react/text {:style {:margin-top 6 :color colors/gray}}
|
||||
(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])})
|
||||
[react/view {:padding 6}
|
||||
[react/text {:style {:color colors/blue}} (i18n/label :t/cancel)]]]])
|
||||
[button/button (merge {:type :secondary
|
||||
:container-style {:padding-horizontal 24}
|
||||
: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/letsubs [pin [:hardwallet/pin]
|
||||
|
@ -268,6 +243,13 @@
|
|||
{:content (fn [] [sheets/fee-bottom-sheet fee-display-symbol])
|
||||
: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/letsubs [fee [:signing/fee]
|
||||
sign [:signing/sign]
|
||||
|
@ -275,7 +257,8 @@
|
|||
{:keys [amount-error gas-error]} [:signing/amount-errors (:address from)]
|
||||
keycard-multiaccount? [:keycard-multiaccount?]
|
||||
prices [:prices]
|
||||
wallet-currency [:wallet/currency]]
|
||||
wallet-currency [:wallet/currency]
|
||||
mainnet? [:mainnet?]]
|
||||
(let [display-symbol (wallet.utils/display-symbol token)
|
||||
fee-display-symbol (wallet.utils/display-symbol (tokens/native-currency chain))]
|
||||
[react/view styles/sheet
|
||||
|
@ -285,6 +268,10 @@
|
|||
[react/view {:padding-top 20}
|
||||
[password-view sign]]
|
||||
[react/view
|
||||
(when-not mainnet?
|
||||
[react/view
|
||||
[network-item]
|
||||
[separator]])
|
||||
[contact-item (i18n/label :t/from) from]
|
||||
[separator]
|
||||
[contact-item (i18n/label :t/to) contact]
|
||||
|
@ -300,47 +287,11 @@
|
|||
:disabled? (or amount-error gas-error)
|
||||
: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/letsubs [tx [:signing/tx]
|
||||
{window-height :height} [:dimensions/window]]
|
||||
;;we use select-keys here because we don't want to update view if other keys in map is changed
|
||||
[signing-view (when tx (select-keys tx [:from :contact :amount :token :approve? :message])) window-height]))
|
||||
(views/letsubs [tx [:signing/tx]]
|
||||
[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
|
||||
(when tx (select-keys tx [:from :contact :amount :token :approve? :message]))
|
||||
#(if (:message %)
|
||||
[message-sheet]
|
||||
[sheet %])]))
|
|
@ -20,6 +20,7 @@
|
|||
[status-im.ui.screens.popover.views :as popover]
|
||||
[status-im.ui.screens.multiaccounts.recover.views :as recover.views]
|
||||
[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.cryptostrikers.views
|
||||
status-im.ui.screens.wallet.collectibles.cryptokitties.views
|
||||
|
@ -167,6 +168,7 @@
|
|||
;; see https://reactnavigation.org/docs/en/state-persistence.html#development-mode
|
||||
:persistNavigationState (when js/goog.DEBUG persist-state)
|
||||
:loadNavigationState (when js/goog.DEBUG load-state)}]
|
||||
[wallet/prepare-transaction]
|
||||
[signing/signing]
|
||||
[bottom-sheet]
|
||||
[popover/popover]]]))})))
|
||||
|
|
|
@ -39,7 +39,7 @@
|
|||
[icons/icon icon {:color colors/white}]
|
||||
[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]
|
||||
portfolio-value [:account-portfolio-value address]
|
||||
window-width [:dimensions/window-width]]
|
||||
|
@ -62,9 +62,15 @@
|
|||
:accessibility-label :share-wallet-address-icon}]]]
|
||||
[react/view {:height 52 :background-color colors/black-transparent-20
|
||||
: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}]
|
||||
[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]
|
||||
(fn [{:keys [name icon amount] :as collectible}]
|
||||
|
|
|
@ -35,20 +35,22 @@
|
|||
:accessibility-label :wallet-backup-recovery-title
|
||||
:on-press #(hide-sheet-and-dispatch [:navigate-to :backup-seed])}])]))
|
||||
|
||||
(defn send-receive [address]
|
||||
(defn send-receive [account]
|
||||
[react/view
|
||||
[list-item/list-item
|
||||
{:theme :action
|
||||
:title :t/wallet-send
|
||||
:icon :main-icons/send
|
||||
: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
|
||||
{:theme :action
|
||||
:title :t/receive
|
||||
:icon :main-icons/receive
|
||||
: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 []
|
||||
[react/view
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
portfolio-value [:account-portfolio-value address]]
|
||||
[react/touchable-highlight {:on-press #(re-frame/dispatch [:navigate-to :wallet-account account])
|
||||
: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}])}
|
||||
[react/view {:style (styles/card color)}
|
||||
[react/view {:flex-direction :row :align-items :center :justify-content :space-between}
|
||||
|
@ -61,16 +61,18 @@
|
|||
(when active?
|
||||
[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}]
|
||||
[list-item/list-item
|
||||
{:title-prefix (wallet.utils/format-amount amount decimals)
|
||||
:title (wallet.utils/display-symbol token)
|
||||
:title-color-override colors/gray
|
||||
:subtitle (str (if value value 0) " " currency)
|
||||
:icon (if icon
|
||||
[list/item-image icon]
|
||||
[chat-icon/custom-icon-view-list (:name token) color])}]))
|
||||
(cond-> {:title-prefix (wallet.utils/format-amount amount decimals)
|
||||
:title (wallet.utils/display-symbol token)
|
||||
:title-color-override colors/gray
|
||||
:subtitle (str (if value value 0) " " currency)
|
||||
:icon (if icon
|
||||
[list/item-image icon]
|
||||
[chat-icon/custom-icon-view-list (:name token) color])}
|
||||
on-press
|
||||
(assoc :on-press #(on-press token)))]))
|
||||
|
||||
(views/defview assets []
|
||||
(views/letsubs [{:keys [tokens nfts]} [:wallet/all-visible-assets-with-values]
|
||||
|
|
|
@ -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})
|
|
@ -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}}]]))
|
|
@ -1,178 +1,10 @@
|
|||
(ns status-im.ui.screens.wallet.components.styles
|
||||
(: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})
|
||||
(:require [status-im.ui.components.colors :as colors]))
|
||||
|
||||
(def recent-recipients
|
||||
{:flex 1
|
||||
: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
|
||||
{:height 1
|
||||
:margin-horizontal 15
|
||||
: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})
|
||||
{:height 1
|
||||
:background-color colors/gray-lighter})
|
|
@ -2,341 +2,45 @@
|
|||
(:require [clojure.string :as string]
|
||||
[re-frame.core :as re-frame]
|
||||
[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.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.styles :as components.styles]
|
||||
[status-im.ui.components.toolbar.actions :as actions]
|
||||
[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.wallet.utils :as wallet.utils]
|
||||
[status-im.utils.core :as utils.core]
|
||||
[status-im.utils.money :as money]
|
||||
[status-im.utils.utils :as utils.utils])
|
||||
[status-im.ui.components.text-input.view :as text-input]
|
||||
[status-im.ui.components.colors :as colors])
|
||||
(: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 []
|
||||
[react/view styles/separator])
|
||||
|
||||
(defn button-text [label]
|
||||
[react/text {:style styles/button-text}
|
||||
label])
|
||||
(defn- recipient-topbar [content]
|
||||
[topbar/toolbar {:transparent? true}
|
||||
[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/letsubs [network-id [:chain-id]]
|
||||
[react/view
|
||||
[react/view styles/network-container
|
||||
[react/view styles/network-icon
|
||||
[vector-icons/icon :main-icons/network {:color :white}]]
|
||||
[react/text
|
||||
{:style {:flex 1
|
||||
:padding-left 16}}
|
||||
(cond (ethereum/testnet? network-id)
|
||||
(i18n/label :t/testnet-text {:testnet (get-in ethereum/chains [(ethereum/chain-id->chain-keyword network-id) :name] "Unknown")})
|
||||
|
||||
(ethereum/sidechain? network-id)
|
||||
(i18n/label :t/sidechain-text {:sidechain (get-in ethereum/chains [(ethereum/chain-id->chain-keyword network-id) :name] "Unknown")})
|
||||
|
||||
:else
|
||||
(i18n/label :t/mainnet-text))]]]))
|
||||
(views/defview contact-code []
|
||||
(views/letsubs [content (reagent/atom nil)]
|
||||
[react/view {:flex 1}
|
||||
[recipient-topbar @content]
|
||||
[react/view
|
||||
[text-input/text-input-with-label
|
||||
{:multiline true
|
||||
:container {:margin 16 :padding-vertical 16 :height 72}
|
||||
:style {:text-align-vertical :top :height 42}
|
||||
:placeholder "0x... or username.domain.eth" ;(i18n/label :t/recipient-code)
|
||||
:on-change-text #(reset! content %)
|
||||
:accessibility-label :recipient-address-input}]
|
||||
[react/text {:style {:color colors/gray :margin-horizontal 16}}
|
||||
"Enter address or username of the recepient"]]]))
|
|
@ -1,13 +1,7 @@
|
|||
(ns status-im.ui.screens.wallet.navigation
|
||||
(:require [status-im.constants :as constants]
|
||||
[status-im.ui.screens.navigation :as navigation]
|
||||
(:require [status-im.ui.screens.navigation :as navigation]
|
||||
[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
|
||||
[db [event]]
|
||||
(let [wallet-set-up-passed? (get-in db [:multiaccount :wallet-set-up-passed?])]
|
||||
|
@ -15,20 +9,6 @@
|
|||
db
|
||||
(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
|
||||
[db [event]]
|
||||
(dissoc db :wallet/custom-token-screen))
|
||||
|
|
|
@ -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]))
|
|
@ -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})
|
|
@ -3,61 +3,12 @@
|
|||
[reagent.core :as reagent]
|
||||
[status-im.ethereum.eip55 :as eip55]
|
||||
[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.copyable-text :as copyable-text]
|
||||
[status-im.ui.components.qr-code-viewer.views :as qr-code-viewer]
|
||||
[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])
|
||||
[status-im.ui.components.react :as react])
|
||||
(: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/letsubs [{:keys [address]} [:popover/popover]
|
||||
chain-id [:chain-id]
|
||||
|
|
|
@ -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]))
|
|
@ -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])])
|
|
@ -1,56 +1,14 @@
|
|||
(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]))
|
||||
(ns status-im.ui.screens.wallet.send.styles)
|
||||
|
||||
(def send-transaction-form
|
||||
{:flex 1
|
||||
:padding-bottom 60})
|
||||
(defn sheet [small-screen?]
|
||||
{:background-color :white
|
||||
:border-top-right-radius 16
|
||||
:border-top-left-radius 16
|
||||
:padding-bottom (if small-screen? 40 60)})
|
||||
|
||||
(def signing-phrase-description
|
||||
{:padding-top 8})
|
||||
|
||||
(def password-container
|
||||
{:flex 1
|
||||
:padding-vertical 20})
|
||||
|
||||
(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})
|
||||
(defn header [small-screen?]
|
||||
{:flex-direction :row
|
||||
:align-items :center
|
||||
:justify-content :space-between
|
||||
:padding-top (when-not small-screen? 16)
|
||||
:padding-left 16})
|
|
@ -1,67 +1,122 @@
|
|||
(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]
|
||||
[reagent.core :as reagent]
|
||||
[status-im.i18n :as i18n]
|
||||
[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.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?]
|
||||
[toolbar/toolbar
|
||||
{:right {:type :next
|
||||
:disabled? (not sign-enabled?)
|
||||
:on-press #(re-frame/dispatch [:wallet.ui/sign-transaction-button-clicked])
|
||||
:accessibility-label :sign-transaction-button
|
||||
:label :t/transactions-sign-transaction}}])
|
||||
(defn header [small-screen?]
|
||||
[react/view (styles/header small-screen?)
|
||||
[react/view {:flex 1}
|
||||
[react/text {:style (merge {:typography :title-bold} (when small-screen? {:font-size 15}))}
|
||||
(i18n/label :t/send-transaction)]]
|
||||
[button/button {:type :secondary
|
||||
:container-style {:padding-horizontal 24}
|
||||
:label (i18n/label :t/cancel)
|
||||
:on-press #(re-frame/dispatch [:set :wallet/prepare-transaction nil])}]])
|
||||
|
||||
;;TODO DEPRECATED
|
||||
(defn- render-send-transaction-view
|
||||
[{:keys [transaction scroll amount-input]}]
|
||||
(let [{:keys [from amount amount-text amount-error token sign-enabled?
|
||||
asset-error to to-name symbol]} transaction]
|
||||
[wallet.components/simple-screen {:avoid-keyboard? true}
|
||||
[topbar/simple-toolbar (i18n/label :t/send-transaction)]
|
||||
[react/view components.styles/flex
|
||||
[wallet.components/network-info]
|
||||
[react/scroll-view {:keyboard-should-persist-taps :never
|
||||
:keyboard-dismiss-mode :on-drag
|
||||
:ref #(reset! scroll %)
|
||||
:on-content-size-change #(when (and scroll @scroll)
|
||||
(.scrollToEnd @scroll))}
|
||||
[react/view styles/send-transaction-form
|
||||
[wallet.components/recipient-selector
|
||||
{:address to
|
||||
:name to-name}]
|
||||
[wallet.components/asset-selector
|
||||
{: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 asset-selector [{:keys [token from]}]
|
||||
(let [{:keys [name icon color]} token]
|
||||
[react/touchable-highlight
|
||||
{:on-press #(do
|
||||
(re-frame/dispatch [:dismiss-keyboard])
|
||||
(re-frame/dispatch [:bottom-sheet/show-sheet
|
||||
{:content (fn [] [sheets/assets (:address from)])
|
||||
:content-height 300}]))}
|
||||
[react/view {:style {:flex-direction :row
|
||||
:align-items :center
|
||||
:margin-left 16}
|
||||
:accessibility-label :choose-asset-button}
|
||||
(if icon
|
||||
[list/item-image (assoc icon :style {:background-color colors/gray-lighter
|
||||
:border-radius 50} :image-style {:width 32 :height 32})]
|
||||
[chat-icon/custom-icon-view-list name color 32])
|
||||
[react/text {:style {:margin-left 8}}
|
||||
(wallet.utils/display-symbol token)]
|
||||
[icons/icon :main-icons/dropdown {:color colors/gray}]]]))
|
||||
|
||||
(defn- send-transaction-view [{:keys [scroll]}]
|
||||
(let [amount-input (atom nil)
|
||||
handler #(when (and scroll @scroll @amount-input (.isFocused @amount-input)) (.scrollToEnd @scroll))]
|
||||
(reagent/create-class
|
||||
{:component-did-mount (fn [_]
|
||||
;;NOTE(goranjovic): keyboardDidShow is for android and keyboardWillShow for ios
|
||||
(.addListener react/keyboard "keyboardDidShow" handler)
|
||||
(.addListener react/keyboard "keyboardWillShow" handler))
|
||||
:reagent-render (fn [opts] (render-send-transaction-view
|
||||
(assoc opts :amount-input amount-input)))})))
|
||||
(defn render-account [account {:keys [amount decimals] :as token}]
|
||||
[list-item/list-item
|
||||
{:icon [chat-icon/custom-icon-view-list (:name account) (:color account)]
|
||||
:title (:name account)
|
||||
:subtitle (str (wallet.utils/format-amount amount decimals)
|
||||
" "
|
||||
(wallet.utils/display-symbol token))
|
||||
:accessories [:chevron]
|
||||
:on-press #(do
|
||||
(re-frame/dispatch [:dismiss-keyboard])
|
||||
(re-frame/dispatch [:bottom-sheet/show-sheet
|
||||
{:content (fn [] [sheets/accounts-list :from])
|
||||
:content-height 300}]))}])
|
||||
|
||||
(defview send-transaction []
|
||||
(letsubs [transaction [:wallet.send/transaction]
|
||||
scroll (atom nil)]
|
||||
[send-transaction-view {:transaction transaction
|
||||
:scroll scroll}]))
|
||||
(defn render-contact [contact from-chat?]
|
||||
(if from-chat?
|
||||
[list-item/list-item {:title (multiaccounts/displayed-name contact)
|
||||
:icon (multiaccounts/displayed-photo contact)}]
|
||||
[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]))
|
|
@ -81,8 +81,8 @@
|
|||
(navigation/navigate-to-cofx (assoc-in cofx [:db :contacts/identity] public-key) :profile nil))))
|
||||
|
||||
(fx/defn handle-eip681 [cofx url]
|
||||
{:dispatch-n [[:navigate-to :wallet-send-transaction]
|
||||
[:wallet/fill-request-from-url url :deep-link]]})
|
||||
{:dispatch-n [[:navigate-to :wallet]
|
||||
[:wallet/fill-request-from-url url]]})
|
||||
|
||||
(defn handle-not-found [full-url]
|
||||
(log/info "universal-links: no handler for " full-url))
|
||||
|
|
|
@ -8,7 +8,8 @@
|
|||
[status-im.ethereum.ens :as ens]
|
||||
[status-im.i18n :as i18n]
|
||||
[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
|
||||
{: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?]
|
||||
{:pre [(not (nil? address))]}
|
||||
(let [name (or name (find-address-name db address))
|
||||
;;TODO request isn't implemented
|
||||
data-path (if request?
|
||||
[:wallet :request-transaction]
|
||||
[:wallet :send-transaction])]
|
||||
(update-in db data-path
|
||||
(fn [{old-symbol :symbol :as old-transaction}]
|
||||
(let [symbol-changed? (not= old-symbol symbol)]
|
||||
(cond-> (assoc old-transaction :to address :to-name name :public-key public-key)
|
||||
value (assoc :amount value)
|
||||
symbol (assoc :symbol symbol)
|
||||
(and gas symbol-changed?) (assoc :gas (money/bignumber gas))
|
||||
from-chat? (assoc :from-chat? from-chat?)
|
||||
(and gasPrice symbol-changed?)
|
||||
(assoc :gas-price (money/bignumber gasPrice))
|
||||
(and symbol (not gasPrice) symbol-changed?)
|
||||
(assoc :gas-price (ethereum/estimate-gas symbol))))))))
|
||||
:wallet/prepare-transaction
|
||||
:wallet/prepare-transaction)]
|
||||
(update db data-path
|
||||
(fn [{old-symbol :symbol :as old-transaction}]
|
||||
(let [symbol-changed? (not= old-symbol symbol)]
|
||||
(cond-> (assoc old-transaction
|
||||
:to address :to-name name :public-key public-key
|
||||
:gas (money/bignumber gas)
|
||||
:gas-price (money/bignumber gasPrice))
|
||||
value (assoc :amount value)
|
||||
symbol (assoc :symbol symbol)
|
||||
from-chat? (assoc :from-chat? from-chat?)))))))
|
||||
|
||||
;;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
|
||||
"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
|
||||
(defn changed-asset [{:keys [db] :as fx} old-symbol new-symbol]
|
||||
(-> fx
|
||||
(merge {:wallet/update-gas-price
|
||||
{:success-event :wallet/update-gas-price-success
|
||||
:edit? false}})
|
||||
(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]
|
||||
(assoc-in [:db :wallet/prepare-transaction :amount] nil)
|
||||
(assoc-in [:db :wallet/prepare-transaction :amount-text] nil)
|
||||
(assoc-in [:db :wallet/prepare-transaction :asset-error]
|
||||
(i18n/label :t/changed-asset-warning {:old old-symbol :new new-symbol}))))
|
||||
|
||||
(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})))
|
||||
|
||||
(defn use-default-eth-gas [fx]
|
||||
|
@ -84,41 +88,48 @@
|
|||
(let [checksum (eip55/address->checksum recipient)]
|
||||
(if (eip55/valid-address-checksum? checksum)
|
||||
{:db (-> db
|
||||
(assoc-in [:wallet :send-transaction :to] checksum)
|
||||
(assoc-in [:wallet :send-transaction :to-name] nil))
|
||||
(assoc-in [:wallet/prepare-transaction :to] checksum)
|
||||
(assoc-in [:wallet/prepare-transaction :modal-opened?] false))
|
||||
:dispatch [:navigate-back]}
|
||||
{:ui/show-error (i18n/label :t/wallet-invalid-address-checksum {:data recipient})}))
|
||||
{:ui/show-error (i18n/label :t/wallet-invalid-address {:data recipient})}))))
|
||||
|
||||
(fx/defn fill-request-from-url
|
||||
{:events [:wallet/fill-request-from-url]}
|
||||
[{{:networks/keys [current-network] :wallet/keys [all-tokens] :as db} :db} data origin]
|
||||
(let [current-chain-id (get-in constants/default-networks [current-network :config :NetworkId])
|
||||
[{{: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])
|
||||
{:keys [address chain-id] :as details} (extract-details data current-chain-id all-tokens)
|
||||
valid-network? (boolean (= current-chain-id chain-id))
|
||||
previous-state (get-in db [:wallet :send-transaction])
|
||||
old-symbol (:symbol previous-state)
|
||||
new-symbol (:symbol details)
|
||||
old-amount (:amount previous-state)
|
||||
new-amount (:value details)
|
||||
new-gas (:gas details)
|
||||
symbol-changed? (and old-symbol new-symbol (not= old-symbol new-symbol))]
|
||||
valid-network? (boolean (= current-chain-id chain-id))
|
||||
previous-state (get db :wallet/prepare-transaction)
|
||||
old-symbol (:symbol previous-state)
|
||||
new-symbol (:symbol details)
|
||||
old-amount (:amount previous-state)
|
||||
new-amount (:value details)
|
||||
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}
|
||||
(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))
|
||||
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)
|
||||
;; NOTE(goranjovic) - the next line is there is because QR code scanning switches the amount to ETH
|
||||
;; automatically, so we need to update the gas limit accordingly. The check for origin screen is there
|
||||
;; so that we wouldn't also switch gas limit to ETH specific if the user pastes address as text.
|
||||
;; We need to check if address is defined so that we wouldn't trigger this behavior when invalid QR is scanned
|
||||
;; (e.g. public-key)
|
||||
(and address (= origin :qr) (not new-gas) symbol-changed?) (use-default-eth-gas)
|
||||
amount-changed? (changed-amount-warning old-amount new-amount)
|
||||
(not (:from previous-state))
|
||||
(update :db assoc-in [:wallet/prepare-transaction :from]
|
||||
(ethereum/get-default-account (get-in db [:multiaccount :accounts])))
|
||||
(not old-symbol)
|
||||
(update :db assoc-in [:wallet/prepare-transaction :symbol] (or new-symbol :ETH))
|
||||
(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
|
||||
{: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]})
|
||||
(fx/defn qr-scanner-result
|
||||
{:events [:wallet.send/qr-scanner-result]}
|
||||
[{db :db :as cofx} data opts]
|
||||
(fx/merge cofx
|
||||
{: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)})
|
|
@ -20,7 +20,9 @@
|
|||
[status-im.wallet.db :as wallet.db]
|
||||
[status-im.ethereum.abi-spec :as abi-spec]
|
||||
[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
|
||||
:wallet/get-balance
|
||||
|
@ -342,17 +344,9 @@
|
|||
(multiaccounts.update/update-settings cofx new-settings {})))
|
||||
|
||||
(fx/defn set-and-validate-amount
|
||||
{:events [:wallet.send/set-and-validate-amount]}
|
||||
{:events [:wallet.send/set-amount-text]}
|
||||
[{:keys [db]} amount]
|
||||
(let [chain (ethereum/chain-keyword db)
|
||||
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))}))
|
||||
{:db (assoc-in db [:wallet/prepare-transaction :amount-text] amount)})
|
||||
|
||||
(fx/defn set-symbol
|
||||
{:events [:wallet.send/set-symbol]}
|
||||
|
@ -365,21 +359,27 @@
|
|||
|
||||
(fx/defn sign-transaction-button-clicked
|
||||
{:events [:wallet.ui/sign-transaction-button-clicked]}
|
||||
[{:keys [db] :as cofx}]
|
||||
(let [{:keys [to symbol amount from]} (get-in cofx [:db :wallet :send-transaction])
|
||||
{:keys [symbol address]} (tokens/asset-for (:wallet/all-tokens db)
|
||||
(ethereum/chain-keyword db)
|
||||
symbol)
|
||||
[{:keys [db] :as cofx} {:keys [to amount from token from-chat? gas gasPrice]}]
|
||||
(let [{:keys [symbol address]} token
|
||||
amount-hex (str "0x" (abi-spec/number-to-hex amount))
|
||||
to-norm (ethereum/normalized-address to)]
|
||||
(signing/sign cofx {:tx-obj (if (= symbol :ETH)
|
||||
{:to to-norm
|
||||
:from from
|
||||
:value amount-hex}
|
||||
{:to (ethereum/normalized-address address)
|
||||
:from from
|
||||
:data (abi-spec/encode "transfer(address,uint256)" [to-norm amount-hex])})
|
||||
:on-result [:navigate-back]})))
|
||||
to-norm (ethereum/normalized-address (if (string? to) to (:address to)))
|
||||
from-address (:address from)]
|
||||
(fx/merge cofx
|
||||
{:db (dissoc db :wallet/prepare-transaction)}
|
||||
#(if from-chat?
|
||||
nil;;TODO from chat, send request message or if ens name sign tx and send tx message
|
||||
(signing/sign % {:tx-obj (if (= symbol :ETH)
|
||||
{:to to-norm
|
||||
: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
|
||||
{: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-error] error))}))
|
||||
|
||||
;;TODO request isn't implemented
|
||||
(fx/defn set-symbol-request
|
||||
{:events [:wallet.request/set-symbol]}
|
||||
[{:keys [db]} symbol]
|
||||
{:db (-> db
|
||||
(assoc-in [:wallet :request-transaction :symbol] symbol))})
|
||||
{:db (assoc-in db [: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)))
|
|
@ -1,8 +1,6 @@
|
|||
(ns status-im.wallet.db
|
||||
(:require [cljs.spec.alpha :as spec]
|
||||
[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.priority-map :refer [empty-transaction-map]]))
|
||||
|
||||
|
@ -25,9 +23,7 @@
|
|||
(spec/def :wallet/balance any?)
|
||||
(spec/def :wallet/filters set?)
|
||||
|
||||
(spec/def :wallet/wallet (spec/keys :opt-un [:wallet/send-transaction
|
||||
:wallet/request-transaction
|
||||
:wallet/transactions-queue
|
||||
(spec/def :wallet/wallet (spec/keys :opt-un [:wallet/transactions-queue
|
||||
:wallet/balance-loading?
|
||||
:wallet/errors
|
||||
:wallet/transactions
|
||||
|
|
|
@ -9,9 +9,9 @@
|
|||
|
||||
;;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.
|
||||
(defn display-symbol [{:keys [symbol-display symbol] :as token}]
|
||||
(when token
|
||||
(clojure.core/name (or symbol-display symbol))))
|
||||
(defn display-symbol [{:keys [symbol-display symbol]}]
|
||||
(when-let [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
|
||||
;; ticker on exchange networks. We handle that with `symbol-exchange` override.
|
||||
|
|
|
@ -463,7 +463,7 @@
|
|||
"finishing-card-setup-steps": "> Loading keys to the card\n> Generating multiaccount",
|
||||
"fleet": "Fleet",
|
||||
"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",
|
||||
"from": "From",
|
||||
"gas-limit": "Gas limit",
|
||||
|
|