feat: implement onboarding modal transition for sign in screen (#16167)
This commit is contained in:
parent
affd2a5e76
commit
c2c79cc1ac
|
@ -1,9 +1,19 @@
|
|||
(ns quo2.components.drawers.drawer-buttons.component-spec
|
||||
(:require [quo2.components.drawers.drawer-buttons.view :as drawer-buttons]
|
||||
[react-native.core :as rn]
|
||||
[test-helpers.component :as h]))
|
||||
[test-helpers.component :as h]
|
||||
[react-native.safe-area :as safe-area]))
|
||||
|
||||
(h/describe "drawer-buttons"
|
||||
(h/before-each
|
||||
(fn []
|
||||
(h/use-fake-timers)))
|
||||
|
||||
(h/after-each
|
||||
(fn []
|
||||
(h/clear-all-timers)
|
||||
(h/use-real-timers)))
|
||||
|
||||
(h/test "the top heading and subheading render"
|
||||
(h/render [drawer-buttons/view
|
||||
{:top-card {:heading :top-heading}
|
||||
|
@ -29,15 +39,16 @@
|
|||
|
||||
(h/test "it clicks the top card"
|
||||
(let [event (h/mock-fn)]
|
||||
(h/render [drawer-buttons/view
|
||||
{:top-card {:on-press event
|
||||
:heading :top-heading}
|
||||
:bottom-card {:heading :bottom-heading}}
|
||||
:top-sub-heading
|
||||
:bottom-sub-heading])
|
||||
(h/fire-event :press (h/get-by-text "top-heading"))
|
||||
(-> (js/expect event)
|
||||
(.toHaveBeenCalled))))
|
||||
(with-redefs [safe-area/get-top (fn [] 10)]
|
||||
(h/render [drawer-buttons/view
|
||||
{:top-card {:on-press event
|
||||
:heading :top-heading}
|
||||
:bottom-card {:heading :bottom-heading}}
|
||||
:top-sub-heading
|
||||
:bottom-sub-heading])
|
||||
(h/fire-event :press (h/get-by-text "top-heading"))
|
||||
(-> (js/expect event)
|
||||
(.toHaveBeenCalled)))))
|
||||
|
||||
(h/test "it clicks the bottom card"
|
||||
(let [event (h/mock-fn)]
|
||||
|
|
|
@ -1,11 +1,20 @@
|
|||
(ns quo2.components.drawers.drawer-buttons.style
|
||||
(:require [quo2.foundations.colors :as colors]))
|
||||
(:require [quo2.foundations.colors :as colors]
|
||||
[react-native.reanimated :as reanimated]))
|
||||
|
||||
(def outer-container
|
||||
{:height 216
|
||||
:border-top-left-radius 20
|
||||
:border-top-right-radius 20
|
||||
:overflow :hidden})
|
||||
(defn outer-container
|
||||
[height border-radius container-style]
|
||||
(reanimated/apply-animations-to-style
|
||||
{:height height
|
||||
:border-top-left-radius border-radius
|
||||
:border-top-right-radius border-radius}
|
||||
(assoc
|
||||
container-style
|
||||
:position :absolute
|
||||
:left 0
|
||||
:right 0
|
||||
:bottom 0
|
||||
:overflow :hidden)))
|
||||
|
||||
(def top-card
|
||||
{:flex 1
|
||||
|
@ -17,7 +26,6 @@
|
|||
|
||||
(def bottom-card
|
||||
{:position :absolute
|
||||
:top 80
|
||||
:left 0
|
||||
:right 0
|
||||
:bottom 0
|
||||
|
@ -41,6 +49,20 @@
|
|||
:align-items :center
|
||||
:border-color colors/white-opa-5})
|
||||
|
||||
(def blur-view-style
|
||||
{:flex 1
|
||||
:border-top-left-radius 20
|
||||
:border-top-right-radius 20})
|
||||
|
||||
(def blur-content-style
|
||||
{:flex 1
|
||||
:background-color :transparent
|
||||
:position :absolute
|
||||
:top 0
|
||||
:left 0
|
||||
:right 0
|
||||
:bottom 0})
|
||||
|
||||
(def bottom-text
|
||||
{:flex 1
|
||||
:color colors/white-70-blur})
|
||||
|
|
|
@ -4,7 +4,12 @@
|
|||
[quo2.components.markdown.text :as text]
|
||||
[quo2.foundations.colors :as colors]
|
||||
[react-native.blur :as blur]
|
||||
[quo2.components.drawers.drawer-buttons.style :as style]))
|
||||
[quo2.components.drawers.drawer-buttons.style :as style]
|
||||
[react-native.reanimated :as reanimated]
|
||||
[react-native.platform :as platform]
|
||||
[react-native.safe-area :as safe-area]))
|
||||
|
||||
(def default-height 216)
|
||||
|
||||
(defn render-bottom
|
||||
[children]
|
||||
|
@ -29,35 +34,74 @@
|
|||
children]]
|
||||
[render-bottom children]))
|
||||
|
||||
(defn render-children-top
|
||||
[children]
|
||||
(if (label? children)
|
||||
[text/text
|
||||
{:size :paragraph-2
|
||||
:style style/top-text
|
||||
:weight :semi-bold}
|
||||
children]
|
||||
children))
|
||||
(defn- f-render-children-top
|
||||
[children top-children-opacity]
|
||||
(let [scale (reanimated/interpolate top-children-opacity [1 0] [1 1.1])
|
||||
padding-left (reanimated/interpolate scale [1 1.1] [0 14])]
|
||||
[reanimated/view
|
||||
{:style (reanimated/apply-animations-to-style
|
||||
{:opacity top-children-opacity
|
||||
:transform [{:scale scale}
|
||||
{:translate-x padding-left}]}
|
||||
{})}
|
||||
(if (label? children)
|
||||
[text/text
|
||||
{:size :paragraph-2
|
||||
:style style/top-text
|
||||
:weight :semi-bold}
|
||||
children]
|
||||
children)]))
|
||||
|
||||
(defn card
|
||||
[{:keys [on-press style heading gap accessibility-label top?]} children]
|
||||
(defn- f-card
|
||||
[{:keys [on-press default-on-press style heading gap accessibility-label top? top-title-opacity
|
||||
top-children-opacity
|
||||
animated-heading]
|
||||
:or {top-title-opacity (reanimated/use-shared-value 1)}}
|
||||
children]
|
||||
[rn/touchable-highlight
|
||||
{:accessibility-label accessibility-label
|
||||
:on-press on-press
|
||||
:on-press (fn []
|
||||
(when on-press
|
||||
(on-press))
|
||||
(when default-on-press
|
||||
(default-on-press)))
|
||||
:border-radius 20
|
||||
:style style
|
||||
:underlay-color (:background-color style)}
|
||||
[rn/view
|
||||
[text/text
|
||||
{:size :heading-1
|
||||
:style (style/heading-text gap)
|
||||
:weight :semi-bold}
|
||||
heading]
|
||||
[reanimated/view
|
||||
{:style (reanimated/apply-animations-to-style
|
||||
{:opacity top-title-opacity}
|
||||
{})}
|
||||
[text/text
|
||||
{:size :heading-1
|
||||
:style (style/heading-text gap)
|
||||
:weight :semi-bold}
|
||||
heading]]
|
||||
(when animated-heading
|
||||
(let [animated-heading-opacity (reanimated/interpolate top-children-opacity [1 0] [0 1])]
|
||||
[reanimated/view
|
||||
{:style (reanimated/apply-animations-to-style
|
||||
{:opacity top-title-opacity}
|
||||
{:position :absolute})}
|
||||
[reanimated/view
|
||||
{:style (reanimated/apply-animations-to-style
|
||||
{:opacity animated-heading-opacity}
|
||||
{})}
|
||||
[text/text
|
||||
{:size :heading-1
|
||||
:style (style/heading-text gap)
|
||||
:weight :semi-bold}
|
||||
animated-heading]]]))
|
||||
(if top?
|
||||
[render-children-top children]
|
||||
[:f> f-render-children-top children top-children-opacity]
|
||||
[render-children-bottom children])]])
|
||||
|
||||
(defn view
|
||||
(defn card
|
||||
[props children]
|
||||
[:f> f-card props children])
|
||||
|
||||
(defn f-view
|
||||
"[view opts]
|
||||
opts
|
||||
{:container-style style-object
|
||||
|
@ -70,29 +114,101 @@
|
|||
child-1 string, keyword or hiccup
|
||||
child-2 string, keyword or hiccup
|
||||
"
|
||||
[{:keys [container-style top-card bottom-card]} child-1 child-2]
|
||||
[rn/view
|
||||
{:style (merge container-style style/outer-container)}
|
||||
[blur/view
|
||||
{:blur-type :dark
|
||||
:blur-amount 10
|
||||
:style {:flex 1
|
||||
:border-top-left-radius 20
|
||||
:border-top-right-radius 20}}]
|
||||
[rn/view
|
||||
{:style {:flex 1
|
||||
:background-color :transparent
|
||||
:position :absolute
|
||||
:top 0
|
||||
:left 0
|
||||
:right 0
|
||||
:bottom 0}}
|
||||
[card
|
||||
(merge {:gap 4
|
||||
:top? true
|
||||
:style style/top-card}
|
||||
top-card) child-1]
|
||||
[card
|
||||
(merge {:style style/bottom-card
|
||||
:gap 20}
|
||||
bottom-card) child-2]]])
|
||||
[{:keys [container-style top-card bottom-card on-init animations-duration animations-delay]}
|
||||
child-1
|
||||
child-2]
|
||||
(let [max-height (+ (:height (rn/get-window)) (if platform/android? (safe-area/get-top) 0))
|
||||
height (reanimated/use-shared-value default-height)
|
||||
top-padding (reanimated/use-shared-value 12)
|
||||
border-radius (reanimated/use-shared-value 20)
|
||||
bottom-view-top (reanimated/use-shared-value 80)
|
||||
top-title-opacity (reanimated/use-shared-value 1)
|
||||
top-children-opacity (reanimated/use-shared-value 1)
|
||||
animations-delay (/ animations-delay 1.4)
|
||||
start-top-animation (fn []
|
||||
(reanimated/animate-shared-value-with-delay bottom-view-top
|
||||
(:height (rn/get-screen))
|
||||
animations-duration
|
||||
:easing4
|
||||
animations-delay)
|
||||
(reanimated/animate-shared-value-with-delay
|
||||
height
|
||||
max-height
|
||||
animations-duration
|
||||
:easing4
|
||||
animations-delay)
|
||||
(reanimated/animate-shared-value-with-delay
|
||||
top-padding
|
||||
(+ 68 (safe-area/get-top))
|
||||
animations-duration
|
||||
:easing4
|
||||
animations-delay)
|
||||
(reanimated/animate-shared-value-with-delay
|
||||
top-children-opacity
|
||||
0
|
||||
animations-duration
|
||||
:easing4 animations-delay)
|
||||
(reanimated/animate-shared-value-with-delay
|
||||
top-title-opacity
|
||||
0
|
||||
0
|
||||
:linear
|
||||
(+ animations-delay animations-duration 500)))
|
||||
reset-top-animation (fn []
|
||||
(reanimated/set-shared-value top-title-opacity 1)
|
||||
(reanimated/animate-shared-value-with-delay bottom-view-top
|
||||
80
|
||||
animations-duration
|
||||
:easing4
|
||||
50)
|
||||
(reanimated/animate-shared-value-with-timing
|
||||
height
|
||||
default-height
|
||||
animations-duration
|
||||
:easing4)
|
||||
(reanimated/animate-shared-value-with-timing
|
||||
top-padding
|
||||
12
|
||||
animations-duration
|
||||
:easing4)
|
||||
(reanimated/animate-shared-value-with-timing
|
||||
top-children-opacity
|
||||
1
|
||||
animations-duration
|
||||
:easing4))]
|
||||
(rn/use-effect (fn []
|
||||
(when on-init
|
||||
(on-init reset-top-animation))))
|
||||
[reanimated/view {:style (style/outer-container height border-radius container-style)}
|
||||
[blur/view
|
||||
{:blur-type :dark
|
||||
:blur-amount 10
|
||||
:style style/blur-view-style}
|
||||
[rn/view
|
||||
{:style style/blur-content-style}
|
||||
[reanimated/view
|
||||
{:style (reanimated/apply-animations-to-style
|
||||
{:padding-top top-padding}
|
||||
style/top-card)}
|
||||
[card
|
||||
(merge {:gap 4
|
||||
:top? true
|
||||
:style {:flex 1}
|
||||
:top-children-opacity top-children-opacity
|
||||
:top-title-opacity top-title-opacity
|
||||
:default-on-press start-top-animation}
|
||||
top-card)
|
||||
child-1]]
|
||||
[reanimated/view
|
||||
{:style (reanimated/apply-animations-to-style
|
||||
{:top bottom-view-top}
|
||||
style/bottom-card)}
|
||||
[card
|
||||
(merge {:style {:flex 1}
|
||||
:gap 10}
|
||||
bottom-card)
|
||||
child-2]]]]]))
|
||||
|
||||
(defn view
|
||||
[props child-1 child-2]
|
||||
[:f> f-view props child-1 child-2])
|
||||
|
|
|
@ -81,7 +81,8 @@
|
|||
{:linear (bezier 0 0 1 1)
|
||||
:easing1 (bezier 0.25 0.1 0.25 1)
|
||||
:easing2 (bezier 0 0.3 0.6 0.9)
|
||||
:easing3 (bezier 0.3 0.3 0.3 0.9)})
|
||||
:easing3 (bezier 0.3 0.3 0.3 0.9)
|
||||
:easing4 (bezier 0.3 0.3 1 1)})
|
||||
|
||||
;; Helper functions
|
||||
(defn get-shared-value
|
||||
|
|
|
@ -80,7 +80,9 @@
|
|||
multiaccount-data (when received-account?
|
||||
(merge account {:password password}))
|
||||
navigate-to-syncing-devices? (and (or connection-success? error-on-pairing?) receiver?)
|
||||
user-in-syncing-devices-screen? (= (:view-id db) :syncing-progress)]
|
||||
user-in-syncing-devices-screen? (or (= (:view-id db) :syncing-progress)
|
||||
(= (:view-id db) :syncing-progress-intro))
|
||||
user-in-sign-in-intro-screen? (= (:view-id db) :sign-in-intro)]
|
||||
(merge {:db (cond-> db
|
||||
connection-success?
|
||||
(assoc-in [:syncing :pairing-status] :connected)
|
||||
|
@ -95,7 +97,9 @@
|
|||
(assoc-in [:syncing :pairing-status] :completed))}
|
||||
(cond
|
||||
(and navigate-to-syncing-devices? (not user-in-syncing-devices-screen?))
|
||||
{:dispatch [:navigate-to :syncing-progress]}
|
||||
{:dispatch (if user-in-sign-in-intro-screen?
|
||||
[:navigate-to-within-stack [:syncing-progress-intro :sign-in-intro]]
|
||||
[:navigate-to :syncing-progress])}
|
||||
|
||||
(and completed-pairing? sender?)
|
||||
{:dispatch [:syncing/clear-states]}
|
||||
|
|
|
@ -336,3 +336,6 @@
|
|||
(def ^:const image-description-in-lightbox? false)
|
||||
|
||||
(def ^:const audio-max-duration-ms 120000)
|
||||
|
||||
(def ^:const onboarding-modal-animation-duration 300)
|
||||
(def ^:const onboarding-modal-animation-delay 400)
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
{:background-color colors/neutral-95
|
||||
:flex-direction :row
|
||||
:position :absolute
|
||||
:overflow :hidden
|
||||
:top 0
|
||||
:bottom 0
|
||||
:left 0
|
||||
|
|
|
@ -4,23 +4,34 @@
|
|||
[utils.re-frame :as rf]
|
||||
[react-native.core :as rn]
|
||||
[status-im2.contexts.onboarding.intro.style :as style]
|
||||
[status-im2.contexts.onboarding.common.background.view :as background]))
|
||||
[status-im2.contexts.onboarding.common.background.view :as background]
|
||||
[status-im2.constants :as constants]
|
||||
[status-im2.contexts.syncing.scan-sync-code.view :as scan-sync-code]
|
||||
[utils.debounce :as debounce]))
|
||||
|
||||
(defn view
|
||||
[]
|
||||
[rn/view {:style style/page-container}
|
||||
[background/view false]
|
||||
[quo/drawer-buttons
|
||||
{:top-card {:on-press (fn []
|
||||
(rf/dispatch [:navigate-to :sign-in])
|
||||
(rf/dispatch [:hide-terms-of-services-opt-in-screen]))
|
||||
:heading (i18n/label :t/sign-in)
|
||||
:accessibility-label :already-use-status-button}
|
||||
:bottom-card {:on-press (fn []
|
||||
(rf/dispatch [:navigate-to :new-to-status])
|
||||
(rf/dispatch [:hide-terms-of-services-opt-in-screen]))
|
||||
:heading (i18n/label :t/new-to-status)
|
||||
:accessibility-label :new-to-status-button}}
|
||||
{:on-init (fn [reset-top-animation-fn]
|
||||
(reset! scan-sync-code/dismiss-animations reset-top-animation-fn))
|
||||
:animations-duration constants/onboarding-modal-animation-duration
|
||||
:animations-delay constants/onboarding-modal-animation-delay
|
||||
:top-card {:on-press (fn []
|
||||
(debounce/dispatch-and-chill [:open-modal
|
||||
:sign-in-intro]
|
||||
2000)
|
||||
(rf/dispatch [:hide-terms-of-services-opt-in-screen]))
|
||||
:heading (i18n/label :t/sign-in)
|
||||
:animated-heading (i18n/label :t/sign-in-by-syncing)
|
||||
:accessibility-label :already-use-status-button}
|
||||
:bottom-card {:on-press (fn []
|
||||
(rf/dispatch [:navigate-to :new-to-status])
|
||||
(rf/dispatch
|
||||
[:hide-terms-of-services-opt-in-screen]))
|
||||
:heading (i18n/label :t/new-to-status)
|
||||
:accessibility-label :new-to-status-button}}
|
||||
[quo/text
|
||||
{:style style/plain-text}
|
||||
(i18n/label :t/you-already-use-status)]
|
||||
|
|
|
@ -28,7 +28,7 @@
|
|||
:accessibility-label :create-new-profile}
|
||||
{:icon :i/multi-profile
|
||||
:label (i18n/label :t/add-existing-status-profile)
|
||||
:on-press #(rf/dispatch [:navigate-to :sign-in])
|
||||
:on-press #(rf/dispatch [:open-modal :sign-in])
|
||||
:accessibility-label :multi-profile}]]])
|
||||
|
||||
(defn show-new-account-options
|
||||
|
|
|
@ -3,9 +3,22 @@
|
|||
[status-im2.contexts.onboarding.common.background.view :as background]
|
||||
[status-im2.contexts.syncing.scan-sync-code.view :as scan-sync-code]))
|
||||
|
||||
(defn navigate-back
|
||||
[]
|
||||
(when @scan-sync-code/navigate-back-fn
|
||||
(@scan-sync-code/navigate-back-fn)))
|
||||
|
||||
(defn view
|
||||
[]
|
||||
[scan-sync-code/view
|
||||
{:title (i18n/label :t/sign-in-by-syncing)
|
||||
:show-bottom-view? true
|
||||
:background [background/view true]}])
|
||||
:background [background/view true]
|
||||
:animated? false}])
|
||||
|
||||
(defn animated-view
|
||||
[]
|
||||
[scan-sync-code/view
|
||||
{:title (i18n/label :t/sign-in-by-syncing)
|
||||
:show-bottom-view? true
|
||||
:animated? true}])
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
(ns status-im2.contexts.onboarding.syncing.progress.style
|
||||
(:require [quo2.foundations.colors :as colors]))
|
||||
|
||||
(def page-container
|
||||
(defn page-container
|
||||
[in-onboarding?]
|
||||
{:flex 1
|
||||
:position :absolute
|
||||
:top 0
|
||||
|
@ -9,7 +10,7 @@
|
|||
:left 0
|
||||
:right 0
|
||||
:padding-bottom 20
|
||||
:background-color colors/neutral-80-opa-80-blur})
|
||||
:background-color (when-not in-onboarding? colors/neutral-80-opa-80-blur)})
|
||||
|
||||
(def page-illustration
|
||||
{:flex 1
|
||||
|
|
|
@ -30,22 +30,23 @@
|
|||
:subtitle-accessibility-label :progress-screen-sub-title}])
|
||||
|
||||
(defn try-again-button
|
||||
[profile-color]
|
||||
[profile-color in-onboarding?]
|
||||
[quo/button
|
||||
{:on-press (fn []
|
||||
(rf/dispatch [:syncing/clear-states])
|
||||
(rf/dispatch [:navigate-back]))
|
||||
(rf/dispatch [:navigate-back-to
|
||||
(if in-onboarding? :sign-in-intro :sign-in)]))
|
||||
:accessibility-label :try-again-later-button
|
||||
:override-background-color (colors/custom-color profile-color 60)
|
||||
:style style/try-again-button}
|
||||
(i18n/label :t/try-again)])
|
||||
|
||||
(defn view
|
||||
[]
|
||||
[in-onboarding?]
|
||||
(let [pairing-status (rf/sub [:pairing/pairing-status])
|
||||
profile-color (:color (rf/sub [:onboarding-2/profile]))]
|
||||
[rn/view {:style style/page-container}
|
||||
[background/view true]
|
||||
[rn/view {:style (style/page-container in-onboarding?)}
|
||||
(when-not in-onboarding? [background/view true])
|
||||
[quo/page-nav]
|
||||
[page-title (pairing-progress pairing-status)]
|
||||
(if (pairing-progress pairing-status)
|
||||
|
@ -54,4 +55,8 @@
|
|||
[rn/view {:style style/page-illustration}
|
||||
[quo/text "[Error here]"]])
|
||||
(when-not (pairing-progress pairing-status)
|
||||
[try-again-button profile-color])]))
|
||||
[try-again-button profile-color in-onboarding?])]))
|
||||
|
||||
(defn view-onboarding
|
||||
[]
|
||||
[view true])
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
(ns status-im2.contexts.syncing.scan-sync-code.style
|
||||
(:require [quo2.foundations.colors :as colors]))
|
||||
(:require [quo2.foundations.colors :as colors]
|
||||
[react-native.reanimated :as reanimated]))
|
||||
|
||||
(def screen-padding 20)
|
||||
|
||||
|
@ -62,7 +63,7 @@
|
|||
[viewfinder]
|
||||
{:position :absolute
|
||||
:left (:x viewfinder)
|
||||
:top (:y viewfinder)})
|
||||
:top 19})
|
||||
|
||||
(def view-finder-border-container
|
||||
{:flex-direction :row
|
||||
|
@ -104,6 +105,7 @@
|
|||
|
||||
(def camera-permission-container
|
||||
{:height 335
|
||||
:margin-top 19
|
||||
:margin-horizontal screen-padding
|
||||
:background-color colors/white-opa-5
|
||||
:border-color colors/white-opa-10
|
||||
|
@ -126,15 +128,17 @@
|
|||
:align-items :center})
|
||||
|
||||
(defn bottom-container
|
||||
[padding-bottom]
|
||||
{:z-index 6
|
||||
:padding-top 12
|
||||
:padding-bottom padding-bottom
|
||||
:background-color colors/white-opa-5
|
||||
:border-top-left-radius 20
|
||||
:border-top-right-radius 20
|
||||
:align-items :center
|
||||
:justify-content :center})
|
||||
[translate-y padding-bottom]
|
||||
(reanimated/apply-animations-to-style
|
||||
{:transform [{:translate-y translate-y}]}
|
||||
{:z-index 6
|
||||
:padding-top 12
|
||||
:padding-bottom padding-bottom
|
||||
:background-color colors/white-opa-5
|
||||
:border-top-left-radius 20
|
||||
:border-top-right-radius 20
|
||||
:align-items :center
|
||||
:justify-content :center}))
|
||||
|
||||
(def bottom-text
|
||||
{:color colors/white
|
||||
|
|
|
@ -14,12 +14,17 @@
|
|||
[utils.i18n :as i18n]
|
||||
[utils.re-frame :as rf]
|
||||
[status-im2.contexts.syncing.utils :as sync-utils]
|
||||
[status-im.utils.platform :as platform]))
|
||||
[status-im.utils.platform :as platform]
|
||||
[react-native.reanimated :as reanimated]
|
||||
[status-im2.constants :as constants]
|
||||
[utils.debounce :as debounce]))
|
||||
|
||||
;; Android allow local network access by default. So, we need this check on iOS only.
|
||||
(defonce preflight-check-passed? (reagent/atom (if platform/ios? false true)))
|
||||
|
||||
(defonce camera-permission-granted? (reagent/atom false))
|
||||
(defonce dismiss-animations (atom nil))
|
||||
(defonce navigate-back-fn (atom nil))
|
||||
|
||||
(defn request-camera-permission
|
||||
[]
|
||||
|
@ -42,47 +47,83 @@
|
|||
[]
|
||||
(rf/dispatch [:syncing/preflight-outbound-check #(reset! preflight-check-passed? %)]))
|
||||
|
||||
(defn- f-header
|
||||
[{:keys [active-tab read-qr-once? title title-opacity subtitle-opacity reset-animations-fn animated?]}]
|
||||
(let [subtitle-translate-x (reanimated/interpolate subtitle-opacity [0 1] [-13 0])
|
||||
subtitle-translate-y (reanimated/interpolate subtitle-opacity [0 1] [-85 0])
|
||||
subtitle-scale (reanimated/interpolate subtitle-opacity [0 1] [0.9 1])
|
||||
controls-translate-y (reanimated/interpolate subtitle-opacity [0 1] [85 0])]
|
||||
[:<>
|
||||
[rn/view {:style style/header-container}
|
||||
[reanimated/view
|
||||
{:style (reanimated/apply-animations-to-style
|
||||
{:opacity subtitle-opacity
|
||||
:transform [{:translate-y controls-translate-y}]}
|
||||
{})}
|
||||
[quo/button
|
||||
{:icon true
|
||||
:type :blur-bg
|
||||
:size 32
|
||||
:accessibility-label :close-sign-in-by-syncing
|
||||
:override-theme :dark
|
||||
:on-press (fn []
|
||||
(if (and animated? reset-animations-fn)
|
||||
(reset-animations-fn)
|
||||
(rf/dispatch [:navigate-back])))}
|
||||
:i/arrow-left]]
|
||||
[reanimated/view
|
||||
{:style (reanimated/apply-animations-to-style
|
||||
{:opacity subtitle-opacity
|
||||
:transform [{:translate-y controls-translate-y}]}
|
||||
{})}
|
||||
[quo/button
|
||||
{:before :i/info
|
||||
:type :blur-bg
|
||||
:size 32
|
||||
:accessibility-label :find-sync-code
|
||||
:override-theme :dark
|
||||
:on-press #(rf/dispatch [:open-modal :find-sync-code])}
|
||||
(i18n/label :t/find-sync-code)]]]
|
||||
[reanimated/view
|
||||
{:style (reanimated/apply-animations-to-style
|
||||
{:opacity title-opacity}
|
||||
{})}
|
||||
[quo/text
|
||||
{:size :heading-1
|
||||
:weight :semi-bold
|
||||
:style style/header-text}
|
||||
title]]
|
||||
[reanimated/view
|
||||
{:style (reanimated/apply-animations-to-style
|
||||
{:opacity subtitle-opacity
|
||||
:transform [{:translate-x subtitle-translate-x}
|
||||
{:translate-y subtitle-translate-y}
|
||||
{:scale subtitle-scale}]}
|
||||
{})}
|
||||
[quo/text
|
||||
{:size :paragraph-1
|
||||
:weight :regular
|
||||
:style style/header-sub-text}
|
||||
(i18n/label :t/synchronise-your-data-across-your-devices)]]
|
||||
[reanimated/view
|
||||
{:style (reanimated/apply-animations-to-style
|
||||
{:opacity subtitle-opacity
|
||||
:transform [{:translate-y controls-translate-y}]}
|
||||
style/tabs-container)}
|
||||
[quo/segmented-control
|
||||
{:size 32
|
||||
:override-theme :dark
|
||||
:blur? true
|
||||
:default-active @active-tab
|
||||
:data [{:id 1 :label (i18n/label :t/scan-sync-qr-code)}
|
||||
{:id 2 :label (i18n/label :t/enter-sync-code)}]
|
||||
:on-change (fn [id]
|
||||
(reset! active-tab id)
|
||||
(reset! read-qr-once? false))}]]]))
|
||||
|
||||
(defn- header
|
||||
[active-tab read-qr-once? title]
|
||||
[:<>
|
||||
[rn/view {:style style/header-container}
|
||||
[quo/button
|
||||
{:icon true
|
||||
:type :blur-bg
|
||||
:size 32
|
||||
:accessibility-label :close-sign-in-by-syncing
|
||||
:override-theme :dark
|
||||
:on-press #(rf/dispatch [:navigate-back])}
|
||||
:i/arrow-left]
|
||||
[quo/button
|
||||
{:before :i/info
|
||||
:type :blur-bg
|
||||
:size 32
|
||||
:accessibility-label :find-sync-code
|
||||
:override-theme :dark
|
||||
:on-press #(rf/dispatch [:open-modal :find-sync-code])}
|
||||
(i18n/label :t/find-sync-code)]]
|
||||
[quo/text
|
||||
{:size :heading-1
|
||||
:weight :semi-bold
|
||||
:style style/header-text}
|
||||
title]
|
||||
[quo/text
|
||||
{:size :paragraph-1
|
||||
:weight :regular
|
||||
:style style/header-sub-text}
|
||||
(i18n/label :t/synchronise-your-data-across-your-devices)]
|
||||
[rn/view {:style style/tabs-container}
|
||||
[quo/segmented-control
|
||||
{:size 32
|
||||
:override-theme :dark
|
||||
:blur? true
|
||||
:default-active @active-tab
|
||||
:data [{:id 1 :label (i18n/label :t/scan-sync-qr-code)}
|
||||
{:id 2 :label (i18n/label :t/enter-sync-code)}]
|
||||
:on-change (fn [id]
|
||||
(reset! active-tab id)
|
||||
(reset! read-qr-once? false))}]]])
|
||||
[props]
|
||||
[:f> f-header props])
|
||||
|
||||
(defn get-labels-and-on-press-method
|
||||
[]
|
||||
|
@ -192,9 +233,6 @@
|
|||
(defn- scan-qr-code-tab
|
||||
[qr-view-finder]
|
||||
[:<>
|
||||
[rn/view {:style style/scan-qr-code-container}]
|
||||
(when (empty? @qr-view-finder)
|
||||
[qr-scan-hole-area qr-view-finder])
|
||||
(if (and @preflight-check-passed?
|
||||
@camera-permission-granted?
|
||||
(boolean (not-empty @qr-view-finder)))
|
||||
|
@ -210,24 +248,30 @@
|
|||
:style {:color colors/white}}
|
||||
"Yet to be implemented"]])
|
||||
|
||||
(defn- bottom-view
|
||||
[insets]
|
||||
(defn- f-bottom-view
|
||||
[insets translate-y]
|
||||
[rn/touchable-without-feedback
|
||||
{:on-press #(js/alert "Yet to be implemented")}
|
||||
[rn/view
|
||||
{:style (style/bottom-container (:bottom insets))}
|
||||
[reanimated/view
|
||||
{:style (style/bottom-container translate-y (:bottom insets))}
|
||||
[quo/text
|
||||
{:size :paragraph-2
|
||||
:weight :medium
|
||||
:style style/bottom-text}
|
||||
(i18n/label :t/i-dont-have-status-on-another-device)]]])
|
||||
|
||||
(defn- bottom-view
|
||||
[insets translate-y]
|
||||
[:f> f-bottom-view insets translate-y])
|
||||
|
||||
(defn- check-qr-code-data
|
||||
[event]
|
||||
(let [connection-string (string/trim (oops/oget event "nativeEvent.codeStringValue"))
|
||||
valid-connection-string? (sync-utils/valid-connection-string? connection-string)]
|
||||
(if valid-connection-string?
|
||||
(rf/dispatch [:syncing/input-connection-string-for-bootstrapping connection-string])
|
||||
(debounce/debounce-and-dispatch [:syncing/input-connection-string-for-bootstrapping
|
||||
connection-string]
|
||||
300)
|
||||
(rf/dispatch [:toasts/upsert
|
||||
{:icon :i/info
|
||||
:icon-color colors/danger-50
|
||||
|
@ -262,47 +306,124 @@
|
|||
:background-color colors/neutral-80-opa-80}]]]))
|
||||
|
||||
(defn f-view
|
||||
[{:keys [title show-bottom-view? background]}]
|
||||
[{:keys [title show-bottom-view? background animated?]}]
|
||||
(let [insets (safe-area/get-insets)
|
||||
active-tab (reagent/atom 1)
|
||||
qr-view-finder (reagent/atom {})]
|
||||
qr-view-finder (reagent/atom {})
|
||||
render-camera? (reagent/atom false)]
|
||||
(fn []
|
||||
(let [camera-ref (atom nil)
|
||||
read-qr-once? (atom false)
|
||||
(let [camera-ref (atom nil)
|
||||
read-qr-once? (atom false)
|
||||
;; The below check is to prevent scanning of any QR code
|
||||
;; when the user is in syncing progress screen
|
||||
user-in-syncing-progress-screen? (= (rf/sub [:view-id]) :syncing-progress)
|
||||
on-read-code (fn [data]
|
||||
(when (and (not @read-qr-once?)
|
||||
(not user-in-syncing-progress-screen?))
|
||||
(reset! read-qr-once? true)
|
||||
(js/setTimeout (fn []
|
||||
(reset! read-qr-once? false))
|
||||
3000)
|
||||
(check-qr-code-data data)))
|
||||
scan-qr-code-tab? (= @active-tab 1)
|
||||
show-camera? (and scan-qr-code-tab?
|
||||
@camera-permission-granted?
|
||||
@preflight-check-passed?)
|
||||
show-holes? (and show-camera?
|
||||
(boolean (not-empty @qr-view-finder)))]
|
||||
on-read-code (fn [data]
|
||||
(when (and (not @read-qr-once?)
|
||||
(not user-in-syncing-progress-screen?))
|
||||
(reset! read-qr-once? true)
|
||||
(js/setTimeout (fn []
|
||||
(reset! read-qr-once? false))
|
||||
3000)
|
||||
(check-qr-code-data data)))
|
||||
scan-qr-code-tab? (= @active-tab 1)
|
||||
show-camera? (and scan-qr-code-tab?
|
||||
@camera-permission-granted?
|
||||
@preflight-check-passed?)
|
||||
show-holes? (and show-camera?
|
||||
(boolean (not-empty @qr-view-finder)))
|
||||
title-opacity (reanimated/use-shared-value (if animated? 0 1))
|
||||
subtitle-opacity (reanimated/use-shared-value (if animated? 0 1))
|
||||
content-opacity (reanimated/use-shared-value (if animated? 0 1))
|
||||
content-translate-y (reanimated/interpolate subtitle-opacity [0 1] [85 0])
|
||||
bottom-view-translate-y (reanimated/use-shared-value
|
||||
(if animated? (+ 42.2 (:bottom insets)) 0))
|
||||
reset-animations-fn
|
||||
(fn []
|
||||
(reset! render-camera? false)
|
||||
(js/setTimeout
|
||||
(fn []
|
||||
(rf/dispatch [:navigate-back])
|
||||
(when @dismiss-animations
|
||||
(@dismiss-animations))
|
||||
(reanimated/animate-shared-value-with-timing
|
||||
content-opacity
|
||||
0
|
||||
(/ constants/onboarding-modal-animation-duration 8)
|
||||
:easing4)
|
||||
(reanimated/animate-shared-value-with-timing
|
||||
subtitle-opacity
|
||||
0
|
||||
(- constants/onboarding-modal-animation-duration
|
||||
constants/onboarding-modal-animation-delay)
|
||||
:easing4)
|
||||
(reanimated/animate-shared-value-with-timing title-opacity
|
||||
0
|
||||
0
|
||||
:easing4))
|
||||
(if show-camera? 500 0)))]
|
||||
(when animated?
|
||||
(reanimated/animate-shared-value-with-delay subtitle-opacity
|
||||
1 constants/onboarding-modal-animation-duration
|
||||
:easing4
|
||||
(/
|
||||
constants/onboarding-modal-animation-delay
|
||||
2))
|
||||
(reanimated/animate-shared-value-with-delay title-opacity
|
||||
1 0
|
||||
:easing4
|
||||
(+ constants/onboarding-modal-animation-duration
|
||||
constants/onboarding-modal-animation-delay))
|
||||
(reanimated/animate-delay bottom-view-translate-y
|
||||
0
|
||||
(+ constants/onboarding-modal-animation-duration
|
||||
constants/onboarding-modal-animation-delay)
|
||||
100))
|
||||
(rn/use-effect
|
||||
(fn []
|
||||
(when animated?
|
||||
(reanimated/animate-shared-value-with-delay content-opacity
|
||||
1 constants/onboarding-modal-animation-duration
|
||||
:easing4
|
||||
(/
|
||||
constants/onboarding-modal-animation-delay
|
||||
2))
|
||||
(js/setTimeout #(reset! render-camera? true)
|
||||
(+ constants/onboarding-modal-animation-duration
|
||||
constants/onboarding-modal-animation-delay
|
||||
300))
|
||||
(reset! navigate-back-fn reset-animations-fn))
|
||||
(when-not @camera-permission-granted?
|
||||
(permissions/permission-granted? :camera
|
||||
#(reset! camera-permission-granted? %)
|
||||
#(reset! camera-permission-granted? false)))))
|
||||
[:<>
|
||||
background
|
||||
[render-camera show-camera? @qr-view-finder camera-ref on-read-code show-holes?]
|
||||
(when (or (not animated?) @render-camera?)
|
||||
[render-camera show-camera? @qr-view-finder camera-ref on-read-code show-holes?])
|
||||
[rn/view {:style (style/root-container (:top insets))}
|
||||
[header active-tab read-qr-once? title]
|
||||
(case @active-tab
|
||||
1 [scan-qr-code-tab qr-view-finder]
|
||||
2 [enter-sync-code-tab]
|
||||
nil)
|
||||
[header
|
||||
{:active-tab active-tab
|
||||
:read-qr-once? read-qr-once?
|
||||
:title title
|
||||
:title-opacity title-opacity
|
||||
:subtitle-opacity subtitle-opacity
|
||||
:reset-animations-fn reset-animations-fn
|
||||
:animated? animated?}]
|
||||
(when (empty? @qr-view-finder)
|
||||
[:<>
|
||||
[rn/view {:style style/scan-qr-code-container}]
|
||||
[qr-scan-hole-area qr-view-finder]])
|
||||
[reanimated/view
|
||||
{:style (reanimated/apply-animations-to-style
|
||||
{:opacity content-opacity
|
||||
:transform [{:translate-y content-translate-y}]}
|
||||
{})}
|
||||
(case @active-tab
|
||||
1 [scan-qr-code-tab qr-view-finder request-camera-permission]
|
||||
2 [enter-sync-code-tab]
|
||||
nil)]
|
||||
[rn/view {:style style/flex-spacer}]
|
||||
(when show-bottom-view? [bottom-view insets])]]))))
|
||||
(when show-bottom-view? [bottom-view insets bottom-view-translate-y])]]))))
|
||||
|
||||
(defn view
|
||||
[props]
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
[re-frame.core :as re-frame]
|
||||
[re-frame.interop :as interop]
|
||||
[react-native.core :as rn]
|
||||
[react-native.languages :as react-native-languages]
|
||||
[react-native.platform :as platform]
|
||||
[react-native.shake :as react-native-shake]
|
||||
[reagent.impl.batching :as batching]
|
||||
|
@ -39,9 +38,8 @@
|
|||
(global-error/register-handler)
|
||||
(notifications/listen-notifications)
|
||||
(.addEventListener rn/app-state "change" #(re-frame/dispatch [:app-state-change %]))
|
||||
(react-native-languages/add-change-listener #(fn [lang]
|
||||
(i18n/set-language lang)
|
||||
(i18n-resources/load-language lang)))
|
||||
(i18n/set-language "en")
|
||||
(i18n-resources/load-language "en")
|
||||
(react-native-shake/add-shake-listener #(re-frame/dispatch [:shake-event]))
|
||||
(utils.universal-links/initialize)
|
||||
|
||||
|
|
|
@ -164,11 +164,18 @@
|
|||
|
||||
(navigation/reg-button-pressed-listener
|
||||
(fn [id]
|
||||
(if (= "dismiss-modal" id)
|
||||
(cond
|
||||
(= "dismiss-modal" id)
|
||||
(do
|
||||
(when-let [event (get-in views/screens [(last @state/modals) :on-dissmiss])]
|
||||
(re-frame/dispatch event))
|
||||
(dissmissModal))
|
||||
(= "RNN.hardwareBackButton" id)
|
||||
(when-let [handler (get-in views/screens
|
||||
[(or (last @state/modals) @state/pushed-screen-id)
|
||||
:hardware-back-button-handler])]
|
||||
(handler))
|
||||
:else
|
||||
(when-let [handler (get-in views/screens [(keyword id) :right-handler])]
|
||||
(handler)))))
|
||||
|
||||
|
|
|
@ -37,6 +37,11 @@
|
|||
:orientation ["portrait"]
|
||||
:backgroundColor colors/neutral-80-opa-80-blur})
|
||||
|
||||
(def onboarding-transparent-layout
|
||||
{:componentBackgroundColor :transparent
|
||||
:orientation ["portrait"]
|
||||
:backgroundColor :transparent})
|
||||
|
||||
(defn navbar
|
||||
([dark?]
|
||||
{:navigationBar {:backgroundColor (if (or dark? (colors/dark?)) colors/neutral-100 colors/white)}})
|
||||
|
|
|
@ -34,7 +34,37 @@
|
|||
[status-im2.contexts.shell.share.view :as share]
|
||||
[status-im2.contexts.onboarding.syncing.results.view :as syncing-results]
|
||||
[status-im2.contexts.onboarding.syncing.progress.view :as syncing-devices]
|
||||
[status-im2.contexts.chat.new-chat.view :as new-chat]))
|
||||
[status-im2.contexts.chat.new-chat.view :as new-chat]
|
||||
[react-native.core :as rn]
|
||||
[status-im2.constants :as constants]))
|
||||
|
||||
(def sign-in-modal-animations
|
||||
{:showModal {:translationY {:from (:height (rn/get-window))
|
||||
:to 0
|
||||
:duration constants/onboarding-modal-animation-duration}
|
||||
:alpha {:from 1
|
||||
:to 1
|
||||
:duration 0}}
|
||||
:dismissModal {:translationY {:from 0
|
||||
:to (:height (rn/get-window))
|
||||
:duration constants/onboarding-modal-animation-duration}
|
||||
:alpha {:from 1
|
||||
:to 0
|
||||
:duration constants/onboarding-modal-animation-duration}}})
|
||||
|
||||
(def push-animations-for-transparent-background
|
||||
{:push {:content {:enter {:translationX {:from (:width (rn/get-window))
|
||||
:to 0
|
||||
:duration constants/onboarding-modal-animation-duration}}
|
||||
:exit {:translationX {:from 0
|
||||
:to (- (:width (rn/get-window)))
|
||||
:duration constants/onboarding-modal-animation-duration}}}}
|
||||
:pop {:content {:exit {:translationX {:from 0
|
||||
:to (:width (rn/get-window))
|
||||
:duration constants/onboarding-modal-animation-duration}}
|
||||
:enter {:translationX {:from (- (:width (rn/get-window)))
|
||||
:to 0
|
||||
:duration constants/onboarding-modal-animation-duration}}}}})
|
||||
|
||||
(defn screens
|
||||
[]
|
||||
|
@ -164,12 +194,26 @@
|
|||
:popGesture false
|
||||
:hardwareBackButton {:dismissModalOnPress false
|
||||
:popStackOnPress false}}}
|
||||
|
||||
{:name :scan-sync-code-page
|
||||
:options options/dark-screen
|
||||
:component scan-sync-code-page/view}
|
||||
|
||||
{:name :sign-in-intro
|
||||
:options {:layout options/onboarding-transparent-layout
|
||||
:animations (merge
|
||||
sign-in-modal-animations
|
||||
push-animations-for-transparent-background)
|
||||
:modalPresentationStyle :overCurrentContext
|
||||
:hardwareBackButton {:dismissModalOnPress false
|
||||
:popStackOnPress false}}
|
||||
:hardware-back-button-handler sign-in/navigate-back
|
||||
:component sign-in/animated-view}
|
||||
|
||||
{:name :sign-in
|
||||
:options {:layout options/onboarding-layout}
|
||||
:options {:theme :dark
|
||||
:modalPresentationStyle :overCurrentContext
|
||||
:layout options/onboarding-layout}
|
||||
:component sign-in/view}
|
||||
|
||||
{:name :syncing-progress
|
||||
|
@ -178,6 +222,13 @@
|
|||
:popGesture false}
|
||||
:component syncing-devices/view}
|
||||
|
||||
{:name :syncing-progress-intro
|
||||
:options {:theme :dark
|
||||
:layout options/onboarding-transparent-layout
|
||||
:animations push-animations-for-transparent-background
|
||||
:popGesture false}
|
||||
:component syncing-devices/view-onboarding}
|
||||
|
||||
{:name :syncing-results
|
||||
:options {:theme :dark}
|
||||
:component syncing-results/view}
|
||||
|
|
Loading…
Reference in New Issue