diff --git a/src/status_im/contexts/profile/settings/view.cljs b/src/status_im/contexts/profile/settings/view.cljs index 80fa6d6fb6..73a2126b9d 100644 --- a/src/status_im/contexts/profile/settings/view.cljs +++ b/src/status_im/contexts/profile/settings/view.cljs @@ -26,7 +26,7 @@ {:list-type :settings :container-style {:padding-bottom 12} :blur? true - :data (filter show-settings-item? data)}])) + :data (doall (filter show-settings-item? data))}])) (defn scroll-handler [event scroll-y] diff --git a/src/status_im/contexts/settings/wallet/network_settings/style.cljs b/src/status_im/contexts/settings/wallet/network_settings/style.cljs new file mode 100644 index 0000000000..9193294fa9 --- /dev/null +++ b/src/status_im/contexts/settings/wallet/network_settings/style.cljs @@ -0,0 +1,25 @@ +(ns status-im.contexts.settings.wallet.network-settings.style + (:require [quo.foundations.colors :as colors])) + +(def title-container + {:padding-horizontal 20 + :margin-vertical 12}) + +(defn page-wrapper + [inset-top] + {:padding-top inset-top + :flex 1}) + +(defn settings-container + [inset-bottom] + {:flex 1 + :padding-bottom inset-bottom}) + +(def networks-container + {:flex 1}) + +(def advanced-settings-container + {:flex-shrink 0}) + +(def testnet-not-available + {:color colors/danger-60}) diff --git a/src/status_im/contexts/settings/wallet/network_settings/testnet_mode/style.cljs b/src/status_im/contexts/settings/wallet/network_settings/testnet_mode/style.cljs new file mode 100644 index 0000000000..9915f63bb2 --- /dev/null +++ b/src/status_im/contexts/settings/wallet/network_settings/testnet_mode/style.cljs @@ -0,0 +1,17 @@ +(ns status-im.contexts.settings.wallet.network-settings.testnet-mode.style) + +(def description + {:padding-top 4 + :padding-bottom 8 + :padding-horizontal 20}) + +(def info-box-container + {:padding-top 4 + :padding-bottom 12 + :padding-horizontal 20}) + +(def drawer-top + {:padding-bottom 4}) + +(def bottom-actions-container + {:flex 1}) diff --git a/src/status_im/contexts/settings/wallet/network_settings/testnet_mode/view.cljs b/src/status_im/contexts/settings/wallet/network_settings/testnet_mode/view.cljs new file mode 100644 index 0000000000..33bb888372 --- /dev/null +++ b/src/status_im/contexts/settings/wallet/network_settings/testnet_mode/view.cljs @@ -0,0 +1,62 @@ +(ns status-im.contexts.settings.wallet.network-settings.testnet-mode.view + (:require [quo.core :as quo] + [react-native.core :as rn] + [status-im.contexts.settings.wallet.network-settings.testnet-mode.style :as style] + [utils.i18n :as i18n] + [utils.re-frame :as rf])) + +(defn hide-bottom-sheet + [] + (rf/dispatch [:hide-bottom-sheet])) + +(defn logout + [] + (rf/dispatch [:logout])) + +(defn on-confirm-change + [enable?] + (hide-bottom-sheet) + (rf/dispatch [:profile.settings/profile-update :test-networks-enabled? enable? {:on-success logout}])) + +(defn testnet-mode-confirm-change-sheet + [{:keys [title description on-confirm on-cancel]}] + (let [customization-color (rf/sub [:profile/customization-color])] + [:<> + [quo/drawer-top + {:title title + :container-style style/drawer-top}] + [quo/text {:style style/description} description] + [rn/view {:style style/info-box-container} + [quo/information-box + {:type :default + :icon :i/info} + (i18n/label :t/change-testnet-mode-logout-info)]] + [quo/bottom-actions + {:container-style {:style style/bottom-actions-container} + :actions :two-actions + :button-one-label (i18n/label :t/confirm) + :button-one-props {:accessibility-label :confirm-testnet-mode-change + :on-press on-confirm + :type :primary + :customization-color customization-color} + :button-two-label (i18n/label :t/cancel) + :button-two-props {:accessibility-label :cancel-testnet-mode-change + :type :dark-grey + :on-press on-cancel}}]])) + +(defn view + [{:keys [enable?]}] + (let [on-confirm (rn/use-callback + #(on-confirm-change enable?) + [enable?])] + (if enable? + [testnet-mode-confirm-change-sheet + {:title (i18n/label :t/turn-on-testnet-mode) + :description (i18n/label :t/testnet-mode-enable-description) + :on-confirm on-confirm + :on-cancel hide-bottom-sheet}] + [testnet-mode-confirm-change-sheet + {:title (i18n/label :t/turn-off-testnet-mode) + :description (i18n/label :t/testnet-mode-disable-description) + :on-confirm on-confirm + :on-cancel hide-bottom-sheet}]))) diff --git a/src/status_im/contexts/settings/wallet/network_settings/view.cljs b/src/status_im/contexts/settings/wallet/network_settings/view.cljs new file mode 100644 index 0000000000..a8cf9b06fe --- /dev/null +++ b/src/status_im/contexts/settings/wallet/network_settings/view.cljs @@ -0,0 +1,128 @@ +(ns status-im.contexts.settings.wallet.network-settings.view + (:require [quo.core :as quo] + [quo.foundations.resources :as resources] + [quo.theme] + [react-native.core :as rn] + [react-native.safe-area :as safe-area] + [status-im.contexts.settings.wallet.network-settings.style :as style] + [status-im.contexts.settings.wallet.network-settings.testnet-mode.view :as testnet] + [utils.i18n :as i18n] + [utils.re-frame :as rf])) + +(defn navigate-back + [] + (rf/dispatch [:navigate-back])) + +(defn make-network-settings-item + [{:keys [details testnet-label testnet-mode?]}] + (let [{:keys [network-name]} details] + (cond-> {:blur? true + :title (i18n/label network-name) + :image :icon-avatar + :image-props {:icon (resources/get-network network-name) + :size :size-20}} + testnet-mode? (assoc + :label :text + :label-props testnet-label)))) + +(defn mainnet-settings + [{:keys [networks testnet-mode?]}] + [quo/category + {:key :mainnet-settings + :data [(make-network-settings-item + {:details (:mainnet networks) + :testnet-mode? testnet-mode? + :testnet-label (i18n/label :t/sepolia-active)})] + :blur? true + :list-type :settings}]) + +(defn layer-2-settings + [{:keys [networks testnet-mode?]}] + [quo/category + {:key :layer-2-settings + :label (i18n/label :t/layer-2) + :data (map make-network-settings-item + [{:details (:optimism networks) + :testnet-mode? testnet-mode? + :testnet-label [quo/text + {:style style/testnet-not-available} + (i18n/label :t/testnet-not-available)]} + {:details (:arbitrum networks) + :testnet-mode? testnet-mode? + :testnet-label (i18n/label :t/sepolia-active)}]) + :blur? true + :list-type :settings}]) + +(defn testnet-mode-setting + [{:keys [testnet-mode? on-enable on-disable]}] + (let [on-change-testnet (rn/use-callback + (fn [active?] + (if active? (on-enable) (on-disable))) + [on-enable on-disable])] + {:blur? true + :title (i18n/label :t/testnet-mode) + :action :selector + :image :icon + :image-props :i/settings + :action-props {:on-change on-change-testnet + :checked? (boolean testnet-mode?)}})) + +(defn advanced-settings + [{:keys [testnet-mode? enable-testnet disable-testnet]}] + [quo/category + {:key :advanced-settings + :label (i18n/label :t/advanced) + :data [(testnet-mode-setting {:testnet-mode? testnet-mode? + :on-enable enable-testnet + :on-disable disable-testnet})] + :blur? true + :list-type :settings}]) + +(defn on-change-testnet + [{:keys [enable? theme]}] + (rf/dispatch [:show-bottom-sheet + {:content (fn [] [testnet/view {:enable? enable?}]) + :theme theme}])) + +(defn view + [] + (let [insets (safe-area/get-insets) + theme (quo.theme/use-theme) + networks-by-name (rf/sub [:wallet/network-details-by-network-name]) + testnet-mode? (rf/sub [:profile/test-networks-enabled?]) + enable-testnet (rn/use-callback + (fn [] + (on-change-testnet {:theme theme + :enable? true})) + [theme]) + disable-testnet (rn/use-callback + (fn [] + (on-change-testnet {:theme theme + :enable? false})) + [theme])] + [quo/overlay + {:type :shell + :container-style (style/page-wrapper (:top insets))} + [quo/page-nav + {:key :header + :background :blur + :icon-name :i/arrow-left + :on-press navigate-back}] + [rn/view {:style style/title-container} + [quo/standard-title + {:title (i18n/label :t/network-settings) + :accessibility-label :network-settings-header}]] + [rn/view {:style (style/settings-container (:bottom insets))} + (when networks-by-name + [rn/view {:style style/networks-container} + [mainnet-settings + {:networks networks-by-name + :testnet-mode? testnet-mode?}] + [layer-2-settings + {:networks networks-by-name + :testnet-mode? testnet-mode?}]]) + [rn/view {:style style/advanced-settings-container} + [advanced-settings + {:testnet-mode? testnet-mode? + :enable-testnet enable-testnet + :disable-testnet disable-testnet}]]]])) diff --git a/src/status_im/contexts/settings/wallet/wallet_options/view.cljs b/src/status_im/contexts/settings/wallet/wallet_options/view.cljs index ddab2704a8..9c0792806e 100644 --- a/src/status_im/contexts/settings/wallet/wallet_options/view.cljs +++ b/src/status_im/contexts/settings/wallet/wallet_options/view.cljs @@ -14,7 +14,7 @@ [] (rf/dispatch [:open-modal :screen/settings.keypairs-and-accounts])) -(defn gen-basic-settings-options +(defn basic-settings-options [] [(when (ff/enabled? ::ff/settings.keypairs-and-accounts) {:title (i18n/label :t/keypairs-and-accounts) @@ -31,10 +31,31 @@ [quo/category {:key :basic-wallet-settings :label (i18n/label :t/keypairs-accounts-and-addresses) - :data (gen-basic-settings-options) + :data (basic-settings-options) :blur? true :list-type :settings}]) +(defn open-network-settings-modal + [] + (rf/dispatch [:open-modal :screen/settings.network-settings])) + +(defn advanced-settings-options + [] + [{:title (i18n/label :t/network-settings) + :blur? true + :on-press open-network-settings-modal + :action :arrow}]) + +(defn advanced-settings + [] + (when (ff/enabled? ::ff/settings.network-settings) + [quo/category + {:key :advanced-wallet-settings + :label (i18n/label :t/advanced) + :data (advanced-settings-options) + :blur? true + :list-type :settings}])) + (defn navigate-back [] (rf/dispatch [:navigate-back])) @@ -53,4 +74,5 @@ [quo/page-top {:title (i18n/label :t/wallet) :title-accessibility-label :wallet-settings-header}] - [basic-settings]])) + [basic-settings] + [advanced-settings]])) diff --git a/src/status_im/feature_flags.cljs b/src/status_im/feature_flags.cljs index da6be9ae2d..19d9db6bb4 100644 --- a/src/status_im/feature_flags.cljs +++ b/src/status_im/feature_flags.cljs @@ -14,6 +14,7 @@ ::settings.wallet-settings (enabled-in-env? :FLAG_WALLET_SETTINGS_ENABLED) ::settings.keypairs-and-accounts (enabled-in-env? :FLAG_WALLET_SETTINGS_KEYPAIRS_AND_ACCOUNTS_ENABLED) + ::wallet.activities (enabled-in-env? :FLAG_WALLET_ACTIVITY_ENABLED) ::wallet.assets-modal-hide (enabled-in-env? :FLAG_ASSETS_MODAL_HIDE) ::wallet.assets-modal-manage-tokens (enabled-in-env? :FLAG_ASSETS_MODAL_MANAGE_TOKENS) ::wallet.bridge-token (enabled-in-env? :FLAG_BRIDGE_TOKEN_ENABLED) diff --git a/src/status_im/navigation/screens.cljs b/src/status_im/navigation/screens.cljs index 1d9a7ac342..0c337e2700 100644 --- a/src/status_im/navigation/screens.cljs +++ b/src/status_im/navigation/screens.cljs @@ -59,6 +59,7 @@ [status-im.contexts.profile.settings.view :as settings] [status-im.contexts.settings.wallet.keypairs-and-accounts.rename.view :as keypair-rename] [status-im.contexts.settings.wallet.keypairs-and-accounts.view :as keypairs-and-accounts] + [status-im.contexts.settings.wallet.network-settings.view :as network-settings] [status-im.contexts.settings.wallet.saved-addresses.view :as saved-addresses-settings] [status-im.contexts.settings.wallet.wallet-options.view :as wallet-options] [status-im.contexts.shell.activity-center.view :as activity-center] @@ -518,6 +519,10 @@ options/dark-screen) :component keypairs-and-accounts/view} + {:name :screen/settings.network-settings + :options options/transparent-modal-screen-options + :component network-settings/view} + {:name :screen/settings-messages :options options/transparent-modal-screen-options :component settings.messages/view} diff --git a/src/status_im/subs/profile.cljs b/src/status_im/subs/profile.cljs index 24b5dcefb0..0730eb732e 100644 --- a/src/status_im/subs/profile.cljs +++ b/src/status_im/subs/profile.cljs @@ -41,7 +41,7 @@ (let [{:keys [images ens-name? customization-color] :as profile} (get profiles target-key-uid) image-name (-> images first :type) override-ring? (when ens-name? false)] - (when profile + (when (and profile port) {:config (if image-name {:type :account diff --git a/src/status_im/subs/wallet/networks.cljs b/src/status_im/subs/wallet/networks.cljs index bbda3035b4..80c2422309 100644 --- a/src/status_im/subs/wallet/networks.cljs +++ b/src/status_im/subs/wallet/networks.cljs @@ -63,6 +63,17 @@ :layer layer))) (sort-by (juxt :layer :short-name))))) +(re-frame/reg-sub + :wallet/network-details-by-network-name + :<- [:wallet/network-details] + (fn [network-details] + (when (seq network-details) + (->> network-details + (group-by :network-name) + (reduce-kv (fn [acc network-key network-group] + (assoc acc network-key (first network-group))) + {}))))) + (re-frame/reg-sub :wallet/network-details-by-chain-id :<- [:wallet/network-details] diff --git a/src/status_im/subs/wallet/networks_test.cljs b/src/status_im/subs/wallet/networks_test.cljs index 564202f283..ac5f962736 100644 --- a/src/status_im/subs/wallet/networks_test.cljs +++ b/src/status_im/subs/wallet/networks_test.cljs @@ -59,3 +59,26 @@ :chain-id 10 :layer 2}] (map #(dissoc % :source :related-chain-id) (rf/sub [sub-name])))))) + +(h/deftest-sub :wallet/network-details-by-network-name + [sub-name] + (testing "returns the prod network data that is accessible by the network name" + (swap! rf-db/app-db assoc-in [:wallet :networks] network-data) + (is + (match? + {:mainnet {:network-name :mainnet + :short-name "eth" + :chain-id 1 + :abbreviated-name "Eth." + :layer 1} + :arbitrum {:network-name :arbitrum + :short-name "arb1" + :abbreviated-name "Arb1." + :chain-id 42161 + :layer 2} + :optimism {:network-name :optimism + :short-name "opt" + :abbreviated-name "Opt." + :chain-id 10 + :layer 2}} + (rf/sub [sub-name]))))) diff --git a/translations/en.json b/translations/en.json index ea9f57a78a..9dcdd69267 100644 --- a/translations/en.json +++ b/translations/en.json @@ -2307,6 +2307,7 @@ "sync-devices-complete-sub-title": "Your devices are now in sync", "synced-with": "Synced with", "confirm-and-leave": "Confirm and leave", + "change-testnet-mode-logout-info": "You’ll be logged out of the app", "membership-requirements-not-met": "Membership requirements not met", "edit-shared-addresses": "Edit shared addresses", "leave-community-farewell": "We’ll be sad to see you go but remember, you can come back at any time! All shared addresses will be unshared.", @@ -2573,6 +2574,8 @@ "keypair-name-input-placeholder": "Collectibles account, Old vault....", "key-pair-name-updated": "Key pair name updated", "goerli-testnet-toggle-confirmation": "Are you sure you want to toggle Goerli? This will log you out and you will have to login again.", + "sepolia-active": "Sepolia active", + "testnet-not-available": "Testnet not available", "bridged-to": "Bridged to {{network}}", "slide-to-bridge": "Slide to bridge", "provider-is-down": "The provider for the following chain(s) is down: {{chains}}", @@ -2599,6 +2602,11 @@ "key-name-error-special-char": "Special characters are not allowed", "key-name-error-too-short": "Key pair name must be at least {{count}} characters", "display": "Display", + "testnet-mode": "Testnet mode", + "turn-on-testnet-mode": "Turn on testnet mode", + "turn-off-testnet-mode": "Turn off testnet mode", + "testnet-mode-enable-description": "In this mode, all blockchain data displayed will come from testnets and all blockchain interactions will be with testnets.\nTestnet mode switches the entire app to using testnets only. Please switch this mode on only if you know exactly why you need to use it.", + "testnet-mode-disable-description": "Are you sure you want to turn off Testnet mode? All future transactions will be performed on live networks with real funds.", "testnet-mode-enabled": "Testnet mode enabled", "online-community-member": "Online", "offline-community-member": "Offline",