diff --git a/src/quo/components/inputs/title_input/view.cljs b/src/quo/components/inputs/title_input/view.cljs index 0c76307892..17c84ae30c 100644 --- a/src/quo/components/inputs/title_input/view.cljs +++ b/src/quo/components/inputs/title_input/view.cljs @@ -23,6 +23,8 @@ return-key-type size theme + on-focus + on-blur container-style] :or {max-length 0 auto-focus false @@ -48,8 +50,14 @@ :accessibility-label :profile-title-input :keyboard-appearance (quo.theme/theme-value :light :dark theme) :return-key-type return-key-type - :on-focus #(swap! focused? (constantly true)) - :on-blur #(swap! focused? (constantly false)) + :on-focus (fn [] + (when (fn? on-focus) + (on-focus)) + (reset! focused? true)) + :on-blur (fn [] + (when (fn? on-blur) + (on-blur)) + (reset! focused? false)) :auto-focus auto-focus :input-mode :text :on-change-text on-change diff --git a/src/status_im/wallet/core.cljs b/src/status_im/wallet/core.cljs index bc059434f6..b301bad40a 100644 --- a/src/status_im/wallet/core.cljs +++ b/src/status_im/wallet/core.cljs @@ -1190,7 +1190,8 @@ ::enable-local-notifications nil :dispatch-n [(when (or (not (utils.mobile-sync/syncing-allowed? cofx)) (chain/binance-chain? db)) - [:transaction/get-fetched-transfers])]} + [:transaction/get-fetched-transfers])] + :dispatch [:wallet/get-accounts-success accounts]} (check-invalid-ens) (initialize-tokens tokens custom-tokens) (initialize-favourites favourites) diff --git a/src/status_im2/common/data_store/wallet.cljs b/src/status_im2/common/data_store/wallet.cljs index 3c4cdada15..ed172b7614 100644 --- a/src/status_im2/common/data_store/wallet.cljs +++ b/src/status_im2/common/data_store/wallet.cljs @@ -1,11 +1,51 @@ (ns status-im2.common.data-store.wallet (:require - clojure.set)) + [clojure.set :as set] + [clojure.string :as string] + [status-im2.constants :as constants] + [utils.number :as utils.number])) + +(defn chain-ids-string->set + [ids-string] + (into #{} + (map utils.number/parse-int) + (string/split ids-string constants/chain-id-separator))) + +(defn chain-ids-set->string + [ids] + (string/join constants/chain-id-separator ids)) + +(defn rpc->account + [account] + (-> account + (set/rename-keys {:prodPreferredChainIds :prod-preferred-chain-ids + :testPreferredChainIds :test-preferred-chain-ids + :createdAt :created-at + :colorId :color}) + (update :prod-preferred-chain-ids chain-ids-string->set) + (update :test-preferred-chain-ids chain-ids-string->set) + (update :type keyword) + (update :color #(if (seq %) (keyword %) constants/account-default-customization-color)))) + +(defn rpc->accounts + [accounts] + (->> (filter #(not (:chat %)) accounts) + (sort-by :position) + (map rpc->account))) + +(defn <-account + [account] + (-> account + (set/rename-keys {:prod-preferred-chain-ids :prodPreferredChainIds + :test-preferred-chain-ids :testPreferredChainIds + :color :colorId}) + (update :prodPreferredChainIds chain-ids-set->string) + (update :testPreferredChainIds chain-ids-set->string))) (defn <-rpc [network] (-> network - (clojure.set/rename-keys + (set/rename-keys {:Prod :prod :Test :test :isTest :test? diff --git a/src/status_im2/common/standard_authentication/enter_password/view.cljs b/src/status_im2/common/standard_authentication/enter_password/view.cljs index d507457aba..04df0a2378 100644 --- a/src/status_im2/common/standard_authentication/enter_password/view.cljs +++ b/src/status_im2/common/standard_authentication/enter_password/view.cljs @@ -9,10 +9,10 @@ [utils.re-frame :as rf])) (defn view - [{:keys [on-enter-password button-label button-icon-left customization-color]}] - (let [{:keys [key-uid] :as profile} (rf/sub [:profile/profile-with-image]) - {:keys [error processing password]} (rf/sub [:profile/login]) - sign-in-enabled? (rf/sub [:sign-in-enabled?])] + [{:keys [on-enter-password button-label button-icon-left]}] + (let [{:keys [key-uid customization-color] :as profile} (rf/sub [:profile/profile-with-image]) + {:keys [error processing password]} (rf/sub [:profile/login]) + sign-in-enabled? (rf/sub [:sign-in-enabled?])] [:<> [rn/view {:style style/enter-password-container} [rn/view diff --git a/src/status_im2/common/standard_authentication/standard_auth/view.cljs b/src/status_im2/common/standard_authentication/standard_auth/view.cljs index 16d4373eea..dd3907b94d 100644 --- a/src/status_im2/common/standard_authentication/standard_auth/view.cljs +++ b/src/status_im2/common/standard_authentication/standard_auth/view.cljs @@ -17,7 +17,7 @@ (defn authorize [{:keys [on-enter-password biometric-auth? on-auth-success on-auth-fail on-close - auth-button-label theme blur? customization-color auth-button-icon-left]}] + auth-button-label theme blur? auth-button-icon-left]}] (biometric/get-supported-type (fn [biometric-type] (if (and biometric-auth? biometric-type) @@ -43,10 +43,9 @@ :shell? blur? :content (fn [] [enter-password/view - {:customization-color customization-color - :on-enter-password on-enter-password - :button-icon-left auth-button-icon-left - :button-label auth-button-label}])}])))))) + {:on-enter-password on-enter-password + :button-icon-left auth-button-icon-left + :button-label auth-button-label}])}])))))) (defn- view-internal [_] @@ -74,7 +73,6 @@ :auth-button-icon-left auth-button-icon-left :theme theme :blur? blur? - :customization-color customization-color :on-enter-password on-enter-password :biometric-auth? biometric-auth? :on-auth-success on-auth-success diff --git a/src/status_im2/constants.cljs b/src/status_im2/constants.cljs index 91446e9307..29f1c8eee0 100644 --- a/src/status_im2/constants.cljs +++ b/src/status_im2/constants.cljs @@ -374,3 +374,9 @@ (def ^:const mainnet-network-name :ethereum) (def ^:const optimism-network-name :optimism) (def ^:const arbitrum-network-name :arbitrum) + +(def ^:const chain-id-separator ":") + +(def ^:const account-default-customization-color :blue) + +(def ^:const wallet-account-name-max-length 20) diff --git a/src/status_im2/contexts/wallet/account/view.cljs b/src/status_im2/contexts/wallet/account/view.cljs index 3889f8e5b1..97d31a99d4 100644 --- a/src/status_im2/contexts/wallet/account/view.cljs +++ b/src/status_im2/contexts/wallet/account/view.cljs @@ -30,32 +30,31 @@ {:id :about :label (i18n/label :t/about) :accessibility-label :about}]) (defn view - [account-address] + [] (let [selected-tab (reagent/atom (:id (first tabs-data)))] (fn [] - (let [account-address (or account-address (rf/sub [:get-screen-params :wallet-accounts])) - account (rf/sub [:wallet/account account-address]) - networks (rf/sub [:wallet/network-details])] + (let [{:keys [name color emoji balance]} (rf/sub [:wallet/current-viewing-account]) + networks (rf/sub [:wallet/network-details])] [rn/view {:style {:flex 1}} [quo/page-nav {:type :wallet-networks :background :blur :icon-name :i/close - :on-press #(rf/dispatch [:navigate-back]) + :on-press #(rf/dispatch [:wallet/close-account-page]) :networks networks :networks-on-press #(js/alert "Pressed Networks") :right-side :account-switcher - :account-switcher {:customization-color :purple + :account-switcher {:customization-color color :on-press #(rf/dispatch [:show-bottom-sheet {:content account-options/view :gradient-cover? true - :customization-color :purple}]) - :emoji "🍑"}}] + :customization-color color}]) + :emoji emoji}}] [quo/account-overview - {:current-value (utils/prettify-balance (:balance account)) - :account-name (:name account) + {:current-value (utils/prettify-balance balance) + :account-name name :account :default - :customization-color :blue}] + :customization-color color}] [quo/wallet-graph {:time-frame :empty}] [quo/wallet-ctas {:send-action #(rf/dispatch [:open-modal :wallet-select-address]) diff --git a/src/status_im2/contexts/wallet/common/screen_base/create_or_edit_account/style.cljs b/src/status_im2/contexts/wallet/common/screen_base/create_or_edit_account/style.cljs index ebb920f267..99561c94ba 100644 --- a/src/status_im2/contexts/wallet/common/screen_base/create_or_edit_account/style.cljs +++ b/src/status_im2/contexts/wallet/common/screen_base/create_or_edit_account/style.cljs @@ -27,6 +27,10 @@ :padding-top 12 :padding-bottom 16}) +(def error-container + {:margin-horizontal 20 + :margin-bottom 16}) + (def divider-1 {:margin-bottom 12}) diff --git a/src/status_im2/contexts/wallet/common/screen_base/create_or_edit_account/view.cljs b/src/status_im2/contexts/wallet/common/screen_base/create_or_edit_account/view.cljs index 525247a396..877defb26e 100644 --- a/src/status_im2/contexts/wallet/common/screen_base/create_or_edit_account/view.cljs +++ b/src/status_im2/contexts/wallet/common/screen_base/create_or_edit_account/view.cljs @@ -3,6 +3,7 @@ [quo.theme :as quo.theme] [react-native.core :as rn] [react-native.safe-area :as safe-area] + [status-im2.constants :as constants] [status-im2.contexts.wallet.common.screen-base.create-or-edit-account.style :as style] [utils.i18n :as i18n] [utils.re-frame :as rf])) @@ -10,7 +11,8 @@ (defn- view-internal [{:keys [margin-top? page-nav-right-side account-name account-color account-emoji on-change-name on-change-color - on-change-emoji section-label bottom-action? bottom-action-label bottom-action-props + on-change-emoji on-focus on-blur section-label bottom-action? + bottom-action-label bottom-action-props custom-bottom-action]} & children] (let [{:keys [top bottom]} (safe-area/get-insets) margin-top (if (false? margin-top?) 0 top) @@ -45,12 +47,13 @@ :i/reaction]] [quo/title-input {:placeholder (i18n/label :t/account-name-input-placeholder) - :max-length 24 + :max-length constants/wallet-account-name-max-length :blur? true :default-value account-name :on-change-text on-change-name :container-style style/title-input-container - :return-key-type :done}] + :on-focus on-focus + :on-blur on-blur}] [quo/divider-line {:container-style style/divider-1}] [quo/section-label {:section (i18n/label :t/colour) diff --git a/src/status_im2/contexts/wallet/common/sheets/account_options/view.cljs b/src/status_im2/contexts/wallet/common/sheets/account_options/view.cljs index 3c1f47bf5f..8ae602f90f 100644 --- a/src/status_im2/contexts/wallet/common/sheets/account_options/view.cljs +++ b/src/status_im2/contexts/wallet/common/sheets/account_options/view.cljs @@ -8,28 +8,37 @@ (defn view [] - [:<> - [quo/drawer-top temp/account-data] - [quo/action-drawer - [[{:icon :i/edit - :accessibility-label :edit - :label (i18n/label :t/edit-account) - :on-press #(rf/dispatch [:navigate-to :wallet-edit-account])} - {:icon :i/copy - :accessibility-label :copy-address - :label (i18n/label :t/copy-address)} - {:icon :i/share - :accessibility-label :share-account - :label (i18n/label :t/share-account)} - {:icon :i/delete - :accessibility-label :remove-account - :label (i18n/label :t/remove-account) - :danger? true}]]] - [quo/divider-line {:container-style {:margin-top 8}}] - [quo/section-label - {:section (i18n/label :t/select-another-account) - :container-style style/drawer-section-label}] - [rn/flat-list - {:data temp/other-accounts - :render-fn (fn [account] [quo/account-item {:account-props account}]) - :style {:margin-horizontal 8}}]]) + (let [{:keys [name color emoji address]} (rf/sub [:wallet/current-viewing-account])] + [:<> + [quo/drawer-top + {:title name + :type :account + :networks [{:network-name :ethereum :short-name "eth"} + {:network-name :optimism :short-name "opt"} + {:network-name :arbitrum :short-name "arb1"}] + :description address + :account-avatar-emoji emoji + :customization-color color}] + [quo/action-drawer + [[{:icon :i/edit + :accessibility-label :edit + :label (i18n/label :t/edit-account) + :on-press #(rf/dispatch [:navigate-to :wallet-edit-account])} + {:icon :i/copy + :accessibility-label :copy-address + :label (i18n/label :t/copy-address)} + {:icon :i/share + :accessibility-label :share-account + :label (i18n/label :t/share-account)} + {:icon :i/delete + :accessibility-label :remove-account + :label (i18n/label :t/remove-account) + :danger? true}]]] + [quo/divider-line {:container-style {:margin-top 8}}] + [quo/section-label + {:section (i18n/label :t/select-another-account) + :container-style style/drawer-section-label}] + [rn/flat-list + {:data temp/other-accounts + :render-fn (fn [account] [quo/account-item {:account-props account}]) + :style {:margin-horizontal 8}}]])) diff --git a/src/status_im2/contexts/wallet/common/sheets/network_preferences/view.cljs b/src/status_im2/contexts/wallet/common/sheets/network_preferences/view.cljs index 155374701f..fb00e80b2a 100644 --- a/src/status_im2/contexts/wallet/common/sheets/network_preferences/view.cljs +++ b/src/status_im2/contexts/wallet/common/sheets/network_preferences/view.cljs @@ -4,59 +4,67 @@ [quo.foundations.resources :as resources] [quo.theme :as quo.theme] [status-im2.contexts.wallet.common.sheets.network-preferences.style :as style] - [utils.i18n :as i18n])) + [utils.i18n :as i18n] + [utils.re-frame :as rf])) -(def mainnet +(defn- mainnet + [account-color] [{:title "Mainnet" :image :icon-avatar :image-props {:icon (resources/get-network :ethereum) :size :size-20} :action :selector - :action-props {:type :checkbox}}]) + :action-props {:type :checkbox + :customization-color account-color}}]) -(def networks-list +(defn- networks-list + [account-color] [{:title "Optimism" :image :icon-avatar :image-props {:icon (resources/get-network :optimism) :size :size-20} :action :selector - :action-props {:type :checkbox}} + :action-props {:type :checkbox + :customization-color account-color}} {:title "Arbitrum" :image :icon-avatar :image-props {:icon (resources/get-network :arbitrum) :size :size-20} :action :selector - :action-props {:type :checkbox}}]) + :action-props {:type :checkbox + :customization-color account-color}}]) (defn- view-internal - [{:keys [address on-save theme]}] - [:<> - [quo/drawer-top - {:title (i18n/label :t/network-preferences) - :description (i18n/label :t/network-preferences-desc)}] - [quo/data-item - {:status :default - :size :default - :description :default - :label :none - :blur? false - :card? true - :title (i18n/label :t/address) - :subtitle address - :container-style (merge style/data-item - {:background-color (colors/theme-colors colors/neutral-2_5 - colors/neutral-90 - theme)})}] - [quo/category - {:list-type :settings - :data mainnet}] - [quo/category - {:list-type :settings - :label (i18n/label :t/layer-2) - :data networks-list}] - [quo/bottom-actions - {:button-one-label (i18n/label :t/update) - :button-one-props {:disabled? true - :on-press on-save}}]]) + [{:keys [on-save theme]}] + (let [{:keys [color address]} (rf/sub [:wallet/current-viewing-account])] + [:<> + [quo/drawer-top + {:title (i18n/label :t/network-preferences) + :description (i18n/label :t/network-preferences-desc)}] + [quo/data-item + {:status :default + :size :default + :description :default + :label :none + :blur? false + :card? true + :title (i18n/label :t/address) + :subtitle address + :container-style (merge style/data-item + {:background-color (colors/theme-colors colors/neutral-2_5 + colors/neutral-90 + theme)})}] + [quo/category + {:list-type :settings + :data (mainnet color)}] + [quo/category + {:list-type :settings + :label (i18n/label :t/layer-2) + :data (networks-list color)}] + [quo/bottom-actions + {:button-one-label (i18n/label :t/update) + :button-one-props {:disabled? true + :on-press on-save + :customization-color color}}]])) (def view (quo.theme/with-theme view-internal)) diff --git a/src/status_im2/contexts/wallet/common/temp.cljs b/src/status_im2/contexts/wallet/common/temp.cljs index ae556ac795..c322ffa7f7 100644 --- a/src/status_im2/contexts/wallet/common/temp.cljs +++ b/src/status_im2/contexts/wallet/common/temp.cljs @@ -149,16 +149,6 @@ :action :icon :on-press-icon on-press-icon}]) -(def account-data - {:title "Trip to Vegas" - :type :account - :networks [{:network-name :ethereum :short-name "eth"} - {:network-name :optimism :short-name "opt"} - {:network-name :arbitrum :short-name "arb1"}] - :description "0x39cf6E0Ba4C4530735616e1Ee7ff5FbCB726fBd4" - :account-avatar-emoji "🍑" - :customization-color :purple}) - (def other-accounts [{:customization-color :flamingo :emoji "🍿" diff --git a/src/status_im2/contexts/wallet/create_account/view.cljs b/src/status_im2/contexts/wallet/create_account/view.cljs index f3009e7ebd..4a4490f827 100644 --- a/src/status_im2/contexts/wallet/create_account/view.cljs +++ b/src/status_im2/contexts/wallet/create_account/view.cljs @@ -6,14 +6,14 @@ [react-native.safe-area :as safe-area] [reagent.core :as reagent] [status-im2.common.standard-authentication.standard-auth.view :as standard-auth] + [status-im2.constants :as constants] + [status-im2.contexts.emoji-picker.utils :as emoji-picker.utils] [status-im2.contexts.wallet.common.utils :as utils] [status-im2.contexts.wallet.create-account.style :as style] [utils.i18n :as i18n] [utils.re-frame :as rf] [utils.responsiveness :refer [iphone-11-Pro-20-pixel-from-width]])) -(def diamond-emoji "\uD83D\uDC8E") - (defn keypair-string [full-name] (let [first-name (utils/get-first-name full-name)] @@ -44,8 +44,8 @@ (let [top (safe-area/get-top) bottom (safe-area/get-bottom) account-color (reagent/atom :blue) - emoji (reagent/atom diamond-emoji) - number-of-accounts (count (rf/sub [:profile/wallet-accounts])) + emoji (reagent/atom (emoji-picker.utils/random-emoji)) + number-of-accounts (count (rf/sub [:wallet/accounts])) account-name (reagent/atom (i18n/label :t/default-account-name {:number (inc number-of-accounts)})) derivation-path (reagent/atom (utils/get-derivation-path number-of-accounts)) @@ -84,7 +84,7 @@ {:customization-color @account-color :placeholder "Type something here" :on-change-text on-change-text - :max-length 24 + :max-length constants/wallet-account-name-max-length :blur? true :disabled? false :default-value @account-name diff --git a/src/status_im2/contexts/wallet/edit_account/view.cljs b/src/status_im2/contexts/wallet/edit_account/view.cljs index f7a2a0884f..702214682d 100644 --- a/src/status_im2/contexts/wallet/edit_account/view.cljs +++ b/src/status_im2/contexts/wallet/edit_account/view.cljs @@ -1,6 +1,8 @@ (ns status-im2.contexts.wallet.edit-account.view (:require [quo.core :as quo] + [quo.foundations.colors :as colors] [quo.theme :as quo.theme] + [react-native.core :as rn] [reagent.core :as reagent] [status-im2.contexts.wallet.common.screen-base.create-or-edit-account.view :as create-or-edit-account] @@ -9,48 +11,88 @@ [utils.i18n :as i18n] [utils.re-frame :as rf])) +(defn- save-account + [{:keys [theme type value account]}] + (let [edited-account-data (assoc account type value) + message (case type + :name :t/edit-wallet-account-name-updated-message + :color :t/edit-wallet-account-colour-updated-message + :emoji :t/edit-wallet-account-emoji-updated-message + nil) + callback #(rf/dispatch [:toasts/upsert + {:id :edit-account + :icon :i/correct + :icon-color (colors/resolve-color :success theme) + :text (i18n/label message)}])] + (rf/dispatch [:wallet/save-account + {:account edited-account-data + :callback callback}]))) + (defn- view-internal - [] - (let [account-name (reagent/atom "Account 1") - account-color (reagent/atom :purple) - account-emoji (reagent/atom "🍑") - account-address "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045" - on-change-name #(reset! account-name %) - on-change-color #(reset! account-color %) - on-change-emoji #(reset! account-emoji %)] + [{:keys [theme]}] + (let [edited-account-name (reagent/atom nil) + show-confirm-button? (reagent/atom false) + on-change-color (fn [edited-color {:keys [color] :as account}] + (when (not= edited-color color) + (save-account {:account account + :type :color + :value edited-color + :theme theme}))) + on-change-emoji (fn [edited-emoji {:keys [emoji] :as account}] + (when (not= edited-emoji emoji) + (save-account {:account account + :type :emoji + :value edited-emoji + :theme theme}))) + on-confirm-name (fn [account] + (rn/dismiss-keyboard!) + (save-account {:account account + :type :name + :value @edited-account-name + :theme theme}))] (fn [] - [create-or-edit-account/view - {:page-nav-right-side [{:icon-name :i/delete - :on-press #(js/alert "Delete account: to be implemented")}] - :account-name @account-name - :account-emoji @account-emoji - :account-color @account-color - :on-change-name on-change-name - :on-change-color on-change-color - :on-change-emoji on-change-emoji - :section-label :t/account-info} - [quo/data-item - {:status :default - :size :default - :description :default - :label :none - :blur? false - :icon-right? true - :right-icon :i/advanced - :card? true - :title (i18n/label :t/address) - :custom-subtitle (fn [] [quo/address-text - {:networks [{:network-name :ethereum :short-name "eth"} - {:network-name :optimism :short-name "opt"} - {:network-name :arbitrum :short-name "arb1"}] - :address account-address - :format :long}]) - :on-press (fn [] - (rf/dispatch [:show-bottom-sheet - {:content (fn [] [network-preferences/view - {:address account-address - :on-save #(js/alert - "calling on save")}])}])) - :container-style style/data-item}]]))) + (let [{:keys [name emoji address color] + :as account} (rf/sub [:wallet/current-viewing-account]) + account-name (or @edited-account-name name) + button-disabled? (or (nil? @edited-account-name) (= name @edited-account-name))] + [create-or-edit-account/view + {:page-nav-right-side [{:icon-name :i/delete + :on-press #(js/alert "Delete account: to be implemented")}] + :account-name account-name + :account-emoji emoji + :account-color color + :on-change-name #(reset! edited-account-name %) + :on-change-color #(on-change-color % account) + :on-change-emoji #(on-change-emoji % account) + :section-label :t/account-info + :on-focus #(reset! show-confirm-button? true) + :on-blur #(reset! show-confirm-button? false) + :bottom-action? @show-confirm-button? + :bottom-action-label :t/update-account-name + :bottom-action-props {:customization-color color + :disabled? button-disabled? + :on-press #(on-confirm-name account)}} + [quo/data-item + {:status :default + :size :default + :description :default + :label :none + :blur? false + :icon-right? true + :right-icon :i/advanced + :card? true + :title (i18n/label :t/address) + :custom-subtitle (fn [] [quo/address-text + {:networks [{:network-name :ethereum :short-name "eth"} + {:network-name :optimism :short-name "opt"} + {:network-name :arbitrum :short-name "arb1"}] + :address address + :format :long}]) + :on-press (fn [] + (rf/dispatch [:show-bottom-sheet + {:content (fn [] [network-preferences/view + {:on-save #(js/alert + "calling on save")}])}])) + :container-style style/data-item}]])))) (def view (quo.theme/with-theme view-internal)) diff --git a/src/status_im2/contexts/wallet/events.cljs b/src/status_im2/contexts/wallet/events.cljs index c50cb138bd..0821ea08ab 100644 --- a/src/status_im2/contexts/wallet/events.cljs +++ b/src/status_im2/contexts/wallet/events.cljs @@ -4,34 +4,103 @@ [camel-snake-kebab.extras :as cske] [clojure.string :as string] [native-module.core :as native-module] + [quo.foundations.colors :as colors] [react-native.background-timer :as background-timer] [status-im2.common.data-store.wallet :as data-store] [status-im2.contexts.wallet.temp :as temp] [taoensso.timbre :as log] [utils.ethereum.chain :as chain] + [utils.i18n :as i18n] [utils.re-frame :as rf] [utils.security.core :as security] [utils.transforms :as types])) -(rf/defn get-wallet-token - {:events [:wallet/get-wallet-token]} - [{:keys [db]}] - (let [params (map :address (:profile/wallet-accounts db))] - {:json-rpc/call [{:method "wallet_getWalletToken" - :params [params] - :on-success #(rf/dispatch [:wallet/get-wallet-token-success %]) - :on-error (fn [error] - (log/info "failed to get wallet token" - {:event :wallet/get-wallet-token - :error error - :params params}))}]})) +(rf/reg-event-fx :wallet/show-account-created-toast + (fn [{:keys [db]} [address]] + (let [{:keys [name]} (get-in db [:wallet :accounts address])] + {:db (update db :wallet dissoc :navigate-to-account :new-account?) + :fx [[:dispatch + [:toasts/upsert + {:id :new-wallet-account-created + :icon :i/correct + :icon-color colors/success-50 + :text (i18n/label :t/account-created {:name name})}]]]}))) -(rf/defn get-wallet-token-success - {:events [:wallet/get-wallet-token-success]} - [{:keys [db]} data] - {:db (assoc db - :wallet/tokens data - :wallet/tokens-loading? false)}) +(rf/reg-event-fx :wallet/navigate-to-account + (fn [{:keys [db]} [address]] + (let [new-account? (get-in db [:wallet :new-account?])] + (cond-> {:db (assoc-in db [:wallet :current-viewing-account-address] address) + :fx [[:dispatch [:navigate-to :wallet-accounts address]]]} + + new-account? + (update :fx conj [:dispatch [:wallet/show-account-created-toast address]]))))) + +(rf/reg-event-fx :wallet/close-account-page + (fn [{:keys [db]}] + {:db (update db :wallet dissoc :current-viewing-account-address) + :fx [[:dispatch [:navigate-back]]]})) + +(rf/reg-event-fx :wallet/get-accounts-success + (fn [{:keys [db]} [accounts]] + (let [wallet-db (get db :wallet) + new-account? (:new-account? wallet-db) + navigate-to-account (:navigate-to-account wallet-db)] + (cond-> {:db (reduce (fn [db {:keys [address] :as account}] + (assoc-in db [:wallet :accounts address] account)) + db + (data-store/rpc->accounts accounts)) + :fx [[:dispatch [:wallet/get-wallet-token]]]} + + new-account? + (update :fx + conj + [:dispatch [:hide-bottom-sheet]] + [:dispatch-later + [{:dispatch [:navigate-back] + :ms 100} + {:dispatch [:wallet/navigate-to-account navigate-to-account] + :ms 300}]]))))) + +(rf/reg-event-fx :wallet/get-accounts + (fn [_] + {:fx [[:json-rpc/call + [{:method "accounts_getAccounts" + :on-success [:wallet/get-accounts-success] + :on-error #(log/info "failed to get accounts " + {:error % + :event :wallet/get-accounts})}]]]})) + +(rf/reg-event-fx + :wallet/save-account + (fn [_ [{:keys [account callback]}]] + {:fx [[:json-rpc/call + [{:method "accounts_saveAccount" + :params [(data-store/<-account account)] + :on-success (fn [] + (rf/dispatch [:wallet/get-accounts]) + (when (fn? callback) + (callback))) + :on-error #(log/info "failed to save account " + {:error % + :event :wallet/save-account})}]]]})) + +(rf/reg-event-fx :wallet/get-wallet-token + (fn [{:keys [db]}] + (let [addresses (map :address (vals (get-in db [:wallet :accounts])))] + {:fx [[:json-rpc/call + [{:method "wallet_getWalletToken" + :params [addresses] + :on-success [:wallet/get-wallet-token-success] + :on-error #(log/info "failed to get wallet token " + {:error % + :event :wallet/get-wallet-token + :params addresses})}]]]}))) + +(rf/reg-event-fx :wallet/get-wallet-token-success + (fn [{:keys [db]} [tokens]] + {:db (assoc db + :wallet/tokens tokens + :wallet/tokens-loading? false)})) (rf/defn scan-address-success {:events [:wallet/scan-address-success]} @@ -67,10 +136,14 @@ :address address :public-key public-key :colorID color}] - {:fx [[:json-rpc/call + {:db (update db + :wallet assoc + :navigate-to-account address + :new-account? true) + :fx [[:json-rpc/call [{:method "accounts_addAccount" :params [sha3-pwd account-config] - :on-success #(rf/dispatch [:navigate-to :wallet-accounts]) + :on-success [:wallet/get-accounts] :on-error #(log/info "failed to create account " %)}]]]}))) (rf/reg-event-fx :wallet/derive-address-and-add-account diff --git a/src/status_im2/contexts/wallet/home/style.cljs b/src/status_im2/contexts/wallet/home/style.cljs index eefd32f03c..b0f065e298 100644 --- a/src/status_im2/contexts/wallet/home/style.cljs +++ b/src/status_im2/contexts/wallet/home/style.cljs @@ -12,10 +12,12 @@ {:height 86}) (def accounts-list - {:padding-horizontal 20 - :padding-top 32 - :padding-bottom 12 - :max-height 112}) + {:padding-top 32 + :padding-bottom 12 + :max-height 112}) + +(def accounts-list-container + {:padding-horizontal 20}) (def empty-container-style {:justify-content :center diff --git a/src/status_im2/contexts/wallet/home/view.cljs b/src/status_im2/contexts/wallet/home/view.cljs index e2bc6d9884..e214cce3a0 100644 --- a/src/status_im2/contexts/wallet/home/view.cljs +++ b/src/status_im2/contexts/wallet/home/view.cljs @@ -40,34 +40,33 @@ {:id :activity :label (i18n/label :t/activity) :accessibility-label :activity-tab}]) (defn account-cards - [{:keys [accounts loading? balances profile]}] + [{:keys [accounts loading? balances profile-color]}] (let [accounts-with-balances (mapv - (fn [account] + (fn [{:keys [color address] :as account}] (assoc account + :customization-color color :type :empty - :customization-color (:customization-color profile) - :on-press #(rf/dispatch [:navigate-to :wallet-accounts (:address account)]) + :on-press #(rf/dispatch [:wallet/navigate-to-account address]) :loading? loading? :balance (utils/prettify-balance - (utils/get-balance-by-address balances (:address account))))) + (utils/get-balance-by-address balances address)))) accounts)] - (conj accounts-with-balances (add-account-placeholder (:customization-color profile))))) + (conj accounts-with-balances (add-account-placeholder profile-color)))) (defn view [] - (rf/dispatch [:wallet/get-wallet-token]) (rf/dispatch [:wallet/request-collectibles {:start-at-index 0 :new-request? true}]) - (let [selected-tab (reagent/atom (:id (first tabs-data)))] + (let [top (safe-area/get-top) + selected-tab (reagent/atom (:id (first tabs-data)))] (fn [] - (let [accounts (rf/sub [:profile/wallet-accounts]) - top (safe-area/get-top) - loading? (rf/sub [:wallet/tokens-loading?]) - balances (rf/sub [:wallet/balances]) - profile (rf/sub [:profile/profile]) - networks (rf/sub [:wallet/network-details])] + (let [accounts (rf/sub [:wallet/accounts]) + loading? (rf/sub [:wallet/tokens-loading?]) + balances (rf/sub [:wallet/balances]) + profile-color (rf/sub [:profile/customization-color]) + networks (rf/sub [:wallet/network-details])] [rn/view {:style {:margin-top top :flex 1}} @@ -79,14 +78,16 @@ {:content temp/wallet-temporary-navigation}])} [quo/wallet-graph {:time-frame :empty}]] [rn/flat-list - {:style style/accounts-list - :data (account-cards {:accounts accounts - :loading? loading? - :balances balances - :profile profile}) - :horizontal true - :separator [rn/view {:style {:width 12}}] - :render-fn quo/account-card}] + {:style style/accounts-list + :content-container-style style/accounts-list-container + :data (account-cards {:accounts accounts + :loading? loading? + :balances balances + :profile-color profile-color}) + :horizontal true + :separator [rn/view {:style {:width 12}}] + :render-fn quo/account-card + :shows-horizontal-scroll-indicator false}] [quo/tabs {:style style/tabs :size 32 diff --git a/src/status_im2/subs/wallet/wallet.cljs b/src/status_im2/subs/wallet/wallet.cljs index dd7cf97c24..59c0c1c4a0 100644 --- a/src/status_im2/subs/wallet/wallet.cljs +++ b/src/status_im2/subs/wallet/wallet.cljs @@ -1,6 +1,5 @@ (ns status-im2.subs.wallet.wallet - (:require [clojure.string :as string] - [re-frame.core :as re-frame] + (:require [re-frame.core :as re-frame] [status-im2.contexts.wallet.common.utils :as utils] [utils.number])) @@ -21,7 +20,7 @@ (defn- calculate-balance [address tokens] - (let [token (get tokens (keyword (string/lower-case address))) + (let [token (get tokens (keyword address)) result (reduce (fn [acc item] (let [total-values (* (total-per-token item) @@ -31,9 +30,17 @@ token)] result)) +(re-frame/reg-sub + :wallet/accounts + :<- [:wallet] + :-> #(->> % + :accounts + vals + (sort-by :position))) + (re-frame/reg-sub :wallet/balances - :<- [:profile/wallet-accounts] + :<- [:wallet/accounts] :<- [:wallet/tokens] (fn [[accounts tokens]] (for [{:keys [address]} accounts] @@ -41,11 +48,10 @@ :balance (calculate-balance address tokens)}))) (re-frame/reg-sub - :wallet/account - :<- [:profile/wallet-accounts] + :wallet/current-viewing-account + :<- [:wallet] :<- [:wallet/balances] - (fn [[accounts balances] [_ account-address]] - (assoc - (utils/get-account-by-address accounts account-address) - :balance - (utils/get-balance-by-address balances account-address)))) + (fn [[{:keys [current-viewing-account-address] :as wallet} balances]] + (-> wallet + (get-in [:accounts current-viewing-account-address]) + (assoc :balance (utils/get-balance-by-address balances current-viewing-account-address))))) diff --git a/src/status_im2/subs/wallet/wallet_test.cljs b/src/status_im2/subs/wallet/wallet_test.cljs index 96442ff169..301c9b1920 100644 --- a/src/status_im2/subs/wallet/wallet_test.cljs +++ b/src/status_im2/subs/wallet/wallet_test.cljs @@ -1,10 +1,13 @@ (ns status-im2.subs.wallet.wallet-test - (:require [cljs.test :refer [is testing]] + (:require [cljs.test :refer [is testing use-fixtures]] [re-frame.db :as rf-db] status-im2.subs.root [test-helpers.unit :as h] [utils.re-frame :as rf])) +(use-fixtures :each + {:before #(reset! rf-db/app-db {})}) + (def tokens {:0x1 [{:decimals 1 :symbol "ETH" @@ -44,40 +47,132 @@ :marketValuesPerCurrency {:USD {:price 1000}}}]}) ;; total should be 1000 (def accounts - [{:address "0x1" - :name "Main account" - :hidden false - :removed false} - {:address "0x2" - :name "Secondary account" - :hidden false - :removed false}]) + {"0x1" {:path "m/44'/60'/0'/0/0" + :emoji "😃" + :key-uid "0x2f5ea39" + :address "0x1" + :wallet false + :name "Account One" + :type :generated + :chat false + :test-preferred-chain-ids #{5 420 421613} + :color :blue + :hidden false + :prod-preferred-chain-ids #{1 10 42161} + :position 0 + :clock 1698945829328 + :created-at 1698928839000 + :operable "fully" + :mixedcase-address "0x7bcDfc75c431" + :public-key "0x04371e2d9d66b82f056bc128064" + :removed false} + "0x2" {:path "m/44'/60'/0'/0/1" + :emoji "💎" + :key-uid "0x2f5ea39" + :address "0x2" + :wallet false + :name "Account Two" + :type :generated + :chat false + :test-preferred-chain-ids #{5 420 421613} + :color :purple + :hidden false + :prod-preferred-chain-ids #{1 10 42161} + :position 1 + :clock 1698945829328 + :created-at 1698928839000 + :operable "fully" + :mixedcase-address "0x7bcDfc75c431" + :public-key "0x04371e2d9d66b82f056bc128064" + :removed false}}) (h/deftest-sub :wallet/balances [sub-name] - (testing "returns vector of maps containing :address and :balance" - (swap! rf-db/app-db assoc - :profile/wallet-accounts accounts - :wallet/tokens tokens) - (is (= [{:address "0x1" - :balance 3250} - {:address "0x2" - :balance 2100}] + (testing "returns seq of maps containing :address and :balance" + (swap! rf-db/app-db #(-> % + (assoc-in [:wallet :accounts] accounts) + (assoc :wallet/tokens tokens))) + (is (= `({:address "0x1" + :balance 3250} + {:address "0x2" + :balance 2100}) (rf/sub [sub-name]))))) -(h/deftest-sub :wallet/account +(h/deftest-sub :wallet/accounts [sub-name] - (testing "returns current account with balance base on the account-address" - (swap! rf-db/app-db assoc - :profile/wallet-accounts accounts - :wallet/tokens tokens - :wallet/balances [{:address "0x1" - :balance 3250} - {:address "0x2" - :balance 2100}]) - (is (= {:address "0x1" - :name "Main account" - :hidden false - :removed false - :balance 3250} - (rf/sub [sub-name "0x1"]))))) + (testing "returns all accounts without balance" + (swap! rf-db/app-db + #(-> % + (assoc-in [:wallet :accounts] accounts) + (assoc :wallet/tokens tokens))) + (is + (= `({:path "m/44'/60'/0'/0/0" + :emoji "😃" + :key-uid "0x2f5ea39" + :address "0x1" + :wallet false + :name "Account One" + :type :generated + :chat false + :test-preferred-chain-ids #{5 420 421613} + :color :blue + :hidden false + :prod-preferred-chain-ids #{1 10 42161} + :position 0 + :clock 1698945829328 + :created-at 1698928839000 + :operable "fully" + :mixedcase-address "0x7bcDfc75c431" + :public-key "0x04371e2d9d66b82f056bc128064" + :removed false} + {:path "m/44'/60'/0'/0/1" + :emoji "💎" + :key-uid "0x2f5ea39" + :address "0x2" + :wallet false + :name "Account Two" + :type :generated + :chat false + :test-preferred-chain-ids #{5 420 421613} + :color :purple + :hidden false + :prod-preferred-chain-ids #{1 10 42161} + :position 1 + :clock 1698945829328 + :created-at 1698928839000 + :operable "fully" + :mixedcase-address "0x7bcDfc75c431" + :public-key "0x04371e2d9d66b82f056bc128064" + :removed false}) + (rf/sub [sub-name]))))) + +(h/deftest-sub :wallet/current-viewing-account + [sub-name] + (testing "returns current account with balance base" + (swap! rf-db/app-db + #(-> % + (assoc-in [:wallet :accounts] accounts) + (assoc-in [:wallet :current-viewing-account-address] "0x1") + (assoc :wallet/tokens tokens))) + (is + (= {:path "m/44'/60'/0'/0/0" + :emoji "😃" + :key-uid "0x2f5ea39" + :address "0x1" + :wallet false + :name "Account One" + :type :generated + :chat false + :test-preferred-chain-ids #{5 420 421613} + :color :blue + :hidden false + :prod-preferred-chain-ids #{1 10 42161} + :position 0 + :clock 1698945829328 + :created-at 1698928839000 + :operable "fully" + :mixedcase-address "0x7bcDfc75c431" + :public-key "0x04371e2d9d66b82f056bc128064" + :removed false + :balance 3250} + (rf/sub [sub-name]))))) diff --git a/translations/en.json b/translations/en.json index cfb80976e6..77f1bdbba4 100644 --- a/translations/en.json +++ b/translations/en.json @@ -2376,5 +2376,10 @@ "derive-addresses": "Derive addresses", "address-activity": "This address has activity", "sign transactions": "sign transactions", - "search-assets": "Search assets" + "search-assets": "Search assets", + "account-created": "{{name}} created", + "update-account-name": "Update account name", + "edit-wallet-account-emoji-updated-message": "Account emoji has been updated", + "edit-wallet-account-name-updated-message": "Account name has been updated", + "edit-wallet-account-colour-updated-message": "Account colour has been updated" }