diff --git a/src/quo/components/list_items/dapp/component_spec.cljs b/src/quo/components/list_items/dapp/component_spec.cljs index a2452be929..d2fb420a59 100644 --- a/src/quo/components/list_items/dapp/component_spec.cljs +++ b/src/quo/components/list_items/dapp/component_spec.cljs @@ -7,8 +7,8 @@ {:dapp {:avatar nil :name "Coingecko" :value "coingecko.com"} + :accessibility-label "dapp-coingecko" :state :default - :action :icon :blur? false :customization-color :blue}) @@ -20,6 +20,6 @@ (h/is-truthy (h/get-by-text (:value (:dapp props))))) (h/test "on-press event" (let [mock-fn (h/mock-fn)] - (h/render [dapp/view (assoc props :on-press-icon mock-fn)]) - (h/fire-event :press (h/get-by-test-id "dapp-component-icon")) + (h/render [dapp/view (assoc props :on-press mock-fn)]) + (h/fire-event :press (h/get-by-label-text "dapp-coingecko")) (h/was-called mock-fn)))) diff --git a/src/quo/components/list_items/dapp/view.cljs b/src/quo/components/list_items/dapp/view.cljs index 9a6ec20926..efe707e0d5 100644 --- a/src/quo/components/list_items/dapp/view.cljs +++ b/src/quo/components/list_items/dapp/view.cljs @@ -1,24 +1,24 @@ (ns quo.components.list-items.dapp.view (:require - [quo.components.icon :as icons] [quo.components.list-items.dapp.style :as style] [quo.components.markdown.text :as text] - [quo.foundations.colors :as colors] [quo.theme :as quo.theme] [react-native.core :as rn] [react-native.fast-image :as fast-image])) (defn view - [{:keys [dapp action on-press on-press-icon] :as props}] + [{:keys [dapp on-press right-component accessibility-label] :as props}] (let [theme (quo.theme/use-theme) [pressed? set-pressed] (rn/use-state false) on-press-in (rn/use-callback #(set-pressed true)) on-press-out (rn/use-callback #(set-pressed false))] [rn/pressable - {:style (style/container (assoc props :pressed? pressed?)) - :on-press on-press - :on-press-in on-press-in - :on-press-out on-press-out} + {:style (style/container (assoc props :pressed? pressed?)) + :accessibility-label accessibility-label + :on-press (when on-press + (fn [] (on-press dapp))) + :on-press-in on-press-in + :on-press-out on-press-out} [rn/view {:style style/container-info} [fast-image/fast-image {:source (:avatar dapp) @@ -34,13 +34,5 @@ :size :paragraph-2 :style (style/style-text-value theme)} (:value dapp)]]] - (when (= action :icon) - [rn/pressable - {:on-press on-press-icon - :testID "dapp-component-icon"} - [icons/icon :i/options - {:color (colors/theme-colors - colors/neutral-50 - colors/neutral-40 - theme) - :accessibility-label :icon}]])])) + (when right-component + [right-component dapp])])) diff --git a/src/quo/components/navigation/page_nav/style.cljs b/src/quo/components/navigation/page_nav/style.cljs index d35a5e80b8..4e1f6763d4 100644 --- a/src/quo/components/navigation/page_nav/style.cljs +++ b/src/quo/components/navigation/page_nav/style.cljs @@ -12,6 +12,10 @@ :justify-content :space-between :align-items :center}) +(def icon-container + {:flex-grow 1 + :flex-basis 1}) + (defn center-content-container [centered?] {:flex 1 @@ -21,13 +25,19 @@ :justify-content (if centered? :center :flex-start)}) (def right-actions-container - {:flex-direction :row}) + {:flex-direction :row + :justify-content :flex-end}) (def right-actions-spacing {:width 12}) -(def right-content-min-size - {:min-width 32 :min-height 32}) +(defn right-content + [min-size?] + (merge + {:flex-grow 1 + :flex-basis 1} + (when min-size? + {:min-height 32}))) (def token-logo {:width 16 :height 16}) diff --git a/src/quo/components/navigation/page_nav/view.cljs b/src/quo/components/navigation/page_nav/view.cljs index 7c388f4fba..3a6136eb66 100644 --- a/src/quo/components/navigation/page_nav/view.cljs +++ b/src/quo/components/navigation/page_nav/view.cljs @@ -30,16 +30,17 @@ & children] (into [rn/view {:style (style/container margin-top)} (when icon-name - [button/button - {:type (button-type background) - :icon-only? true - :size 32 - :on-press on-press - :background (if behind-overlay? - :blur - (when (button-properties/backgrounds background) background)) - :accessibility-label accessibility-label} - icon-name])] + [rn/view {:style style/icon-container} + [button/button + {:type (button-type background) + :icon-only? true + :size 32 + :on-press on-press + :background (if behind-overlay? + :blur + (when (button-properties/backgrounds background) background)) + :accessibility-label accessibility-label} + icon-name]])] children)) (defn- right-section-spacing [] [rn/view {:style style/right-actions-spacing}]) @@ -88,7 +89,7 @@ [{:keys [background content max-actions min-size? support-account-switcher? behind-overlay?] :or {support-account-switcher? true}}] - [rn/view (when min-size? {:style style/right-content-min-size}) + [rn/view (style/right-content min-size?) (when (coll? content) (into [rn/view {:style style/right-actions-container}] (add-right-buttons-xf max-actions background behind-overlay? support-account-switcher?) diff --git a/src/quo/components/text_combinations/standard_title/component_spec.cljs b/src/quo/components/text_combinations/standard_title/component_spec.cljs index c690299359..a41cf4d627 100644 --- a/src/quo/components/text_combinations/standard_title/component_spec.cljs +++ b/src/quo/components/text_combinations/standard_title/component_spec.cljs @@ -1,5 +1,6 @@ (ns quo.components.text-combinations.standard-title.component-spec - (:require [quo.components.text-combinations.standard-title.view :as standard-title] + (:require [quo.components.markdown.text :as text] + [quo.components.text-combinations.standard-title.view :as standard-title] [test-helpers.component :as h])) (h/describe "Text combinations - Standard title" @@ -48,4 +49,12 @@ :right :tag :on-press on-press-fn}]) (h/fire-event :on-press (h/get-by-label-text :standard-title-tag)) - (h/was-called-times on-press-fn 1))))) + (h/was-called-times on-press-fn 1)))) + + (h/describe "Custom content variant" + (h/test "Default render" + (h/render [standard-title/view + {:title "This is a title" + :right [text/text "Right"]}]) + (h/is-truthy (h/get-by-text "This is a title")) + (h/is-truthy (h/get-by-text "Right"))))) diff --git a/src/quo/components/text_combinations/standard_title/view.cljs b/src/quo/components/text_combinations/standard_title/view.cljs index cbc049d932..cc150848cc 100644 --- a/src/quo/components/text_combinations/standard_title/view.cljs +++ b/src/quo/components/text_combinations/standard_title/view.cljs @@ -66,4 +66,4 @@ :counter [right-counter props] :action [right-action props] :tag [right-tag props] - nil)]) + right)]) diff --git a/src/status_im/contexts/preview/quo/list_items/dapp.cljs b/src/status_im/contexts/preview/quo/list_items/dapp.cljs index dde1d9aa51..f14e18f7fe 100644 --- a/src/status_im/contexts/preview/quo/list_items/dapp.cljs +++ b/src/status_im/contexts/preview/quo/list_items/dapp.cljs @@ -28,10 +28,10 @@ :name "Coingecko" :value "coingecko.com"} :state :default - :action :icon :blur? false :customization-color :blue - :on-press-icon (fn [] (js/alert "Button pressed"))})] + :on-press (fn [{:keys [name]}] + (js/alert (str name " got pressed")))})] (fn [] [preview/preview-container {:state state :descriptor descriptor} [rn/view diff --git a/src/status_im/contexts/wallet/common/account_switcher/view.cljs b/src/status_im/contexts/wallet/common/account_switcher/view.cljs index 314fdcd210..a9097a005a 100644 --- a/src/status_im/contexts/wallet/common/account_switcher/view.cljs +++ b/src/status_im/contexts/wallet/common/account_switcher/view.cljs @@ -15,6 +15,10 @@ :select-account {:content select-account/view} nil)) +(defn- on-dapps-press + [switcher-type] + (rf/dispatch [:show-bottom-sheet (get-bottom-sheet-args switcher-type)])) + (defn view [{:keys [type on-press accessibility-label icon-name switcher-type margin-top] :or {icon-name :i/close @@ -39,7 +43,6 @@ {:content-type :account-switcher :customization-color color - :on-press #(rf/dispatch [:show-bottom-sheet - (get-bottom-sheet-args switcher-type)]) + :on-press #(on-dapps-press switcher-type) :emoji emoji :type (when watch-only? :watch-only)}]}])) diff --git a/src/status_im/contexts/wallet/connected_dapps/disconnect_dapp/style.cljs b/src/status_im/contexts/wallet/connected_dapps/disconnect_dapp/style.cljs new file mode 100644 index 0000000000..79fcc617ec --- /dev/null +++ b/src/status_im/contexts/wallet/connected_dapps/disconnect_dapp/style.cljs @@ -0,0 +1,6 @@ +(ns status-im.contexts.wallet.connected-dapps.disconnect-dapp.style) + +(def content-wrapper + {:padding-vertical 8 + :padding-horizontal 20 + :gap 12}) diff --git a/src/status_im/contexts/wallet/connected_dapps/disconnect_dapp/view.cljs b/src/status_im/contexts/wallet/connected_dapps/disconnect_dapp/view.cljs new file mode 100644 index 0000000000..f7ca977abe --- /dev/null +++ b/src/status_im/contexts/wallet/connected_dapps/disconnect_dapp/view.cljs @@ -0,0 +1,33 @@ +(ns status-im.contexts.wallet.connected-dapps.disconnect-dapp.view + (:require + [quo.core :as quo] + [react-native.core :as rn] + [status-im.contexts.wallet.connected-dapps.disconnect-dapp.style :as style] + [utils.i18n :as i18n] + [utils.re-frame :as rf])) + +(defn view + [{:keys [customization-color dapp on-disconnect]}] + (let [{:keys [avatar name]} dapp] + [:<> + [quo/drawer-top + {:type :context-tag + :context-tag-type :default + :full-name name + :profile-picture avatar + :title (i18n/label :t/disconnect-dapp) + :customization-color customization-color}] + [rn/view {:style style/content-wrapper} + [quo/text + {:size :paragraph-1} + (i18n/label :t/disconnect-dapp-confirmation {:dapp name})]] + [quo/bottom-actions + {:actions :two-actions + :button-one-label (i18n/label :t/disconnect) + :button-one-props {:type :danger + :accessibility-label :block-contact + :on-press on-disconnect} + :button-two-label (i18n/label :t/cancel) + :button-two-props {:type :grey + :accessibility-label :cancel + :on-press #(rf/dispatch [:hide-bottom-sheet])}}]])) diff --git a/src/status_im/contexts/wallet/connected_dapps/style.cljs b/src/status_im/contexts/wallet/connected_dapps/style.cljs index 068690f329..b011ac5def 100644 --- a/src/status_im/contexts/wallet/connected_dapps/style.cljs +++ b/src/status_im/contexts/wallet/connected_dapps/style.cljs @@ -9,9 +9,35 @@ :padding-horizontal screen-padding :margin-vertical 12}) -(defn header-text - [bottom-padding?] - {:padding-horizontal screen-padding - :padding-top 12 - :padding-bottom (when bottom-padding? 8) - :color colors/black}) +(def header-wrapper + {:flex-direction :column + :height 96 + :gap 8 + :padding-horizontal 20 + :padding-vertical 12}) + +(def account-details-wrapper + {:align-items :flex-start}) + +(def empty-container-style + {:justify-content :center + :flex 1 + :margin-bottom 44}) + +(defn dapps-container + [bottom] + {:padding-horizontal 20 + :padding-vertical 8 + :margin-bottom bottom + :flex 1}) + +(defn dapps-list + [theme] + {:border-radius 16 + :border-width 1 + :border-color (colors/theme-colors colors/neutral-10 colors/neutral-80 theme)}) + +(defn separator + [theme] + {:height 1 + :background-color (colors/theme-colors colors/neutral-10 colors/neutral-80 theme)}) diff --git a/src/status_im/contexts/wallet/connected_dapps/view.cljs b/src/status_im/contexts/wallet/connected_dapps/view.cljs index 1fa004a343..be7d079e4b 100644 --- a/src/status_im/contexts/wallet/connected_dapps/view.cljs +++ b/src/status_im/contexts/wallet/connected_dapps/view.cljs @@ -1,29 +1,129 @@ (ns status-im.contexts.wallet.connected-dapps.view (:require [quo.core :as quo] + [quo.foundations.colors :as colors] + [quo.theme] [react-native.core :as rn] + [react-native.safe-area :as safe-area] + [status-im.common.plus-button.view :as plus-button] + [status-im.common.resources :as resources] + [status-im.contexts.wallet.connected-dapps.disconnect-dapp.view :as disconnect-dapp] [status-im.contexts.wallet.connected-dapps.style :as style] + [utils.i18n :as i18n] [utils.re-frame :as rf])) +(defn- on-disconnect + [wallet-account {:keys [name topic]}] + (rf/dispatch [:hide-bottom-sheet]) + (rf/dispatch + [:wallet-connect/disconnect-dapp + {:topic topic + :on-success (fn [] + (rf/dispatch [:wallet-connect/remove-pairing-by-topic topic]) + (rf/dispatch [:toasts/upsert + {:id :dapp-disconnect-success + :type :positive + :text (i18n/label :t/disconnect-dapp-success + {:dapp name + :account (:name wallet-account)})}])) + :on-fail (fn [] + (rf/dispatch [:toasts/upsert + {:id :dapp-disconnect-failure + :type :negative + :text (i18n/label :t/disconnect-dapp-fail + {:dapp name + :account (:name wallet-account)})}]))}])) + +(defn- on-dapp-disconnect-press + [wallet-account dapp] + (rf/dispatch [:show-bottom-sheet + {:content (fn [] [disconnect-dapp/view + {:customization-color (:color wallet-account) + :dapp dapp + :on-disconnect #(on-disconnect wallet-account dapp)}])}])) + +(defn- account-details + [{:keys [name emoji color]}] + (let [theme (quo.theme/use-theme)] + [rn/view {:style style/account-details-wrapper} + [quo/context-tag + {:theme theme + :type :account + :size 24 + :account-name name + :emoji emoji + :customization-color color}]])) + (defn- header - [{:keys [title subtitle]}] - [:<> - [rn/view {:style style/header-container} - [quo/button - {:icon-only? true - :type :grey - :background :blur - :size 32 - :accessibility-label :close-scan-qr-code - :on-press #(rf/dispatch [:navigate-back])} - :i/close]] - [quo/text - {:size :heading-1 - :weight :semi-bold - :style (style/header-text (when subtitle true))} - title]]) + [{:keys [title wallet-account on-close on-add]}] + (let [{:keys [color]} wallet-account] + [:<> + [rn/view {:style style/header-container} + [quo/button + {:icon-only? true + :type :grey + :background :blur + :size 32 + :accessibility-label :connected-dapps-close + :on-press on-close} + :i/close]] + [rn/view {:style style/header-wrapper} + [quo/standard-title + {:title title + :accessibility-label :connected-dapps + :customization-color color + :right [plus-button/plus-button + {:on-press on-add + :accessibility-label :connected-dapps-add + :customization-color color}]}] + [rn/view {:flex 1} + [account-details wallet-account]]]])) (defn view [] - [rn/view {:style {:flex 1}} - [header {:title "Connected dApps"}]]) + (let [{:keys [bottom]} (safe-area/get-insets) + {:keys [color] :as wallet-account} (rf/sub [:wallet/current-viewing-account]) + pairings (rf/sub [:wallet-connect/pairings]) + theme (quo.theme/use-theme)] + [rn/view {:flex 1} + [header + {:title (i18n/label :t/connected-dapps) + :wallet-account wallet-account + :on-close #(rf/dispatch [:navigate-back]) + :on-add #(js/alert "Feature not implemented")}] + (if (empty? pairings) + [quo/empty-state + {:title (i18n/label :t/no-dapps) + :description (i18n/label :t/no-dapps-description) + :image (resources/get-themed-image :no-dapps theme) + :container-style style/empty-container-style}] + [rn/view (style/dapps-container bottom) + [rn/flat-list + {:data pairings + :always-bounce-vertical false + :content-container-style (style/dapps-list theme) + :render-fn (fn [{:keys [topic] + {:keys [icons name url]} :peerMetadata}] + [quo/dapp + {:dapp {:avatar (get icons 0) + :name name + :value url + :topic topic} + :accessibility-label (str "dapp-" topic) + :state :default + :action :icon + :blur? false + :customization-color color + :right-component (fn [dapp] + [rn/pressable + {:on-press (fn [] + (on-dapp-disconnect-press + wallet-account + dapp))} + [quo/icon :i/disconnect + {:color (colors/theme-colors + colors/neutral-50 + colors/neutral-40 + theme) + :accessibility-label :icon}]])}]) + :separator [rn/view {:style (style/separator theme)}]}]])])) diff --git a/src/status_im/contexts/wallet/wallet_connect/effects.cljs b/src/status_im/contexts/wallet/wallet_connect/effects.cljs index cb1ad13d54..89cbf1f99e 100644 --- a/src/status_im/contexts/wallet/wallet_connect/effects.cljs +++ b/src/status_im/contexts/wallet/wallet_connect/effects.cljs @@ -29,6 +29,14 @@ (js->clj :keywordize-keys true) handler))))) +(rf/reg-fx + :effects.wallet-connect/fetch-pairings + (fn [{:keys [web3-wallet on-success on-fail]}] + (-> (.. web3-wallet -core -pairing) + (.getPairings) + (promesa/then on-success) + (promesa/catch on-fail)))) + (rf/reg-fx :effects.wallet-connect/pair (fn [{:keys [web3-wallet url on-success on-fail]}] @@ -37,6 +45,21 @@ (promesa/then on-success) (promesa/catch on-fail)))) +(rf/reg-fx + :effects.wallet-connect/disconnect + (fn [{:keys [web3-wallet topic on-success on-fail]}] + (-> (.. web3-wallet -core -pairing) + (.disconnect (clj->js {:topic topic})) + (promesa/then on-success) + (promesa/catch on-fail)))) + +(rf/reg-fx + :effects.wallet-connect/fetch-active-sessions + (fn [{:keys [web3-wallet on-success on-fail]}] + (-> (.getActiveSessions web3-wallet) + (promesa/then on-success) + (promesa/catch on-fail)))) + (rf/reg-fx :effects.wallet-connect/approve-session (fn [{:keys [web3-wallet proposal supported-namespaces on-success on-fail]}] diff --git a/src/status_im/contexts/wallet/wallet_connect/events.cljs b/src/status_im/contexts/wallet/wallet_connect/events.cljs index 55f93292b8..4f9f6ca0be 100644 --- a/src/status_im/contexts/wallet/wallet_connect/events.cljs +++ b/src/status_im/contexts/wallet/wallet_connect/events.cljs @@ -15,7 +15,13 @@ :wallet-connect/on-init-success (fn [{:keys [db]} [web3-wallet]] {:db (assoc db :wallet-connect/web3-wallet web3-wallet) - :fx [[:dispatch [:wallet-connect/register-event-listeners]]]})) + :fx [[:dispatch [:wallet-connect/register-event-listeners]] + [:effects.wallet-connect/fetch-pairings + {:web3-wallet web3-wallet + :on-fail #(log/error "Failed to get dApp pairings" {:error %}) + :on-success (fn [data] + (rf/dispatch [:wallet-connect/set-pairings + (js->clj data :keywordize-keys true)]))}]]})) (rf/reg-event-fx :wallet-connect/register-event-listeners @@ -43,6 +49,29 @@ (fn [{:keys [db]}] {:db (dissoc db :wallet-connect/current-proposal)})) +(rf/reg-event-fx + :wallet-connect/set-pairings + (fn [{:keys [db]} [pairings]] + {:db (assoc db :wallet-connect/pairings pairings)})) + +(rf/reg-event-fx + :wallet-connect/remove-pairing-by-topic + (fn [{:keys [db]} [topic]] + {:db (update db + :wallet-connect/pairings + (fn [pairings] + (remove #(= (:topic %) topic) pairings)))})) + +(rf/reg-event-fx + :wallet-connect/disconnect-dapp + (fn [{:keys [db]} [{:keys [topic on-success on-fail]}]] + (let [web3-wallet (get db :wallet-connect/web3-wallet)] + {:fx [[:effects.wallet-connect/disconnect + {:web3-wallet web3-wallet + :topic topic + :on-fail on-fail + :on-success on-success}]]}))) + (rf/reg-event-fx :wallet-connect/pair (fn [{:keys [db]} [url]] @@ -53,6 +82,15 @@ :on-fail #(log/error "Failed to pair with dApp" {:error %}) :on-success #(log/info "dApp paired successfully")}]]}))) +(rf/reg-event-fx + :wallet-connect/fetch-active-sessions + (fn [{:keys [db]}] + (let [web3-wallet (get db :wallet-connect/web3-wallet)] + {:fx [[:effects.wallet-connect/fetch-active-sessions + {:web3-wallet web3-wallet + :on-fail #(log/error "Failed to get active sessions" {:error %}) + :on-success #(log/info "Got active sessions successfully" {:sessions %})}]]}))) + (rf/reg-event-fx :wallet-connect/approve-session (fn [{:keys [db]}] @@ -68,11 +106,12 @@ ;; - wallet account dapps -> account that is selected address (-> accounts keys first) formatted-address (str (first crosschain-ids) ":" address) - supported-namespaces (clj->js {:eip155 - {:chains crosschain-ids - :methods constants/wallet-connect-supported-methods - :events constants/wallet-connect-supported-events - :accounts [formatted-address]}})] + supported-namespaces (clj->js + {:eip155 + {:chains crosschain-ids + :methods constants/wallet-connect-supported-methods + :events constants/wallet-connect-supported-events + :accounts [formatted-address]}})] {:fx [[:effects.wallet-connect/approve-session {:web3-wallet web3-wallet :proposal current-proposal diff --git a/src/status_im/subs/root.cljs b/src/status_im/subs/root.cljs index 3b13ef3e04..84794229bf 100644 --- a/src/status_im/subs/root.cljs +++ b/src/status_im/subs/root.cljs @@ -169,6 +169,7 @@ ;;wallet-connect (reg-root-key-sub :wallet-connect/web3-wallet :wallet-connect/web3-wallet) (reg-root-key-sub :wallet-connect/current-proposal :wallet-connect/current-proposal) +(reg-root-key-sub :wallet-connect/pairings :wallet-connect/pairings) ;;biometrics (reg-root-key-sub :biometrics :biometrics) diff --git a/translations/en.json b/translations/en.json index 5a4315119d..9c3c6ab0eb 100644 --- a/translations/en.json +++ b/translations/en.json @@ -2412,6 +2412,7 @@ "keypair-title": "{{name}}'s default key pair", "about": "About", "no-permissions": "No permissions", + "connected-dapps": "Connected dApps", "no-dapps": "No connected dApps", "days": "Days", "add-account": "Add account", @@ -2467,6 +2468,9 @@ "scan-sync-code-placeholder": "cs2:4FH...", "visit-dapp": "Visit dApp", "disconnect-dapp": "Disconnect dApp", + "disconnect-dapp-confirmation": "Are you sure you want to disconnect {{dapp}}?", + "disconnect-dapp-success": "{{dapp}} disconnected from {{account}}", + "disconnect-dapp-fail": "Failed to disconnect {{dapp}} from {{account}}", "edit-account": "Edit account", "share-account": "Share account", "remove-account": "Remove account",