Compare commits
12
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e39c249868 | ||
|
|
c6addce62a | ||
|
|
18e2652ec3 | ||
|
|
e938c2393a | ||
|
|
ac2d10bc5d | ||
|
|
e825f930fa | ||
|
|
394dfde87b | ||
|
|
e4db23b0a9 | ||
|
|
94ddbbcd2e | ||
|
|
ac27314547 | ||
|
|
7a4b12acf4 | ||
|
|
4e6dea6b36 |
@@ -61,6 +61,35 @@ the source file. For a real example, see
|
||||
[rn/view (do-something)]])
|
||||
```
|
||||
|
||||
### Always add styles inside the `:style` key
|
||||
|
||||
Although when compiling ReactNative for mobile some components are able work with
|
||||
their styles in the top-level of the properties map, prefer to add them inside the
|
||||
`:style` key in order to separate styles from properties:
|
||||
|
||||
```clojure
|
||||
;; bad
|
||||
[rn/button {:flex 1
|
||||
:padding-vertical 10
|
||||
:padding-horizontal 20
|
||||
:on-press #(js/alert "Hi!")
|
||||
:title "Button"}]
|
||||
|
||||
;; good
|
||||
[rn/button {:style {:flex 1
|
||||
:padding-vertical 10
|
||||
:padding-horizontal 20}
|
||||
:on-press #(js/alert "Hi!")
|
||||
:title "Button"}]
|
||||
|
||||
;; better
|
||||
;; (define them in a style ns & place them inside `:style` key)
|
||||
[rn/button {:style (style/button)
|
||||
:on-press #(js/alert "Hi!")
|
||||
:title "Button"}
|
||||
]
|
||||
```
|
||||
|
||||
### Don't use percents to define width/height
|
||||
|
||||
In ReactNative, all layouts use the [flexbox
|
||||
|
||||
+7
-1
@@ -45,7 +45,13 @@
|
||||
:devtools {:autobuild #shadow/env ["SHADOW_AUTOBUILD_ENABLED" :default true :as :bool]}
|
||||
:dev {:devtools {:after-load status-im2.setup.hot-reload/reload
|
||||
:build-notify status-im2.setup.hot-reload/build-notify
|
||||
:preloads [re-frisk-remote.preload]}
|
||||
:preloads [re-frisk-remote.preload
|
||||
;; In order to use component test helpers in
|
||||
;; the REPL we need to preload namespaces
|
||||
;; that are not normally required by
|
||||
;; production code, such as
|
||||
;; @testing-library/react-native.
|
||||
test-helpers.component]}
|
||||
:closure-defines
|
||||
{status-im2.config/POKT_TOKEN #shadow/env "POKT_TOKEN"
|
||||
status-im2.config/OPENSEA_API_KEY #shadow/env "OPENSEA_API_KEY"}
|
||||
|
||||
@@ -16,15 +16,13 @@
|
||||
:height 230
|
||||
:border-radius 20}
|
||||
:on-press on-press}
|
||||
[rn/view
|
||||
{:flex 1}
|
||||
[rn/view {:flex 1}
|
||||
[rn/view (style/community-cover-container 60)
|
||||
[rn/image
|
||||
{:source cover
|
||||
:style
|
||||
{:flex 1
|
||||
:border-top-right-radius 20
|
||||
:border-top-left-radius 20}}]]
|
||||
:style {:flex 1
|
||||
:border-top-right-radius 20
|
||||
:border-top-left-radius 20}}]]
|
||||
[rn/view (style/card-view-content-container 12)
|
||||
[rn/view (style/card-view-chat-icon 48)
|
||||
[icon/community-icon {:images images} 48]]
|
||||
@@ -38,7 +36,8 @@
|
||||
{:title name
|
||||
:description description}]
|
||||
[rn/view {:style (style/card-stats-position)}
|
||||
[community-view/community-stats-column :card-view]]
|
||||
[community-view/community-stats-column
|
||||
{:type :card-view}]]
|
||||
[rn/view {:style (style/community-tags-position)}
|
||||
[community-view/community-tags tags]]]]]]])
|
||||
|
||||
|
||||
@@ -64,7 +64,8 @@
|
||||
colors/neutral-40
|
||||
colors/neutral-60))}}
|
||||
name]
|
||||
[community-view/community-stats-column :list-view]]
|
||||
[community-view/community-stats-column
|
||||
{:type :list-view}]]
|
||||
(if (= status :gated)
|
||||
[community-view/permission-tag-container
|
||||
{:locked? locked?
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
members-count]])
|
||||
|
||||
(defn community-stats-column
|
||||
[type]
|
||||
[{:keys [type]}]
|
||||
(let [icon-color (colors/theme-colors colors/neutral-50 colors/neutral-40)]
|
||||
[rn/view
|
||||
(if (= type :card-view)
|
||||
@@ -46,11 +46,12 @@
|
||||
^{:key name}
|
||||
[rn/view {:margin-right 8}
|
||||
[tag/tag
|
||||
{:size 24
|
||||
:label name
|
||||
:type :emoji
|
||||
:labelled? true
|
||||
:resource emoji}]])])
|
||||
{:size 24
|
||||
:label name
|
||||
:type :emoji
|
||||
:labelled? true
|
||||
:scrollable? true
|
||||
:resource emoji}]])])
|
||||
|
||||
(defn community-title
|
||||
[{:keys [title description size] :or {size :small}}]
|
||||
|
||||
@@ -62,7 +62,7 @@
|
||||
:bottom 0
|
||||
:left 0
|
||||
:right 0
|
||||
:height 20
|
||||
:border-radius 20
|
||||
:padding-horizontal padding-horizontal
|
||||
:border-top-right-radius 16
|
||||
:border-top-left-radius 16
|
||||
|
||||
@@ -91,7 +91,7 @@
|
||||
:color (:text colors-by-status))]
|
||||
(if multiple-lines?
|
||||
(assoc base-props :text-align-vertical :top)
|
||||
(assoc base-props :height (if small? 30 38)))))
|
||||
(assoc base-props :height (if small? 30 38) :line-height nil))))
|
||||
|
||||
(defn right-icon-touchable-area
|
||||
[small?]
|
||||
|
||||
@@ -150,13 +150,15 @@
|
||||
:justify-content :flex-end)}
|
||||
(let [last-icon-index (-> right-section-buttons count dec)]
|
||||
(map-indexed (fn [index
|
||||
{:keys [icon on-press type style icon-override-theme]
|
||||
{:keys [icon on-press type style icon-override-theme accessibility-label]
|
||||
:or {type :grey}}]
|
||||
^{:key index}
|
||||
[rn/view
|
||||
{:style (assoc style
|
||||
:margin-right
|
||||
(if (= index last-icon-index) 0 8))}
|
||||
(cond-> {:style (assoc style
|
||||
:margin-right
|
||||
(if (= index last-icon-index) 0 8))}
|
||||
accessibility-label (assoc :accessibility-label accessibility-label
|
||||
:accessible true))
|
||||
[button/button
|
||||
{:on-press on-press
|
||||
:icon true
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
(ns quo2.components.selectors.disclaimer.component-spec
|
||||
(:require [quo2.components.selectors.disclaimer.view :as disclaimer]
|
||||
[test-helpers.component :as h]))
|
||||
|
||||
(h/describe "Disclaimer tests"
|
||||
(h/test "Default render of toggle component"
|
||||
(h/render [disclaimer/view {:on-change (h/mock-fn)} "test"])
|
||||
(h/is-truthy (h/get-by-label-text :checkbox-off)))
|
||||
|
||||
(h/test "Renders its text"
|
||||
(let [text "I accept this disclaimer"]
|
||||
(h/render [disclaimer/view {} text])
|
||||
(h/is-truthy (h/get-by-text text))))
|
||||
|
||||
(h/test "On change event gets fire after press"
|
||||
(let [mock-fn (h/mock-fn)]
|
||||
(h/render [disclaimer/view {:on-change mock-fn} "test"])
|
||||
(h/fire-event :press (h/get-by-label-text :checkbox-off))
|
||||
(h/was-called mock-fn)))
|
||||
|
||||
(h/describe "It's rendered according to its `checked?` property"
|
||||
(h/test "checked? true"
|
||||
(h/render [disclaimer/view {:checked? true} "test"])
|
||||
(h/is-null (h/query-by-label-text :checkbox-off))
|
||||
(h/is-truthy (h/query-by-label-text :checkbox-on)))
|
||||
(h/test "checked? false"
|
||||
(h/render [disclaimer/view {:checked? false} "test"])
|
||||
(h/is-null (h/query-by-label-text :checkbox-on))
|
||||
(h/is-truthy (h/query-by-label-text :checkbox-off)))))
|
||||
@@ -2,14 +2,16 @@
|
||||
(:require [quo2.foundations.colors :as colors]))
|
||||
|
||||
(defn container
|
||||
[]
|
||||
{:flex-direction :row
|
||||
:background-color (colors/theme-colors colors/neutral-5 colors/neutral-80-opa-40)
|
||||
:padding 11
|
||||
:align-self :stretch
|
||||
:border-radius 12
|
||||
:border-width 1
|
||||
:border-color (colors/theme-colors colors/neutral-20 colors/neutral-70)})
|
||||
[blur?]
|
||||
(let [dark-background (if blur? colors/white-opa-5 colors/neutral-80-opa-40)
|
||||
dark-border (if blur? colors/white-opa-10 colors/neutral-70)]
|
||||
{:flex-direction :row
|
||||
:background-color (colors/theme-colors colors/neutral-5 dark-background)
|
||||
:padding 11
|
||||
:align-self :stretch
|
||||
:border-radius 12
|
||||
:border-width 1
|
||||
:border-color (colors/theme-colors colors/neutral-20 dark-border)}))
|
||||
|
||||
(def text
|
||||
{:margin-left 8})
|
||||
|
||||
@@ -5,9 +5,9 @@
|
||||
[react-native.core :as rn]))
|
||||
|
||||
(defn view
|
||||
[{:keys [checked? on-change accessibility-label container-style]} label]
|
||||
[{:keys [checked? blur? on-change accessibility-label container-style]} label]
|
||||
[rn/view
|
||||
{:style (merge container-style (style/container))}
|
||||
{:style (merge container-style (style/container blur?))}
|
||||
[selectors/checkbox
|
||||
{:accessibility-label accessibility-label
|
||||
:on-change on-change
|
||||
|
||||
@@ -19,5 +19,6 @@
|
||||
[quo2.components.record-audio.record-audio.--tests--.record-audio-component-spec]
|
||||
[quo2.components.record-audio.soundtrack.--tests--.soundtrack-component-spec]
|
||||
[quo2.components.selectors.--tests--.selectors-component-spec]
|
||||
[quo2.components.selectors.disclaimer.component-spec]
|
||||
[quo2.components.selectors.filter.component-spec]
|
||||
[quo2.components.tags.--tests--.status-tags-component-spec]))
|
||||
|
||||
@@ -225,8 +225,12 @@
|
||||
|
||||
(defn format-members
|
||||
[count]
|
||||
(if (> count 1000000)
|
||||
(cond
|
||||
(> count 1000000)
|
||||
(str (with-precision (/ count 1000000) 1) (i18n/label :t/M))
|
||||
(if (and (> count 999) (< count 1000000))
|
||||
(str (with-precision (/ count 1000) 1) (i18n/label :t/K))
|
||||
count)))
|
||||
|
||||
(< 999 count 1000000)
|
||||
(str (with-precision (/ count 1000) 1) (i18n/label :t/K))
|
||||
|
||||
:else
|
||||
count))
|
||||
|
||||
@@ -89,7 +89,7 @@
|
||||
|
||||
|
||||
(defn display-picture
|
||||
[scroll-height cover]
|
||||
[scroll-height logo]
|
||||
(let [input-range (if platform/ios? [-67 10] [0 150])
|
||||
y (reanimated/use-shared-value scroll-height)
|
||||
animation (reanimated/interpolate y
|
||||
@@ -104,7 +104,7 @@
|
||||
[reanimated/view
|
||||
{:style (style/display-picture-container animation)}
|
||||
[rn/image
|
||||
{:source cover
|
||||
{:source logo
|
||||
:style style/display-picture}]]))
|
||||
|
||||
(defn scroll-page
|
||||
@@ -117,7 +117,7 @@
|
||||
[:<>
|
||||
[:f> scroll-page-header @scroll-height height name page-nav-right-section-buttons
|
||||
logo sticky-header top-nav title-colum navigate-back?]
|
||||
[rn/scroll-view
|
||||
[rn/flat-list
|
||||
{:content-container-style (style/scroll-view-container
|
||||
(diff-with-max-min @scroll-height 16 0))
|
||||
:shows-vertical-scroll-indicator false
|
||||
@@ -128,21 +128,25 @@
|
||||
event
|
||||
"nativeEvent.contentOffset.y")))
|
||||
(when on-scroll
|
||||
(on-scroll @scroll-height)))}
|
||||
(when cover-image
|
||||
[rn/view {:style {:height 151}}
|
||||
[rn/image
|
||||
{:source cover-image
|
||||
;; Using negative margin-bottom as a workaround because on Android,
|
||||
;; ScrollView clips its children despite setting overflow: 'visible'.
|
||||
;; Related issue: https://github.com/facebook/react-native/issues/31218
|
||||
:style {:margin-bottom -16
|
||||
:flex 1}}]])
|
||||
(when children
|
||||
[rn/view
|
||||
{:flex 1
|
||||
:border-radius (diff-with-max-min @scroll-height 16 0)
|
||||
:background-color background-color}
|
||||
(when cover-image
|
||||
[:f> display-picture @scroll-height logo])
|
||||
children])]])))
|
||||
(on-scroll @scroll-height)))
|
||||
:header [rn/view
|
||||
(when cover-image
|
||||
[rn/view {:style {:height 151}}
|
||||
[rn/image
|
||||
{:source cover-image
|
||||
;; Using negative margin-bottom as a workaround because
|
||||
;; on Android,
|
||||
;; ScrollView clips its children despite setting
|
||||
;; overflow: 'visible'.
|
||||
;; Related issue:
|
||||
;; https://github.com/facebook/react-native/issues/31218
|
||||
:style {:margin-bottom -16
|
||||
:flex 1}}]])
|
||||
(when children
|
||||
[rn/view
|
||||
{:flex 1
|
||||
:border-radius (diff-with-max-min @scroll-height 16 0)
|
||||
:background-color background-color}
|
||||
(when cover-image
|
||||
[:f> display-picture @scroll-height logo])
|
||||
children])]}]])))
|
||||
@@ -318,3 +318,8 @@
|
||||
(def ^:const empty-category-id :communities/not-categorized)
|
||||
|
||||
(def ^:const seed-phrase-valid-length #{12 18 24})
|
||||
|
||||
(def ^:const auth-method-password "password")
|
||||
(def ^:const auth-method-biometric "biometric")
|
||||
(def ^:const auth-method-biometric-prepare "biometric-prepare")
|
||||
(def ^:const auth-method-none "none")
|
||||
|
||||
@@ -27,6 +27,10 @@
|
||||
(concat
|
||||
(when (and outgoing
|
||||
(not (or deleted? deleted-for-me?))
|
||||
;; temporarily disable edit image message until
|
||||
;; https://github.com/status-im/status-mobile/issues/15298
|
||||
;; is implemented
|
||||
(not= content-type constants/content-type-image)
|
||||
(not= content-type constants/content-type-audio))
|
||||
[{:type :main
|
||||
:on-press #(rf/dispatch [:chat.ui/edit-message message-data])
|
||||
|
||||
@@ -22,10 +22,11 @@
|
||||
|
||||
(def featured-list-container
|
||||
{:flex-direction :row
|
||||
:overflow :hidden
|
||||
:margin-bottom 24
|
||||
:margin-left 20
|
||||
:padding-right 20})
|
||||
:overflow :hidden})
|
||||
|
||||
(def flat-list-container
|
||||
{:padding-bottom 24
|
||||
:padding-horizontal 20})
|
||||
|
||||
(def other-communities-container
|
||||
{:flex 1
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
[reagent.core :as reagent]
|
||||
[status-im2.common.resources :as resources]
|
||||
[status-im2.contexts.communities.menus.community-options.view :as options]
|
||||
[status-im.ui.screens.communities.community :as community]
|
||||
[status-im.ui.components.react :as react]
|
||||
[react-native.platform :as platform]
|
||||
[status-im2.common.scroll-page.view :as scroll-page]
|
||||
@@ -22,23 +21,28 @@
|
||||
:group [{:id 1
|
||||
:token-icon (resources/get-mock-image :status-logo)}]}]}})
|
||||
|
||||
(defn render-fn
|
||||
[community-item _ _ {:keys [width view-type]}]
|
||||
(let [item (merge community-item
|
||||
(get mock-community-item-data :data))
|
||||
cover {:uri (get-in (:images item) [:banner :uri])}]
|
||||
(defn community-list-item
|
||||
[{:keys [id] :as community} _ _ {:keys [width view-type]}]
|
||||
(let [community-item (merge
|
||||
community
|
||||
(get mock-community-item-data :data))
|
||||
cover {:uri (get-in (:images community) [:banner :uri])}]
|
||||
(if (= view-type :card-view)
|
||||
[quo/community-card-view-item (assoc item :width width :cover cover)
|
||||
#(rf/dispatch [:navigate-to :community-overview (:id item)])]
|
||||
[quo/community-card-view-item (assoc community-item :width width :cover cover)
|
||||
#(rf/dispatch [:navigate-to :community-overview (:id community)])]
|
||||
[quo/communities-list-view-item
|
||||
{:on-press (fn []
|
||||
(rf/dispatch [:communities/load-category-states (:id item)])
|
||||
(rf/dispatch [:communities/load-category-states id])
|
||||
(rf/dispatch [:dismiss-keyboard])
|
||||
<<<<<<< HEAD
|
||||
(rf/dispatch [:navigate-to :community-overview (:id item)]))
|
||||
=======
|
||||
(rf/dispatch [:navigate-to :community {:community-id id}]))
|
||||
>>>>>>> 1675fef63 (Fix: community data not displayed)
|
||||
:on-long-press #(rf/dispatch
|
||||
[:bottom-sheet/show-sheet
|
||||
{:content (fn []
|
||||
[options/community-options-bottom-sheet (:id item)])}])}])))
|
||||
[options/community-options-bottom-sheet id])}])}])))
|
||||
|
||||
(defn screen-title
|
||||
[]
|
||||
@@ -89,7 +93,6 @@
|
||||
:label (i18n/label :t/gated)
|
||||
:accessibility-label :gated-communities-tab}]}]])
|
||||
|
||||
|
||||
(defn featured-list
|
||||
[communities view-type]
|
||||
(let [view-size (reagent/atom 0)]
|
||||
@@ -98,7 +101,7 @@
|
||||
{:style style/featured-list-container
|
||||
:on-layout #(swap! view-size
|
||||
(fn []
|
||||
(- (oops/oget % "nativeEvent.layout.width") 20)))}
|
||||
(- (oops/oget % "nativeEvent.layout.width") 40)))}
|
||||
(when-not (= @view-size 0)
|
||||
[rn/flat-list
|
||||
{:key-fn :id
|
||||
@@ -107,9 +110,22 @@
|
||||
:shows-horizontal-scroll-indicator false
|
||||
:separator [rn/view {:width 12}]
|
||||
:data communities
|
||||
:render-fn render-fn
|
||||
:render-fn community-list-item
|
||||
:render-data {:width @view-size
|
||||
:view-type view-type}}])])))
|
||||
:view-type view-type}
|
||||
:contentContainerStyle style/flat-list-container}])])))
|
||||
|
||||
(defn other-communities-list
|
||||
[{:keys [communities view-type]}]
|
||||
[rn/view style/other-communities-container
|
||||
[rn/flat-list
|
||||
{:key-fn :id
|
||||
:keyboard-should-persist-taps :always
|
||||
:separator [rn/view {:height 16}]
|
||||
:data communities
|
||||
:render-fn community-list-item
|
||||
:contentContainerStyle style/flat-list-container
|
||||
:render-data {:view-type view-type}}]])
|
||||
|
||||
(defn discover-communities-header
|
||||
[{:keys [featured-communities-count
|
||||
@@ -120,39 +136,10 @@
|
||||
[screen-title]
|
||||
[featured-communities-header featured-communities-count]
|
||||
[featured-list featured-communities view-type]
|
||||
[quo/separator]
|
||||
[rn/view {:style {:margin-horizontal 20}}
|
||||
[quo/separator]]
|
||||
[discover-communities-segments selected-tab false]])
|
||||
|
||||
(defn other-communities-list
|
||||
[{:keys [communities communities-ids view-type]}]
|
||||
[rn/view {:style style/other-communities-container}
|
||||
(map-indexed
|
||||
(fn [inner-index item]
|
||||
(let [community-id (when communities-ids item)
|
||||
community (if communities
|
||||
item
|
||||
[rf/sub [:communities/home-item community-id]])]
|
||||
[rn/view
|
||||
{:key (str inner-index (:id community))
|
||||
:margin-bottom 16}
|
||||
(if (= view-type :card-view)
|
||||
[quo/community-card-view-item
|
||||
(merge community
|
||||
(get mock-community-item-data :data))
|
||||
#(rf/dispatch [:navigate-to :community-overview (:id community)])]
|
||||
[quo/communities-list-view-item
|
||||
{:on-press (fn []
|
||||
(rf/dispatch [:communities/load-category-states (:id community)])
|
||||
(rf/dispatch [:dismiss-keyboard])
|
||||
(rf/dispatch [:navigate-to :community-overview (:id community)]))
|
||||
:on-long-press #(rf/dispatch [:bottom-sheet/show-sheet
|
||||
{:content (fn []
|
||||
;; TODO implement with quo2
|
||||
[community/community-actions community])}])}
|
||||
(merge community
|
||||
(get mock-community-item-data :data))])]))
|
||||
(if communities communities communities-ids))])
|
||||
|
||||
(defn communities-lists
|
||||
[selected-tab view-type]
|
||||
[rn/view {:style {:flex 1}}
|
||||
|
||||
@@ -295,14 +295,14 @@
|
||||
|
||||
(defn page-nav-right-section-buttons
|
||||
[id]
|
||||
[{:icon :i/options
|
||||
:background-color (scroll-page/icon-color)
|
||||
:on-press #(rf/dispatch
|
||||
[:bottom-sheet/show-sheet
|
||||
{:content
|
||||
(fn []
|
||||
[options/community-options-bottom-sheet
|
||||
id])}])}])
|
||||
[{:icon :i/options
|
||||
:background-color (scroll-page/icon-color)
|
||||
:accessibility-label :community-options-for-community
|
||||
:on-press #(rf/dispatch
|
||||
[:bottom-sheet/show-sheet
|
||||
{:content (fn []
|
||||
[options/community-options-bottom-sheet
|
||||
id])}])}])
|
||||
|
||||
(defn pick-first-category-by-height
|
||||
[scroll-height first-channel-height categories-heights]
|
||||
|
||||
@@ -12,3 +12,12 @@
|
||||
:background-color colors/neutral-80-opa-80-blur})
|
||||
|
||||
(def navigation-bar {:height 56})
|
||||
|
||||
(def image-container
|
||||
{:margin-top 20
|
||||
:margin-bottom 24
|
||||
:background-color colors/danger-50
|
||||
:border-radius 20
|
||||
:flex 1
|
||||
:align-items :center
|
||||
:justify-content :center})
|
||||
|
||||
@@ -5,7 +5,8 @@
|
||||
[status-im2.contexts.onboarding.enable-biometrics.style :as style]
|
||||
[utils.i18n :as i18n]
|
||||
[status-im2.contexts.onboarding.common.background.view :as background]
|
||||
[utils.re-frame :as rf]))
|
||||
[utils.re-frame :as rf]
|
||||
[status-im.multiaccounts.biometric.core :as biometric]))
|
||||
|
||||
(defn navigation-bar
|
||||
[]
|
||||
@@ -17,18 +18,39 @@
|
||||
|
||||
(defn page
|
||||
[]
|
||||
[rn/view {:style style/page-container}
|
||||
[navigation-bar]
|
||||
[rn/view {:style {:padding-horizontal 20}}
|
||||
[quo/text
|
||||
{:size :heading-1
|
||||
:weight :semi-bold
|
||||
:style {:color colors/white}} "Enable-biometrics"]
|
||||
[quo/button
|
||||
{:on-press #(rf/dispatch [:onboarding-2/create-account-and-login])
|
||||
:type :grey
|
||||
:override-theme :dark
|
||||
:style {}} (i18n/label :t/continue)]]])
|
||||
(let [supported-biometric (rf/sub [:supported-biometric-auth])
|
||||
bio-type-label (biometric/get-label supported-biometric)
|
||||
profile-color (:color (rf/sub [:onboarding-2/profile]))]
|
||||
[rn/view {:style style/page-container}
|
||||
[navigation-bar]
|
||||
[rn/view
|
||||
{:style {:padding-horizontal 20
|
||||
:flex 1}}
|
||||
[quo/text
|
||||
{:size :heading-1
|
||||
:weight :semi-bold
|
||||
:style {:color colors/white}} (i18n/label :t/enable-biometrics)]
|
||||
[quo/text
|
||||
{:size :paragraph-1
|
||||
:style {:color colors/white
|
||||
:margin-top 8}}
|
||||
(i18n/label :t/use-biometrics)]
|
||||
;; TODO(@briansztamfater): Replace view with image view with the real illustration,
|
||||
;; https://github.com/status-im/status-mobile/issues/15445
|
||||
[rn/view {:style style/image-container}
|
||||
[quo/text {:size :paragraph-1}
|
||||
"Illustration here"]]
|
||||
[rn/view {:style {:margin-bottom 55}}
|
||||
[quo/button
|
||||
{:on-press #(rf/dispatch [:onboarding-2/enable-biometrics])
|
||||
:before :i/face-id
|
||||
:override-background-color (colors/custom-color profile-color 50)}
|
||||
(i18n/label :t/biometric-enable-button {:bio-type-label bio-type-label})]
|
||||
[quo/button
|
||||
{:on-press #(rf/dispatch [:onboarding-2/create-account-and-login])
|
||||
:override-background-color colors/white-opa-5
|
||||
:style {:margin-top 12}}
|
||||
(i18n/label :t/maybe-later)]]]]))
|
||||
|
||||
(defn enable-biometrics
|
||||
[]
|
||||
|
||||
@@ -7,7 +7,9 @@
|
||||
[clojure.string :as string]
|
||||
[utils.security.core :as security]
|
||||
[status-im.native-module.core :as status]
|
||||
[status-im.ethereum.core :as ethereum]))
|
||||
[status-im.ethereum.core :as ethereum]
|
||||
[status-im2.constants :as constants]
|
||||
[utils.i18n :as i18n]))
|
||||
|
||||
(re-frame/reg-fx
|
||||
:multiaccount/create-account-and-login
|
||||
@@ -36,6 +38,29 @@
|
||||
{:db (update db :onboarding-2/profile merge onboarding-data)
|
||||
:dispatch [:navigate-to :create-profile-password]})
|
||||
|
||||
(rf/defn enable-biometrics
|
||||
{:events [:onboarding-2/enable-biometrics]}
|
||||
[_]
|
||||
{:biometric-auth/authenticate [#(rf/dispatch [:onboarding-2/biometrics-done %]) {}]})
|
||||
|
||||
(rf/defn show-biometrics-message
|
||||
[cofx bioauth-message bioauth-code]
|
||||
(let [content (or (when (get #{"NOT_AVAILABLE" "NOT_ENROLLED"} bioauth-code)
|
||||
(i18n/label :t/grant-face-id-permissions))
|
||||
bioauth-message)]
|
||||
(when content
|
||||
{:utils/show-popup
|
||||
{:title (i18n/label :t/biometric-auth-login-error-title)
|
||||
:content content}})))
|
||||
|
||||
(rf/defn biometrics-done
|
||||
{:events [:onboarding-2/biometrics-done]}
|
||||
[{:keys [db] :as cofx} {:keys [bioauth-success bioauth-message bioauth-code]}]
|
||||
(if bioauth-success
|
||||
{:db (assoc-in db [:onboarding-2/profile :auth-method] constants/auth-method-biometric)
|
||||
:dispatch [:onboarding-2/create-account-and-login]}
|
||||
(show-biometrics-message cofx bioauth-message bioauth-code)))
|
||||
|
||||
(defn strip-file-prefix
|
||||
[path]
|
||||
(when path
|
||||
@@ -87,7 +112,9 @@
|
||||
(rf/defn password-set
|
||||
{:events [:onboarding-2/password-set]}
|
||||
[{:keys [db]} password]
|
||||
{:db (assoc-in db [:onboarding-2/profile :password] password)
|
||||
{:db (-> db
|
||||
(assoc-in [:onboarding-2/profile :password] password)
|
||||
(assoc-in [:onboarding-2/profile :auth-method] constants/auth-method-password))
|
||||
:dispatch [:navigate-to :enable-biometrics]})
|
||||
|
||||
(rf/defn seed-phrase-entered
|
||||
|
||||
@@ -42,19 +42,19 @@
|
||||
:padding-horizontal 20})
|
||||
|
||||
(def multi-profile-button
|
||||
{:align-self :flex-end})
|
||||
{:align-self :flex-end
|
||||
:margin-bottom 20})
|
||||
|
||||
(def login-profile-card
|
||||
{:margin-vertical 20})
|
||||
|
||||
(def keyboard-avoiding-view
|
||||
{:flex 1})
|
||||
{:margin-bottom 20})
|
||||
|
||||
(def info-message
|
||||
{:margin-top 8})
|
||||
|
||||
(def forget-password-button
|
||||
{:margin-vertical 8})
|
||||
|
||||
(defn login-button
|
||||
[]
|
||||
{:margin-top 8
|
||||
:margin-bottom (if platform/android? 20 46)})
|
||||
{:margin-bottom (if platform/android? 20 46)})
|
||||
|
||||
|
||||
@@ -135,18 +135,19 @@
|
||||
profile-picture (:uri (first (:images multiaccount)))]
|
||||
[rn/keyboard-avoiding-view
|
||||
{:style style/login-container}
|
||||
[rn/view
|
||||
{:style {:flex 1}}
|
||||
[quo/button
|
||||
{:size 32
|
||||
:type :blur-bg
|
||||
:icon true
|
||||
:on-press #(reset! show-profiles? true)
|
||||
:override-theme :dark
|
||||
:width 32
|
||||
:accessibility-label :show-profiles
|
||||
:style style/multi-profile-button}
|
||||
:i/multi-profile]
|
||||
[quo/button
|
||||
{:size 32
|
||||
:type :blur-bg
|
||||
:icon true
|
||||
:on-press #(reset! show-profiles? true)
|
||||
:override-theme :dark
|
||||
:width 32
|
||||
:accessibility-label :show-profiles
|
||||
:style style/multi-profile-button}
|
||||
:i/multi-profile]
|
||||
[rn/scroll-view
|
||||
{:keyboard-should-persist-taps :always
|
||||
:style {:flex 1}}
|
||||
[quo/profile-card
|
||||
{:name name
|
||||
:customization-color (or customization-color :primary)
|
||||
@@ -178,7 +179,8 @@
|
||||
:type :ghost
|
||||
:before :i/info
|
||||
:accessibility-label :forget-password-button
|
||||
:override-theme :dark}
|
||||
:override-theme :dark
|
||||
:style style/forget-password-button}
|
||||
(i18n/label :t/forget-password)]
|
||||
[quo/button
|
||||
{:size 40
|
||||
|
||||
@@ -1,28 +1,56 @@
|
||||
(ns status-im2.contexts.quo-preview.selectors.disclaimer
|
||||
(:require [quo2.components.buttons.button :as button]
|
||||
[quo2.components.selectors.disclaimer.view :as quo]
|
||||
[quo2.components.selectors.disclaimer.view :as disclaimer]
|
||||
[quo2.foundations.colors :as colors]
|
||||
[quo2.theme :as theme]
|
||||
[react-native.core :as rn]
|
||||
[reagent.core :as reagent]))
|
||||
[reagent.core :as reagent]
|
||||
[status-im2.contexts.quo-preview.preview :as preview]))
|
||||
|
||||
(def descriptor
|
||||
[{:label "Checked:"
|
||||
:key :checked?
|
||||
:type :boolean}
|
||||
{:label "Blur (only for dark theme):"
|
||||
:key :blur?
|
||||
:type :boolean}
|
||||
{:label "Text"
|
||||
:key :text
|
||||
:type :text}])
|
||||
|
||||
(defn blur-background
|
||||
[blur?]
|
||||
(when (and blur? (theme/dark?))
|
||||
[rn/view
|
||||
{:style {:position :absolute
|
||||
:top 0
|
||||
:bottom 0
|
||||
:left 0
|
||||
:right 0}}
|
||||
[preview/blur-view
|
||||
{:style {:flex 1}
|
||||
:show-blur-background? true}]]))
|
||||
|
||||
(defn cool-preview
|
||||
[]
|
||||
(let [checked? (reagent/atom false)]
|
||||
(let [state (reagent/atom {:checked? false
|
||||
:blur? true
|
||||
:text "I agree with the community rules"})]
|
||||
(fn []
|
||||
[rn/view
|
||||
{:margin-bottom 50
|
||||
:padding-vertical 16
|
||||
:padding-horizontal 20}
|
||||
[rn/view
|
||||
{:padding-vertical 60
|
||||
:align-items :center}
|
||||
[quo/view
|
||||
{:container-style {:margin-bottom 40}
|
||||
:on-change #(swap! checked? not)}
|
||||
"I agree with the community rules"]
|
||||
[button/button
|
||||
{:disabled (not @checked?)}
|
||||
"submit"]]])))
|
||||
(let [{:keys [blur? checked? text]} @state]
|
||||
[rn/view {:style {:flex 1}}
|
||||
[rn/view {:style {:flex 1}}
|
||||
[preview/customizer state descriptor]]
|
||||
[rn/view {:style {:padding-horizontal 15}}
|
||||
[blur-background blur?]
|
||||
[rn/view {:style {:margin-vertical 50}}
|
||||
[disclaimer/view
|
||||
{:blur? blur?
|
||||
:checked? checked?
|
||||
:on-change #(swap! state update :checked? not)}
|
||||
text]]
|
||||
[button/button {:disabled (not checked?)}
|
||||
"submit"]]]))))
|
||||
|
||||
(defn preview-disclaimer
|
||||
[]
|
||||
|
||||
@@ -151,7 +151,9 @@
|
||||
|
||||
{:name :create-profile-password
|
||||
:options {:statusBar {:style :light}
|
||||
:topBar {:visible false}
|
||||
:topBar {:visible false
|
||||
:backButton {:popStackOnPress false}}
|
||||
|
||||
:navigationBar {:backgroundColor colors/black}}
|
||||
:insets {:top false}
|
||||
:component create-password/create-password}
|
||||
@@ -164,9 +166,13 @@
|
||||
:component enable-biometrics/enable-biometrics}
|
||||
|
||||
{:name :generating-keys
|
||||
:options {:statusBar {:style :light}
|
||||
:topBar {:visible false}
|
||||
:navigationBar {:backgroundColor colors/black}}
|
||||
:options {:statusBar {:style :light}
|
||||
:navigationBar {:backgroundColor colors/black}
|
||||
:popGesture false
|
||||
:hardwareBackButton {:dismissModalOnPress false
|
||||
:popStackOnPress false}
|
||||
:topBar {:visible false
|
||||
:backButton {:popStackOnPress false}}}
|
||||
:insets {:top false}
|
||||
:component generating-keys/generating-keys}
|
||||
|
||||
@@ -178,9 +184,13 @@
|
||||
:component enter-seed-phrase/enter-seed-phrase}
|
||||
|
||||
{:name :enable-notifications
|
||||
:options {:statusBar {:style :light}
|
||||
:topBar {:visible false}
|
||||
:navigationBar {:backgroundColor colors/black}}
|
||||
:options {:statusBar {:style :light}
|
||||
:navigationBar {:backgroundColor colors/black}
|
||||
:popGesture false
|
||||
:hardwareBackButton {:dismissModalOnPress false
|
||||
:popStackOnPress false}
|
||||
:topBar {:visible false
|
||||
:backButton {:popStackOnPress false}}}
|
||||
:insets {:top false}
|
||||
:component enable-notifications/enable-notifications}
|
||||
|
||||
|
||||
+149
-37
@@ -1,69 +1,181 @@
|
||||
(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]
|
||||
[reagent.core :as reagent]))
|
||||
(:require
|
||||
["@testing-library/react-native" :as rtl]
|
||||
[camel-snake-kebab.core :as camel-snake-kebab]
|
||||
[reagent.core :as reagent]
|
||||
[utils.i18n :as i18n]))
|
||||
|
||||
;;;; React Native Testing Library
|
||||
|
||||
(defn- with-node-or-screen
|
||||
"Wrap RN Testing Library `method-name` and call it either on a ReactTestInstance
|
||||
or directly on the screen object.
|
||||
|
||||
`method-name` can be either the name of the native method, or a kebab case
|
||||
keyword.
|
||||
|
||||
It is often necessary in REPL sessions to call methods on the returned
|
||||
instance of RNTL `render` method, otherwise you can get weird errors, like
|
||||
'Unable to find node on an unmounted component'. This happens because RNTL was
|
||||
mainly conceptualized to run inside a test runner that automatically cleans up
|
||||
everything after each test.
|
||||
|
||||
Usage:
|
||||
(def get-by-text (wrap-screen-or-node :get-by-text))
|
||||
|
||||
In another file, and with the REPL running with the Shadow-CLJS `:mobile`
|
||||
target:
|
||||
|
||||
(comment
|
||||
;; Consider using a shorter var name when playing in a REPL.
|
||||
(def component (h/render [quo/counter {} 50]))
|
||||
(h/get-by-text component \"50\")
|
||||
|
||||
;; Or without the node it works too, but it is only reliable inside
|
||||
;; a test runner.
|
||||
(h/get-by-text \"50\"))
|
||||
"
|
||||
[method-name]
|
||||
(let [method-name (camel-snake-kebab/->camelCaseString method-name)]
|
||||
(fn [& args]
|
||||
(if (= js/Object (type (first args))) ; Check if it's a node instance.
|
||||
(let [method (aget (first args) method-name)]
|
||||
(apply method (clj->js (rest args))))
|
||||
(let [method (aget rtl/screen method-name)]
|
||||
(apply method (clj->js args)))))))
|
||||
|
||||
(defn render
|
||||
[component]
|
||||
(rtl/render (reagent/as-element component)))
|
||||
|
||||
(def unmount
|
||||
"Unmount rendered component.
|
||||
Sometimes useful to be called in a REPL, but unnecessary when rendering
|
||||
components with Jest, since components are automatically unmounted after each
|
||||
test."
|
||||
(with-node-or-screen :unmount))
|
||||
|
||||
(def debug
|
||||
"Pretty-print to STDOUT the current component tree."
|
||||
(with-node-or-screen :debug))
|
||||
|
||||
(defn fire-event
|
||||
([event-name element]
|
||||
(fire-event event-name element nil))
|
||||
([event-name element data]
|
||||
([event-name node]
|
||||
(fire-event event-name node nil))
|
||||
([event-name node data]
|
||||
(rtl/fireEvent
|
||||
element
|
||||
node
|
||||
(camel-snake-kebab/->camelCaseString event-name)
|
||||
(clj->js data))))
|
||||
|
||||
(defn debug
|
||||
[element]
|
||||
(rtl/screen.debug element))
|
||||
;;; Queries: find-*
|
||||
;;
|
||||
;; find-* functions don't work in the REPL because the returned promise is
|
||||
;; always rejected with ReferenceError: Can't find variable: MessageChannel
|
||||
;;
|
||||
;; For this reason, find-* functions only work within the Jest runtime, hence
|
||||
;; using the wrapper function `with-node-or-screen` is unnecessary.
|
||||
|
||||
(defn get-by-test-id
|
||||
[test-id]
|
||||
(rtl/screen.getByTestId (name test-id)))
|
||||
(def find-by-text (comp rtl/screen.findByText name))
|
||||
|
||||
(defn get-by-text
|
||||
[text]
|
||||
(rtl/screen.getByText text))
|
||||
;;; Queries that work with a REPL and with Jest
|
||||
|
||||
(defn find-by-text
|
||||
[text]
|
||||
(rtl/screen.findByText text))
|
||||
(def get-all-by-text (with-node-or-screen :get-all-by-text))
|
||||
(def get-by-text (with-node-or-screen :get-by-text))
|
||||
(def query-all-by-text (with-node-or-screen :query-all-by-text))
|
||||
(def query-by-text (with-node-or-screen :query-by-text))
|
||||
|
||||
(defn get-by-label-text
|
||||
[label]
|
||||
(rtl/screen.getByLabelText (name label)))
|
||||
(def get-all-by-label-text (with-node-or-screen :get-all-by-label-text))
|
||||
(def get-by-label-text (with-node-or-screen :get-by-label-text))
|
||||
(def query-all-by-label-text (with-node-or-screen :query-all-by-label-text))
|
||||
(def query-by-label-text (with-node-or-screen :query-by-label-text))
|
||||
|
||||
(defn query-by-label-text
|
||||
"Returns `nil` when label is not found."
|
||||
[label]
|
||||
(rtl/screen.queryByLabelText (name label)))
|
||||
(def get-all-by-display-value (with-node-or-screen :get-all-by-display-value))
|
||||
(def get-by-display-value (with-node-or-screen :get-by-display-value))
|
||||
(def query-all-by-display-value (with-node-or-screen :query-all-by-display-value))
|
||||
(def query-by-display-value (with-node-or-screen :query-by-display-value))
|
||||
|
||||
(def get-all-by-placeholder-text (with-node-or-screen :get-all-by-placeholder-text))
|
||||
(def get-by-placeholder-text (with-node-or-screen :get-by-placeholder-text))
|
||||
(def query-all-by-placeholder-text (with-node-or-screen :query-all-by-placeholder-text))
|
||||
(def query-by-placeholder-text (with-node-or-screen :query-by-placeholder-text))
|
||||
|
||||
(def get-all-by-role (with-node-or-screen :get-all-by-role))
|
||||
(def get-by-role (with-node-or-screen :get-by-role))
|
||||
(def query-all-by-role (with-node-or-screen :query-all-by-role))
|
||||
(def query-by-role (with-node-or-screen :query-by-role))
|
||||
|
||||
(def get-all-by-test-id (with-node-or-screen :get-all-by-test-id))
|
||||
(def get-by-test-id (with-node-or-screen :get-by-test-id))
|
||||
(def query-all-by-test-id (with-node-or-screen :query-all-by-test-id))
|
||||
(def query-by-test-id (with-node-or-screen :query-by-test-id))
|
||||
|
||||
(defn- prepare-translation
|
||||
[translation]
|
||||
(if (exists? js/jest)
|
||||
;; Translations are treated differently when running with Jest. See
|
||||
;; test/jest/jestSetup.js for more details.
|
||||
(str "tx:" (name translation))
|
||||
(i18n/label translation)))
|
||||
|
||||
(defn get-all-by-translation-text
|
||||
([translation]
|
||||
(get-all-by-translation-text rtl/screen translation))
|
||||
([^js node translation & args]
|
||||
(apply (with-node-or-screen :get-all-by-text) node (prepare-translation translation) args)))
|
||||
|
||||
(defn get-by-translation-text
|
||||
[keyword]
|
||||
(get-by-text (str "tx:" (name keyword))))
|
||||
([translation]
|
||||
(get-by-translation-text rtl/screen translation))
|
||||
([^js node translation & args]
|
||||
(apply (with-node-or-screen :get-by-text) node (prepare-translation translation) args)))
|
||||
|
||||
(defn get-all-by-label-text
|
||||
[label]
|
||||
(rtl/screen.getAllByLabelText (name label)))
|
||||
(defn query-by-translation-text
|
||||
([translation]
|
||||
(query-by-translation-text rtl/screen translation))
|
||||
([^js node translation & args]
|
||||
(apply (with-node-or-screen :query-by-text) node (prepare-translation translation) args)))
|
||||
|
||||
(defn expect [match] (js/expect match))
|
||||
(defn query-all-by-translation-text
|
||||
([translation]
|
||||
(query-all-by-translation-text rtl/screen translation))
|
||||
([^js node translation & args]
|
||||
(apply (with-node-or-screen :query-all-by-text) node (prepare-translation translation) args)))
|
||||
|
||||
(defn use-fake-timers [] (js/jest.useFakeTimers))
|
||||
;;; Jest utilities
|
||||
|
||||
(defn clear-all-timers [] (js/jest.clearAllTimers))
|
||||
(def ^:private jest?
|
||||
(exists? js/jest))
|
||||
|
||||
(defn use-real-timers [] (js/jest.useRealTimers))
|
||||
(defn expect
|
||||
[match]
|
||||
(js/expect match))
|
||||
|
||||
(defn use-fake-timers
|
||||
[]
|
||||
(when jest?
|
||||
(js/jest.useFakeTimers)))
|
||||
|
||||
(defn clear-all-timers
|
||||
[]
|
||||
(when jest?
|
||||
(js/jest.clearAllTimers)))
|
||||
|
||||
(defn use-real-timers
|
||||
[]
|
||||
(when jest?
|
||||
(js/jest.useRealTimers)))
|
||||
|
||||
(defn advance-timers-by-time
|
||||
[time-ms]
|
||||
(js/jest.advanceTimersByTime time-ms))
|
||||
(when jest?
|
||||
(js/jest.advanceTimersByTime time-ms)))
|
||||
|
||||
(def mock-fn js/jest.fn)
|
||||
(def mock-fn
|
||||
(when jest?
|
||||
js/jest.fn))
|
||||
|
||||
(defn is-truthy
|
||||
[element]
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"_comment": "Instead use: scripts/update-status-go.sh <rev>",
|
||||
"owner": "status-im",
|
||||
"repo": "status-go",
|
||||
"version": "v0.140.2",
|
||||
"commit-sha1": "bcca0b399d8b8b3f0ef438c4ebc39f4f4359da26",
|
||||
"src-sha256": "1m625v69x4mzh5gnvxnf0i6xxyxhwc1ic39f5xf72ybaika1arzn"
|
||||
"version": "v0.141.1",
|
||||
"commit-sha1": "458f28178ca15732df8e6c806ef5326fad44427c",
|
||||
"src-sha256": "1hhfswn17dja62rxkb572afd6fsx86wfxs4j666lhscdz7zzs4gx"
|
||||
}
|
||||
|
||||
@@ -2052,5 +2052,7 @@
|
||||
"ensure-qr-code-is-in-focus-to-scan":"Ensure that the QR code is in focus to scan",
|
||||
"error-this-is-not-a-sync-qr-code": "Oops! This is not a sync QR code",
|
||||
"error-syncing-connection-failed": "Oops! Connection failed. Try again",
|
||||
"camera-permission-denied": "Permission denied"
|
||||
"camera-permission-denied": "Permission denied",
|
||||
"enable-biometrics": "Enable biometrics",
|
||||
"use-biometrics": "Use biometrics to fill in your password"
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user