diff --git a/src/quo/components/drawers/drawer_top/component_spec.cljs b/src/quo/components/drawers/drawer_top/component_spec.cljs index 10385fe11a..3a2793a5a3 100644 --- a/src/quo/components/drawers/drawer_top/component_spec.cljs +++ b/src/quo/components/drawers/drawer_top/component_spec.cljs @@ -80,10 +80,10 @@ (h/is-truthy (h/get-by-text "0x62b...0a5")) (h/is-truthy (h/get-by-label-text :account-avatar))) - (h/test "component renders in keypair type when keycard? is false" + (h/test "component renders keypair type with default label" (h/render-with-theme-provider [quo/drawer-top {:title "Title" - :keycard? false + :stored nil :icon-avatar :i/placeholder :type :keypair}] theme) @@ -91,10 +91,21 @@ (-> (h/expect (h/get-by-translation-text :t/on-device)) (.toBeTruthy))) - (h/test "component renders in keypair type when keycard? is true" + (h/test "component renders keypair type when stored on device" (h/render-with-theme-provider [quo/drawer-top {:title "Title" - :keycard? true + :stored :on-device + :icon-avatar :i/placeholder + :type :keypair}] + theme) + (h/is-truthy (h/get-by-text "Title")) + (-> (h/expect (h/get-by-translation-text :t/on-device)) + (.toBeTruthy))) + + (h/test "component renders keypair type when stored on keycard" + (h/render-with-theme-provider [quo/drawer-top + {:title "Title" + :stored :on-keycard :icon-avatar :i/placeholder :type :keypair}] theme) @@ -102,6 +113,17 @@ (-> (h/expect (h/get-by-translation-text :t/on-keycard)) (.toBeTruthy))) + (h/test "component renders keypair type when considered missing" + (h/render-with-theme-provider [quo/drawer-top + {:title "Title" + :stored :missing + :icon-avatar :i/placeholder + :type :keypair}] + theme) + (h/is-truthy (h/get-by-text "Title")) + (-> (h/expect (h/get-by-translation-text :t/import-to-use-derived-accounts)) + (.toBeTruthy))) + (h/test "component renders in default-keypair type" (h/render-with-theme-provider [quo/drawer-top {:title "Title" diff --git a/src/quo/components/drawers/drawer_top/view.cljs b/src/quo/components/drawers/drawer_top/view.cljs index 5ecc2c3fcc..61a21df6af 100644 --- a/src/quo/components/drawers/drawer_top/view.cljs +++ b/src/quo/components/drawers/drawer_top/view.cljs @@ -37,16 +37,18 @@ nil)) (defn- keypair-subtitle - [{:keys [theme blur? keycard?]}] + [{:keys [theme blur? stored]}] [rn/view {:style style/row} [text/text {:size :paragraph-2 :weight :regular :style (style/description theme blur?)} - (if keycard? - (i18n/label :t/on-keycard) + (case stored + :on-device (i18n/label :t/on-device) + :on-keycard (i18n/label :t/on-keycard) + :missing (i18n/label :t/import-to-use-derived-accounts) (i18n/label :t/on-device))] - (when keycard? + (when (= stored :on-keycard) [icons/icon :i/keycard-card {:color (colors/theme-colors colors/neutral-50 colors/neutral-40 theme) @@ -103,14 +105,14 @@ description]) (defn- subtitle - [{:keys [type theme blur? keycard? networks description community-name community-logo + [{:keys [type theme blur? stored networks description community-name community-logo context-tag-type account-name emoji customization-color full-name profile-picture]}] (cond (= :keypair type) [keypair-subtitle - {:theme theme - :blur? blur? - :keycard? keycard?}] + {:theme theme + :blur? blur? + :stored stored}] (= :account type) [account-subtitle @@ -195,7 +197,7 @@ (defn view [{:keys [title title-icon type description blur? community-name community-logo button-icon account-name emoji context-tag-type button-type container-style - on-button-press on-button-long-press profile-picture keycard? networks label full-name + on-button-press on-button-long-press profile-picture stored networks label full-name button-disabled? account-avatar-emoji account-avatar-type customization-color icon-avatar]}] (let [theme (quo.theme/use-theme)] [rn/view {:style (merge style/container container-style)} @@ -220,7 +222,7 @@ {:type type :theme theme :blur? blur? - :keycard? keycard? + :stored stored :networks networks :description description :community-name community-name diff --git a/src/quo/components/list_items/missing_keypair/component_spec.cljs b/src/quo/components/list_items/missing_keypair/component_spec.cljs new file mode 100644 index 0000000000..f209816f3f --- /dev/null +++ b/src/quo/components/list_items/missing_keypair/component_spec.cljs @@ -0,0 +1,30 @@ +(ns quo.components.list-items.missing-keypair.component-spec + (:require + [quo.components.list-items.missing-keypair.view :as missing-keypair] + [test-helpers.component :as h])) + +(def keypair-data + {:accounts [] + :name "Key Pair Name"}) + +(def props + {:keypair keypair-data + :blur? true + :on-options-press (fn [])}) + +(h/describe "List items: missing keypair item" + (h/test "Test item container renders" + (h/render-with-theme-provider [missing-keypair/view props]) + (h/is-truthy (h/get-by-label-text :missing-keypair-item))) + (h/test "Test keypair icon renders" + (h/render-with-theme-provider [missing-keypair/view props]) + (h/is-truthy (h/get-by-label-text :icon))) + (h/test "Test name renders" + (h/render-with-theme-provider [missing-keypair/view props]) + (h/is-truthy (h/get-by-label-text :name))) + (h/test "Test preview-list renders" + (h/render-with-theme-provider [missing-keypair/view props]) + (h/is-truthy (h/get-by-label-text :preview-list))) + (h/test "Test options button renders" + (h/render-with-theme-provider [missing-keypair/view props]) + (h/is-truthy (h/get-by-label-text :options-button)))) diff --git a/src/quo/components/list_items/missing_keypair/schema.cljs b/src/quo/components/list_items/missing_keypair/schema.cljs new file mode 100644 index 0000000000..bc86c4c063 --- /dev/null +++ b/src/quo/components/list_items/missing_keypair/schema.cljs @@ -0,0 +1,25 @@ +(ns quo.components.list-items.missing-keypair.schema) + +(def ^:private ?base + [:map + [:blur? {:optional true} [:maybe :boolean]] + [:keypair + [:map + [:key-uid :string] + [:name :string] + [:accounts + [:sequential + [:map {:closed true} + [:type [:enum :default]] + [:emoji :string] + [:customization-color {:optional true} [:maybe :schema.common/customization-color]]]]]]]]) + +(def ^:private ?on-option-press + [:map + [:on-options-press {:optional true} [:maybe fn?]]]) + +(def ?schema + [:=> + [:cat + [:merge ?base ?on-option-press]] + :any]) diff --git a/src/quo/components/list_items/missing_keypair/style.cljs b/src/quo/components/list_items/missing_keypair/style.cljs new file mode 100644 index 0000000000..edc34857c9 --- /dev/null +++ b/src/quo/components/list_items/missing_keypair/style.cljs @@ -0,0 +1,46 @@ +(ns quo.components.list-items.missing-keypair.style + (:require + [quo.foundations.colors :as colors])) + +(defn container + [{:keys [blur? theme]}] + {:flex-direction :row + :align-items :center + :flex 1 + :padding-right 12 + :padding-left 8 + :padding-vertical 8 + :border-radius 12 + :border-width (if blur? 0 1) + :border-color (colors/theme-colors colors/neutral-10 + colors/neutral-80 + theme) + :background-color (if blur? + colors/white-opa-5 + (colors/theme-colors colors/neutral-2_5 + colors/neutral-80-opa-40 + theme))}) + +(defn icon-container + [{:keys [blur? theme]}] + {:border-radius 32 + :border-width 1 + :border-color (if blur? + colors/white-opa-5 + (colors/theme-colors colors/neutral-20 + colors/neutral-80 + theme))}) + +(def name-container + {:flex 1 + :padding-right 16 + :padding-left 8}) + +(def preview-list-container + {:padding-right 16}) + +(defn options-icon-color + [{:keys [theme blur?]}] + (if blur? + colors/white-opa-70 + (colors/theme-colors colors/neutral-50 colors/neutral-40 theme))) diff --git a/src/quo/components/list_items/missing_keypair/view.cljs b/src/quo/components/list_items/missing_keypair/view.cljs new file mode 100644 index 0000000000..8f03f9f4dc --- /dev/null +++ b/src/quo/components/list_items/missing_keypair/view.cljs @@ -0,0 +1,56 @@ +(ns quo.components.list-items.missing-keypair.view + (:require + [quo.components.avatars.icon-avatar :as icon-avatar] + [quo.components.icon :as icon] + [quo.components.list-items.missing-keypair.schema :as component-schema] + [quo.components.list-items.missing-keypair.style :as style] + [quo.components.list-items.preview-list.view :as preview-list] + [quo.components.markdown.text :as text] + [quo.theme] + [react-native.core :as rn] + [schema.core :as schema])) + +(defn- internal-view + [{{:keys [accounts name]} :keypair + :keys [keypair blur? on-options-press]}] + (let [theme (quo.theme/use-theme) + on-keypair-options-press (rn/use-callback + (fn [event] + (on-options-press event keypair)) + [keypair on-options-press])] + [rn/view + {:style (style/container {:theme theme + :blur? blur?}) + :accessibility-label :missing-keypair-item} + [rn/view + {:style (style/icon-container {:theme theme + :blur? blur?}) + :accessibility-label :icon} + [icon-avatar/icon-avatar + {:size :size-32 + :icon :i/seed + :color :neutral + :border? false}]] + [rn/view + {:style style/name-container + :accessibility-label :name} + [text/text + {:weight :semi-bold} + name]] + [rn/view + {:accessibility-label :preview-list} + [preview-list/view + {:blur? blur? + :type :accounts + :size :size-24 + :number (count accounts) + :container-style style/preview-list-container} + accounts]] + [rn/pressable {:on-press on-keypair-options-press} + [icon/icon :i/options + {:color (style/options-icon-color + {:theme theme + :blur? blur?}) + :accessibility-label :options-button}]]])) + +(def view (schema/instrument #'internal-view component-schema/?schema)) diff --git a/src/quo/components/text_combinations/standard_title/style.cljs b/src/quo/components/text_combinations/standard_title/style.cljs index f4b6364fc8..2f4f9c7a52 100644 --- a/src/quo/components/text_combinations/standard_title/style.cljs +++ b/src/quo/components/text_combinations/standard_title/style.cljs @@ -3,7 +3,6 @@ (def container {:flex-direction :row - :flex 1 :justify-content :space-between}) (def right-counter diff --git a/src/quo/components/text_combinations/standard_title/view.cljs b/src/quo/components/text_combinations/standard_title/view.cljs index 0f6c2c13c7..cbc049d932 100644 --- a/src/quo/components/text_combinations/standard_title/view.cljs +++ b/src/quo/components/text_combinations/standard_title/view.cljs @@ -55,8 +55,8 @@ :icon-color (style/right-tag-icon-color blur? theme)}])) (defn view - [{:keys [title right accessibility-label] :as props}] - [rn/view {:style style/container} + [{:keys [title right accessibility-label container-style] :as props}] + [rn/view {:style (merge style/container container-style)} [text/text {:size :heading-1 :weight :semi-bold diff --git a/src/quo/components/wallet/missing_keypairs/component_spec.cljs b/src/quo/components/wallet/missing_keypairs/component_spec.cljs new file mode 100644 index 0000000000..775aadcd75 --- /dev/null +++ b/src/quo/components/wallet/missing_keypairs/component_spec.cljs @@ -0,0 +1,24 @@ +(ns quo.components.wallet.missing-keypairs.component-spec + (:require + [quo.components.wallet.missing-keypairs.view :as missing-keypairs] + [test-helpers.component :as h])) + +(def ^:private theme :dark) + +(def props + {:blur? true + :container-style {} + :on-options-press (fn []) + :keypairs [{:type :seed + :name name + :key-uid "123" + :accounts [{:customization-color :turquoise + :emoji "\uD83C\uDFB2" + :type :default}]}]}) + +(h/describe "Wallet: Missing key pairs" + (h/test "Missing key pair title renders" + (h/render-with-theme-provider [missing-keypairs/view props] + theme) + (h/is-truthy (h/get-by-label-text :title)) + (h/is-truthy (h/get-by-label-text :t/import-to-use-derived-accounts)))) diff --git a/src/quo/components/wallet/missing_keypairs/style.cljs b/src/quo/components/wallet/missing_keypairs/style.cljs new file mode 100644 index 0000000000..10236e8a72 --- /dev/null +++ b/src/quo/components/wallet/missing_keypairs/style.cljs @@ -0,0 +1,28 @@ +(ns quo.components.wallet.missing-keypairs.style + (:require + [quo.foundations.colors :as colors])) + +(def container + {:border-width 1 + :border-radius 16 + :padding 8 + :border-color colors/warning-50-opa-20 + :background-color colors/warning-50-opa-5}) + +(def title-icon-container + {:top 1}) + +(def title-info-container + {:padding-left 8}) + +(def title-container + {:align-items :flex-start + :flex-direction :row + :padding-left 4 + :padding-bottom 12}) + +(defn subtitle + [blur? theme] + {:color (if blur? + colors/white-opa-40 + (colors/theme-colors colors/neutral-50 colors/neutral-40 theme))}) diff --git a/src/quo/components/wallet/missing_keypairs/view.cljs b/src/quo/components/wallet/missing_keypairs/view.cljs new file mode 100644 index 0000000000..bbfa655f11 --- /dev/null +++ b/src/quo/components/wallet/missing_keypairs/view.cljs @@ -0,0 +1,53 @@ +(ns quo.components.wallet.missing-keypairs.view + (:require + [quo.components.icon :as icon] + [quo.components.list-items.missing-keypair.view :as missing-keypair] + [quo.components.markdown.text :as text] + [quo.components.wallet.missing-keypairs.style :as style] + [quo.foundations.colors :as colors] + [quo.theme] + [react-native.core :as rn] + [utils.i18n :as i18n])) + +(defn title-view + [{:keys [keypairs blur?]}] + (let [theme (quo.theme/use-theme)] + [rn/view + {:accessibility-label :title + :style style/title-container} + [rn/view + {:style style/title-icon-container} + [icon/icon :i/info + {:size 20 + :color colors/warning-60}]] + [rn/view + {:style style/title-info-container} + [text/text + {:weight :medium + :style {:color colors/warning-60}} + (i18n/label :t/amount-missing-keypairs + {:amount (str (count keypairs))})] + [text/text + {:size :paragraph-2 + :style (style/subtitle blur? theme)} + (i18n/label :t/import-to-use-derived-accounts)]]])) + +(defn- missing-keypair-item + [keypair _index _separators + {:keys [blur? on-options-press]}] + [missing-keypair/view + {:keypair keypair + :blur? blur? + :on-options-press on-options-press}]) + +(defn view + [{:keys [blur? keypairs container-style on-options-press] :as props}] + [rn/view + {:style (merge style/container container-style)} + [title-view props] + [rn/flat-list + {:data keypairs + :render-fn missing-keypair-item + :render-data {:blur? blur? + :on-options-press on-options-press} + :separator [rn/view {:style {:height 8}}]}]]) diff --git a/src/quo/core.cljs b/src/quo/core.cljs index ae01284e96..b6381d2db1 100644 --- a/src/quo/core.cljs +++ b/src/quo/core.cljs @@ -85,6 +85,7 @@ quo.components.list-items.community.view quo.components.list-items.dapp.view quo.components.list-items.menu-item + quo.components.list-items.missing-keypair.view quo.components.list-items.network-list.view quo.components.list-items.preview-list.view quo.components.list-items.quiz-item.view @@ -169,6 +170,7 @@ quo.components.wallet.approval-label.view quo.components.wallet.confirmation-progress.view quo.components.wallet.keypair.view + quo.components.wallet.missing-keypairs.view quo.components.wallet.network-amount.view quo.components.wallet.network-bridge.view quo.components.wallet.network-link.view @@ -320,6 +322,7 @@ (def community-list quo.components.list-items.community.view/view) (def dapp quo.components.list-items.dapp.view/view) (def menu-item quo.components.list-items.menu-item/menu-item) +(def missing-keypair quo.components.list-items.missing-keypair.view/view) (def network-list quo.components.list-items.network-list.view/view) (def preview-list quo.components.list-items.preview-list.view/view) (def quiz-item quo.components.list-items.quiz-item.view/view) @@ -440,6 +443,7 @@ (def approval-label quo.components.wallet.approval-label.view/view) (def confirmation-progress quo.components.wallet.confirmation-progress.view/view) (def keypair quo.components.wallet.keypair.view/view) +(def missing-keypairs quo.components.wallet.missing-keypairs.view/view) (def network-amount quo.components.wallet.network-amount.view/view) (def network-bridge quo.components.wallet.network-bridge.view/view) (def network-routing quo.components.wallet.network-routing.view/view) diff --git a/src/status_im/contexts/preview/quo/drawers/drawer_top.cljs b/src/status_im/contexts/preview/quo/drawers/drawer_top.cljs index e8911eacb7..63010c4feb 100644 --- a/src/status_im/contexts/preview/quo/drawers/drawer_top.cljs +++ b/src/status_im/contexts/preview/quo/drawers/drawer_top.cljs @@ -25,8 +25,11 @@ :value "null"}]} {:key :blur? :type :boolean} - {:key :keycard? - :type :boolean} + {:key :stored + :type :select + :options [{:key :on-device} + {:key :on-keycard} + {:key :missing}]} {:key :title :type :text} {:key :description @@ -44,7 +47,7 @@ :title "Title" :type :default :label "Drawer label" - :keycard? true + :stored :on-device :networks [{:network-name :ethereum :short-name "eth"}] :description "0x62b...0a5" :button-icon :i/placeholder diff --git a/src/status_im/contexts/preview/quo/list_items/missing_keypair.cljs b/src/status_im/contexts/preview/quo/list_items/missing_keypair.cljs new file mode 100644 index 0000000000..2ecd787a02 --- /dev/null +++ b/src/status_im/contexts/preview/quo/list_items/missing_keypair.cljs @@ -0,0 +1,32 @@ +(ns status-im.contexts.preview.quo.list-items.missing-keypair + (:require + [quo.core :as quo] + [react-native.core :as rn] + [reagent.core :as reagent] + [status-im.contexts.preview.quo.preview :as preview])) + +(def descriptor + [{:key :blur? + :type :boolean}]) + +(def component-props + {:blur? false + :keypair {:type :seed + :key-uid "0x01" + :name "Trip to Vegas" + :accounts [{:type :default + :emoji "🍑" + :customization-color :purple}]}}) + +(defn view + [] + (let [state (reagent/atom component-props)] + (fn [] + [preview/preview-container + {:state state + :descriptor descriptor + :blur? (:blur? @state) + :show-blur-background? true + :blur-dark-only? true} + [rn/view {:style {:align-items :flex-start}} + [quo/missing-keypair @state]]]))) diff --git a/src/status_im/contexts/preview/quo/main.cljs b/src/status_im/contexts/preview/quo/main.cljs index 26c51569a7..8a022b845e 100644 --- a/src/status_im/contexts/preview/quo/main.cljs +++ b/src/status_im/contexts/preview/quo/main.cljs @@ -103,6 +103,7 @@ [status-im.contexts.preview.quo.list-items.address :as address] [status-im.contexts.preview.quo.list-items.channel :as channel] [status-im.contexts.preview.quo.list-items.dapp :as dapp] + [status-im.contexts.preview.quo.list-items.missing-keypair :as missing-keypair] [status-im.contexts.preview.quo.list-items.network-list :as network-list] [status-im.contexts.preview.quo.list-items.preview-lists :as preview-lists] [status-im.contexts.preview.quo.list-items.quiz-item :as quiz-item] @@ -196,6 +197,7 @@ [status-im.contexts.preview.quo.wallet.confirmation-progress :as confirmation-progress] [status-im.contexts.preview.quo.wallet.keypair :as keypair] + [status-im.contexts.preview.quo.wallet.missing-keypairs :as missing-keypairs] [status-im.contexts.preview.quo.wallet.network-amount :as network-amount] [status-im.contexts.preview.quo.wallet.network-bridge :as network-bridge] [status-im.contexts.preview.quo.wallet.network-link :as network-link] @@ -380,6 +382,8 @@ :component community-list-item/view} {:name :dapp :component dapp/preview} + {:name :missing-keypair + :component missing-keypair/view} {:name :network-list :component network-list/view} {:name :preview-lists @@ -535,6 +539,8 @@ {:name :confirmation-progress :component confirmation-progress/view} {:name :keypair :component keypair/view} + {:name :missing-keypairs + :component missing-keypairs/view} {:name :network-amount :component network-amount/view} {:name :network-bridge :component network-bridge/view} {:name :network-link :component network-link/view} diff --git a/src/status_im/contexts/preview/quo/wallet/missing_keypairs.cljs b/src/status_im/contexts/preview/quo/wallet/missing_keypairs.cljs new file mode 100644 index 0000000000..22e21f3f6a --- /dev/null +++ b/src/status_im/contexts/preview/quo/wallet/missing_keypairs.cljs @@ -0,0 +1,57 @@ +(ns status-im.contexts.preview.quo.wallet.missing-keypairs + (:require + [quo.core :as quo] + [react-native.core :as rn] + [reagent.core :as reagent] + [status-im.contexts.preview.quo.preview :as preview])) + +(def keypair + {:key-uid "0x01" + :type "seed" + :name "My Key Pair" + :blur? false}) + +(def accounts + [{:customization-color :turquoise + :emoji "\uD83C\uDFB2" + :type :default} + {:customization-color :purple + :emoji "\uD83C\uDF7F" + :type :default} + {:customization-color :army + :emoji "\uD83D\uDCC8" + :type :default} + {:customization-color :orange + :emoji "\uD83C\uDFF0" + :type :default} + {:customization-color :yellow + :emoji "\uD83C\uDFDD️" + :type :default}]) + +(def descriptor + [{:key :blur? :type :boolean}]) + +(def component-props + {:blur? false + :keypairs [{:name (:name keypair) + :key-uid (:key-uid keypair) + :type (keyword (:type keypair)) + :accounts accounts}]}) + +(defn view + [] + (let [state (reagent/atom {:blur? false})] + (fn [] + [preview/preview-container + {:state state + :descriptor descriptor + :blur? (:blur? @state) + :show-blur-background? true + :blur-dark-only? true + :blur-height 400 + :component-container-style {:padding-vertical 30 + :flex-direction :row + :justify-content :center}} + [rn/view {:style {:flex 1}} + [quo/missing-keypairs + (merge component-props @state)]]]))) diff --git a/src/status_im/contexts/settings/wallet/keypairs_and_accounts/style.cljs b/src/status_im/contexts/settings/wallet/keypairs_and_accounts/style.cljs index 4ab69d0c5f..8396d0e9ae 100644 --- a/src/status_im/contexts/settings/wallet/keypairs_and_accounts/style.cljs +++ b/src/status_im/contexts/settings/wallet/keypairs_and_accounts/style.cljs @@ -16,3 +16,10 @@ (def keypair-container-style {:margin-horizontal 20 :margin-vertical 8}) + +(def missing-keypairs-container-style + {:margin-horizontal 20 + :margin-vertical 8}) + +(def settings-keypairs-container + {:flex 1}) diff --git a/src/status_im/contexts/settings/wallet/keypairs_and_accounts/view.cljs b/src/status_im/contexts/settings/wallet/keypairs_and_accounts/view.cljs index 070c53f679..0a13a4b5e3 100644 --- a/src/status_im/contexts/settings/wallet/keypairs_and_accounts/view.cljs +++ b/src/status_im/contexts/settings/wallet/keypairs_and_accounts/view.cljs @@ -20,6 +20,21 @@ {:content (fn [] [actions/view props keypair]) :theme theme}])) +(defn options-drawer-props + [{{:keys [name]} :keypair + :keys [type stored theme shortened-key customization-color profile-picture]}] + (cond-> {:theme theme + :type type + :blur? true + :title name + :stored stored} + (= type :default-keypair) + (assoc :description shortened-key + :customization-color customization-color + :profile-picture profile-picture) + (= type :keypair) + (assoc :icon-avatar :i/seed))) + (defn- keypair [{keypair-type :type :keys [accounts name] @@ -33,22 +48,19 @@ on-press (rn/use-callback (fn [] (on-options-press - (cond-> {:theme theme - :blur? true - :title name} - default-keypair? - (assoc :type :default-keypair - :description shortened-key - :customization-color customization-color - :profile-picture profile-picture) - (not default-keypair?) - (assoc :type :keypair - :icon-avatar :i/seed)) + (options-drawer-props + {:theme theme + :keypair item + :type (if default-keypair? :default-keypair :keypair) + :stored :on-device + :shortened-key shortened-key + :customization-color customization-color + :profile-picture profile-picture}) item)) - [customization-color default-keypair? name + [customization-color default-keypair? item profile-picture shortened-key theme])] [quo/keypair - {:blur? false + {:blur? true :status-indicator false :stored :on-device :action :options @@ -61,13 +73,27 @@ :details {:full-name name :address shortened-key}}])) +(defn on-missing-keypair-options-press + [_event keypair-data] + (rf/dispatch [:show-bottom-sheet + {:theme :dark + :content (fn [] [actions/view + (options-drawer-props + {:theme :dark + :type :keypair + :stored :missing + :blur? true + :keypair keypair-data}) + keypair-data])}])) + (defn view [] - (let [insets (safe-area/get-insets) - compressed-key (rf/sub [:profile/compressed-key]) - profile-picture (rf/sub [:profile/image]) - customization-color (rf/sub [:profile/customization-color]) - quo-keypairs-accounts (rf/sub [:wallet/settings-keypairs-accounts])] + (let [insets (safe-area/get-insets) + compressed-key (rf/sub [:profile/compressed-key]) + profile-picture (rf/sub [:profile/image]) + customization-color (rf/sub [:profile/customization-color]) + {missing-keypairs :missing + operable-keypairs :operable} (rf/sub [:wallet/settings-keypairs-accounts])] [quo/overlay {:type :shell :container-style (style/page-wrapper (:top insets))} @@ -81,9 +107,15 @@ {:title (i18n/label :t/keypairs-and-accounts) :accessibility-label :keypairs-and-accounts-header :customization-color customization-color}]] - [rn/view {:style {:flex 1}} + [rn/view {:style style/settings-keypairs-container} + (when (seq missing-keypairs) + [quo/missing-keypairs + {:blur? true + :keypairs missing-keypairs + :container-style style/missing-keypairs-container-style + :on-options-press on-missing-keypair-options-press}]) [rn/flat-list - {:data quo-keypairs-accounts + {:data operable-keypairs :render-fn keypair :render-data {:profile-picture profile-picture :compressed-key compressed-key diff --git a/src/status_im/contexts/wallet/data_store.cljs b/src/status_im/contexts/wallet/data_store.cljs index f4f768e458..f52c5264d8 100644 --- a/src/status_im/contexts/wallet/data_store.cljs +++ b/src/status_im/contexts/wallet/data_store.cljs @@ -42,6 +42,7 @@ (update :prod-preferred-chain-ids chain-ids-string->set) (update :test-preferred-chain-ids chain-ids-string->set) (update :type keyword) + (update :operable keyword) (update :color #(if (seq %) (keyword %) constants/account-default-customization-color)) (update :emoji sanitize-emoji) (assoc :default-account? (:wallet account)) diff --git a/src/status_im/contexts/wallet/send/input_amount/component_spec.cljs b/src/status_im/contexts/wallet/send/input_amount/component_spec.cljs index b6982410c6..4cc77f5811 100644 --- a/src/status_im/contexts/wallet/send/input_amount/component_spec.cljs +++ b/src/status_im/contexts/wallet/send/input_amount/component_spec.cljs @@ -36,7 +36,7 @@ :position 1 :clock 1698945829328 :created-at 1698928839000 - :operable "fully" + :operable :fully :mixedcase-address "0x7bcDfc75c431" :public-key "0x04371e2d9d66b82f056bc128064" :removed false} diff --git a/src/status_im/contexts/wallet/send/transaction_progress/view.cljs b/src/status_im/contexts/wallet/send/transaction_progress/view.cljs index 4f08455a6d..e4aaa0c2bc 100644 --- a/src/status_im/contexts/wallet/send/transaction_progress/view.cljs +++ b/src/status_im/contexts/wallet/send/transaction_progress/view.cljs @@ -71,4 +71,5 @@ {:source (resources/get-image :transaction-progress) :style {:margin-bottom 12}}] [quo/standard-title - {:title (titles (combined-status-overview transaction-details))}]]])))) + {:container-style {:flex 1} + :title (titles (combined-status-overview transaction-details))}]]])))) diff --git a/src/status_im/navigation/screens.cljs b/src/status_im/navigation/screens.cljs index 2885067b95..3438d62216 100644 --- a/src/status_im/navigation/screens.cljs +++ b/src/status_im/navigation/screens.cljs @@ -520,9 +520,7 @@ :component saved-addresses-settings/view} {:name :screen/settings.keypairs-and-accounts - :options (merge - options/transparent-modal-screen-options - options/dark-screen) + :options options/transparent-modal-screen-options :component keypairs-and-accounts/view} {:name :screen/settings.network-settings diff --git a/src/status_im/subs/wallet/wallet.cljs b/src/status_im/subs/wallet/wallet.cljs index 43b713820b..c002f25a5b 100644 --- a/src/status_im/subs/wallet/wallet.cljs +++ b/src/status_im/subs/wallet/wallet.cljs @@ -195,17 +195,40 @@ :state :default :action :none}))))) +(defn- format-settings-missing-keypair-accounts + [accounts] + (->> accounts + (map (fn [{:keys [customization-color emoji]}] + {:customization-color customization-color + :emoji emoji + :type :default})))) + (rf/reg-sub :wallet/settings-keypairs-accounts :<- [:wallet/keypairs] - (fn [keypairs [_ format-options]] - (->> keypairs - (map (fn [{:keys [accounts name type key-uid]}] - {:type (keyword type) - :name name - :key-uid key-uid - :accounts (format-settings-keypair-accounts accounts format-options)}))))) - + :<- [:wallet/accounts] + (fn [[keypairs accounts] [_ format-options]] + (let [grouped-accounts (->> accounts + (map #(select-keys % [:operable :key-uid])) + (group-by :operable)) + operable-key-pair-ids (->> (map :key-uid (:fully grouped-accounts)) + (into #{})) + missing-key-pair-ids (->> (map :key-uid (:no grouped-accounts)) + (into #{}))] + {:operable (->> keypairs + (filter #(contains? operable-key-pair-ids (:key-uid %))) + (map (fn [{:keys [accounts name type key-uid]}] + {:type (keyword type) + :name name + :key-uid key-uid + :accounts (format-settings-keypair-accounts accounts format-options)}))) + :missing (->> keypairs + (filter #(contains? missing-key-pair-ids (:key-uid %))) + (map (fn [{:keys [accounts name type key-uid]}] + {:type (keyword type) + :name name + :key-uid key-uid + :accounts (format-settings-missing-keypair-accounts accounts)})))}))) (rf/reg-sub :wallet/derivation-path-state :<- [:wallet/create-account] diff --git a/src/status_im/subs/wallet/wallet_test.cljs b/src/status_im/subs/wallet/wallet_test.cljs index 49fb6b5656..2349a67cbd 100644 --- a/src/status_im/subs/wallet/wallet_test.cljs +++ b/src/status_im/subs/wallet/wallet_test.cljs @@ -104,7 +104,7 @@ :position 0 :clock 1698945829328 :created-at 1698928839000 - :operable "fully" + :operable :fully :mixedcase-address "0x7bcDfc75c431" :public-key "0x04371e2d9d66b82f056bc128064" :removed false @@ -125,7 +125,7 @@ :position 1 :clock 1698945829328 :created-at 1698928839000 - :operable "fully" + :operable :fully :mixedcase-address "0x7bcDfc75c431" :public-key "0x04371e2d9d66b82f056bc128064" :removed false @@ -146,7 +146,7 @@ :position 2 :clock 1698945829328 :created-at 1698928839000 - :operable "fully" + :operable :fully :mixedcase-address "0x7bcDfc75c431" :public-key "0x" :removed false @@ -221,7 +221,7 @@ :position 0 :clock 1698945829328 :created-at 1698928839000 - :operable "fully" + :operable :fully :mixedcase-address "0x7bcDfc75c431" :public-key "0x04371e2d9d66b82f056bc128064" :removed false @@ -243,7 +243,7 @@ :position 1 :clock 1698945829328 :created-at 1698928839000 - :operable "fully" + :operable :fully :mixedcase-address "0x7bcDfc75c431" :public-key "0x04371e2d9d66b82f056bc128064" :removed false @@ -265,7 +265,7 @@ :position 2 :clock 1698945829328 :created-at 1698928839000 - :operable "fully" + :operable :fully :mixedcase-address "0x7bcDfc75c431" :public-key "0x" :removed false @@ -308,7 +308,7 @@ :position 0 :clock 1698945829328 :created-at 1698928839000 - :operable "fully" + :operable :fully :mixedcase-address "0x7bcDfc75c431" :public-key "0x04371e2d9d66b82f056bc128064" :removed false @@ -369,7 +369,7 @@ :position 0 :clock 1698945829328 :created-at 1698928839000 - :operable "fully" + :operable :fully :mixedcase-address "0x7bcDfc75c431" :public-key "0x04371e2d9d66b82f056bc128064" :removed false @@ -391,7 +391,7 @@ :position 2 :clock 1698945829328 :created-at 1698928839000 - :operable "fully" + :operable :fully :mixedcase-address "0x7bcDfc75c431" :public-key "0x" :removed false @@ -426,7 +426,7 @@ :position 0 :clock 1698945829328 :created-at 1698928839000 - :operable "fully" + :operable :fully :mixedcase-address "0x7bcDfc75c431" :public-key "0x04371e2d9d66b82f056bc128064" :removed false @@ -449,7 +449,7 @@ :position 1 :clock 1698945829328 :created-at 1698928839000 - :operable "fully" + :operable :fully :mixedcase-address "0x7bcDfc75c431" :public-key "0x04371e2d9d66b82f056bc128064" :removed false @@ -568,7 +568,7 @@ :hidden false :removed false}) -(def wallet-account +(def operable-wallet-account {:path "m/44'/60'/0'/0/0" :emoji "🤡" :key-uid "abc" @@ -579,83 +579,147 @@ :chat false :customization-color :primary :hidden false + :operable :fully :removed false}) -(def keypairs-accounts +(def inoperable-wallet-account + {:path "m/44'/60'/0'/0/0" + :emoji "🧠" + :key-uid "def" + :address "address-3" + :wallet true + :name "My Other Account" + :type "generated" + :chat false + :customization-color :primary + :hidden false + :operable :no + :removed false}) + +(def default-keypair-accounts {:key-uid "abc" :name "My Profile" :type "profile" :accounts []}) +(def seed-phrase-keypair-accounts + {:key-uid "def" + :name "My Key Pair" + :type "seed" + :accounts []}) + (h/deftest-sub :wallet/settings-keypairs-accounts [sub-name] (testing "returns formatted key-pairs and accounts" (swap! rf-db/app-db - assoc-in - [:wallet :keypairs] - [(assoc keypairs-accounts - :accounts - [wallet-account])]) + (fn [db] + (-> db + (assoc-in + [:wallet :keypairs] + [(assoc default-keypair-accounts + :accounts + [operable-wallet-account]) + (assoc seed-phrase-keypair-accounts + :accounts + [inoperable-wallet-account])]) + (assoc-in + [:wallet :accounts] + {(:address operable-wallet-account) operable-wallet-account + (:address inoperable-wallet-account) inoperable-wallet-account})))) - (let [{:keys [customization-color name address emoji]} wallet-account] - (is - (match? [{:name (:name keypairs-accounts) - :type (keyword (:type keypairs-accounts)) - :accounts [{:account-props {:customization-color customization-color - :size 32 - :emoji emoji - :type :default - :name name - :address address} - :networks [] - :state :default - :action :none}]}] - (rf/sub [sub-name]))))) + (is + (match? + {:missing [{:name (:name seed-phrase-keypair-accounts) + :key-uid (:key-uid seed-phrase-keypair-accounts) + :type (keyword (:type seed-phrase-keypair-accounts)) + :accounts [{:customization-color (:customization-color inoperable-wallet-account) + :emoji (:emoji inoperable-wallet-account) + :type :default}]}] + :operable [{:name (:name default-keypair-accounts) + :key-uid (:key-uid default-keypair-accounts) + :type (keyword (:type default-keypair-accounts)) + :accounts [{:account-props {:customization-color (:customization-color + operable-wallet-account) + :size 32 + :emoji (:emoji operable-wallet-account) + :type :default + :name (:name operable-wallet-account) + :address (:address operable-wallet-account)} + :networks [] + :state :default + :action :none}]}]} + (rf/sub [sub-name])))) (testing "allows for passing account format options" (swap! rf-db/app-db - assoc-in - [:wallet :keypairs] - [(assoc keypairs-accounts - :accounts - [wallet-account])]) + (fn [db] + (-> db + (assoc-in + [:wallet :keypairs] + [(assoc default-keypair-accounts + :accounts + [operable-wallet-account])]) + (assoc-in + [:wallet :accounts] + {(:address operable-wallet-account) operable-wallet-account})))) (let [{:keys [customization-color name address - emoji]} wallet-account + emoji]} operable-wallet-account network-options [{:network-name :ethereum :short-name "eth"} {:network-name :optimism :short-name "oeth"} {:network-name :arbitrum :short-name "arb1"}] size-option 20] (is - (match? [{:name (:name keypairs-accounts) - :type (keyword (:type keypairs-accounts)) - :accounts [{:account-props {:customization-color customization-color - :size size-option - :emoji emoji - :type :default - :name name - :address address} - :networks network-options - :state :default - :action :none}]}] + (match? {:missing [] + :operable [{:name (:name default-keypair-accounts) + :key-uid (:key-uid default-keypair-accounts) + :type (keyword (:type default-keypair-accounts)) + :accounts [{:account-props {:customization-color customization-color + :size size-option + :emoji emoji + :type :default + :name name + :address address} + :networks network-options + :state :default + :action :none}]}]} (rf/sub [sub-name {:networks network-options :size size-option}]))))) (testing "filters non-wallet accounts" (swap! rf-db/app-db - assoc-in - [:wallet :keypairs] - [(assoc keypairs-accounts - :accounts - [chat-account])]) + (fn [db] + (-> db + (assoc-in + [:wallet :keypairs] + [(assoc default-keypair-accounts + :accounts + [operable-wallet-account + chat-account])]) + (assoc-in + [:wallet :accounts] + {(:address operable-wallet-account) operable-wallet-account + (:address chat-account) chat-account})))) (is - (match? [{:name (:name keypairs-accounts) - :type (keyword (:type keypairs-accounts)) - :accounts []}] - (rf/sub [sub-name]))))) + (match? + {:missing [] + :operable [{:name (:name default-keypair-accounts) + :key-uid (:key-uid default-keypair-accounts) + :type (keyword (:type default-keypair-accounts)) + :accounts [{:account-props {:customization-color (:customization-color + operable-wallet-account) + :size 32 + :emoji (:emoji operable-wallet-account) + :type :default + :name (:name operable-wallet-account) + :address (:address operable-wallet-account)} + :networks [] + :state :default + :action :none}]}]} + (rf/sub [sub-name]))))) (def local-suggestions ["a" "b"]) diff --git a/translations/en.json b/translations/en.json index 0ccd0bbdf2..021d277a88 100644 --- a/translations/en.json +++ b/translations/en.json @@ -2625,6 +2625,8 @@ "invite-friend-to-status": "Invite friends to Status", "enter-private-key": "Enter the private key of an address", "enter-private-key-placeholder": "Enter your private key", + "import-to-use-derived-accounts": "Import to use derived accounts", + "amount-missing-keypairs": "{{amount} missing key pairs", "import-private-key-info": "New addresses cannot be derived from an account imported from a private key. Import using a seed phrase if you wish to derive addresses.", "invalid-private-key": "It’s not a valid private key", "private-key-public-address": "Public address of private key",