Implement edit account and refactor wallet accounts events & subs (#17798)

This commit updates the following:

- Allow users to create new wallet accounts without having to re-login (latest account data is fetched immediately)
- Updates the max length of the wallet account name to 20
- Updates the account cards in the wallet home screen to render the actual account colour
- Updates the (individual) account screen to show the correct color, emoji, name and address
- Allows users to edit account name, colour and emoji
- The rest of the wallet screens would see the updated account information immediately
- Fixes the color (uses profile color) of the context tag and button color in the authentication (enter password) bottom sheet
- Fixes the overflowing of the "+" card in the wallet home when there are two or more accounts.

Signed-off-by: Mohamed Javid <19339952+smohamedjavid@users.noreply.github.com>
This commit is contained in:
Mohamed Javid 2023-11-08 22:39:32 +08:00 committed by GitHub
parent 742c451000
commit 6bb1779723
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 525 additions and 235 deletions

View File

@ -23,6 +23,8 @@
return-key-type return-key-type
size size
theme theme
on-focus
on-blur
container-style] container-style]
:or {max-length 0 :or {max-length 0
auto-focus false auto-focus false
@ -48,8 +50,14 @@
:accessibility-label :profile-title-input :accessibility-label :profile-title-input
:keyboard-appearance (quo.theme/theme-value :light :dark theme) :keyboard-appearance (quo.theme/theme-value :light :dark theme)
:return-key-type return-key-type :return-key-type return-key-type
:on-focus #(swap! focused? (constantly true)) :on-focus (fn []
:on-blur #(swap! focused? (constantly false)) (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 :auto-focus auto-focus
:input-mode :text :input-mode :text
:on-change-text on-change :on-change-text on-change

View File

@ -1190,7 +1190,8 @@
::enable-local-notifications nil ::enable-local-notifications nil
:dispatch-n [(when (or (not (utils.mobile-sync/syncing-allowed? cofx)) :dispatch-n [(when (or (not (utils.mobile-sync/syncing-allowed? cofx))
(chain/binance-chain? db)) (chain/binance-chain? db))
[:transaction/get-fetched-transfers])]} [:transaction/get-fetched-transfers])]
:dispatch [:wallet/get-accounts-success accounts]}
(check-invalid-ens) (check-invalid-ens)
(initialize-tokens tokens custom-tokens) (initialize-tokens tokens custom-tokens)
(initialize-favourites favourites) (initialize-favourites favourites)

View File

@ -1,11 +1,51 @@
(ns status-im2.common.data-store.wallet (ns status-im2.common.data-store.wallet
(:require (: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 (defn <-rpc
[network] [network]
(-> network (-> network
(clojure.set/rename-keys (set/rename-keys
{:Prod :prod {:Prod :prod
:Test :test :Test :test
:isTest :test? :isTest :test?

View File

@ -9,10 +9,10 @@
[utils.re-frame :as rf])) [utils.re-frame :as rf]))
(defn view (defn view
[{:keys [on-enter-password button-label button-icon-left customization-color]}] [{:keys [on-enter-password button-label button-icon-left]}]
(let [{:keys [key-uid] :as profile} (rf/sub [:profile/profile-with-image]) (let [{:keys [key-uid customization-color] :as profile} (rf/sub [:profile/profile-with-image])
{:keys [error processing password]} (rf/sub [:profile/login]) {:keys [error processing password]} (rf/sub [:profile/login])
sign-in-enabled? (rf/sub [:sign-in-enabled?])] sign-in-enabled? (rf/sub [:sign-in-enabled?])]
[:<> [:<>
[rn/view {:style style/enter-password-container} [rn/view {:style style/enter-password-container}
[rn/view [rn/view

View File

@ -17,7 +17,7 @@
(defn authorize (defn authorize
[{:keys [on-enter-password biometric-auth? on-auth-success on-auth-fail on-close [{: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 (biometric/get-supported-type
(fn [biometric-type] (fn [biometric-type]
(if (and biometric-auth? biometric-type) (if (and biometric-auth? biometric-type)
@ -43,10 +43,9 @@
:shell? blur? :shell? blur?
:content (fn [] :content (fn []
[enter-password/view [enter-password/view
{:customization-color customization-color {:on-enter-password on-enter-password
:on-enter-password on-enter-password :button-icon-left auth-button-icon-left
:button-icon-left auth-button-icon-left :button-label auth-button-label}])}]))))))
:button-label auth-button-label}])}]))))))
(defn- view-internal (defn- view-internal
[_] [_]
@ -74,7 +73,6 @@
:auth-button-icon-left auth-button-icon-left :auth-button-icon-left auth-button-icon-left
:theme theme :theme theme
:blur? blur? :blur? blur?
:customization-color customization-color
:on-enter-password on-enter-password :on-enter-password on-enter-password
:biometric-auth? biometric-auth? :biometric-auth? biometric-auth?
:on-auth-success on-auth-success :on-auth-success on-auth-success

View File

@ -374,3 +374,9 @@
(def ^:const mainnet-network-name :ethereum) (def ^:const mainnet-network-name :ethereum)
(def ^:const optimism-network-name :optimism) (def ^:const optimism-network-name :optimism)
(def ^:const arbitrum-network-name :arbitrum) (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)

View File

@ -30,32 +30,31 @@
{:id :about :label (i18n/label :t/about) :accessibility-label :about}]) {:id :about :label (i18n/label :t/about) :accessibility-label :about}])
(defn view (defn view
[account-address] []
(let [selected-tab (reagent/atom (:id (first tabs-data)))] (let [selected-tab (reagent/atom (:id (first tabs-data)))]
(fn [] (fn []
(let [account-address (or account-address (rf/sub [:get-screen-params :wallet-accounts])) (let [{:keys [name color emoji balance]} (rf/sub [:wallet/current-viewing-account])
account (rf/sub [:wallet/account account-address]) networks (rf/sub [:wallet/network-details])]
networks (rf/sub [:wallet/network-details])]
[rn/view {:style {:flex 1}} [rn/view {:style {:flex 1}}
[quo/page-nav [quo/page-nav
{:type :wallet-networks {:type :wallet-networks
:background :blur :background :blur
:icon-name :i/close :icon-name :i/close
:on-press #(rf/dispatch [:navigate-back]) :on-press #(rf/dispatch [:wallet/close-account-page])
:networks networks :networks networks
:networks-on-press #(js/alert "Pressed Networks") :networks-on-press #(js/alert "Pressed Networks")
:right-side :account-switcher :right-side :account-switcher
:account-switcher {:customization-color :purple :account-switcher {:customization-color color
:on-press #(rf/dispatch [:show-bottom-sheet :on-press #(rf/dispatch [:show-bottom-sheet
{:content account-options/view {:content account-options/view
:gradient-cover? true :gradient-cover? true
:customization-color :purple}]) :customization-color color}])
:emoji "🍑"}}] :emoji emoji}}]
[quo/account-overview [quo/account-overview
{:current-value (utils/prettify-balance (:balance account)) {:current-value (utils/prettify-balance balance)
:account-name (:name account) :account-name name
:account :default :account :default
:customization-color :blue}] :customization-color color}]
[quo/wallet-graph {:time-frame :empty}] [quo/wallet-graph {:time-frame :empty}]
[quo/wallet-ctas [quo/wallet-ctas
{:send-action #(rf/dispatch [:open-modal :wallet-select-address]) {:send-action #(rf/dispatch [:open-modal :wallet-select-address])

View File

@ -27,6 +27,10 @@
:padding-top 12 :padding-top 12
:padding-bottom 16}) :padding-bottom 16})
(def error-container
{:margin-horizontal 20
:margin-bottom 16})
(def divider-1 (def divider-1
{:margin-bottom 12}) {:margin-bottom 12})

View File

@ -3,6 +3,7 @@
[quo.theme :as quo.theme] [quo.theme :as quo.theme]
[react-native.core :as rn] [react-native.core :as rn]
[react-native.safe-area :as safe-area] [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] [status-im2.contexts.wallet.common.screen-base.create-or-edit-account.style :as style]
[utils.i18n :as i18n] [utils.i18n :as i18n]
[utils.re-frame :as rf])) [utils.re-frame :as rf]))
@ -10,7 +11,8 @@
(defn- view-internal (defn- view-internal
[{:keys [margin-top? page-nav-right-side account-name account-color account-emoji on-change-name [{:keys [margin-top? page-nav-right-side account-name account-color account-emoji on-change-name
on-change-color 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] custom-bottom-action]} & children]
(let [{:keys [top bottom]} (safe-area/get-insets) (let [{:keys [top bottom]} (safe-area/get-insets)
margin-top (if (false? margin-top?) 0 top) margin-top (if (false? margin-top?) 0 top)
@ -45,12 +47,13 @@
:i/reaction]] :i/reaction]]
[quo/title-input [quo/title-input
{:placeholder (i18n/label :t/account-name-input-placeholder) {:placeholder (i18n/label :t/account-name-input-placeholder)
:max-length 24 :max-length constants/wallet-account-name-max-length
:blur? true :blur? true
:default-value account-name :default-value account-name
:on-change-text on-change-name :on-change-text on-change-name
:container-style style/title-input-container :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/divider-line {:container-style style/divider-1}]
[quo/section-label [quo/section-label
{:section (i18n/label :t/colour) {:section (i18n/label :t/colour)

View File

@ -8,28 +8,37 @@
(defn view (defn view
[] []
[:<> (let [{:keys [name color emoji address]} (rf/sub [:wallet/current-viewing-account])]
[quo/drawer-top temp/account-data] [:<>
[quo/action-drawer [quo/drawer-top
[[{:icon :i/edit {:title name
:accessibility-label :edit :type :account
:label (i18n/label :t/edit-account) :networks [{:network-name :ethereum :short-name "eth"}
:on-press #(rf/dispatch [:navigate-to :wallet-edit-account])} {:network-name :optimism :short-name "opt"}
{:icon :i/copy {:network-name :arbitrum :short-name "arb1"}]
:accessibility-label :copy-address :description address
:label (i18n/label :t/copy-address)} :account-avatar-emoji emoji
{:icon :i/share :customization-color color}]
:accessibility-label :share-account [quo/action-drawer
:label (i18n/label :t/share-account)} [[{:icon :i/edit
{:icon :i/delete :accessibility-label :edit
:accessibility-label :remove-account :label (i18n/label :t/edit-account)
:label (i18n/label :t/remove-account) :on-press #(rf/dispatch [:navigate-to :wallet-edit-account])}
:danger? true}]]] {:icon :i/copy
[quo/divider-line {:container-style {:margin-top 8}}] :accessibility-label :copy-address
[quo/section-label :label (i18n/label :t/copy-address)}
{:section (i18n/label :t/select-another-account) {:icon :i/share
:container-style style/drawer-section-label}] :accessibility-label :share-account
[rn/flat-list :label (i18n/label :t/share-account)}
{:data temp/other-accounts {:icon :i/delete
:render-fn (fn [account] [quo/account-item {:account-props account}]) :accessibility-label :remove-account
:style {:margin-horizontal 8}}]]) :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}}]]))

View File

@ -4,59 +4,67 @@
[quo.foundations.resources :as resources] [quo.foundations.resources :as resources]
[quo.theme :as quo.theme] [quo.theme :as quo.theme]
[status-im2.contexts.wallet.common.sheets.network-preferences.style :as style] [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" [{:title "Mainnet"
:image :icon-avatar :image :icon-avatar
:image-props {:icon (resources/get-network :ethereum) :image-props {:icon (resources/get-network :ethereum)
:size :size-20} :size :size-20}
:action :selector :action :selector
:action-props {:type :checkbox}}]) :action-props {:type :checkbox
:customization-color account-color}}])
(def networks-list (defn- networks-list
[account-color]
[{:title "Optimism" [{:title "Optimism"
:image :icon-avatar :image :icon-avatar
:image-props {:icon (resources/get-network :optimism) :image-props {:icon (resources/get-network :optimism)
:size :size-20} :size :size-20}
:action :selector :action :selector
:action-props {:type :checkbox}} :action-props {:type :checkbox
:customization-color account-color}}
{:title "Arbitrum" {:title "Arbitrum"
:image :icon-avatar :image :icon-avatar
:image-props {:icon (resources/get-network :arbitrum) :image-props {:icon (resources/get-network :arbitrum)
:size :size-20} :size :size-20}
:action :selector :action :selector
:action-props {:type :checkbox}}]) :action-props {:type :checkbox
:customization-color account-color}}])
(defn- view-internal (defn- view-internal
[{:keys [address on-save theme]}] [{:keys [on-save theme]}]
[:<> (let [{:keys [color address]} (rf/sub [:wallet/current-viewing-account])]
[quo/drawer-top [:<>
{:title (i18n/label :t/network-preferences) [quo/drawer-top
:description (i18n/label :t/network-preferences-desc)}] {:title (i18n/label :t/network-preferences)
[quo/data-item :description (i18n/label :t/network-preferences-desc)}]
{:status :default [quo/data-item
:size :default {:status :default
:description :default :size :default
:label :none :description :default
:blur? false :label :none
:card? true :blur? false
:title (i18n/label :t/address) :card? true
:subtitle address :title (i18n/label :t/address)
:container-style (merge style/data-item :subtitle address
{:background-color (colors/theme-colors colors/neutral-2_5 :container-style (merge style/data-item
colors/neutral-90 {:background-color (colors/theme-colors colors/neutral-2_5
theme)})}] colors/neutral-90
[quo/category theme)})}]
{:list-type :settings [quo/category
:data mainnet}] {:list-type :settings
[quo/category :data (mainnet color)}]
{:list-type :settings [quo/category
:label (i18n/label :t/layer-2) {:list-type :settings
:data networks-list}] :label (i18n/label :t/layer-2)
[quo/bottom-actions :data (networks-list color)}]
{:button-one-label (i18n/label :t/update) [quo/bottom-actions
:button-one-props {:disabled? true {:button-one-label (i18n/label :t/update)
:on-press on-save}}]]) :button-one-props {:disabled? true
:on-press on-save
:customization-color color}}]]))
(def view (quo.theme/with-theme view-internal)) (def view (quo.theme/with-theme view-internal))

View File

@ -149,16 +149,6 @@
:action :icon :action :icon
:on-press-icon on-press-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 (def other-accounts
[{:customization-color :flamingo [{:customization-color :flamingo
:emoji "🍿" :emoji "🍿"

View File

@ -6,14 +6,14 @@
[react-native.safe-area :as safe-area] [react-native.safe-area :as safe-area]
[reagent.core :as reagent] [reagent.core :as reagent]
[status-im2.common.standard-authentication.standard-auth.view :as standard-auth] [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.common.utils :as utils]
[status-im2.contexts.wallet.create-account.style :as style] [status-im2.contexts.wallet.create-account.style :as style]
[utils.i18n :as i18n] [utils.i18n :as i18n]
[utils.re-frame :as rf] [utils.re-frame :as rf]
[utils.responsiveness :refer [iphone-11-Pro-20-pixel-from-width]])) [utils.responsiveness :refer [iphone-11-Pro-20-pixel-from-width]]))
(def diamond-emoji "\uD83D\uDC8E")
(defn keypair-string (defn keypair-string
[full-name] [full-name]
(let [first-name (utils/get-first-name full-name)] (let [first-name (utils/get-first-name full-name)]
@ -44,8 +44,8 @@
(let [top (safe-area/get-top) (let [top (safe-area/get-top)
bottom (safe-area/get-bottom) bottom (safe-area/get-bottom)
account-color (reagent/atom :blue) account-color (reagent/atom :blue)
emoji (reagent/atom diamond-emoji) emoji (reagent/atom (emoji-picker.utils/random-emoji))
number-of-accounts (count (rf/sub [:profile/wallet-accounts])) number-of-accounts (count (rf/sub [:wallet/accounts]))
account-name (reagent/atom (i18n/label :t/default-account-name account-name (reagent/atom (i18n/label :t/default-account-name
{:number (inc number-of-accounts)})) {:number (inc number-of-accounts)}))
derivation-path (reagent/atom (utils/get-derivation-path number-of-accounts)) derivation-path (reagent/atom (utils/get-derivation-path number-of-accounts))
@ -84,7 +84,7 @@
{:customization-color @account-color {:customization-color @account-color
:placeholder "Type something here" :placeholder "Type something here"
:on-change-text on-change-text :on-change-text on-change-text
:max-length 24 :max-length constants/wallet-account-name-max-length
:blur? true :blur? true
:disabled? false :disabled? false
:default-value @account-name :default-value @account-name

View File

@ -1,6 +1,8 @@
(ns status-im2.contexts.wallet.edit-account.view (ns status-im2.contexts.wallet.edit-account.view
(:require [quo.core :as quo] (:require [quo.core :as quo]
[quo.foundations.colors :as colors]
[quo.theme :as quo.theme] [quo.theme :as quo.theme]
[react-native.core :as rn]
[reagent.core :as reagent] [reagent.core :as reagent]
[status-im2.contexts.wallet.common.screen-base.create-or-edit-account.view :as [status-im2.contexts.wallet.common.screen-base.create-or-edit-account.view :as
create-or-edit-account] create-or-edit-account]
@ -9,48 +11,88 @@
[utils.i18n :as i18n] [utils.i18n :as i18n]
[utils.re-frame :as rf])) [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 (defn- view-internal
[] [{:keys [theme]}]
(let [account-name (reagent/atom "Account 1") (let [edited-account-name (reagent/atom nil)
account-color (reagent/atom :purple) show-confirm-button? (reagent/atom false)
account-emoji (reagent/atom "🍑") on-change-color (fn [edited-color {:keys [color] :as account}]
account-address "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045" (when (not= edited-color color)
on-change-name #(reset! account-name %) (save-account {:account account
on-change-color #(reset! account-color %) :type :color
on-change-emoji #(reset! account-emoji %)] :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 [] (fn []
[create-or-edit-account/view (let [{:keys [name emoji address color]
{:page-nav-right-side [{:icon-name :i/delete :as account} (rf/sub [:wallet/current-viewing-account])
:on-press #(js/alert "Delete account: to be implemented")}] account-name (or @edited-account-name name)
:account-name @account-name button-disabled? (or (nil? @edited-account-name) (= name @edited-account-name))]
:account-emoji @account-emoji [create-or-edit-account/view
:account-color @account-color {:page-nav-right-side [{:icon-name :i/delete
:on-change-name on-change-name :on-press #(js/alert "Delete account: to be implemented")}]
:on-change-color on-change-color :account-name account-name
:on-change-emoji on-change-emoji :account-emoji emoji
:section-label :t/account-info} :account-color color
[quo/data-item :on-change-name #(reset! edited-account-name %)
{:status :default :on-change-color #(on-change-color % account)
:size :default :on-change-emoji #(on-change-emoji % account)
:description :default :section-label :t/account-info
:label :none :on-focus #(reset! show-confirm-button? true)
:blur? false :on-blur #(reset! show-confirm-button? false)
:icon-right? true :bottom-action? @show-confirm-button?
:right-icon :i/advanced :bottom-action-label :t/update-account-name
:card? true :bottom-action-props {:customization-color color
:title (i18n/label :t/address) :disabled? button-disabled?
:custom-subtitle (fn [] [quo/address-text :on-press #(on-confirm-name account)}}
{:networks [{:network-name :ethereum :short-name "eth"} [quo/data-item
{:network-name :optimism :short-name "opt"} {:status :default
{:network-name :arbitrum :short-name "arb1"}] :size :default
:address account-address :description :default
:format :long}]) :label :none
:on-press (fn [] :blur? false
(rf/dispatch [:show-bottom-sheet :icon-right? true
{:content (fn [] [network-preferences/view :right-icon :i/advanced
{:address account-address :card? true
:on-save #(js/alert :title (i18n/label :t/address)
"calling on save")}])}])) :custom-subtitle (fn [] [quo/address-text
:container-style style/data-item}]]))) {: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)) (def view (quo.theme/with-theme view-internal))

View File

@ -4,34 +4,103 @@
[camel-snake-kebab.extras :as cske] [camel-snake-kebab.extras :as cske]
[clojure.string :as string] [clojure.string :as string]
[native-module.core :as native-module] [native-module.core :as native-module]
[quo.foundations.colors :as colors]
[react-native.background-timer :as background-timer] [react-native.background-timer :as background-timer]
[status-im2.common.data-store.wallet :as data-store] [status-im2.common.data-store.wallet :as data-store]
[status-im2.contexts.wallet.temp :as temp] [status-im2.contexts.wallet.temp :as temp]
[taoensso.timbre :as log] [taoensso.timbre :as log]
[utils.ethereum.chain :as chain] [utils.ethereum.chain :as chain]
[utils.i18n :as i18n]
[utils.re-frame :as rf] [utils.re-frame :as rf]
[utils.security.core :as security] [utils.security.core :as security]
[utils.transforms :as types])) [utils.transforms :as types]))
(rf/defn get-wallet-token (rf/reg-event-fx :wallet/show-account-created-toast
{:events [:wallet/get-wallet-token]} (fn [{:keys [db]} [address]]
[{:keys [db]}] (let [{:keys [name]} (get-in db [:wallet :accounts address])]
(let [params (map :address (:profile/wallet-accounts db))] {:db (update db :wallet dissoc :navigate-to-account :new-account?)
{:json-rpc/call [{:method "wallet_getWalletToken" :fx [[:dispatch
:params [params] [:toasts/upsert
:on-success #(rf/dispatch [:wallet/get-wallet-token-success %]) {:id :new-wallet-account-created
:on-error (fn [error] :icon :i/correct
(log/info "failed to get wallet token" :icon-color colors/success-50
{:event :wallet/get-wallet-token :text (i18n/label :t/account-created {:name name})}]]]})))
:error error
:params params}))}]}))
(rf/defn get-wallet-token-success (rf/reg-event-fx :wallet/navigate-to-account
{:events [:wallet/get-wallet-token-success]} (fn [{:keys [db]} [address]]
[{:keys [db]} data] (let [new-account? (get-in db [:wallet :new-account?])]
{:db (assoc db (cond-> {:db (assoc-in db [:wallet :current-viewing-account-address] address)
:wallet/tokens data :fx [[:dispatch [:navigate-to :wallet-accounts address]]]}
:wallet/tokens-loading? false)})
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 (rf/defn scan-address-success
{:events [:wallet/scan-address-success]} {:events [:wallet/scan-address-success]}
@ -67,10 +136,14 @@
:address address :address address
:public-key public-key :public-key public-key
:colorID color}] :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" [{:method "accounts_addAccount"
:params [sha3-pwd account-config] :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 " %)}]]]}))) :on-error #(log/info "failed to create account " %)}]]]})))
(rf/reg-event-fx :wallet/derive-address-and-add-account (rf/reg-event-fx :wallet/derive-address-and-add-account

View File

@ -12,10 +12,12 @@
{:height 86}) {:height 86})
(def accounts-list (def accounts-list
{:padding-horizontal 20 {:padding-top 32
:padding-top 32 :padding-bottom 12
:padding-bottom 12 :max-height 112})
:max-height 112})
(def accounts-list-container
{:padding-horizontal 20})
(def empty-container-style (def empty-container-style
{:justify-content :center {:justify-content :center

View File

@ -40,34 +40,33 @@
{:id :activity :label (i18n/label :t/activity) :accessibility-label :activity-tab}]) {:id :activity :label (i18n/label :t/activity) :accessibility-label :activity-tab}])
(defn account-cards (defn account-cards
[{:keys [accounts loading? balances profile]}] [{:keys [accounts loading? balances profile-color]}]
(let [accounts-with-balances (let [accounts-with-balances
(mapv (mapv
(fn [account] (fn [{:keys [color address] :as account}]
(assoc account (assoc account
:customization-color color
:type :empty :type :empty
:customization-color (:customization-color profile) :on-press #(rf/dispatch [:wallet/navigate-to-account address])
:on-press #(rf/dispatch [:navigate-to :wallet-accounts (:address account)])
:loading? loading? :loading? loading?
:balance (utils/prettify-balance :balance (utils/prettify-balance
(utils/get-balance-by-address balances (:address account))))) (utils/get-balance-by-address balances address))))
accounts)] accounts)]
(conj accounts-with-balances (add-account-placeholder (:customization-color profile))))) (conj accounts-with-balances (add-account-placeholder profile-color))))
(defn view (defn view
[] []
(rf/dispatch [:wallet/get-wallet-token])
(rf/dispatch [:wallet/request-collectibles (rf/dispatch [:wallet/request-collectibles
{:start-at-index 0 {:start-at-index 0
:new-request? true}]) :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 [] (fn []
(let [accounts (rf/sub [:profile/wallet-accounts]) (let [accounts (rf/sub [:wallet/accounts])
top (safe-area/get-top) loading? (rf/sub [:wallet/tokens-loading?])
loading? (rf/sub [:wallet/tokens-loading?]) balances (rf/sub [:wallet/balances])
balances (rf/sub [:wallet/balances]) profile-color (rf/sub [:profile/customization-color])
profile (rf/sub [:profile/profile]) networks (rf/sub [:wallet/network-details])]
networks (rf/sub [:wallet/network-details])]
[rn/view [rn/view
{:style {:margin-top top {:style {:margin-top top
:flex 1}} :flex 1}}
@ -79,14 +78,16 @@
{:content temp/wallet-temporary-navigation}])} {:content temp/wallet-temporary-navigation}])}
[quo/wallet-graph {:time-frame :empty}]] [quo/wallet-graph {:time-frame :empty}]]
[rn/flat-list [rn/flat-list
{:style style/accounts-list {:style style/accounts-list
:data (account-cards {:accounts accounts :content-container-style style/accounts-list-container
:loading? loading? :data (account-cards {:accounts accounts
:balances balances :loading? loading?
:profile profile}) :balances balances
:horizontal true :profile-color profile-color})
:separator [rn/view {:style {:width 12}}] :horizontal true
:render-fn quo/account-card}] :separator [rn/view {:style {:width 12}}]
:render-fn quo/account-card
:shows-horizontal-scroll-indicator false}]
[quo/tabs [quo/tabs
{:style style/tabs {:style style/tabs
:size 32 :size 32

View File

@ -1,6 +1,5 @@
(ns status-im2.subs.wallet.wallet (ns status-im2.subs.wallet.wallet
(:require [clojure.string :as string] (:require [re-frame.core :as re-frame]
[re-frame.core :as re-frame]
[status-im2.contexts.wallet.common.utils :as utils] [status-im2.contexts.wallet.common.utils :as utils]
[utils.number])) [utils.number]))
@ -21,7 +20,7 @@
(defn- calculate-balance (defn- calculate-balance
[address tokens] [address tokens]
(let [token (get tokens (keyword (string/lower-case address))) (let [token (get tokens (keyword address))
result (reduce result (reduce
(fn [acc item] (fn [acc item]
(let [total-values (* (total-per-token item) (let [total-values (* (total-per-token item)
@ -31,9 +30,17 @@
token)] token)]
result)) result))
(re-frame/reg-sub
:wallet/accounts
:<- [:wallet]
:-> #(->> %
:accounts
vals
(sort-by :position)))
(re-frame/reg-sub (re-frame/reg-sub
:wallet/balances :wallet/balances
:<- [:profile/wallet-accounts] :<- [:wallet/accounts]
:<- [:wallet/tokens] :<- [:wallet/tokens]
(fn [[accounts tokens]] (fn [[accounts tokens]]
(for [{:keys [address]} accounts] (for [{:keys [address]} accounts]
@ -41,11 +48,10 @@
:balance (calculate-balance address tokens)}))) :balance (calculate-balance address tokens)})))
(re-frame/reg-sub (re-frame/reg-sub
:wallet/account :wallet/current-viewing-account
:<- [:profile/wallet-accounts] :<- [:wallet]
:<- [:wallet/balances] :<- [:wallet/balances]
(fn [[accounts balances] [_ account-address]] (fn [[{:keys [current-viewing-account-address] :as wallet} balances]]
(assoc (-> wallet
(utils/get-account-by-address accounts account-address) (get-in [:accounts current-viewing-account-address])
:balance (assoc :balance (utils/get-balance-by-address balances current-viewing-account-address)))))
(utils/get-balance-by-address balances account-address))))

View File

@ -1,10 +1,13 @@
(ns status-im2.subs.wallet.wallet-test (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] [re-frame.db :as rf-db]
status-im2.subs.root status-im2.subs.root
[test-helpers.unit :as h] [test-helpers.unit :as h]
[utils.re-frame :as rf])) [utils.re-frame :as rf]))
(use-fixtures :each
{:before #(reset! rf-db/app-db {})})
(def tokens (def tokens
{:0x1 [{:decimals 1 {:0x1 [{:decimals 1
:symbol "ETH" :symbol "ETH"
@ -44,40 +47,132 @@
:marketValuesPerCurrency {:USD {:price 1000}}}]}) ;; total should be 1000 :marketValuesPerCurrency {:USD {:price 1000}}}]}) ;; total should be 1000
(def accounts (def accounts
[{:address "0x1" {"0x1" {:path "m/44'/60'/0'/0/0"
:name "Main account" :emoji "😃"
:hidden false :key-uid "0x2f5ea39"
:removed false} :address "0x1"
{:address "0x2" :wallet false
:name "Secondary account" :name "Account One"
:hidden false :type :generated
:removed false}]) :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 (h/deftest-sub :wallet/balances
[sub-name] [sub-name]
(testing "returns vector of maps containing :address and :balance" (testing "returns seq of maps containing :address and :balance"
(swap! rf-db/app-db assoc (swap! rf-db/app-db #(-> %
:profile/wallet-accounts accounts (assoc-in [:wallet :accounts] accounts)
:wallet/tokens tokens) (assoc :wallet/tokens tokens)))
(is (= [{:address "0x1" (is (= `({:address "0x1"
:balance 3250} :balance 3250}
{:address "0x2" {:address "0x2"
:balance 2100}] :balance 2100})
(rf/sub [sub-name]))))) (rf/sub [sub-name])))))
(h/deftest-sub :wallet/account (h/deftest-sub :wallet/accounts
[sub-name] [sub-name]
(testing "returns current account with balance base on the account-address" (testing "returns all accounts without balance"
(swap! rf-db/app-db assoc (swap! rf-db/app-db
:profile/wallet-accounts accounts #(-> %
:wallet/tokens tokens (assoc-in [:wallet :accounts] accounts)
:wallet/balances [{:address "0x1" (assoc :wallet/tokens tokens)))
:balance 3250} (is
{:address "0x2" (= `({:path "m/44'/60'/0'/0/0"
:balance 2100}]) :emoji "😃"
(is (= {:address "0x1" :key-uid "0x2f5ea39"
:name "Main account" :address "0x1"
:hidden false :wallet false
:removed false :name "Account One"
:balance 3250} :type :generated
(rf/sub [sub-name "0x1"]))))) :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])))))

View File

@ -2376,5 +2376,10 @@
"derive-addresses": "Derive addresses", "derive-addresses": "Derive addresses",
"address-activity": "This address has activity", "address-activity": "This address has activity",
"sign transactions": "sign transactions", "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"
} }