feat: implement onboarding modal transition for sign in screen (#16167)

This commit is contained in:
Brian Sztamfater 2023-07-04 12:43:08 -03:00 committed by GitHub
parent affd2a5e76
commit c2c79cc1ac
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 553 additions and 181 deletions

View File

@ -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)]

View File

@ -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})
@ -51,4 +73,4 @@
(defn heading-text
[gap]
{:color colors/white
:margin-bottom gap})
:margin-bottom gap})

View File

@ -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])

View File

@ -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

View File

@ -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]}

View File

@ -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)

View File

@ -5,7 +5,6 @@
{:background-color colors/neutral-95
:flex-direction :row
:position :absolute
:overflow :hidden
:top 0
:bottom 0
:left 0

View File

@ -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)]

View File

@ -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

View File

@ -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}])

View File

@ -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

View File

@ -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])

View File

@ -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

View File

@ -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]

View File

@ -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)

View File

@ -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)))))

View File

@ -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)}})

View File

@ -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}