From ff790d4152af10cb0b1149fcf048089c1dc8547c Mon Sep 17 00:00:00 2001 From: andrey Date: Fri, 11 Sep 2020 10:14:15 +0200 Subject: [PATCH] wallet favourites --- resources/images/icons/favourite@2x.png | Bin 0 -> 1200 bytes resources/images/icons/favourite@3x.png | Bin 0 -> 1766 bytes src/status_im/ethereum/json_rpc.cljs | 2 + src/status_im/multiaccounts/login/core.cljs | 19 +- src/status_im/search/core.cljs | 7 +- src/status_im/subs.cljs | 73 ++++ .../ui/screens/add_new/new_chat/events.cljs | 1 + src/status_im/ui/screens/routing/main.cljs | 25 +- src/status_im/ui/screens/views.cljs | 2 - .../ui/screens/wallet/components/views.cljs | 43 +-- .../ui/screens/wallet/recipient/views.cljs | 315 +++++++++++++++++ .../ui/screens/wallet/send/sheets.cljs | 4 +- .../ui/screens/wallet/send/styles.cljs | 7 +- .../ui/screens/wallet/send/views.cljs | 325 +++++++++--------- .../wallet/choose_recipient/core.cljs | 74 ++-- src/status_im/wallet/core.cljs | 25 +- src/status_im/wallet/recipient/core.cljs | 111 ++++++ status-go-version.json | 6 +- translations/en.json | 10 + 19 files changed, 763 insertions(+), 286 deletions(-) create mode 100644 resources/images/icons/favourite@2x.png create mode 100644 resources/images/icons/favourite@3x.png create mode 100644 src/status_im/ui/screens/wallet/recipient/views.cljs create mode 100644 src/status_im/wallet/recipient/core.cljs diff --git a/resources/images/icons/favourite@2x.png b/resources/images/icons/favourite@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..612a597b4ba3cfd00050bc02e16194da594ce104 GIT binary patch literal 1200 zcmV;h1W)^kP))5-P&z&VLI6T0qbm z-TG~)2$d-S=yejy`~?+3Km{27eCYd4C>ffxpugy__!oJ0Syb| zC3pc;-i@K50tih7@)sn0<$2;)LfHgCO-{y^5H_Y0mc;lI-}|^JkB}aO3>6@t3mb0A zC}thCV3`YkZyvr+bFJyCm1?f>Uq2^rGCg2QAy*S096-F-+567H=5-S`-cmk_>=OID zT*Us_@4YRcDpIbdSj^W=2PVgwgT}Z&oZ0*mkMnXRw+fjTbc3x) zl-688_Zx&3g_2-oeQ&^AO4&)@p_%dMu0-Ff3NfD_q$n|4CD>t=qOk0hy`7>U0j{`?4tPsX`!z`Y6}szT2F#AzNF@<|)h?8xdd_czv7fjUpj1sjlqKm}J3 z4Y}r{$8|Y&8#SFm$iub8n7i9huu3gWQ_9=C0S=UwkEVf{ZOFs-y}zLbLc=03wyxh zp<_Gs?S#b)a(5E0s!*4!)F5kEva$;{#li{*;T_pPf!uI;7Ar*#u5X#il01(Tk)C)B z1>xMt%)wP&xa^BFem5CbTo77NHB6H#W02W|`4y*%)9V^FYSgH)6F&gm65jji<#&Pr O0000K# zmi;#x?5?fyM*^m83~6IV@{=NrvEA)*)vK!44nTnd1qu`>P@q8e_;}N;efr$5!UjRW zhJcMDgzWOR5KucgtoC68AYnrwK>itSBSH-}1RG(XcBfk=3vEyBDqr)y4Zubi$Oc)P z-}~ZGc_McXYX8;Uwr%;O`^~Y{&&_&$3^^iq50WyTZ{&B`fSi%PfrN}r>twuh$QijB zh*NunoTDh^P1d;@NC=UlD9o3vb0g^_a*o15wbwkZ4Ae8PS zr{>b1t=&Mnz6%jG(uV5I?Luqqh2vUDIwrLbPmij5y5p}qy$70Nd3xWv`!gF1fU^>O zqpV}mpQP|nP$ExxOF!)hiEmNK5(wNTou?E%m&CElxa*?Adm(2HWIOVSvfR)=7e}s( zUwKr9pwk=D%^CpghZ+6?CWMZq3i0RZGH#QptVBWB) z$3Pg-9NAD~8jwEmt3oxlEcrCC)cqnOF7zOp;3b){%BG~jo*@hbr$9)QReACsJKYP) zdTm&BjOhm`{2QPK*}E4xkz7tBDh7*58pT`40s^jxds>SQJ3Fe=1DdOgjKHGsUv$hu z4{}_$NyNHMo4c?Ad>2R0#p!8|P>KKOhVM=f>w~n{pko`9;_m?EK3(x08J9z@M4?`K zMTie46TDb-P^v%)xPZyDns6n0%4#4LjHC<9|VZYlcV37umPYf$8&7O{Ctrnv_mXSwM3eYc+!-u)1zM(x^0$7G7hT+ zC(LO}V5MB-m;CY;Kv&m@-o$d7>+ZZc%q-Vx3&dLVH)Q?;b#Qu=p7%nmoUFMjrdr}5 zh9rlMqYkF~t1Q8D*|Oy=Y7z<8TJA(uR-Mbry%-PZx}LbDQ~Gx8E&0F`hm7I1FZ7^L zpJj)yF%IKZ%_(x8)~qIFI8>^6_WN?= zxNy9i{yD_=aZ6>YBpB`HlQW#EtU{7HTz~HFQ_*;<`)!p>l?0=Dkk=OYqPr%88CRqXG!DbKV8&iT8ElVRjL1)AFoszP#LHAPrYR4dMkb0P6CR3>4HqGj)kV_y-I@AyUtulK3bZ%j1GfY3C_wm&?c-{HC<=DUjcr&uD% zs@_WwLNk_O9x@tYmP)^N7IfbiuBC4>&yMT8vtwSCR!c1cpjvJHE)GTwB&0~$6X77} zzFUjJfsY||U`Lk;^^-8E8W_)FBJt^Ys|&accounts accounts))} (wallet/initialize-tokens custom-tokens) + (wallet/initialize-favourites favourites) (wallet/update-balances nil) (prices/update-prices))) @@ -154,11 +155,17 @@ (js/Promise. (fn [resolve reject] (json-rpc/call {:method "wallet_getCustomTokens" + :on-success resolve + :on-error reject}))) + (js/Promise. + (fn [resolve reject] + (json-rpc/call {:method "wallet_getFavourites" :on-success resolve :on-error reject})))])) - (.then (fn [[accounts custom-tokens]] + (.then (fn [[accounts custom-tokens favourites]] (callback accounts - (mapv #(update % :symbol keyword) custom-tokens)))) + (mapv #(update % :symbol keyword) custom-tokens) + favourites))) (.catch (fn [_] (log/error "Failed to initialize wallet")))))) @@ -186,9 +193,9 @@ :networks/networks networks :multiaccount multiaccount)) ::initialize-wallet - (fn [accounts custom-tokens] + (fn [accounts custom-tokens favourites] (re-frame/dispatch [::initialize-wallet - accounts custom-tokens]))} + accounts custom-tokens favourites]))} notifications-enabled? (assoc ::notifications/enable nil)) (acquisition/login) @@ -269,7 +276,7 @@ :default-mailserver true}) (multiaccounts/switch-preview-privacy-mode-flag) (logging/set-log-level (:log-level multiaccount)) - (initialize-wallet accounts nil)))) + (initialize-wallet accounts nil nil)))) (defn- keycard-setup? [cofx] (boolean (get-in cofx [:db :keycard :flow]))) diff --git a/src/status_im/search/core.cljs b/src/status_im/search/core.cljs index 2f773921f0..caaa4f5eeb 100644 --- a/src/status_im/search/core.cljs +++ b/src/status_im/search/core.cljs @@ -14,4 +14,9 @@ (fx/defn token-filter-changed {:events [:search/token-filter-changed]} [cofx search-filter] - {:db (assoc-in (:db cofx) [:ui/search :token-filter] search-filter)}) \ No newline at end of file + {:db (assoc-in (:db cofx) [:ui/search :token-filter] search-filter)}) + +(fx/defn recipient-filter-changed + {:events [:search/recipient-filter-changed]} + [cofx search-filter] + {:db (assoc-in (:db cofx) [:ui/search :recipient-filter] search-filter)}) \ No newline at end of file diff --git a/src/status_im/subs.cljs b/src/status_im/subs.cljs index 469ed2cff4..d119f6a4cd 100644 --- a/src/status_im/subs.cljs +++ b/src/status_im/subs.cljs @@ -163,6 +163,8 @@ (reg-root-key-sub :wallet/prepare-transaction :wallet/prepare-transaction) (reg-root-key-sub :wallet-service/manual-setting :wallet-service/manual-setting) (reg-root-key-sub :wallet-service/state :wallet-service/state) +(reg-root-key-sub :wallet/recipient :wallet/recipient) +(reg-root-key-sub :wallet/favourites :wallet/favourites) ;;commands (reg-root-key-sub :commands/select-account :commands/select-account) @@ -367,6 +369,12 @@ (fn [search] (get search :home-filter))) +(re-frame/reg-sub + :search/recipient-filter + :<- [:ui/search] + (fn [search] + (get search :recipient-filter))) + (re-frame/reg-sub :search/currency-filter :<- [:ui/search] @@ -531,6 +539,23 @@ (fn [accounts] (filter #(not= (:type %) :watch) accounts))) +(defn filter-recipient-accounts + [search-filter {:keys [name]}] + (string/includes? (string/lower-case (str name)) search-filter)) + +(re-frame/reg-sub + :accounts-for-recipient + :<- [:multiaccount/accounts] + :<- [:wallet/prepare-transaction] + :<- [:search/recipient-filter] + (fn [[accounts {:keys [from]} search-filter]] + (let [accounts (remove #(= (:address %) (:address from)) accounts)] + (if (string/blank? search-filter) + accounts + (filter (partial filter-recipient-accounts + (string/lower-case search-filter)) + accounts))))) + (re-frame/reg-sub :add-account-disabled? :<- [:multiaccount/accounts] @@ -1285,6 +1310,21 @@ (fn [currency-id] (get constants/currencies currency-id))) +(defn filter-recipient-favs + [search-filter {:keys [name]}] + (string/includes? (string/lower-case (str name)) search-filter)) + +(re-frame/reg-sub + :wallet/favourites-filtered + :<- [:wallet/favourites] + :<- [:search/recipient-filter] + (fn [[favs search-filter]] + (let [favs (vals favs)] + (if (string/blank? search-filter) + favs + (filter (partial filter-recipient-favs + (string/lower-case search-filter)) + favs))))) ;;WALLET TRANSACTIONS ================================================================================================== (re-frame/reg-sub @@ -1434,6 +1474,17 @@ (keep #(enrich-transaction-for-list filters % address)) (group-transactions-by-date))})) +(re-frame/reg-sub + :wallet/recipient-recent-txs + (fn [[_ address] _] + [(re-frame/subscribe [:wallet.transactions/transactions address])]) + (fn [[transactions] _] + (->> transactions + vals + (sort-by :timestamp >) + (remove #(= (:type %) :pending)) + (take 3)))) + (re-frame/reg-sub :wallet.transactions.details/current-transaction (fn [[_ _ address] _] @@ -1620,6 +1671,28 @@ (fn [blocked-contacts] (count blocked-contacts))) +(defn filter-recipient-contacts + [search-filter {:keys [names]}] + (let [{:keys [nickname three-words-name ens-name]} names] + (or + (when ens-name + (string/includes? (string/lower-case (str ens-name)) search-filter)) + (string/includes? (string/lower-case three-words-name) search-filter) + (when nickname + (string/includes? (string/lower-case nickname) search-filter))))) + +(re-frame/reg-sub + :contacts/active-with-ens-names + :<- [:contacts/active] + :<- [:search/recipient-filter] + (fn [[contacts search-filter]] + (let [contacts (filter :ens-verified contacts)] + (if (string/blank? search-filter) + contacts + (filter (partial filter-recipient-contacts + (string/lower-case search-filter)) + contacts))))) + (re-frame/reg-sub :contacts/current-contact :<- [:contacts/contacts] diff --git a/src/status_im/ui/screens/add_new/new_chat/events.cljs b/src/status_im/ui/screens/add_new/new_chat/events.cljs index bdef4374c6..059b9a5ff9 100644 --- a/src/status_im/ui/screens/add_new/new_chat/events.cljs +++ b/src/status_im/ui/screens/add_new/new_chat/events.cljs @@ -43,6 +43,7 @@ is-ens? (and (not is-public-key?) (ens/valid-eth-name-prefix? new-identity)) error (db/validate-pub-key db new-identity)] + (reset! resolve-last-id nil) (merge {:db (assoc db :contacts/new-identity {:public-key new-identity diff --git a/src/status_im/ui/screens/routing/main.cljs b/src/status_im/ui/screens/routing/main.cljs index 1343cc4f21..15a1c3d829 100644 --- a/src/status_im/ui/screens/routing/main.cljs +++ b/src/status_im/ui/screens/routing/main.cljs @@ -2,7 +2,7 @@ (:require-macros [status-im.utils.views :as views]) (:require [status-im.ui.screens.profile.tribute-to-talk.views :as tr-to-talk] [status-im.ui.screens.add-new.new-public-chat.view :as new-public-chat] - [status-im.ui.screens.wallet.components.views :as wallet.components] + [status-im.ui.screens.wallet.recipient.views :as recipient] [status-im.ui.screens.qr-scanner.views :as qr-scanner] [status-im.ui.screens.stickers.views :as stickers] [status-im.ui.screens.home.views :as home] @@ -24,7 +24,8 @@ [status-im.utils.config :as config] [status-im.ui.screens.chat.image.preview.views :as image-preview] [status-im.ui.screens.profile.contact.views :as contact] - [status-im.ui.screens.notifications-settings.views :as notifications-settings])) + [status-im.ui.screens.notifications-settings.views :as notifications-settings] + [status-im.ui.screens.wallet.send.views :as wallet])) (defonce main-stack (navigation/create-stack)) (defonce bottom-tabs (navigation/create-bottom-tabs)) @@ -103,8 +104,14 @@ :transition :presentation-ios :insets {:bottom true} :component group-chat/add-participants-toggle-list} - {:name :contact-code - :component wallet.components/contact-code} + {:name :recipient + :transition :presentation-ios + :insets {:bottom true} + :component recipient/recipient} + {:name :new-favourite + :transition :presentation-ios + :insets {:bottom true} + :component recipient/new-favourite} {:name :qr-scanner :insets {:top false :bottom false} :component qr-scanner/qr-scanner} @@ -122,7 +129,15 @@ {:name :notifications-onboarding :back-handler :noop :insets {:bottom true} - :component notifications-settings/notifications-onboarding}] + :component notifications-settings/notifications-onboarding} + {:name :prepare-send-transaction + :transition :presentation-ios + :insets {:bottom true} + :component wallet/prepare-send-transaction} + {:name :request-transaction + :transition :presentation-ios + :insets {:bottom true} + :component wallet/request-transaction}] (when config/quo-preview-enabled? [{:name :quo-preview diff --git a/src/status_im/ui/screens/views.cljs b/src/status_im/ui/screens/views.cljs index 95e8f1f90c..d3a1f74de6 100644 --- a/src/status_im/ui/screens/views.cljs +++ b/src/status_im/ui/screens/views.cljs @@ -104,8 +104,6 @@ {:enableURLHandling true :initialState @state})) [main-app-navigator]] - [wallet/prepare-transaction] - [wallet/request-transaction] [wallet/select-account] [signing/signing] [bottom-sheet] diff --git a/src/status_im/ui/screens/wallet/components/views.cljs b/src/status_im/ui/screens/wallet/components/views.cljs index 4a6c69db4d..c6e77a1e17 100644 --- a/src/status_im/ui/screens/wallet/components/views.cljs +++ b/src/status_im/ui/screens/wallet/components/views.cljs @@ -1,43 +1,6 @@ (ns status-im.ui.screens.wallet.components.views - (:require [clojure.string :as string] - [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.topbar :as topbar] - [status-im.ui.screens.wallet.components.styles :as styles] - [quo.core :as quo] - [status-im.ui.components.colors :as colors] - [status-im.utils.debounce :as debounce]) - (:require-macros [status-im.utils.views :as views])) + (:require [status-im.ui.components.react :as react] + [status-im.ui.screens.wallet.components.styles :as styles])) (defn separator [] - [react/view (styles/separator)]) - -(defn- recipient-topbar [content] - [topbar/topbar {:navigation {:label (i18n/label :t/cancel) - :on-press #(do - (re-frame/dispatch [:set-in [:wallet/prepare-transaction :modal-opened?] false]) - (re-frame/dispatch [:navigate-back]))} - :title (i18n/label :t/recipient) - :right-accessories [{:on-press #(debounce/dispatch-and-chill [:wallet.send/set-recipient content] 3000) - :label (i18n/label :t/done) - :disabled (string/blank? content)}]}]) - -(views/defview contact-code [] - (views/letsubs [content (reagent/atom nil)] - [react/view {:flex 1} - [recipient-topbar @content] - [react/view {:padding-horizontal 16 - :padding-vertical 24 - :flex 1} - [quo/text-input - {:multiline true - :height 98 - :placeholder (i18n/label :t/recipient-code-placeholder) - :text-align-vertical :top - :on-change-text #(reset! content %) - :accessibility-label :recipient-address-input}] - [react/text {:style {:color colors/gray - :margin-vertical 16}} - (i18n/label :t/enter-recipient-address-or-username)]]])) + [react/view (styles/separator)]) \ No newline at end of file diff --git a/src/status_im/ui/screens/wallet/recipient/views.cljs b/src/status_im/ui/screens/wallet/recipient/views.cljs new file mode 100644 index 0000000000..927ac73783 --- /dev/null +++ b/src/status_im/ui/screens/wallet/recipient/views.cljs @@ -0,0 +1,315 @@ +(ns status-im.ui.screens.wallet.recipient.views + (:require [clojure.string :as string] + [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.topbar :as topbar] + [quo.core :as quo] + [status-im.ui.components.colors :as colors] + [status-im.utils.debounce :as debounce] + [status-im.ui.components.search-input.view :as search-input] + [status-im.ui.screens.wallet.components.views :as components] + [status-im.ui.components.icons.vector-icons :as icons] + [status-im.ui.components.chat-icon.screen :as chat-icon] + [status-im.multiaccounts.core :as multiaccounts] + [status-im.utils.utils :as utils] + [status-im.ui.components.keyboard-avoid-presentation :as kb-presentation] + [status-im.ui.components.toolbar :as toolbar] + [status-im.ethereum.core :as ethereum] + [status-im.ethereum.stateofus :as stateofus]) + (:require-macros [status-im.utils.views :as views])) + +(defn- recipient-topbar [] + [topbar/topbar + {:navigation {:on-press + #(do + (re-frame/dispatch [:wallet/recipient-modal-closed]) + (re-frame/dispatch [:search/recipient-filter-changed nil]) + (re-frame/dispatch [:navigate-back]))} + :modal? true + :border-bottom false + :title (i18n/label :t/recipient) + :right-accessories + [{:icon :qr + :accessibility-label :scan-contact-code-button + :on-press #(re-frame/dispatch [:wallet.send/qr-scanner + {:handler :wallet.send/qr-scanner-result}])}]}]) + +(defonce search-active? (reagent/atom false)) + +(defn search-input-wrapper [] + (let [search-filter @(re-frame/subscribe [:search/recipient-filter])] + [react/view {:padding-horizontal 16 + :padding-vertical 10} + [search-input/search-input + {:search-active? search-active? + :search-filter search-filter + :on-cancel #(re-frame/dispatch [:search/recipient-filter-changed nil]) + :on-change (fn [text] + (re-frame/dispatch [:search/recipient-filter-changed text]) + (re-frame/dispatch [:set-in [:contacts/new-identity :state] :searching]) + (debounce/debounce-and-dispatch [:new-chat/set-new-identity text] 300))}]])) + +(defn section [_ _ _] + (let [opened? (reagent/atom false)] + (fn [title cnt content] + [react/view {:padding-vertical 8} + [quo/list-item + {:title title + :on-press #(swap! opened? not) + :accessory + [react/view {:flex-direction :row :align-items :center} + (when (pos? cnt) + [react/text {:style {:color colors/gray}} cnt]) + [icons/icon (if @opened? :main-icons/dropdown :main-icons/next) + {:container-style {:align-items :center + :margin-left 8 + :justify-content :center} + :resize-mode :center + :color colors/black}]]}] + (when @opened? + content)]))) + +(defn render-account [account] + [quo/list-item + {:icon [chat-icon/custom-icon-view-list (:name account) (:color account)] + :title (:name account) + :on-press #(re-frame/dispatch [:wallet.send/set-recipient (:address account)]) + :subtitle [quo/text {:monospace true + :color :secondary} + (utils/get-shortened-checksum-address (:address account))]}]) + +(def scroll-view-ref (atom nil)) + +(defn contacts-list-item [{:keys [name] :as contact}] + (let [[first-name second-name] (multiaccounts/contact-two-names contact true)] + [quo/list-item + {:title first-name + :subtitle second-name + :on-press #(do + (some-> ^js @scroll-view-ref (.scrollTo #js {:x 0 :animated true})) + (re-frame/dispatch [:wallet.recipient/focus-input]) + (re-frame/dispatch [:wallet.recipient/address-changed name])) + :icon [chat-icon/contact-icon-contacts-tab + (multiaccounts/displayed-photo contact)]}])) + +(defn empty-items [icon title] + [react/view {:height 94 :align-items :center :justify-content :center} + [icons/icon icon + {:color colors/gray}] + [react/text {:style {:color colors/gray :margin-top 8}} + title]]) + +(views/defview accounts-section [] + (views/letsubs [accounts [:accounts-for-recipient]] + (let [cnt (count accounts)] + [section + (i18n/label :t/my-accounts) + cnt + (if (> cnt 0) + [react/view + (for [account accounts] + [render-account account])] + [empty-items :main-icons/address (i18n/label :t/my-accounts-empty)])]))) + +(defn render-recent [{:keys [from to type amount-text currency-text]}] + (let [inbound? (= type :inbound) + address (if inbound? from to)] + [quo/list-item + {:title [quo/text {:monospace true} + (utils/get-shortened-checksum-address address)] + :on-press #(do + (re-frame/dispatch [:wallet.recipient/focus-input]) + (re-frame/dispatch [:wallet.recipient/address-changed address])) + :size :small + :accessory [react/text {:style {:flex-shrink 1 + :color colors/gray}} + (str (if inbound? "↓ " "↑ ") amount-text " " currency-text)]}])) + +(defn recent-section [] + (let [{:keys [from]} @(re-frame/subscribe [:wallet/prepare-transaction]) + txs @(re-frame/subscribe [:wallet/recipient-recent-txs (:address from)]) + cnt (count txs)] + [section + (i18n/label :t/recent) + cnt + (if (> cnt 0) + [react/view + (for [tx txs] + [render-recent tx])] + [empty-items :main-icons/history (i18n/label :t/recent-empty)])])) + +(defn render-fav [{:keys [address name]}] + (let [noname? (string/blank? name) + short-address (utils/get-shortened-checksum-address address)] + [quo/list-item + {:icon [chat-icon/custom-icon-view-list + (if noname? " 2" name) + (rand-nth colors/chat-colors)] + :title (if noname? + [quo/text {:monospace true} + short-address] + name) + :subtitle (when-not noname? [quo/text {:monospace true + :color :secondary} + short-address]) + :on-press #(re-frame/dispatch [:wallet.send/set-recipient address]) + :size (when noname? :small)}])) + +(views/defview fav-section [] + (views/letsubs [favourites [:wallet/favourites-filtered]] + (let [cnt (count favourites)] + [section + (i18n/label :t/favourites) + cnt + [react/view + ;;TODO implement later + #_[quo/list-item + {:title "Add favourite" + :icon :main-icons/add + :theme :accent}] + (if (> cnt 0) + [react/view + (for [data favourites] + [render-fav data])] + [empty-items :main-icons/favourite (i18n/label :t/favourites-empty)])]]))) + +(views/defview contacts-section [] + (views/letsubs [contacts [:contacts/active-with-ens-names]] + (let [cnt (count contacts)] + [section + (i18n/label :t/contacts) + cnt + (if (> cnt 0) + [react/view + (for [contact contacts] + [contacts-list-item contact])] + [empty-items :main-icons/username (i18n/label :t/contacts-empty)])]))) + +(views/defview search-results [] + (views/letsubs [contacts [:contacts/active-with-ens-names] + favourites [:wallet/favourites-filtered] + accounts [:accounts-for-recipient]] + [react/view + (for [account accounts] + [render-account account]) + (for [data favourites] + [render-fav data]) + (for [contact contacts] + [contacts-list-item contact])])) + +(defn accordion [search-filter] + (if (not (string/blank? search-filter)) + [search-results] + [react/view + [components/separator] + [accounts-section] + [components/separator] + [recent-section] + [components/separator] + [fav-section] + [components/separator] + [contacts-section] + [components/separator]])) + +(views/defview new-favourite [] + (views/letsubs [{:keys [resolved-address]} [:wallet/recipient] + fav-name (atom "")] + [kb-presentation/keyboard-avoiding-view {:style {:flex 1}} + [react/view {:flex 1} + [topbar/topbar + {:modal? true + :title (i18n/label :t/new-favourite)}] + [react/scroll-view {:style {:flex 1}} + [react/view {:padding-horizontal 16} + [react/view {:flex-direction :row :justify-content :space-between + :align-items :center :height 40 :margin-vertical 8} + [quo/text (i18n/label :t/address-or-ens-name)]] + [quo/text-input + {:multiline true + :default-value resolved-address + :height 70 + :editable false}]] + [react/view {:height 16}] + [quo/list-header (i18n/label :t/name-optional)] + [react/view {:padding-horizontal 16} + [quo/text-input {:show-cancel false + :on-change-text #(reset! fav-name %)}]]] + [toolbar/toolbar + {:show-border? true + :center + [quo/button + {:accessibility-label :add-fav + :type :secondary + :on-press #(re-frame/dispatch [:wallet/add-favourite resolved-address @fav-name])} + (i18n/label :t/add)]}]]])) + +(views/defview recipient [] + (views/letsubs [{:keys [address resolved-address searching] :as recip} [:wallet/recipient] + search-filter [:search/recipient-filter] + input-focused? (reagent/atom false)] + (let [disabled? (or searching (not resolved-address))] + [kb-presentation/keyboard-avoiding-view {:style {:flex 1}} + [react/view {:flex 1} + [recipient-topbar] + [search-input-wrapper] + [react/scroll-view {:style {:flex 1} + :keyboard-should-persist-taps :handled + :ref #(reset! scroll-view-ref %)} + [react/view + [components/separator] + [react/view {:padding-horizontal 16 :margin-bottom 16} + [react/view {:flex-direction :row :justify-content :space-between + :align-items :center :height 40 :margin-vertical 8} + [quo/text (i18n/label :t/address-or-ens-name)] + [quo/button {:type :secondary + :on-press #(re-frame/dispatch [:wallet.recipient/address-paste-pressed])} + (i18n/label :t/paste)]] + [quo/text-input + {:multiline true + :get-ref #(when (and recip %) + (re-frame/dispatch [:set-in [:wallet/recipient :inp-ref] %])) + :on-focus #(reset! input-focused? true) + :on-blur #(reset! input-focused? false) + :default-value address + :height 70 + :placeholder (i18n/label :t/recipient-code-placeholder) + :text-align-vertical :top + :on-change-text #(do + (re-frame/dispatch [:set-in [:wallet/recipient :searching] :searching]) + (debounce/debounce-and-dispatch [:wallet.recipient/address-changed %] 600)) + :accessibility-label :recipient-address-input}]] + [react/view {:align-items :center :height 30 :padding-bottom 8} + (if searching + [react/small-loading-indicator] + (when resolved-address + [quo/text {:style {:margin-horizontal 16} + :size :small + :align :center + :color :secondary} + (when-not (ethereum/address? address) + (str (stateofus/username-with-domain address) " • ")) + [quo/text {:monospace true + :size :inherit + :color :inherit} + (utils/get-shortened-address resolved-address)]]))] + [accordion search-filter]]] + (when @input-focused? + [toolbar/toolbar + {:show-border? true + :left + [quo/button + {:accessibility-label :participant-add-to-favs + :type :secondary + :disabled disabled? + :on-press #(re-frame/dispatch [:navigate-to :new-favourite])} + (i18n/label :t/add-to-favourites)] + :right + [quo/button + {:accessibility-label :participant-done + :type :secondary + :after :main-icons/next + :disabled disabled? + :on-press #(re-frame/dispatch [:wallet.send/set-recipient resolved-address])} + (i18n/label :t/done)]}])]]))) \ No newline at end of file diff --git a/src/status_im/ui/screens/wallet/send/sheets.cljs b/src/status_im/ui/screens/wallet/send/sheets.cljs index c97a006cf2..8364f146e5 100644 --- a/src/status_im/ui/screens/wallet/send/sheets.cljs +++ b/src/status_im/ui/screens/wallet/send/sheets.cljs @@ -50,9 +50,7 @@ :icon :main-icons/qr :theme :accent :accessibility-label :chose-recipient-scan-qr - :on-press #(re-frame/dispatch [:wallet.send/qr-scanner {:handler :wallet.send/qr-scanner-result - :cancel-handler :wallet.send/qr-scanner-cancel - :modal-opened? true}])} + :on-press #(re-frame/dispatch [:wallet.send/qr-scanner {:handler :wallet.send/qr-scanner-result}])} {:title (i18n/label :t/recipient-code) :icon :main-icons/address :theme :accent diff --git a/src/status_im/ui/screens/wallet/send/styles.cljs b/src/status_im/ui/screens/wallet/send/styles.cljs index f2adee806d..fad5aecbfd 100644 --- a/src/status_im/ui/screens/wallet/send/styles.cljs +++ b/src/status_im/ui/screens/wallet/send/styles.cljs @@ -1,11 +1,14 @@ (ns status-im.ui.screens.wallet.send.styles (:require [status-im.ui.components.colors :as colors])) -(defn sheet [small-screen?] +(defn sheet [] + {:flex 1}) + +(defn acc-sheet [] {:background-color colors/white :border-top-right-radius 16 :border-top-left-radius 16 - :padding-bottom (if small-screen? 40 60)}) + :padding-bottom 60}) (defn header [small-screen?] {:flex-direction :row diff --git a/src/status_im/ui/screens/wallet/send/views.cljs b/src/status_im/ui/screens/wallet/send/views.cljs index ce26653aa0..a25d8aeef7 100644 --- a/src/status_im/ui/screens/wallet/send/views.cljs +++ b/src/status_im/ui/screens/wallet/send/views.cljs @@ -19,7 +19,9 @@ [status-im.utils.utils :as utils] [status-im.utils.money :as money] [quo.core :as quo] - [status-im.ethereum.core :as ethereum])) + [status-im.ethereum.core :as ethereum] + [status-im.ui.components.keyboard-avoid-presentation :as kb-presentation] + [status-im.ui.components.topbar :as topbar])) (defn header [{:keys [label small-screen?]}] [react/view (styles/header small-screen?) @@ -69,23 +71,26 @@ (defn render-contact [contact from-chat?] (if from-chat? [quo/list-item {:title (multiaccounts/displayed-name contact) - :subtitle (:address contact) + :subtitle [quo/text {:monospace true + :color :secondary} + (utils/get-shortened-checksum-address (:address contact))] :icon [chat-icon/contact-icon-contacts-tab (multiaccounts/displayed-photo contact)]}] [quo/list-item - {:title (if-not contact - (i18n/label :t/wallet-choose-recipient) - [quo/text {:size :large - :monospace true} - (utils/get-shortened-checksum-address - (if (string? contact) contact (:address contact)))]) - :accessibility-label :choose-recipient-button - :on-press #(do - (re-frame/dispatch [:dismiss-keyboard]) - (re-frame/dispatch [:bottom-sheet/show-sheet - {:content sheets/choose-recipient - :content-height 200}])) - :chevron true}])) + (merge {:title (if-not contact + (i18n/label :t/wallet-choose-recipient) + [quo/text {:size :large + :monospace true} + (utils/get-shortened-checksum-address + (if (string? contact) contact (:address contact)))]) + :accessibility-label :choose-recipient-button + :on-press #(do + (re-frame/dispatch [:dismiss-keyboard]) + (re-frame/dispatch [:wallet.send/navigate-to-recipient-code])) + :chevron true} + (when-not contact + {:icon :main-icons/add + :theme :accent}))])) (defn set-max [token] [react/touchable-highlight @@ -102,140 +107,12 @@ [react/text {:style {:color colors/gray :margin-left 16 :margin-bottom 8 :font-size 15 :line-height 22}} (str (i18n/format-currency (* amount price) (:code wallet-currency)) " " (:code wallet-currency))]))) -(views/defview sheet [_] - (views/letsubs [{:keys [amount-error amount-text - request? - from token to sign-enabled? from-chat?] - :as tx} - [:wallet.send/prepare-transaction-with-balance] - prices [:prices] - wallet-currency [:wallet/currency] - window-height [:dimensions/window-height] - window-width [:dimensions/window-width] - keyboard-height [:keyboard-height]] - (let [small-screen? (< (- window-height keyboard-height) 450) - to-norm (ethereum/normalized-hex (if (string? to) to (:address to)))] - [react/view {:style (styles/sheet small-screen?)} - [react/view {:flex-direction :row :padding-horizontal 16 :align-items :center - :margin-top 12 :margin-bottom 4} - [react/text-input - {:style {:font-size (if small-screen? 24 38) - :max-width (- (* (/ window-width 4) 3) 106) - :color (if amount-error colors/red colors/black)} - :keyboard-type :decimal-pad - :auto-capitalize :words - :accessibility-label :amount-input - :default-value amount-text - :editable (not request?) - :auto-focus true - :on-change-text #(re-frame/dispatch [:wallet.send/set-amount-text %]) - :placeholder "0.0 "}] - [asset-selector tx window-width] - (when amount-error - [tooltip/tooltip (if from - amount-error - (i18n/label :t/select-account-first)) - {:bottom-value 2 - :font-size 12}])] - [fiat-value amount-text token prices wallet-currency] - (when-not (or request? from-chat?) - [set-max token]) - [components/separator] - (when-not small-screen? - [quo/list-header (i18n/label :t/from)]) - [react/view {:flex-direction :row :flex 1 :align-items :center} - (when small-screen? - [react/i18n-text {:style {:width 50 :text-align :right :color colors/gray} :key :t/from}]) - [react/view {:flex 1} - [render-account from token :wallet.send/set-field]]] - (when-not small-screen? - [quo/list-header - (i18n/label :t/to)]) - [react/view {:flex-direction :row :flex 1 :align-items :center} - (when small-screen? - [react/i18n-text {:style {:width 50 :text-align :right :color colors/gray} :key :t/to}]) - [react/view {:flex 1} - [render-contact to from-chat?]]] - [toolbar/toolbar - {:left - [quo/button - {:type :secondary - :on-press #(re-frame/dispatch [:wallet/cancel-transaction-command])} - (i18n/label :t/cancel)] - :right - [quo/button - {:accessibility-label :send-transaction-bottom-sheet - :disabled (not sign-enabled?) - :on-press #(re-frame/dispatch - [(cond - request? - :wallet.ui/sign-transaction-button-clicked-from-request - from-chat? - :wallet.ui/sign-transaction-button-clicked-from-chat - :else - :wallet.ui/sign-transaction-button-clicked) tx])} - (if (and (not request?) from-chat? (not to-norm)) - (i18n/label :t/wallet-send) - (i18n/label :t/next))]}]]))) -(views/defview request-sheet [_] - (views/letsubs [{:keys [amount-error amount-text from token sign-enabled?] :as tx} - [:wallet.request/prepare-transaction-with-balance] - window-height [:dimensions/window-height] - window-width [:dimensions/window-width] - prices [:prices] - wallet-currency [:wallet/currency] - keyboard-height [:keyboard-height]] - (let [small-screen? (< (- window-height keyboard-height) 450)] - [react/view {:style (styles/sheet small-screen?)} - [header {:small-screen? small-screen? - :label :t/request-transaction}] - [react/view {:flex-direction :row :padding-horizontal 24 :align-items :center - :margin-vertical (if small-screen? 8 16)} - [react/text-input - {:style {:font-size (if small-screen? 24 38) - :color (when amount-error colors/red) - :flex-shrink 1} - :keyboard-type :decimal-pad - :auto-capitalize :words - :accessibility-label :amount-input - :default-value amount-text - :auto-focus true - :on-change-text #(re-frame/dispatch [:wallet.request/set-amount-text %]) - :placeholder "0.0 "}] - [asset-selector tx window-width] - (when amount-error - [tooltip/tooltip amount-error {:bottom-value 2 - :font-size 12}])] - [fiat-value amount-text token prices wallet-currency] - [components/separator] - (when-not small-screen? - [quo/list-header - (i18n/label :t/to)]) - [react/view {:flex-direction :row :flex 1 :align-items :center} - (when small-screen? - [react/i18n-text {:style {:width 50 :text-align :right :color colors/gray} :key :t/to}]) - [react/view {:flex 1} - [render-account from token :wallet.request/set-field]]] - [toolbar/toolbar - {:left - [react/view {:padding-horizontal 8} - [quo/button - {:type :secondary - :on-press #(re-frame/dispatch [:wallet/cancel-transaction-command])} - (i18n/label :t/cancel)]] - :right - [quo/button - {:accessibility-label :request-transaction-bottom-sheet - :disabled (not sign-enabled?) - :on-press #(re-frame/dispatch - [:wallet.ui/request-transaction-button-clicked tx])} - (i18n/label :t/wallet-request)]}]]))) (views/defview select-account-sheet [{:keys [from message]}] (views/letsubs [window-height [:dimensions/window-height] keyboard-height [:keyboard-height]] (let [small-screen? (< (- window-height keyboard-height) 450)] - [react/view {:style (styles/sheet small-screen?)} + [react/view {:style (styles/acc-sheet)} [header {:small-screen? small-screen? :label :t/select-account}] [react/view {:flex-direction :row :padding-horizontal 24 :align-items :center @@ -265,32 +142,144 @@ (:address from)])} (i18n/label :t/share)]}]]))) -(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)) - (not (:request-command? tx))) - (select-keys tx [:from-chat?])) - sheet])) - -(defview request-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)) - (:request-command? tx)) - (select-keys tx [:from-chat? :request?])) - request-sheet])) - (defview select-account [] (letsubs [data [:commands/select-account]] [bottom-panel/animated-bottom-panel data select-account-sheet])) + +(views/defview request-transaction [_] + (views/letsubs [{:keys [amount-error amount-text from token sign-enabled?] :as tx} + [:wallet.request/prepare-transaction-with-balance] + window-width [:dimensions/window-width] + prices [:prices] + wallet-currency [:wallet/currency]] + [kb-presentation/keyboard-avoiding-view {:style {:flex 1}} + [react/view {:flex 1} + [topbar/topbar + {:navigation {:on-press + #(do + (re-frame/dispatch [:wallet/cancel-transaction-command]) + (re-frame/dispatch [:navigate-back]))} + :modal? true + :border-bottom true + :title (i18n/label :t/request-transaction)}] + [react/scroll-view {:style {:flex 1} + :keyboard-should-persist-taps :handled} + [react/view {:style (styles/sheet)} + [react/view {:flex-direction :row :padding-horizontal 24 :align-items :center + :margin-vertical 16} + [react/text-input + {:style {:font-size 38 + :color (when amount-error colors/red) + :flex-shrink 1} + :keyboard-type :decimal-pad + :auto-capitalize :words + :accessibility-label :amount-input + :default-value amount-text + :auto-focus true + :on-change-text #(re-frame/dispatch [:wallet.request/set-amount-text %]) + :placeholder "0.0 "}] + [asset-selector tx window-width] + (when amount-error + [tooltip/tooltip amount-error {:bottom-value 2 + :font-size 12}])] + [fiat-value amount-text token prices wallet-currency] + [components/separator] + [quo/list-header + (i18n/label :t/to)] + [react/view {:flex-direction :row :flex 1 :align-items :center} + [react/view {:flex 1} + [render-account from token :wallet.request/set-field]]]]] + [toolbar/toolbar + {:show-border? true + :right + [quo/button + {:type :secondary + :after :main-icon/next + :accessibility-label :request-transaction-bottom-sheet + :disabled (not sign-enabled?) + :on-press #(do + (re-frame/dispatch + [:wallet.ui/request-transaction-button-clicked tx]) + (re-frame/dispatch [:navigate-back]))} + (i18n/label :t/wallet-request)]}]]])) + +(views/defview prepare-send-transaction [_] + (views/letsubs [{:keys [amount-error amount-text + request? + from token to sign-enabled? from-chat?] + :as tx} + [:wallet.send/prepare-transaction-with-balance] + prices [:prices] + wallet-currency [:wallet/currency] + window-width [:dimensions/window-width]] + (let [to-norm (ethereum/normalized-hex (if (string? to) to (:address to)))] + [kb-presentation/keyboard-avoiding-view {:style {:flex 1}} + [react/view {:flex 1} + [topbar/topbar + {:navigation {:on-press + #(do + (re-frame/dispatch [:wallet/cancel-transaction-command]) + (re-frame/dispatch [:navigate-back]))} + :modal? true + :border-bottom true + :title (i18n/label :t/send-transaction)}] + [react/scroll-view {:style {:flex 1} + :keyboard-should-persist-taps :handled} + [react/view {:style (styles/sheet)} + [react/view {:flex-direction :row :padding-horizontal 16 :align-items :center + :margin-top 12 :margin-bottom 4} + [react/text-input + {:style {:font-size 38 + :max-width (- (* (/ window-width 4) 3) 106) + :color (if amount-error colors/red colors/black)} + :keyboard-type :decimal-pad + :auto-capitalize :words + :accessibility-label :amount-input + :default-value amount-text + :editable (not request?) + :auto-focus true + :on-change-text #(re-frame/dispatch [:wallet.send/set-amount-text %]) + :placeholder "0.0 "}] + [asset-selector tx window-width] + (when amount-error + [tooltip/tooltip (if from + amount-error + (i18n/label :t/select-account-first)) + {:bottom-value 2 + :font-size 12}])] + [fiat-value amount-text token prices wallet-currency] + (when-not (or request? from-chat?) + [set-max token]) + [components/separator] + [quo/list-header (i18n/label :t/from)] + [react/view {:flex-direction :row :flex 1 :align-items :center} + [react/view {:flex 1} + [render-account from token :wallet.send/set-field]]] + [quo/list-header + (i18n/label :t/to)] + [react/view {:flex-direction :row :flex 1 :align-items :center} + [react/view {:flex 1} + [render-contact to from-chat?]]]]] + [toolbar/toolbar + {:show-border? true + :right + [quo/button + {:accessibility-label :send-transaction-bottom-sheet + :type :secondary + :after :main-icon/next + :disabled (not sign-enabled?) + :on-press #(do + (re-frame/dispatch + [(cond + request? + :wallet.ui/sign-transaction-button-clicked-from-request + from-chat? + :wallet.ui/sign-transaction-button-clicked-from-chat + :else + :wallet.ui/sign-transaction-button-clicked) tx]) + (re-frame/dispatch [:navigate-back]))} + (if (and (not request?) from-chat? (not to-norm)) + (i18n/label :t/wallet-send) + (i18n/label :t/next))]}]]]))) \ No newline at end of file diff --git a/src/status_im/wallet/choose_recipient/core.cljs b/src/status_im/wallet/choose_recipient/core.cljs index 771f775bdb..b5d282b25a 100644 --- a/src/status_im/wallet/choose_recipient/core.cljs +++ b/src/status_im/wallet/choose_recipient/core.cljs @@ -2,7 +2,6 @@ (:require [re-frame.core :as re-frame] [status-im.contact.db :as contact.db] [status-im.ethereum.core :as ethereum] - [status-im.ethereum.eip55 :as eip55] [status-im.ethereum.eip681 :as eip681] [status-im.ethereum.ens :as ens] [status-im.i18n :as i18n] @@ -11,9 +10,7 @@ [status-im.router.core :as router] [status-im.qr-scanner.core :as qr-scaner] [status-im.ui.components.bottom-sheet.core :as bottom-sheet] - [status-im.navigation :as navigation] - [clojure.string :as string] - [status-im.ethereum.stateofus :as stateofus])) + [status-im.navigation :as navigation])) ;; FIXME(Ferossgp): Should be part of QR scanner not wallet (fx/defn toggle-flashlight @@ -30,10 +27,14 @@ (assoc-in fx [:db :wallet :send-transaction :gas] ethereum/default-transaction-gas)) -(re-frame/reg-fx - ::resolve-address - (fn [{:keys [registry ens-name cb]}] - (ens/get-addr registry ens-name cb))) +(fx/defn set-recipient + {:events [:wallet.send/set-recipient]} + [{:keys [db]} address] + {:db (-> db + (dissoc :wallet/recipient) + (assoc-in [:ui/search :recipient-filter] nil) + (assoc-in [:wallet/prepare-transaction :to] address)) + :dispatch [:navigate-back]}) (re-frame/reg-fx ::resolve-addresses @@ -50,29 +51,6 @@ (.catch (fn [error] (js/console.log error)))))) -(fx/defn set-recipient - {:events [:wallet.send/set-recipient ::recipient-address-resolved]} - [{:keys [db]} raw-recipient] - (let [chain (ethereum/chain-keyword db) - recipient (when raw-recipient (string/trim raw-recipient))] - (cond - (ethereum/address? recipient) - (let [checksum (eip55/address->checksum recipient)] - (if (eip55/valid-address-checksum? checksum) - {:db (-> db - (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})})) - (not (string/blank? recipient)) - {::resolve-address {:registry (get ens/ens-registries chain) - :ens-name (if (= (.indexOf ^js recipient ".") -1) - (stateofus/subdomain recipient) - recipient) - :cb #(re-frame/dispatch [::recipient-address-resolved %])}} - :else - {:ui/show-error (i18n/label :t/wallet-invalid-address {:data recipient})}))) - (defn- fill-prepare-transaction-details [db {:keys [address name value symbol gas gasPrice gasLimit] @@ -101,30 +79,29 @@ (let [{:keys [address] :as details} (eip681/extract-request-details data all-tokens)] (if address - (if (:wallet/prepare-transaction db) - {:db (update db :wallet/prepare-transaction assoc - :to address :to-name (find-address-name db address))} - (let [current-chain-id (get-in networks [current-network :config :NetworkId])] - (merge {:db (fill-prepare-transaction-details db details all-tokens)} - (when (and chain-id (not= current-chain-id chain-id)) - {:ui/show-error (i18n/label :t/wallet-invalid-chain-id - {:data uri :chain current-chain-id})})))) + (if (:wallet/recipient db) + {:db (update db :wallet/recipient assoc :resolved-address address + :address address) + ;;android + :dispatch-later [{:ms 1000 :dispatch [:wallet.recipient/focus-input]}]} + (if (:wallet/prepare-transaction db) + {:db (update db :wallet/prepare-transaction assoc + :to address :to-name (find-address-name db address))} + (let [current-chain-id (get-in networks [current-network :config :NetworkId])] + (merge {:db (fill-prepare-transaction-details db details all-tokens) + :dispatch [:navigate-to :prepare-send-transaction]} + (when (and chain-id (not= current-chain-id chain-id)) + {:ui/show-error (i18n/label :t/wallet-invalid-chain-id + {:data uri :chain current-chain-id})}))))) {:ui/show-error (i18n/label :t/wallet-invalid-address {:data uri})}))) (fx/defn qr-scanner-allowed {:events [:wallet.send/qr-scanner]} [{:keys [db] :as cofx} options] (fx/merge cofx - (when (:modal-opened? options) - {:db (assoc-in db [:wallet/prepare-transaction :modal-opened?] true)}) (bottom-sheet/hide-bottom-sheet) (qr-scaner/scan-qr-code options))) -(fx/defn qr-scanner-cancel - {:events [:wallet.send/qr-scanner-cancel]} - [{db :db} _] - {:db (assoc-in db [:wallet/prepare-transaction :modal-opened?] false)}) - (fx/defn parse-eip681-uri-and-resolve-ens {:events [:wallet/parse-eip681-uri-and-resolve-ens]} [{db :db :as cofx} {:keys [message uri paths ens-names error]}] @@ -154,7 +131,4 @@ [cofx data _] (fx/merge cofx (navigation/navigate-back) - (parse-eip681-uri-and-resolve-ens (router/match-eip681 data)) - (fn [{:keys [db]}] - (when (get-in db [:wallet/prepare-transaction :modal-opened?]) - {:db (assoc-in db [:wallet/prepare-transaction :modal-opened?] false)})))) + (parse-eip681-uri-and-resolve-ens (router/match-eip681 data)))) diff --git a/src/status_im/wallet/core.cljs b/src/status_im/wallet/core.cljs index fe6fc33c81..332f8577c5 100644 --- a/src/status_im/wallet/core.cljs +++ b/src/status_im/wallet/core.cljs @@ -26,7 +26,8 @@ [status-im.wallet.prices :as prices] [status-im.wallet.utils :as wallet.utils] [status-im.native-module.core :as status] - [status-im.ui.screens.mobile-network-settings.utils :as mobile-network-utils])) + [status-im.ui.screens.mobile-network-settings.utils :as mobile-network-utils] + status-im.wallet.recipient.core)) (defn get-balance [{:keys [address on-success on-error]}] @@ -208,6 +209,13 @@ (when config/erc20-contract-warnings-enabled? {:wallet/validate-tokens [default-tokens all-default-tokens]})))) +(fx/defn initialize-favourites + [{:keys [db]} favourites] + {:db (assoc db :wallet/favourites (reduce (fn [acc {:keys [address] :as favourit}] + (assoc acc address favourit)) + {} + favourites))}) + (fx/defn update-balances [{{:keys [network-status :wallet/all-tokens multiaccount :multiaccount/accounts] :as db} :db @@ -419,7 +427,8 @@ :symbol symbol :amount-text amount-text :request? true - :from-chat? true})})) + :from-chat? true}) + :dispatch [:navigate-to :prepare-send-transaction]})) (fx/defn sign-transaction-button-clicked-from-request {:events [:wallet.ui/sign-transaction-button-clicked-from-request]} @@ -513,7 +522,8 @@ (:multiaccount/accounts db)) :to contact :symbol :ETH - :from-chat? true})} + :from-chat? true}) + :dispatch [:navigate-to :prepare-send-transaction]} ens-verified (assoc ::resolve-address {:registry (get ens/ens-registries chain) @@ -535,7 +545,8 @@ contact.db/enrich-contact)) :symbol :ETH :from-chat? true - :request-command? true})})) + :request-command? true}) + :dispatch [:navigate-to :request-transaction]})) (fx/defn prepare-transaction-from-wallet {:events [:wallet/prepare-transaction-from-wallet]} @@ -545,6 +556,7 @@ :to nil :symbol :ETH :from-chat? false}) + :dispatch [:navigate-to :prepare-send-transaction] :signing/update-gas-price {:success-event :wallet.send/update-gas-price-success}}) (fx/defn cancel-transaction-command @@ -595,9 +607,10 @@ {:events [:wallet.send/navigate-to-recipient-code]} [{:keys [db] :as cofx}] (fx/merge cofx - {:db (assoc-in db [:wallet/prepare-transaction :modal-opened?] true)} + {:db (-> db + (assoc :wallet/recipient {}))} (bottom-sheet/hide-bottom-sheet) - (navigation/navigate-to-cofx :contact-code nil))) + (navigation/navigate-to-cofx :recipient nil))) (fx/defn show-delete-account-confirmation {:events [:wallet.settings/show-delete-account-confirmation]} diff --git a/src/status_im/wallet/recipient/core.cljs b/src/status_im/wallet/recipient/core.cljs new file mode 100644 index 0000000000..5d354c1018 --- /dev/null +++ b/src/status_im/wallet/recipient/core.cljs @@ -0,0 +1,111 @@ +(ns status-im.wallet.recipient.core + (:require [re-frame.core :as re-frame] + [status-im.ui.components.react :as react] + [clojure.string :as string] + [status-im.utils.fx :as fx] + [status-im.utils.utils :as utils] + [status-im.ethereum.ens :as ens] + [status-im.ethereum.core :as ethereum] + [status-im.utils.random :as random] + [status-im.ethereum.eip55 :as eip55] + [status-im.i18n :as i18n] + [status-im.ethereum.stateofus :as stateofus] + [status-im.navigation :as navigation] + [status-im.ethereum.json-rpc :as json-rpc])) + +;;NOTE we want to handle only last resolve +(def resolve-last-id (atom nil)) + +(re-frame/reg-fx + ::resolve-address + (fn [{:keys [registry ens-name cb]}] + (ens/get-addr registry ens-name cb))) + +(re-frame/reg-fx + :wallet.recipient/address-paste + (fn [inp-ref] + (react/get-from-clipboard + #(do + (when inp-ref (.focus inp-ref)) + (re-frame/dispatch [:wallet.recipient/address-changed (string/trim %)]))))) + +(re-frame/reg-fx + :wallet.recipient/address-paste + (fn [inp-ref] + (react/get-from-clipboard + #(do + (when inp-ref (.focus inp-ref)) + (re-frame/dispatch [:wallet.recipient/address-changed (string/trim %)]))))) + +(re-frame/reg-fx + ::focus-input + (fn [inp-ref] + (when inp-ref + (.focus inp-ref)))) + +(fx/defn focus-input + {:events [:wallet.recipient/focus-input]} + [{:keys [db]}] + {::focus-input (get-in db [:wallet/recipient :inp-ref])}) + +(fx/defn address-paste-pressed + {:events [:wallet.recipient/address-paste-pressed]} + [{:keys [db]}] + {:wallet.recipient/address-paste (get-in db [:wallet/recipient :inp-ref])}) + +(fx/defn set-recipient + {:events [::recipient-address-resolved]} + [{:keys [db]} raw-recipient id] + (when (or (not id) (= id @resolve-last-id)) + (reset! resolve-last-id nil) + (let [chain (ethereum/chain-keyword db) + recipient (utils/safe-trim raw-recipient)] + (cond + (ethereum/address? recipient) + (let [checksum (eip55/address->checksum recipient)] + (if (eip55/valid-address-checksum? checksum) + {:db (-> db + (assoc-in [:wallet/recipient :searching] false) + (assoc-in [:wallet/recipient :resolved-address] checksum))} + {:ui/show-error (i18n/label :t/wallet-invalid-address-checksum {:data recipient}) + :db (assoc-in db [:wallet/recipient :searching] false)})) + (and (not (string/blank? recipient)) (ens/valid-eth-name-prefix? recipient)) + (let [ens-name (if (= (.indexOf ^js recipient ".") -1) + (stateofus/subdomain recipient) + recipient)] + (if (ens/is-valid-eth-name? ens-name) + (do + (reset! resolve-last-id (random/id)) + {::resolve-address + {:registry (get ens/ens-registries chain) + :ens-name ens-name + :cb #(re-frame/dispatch [::recipient-address-resolved % @resolve-last-id])}}) + {:db (assoc-in db [:wallet/recipient :searching] false)})) + :else + {:db (assoc-in db [:wallet/recipient :searching] false)})))) + +(fx/defn address-changed + {:events [:wallet.recipient/address-changed]} + [{:keys [db] :as cofx} new-identity] + (fx/merge cofx + {:db (update db :wallet/recipient assoc :address new-identity :resolved-address nil + :searching true)} + (set-recipient new-identity nil))) + +(fx/defn recipient-modal-closed + {:events [:wallet/recipient-modal-closed]} + [{:keys [db]}] + {:db (dissoc db :wallet/recipient)}) + +(fx/defn add-favourite + {:events [:wallet/add-favourite]} + [{:keys [db] :as cofx} address name] + (let [new-favourite {:address address + :name (or name "")}] + (fx/merge cofx + {:db (assoc-in db [:wallet/favourites address] new-favourite) + ::focus-input (get-in db [:wallet/recipient :inp-ref]) + ::json-rpc/call [{:method "wallet_addFavourite" + :params [new-favourite] + :on-success #()}]} + (navigation/navigate-back)))) diff --git a/status-go-version.json b/status-go-version.json index c9424cf4cc..9b2d540bdc 100644 --- a/status-go-version.json +++ b/status-go-version.json @@ -2,7 +2,7 @@ "_comment": "DO NOT EDIT THIS FILE BY HAND. USE 'scripts/update-status-go.sh ' instead", "owner": "status-im", "repo": "status-go", - "version": "v0.62.1", - "commit-sha1": "36e742cb70fb7372e0d57ba391523613f6ed87d5", - "src-sha256": "06639pfr782zpp7ysaz4ljlsp87pswdnwlq0j15v7z75xfg4yz6p" + "version": "v0.62.2", + "commit-sha1": "682722b9734cef6f3c6832575d78c6996988a379", + "src-sha256": "0ry6l6f154j7d8zp4dw5gs5pwygmjr61hxnpj0yr32h7i6zdna88" } diff --git a/translations/en.json b/translations/en.json index e7fa622f33..c9bcd1bdde 100644 --- a/translations/en.json +++ b/translations/en.json @@ -1263,5 +1263,15 @@ "membership-description": "Group membership requires you to be accepted by the group admin", "group-membership-request": "Group membership request", "members-limit-reached": "Members limit reached", + "favourites": "Favourites", + "new-favourite": "New favourite", + "add-to-favourites": "Add to favourites", + "favourites-empty": "Addresses added to favourites will appear here", + "contacts-empty": "Contacts with ENS names will appear here", + "my-accounts": "My accounts", + "my-accounts-empty": "Your available accounts will appear here", + "recent-empty": "Recently used addresses will appear here", + "address-or-ens-name": "Address or ENS name", + "name-optional": "Name (optional)", "mute": "Mute" }