[#17023] Share qr code variants (#17736)

* Align docstring

* Create share-qr-code component

* Remove empty style file

* Implement common.share-qr-code including call to media server

* Add share-qr-code preview screen

* Use share-qr-code component in shell's share screen

* Add tests and some fixes
This commit is contained in:
Ulises Manuel 2023-10-31 12:35:28 -06:00 committed by GitHub
parent ca822ff51d
commit 4a874ce48d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 709 additions and 198 deletions

View File

@ -64,11 +64,11 @@
(defn- view-internal
"[preview-list opts items]
opts
{:type :user/:communities/:accounts/:tokens/:collectibles/:dapps/:network
:size :size-32 | :size-24 | :size-20 | :size-16 | :size-14
:number number of items in the list (optional)
:blur? overflow-label blur?}
items preview list items (only 4 items is required for preview)
{:type :user/:communities/:accounts/:tokens/:collectibles/:dapps/:network
:size :size-32 | :size-24 | :size-20 | :size-16 | :size-14
:number number of items in the list (optional)
:blur? overflow-label blur?}
items preview list items (only 4 items is required for preview)
"
[{:keys [type size number blur?]} items]
(let [size-key (if (contains? properties/sizes size) size :size-24)

View File

@ -1,34 +1,119 @@
(ns quo.components.share.share-qr-code.component-spec
(:require
[quo.components.share.share-qr-code.view :as share-qr-code]
[test-helpers.component :as h]))
(:require [quo.components.share.share-qr-code.view :as share-qr-code]
[test-helpers.component :as h]))
(defn render-share-qr-code
[{share-qr-type :type :as props}]
(let [component-rendered (h/render [share-qr-code/view {:type share-qr-type}])
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 [share-qr-code/view props])))
(h/describe "Share QR Code component"
(h/test "renders share qr code component"
(h/render [share-qr-code/view
{:link-title " A test title"}])
(-> (js/expect (h/get-by-text "A test title"))
(.toBeTruthy)))
(h/describe "Renders share-qr-code component in all types"
(let [qr-label "Text shown below QR"]
(h/test "Profile"
(render-share-qr-code {:type :profile
:qr-data qr-label})
(h/is-truthy (h/get-by-text qr-label)))
(h/test "renders share qr code url"
(h/render [share-qr-code/view
{:qr-url " A test url"}])
(-> (js/expect (h/get-by-text "A test url"))
(.toBeTruthy)))
(h/test "Wallet Legacy"
(render-share-qr-code {:type :wallet-legacy
:qr-data qr-label
:emoji "👻"})
(h/is-truthy (h/get-by-text qr-label)))
(h/test "on press link event fires"
(let [event (h/mock-fn)]
(h/render [share-qr-code/view
{:url-on-press event
:qr-url " A test url"}])
(h/fire-event :press (h/get-by-text "A test url"))
(-> (js/expect event)
(.toHaveBeenCalledTimes 1))))
(h/test "Wallet Multichain"
(render-share-qr-code {:type :wallet-multichain
:qr-data qr-label
:emoji "👻"})
(h/is-truthy (h/get-by-text qr-label)))))
(h/test "on press share event fires"
(let [event (h/mock-fn)]
(h/render [share-qr-code/view
{:share-on-press event}])
(h/fire-event :press (h/get-by-label-text :share-profile))
(-> (js/expect event)
(.toHaveBeenCalledTimes 1)))))
(h/describe "Fires all events for all types"
(letfn [(test-fire-events [props test-seq]
(doseq [{:keys [test-name event-name
callback-prop-key
accessibility-label]} test-seq
:let [event-fn (h/mock-fn)]]
(h/test test-name
(render-share-qr-code (assoc props callback-prop-key event-fn))
(h/fire-event event-name (h/get-by-label-text accessibility-label))
(h/was-called-times event-fn 1))))]
(h/describe "Profile"
(test-fire-events
{:type :profile}
[{:test-name "Text pressed"
:accessibility-label :share-qr-code-info-text
:event-name :press
:callback-prop-key :on-text-press}
{:test-name "Text long pressed"
:accessibility-label :share-qr-code-info-text
:event-name :long-press
:callback-prop-key :on-text-long-press}
{:test-name "Share button"
:accessibility-label :link-to-profile
:event-name :press
:callback-prop-key :on-share-press}]))
(h/describe "Wallet Legacy"
(test-fire-events
{:type :wallet-legacy :emoji "👽"}
[{:test-name "Text pressed"
:accessibility-label :share-qr-code-info-text
:event-name :press
:callback-prop-key :on-text-press}
{:test-name "Text long pressed"
:accessibility-label :share-qr-code-info-text
:event-name :long-press
:callback-prop-key :on-text-long-press}
{:test-name "Share button pressed"
:accessibility-label :link-to-profile
:event-name :press
:callback-prop-key :on-share-press}
{:test-name "Info icon pressed"
:accessibility-label :share-qr-code-info-icon
:event-name :press
:callback-prop-key :on-info-press}
{:test-name "Legacy tab pressed"
:accessibility-label :share-qr-code-legacy-tab
:event-name :press
:callback-prop-key :on-legacy-press}
{:test-name "Multichain tab pressed"
:accessibility-label :share-qr-code-multichain-tab
:event-name :press
:callback-prop-key :on-multichain-press}]))
(h/describe "Wallet Multichain"
(test-fire-events
{:type :wallet-multichain :emoji "👽"}
[{:test-name "Text pressed"
:accessibility-label :share-qr-code-info-text
:event-name :press
:callback-prop-key :on-text-press}
{:test-name "Text long pressed"
:accessibility-label :share-qr-code-info-text
:event-name :long-press
:callback-prop-key :on-text-long-press}
{:test-name "Share button pressed"
:accessibility-label :link-to-profile
:event-name :press
:callback-prop-key :on-share-press}
{:test-name "Info icon pressed"
:accessibility-label :share-qr-code-info-icon
:event-name :press
:callback-prop-key :on-info-press}
{:test-name "Legacy tab pressed"
:accessibility-label :share-qr-code-legacy-tab
:event-name :press
:callback-prop-key :on-legacy-press}
{:test-name "Multichain tab pressed"
:accessibility-label :share-qr-code-multichain-tab
:event-name :press
:callback-prop-key :on-multichain-press}
{:test-name "Settings pressed"
:accessibility-label :share-qr-code-settings
:event-name :press
:callback-prop-key :on-settings-press}])))))

View File

@ -1,45 +1,116 @@
(ns quo.components.share.share-qr-code.style
(:require
[quo.foundations.colors :as colors]))
(:require [quo.foundations.colors :as colors]))
(def qr-code-container
{:padding-top 12
:padding-horizontal 12
:padding-bottom 8
:border-radius 16
:background-color colors/white-opa-5
:flex-direction :column
:justify-content :center
:align-items :center})
(def outer-container
{:border-radius 16
:width "100%"
:overflow :hidden})
(def profile-address-column
{:margin-horizontal :auto
:flex 4})
(def overlay-color colors/white-opa-5)
(def profile-address-container
(def ^:private padding-horizontal 12)
(def content-container
{:z-index 1
:padding-horizontal padding-horizontal
:padding-top 12
:padding-bottom 8})
;;; Header
(def header-container
{:flex-direction :row
:margin-bottom 12})
(def header-tab-active {:background-color colors/white-opa-20})
(def header-tab-inactive {:background-color colors/white-opa-5})
(def space-between-tabs {:width 8})
(def info-icon
{:margin-left :auto
:align-self :center})
(def info-icon-color colors/white-opa-40)
;;; QR code
(defn qr-code-size
[total-width]
(- total-width (* 2 padding-horizontal)))
;;; Bottom part
(def bottom-container
{:margin-top 8
:flex-direction :row
:justify-content :space-between})
(def title {:color colors/white-opa-40})
(def share-button-size 32)
(def ^:private share-button-gap 16)
(defn share-button-container
[alignment]
{:justify-content (if (= alignment :top) :flex-start :center)
:margin-left share-button-gap})
(defn data-text
[total-width]
{:width (- total-width (* 2 padding-horizontal) share-button-size share-button-gap)})
;;; Wallet variants
(def wallet-data-and-share-container
{:margin-top 2
:flex-direction :row
:justify-content :space-between})
(def wallet-legacy-container {:flex 1})
(def wallet-multichain-container {:flex 1 :margin-top 4})
(def wallet-multichain-networks
{:flex-direction :row
:justify-content :space-between
:margin-top 6})
:margin-bottom 8})
(def profile-address-content-container
{:padding-top 2
:align-self :flex-start})
(def wallet-multichain-data-container {:margin-top 4})
(def profile-address-content
{:color colors/white})
;;; Dashed line
(def divider-container
{:height 8
:margin-horizontal 4
:justify-content :center
:overflow :hidden})
(def profile-address-label
{:color colors/white-opa-40})
(def ^:private padding-for-divider (+ padding-horizontal 4))
(def ^:private dashed-line-width 2)
(def ^:private dashed-line-space 4)
(def share-button-container
{:flex 1
:flex-direction :column
:justify-content :center
:align-items :flex-end})
(def dashed-line
{:flex-direction :row
:margin-left -1})
(def icon-container
{:height 36
:flex-direction :row
:align-items :center
:justify-content :space-between
:padding-bottom 12})
(def line
{:background-color colors/white-opa-20
:width dashed-line-width
:height 1})
(def line-space
{:width dashed-line-space
:height 1})
(defn number-lines-and-spaces-to-fill
[component-width]
(let [line-and-space-width (+ dashed-line-width dashed-line-space)
width-to-fill (- component-width (* 2 padding-for-divider))
number-of-lines (* (/ width-to-fill line-and-space-width) 2)]
(inc (int number-of-lines))))
(def ^:private get-network-full-name
{"eth" :ethereum
"opt" :optimism
"arb1" :arbitrum})
(defn network-short-name-text
[network-short-name]
{:color (-> network-short-name
(get-network-full-name :unknown)
(colors/resolve-color nil))})

View File

@ -1,46 +1,249 @@
(ns quo.components.share.share-qr-code.view
(:require
[quo.components.buttons.button.view :as button]
[quo.components.markdown.text :as text]
[quo.components.share.qr-code.view :as qr-code]
[quo.components.share.share-qr-code.style :as style]
[quo.foundations.colors :as colors]
[react-native.blur :as blur]
[react-native.core :as rn]))
(:require [clojure.set]
[clojure.string :as string]
[oops.core :as oops]
[quo.components.buttons.button.view :as button]
[quo.components.icon :as icon]
[quo.components.list-items.preview-list.view :as preview-list]
[quo.components.markdown.text :as text]
[quo.components.share.qr-code.view :as qr-code]
[quo.components.share.share-qr-code.style :as style]
[quo.components.tabs.tab.view :as tab]
[quo.foundations.resources :as quo.resources]
[quo.theme]
[react-native.blur :as blur]
[react-native.core :as rn]
[react-native.platform :as platform]
[reagent.core :as reagent]
[utils.i18n :as i18n]))
(defn- line [] [rn/view {:style style/line}])
(defn- space [] [rn/view {:style style/line-space}])
(defn- dashed-line
[width]
(into [rn/view {:style style/dashed-line}]
(take (style/number-lines-and-spaces-to-fill width))
(cycle [[line] [space]])))
(defn- header
[{:keys [share-qr-type on-info-press on-legacy-press on-multichain-press]}]
[rn/view {:style style/header-container}
[tab/view
{:accessibility-label :share-qr-code-legacy-tab
:id :wallet-legacy-tab
:active-item-container-style style/header-tab-active
:item-container-style style/header-tab-inactive
:size 24
:active (= :wallet-legacy share-qr-type)
:on-press on-legacy-press}
(i18n/label :t/legacy)]
[rn/view {:style style/space-between-tabs}]
[tab/view
{:accessibility-label :share-qr-code-multichain-tab
:id :wallet-multichain-tab
:active-item-container-style style/header-tab-active
:item-container-style style/header-tab-inactive
:size 24
:active (= :wallet-multichain share-qr-type)
:on-press on-multichain-press}
(i18n/label :t/multichain)]
[rn/pressable
{:accessibility-label :share-qr-code-info-icon
:style style/info-icon
:on-press on-info-press
:hit-slop 6}
[icon/icon :i/info
{:size 20
:color style/info-icon-color}]]])
(defn- info-label
[share-qr-code-type]
[text/text {:size :paragraph-2 :weight :medium :style style/title}
(if (= share-qr-code-type :profile)
(i18n/label :t/link-to-profile)
(i18n/label :t/wallet-address))])
(defn- info-text
[{:keys [width on-press on-long-press ellipsize?]} qr-data-text]
[rn/pressable
{:accessibility-label :share-qr-code-info-text
:style (style/data-text width)
:on-press on-press
:on-long-press on-long-press}
[text/text
(cond-> {:size :paragraph-1
:weight :monospace}
ellipsize? (assoc :number-of-lines 1
:ellipsize-mode :middle))
qr-data-text]])
(defn- share-button
[{:keys [alignment on-press]}]
[rn/view {:style (style/share-button-container alignment)}
[button/button
{:icon-only? true
:type :grey
:background :blur
:size style/share-button-size
:accessibility-label :link-to-profile
:on-press on-press}
:i/share]])
(defn- network-colored-text
[network-short-name]
[text/text {:style (style/network-short-name-text network-short-name)}
(str network-short-name ":")])
(defn- wallet-multichain-colored-address
[full-address]
(let [[networks address] (as-> full-address $
(string/split $ ":")
[(butlast $) (last $)])
->network-hiccup-xf (map #(vector network-colored-text %))]
(as-> networks $
(into [:<>] ->network-hiccup-xf $)
(conj $ address))))
(defn- profile-bottom
[{:keys [component-width qr-data on-text-press on-text-long-press on-share-press share-qr-type]}]
[:<>
[rn/view
[info-label share-qr-type]
[info-text
{:width component-width
:ellipsize? true
:on-press on-text-press
:on-long-press on-text-long-press}
qr-data]]
[share-button
{:alignment :center
:on-press on-share-press}]])
(defn- wallet-legacy-bottom
[{:keys [share-qr-type component-width qr-data on-text-press on-text-long-press on-share-press]}]
[rn/view {:style style/wallet-legacy-container}
[info-label share-qr-type]
[rn/view {:style style/wallet-data-and-share-container}
[info-text
{:width component-width
:on-press on-text-press
:on-long-press on-text-long-press}
qr-data]
[share-button
{:alignment :top
:on-press on-share-press}]]])
(def ^:private known-networks #{:ethereum :optimism :arbitrum})
(defn- get-network-image-source
[network]
{:source (quo.resources/get-network (get known-networks network :unknown))})
(defn wallet-multichain-bottom
[{:keys [share-qr-type component-width qr-data on-text-press on-text-long-press
on-share-press networks on-settings-press]}]
[rn/view {:style style/wallet-multichain-container}
[rn/view {:style style/wallet-multichain-networks}
[preview-list/view {:type :network :size :size-32}
(map get-network-image-source networks)]
[button/button
{:icon-only? true
:type :grey
:background :blur
:size 32
:accessibility-label :share-qr-code-settings
:on-press on-settings-press}
:i/advanced]]
[rn/view {:style style/divider-container}
[dashed-line component-width]]
[rn/view {:style style/wallet-multichain-data-container}
[info-label share-qr-type]
[rn/view {:style style/wallet-data-and-share-container}
[info-text
{:width component-width
:on-press on-text-press
:on-long-press on-text-long-press}
[wallet-multichain-colored-address qr-data]]
[share-button
{:alignment :top
:on-press on-share-press}]]]])
(defn- share-qr-code
[{:keys [share-qr-type qr-image-uri component-width customization-color full-name
profile-picture emoji]
:as props}]
[rn/view {:style style/content-container}
(when (#{:wallet-legacy :wallet-multichain} share-qr-type)
[header props])
[quo.theme/provider {:theme :light}
[qr-code/view
{:qr-image-uri qr-image-uri
:size (style/qr-code-size component-width)
:avatar (if (= share-qr-type :profile)
:profile
:wallet-account)
:customization-color customization-color
:full-name full-name
:profile-picture profile-picture
:emoji emoji}]]
[rn/view {:style style/bottom-container}
(case share-qr-type
:profile [profile-bottom props]
:wallet-legacy [wallet-legacy-bottom props]
:wallet-multichain [wallet-multichain-bottom props]
nil)]])
(defn view
[{:keys [qr-image-uri link-title url-on-press url-on-long-press qr-url share-on-press]}]
[blur/ios-view
{:style style/qr-code-container
:blur-type :light}
[qr-code/view {:qr-image-uri qr-image-uri}]
[rn/view {:style style/profile-address-container}
[rn/view {:style style/profile-address-column}
[text/text
{:size :paragraph-2
:weight :medium
:style style/profile-address-label}
link-title]
[rn/touchable-highlight
{:active-opacity 1
:underlay-color colors/neutral-80-opa-1-blur
:background-color :transparent
:on-press url-on-press
:on-long-press url-on-long-press
:style style/profile-address-content-container}
[text/text
{:style style/profile-address-content
:size :paragraph-1
:weight :medium
:ellipsize-mode :middle
:number-of-lines 1}
qr-url]]]
[rn/view {:style style/share-button-container}
[button/button
{:icon-only? true
:type :grey
:background :blur
:size 32
:accessibility-label :share-profile
:on-press share-on-press}
:i/share]]]])
"Receives the following properties:
- type: :profile | :wallet-legacy | :wallet-multichain
- qr-image-uri: Image source value.
- qr-data: Text to show below the QR code.
- on-text-press: Callback for the `qr-data` text.
- on-text-long-press: Callback for the `qr-data` text.
- on-share-press: Callback for the share button.
- customization-color: Custom color for the QR code component.
- unblur-on-android?: [Android only] disables blur for this component.
Depending on the `type`, different properties are accepted:
`:profile`
- full-name: User full name.
- profile-picture: map ({:source image-source}) or any image source.
`:wallet-legacy`
- emoji: Emoji in a string to show in the QR code.
- on-info-press: Callback for the info icon.
- on-legacy-press: Callback for the legacy tab.
- on-multichain-press: Callback for the multichain tab.
`:wallet-multichain`
- networks: A vector of network names as keywords (`[:ethereum, :my-net, ...]`).
- on-settings-press: Callback for the settings button.
- emoji: Emoji in a string to show in the QR code.
- on-info-press: Callback for the info icon.
- on-legacy-press: Callback for the legacy tab.
- on-multichain-press: Callback for the multichain tab.
WARNING on Android:
Sometimes while using a blur layer on top of another on Android, this component looks
bad because of the `blur/view`, so we can set `unblur-on-android? true` to fix it.
"
[{:keys [unblur-on-android?] :as props}]
(reagent/with-let [component-width (reagent/atom nil)
container-component (if (and platform/android? unblur-on-android?)
[rn/view {:background-color style/overlay-color}]
[blur/view
{:blur-radius 20
:blur-amount 20
:blur-type :transparent
:overlay-color style/overlay-color
:background-color style/overlay-color}])]
[quo.theme/provider {:theme :dark}
[rn/view
{:accessibility-label :share-qr-code
:style style/outer-container
:on-layout #(reset! component-width (oops/oget % "nativeEvent.layout.width"))}
(conj container-component
(when @component-width
[share-qr-code
(-> props
(assoc :component-width @component-width)
(clojure.set/rename-keys {:type :share-qr-type}))]))]]))

View File

@ -36,3 +36,64 @@
:qr-size (or 400 (int size))
:error-level :highest})]
[quo/qr-code (assoc props :qr-image-uri qr-media-server-uri)]))
(defn- get-network-short-name-url
[network]
(case network
:ethereum "eth:"
:optimism "opt:"
:arbitrum "arb1:"
(str (name network) ":")))
(defn- get-qr-data-for-wallet-multichain
[qr-data networks]
(as-> networks $
(map get-network-short-name-url $)
(apply str $)
(str $ qr-data)))
(defn share-qr-code
"Receives the following properties:
- type: :profile | :wallet-legacy | :wallet-multichain
- qr-data: Text to encode in the QR code
- qr-data-label-shown: Text to show below the QR code
- on-text-press: Callback for the `qr-data-label-shown` text.
- on-text-long-press: Callback for the `qr-data-label-shown` text.
- on-share-press: Callback for the share button.
- customization-color: Custom color for the QR code component.
- unblur-on-android?: [Android only] disables blur for this component.
Depending on the `type`, different properties are accepted:
`:profile`
- full-name: User full name.
- profile-picture: map ({:source image-source}) or any image source.
`:wallet-legacy`
- emoji: Emoji in a string to show in the QR code.
- on-info-press: Callback for the info icon.
- on-legacy-press: Callback for the legacy tab.
- on-multichain-press: Callback for the multichain tab.
`:wallet-multichain`
- networks: A vector of network names (`[:ethereum, :optimism, :my-net, ,,,]`)
they will add network prefixes to the address in
`qr-data-label-shown` and the QR code generated.
- on-settings-press: Callback for the settings button.
- emoji: Emoji in a string to show in the QR code.
- on-info-press: Callback for the info icon.
- on-legacy-press: Callback for the legacy tab.
- on-multichain-press: Callback for the multichain tab."
[{:keys [qr-data qr-data-label-shown networks]
share-qr-type :type
:as props}]
(let [label (or qr-data-label-shown qr-data)
string-to-encode (if (= share-qr-type :wallet-multichain)
(get-qr-data-for-wallet-multichain qr-data networks)
qr-data)
qr-media-server-uri (image-server/get-qr-image-uri-for-any-url
{:url string-to-encode
:port (rf/sub [:mediaserver/port])
:qr-size 600
:error-level :highest})]
[quo/share-qr-code
(assoc props
:qr-data label
:qr-image-uri qr-media-server-uri)]))

View File

@ -1,34 +1,142 @@
(ns status-im2.contexts.quo-preview.share.share-qr-code
(:require
[quo.core :as quo]
[react-native.core :as rn]
[reagent.core :as reagent]
[status-im2.common.resources :as resources]
[status-im2.contexts.quo-preview.preview :as preview]
[utils.image-server :as image-server]
[utils.re-frame :as rf]))
(def descriptor
[{:key :url :type :text}
{:key :link-title :type :text}])
[{:key :qr-data
:label "QR data:"
:type :text}
{:key :type
:type :select
:options [{:key :profile}
{:key :wallet-legacy}
{:key :wallet-multichain}]}])
(def profile-descriptor
[{:key :profile-picture
:type :select
:options [{:key (resources/get-mock-image :user-picture-female2)
:value "User 1"}
{:key (resources/get-mock-image :user-picture-male4)
:value "User 2"}
{:key nil
:value "No picture"}]}
{:key :full-name
:type :text}
(preview/customization-color-option)])
(def wallet-legacy-descriptor
[{:key :emoji
:type :select
:options [{:key "🐈"}
{:key "👻"}
{:key "🐧"}]}
(preview/customization-color-option)])
(def possible-networks [:ethereum :optimism :arbitrum :myNet])
(def wallet-multichain-descriptor
[{:key :emoji
:type :select
:options [{:key "🐈"}
{:key "👻"}
{:key "🐧"}]}
(preview/customization-color-option)
{:key :networks
:type :select
:options [{:key (take 1 possible-networks)
:value "Ethereum"}
{:key (take 2 possible-networks)
:value "Ethereum and Optimism"}
{:key (take 3 possible-networks)
:value "Ethereum, Optimism and Arbitrum"}
{:key (take 4 possible-networks)
:value "Ethereum, Optimism, Arbitrum and unknown"}]}])
(defn- get-network-short-name-url
[network]
(case network
:ethereum "eth:"
:optimism "opt:"
:arbitrum "arb1:"
(str (name network) ":")))
(def ^:private profile-link
"https://join.status.im/u/zQ3shfc5Wqnu")
(def ^:private wallet-address "0x39cf6E0Ba4C4530735616e1Ee7ff5FbCB726fBd2")
(defn- set-qr-data-based-on-type
[_ state-atom {old-type :type :as _old-state} {new-type :type :as _new-state}]
(when (not= old-type new-type)
(swap! state-atom assoc
:qr-data
(if (= new-type :profile)
profile-link
wallet-address))))
(defn view
[]
(let [state (reagent/atom {:info-button? true
:link-title "Link to profile"
:url "status.app/u/zQ34e1zlOdas0pKnvrweeedsasas12adjie8"})]
(let [state (reagent/atom {:type :profile
:qr-data profile-link
:on-share-press #(js/alert "share pressed")
:on-text-press #(js/alert "text pressed")
:on-text-long-press #(js/alert "text long press")
:profile-picture nil
:full-name "My User"
:customization-color :purple
:emoji "🐈"
:on-info-press #(js/alert "Info pressed")
:on-legacy-press #(js/alert (str "Tab " % " pressed"))
:on-multichain-press #(js/alert (str "Tab " % " pressed"))
:networks (take 2 possible-networks)
:on-settings-press #(js/alert "Settings pressed")})
_ (add-watch state :change set-qr-data-based-on-type)]
(fn []
(let [qr-media-server-uri (image-server/get-qr-image-uri-for-any-url
{:url (:url @state)
(let [qr-url (if (= (:type @state) :wallet-multichain)
(as-> (:networks @state) $
(map get-network-short-name-url $)
(apply str $)
(str $ (:qr-data @state)))
(:qr-data @state))
qr-media-server-uri (image-server/get-qr-image-uri-for-any-url
{:url qr-url
:port (rf/sub [:mediaserver/port])
:qr-size 300
:error-level :highest})]
:qr-size 500
:error-level :highest})
typed-descriptor (concat descriptor
(case (:type @state)
:profile profile-descriptor
:wallet-legacy wallet-legacy-descriptor
:wallet-multichain wallet-multichain-descriptor
nil))]
[preview/preview-container
{:state state
:descriptor descriptor
:component-container-style {:padding-vertical 20}}
[quo/share-qr-code
{:qr-image-uri qr-media-server-uri
:link-title (:link-title @state)
:url-on-press #(js/alert "url pressed")
:url-on-long-press #(js/alert "url long pressed")
:share-on-press #(js/alert "share pressed")
:qr-url (:url @state)}]]))))
:descriptor typed-descriptor
:component-container-style {:padding-horizontal 0}}
[rn/view
{:style {:flex 1
:justify-content :flex-end
:align-items :center
:padding-horizontal 20
:padding-vertical 30}}
[rn/view
{:style {:position :absolute
:top 0
:bottom 0
:left 0
:right 0}}
[rn/image
{:style {:flex 1
:resize-mode :stretch}
:source (resources/get-mock-image :dark-blur-bg)}]]
[quo/share-qr-code
(assoc @state
:qr-image-uri qr-media-server-uri
:qr-data qr-url)]]]))))

View File

@ -29,15 +29,8 @@
:color colors/white})
(def qr-code-container
{:padding-top 12
:padding-bottom 8
:padding-horizontal 12
:border-radius 16
:margin-top 8
:margin-horizontal screen-padding
:background-color colors/white-opa-5
:flex-direction :column
:justify-content :center})
{:margin-horizontal 20
:margin-top 8})
(def emoji-hash-container
{:border-radius 16

View File

@ -5,8 +5,8 @@
[quo.foundations.colors :as colors]
[react-native.blur :as blur]
[react-native.core :as rn]
[react-native.navigation :as navigation]
[react-native.platform :as platform]
[react-native.safe-area :as safe-area]
[reagent.core :as reagent]
[status-im.multiaccounts.core :as multiaccounts]
[status-im.ui.components.list-selection :as list-selection]
@ -45,10 +45,9 @@
(i18n/label :t/share)]])
(defn profile-tab
[window-width]
[]
(let [{:keys [emoji-hash compressed-key customization-color display-name]
:as profile} (rf/sub [:profile/profile])
qr-size (int (- window-width 64))
profile-url (str image-server/status-profile-base-url compressed-key)
profile-photo-uri (:uri (multiaccounts/displayed-photo profile))
abbreviated-url (address/get-abbreviated-profile-url
@ -57,46 +56,21 @@
emoji-hash-string (string/join emoji-hash)]
[:<>
[rn/view {:style style/qr-code-container}
[qr-codes/qr-code
{:url profile-url
:size qr-size
:avatar :profile
[qr-codes/share-qr-code
{:type :profile
:unblur-on-android? true
:qr-data profile-url
:qr-data-label-shown abbreviated-url
:on-share-press #(list-selection/open-share {:message profile-url})
:on-text-press #(rf/dispatch [:share/copy-text-and-show-toast
{:text-to-copy 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 profile-url
:post-copy-message (i18n/label :t/link-to-profile-copied)}])
:profile-picture profile-photo-uri
:full-name display-name
:customization-color customization-color
:profile-picture profile-photo-uri}]
[rn/view {:style style/profile-address-container}
[rn/view {:style style/profile-address-column}
[quo/text
{:size :paragraph-2
:weight :medium
:style style/profile-address-label}
(i18n/label :t/link-to-profile)]
[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 profile-url
:post-copy-message (i18n/label :t/link-to-profile-copied)}])
:on-long-press #(rf/dispatch [:share/copy-text-and-show-toast
{:text-to-copy profile-url
:post-copy-message (i18n/label :t/link-to-profile-copied)}])}
[quo/text
{:style style/profile-address-content
:size :paragraph-1
:weight :medium
:ellipsize-mode :middle
:number-of-lines 1}
abbreviated-url]]]
[rn/view {:style style/share-button-container}
[quo/button
{:icon-only? true
:type :grey
:background :blur
:size 32
:accessibility-label :link-to-profile
:on-press #(list-selection/open-share {:message profile-url})}
:i/share]]]]
:customization-color customization-color}]]
[rn/view {:style style/emoji-hash-container}
[rn/view {:style style/emoji-address-container}
@ -138,7 +112,7 @@
[rn/text {:style style/wip-style} "not implemented"])
(defn tab-content
[window-width]
[]
(let [selected-tab (reagent/atom :profile)]
(fn []
[:<>
@ -154,18 +128,14 @@
{:id :wallet
:label (i18n/label :t/wallet)}]}]]
(if (= @selected-tab :profile)
[profile-tab window-width]
[profile-tab]
[wallet-tab])])))
(defn view
[]
(let [window-width (rf/sub [:dimensions/window-width])]
(fn []
[rn/view
{:flex 1
:padding-top (navigation/status-bar-height)}
[blur/view
{:style style/blur
:blur-amount 20
:blur-radius (if platform/android? 25 10)}]
[tab-content window-width]])))
[rn/view {:flex 1 :padding-top (safe-area/get-top)}
[blur/view
{:style style/blur
:blur-amount 20
:blur-radius (if platform/android? 25 10)}]
[tab-content]])

View File

@ -1,12 +1,12 @@
(ns test-helpers.component
"Helpers for writing component tests using React Native Testing Library."
(:require-macros test-helpers.component)
(:require
["@testing-library/react-native" :as rtl]
[camel-snake-kebab.core :as camel-snake-kebab]
[quo.theme :as quo.theme]
[reagent.core :as reagent]
[utils.i18n :as i18n]))
(:require-macros [test-helpers.component])
(:require ["@testing-library/react-native" :as rtl]
[camel-snake-kebab.core :as camel-snake-kebab]
[oops.core :as oops]
[quo.theme :as quo.theme]
[reagent.core :as reagent]
[utils.i18n :as i18n]))
;;;; React Native Testing Library
@ -233,3 +233,21 @@
([element prop] (has-prop element prop js/undefined))
([element prop value]
(.toHaveProp (js/expect element) (camel-snake-kebab/->camelCaseString prop) value)))
(defn get-rerender-fn
"Returns a rerender function from React Native Testing Library.
Takes a JS Object representing a component and returns a function that accepts hiccup
representing the component to rerender with the new props to rerender it.
e.g.
(let [rerender-fn (h/get-rerender-fn
(h/render [my-component {:prop-1 :A
:prop-2 :B}]))]
;; Rerenders `my-component` with the new props
(rerender-fn [my-component {:prop-1 :new-A
:prop-2 :new-B}]))
"
[component]
(fn [component-updated]
(let [rerender-fn (oops/oget component "rerender")
react-element (reagent/as-element component-updated)]
(rerender-fn react-element))))

View File

@ -859,6 +859,7 @@
"leave-community?": "Leave community?",
"leave-community-message": "Well be sad to see you go but remember, you can come back at any time!",
"left-community": "You left “{{community}}”",
"legacy": "Legacy",
"joined": "Joined",
"leave-group": "Leave group",
"left": "left",
@ -960,6 +961,7 @@
"multiaccount-exists-content": "Keys for this account already exist and cant be added again. If youve lost your password, passcode or Keycard, uninstall the app, reinstall and access your keys by entering your seed phrase",
"multiaccounts-recover-enter-phrase-text": "Enter 12, 15, 18, 21 or 24 words.\nSeparate words by a single space.",
"multiaccounts-recover-enter-phrase-title": "Enter your seed phrase",
"multichain": "Multichain",
"name": "Name",
"name-of-token": "The name of your token",
"need-help": "Need help?",