feat(shell): add share qr wallet accounts feature(#18511)
Co-authored-by: Paul Fitzgerald <paulfitz99@gmail.com>
This commit is contained in:
parent
92dcd1140b
commit
3e787ff112
|
@ -1,5 +0,0 @@
|
||||||
{:lint-as
|
|
||||||
{rewrite-clj.zip/subedit-> clojure.core/->
|
|
||||||
rewrite-clj.zip/subedit->> clojure.core/->>
|
|
||||||
rewrite-clj.zip/edit-> clojure.core/->
|
|
||||||
rewrite-clj.zip/edit->> clojure.core/->>}}
|
|
|
@ -1 +0,0 @@
|
||||||
{:hooks {:analyze-call {taoensso.encore/defalias taoensso.encore/defalias}}}
|
|
|
@ -1,16 +0,0 @@
|
||||||
(ns taoensso.encore
|
|
||||||
(:require
|
|
||||||
[clj-kondo.hooks-api :as hooks]))
|
|
||||||
|
|
||||||
(defn defalias [{:keys [node]}]
|
|
||||||
(let [[sym-raw src-raw] (rest (:children node))
|
|
||||||
src (if src-raw src-raw sym-raw)
|
|
||||||
sym (if src-raw
|
|
||||||
sym-raw
|
|
||||||
(symbol (name (hooks/sexpr src))))]
|
|
||||||
{:node (with-meta
|
|
||||||
(hooks/list-node
|
|
||||||
[(hooks/token-node 'def)
|
|
||||||
(hooks/token-node (hooks/sexpr sym))
|
|
||||||
(hooks/token-node (hooks/sexpr src))])
|
|
||||||
(meta src))}))
|
|
|
@ -0,0 +1,75 @@
|
||||||
|
(ns status-im.contexts.shell.share.profile.view
|
||||||
|
(:require
|
||||||
|
[clojure.string :as string]
|
||||||
|
[legacy.status-im.ui.components.list-selection :as list-selection]
|
||||||
|
[quo.core :as quo]
|
||||||
|
[quo.foundations.colors :as colors]
|
||||||
|
[react-native.core :as rn]
|
||||||
|
[status-im.common.qr-codes.view :as qr-codes]
|
||||||
|
[status-im.contexts.profile.utils :as profile.utils]
|
||||||
|
[status-im.contexts.shell.share.style :as style]
|
||||||
|
[utils.address :as address]
|
||||||
|
[utils.i18n :as i18n]
|
||||||
|
[utils.re-frame :as rf]))
|
||||||
|
|
||||||
|
(defn profile-tab
|
||||||
|
[]
|
||||||
|
(let [{:keys [emoji-hash
|
||||||
|
customization-color
|
||||||
|
universal-profile-url]
|
||||||
|
:as profile} (rf/sub [:profile/profile])
|
||||||
|
abbreviated-url (address/get-abbreviated-profile-url
|
||||||
|
universal-profile-url)
|
||||||
|
emoji-hash-string (string/join emoji-hash)]
|
||||||
|
[:<>
|
||||||
|
[rn/view {:style style/qr-code-container}
|
||||||
|
[qr-codes/share-qr-code
|
||||||
|
{:type :profile
|
||||||
|
:unblur-on-android? true
|
||||||
|
:qr-data universal-profile-url
|
||||||
|
:qr-data-label-shown abbreviated-url
|
||||||
|
:on-share-press #(list-selection/open-share {:message universal-profile-url})
|
||||||
|
:on-text-press #(rf/dispatch [:share/copy-text-and-show-toast
|
||||||
|
{:text-to-copy universal-profile-url
|
||||||
|
:post-copy-message (i18n/label :t/link-to-profile-copied)}])
|
||||||
|
:on-text-long-press #(rf/dispatch [:share/copy-text-and-show-toast
|
||||||
|
{:text-to-copy universal-profile-url
|
||||||
|
:post-copy-message (i18n/label :t/link-to-profile-copied)}])
|
||||||
|
:profile-picture (:uri (profile.utils/photo profile))
|
||||||
|
:full-name (profile.utils/displayed-name profile)
|
||||||
|
:customization-color customization-color}]]
|
||||||
|
|
||||||
|
[rn/view {:style style/emoji-hash-container}
|
||||||
|
[rn/view {:style style/emoji-address-container}
|
||||||
|
[rn/view {:style style/emoji-address-column}
|
||||||
|
[quo/text
|
||||||
|
{:size :paragraph-2
|
||||||
|
:weight :medium
|
||||||
|
:style style/emoji-hash-label}
|
||||||
|
(i18n/label :t/emoji-hash)]
|
||||||
|
[rn/touchable-highlight
|
||||||
|
{:active-opacity 1
|
||||||
|
:underlay-color colors/neutral-80-opa-1-blur
|
||||||
|
:background-color :transparent
|
||||||
|
:on-press #(rf/dispatch [:share/copy-text-and-show-toast
|
||||||
|
{:text-to-copy emoji-hash-string
|
||||||
|
:post-copy-message (i18n/label :t/emoji-hash-copied)}])
|
||||||
|
:on-long-press #(rf/dispatch [:share/copy-text-and-show-toast
|
||||||
|
{:text-to-copy emoji-hash-string
|
||||||
|
:post-copy-message (i18n/label :t/emoji-hash-copied)}])}
|
||||||
|
[rn/text {:style style/emoji-hash-content} emoji-hash-string]]]]
|
||||||
|
[rn/view {:style style/emoji-share-button-container}
|
||||||
|
[quo/button
|
||||||
|
{:icon-only? true
|
||||||
|
:type :grey
|
||||||
|
:background :blur
|
||||||
|
:size 32
|
||||||
|
:accessibility-label :link-to-profile
|
||||||
|
:container-style {:margin-right 12}
|
||||||
|
:on-press #(rf/dispatch [:share/copy-text-and-show-toast
|
||||||
|
{:text-to-copy emoji-hash-string
|
||||||
|
:post-copy-message (i18n/label :t/emoji-hash-copied)}])
|
||||||
|
:on-long-press #(rf/dispatch [:share/copy-text-and-show-toast
|
||||||
|
{:text-to-copy emoji-hash-string
|
||||||
|
:post-copy-message (i18n/label :t/emoji-hash-copied)}])}
|
||||||
|
:i/copy]]]]))
|
|
@ -1,18 +1,14 @@
|
||||||
(ns status-im.contexts.shell.share.view
|
(ns status-im.contexts.shell.share.view
|
||||||
(:require
|
(:require
|
||||||
[clojure.string :as string]
|
|
||||||
[legacy.status-im.ui.components.list-selection :as list-selection]
|
|
||||||
[quo.core :as quo]
|
[quo.core :as quo]
|
||||||
[quo.foundations.colors :as colors]
|
|
||||||
[react-native.blur :as blur]
|
[react-native.blur :as blur]
|
||||||
[react-native.core :as rn]
|
[react-native.core :as rn]
|
||||||
[react-native.platform :as platform]
|
[react-native.platform :as platform]
|
||||||
[react-native.safe-area :as safe-area]
|
[react-native.safe-area :as safe-area]
|
||||||
[reagent.core :as reagent]
|
[reagent.core :as reagent]
|
||||||
[status-im.common.qr-codes.view :as qr-codes]
|
[status-im.contexts.shell.share.profile.view :as profile-view]
|
||||||
[status-im.contexts.profile.utils :as profile.utils]
|
|
||||||
[status-im.contexts.shell.share.style :as style]
|
[status-im.contexts.shell.share.style :as style]
|
||||||
[utils.address :as address]
|
[status-im.contexts.shell.share.wallet.view :as wallet-view]
|
||||||
[utils.i18n :as i18n]
|
[utils.i18n :as i18n]
|
||||||
[utils.re-frame :as rf]))
|
[utils.re-frame :as rf]))
|
||||||
|
|
||||||
|
@ -43,72 +39,6 @@
|
||||||
:style style/header-heading}
|
:style style/header-heading}
|
||||||
(i18n/label :t/share)]])
|
(i18n/label :t/share)]])
|
||||||
|
|
||||||
(defn profile-tab
|
|
||||||
[]
|
|
||||||
(let [{:keys [emoji-hash
|
|
||||||
customization-color
|
|
||||||
universal-profile-url]
|
|
||||||
:as profile} (rf/sub [:profile/profile])
|
|
||||||
abbreviated-url (address/get-abbreviated-profile-url
|
|
||||||
universal-profile-url)
|
|
||||||
emoji-hash-string (string/join emoji-hash)]
|
|
||||||
[:<>
|
|
||||||
[rn/view {:style style/qr-code-container}
|
|
||||||
[qr-codes/share-qr-code
|
|
||||||
{:type :profile
|
|
||||||
:unblur-on-android? true
|
|
||||||
:qr-data universal-profile-url
|
|
||||||
:qr-data-label-shown abbreviated-url
|
|
||||||
:on-share-press #(list-selection/open-share {:message universal-profile-url})
|
|
||||||
:on-text-press #(rf/dispatch [:share/copy-text-and-show-toast
|
|
||||||
{:text-to-copy universal-profile-url
|
|
||||||
:post-copy-message (i18n/label :t/link-to-profile-copied)}])
|
|
||||||
:on-text-long-press #(rf/dispatch [:share/copy-text-and-show-toast
|
|
||||||
{:text-to-copy universal-profile-url
|
|
||||||
:post-copy-message (i18n/label :t/link-to-profile-copied)}])
|
|
||||||
:profile-picture (:uri (profile.utils/photo profile))
|
|
||||||
:full-name (profile.utils/displayed-name profile)
|
|
||||||
:customization-color customization-color}]]
|
|
||||||
|
|
||||||
[rn/view {:style style/emoji-hash-container}
|
|
||||||
[rn/view {:style style/emoji-address-container}
|
|
||||||
[rn/view {:style style/emoji-address-column}
|
|
||||||
[quo/text
|
|
||||||
{:size :paragraph-2
|
|
||||||
:weight :medium
|
|
||||||
:style style/emoji-hash-label}
|
|
||||||
(i18n/label :t/emoji-hash)]
|
|
||||||
[rn/touchable-highlight
|
|
||||||
{:active-opacity 1
|
|
||||||
:underlay-color colors/neutral-80-opa-1-blur
|
|
||||||
:background-color :transparent
|
|
||||||
:on-press #(rf/dispatch [:share/copy-text-and-show-toast
|
|
||||||
{:text-to-copy emoji-hash-string
|
|
||||||
:post-copy-message (i18n/label :t/emoji-hash-copied)}])
|
|
||||||
:on-long-press #(rf/dispatch [:share/copy-text-and-show-toast
|
|
||||||
{:text-to-copy emoji-hash-string
|
|
||||||
:post-copy-message (i18n/label :t/emoji-hash-copied)}])}
|
|
||||||
[rn/text {:style style/emoji-hash-content} emoji-hash-string]]]]
|
|
||||||
[rn/view {:style style/emoji-share-button-container}
|
|
||||||
[quo/button
|
|
||||||
{:icon-only? true
|
|
||||||
:type :grey
|
|
||||||
:background :blur
|
|
||||||
:size 32
|
|
||||||
:accessibility-label :link-to-profile
|
|
||||||
:container-style {:margin-right 12}
|
|
||||||
:on-press #(rf/dispatch [:share/copy-text-and-show-toast
|
|
||||||
{:text-to-copy emoji-hash-string
|
|
||||||
:post-copy-message (i18n/label :t/emoji-hash-copied)}])
|
|
||||||
:on-long-press #(rf/dispatch [:share/copy-text-and-show-toast
|
|
||||||
{:text-to-copy emoji-hash-string
|
|
||||||
:post-copy-message (i18n/label :t/emoji-hash-copied)}])}
|
|
||||||
:i/copy]]]]))
|
|
||||||
|
|
||||||
(defn wallet-tab
|
|
||||||
[]
|
|
||||||
[rn/text {:style style/wip-style} "not implemented"])
|
|
||||||
|
|
||||||
(defn tab-content
|
(defn tab-content
|
||||||
[]
|
[]
|
||||||
(let [selected-tab (reagent/atom :profile)]
|
(let [selected-tab (reagent/atom :profile)]
|
||||||
|
@ -126,8 +56,8 @@
|
||||||
{:id :wallet
|
{:id :wallet
|
||||||
:label (i18n/label :t/wallet)}]}]]
|
:label (i18n/label :t/wallet)}]}]]
|
||||||
(if (= @selected-tab :profile)
|
(if (= @selected-tab :profile)
|
||||||
[profile-tab]
|
[profile-view/profile-tab]
|
||||||
[wallet-tab])])))
|
[wallet-view/wallet-tab])])))
|
||||||
|
|
||||||
(defn view
|
(defn view
|
||||||
[]
|
[]
|
||||||
|
|
|
@ -0,0 +1,44 @@
|
||||||
|
(ns status-im.contexts.shell.share.wallet.component-spec
|
||||||
|
(:require
|
||||||
|
[status-im.contexts.shell.share.wallet.view :as wallet-view]
|
||||||
|
status-im.contexts.wallet.events
|
||||||
|
[test-helpers.component :as h]))
|
||||||
|
|
||||||
|
(defn render-wallet-view
|
||||||
|
[]
|
||||||
|
(let [component-rendered (h/render [wallet-view/wallet-tab])
|
||||||
|
rerender-fn (h/get-rerender-fn component-rendered)
|
||||||
|
share-qr-code (h/get-by-label-text :share-qr-code)]
|
||||||
|
;; Fires on-layout since it's needed to render the content
|
||||||
|
(h/fire-event :layout share-qr-code #js {:nativeEvent #js {:layout #js {:width 500}}})
|
||||||
|
(rerender-fn [wallet-view/wallet-tab])))
|
||||||
|
|
||||||
|
(h/describe "share wallet addresses"
|
||||||
|
(h/setup-restorable-re-frame)
|
||||||
|
(h/before-each
|
||||||
|
(fn []
|
||||||
|
(h/setup-subs {:dimensions/window-width 500
|
||||||
|
:mediaserver/port 200
|
||||||
|
:wallet/accounts [{:address "0x707f635951193ddafbb40971a0fcaab8a6415160"
|
||||||
|
:name "Wallet One"
|
||||||
|
:emoji "😆"
|
||||||
|
:color :blue}]})))
|
||||||
|
|
||||||
|
(h/test "should display the the wallet tab"
|
||||||
|
(render-wallet-view)
|
||||||
|
(h/wait-for #(h/is-truthy (h/get-by-text "Wallet One"))))
|
||||||
|
|
||||||
|
(h/test "should display the the legacy account"
|
||||||
|
(render-wallet-view)
|
||||||
|
(-> (h/wait-for #(h/get-by-label-text :share-qr-code-legacy-tab))
|
||||||
|
(.then (fn []
|
||||||
|
(h/fire-event :press (h/get-by-label-text :share-qr-code-legacy-tab))
|
||||||
|
(-> (h/wait-for #(h/is-falsy (h/query-by-text "eth:"))))))))
|
||||||
|
|
||||||
|
(h/test "should display the the multichain account"
|
||||||
|
(render-wallet-view)
|
||||||
|
(-> (h/wait-for #(h/get-by-label-text :share-qr-code-multichain-tab))
|
||||||
|
(.then (fn []
|
||||||
|
(h/fire-event :press (h/get-by-label-text :share-qr-code-multichain-tab))
|
||||||
|
(-> (h/wait-for #(h/is-truthy (h/query-by-text "eth:")))))))))
|
||||||
|
|
|
@ -0,0 +1,91 @@
|
||||||
|
(ns status-im.contexts.shell.share.wallet.view
|
||||||
|
(:require
|
||||||
|
[quo.core :as quo]
|
||||||
|
[react-native.core :as rn]
|
||||||
|
[react-native.platform :as platform]
|
||||||
|
[react-native.share :as share]
|
||||||
|
[reagent.core :as reagent]
|
||||||
|
[status-im.contexts.shell.share.style :as style]
|
||||||
|
[status-im.contexts.wallet.common.sheets.network-preferences.view :as network-preferences]
|
||||||
|
[status-im.contexts.wallet.common.utils :as utils]
|
||||||
|
[utils.i18n :as i18n]
|
||||||
|
[utils.image-server :as image-server]
|
||||||
|
[utils.re-frame :as rf]))
|
||||||
|
|
||||||
|
(def qr-size 500)
|
||||||
|
|
||||||
|
(defn- share-action
|
||||||
|
[address share-title]
|
||||||
|
(share/open
|
||||||
|
(if platform/ios?
|
||||||
|
{:activityItemSources [{:placeholderItem {:type "text"
|
||||||
|
:content address}
|
||||||
|
:item {:default {:type "text"
|
||||||
|
:content
|
||||||
|
address}}
|
||||||
|
:linkMetadata {:title share-title}}]}
|
||||||
|
{:title share-title
|
||||||
|
:subject share-title
|
||||||
|
:message address
|
||||||
|
:isNewTask true})))
|
||||||
|
|
||||||
|
(defn- open-preferences
|
||||||
|
[selected-networks]
|
||||||
|
(rf/dispatch [:show-bottom-sheet
|
||||||
|
{:theme :dark
|
||||||
|
:shell? true
|
||||||
|
:content
|
||||||
|
(fn []
|
||||||
|
[network-preferences/view
|
||||||
|
{:blur? true
|
||||||
|
:selected-networks (set selected-networks)
|
||||||
|
:on-save (fn [chain-ids]
|
||||||
|
(rf/dispatch [:hide-bottom-sheet])
|
||||||
|
(reset! selected-networks (map #(get utils/id->network %)
|
||||||
|
chain-ids)))}])}]))
|
||||||
|
(defn wallet-qr-code-item
|
||||||
|
[account width index]
|
||||||
|
(let [selected-networks (reagent/atom [:ethereum :optimism :arbitrum])
|
||||||
|
wallet-type (reagent/atom :wallet-legacy)]
|
||||||
|
(fn []
|
||||||
|
(let [share-title (str (:name account) " " (i18n/label :t/address))
|
||||||
|
qr-url (utils/get-wallet-qr {:wallet-type @wallet-type
|
||||||
|
:selected-networks @selected-networks
|
||||||
|
:address (:address account)})
|
||||||
|
qr-media-server-uri (image-server/get-qr-image-uri-for-any-url
|
||||||
|
{:url qr-url
|
||||||
|
:port (rf/sub [:mediaserver/port])
|
||||||
|
:qr-size qr-size
|
||||||
|
:error-level :highest})]
|
||||||
|
[rn/view {:style {:width width :margin-left (if (zero? index) 0 -30)}}
|
||||||
|
[rn/view {:style style/qr-code-container}
|
||||||
|
[quo/share-qr-code
|
||||||
|
{:type @wallet-type
|
||||||
|
:qr-image-uri qr-media-server-uri
|
||||||
|
:qr-data qr-url
|
||||||
|
:networks @selected-networks
|
||||||
|
:on-share-press #(share-action qr-url share-title)
|
||||||
|
:profile-picture nil
|
||||||
|
:unblur-on-android? true
|
||||||
|
:full-name (:name account)
|
||||||
|
:customization-color (:color account)
|
||||||
|
:emoji (:emoji account)
|
||||||
|
:on-multichain-press #(reset! wallet-type :wallet-multichain)
|
||||||
|
:on-legacy-press #(reset! wallet-type :wallet-legacy)
|
||||||
|
:on-settings-press #(open-preferences @selected-networks)}]]]))))
|
||||||
|
|
||||||
|
(defn wallet-tab
|
||||||
|
[]
|
||||||
|
(let [accounts (rf/sub [:wallet/accounts])
|
||||||
|
width (rf/sub [:dimensions/window-width])]
|
||||||
|
[rn/flat-list
|
||||||
|
{:horizontal true
|
||||||
|
:deceleration-rate 0.9
|
||||||
|
:snap-to-alignment "start"
|
||||||
|
:snap-to-interval (- width 30)
|
||||||
|
:disable-interval-momentum true
|
||||||
|
:scroll-event-throttle 64
|
||||||
|
:data accounts
|
||||||
|
:directional-lock-enabled true
|
||||||
|
:render-fn (fn [account index]
|
||||||
|
(wallet-qr-code-item account width index))}]))
|
|
@ -19,15 +19,16 @@
|
||||||
[address share-title]
|
[address share-title]
|
||||||
(share/open
|
(share/open
|
||||||
(if platform/ios?
|
(if platform/ios?
|
||||||
{:activity-item-sources [{:placeholder-item {:type "text"
|
{:activityItemSources [{:placeholderItem {:type "text"
|
||||||
:content address}
|
:content address}
|
||||||
:item {:default {:type "text"
|
:item {:default {:type "text"
|
||||||
:content
|
:content
|
||||||
address}}
|
address}}
|
||||||
:link-metadata {:title share-title}}]}
|
:linkMetadata {:title share-title}}]}
|
||||||
{:title share-title
|
{:title share-title
|
||||||
:subject share-title
|
:subject share-title
|
||||||
:message address})))
|
:message address
|
||||||
|
:isNewTask true})))
|
||||||
|
|
||||||
(defn- open-preferences
|
(defn- open-preferences
|
||||||
[selected-networks]
|
[selected-networks]
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
[status-im.common.floating-button-page.component-spec]
|
[status-im.common.floating-button-page.component-spec]
|
||||||
[status-im.contexts.chat.messenger.messages.content.audio.component-spec]
|
[status-im.contexts.chat.messenger.messages.content.audio.component-spec]
|
||||||
[status-im.contexts.communities.actions.community-options.component-spec]
|
[status-im.contexts.communities.actions.community-options.component-spec]
|
||||||
|
[status-im.contexts.shell.share.wallet.component-spec]
|
||||||
[status-im.contexts.wallet.add-address-to-watch.component-spec]
|
[status-im.contexts.wallet.add-address-to-watch.component-spec]
|
||||||
[status-im.contexts.wallet.add-address-to-watch.confirm-address.component-spec]
|
[status-im.contexts.wallet.add-address-to-watch.confirm-address.component-spec]
|
||||||
[status-im.contexts.wallet.create-account.edit-derivation-path.component-spec]
|
[status-im.contexts.wallet.create-account.edit-derivation-path.component-spec]
|
||||||
|
|
|
@ -11,6 +11,10 @@ jest.mock('react-native-fs', () => ({
|
||||||
default: {},
|
default: {},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
jest.mock('react-native-share', () => ({
|
||||||
|
default: {},
|
||||||
|
}));
|
||||||
|
|
||||||
jest.mock('react-native-navigation', () => ({
|
jest.mock('react-native-navigation', () => ({
|
||||||
getNavigationConstants: () => ({ constants: [] }),
|
getNavigationConstants: () => ({ constants: [] }),
|
||||||
Navigation: {
|
Navigation: {
|
||||||
|
|
Loading…
Reference in New Issue