Implement display of missing key pairs in wallet settings (#20094)

* fix: ensure the keypairs use blur for their theme

* fix: allow for container-style to be passed to standard-title component

* chore: add translations for for missing-keypair labels

* tweak: add support for missing-keypair type for drawer-top component

* tweak: add initial implementation of missing-keypair list-item component

* tweak: add initial implementation of missing-keypairs list component

* feature: add initial implementation of displaying missing key-pairs

* tweak: update missing-keypair list-item to support blur, light, and dark mode

* chore: add missing-keypair list-item preview

* chore: add missing-keypairs preview

* chore: decode :operable key in account to be keyword

* tweak: remove unneeded keyword decoding

* tweak: update drawer-top component to use keypair and stored field

* tidy: revert change for checking for not default-keypair in actions menu
This commit is contained in:
Sean Hagstrom 2024-05-22 18:30:32 +01:00 committed by GitHub
parent 47b4ab923b
commit 60ef4fab4b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
25 changed files with 626 additions and 111 deletions

View File

@ -80,10 +80,10 @@
(h/is-truthy (h/get-by-text "0x62b...0a5")) (h/is-truthy (h/get-by-text "0x62b...0a5"))
(h/is-truthy (h/get-by-label-text :account-avatar))) (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 (h/render-with-theme-provider [quo/drawer-top
{:title "Title" {:title "Title"
:keycard? false :stored nil
:icon-avatar :i/placeholder :icon-avatar :i/placeholder
:type :keypair}] :type :keypair}]
theme) theme)
@ -91,10 +91,21 @@
(-> (h/expect (h/get-by-translation-text :t/on-device)) (-> (h/expect (h/get-by-translation-text :t/on-device))
(.toBeTruthy))) (.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 (h/render-with-theme-provider [quo/drawer-top
{:title "Title" {: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 :icon-avatar :i/placeholder
:type :keypair}] :type :keypair}]
theme) theme)
@ -102,6 +113,17 @@
(-> (h/expect (h/get-by-translation-text :t/on-keycard)) (-> (h/expect (h/get-by-translation-text :t/on-keycard))
(.toBeTruthy))) (.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/test "component renders in default-keypair type"
(h/render-with-theme-provider [quo/drawer-top (h/render-with-theme-provider [quo/drawer-top
{:title "Title" {:title "Title"

View File

@ -37,16 +37,18 @@
nil)) nil))
(defn- keypair-subtitle (defn- keypair-subtitle
[{:keys [theme blur? keycard?]}] [{:keys [theme blur? stored]}]
[rn/view {:style style/row} [rn/view {:style style/row}
[text/text [text/text
{:size :paragraph-2 {:size :paragraph-2
:weight :regular :weight :regular
:style (style/description theme blur?)} :style (style/description theme blur?)}
(if keycard? (case stored
(i18n/label :t/on-keycard) :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))] (i18n/label :t/on-device))]
(when keycard? (when (= stored :on-keycard)
[icons/icon [icons/icon
:i/keycard-card :i/keycard-card
{:color (colors/theme-colors colors/neutral-50 colors/neutral-40 theme) {:color (colors/theme-colors colors/neutral-50 colors/neutral-40 theme)
@ -103,14 +105,14 @@
description]) description])
(defn- subtitle (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]}] context-tag-type account-name emoji customization-color full-name profile-picture]}]
(cond (cond
(= :keypair type) (= :keypair type)
[keypair-subtitle [keypair-subtitle
{:theme theme {:theme theme
:blur? blur? :blur? blur?
:keycard? keycard?}] :stored stored}]
(= :account type) (= :account type)
[account-subtitle [account-subtitle
@ -195,7 +197,7 @@
(defn view (defn view
[{:keys [title title-icon type description blur? community-name community-logo button-icon [{:keys [title title-icon type description blur? community-name community-logo button-icon
account-name emoji context-tag-type button-type container-style 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]}] button-disabled? account-avatar-emoji account-avatar-type customization-color icon-avatar]}]
(let [theme (quo.theme/use-theme)] (let [theme (quo.theme/use-theme)]
[rn/view {:style (merge style/container container-style)} [rn/view {:style (merge style/container container-style)}
@ -220,7 +222,7 @@
{:type type {:type type
:theme theme :theme theme
:blur? blur? :blur? blur?
:keycard? keycard? :stored stored
:networks networks :networks networks
:description description :description description
:community-name community-name :community-name community-name

View File

@ -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))))

View File

@ -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])

View File

@ -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)))

View File

@ -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))

View File

@ -3,7 +3,6 @@
(def container (def container
{:flex-direction :row {:flex-direction :row
:flex 1
:justify-content :space-between}) :justify-content :space-between})
(def right-counter (def right-counter

View File

@ -55,8 +55,8 @@
:icon-color (style/right-tag-icon-color blur? theme)}])) :icon-color (style/right-tag-icon-color blur? theme)}]))
(defn view (defn view
[{:keys [title right accessibility-label] :as props}] [{:keys [title right accessibility-label container-style] :as props}]
[rn/view {:style style/container} [rn/view {:style (merge style/container container-style)}
[text/text [text/text
{:size :heading-1 {:size :heading-1
:weight :semi-bold :weight :semi-bold

View File

@ -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))))

View File

@ -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))})

View File

@ -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}}]}]])

View File

@ -85,6 +85,7 @@
quo.components.list-items.community.view quo.components.list-items.community.view
quo.components.list-items.dapp.view quo.components.list-items.dapp.view
quo.components.list-items.menu-item quo.components.list-items.menu-item
quo.components.list-items.missing-keypair.view
quo.components.list-items.network-list.view quo.components.list-items.network-list.view
quo.components.list-items.preview-list.view quo.components.list-items.preview-list.view
quo.components.list-items.quiz-item.view quo.components.list-items.quiz-item.view
@ -169,6 +170,7 @@
quo.components.wallet.approval-label.view quo.components.wallet.approval-label.view
quo.components.wallet.confirmation-progress.view quo.components.wallet.confirmation-progress.view
quo.components.wallet.keypair.view quo.components.wallet.keypair.view
quo.components.wallet.missing-keypairs.view
quo.components.wallet.network-amount.view quo.components.wallet.network-amount.view
quo.components.wallet.network-bridge.view quo.components.wallet.network-bridge.view
quo.components.wallet.network-link.view quo.components.wallet.network-link.view
@ -320,6 +322,7 @@
(def community-list quo.components.list-items.community.view/view) (def community-list quo.components.list-items.community.view/view)
(def dapp quo.components.list-items.dapp.view/view) (def dapp quo.components.list-items.dapp.view/view)
(def menu-item quo.components.list-items.menu-item/menu-item) (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 network-list quo.components.list-items.network-list.view/view)
(def preview-list quo.components.list-items.preview-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) (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 approval-label quo.components.wallet.approval-label.view/view)
(def confirmation-progress quo.components.wallet.confirmation-progress.view/view) (def confirmation-progress quo.components.wallet.confirmation-progress.view/view)
(def keypair quo.components.wallet.keypair.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-amount quo.components.wallet.network-amount.view/view)
(def network-bridge quo.components.wallet.network-bridge.view/view) (def network-bridge quo.components.wallet.network-bridge.view/view)
(def network-routing quo.components.wallet.network-routing.view/view) (def network-routing quo.components.wallet.network-routing.view/view)

View File

@ -25,8 +25,11 @@
:value "null"}]} :value "null"}]}
{:key :blur? {:key :blur?
:type :boolean} :type :boolean}
{:key :keycard? {:key :stored
:type :boolean} :type :select
:options [{:key :on-device}
{:key :on-keycard}
{:key :missing}]}
{:key :title {:key :title
:type :text} :type :text}
{:key :description {:key :description
@ -44,7 +47,7 @@
:title "Title" :title "Title"
:type :default :type :default
:label "Drawer label" :label "Drawer label"
:keycard? true :stored :on-device
:networks [{:network-name :ethereum :short-name "eth"}] :networks [{:network-name :ethereum :short-name "eth"}]
:description "0x62b...0a5" :description "0x62b...0a5"
:button-icon :i/placeholder :button-icon :i/placeholder

View File

@ -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]]])))

View File

@ -103,6 +103,7 @@
[status-im.contexts.preview.quo.list-items.address :as address] [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.channel :as channel]
[status-im.contexts.preview.quo.list-items.dapp :as dapp] [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.network-list :as network-list]
[status-im.contexts.preview.quo.list-items.preview-lists :as preview-lists] [status-im.contexts.preview.quo.list-items.preview-lists :as preview-lists]
[status-im.contexts.preview.quo.list-items.quiz-item :as quiz-item] [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 [status-im.contexts.preview.quo.wallet.confirmation-progress :as
confirmation-progress] confirmation-progress]
[status-im.contexts.preview.quo.wallet.keypair :as keypair] [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-amount :as network-amount]
[status-im.contexts.preview.quo.wallet.network-bridge :as network-bridge] [status-im.contexts.preview.quo.wallet.network-bridge :as network-bridge]
[status-im.contexts.preview.quo.wallet.network-link :as network-link] [status-im.contexts.preview.quo.wallet.network-link :as network-link]
@ -380,6 +382,8 @@
:component community-list-item/view} :component community-list-item/view}
{:name :dapp {:name :dapp
:component dapp/preview} :component dapp/preview}
{:name :missing-keypair
:component missing-keypair/view}
{:name :network-list {:name :network-list
:component network-list/view} :component network-list/view}
{:name :preview-lists {:name :preview-lists
@ -535,6 +539,8 @@
{:name :confirmation-progress {:name :confirmation-progress
:component confirmation-progress/view} :component confirmation-progress/view}
{:name :keypair :component keypair/view} {:name :keypair :component keypair/view}
{:name :missing-keypairs
:component missing-keypairs/view}
{:name :network-amount :component network-amount/view} {:name :network-amount :component network-amount/view}
{:name :network-bridge :component network-bridge/view} {:name :network-bridge :component network-bridge/view}
{:name :network-link :component network-link/view} {:name :network-link :component network-link/view}

View File

@ -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)]]])))

View File

@ -16,3 +16,10 @@
(def keypair-container-style (def keypair-container-style
{:margin-horizontal 20 {:margin-horizontal 20
:margin-vertical 8}) :margin-vertical 8})
(def missing-keypairs-container-style
{:margin-horizontal 20
:margin-vertical 8})
(def settings-keypairs-container
{:flex 1})

View File

@ -20,6 +20,21 @@
{:content (fn [] [actions/view props keypair]) {:content (fn [] [actions/view props keypair])
:theme theme}])) :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 (defn- keypair
[{keypair-type :type [{keypair-type :type
:keys [accounts name] :keys [accounts name]
@ -33,22 +48,19 @@
on-press (rn/use-callback on-press (rn/use-callback
(fn [] (fn []
(on-options-press (on-options-press
(cond-> {:theme theme (options-drawer-props
:blur? true {:theme theme
:title name} :keypair item
default-keypair? :type (if default-keypair? :default-keypair :keypair)
(assoc :type :default-keypair :stored :on-device
:description shortened-key :shortened-key shortened-key
:customization-color customization-color :customization-color customization-color
:profile-picture profile-picture) :profile-picture profile-picture})
(not default-keypair?)
(assoc :type :keypair
:icon-avatar :i/seed))
item)) item))
[customization-color default-keypair? name [customization-color default-keypair? item
profile-picture shortened-key theme])] profile-picture shortened-key theme])]
[quo/keypair [quo/keypair
{:blur? false {:blur? true
:status-indicator false :status-indicator false
:stored :on-device :stored :on-device
:action :options :action :options
@ -61,13 +73,27 @@
:details {:full-name name :details {:full-name name
:address shortened-key}}])) :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 (defn view
[] []
(let [insets (safe-area/get-insets) (let [insets (safe-area/get-insets)
compressed-key (rf/sub [:profile/compressed-key]) compressed-key (rf/sub [:profile/compressed-key])
profile-picture (rf/sub [:profile/image]) profile-picture (rf/sub [:profile/image])
customization-color (rf/sub [:profile/customization-color]) customization-color (rf/sub [:profile/customization-color])
quo-keypairs-accounts (rf/sub [:wallet/settings-keypairs-accounts])] {missing-keypairs :missing
operable-keypairs :operable} (rf/sub [:wallet/settings-keypairs-accounts])]
[quo/overlay [quo/overlay
{:type :shell {:type :shell
:container-style (style/page-wrapper (:top insets))} :container-style (style/page-wrapper (:top insets))}
@ -81,9 +107,15 @@
{:title (i18n/label :t/keypairs-and-accounts) {:title (i18n/label :t/keypairs-and-accounts)
:accessibility-label :keypairs-and-accounts-header :accessibility-label :keypairs-and-accounts-header
:customization-color customization-color}]] :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 [rn/flat-list
{:data quo-keypairs-accounts {:data operable-keypairs
:render-fn keypair :render-fn keypair
:render-data {:profile-picture profile-picture :render-data {:profile-picture profile-picture
:compressed-key compressed-key :compressed-key compressed-key

View File

@ -42,6 +42,7 @@
(update :prod-preferred-chain-ids chain-ids-string->set) (update :prod-preferred-chain-ids chain-ids-string->set)
(update :test-preferred-chain-ids chain-ids-string->set) (update :test-preferred-chain-ids chain-ids-string->set)
(update :type keyword) (update :type keyword)
(update :operable keyword)
(update :color #(if (seq %) (keyword %) constants/account-default-customization-color)) (update :color #(if (seq %) (keyword %) constants/account-default-customization-color))
(update :emoji sanitize-emoji) (update :emoji sanitize-emoji)
(assoc :default-account? (:wallet account)) (assoc :default-account? (:wallet account))

View File

@ -36,7 +36,7 @@
:position 1 :position 1
:clock 1698945829328 :clock 1698945829328
:created-at 1698928839000 :created-at 1698928839000
:operable "fully" :operable :fully
:mixedcase-address "0x7bcDfc75c431" :mixedcase-address "0x7bcDfc75c431"
:public-key "0x04371e2d9d66b82f056bc128064" :public-key "0x04371e2d9d66b82f056bc128064"
:removed false} :removed false}

View File

@ -71,4 +71,5 @@
{:source (resources/get-image :transaction-progress) {:source (resources/get-image :transaction-progress)
:style {:margin-bottom 12}}] :style {:margin-bottom 12}}]
[quo/standard-title [quo/standard-title
{:title (titles (combined-status-overview transaction-details))}]]])))) {:container-style {:flex 1}
:title (titles (combined-status-overview transaction-details))}]]]))))

View File

@ -520,9 +520,7 @@
:component saved-addresses-settings/view} :component saved-addresses-settings/view}
{:name :screen/settings.keypairs-and-accounts {:name :screen/settings.keypairs-and-accounts
:options (merge :options options/transparent-modal-screen-options
options/transparent-modal-screen-options
options/dark-screen)
:component keypairs-and-accounts/view} :component keypairs-and-accounts/view}
{:name :screen/settings.network-settings {:name :screen/settings.network-settings

View File

@ -195,17 +195,40 @@
:state :default :state :default
:action :none}))))) :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 (rf/reg-sub
:wallet/settings-keypairs-accounts :wallet/settings-keypairs-accounts
:<- [:wallet/keypairs] :<- [:wallet/keypairs]
(fn [keypairs [_ format-options]] :<- [:wallet/accounts]
(->> keypairs (fn [[keypairs accounts] [_ format-options]]
(map (fn [{:keys [accounts name type key-uid]}] (let [grouped-accounts (->> accounts
{:type (keyword type) (map #(select-keys % [:operable :key-uid]))
:name name (group-by :operable))
:key-uid key-uid operable-key-pair-ids (->> (map :key-uid (:fully grouped-accounts))
:accounts (format-settings-keypair-accounts accounts format-options)}))))) (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 (rf/reg-sub
:wallet/derivation-path-state :wallet/derivation-path-state
:<- [:wallet/create-account] :<- [:wallet/create-account]

View File

@ -104,7 +104,7 @@
:position 0 :position 0
:clock 1698945829328 :clock 1698945829328
:created-at 1698928839000 :created-at 1698928839000
:operable "fully" :operable :fully
:mixedcase-address "0x7bcDfc75c431" :mixedcase-address "0x7bcDfc75c431"
:public-key "0x04371e2d9d66b82f056bc128064" :public-key "0x04371e2d9d66b82f056bc128064"
:removed false :removed false
@ -125,7 +125,7 @@
:position 1 :position 1
:clock 1698945829328 :clock 1698945829328
:created-at 1698928839000 :created-at 1698928839000
:operable "fully" :operable :fully
:mixedcase-address "0x7bcDfc75c431" :mixedcase-address "0x7bcDfc75c431"
:public-key "0x04371e2d9d66b82f056bc128064" :public-key "0x04371e2d9d66b82f056bc128064"
:removed false :removed false
@ -146,7 +146,7 @@
:position 2 :position 2
:clock 1698945829328 :clock 1698945829328
:created-at 1698928839000 :created-at 1698928839000
:operable "fully" :operable :fully
:mixedcase-address "0x7bcDfc75c431" :mixedcase-address "0x7bcDfc75c431"
:public-key "0x" :public-key "0x"
:removed false :removed false
@ -221,7 +221,7 @@
:position 0 :position 0
:clock 1698945829328 :clock 1698945829328
:created-at 1698928839000 :created-at 1698928839000
:operable "fully" :operable :fully
:mixedcase-address "0x7bcDfc75c431" :mixedcase-address "0x7bcDfc75c431"
:public-key "0x04371e2d9d66b82f056bc128064" :public-key "0x04371e2d9d66b82f056bc128064"
:removed false :removed false
@ -243,7 +243,7 @@
:position 1 :position 1
:clock 1698945829328 :clock 1698945829328
:created-at 1698928839000 :created-at 1698928839000
:operable "fully" :operable :fully
:mixedcase-address "0x7bcDfc75c431" :mixedcase-address "0x7bcDfc75c431"
:public-key "0x04371e2d9d66b82f056bc128064" :public-key "0x04371e2d9d66b82f056bc128064"
:removed false :removed false
@ -265,7 +265,7 @@
:position 2 :position 2
:clock 1698945829328 :clock 1698945829328
:created-at 1698928839000 :created-at 1698928839000
:operable "fully" :operable :fully
:mixedcase-address "0x7bcDfc75c431" :mixedcase-address "0x7bcDfc75c431"
:public-key "0x" :public-key "0x"
:removed false :removed false
@ -308,7 +308,7 @@
:position 0 :position 0
:clock 1698945829328 :clock 1698945829328
:created-at 1698928839000 :created-at 1698928839000
:operable "fully" :operable :fully
:mixedcase-address "0x7bcDfc75c431" :mixedcase-address "0x7bcDfc75c431"
:public-key "0x04371e2d9d66b82f056bc128064" :public-key "0x04371e2d9d66b82f056bc128064"
:removed false :removed false
@ -369,7 +369,7 @@
:position 0 :position 0
:clock 1698945829328 :clock 1698945829328
:created-at 1698928839000 :created-at 1698928839000
:operable "fully" :operable :fully
:mixedcase-address "0x7bcDfc75c431" :mixedcase-address "0x7bcDfc75c431"
:public-key "0x04371e2d9d66b82f056bc128064" :public-key "0x04371e2d9d66b82f056bc128064"
:removed false :removed false
@ -391,7 +391,7 @@
:position 2 :position 2
:clock 1698945829328 :clock 1698945829328
:created-at 1698928839000 :created-at 1698928839000
:operable "fully" :operable :fully
:mixedcase-address "0x7bcDfc75c431" :mixedcase-address "0x7bcDfc75c431"
:public-key "0x" :public-key "0x"
:removed false :removed false
@ -426,7 +426,7 @@
:position 0 :position 0
:clock 1698945829328 :clock 1698945829328
:created-at 1698928839000 :created-at 1698928839000
:operable "fully" :operable :fully
:mixedcase-address "0x7bcDfc75c431" :mixedcase-address "0x7bcDfc75c431"
:public-key "0x04371e2d9d66b82f056bc128064" :public-key "0x04371e2d9d66b82f056bc128064"
:removed false :removed false
@ -449,7 +449,7 @@
:position 1 :position 1
:clock 1698945829328 :clock 1698945829328
:created-at 1698928839000 :created-at 1698928839000
:operable "fully" :operable :fully
:mixedcase-address "0x7bcDfc75c431" :mixedcase-address "0x7bcDfc75c431"
:public-key "0x04371e2d9d66b82f056bc128064" :public-key "0x04371e2d9d66b82f056bc128064"
:removed false :removed false
@ -568,7 +568,7 @@
:hidden false :hidden false
:removed false}) :removed false})
(def wallet-account (def operable-wallet-account
{:path "m/44'/60'/0'/0/0" {:path "m/44'/60'/0'/0/0"
:emoji "🤡" :emoji "🤡"
:key-uid "abc" :key-uid "abc"
@ -579,83 +579,147 @@
:chat false :chat false
:customization-color :primary :customization-color :primary
:hidden false :hidden false
:operable :fully
:removed false}) :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" {:key-uid "abc"
:name "My Profile" :name "My Profile"
:type "profile" :type "profile"
:accounts []}) :accounts []})
(def seed-phrase-keypair-accounts
{:key-uid "def"
:name "My Key Pair"
:type "seed"
:accounts []})
(h/deftest-sub :wallet/settings-keypairs-accounts (h/deftest-sub :wallet/settings-keypairs-accounts
[sub-name] [sub-name]
(testing "returns formatted key-pairs and accounts" (testing "returns formatted key-pairs and accounts"
(swap! rf-db/app-db (swap! rf-db/app-db
assoc-in (fn [db]
[:wallet :keypairs] (-> db
[(assoc keypairs-accounts (assoc-in
:accounts [:wallet :keypairs]
[wallet-account])]) [(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
(is (match?
(match? [{:name (:name keypairs-accounts) {:missing [{:name (:name seed-phrase-keypair-accounts)
:type (keyword (:type keypairs-accounts)) :key-uid (:key-uid seed-phrase-keypair-accounts)
:accounts [{:account-props {:customization-color customization-color :type (keyword (:type seed-phrase-keypair-accounts))
:size 32 :accounts [{:customization-color (:customization-color inoperable-wallet-account)
:emoji emoji :emoji (:emoji inoperable-wallet-account)
:type :default :type :default}]}]
:name name :operable [{:name (:name default-keypair-accounts)
:address address} :key-uid (:key-uid default-keypair-accounts)
:networks [] :type (keyword (:type default-keypair-accounts))
:state :default :accounts [{:account-props {:customization-color (:customization-color
:action :none}]}] operable-wallet-account)
(rf/sub [sub-name]))))) :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" (testing "allows for passing account format options"
(swap! rf-db/app-db (swap! rf-db/app-db
assoc-in (fn [db]
[:wallet :keypairs] (-> db
[(assoc keypairs-accounts (assoc-in
:accounts [:wallet :keypairs]
[wallet-account])]) [(assoc default-keypair-accounts
:accounts
[operable-wallet-account])])
(assoc-in
[:wallet :accounts]
{(:address operable-wallet-account) operable-wallet-account}))))
(let [{:keys [customization-color (let [{:keys [customization-color
name name
address address
emoji]} wallet-account emoji]} operable-wallet-account
network-options [{:network-name :ethereum :short-name "eth"} network-options [{:network-name :ethereum :short-name "eth"}
{:network-name :optimism :short-name "oeth"} {:network-name :optimism :short-name "oeth"}
{:network-name :arbitrum :short-name "arb1"}] {:network-name :arbitrum :short-name "arb1"}]
size-option 20] size-option 20]
(is (is
(match? [{:name (:name keypairs-accounts) (match? {:missing []
:type (keyword (:type keypairs-accounts)) :operable [{:name (:name default-keypair-accounts)
:accounts [{:account-props {:customization-color customization-color :key-uid (:key-uid default-keypair-accounts)
:size size-option :type (keyword (:type default-keypair-accounts))
:emoji emoji :accounts [{:account-props {:customization-color customization-color
:type :default :size size-option
:name name :emoji emoji
:address address} :type :default
:networks network-options :name name
:state :default :address address}
:action :none}]}] :networks network-options
:state :default
:action :none}]}]}
(rf/sub [sub-name (rf/sub [sub-name
{:networks network-options {:networks network-options
:size size-option}]))))) :size size-option}])))))
(testing "filters non-wallet accounts" (testing "filters non-wallet accounts"
(swap! rf-db/app-db (swap! rf-db/app-db
assoc-in (fn [db]
[:wallet :keypairs] (-> db
[(assoc keypairs-accounts (assoc-in
:accounts [:wallet :keypairs]
[chat-account])]) [(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 (is
(match? [{:name (:name keypairs-accounts) (match?
:type (keyword (:type keypairs-accounts)) {:missing []
:accounts []}] :operable [{:name (:name default-keypair-accounts)
(rf/sub [sub-name]))))) :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"]) (def local-suggestions ["a" "b"])

View File

@ -2625,6 +2625,8 @@
"invite-friend-to-status": "Invite friends to Status", "invite-friend-to-status": "Invite friends to Status",
"enter-private-key": "Enter the private key of an address", "enter-private-key": "Enter the private key of an address",
"enter-private-key-placeholder": "Enter your private key", "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.", "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": "Its not a valid private key", "invalid-private-key": "Its not a valid private key",
"private-key-public-address": "Public address of private key", "private-key-public-address": "Public address of private key",