New intro screen (#15127)

* feat: add new intro page

* e2e: new intro fix

---------

Co-authored-by: Churikova Tetiana <tatiana@status.im>
This commit is contained in:
Jamie Caprani 2023-03-06 14:42:30 +00:00 committed by GitHub
parent 404ae82cfe
commit 2861190e5b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 215 additions and 301 deletions

View File

Before

Width:  |  Height:  |  Size: 1.3 MiB

After

Width:  |  Height:  |  Size: 1.3 MiB

View File

Before

Width:  |  Height:  |  Size: 2.4 MiB

After

Width:  |  Height:  |  Size: 2.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 788 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

View File

@ -4,22 +4,24 @@
(def outer-container {:height 216})
(def top-card
{:flex 1
:padding-vertical 12
:padding-horizontal 20
:border-radius 20
:background-color colors/neutral-80})
{:flex 1
:padding-vertical 12
:padding-horizontal 20
:border-top-left-radius 20
:border-top-right-radius 20
:background-color colors/neutral-80})
(def bottom-card
{:position :absolute
:top 80
:left 0
:right 0
:bottom 0
:padding-vertical 12
:padding-horizontal 20
:border-radius 20
:background-color (colors/alpha colors/white 0.05)})
{:position :absolute
:top 80
:left 0
:right 0
:bottom 0
:padding-vertical 12
:padding-horizontal 20
:border-top-left-radius 20
:border-top-right-radius 20
:background-color (colors/alpha colors/white 0.05)})
(def bottom-container
{:flex-direction :row

View File

@ -39,13 +39,14 @@
children))
(defn card
[{:keys [on-press style heading gap top?]} children]
[{:keys [on-press style heading gap accessibility-label top?]} children]
[rn/touchable-highlight
{:on-press on-press
:border-radius 20
:style style
:underlay-color (:background-color style)}
[rn/view {}
{:accessibility-label accessibility-label
:on-press on-press
:border-radius 20
:style style
:underlay-color (:background-color style)}
[rn/view
[text/text
{:size :heading-1
:style (style/heading-text gap)
@ -60,9 +61,11 @@
opts
{:container-style style-object
:top-card { :on-press event
:heading string}
:heading string
:accessibility-label keyword}
:bottom-card { :on-press event
:heading string}}
:heading string
:accessibility-label keyword}}
child-1 string, keyword or hiccup
child-2 string, keyword or hiccup
"

View File

@ -1,47 +0,0 @@
(ns status-im.ui.screens.onboarding.intro.styles
(:require [quo.animated :as animated]
[quo.design-system.colors :as colors]))
(def dot-size 6)
(def progress-size 36)
(def intro-view
{:flex 1
:justify-content :flex-end
:margin-bottom 12})
(defn dot-selector
[]
{:flex-direction :row
:justify-content :space-between
:margin-bottom 16
:align-items :center})
(defn dot-style
[active]
{:background-color colors/blue-light
:overflow :hidden
:opacity 1
:margin-horizontal 2
:width (animated/mix active dot-size progress-size)
:height dot-size
:border-radius 3})
(defn dot-progress
[active progress]
{:background-color colors/blue
:height dot-size
:width progress-size
:opacity (animated/mix active 0 1)
:transform [{:translateX (animated/mix progress (- progress-size) 0)}]})
(defn wizard-text-with-height
[height]
(merge {:color colors/gray
:text-align :center}
(when-not (zero? height)
{:height height})))
(def wizard-title
{:margin-bottom 16
:text-align :center})

View File

@ -1,204 +0,0 @@
(ns status-im.ui.screens.onboarding.intro.views
(:require [quo.animated :as animated]
[quo.core :as quo]
[quo.design-system.colors :as colors]
[quo.design-system.typography :as typography]
[quo.react-native :as rn]
[re-frame.core :as re-frame]
[reagent.core :as reagent]
[utils.i18n :as i18n]
[status-im.react-native.resources :as resources]
[status-im.ui.components.react :as react]
[status-im.ui.screens.onboarding.intro.styles :as styles]))
(defonce index (reagent/atom 0))
(defn code
[val]
^{:key val}
[animated/code {:exec val}])
(defn dot
[]
(let [active (animated/value 0)
active-transition (animated/with-timing-transition active {:duration 100})]
(fn [{:keys [selected progress]}]
[animated/view {:style (styles/dot-style active-transition)}
[code (animated/set active (if selected 1 0))]
[animated/view {:style (styles/dot-progress active-transition progress)}]])))
(defn dots-selector
[{:keys [n progress]}]
(let [selected @index]
[react/view {:style (styles/dot-selector)}
(for [i (range n)]
^{:key i}
[dot
{:progress progress
:selected (= selected i)}])]))
(defn slides-view
[slides width]
(let [height (reagent/atom 0)
text-height (reagent/atom 0)
text-temp-height (atom 0)
text-temp-timer (atom nil)]
(fn [_]
[:<>
(doall
(for [s slides]
^{:key (:title s)}
[react/view
{:style {:flex 1
:width width
:justify-content :flex-end
:align-items :center
:padding-horizontal 32}}
(let [size (min width @height)]
[react/view
{:style {:flex 1}
:on-layout (fn [^js e]
(let [new-height (-> e .-nativeEvent .-layout .-height)]
(swap! height #(if (pos? %) (min % new-height) new-height))))}
[react/image
{:source (:image s)
:resize-mode :contain
:style {:width size
:height size}}]])
[quo/text
{:style styles/wizard-title
:align :center
:weight :bold
:size :x-large}
(i18n/label (:title s))]
[quo/text
{:style (styles/wizard-text-with-height @text-height)
:on-layout
(fn [^js e]
(let [new-height (-> e .-nativeEvent .-layout .-height)]
(when (and (not= new-height @text-temp-height)
(not (zero? new-height))
(< new-height 200))
(swap! text-temp-height #(if (pos? %) (max % new-height) new-height))
(when @text-temp-timer (js/clearTimeout @text-temp-timer))
(reset! text-temp-timer
(js/setTimeout #(reset! text-height @text-temp-height) 500)))))}
(i18n/label (:text s))]]))])))
(defn carousel
[slides width]
;;TODO this is really not the best implementation, must be a better way
(let [scroll-x (reagent/atom 0)
scroll-view-ref (atom nil)
manual-scroll (atom false)
progress (animated/value 1)
autoscroll (animated/value 1)
finished (animated/value 0)
clock (animated/clock)
go-next (fn []
(let [x (if (>= @scroll-x
(- (* (dec (count slides))
width)
5))
0
(+ @scroll-x width))]
(reset! index (Math/round (/ x width)))
(some-> ^js @scroll-view-ref
(.scrollTo #js {:x x :animated true}))))
code (animated/block
[(animated/cond* (animated/and* (animated/not* (animated/clock-running clock))
autoscroll)
(animated/start-clock clock))
(animated/cond* (animated/and* (animated/clock-running clock)
(animated/not* autoscroll))
[(animated/stop-clock clock)
(animated/set finished 1)])
(animated/set progress
(animated/cancelable-loop
{:clock clock
:finished finished
:duration 4000
:on-reach go-next}))])
cancel-animation (fn []
(reset! manual-scroll true)
(animated/set-value autoscroll 0))
restart-animation (fn []
(animated/set-value autoscroll 1))]
(fn [_ _]
[react/view
{:style {:align-items :center
:flex 1
:justify-content :flex-end}}
[animated/code {:exec code}]
[react/scroll-view
{:horizontal true
:paging-enabled true
:ref #(reset! scroll-view-ref %)
:shows-vertical-scroll-indicator false
:shows-horizontal-scroll-indicator false
:pinch-gesture-enabled false
:scroll-event-throttle 16
:on-scroll #(let [x (.-nativeEvent.contentOffset.x ^js %)]
(when @manual-scroll
;; NOTE: Will be not synced if velocity is big
(reset! index (Math/round (/ x width))))
(reset! scroll-x x))
:on-scroll-begin-drag cancel-animation
:on-scroll-end-drag restart-animation
:on-momentum-scroll-end #(reset! manual-scroll false)
:style {:margin-bottom 16}}
[slides-view slides width]]
[dots-selector
{:progress progress
:n (count slides)}]])))
(defonce tos-accepted (reagent/atom false))
(defn intro
[]
[react/view {:style styles/intro-view}
[carousel
[{:image (resources/get-theme-image :chat)
:title :intro-title1
:text :intro-text1}
{:image (resources/get-theme-image :wallet)
:title :intro-title2
:text :intro-text2}
{:image (resources/get-theme-image :browser)
:title :intro-title3
:text :intro-text3}]
@(re-frame/subscribe [:dimensions/window-width])]
[react/view {:style {:align-items :center}}
[react/view
{:flex-direction :row
:justify-content :space-between
:align-items :center
:margin-top 36
:margin-bottom 24}
[quo/checkbox
{:value @tos-accepted
:on-change #(swap! tos-accepted not)}]
[rn/touchable-opacity {:test-ID :terms-of-service :on-press #(swap! tos-accepted not)}
[react/nested-text {:style {:margin-left 12}}
(i18n/label :t/accept-status-tos-prefix)
[{:style (merge {:color colors/blue}
typography/font-medium)
:on-press #(re-frame/dispatch [:open-modal :terms-of-service])
:accessibility-label :terms-of-service-link}
" "
(i18n/label :t/terms-of-service)]]]]
[react/view {:style {:margin-bottom 24}}
[quo/button
{:test-ID :get-started
:disabled (not @tos-accepted)
:on-press #(do (re-frame/dispatch [:init-root :onboarding])
;; clear atom state for next use
(reset! tos-accepted false)
(re-frame/dispatch [:hide-terms-of-services-opt-in-screen]))}
(i18n/label :t/get-started)]]
[react/text
{:style {:color colors/blue}
:on-press #(re-frame/dispatch [:open-modal :privacy-policy])
:accessibility-label :privacy-policy-link}
(i18n/label :t/privacy-policy)]]])

View File

@ -60,7 +60,6 @@
[status-im.ui.screens.notifications-settings.views :as notifications-settings]
[status-im.ui.screens.offline-messaging-settings.edit-mailserver.views :as edit-mailserver]
[status-im.ui.screens.offline-messaging-settings.views :as offline-messaging-settings]
[status-im.ui.screens.onboarding.intro.views :as onboarding.intro]
[status-im.ui.screens.onboarding.keys.views :as onboarding.keys]
[status-im.ui.screens.onboarding.notifications.views :as onboarding.notifications]
[status-im.ui.screens.onboarding.password.views :as onboarding.password]
@ -130,11 +129,6 @@
{:name :progress
:component progress/progress}
;[Onboarding]
{:name :intro
:insets {:bottom true}
:component onboarding.intro/intro}
;[Onboarding]
{:name :get-your-keys
:insets {:bottom true}

View File

@ -2,6 +2,10 @@
(def ui
{:add-new-contact (js/require "../resources/images/ui2/add-contact.png")
:intro-1 (js/require "../resources/images/ui2/intro-1.png")
:intro-2 (js/require "../resources/images/ui2/intro-2.png")
:intro-3 (js/require "../resources/images/ui2/intro-3.png")
:intro-4 (js/require "../resources/images/ui2/intro-4.png")
:lifestyle (js/require "../resources/images/ui2/lifestyle.png")
:music (js/require "../resources/images/ui2/music.png")
:podcasts (js/require "../resources/images/ui2/podcasts.png")

View File

@ -0,0 +1,61 @@
(ns status-im2.contexts.onboarding.common.intro.style
(:require
[react-native.platform :as platform]
[quo2.foundations.colors :as colors]))
(def progress-bar-container
{:background-color colors/neutral-100
:flex-direction :row
:margin-vertical 16})
(defn progress-bar-item
[index position end?]
{:height 2
:flex 1
:border-width 1
:border-color (if (= index position) colors/white colors/white-opa-10)
:margin-right (if end? 0 8)})
(def carousel
{:height 92
;; (padding-top) This insets issue needs a consistent implementation across all screens.
:padding-top (if platform/android? 0 44)
:position :absolute
:top 0
:bottom 0
:left 0
:right 0
:z-index 2
:background-color colors/neutral-100
:padding-vertical 12
:padding-horizontal 20})
(def carousel-text
{:background-color colors/neutral-100
:color colors/white})
(def page-container
{:flex 1
:justify-content :flex-end})
(def page-image
{:position :absolute
:background-color colors/neutral-100
:top 0
:bottom 0
:left 0
:right 0
:width "100%"
:aspect-ratio 1})
(def text-container
{:flex 1
:flex-wrap :wrap})
(def plain-text
{:flex 1
:color (colors/alpha colors/white 0.7)})
(def highlighted-text
{:flex 1
:color colors/white})

View File

@ -0,0 +1,88 @@
(ns status-im2.contexts.onboarding.common.intro.view
(:require [utils.i18n :as i18n]
[quo2.core :as quo]
[react-native.core :as rn]
[reagent.core :as reagent]
[utils.re-frame :as rf]
[status-im2.contexts.onboarding.common.intro.style :as style]
[status-im2.common.resources :as resources]))
(def carousels
[{:image (resources/get-image :intro-1)
:text (i18n/label :t/join-decentralised-communities)
:sub-text (i18n/label :t/participate-in-the-metaverse)}
{:image (resources/get-image :intro-2)
:text (i18n/label :t/chat-with-friends)
:sub-text (i18n/label :t/with-full-encryption)}
{:image (resources/get-image :intro-3)
:text (i18n/label :t/own-your-crypto)
:sub-text (i18n/label :t/use-the-multichain-wallet)}
{:image (resources/get-image :intro-4)
:text (i18n/label :t/discover-web3)
:sub-text (i18n/label :t/explore-the-decentralized-web)}])
(defn progress-bar
[index]
[rn/view style/progress-bar-container
[rn/view {:style (style/progress-bar-item index 0 false)}]
[rn/view {:style (style/progress-bar-item index 1 false)}]
[rn/view {:style (style/progress-bar-item index 2 false)}]
[rn/view {:style (style/progress-bar-item index 3 true)}]])
;; TODO: carousel component is to be correctly implemented as quo2 component with animations etc in:
;; https://github.com/status-im/status-mobile/issues/15012
(defn carousel
[index]
[rn/view {:style style/carousel}
[progress-bar index]
[quo/text
{:style style/carousel-text
:weight :semi-bold
:size :heading-2} (get-in carousels [index :text])]
[quo/text
{:style style/carousel-text
:size :paragraph-1} (get-in carousels [index :sub-text])]])
(defn set-index
[old-index]
(mod (inc old-index) 4))
(defn view
[]
(reagent/with-let
[carousel-index (reagent/atom 0)
interval-id
(js/setInterval
#(swap! carousel-index set-index)
1500)]
[rn/view {:style style/page-container}
[carousel @carousel-index]
[rn/image
{:style style/page-image
:source (get-in carousels [@carousel-index :image])}]
[quo/drawer-buttons
{:top-card {:on-press (fn []
(rf/dispatch [:init-root :onboarding])
(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 [:init-root :onboarding])
(rf/dispatch [:hide-terms-of-services-opt-in-screen]))
:heading (i18n/label :t/im-new-to-status)
:accessibility-label :new-to-status-button}}
(i18n/label :t/you-already-use-status)
[quo/text
{:style style/text-container}
[quo/text
{:size :paragraph-2
:style style/plain-text
:weight :semi-bold}
(i18n/label :t/by-continuing-you-accept)]
[quo/text
{:on-press #(rf/dispatch [:open-modal :privacy-policy])
:size :paragraph-2
:style style/highlighted-text
:weight :semi-bold}
(i18n/label :t/terms-of-service)]]]]
(finally (js/clearInterval interval-id))))

View File

@ -49,7 +49,7 @@
multiaccount
[:key-uid :name :public-key :identicon :images]))
(keychain/get-auth-method (:key-uid multiaccount))))
(navigation/init-root cofx :intro))))
(navigation/init-root cofx :intro-stack))))
(rf/defn initialize-multiaccounts
{:events [:setup/initialize-multiaccounts]}

View File

@ -62,16 +62,7 @@
(defn old-roots
[]
;;TABS
{;;INTRO (onboarding carousel)
:intro
{:root {:stack {:children [{:component {:name :intro
:id :intro
:options (status-bar-options)}}]
:options (merge (default-root)
(status-bar-options)
{:topBar (assoc (topbar-options) :visible false)})}}}
;; ONBOARDING
{;; ONBOARDING
:onboarding
{:root {:stack {:id :onboarding
:children [{:component {:name :get-your-keys
@ -148,6 +139,16 @@
[]
;;TABS
(merge (old-roots)
;;INTRO (onboarding carousel)
{:intro-stack
{:root {:stack {:id :intro
:children [{:component {:name :intro
:id :intro
:options
{:statusBar (merge
(status-bar-options)
{:style :light})
:topBar {:visible false}}}}]}}}}
{:shell-stack
{:root
{:stack {:id :shell-stack

View File

@ -1,6 +1,7 @@
(ns status-im2.navigation.screens
(:require [utils.i18n :as i18n] ;; TODO remove when not used anymore
[status-im.ui.screens.screens :as old-screens]
[status-im2.contexts.onboarding.common.intro.view :as intro]
[status-im2.contexts.activity-center.view :as activity-center]
[status-im2.contexts.chat.messages.view :as chat]
[status-im2.contexts.add-new-contact.views :as add-new-contact]
@ -24,7 +25,12 @@
[]
(concat
(old-screens/screens)
[{:name :activity-center
[{:name :intro
:options {:topBar {:visible false}}
:insets {:top false}
:component intro/view}
{:name :activity-center
:options {:topBar {:visible false}}
:component activity-center/view}

View File

@ -125,6 +125,10 @@ class SignInView(BaseView):
super().__init__(driver)
self.driver = driver
# intro screen
self.sign_in_intro_button = Button(self.driver, accessibility_id="already-use-status-button")
self.i_m_new_in_status_button = Button(self.driver, accessibility_id="new-to-status-button")
self.password_input = EditBox(self.driver, accessibility_id="password-input")
self.migration_password_input = EditBox(self.driver, accessibility_id="enter-password-input")
self.sign_in_button = SignInButton(self.driver)
@ -177,8 +181,7 @@ class SignInView(BaseView):
self.driver.info("## Creating new multiaccount (password:'%s', keycard:'%s')" % (password, str(keycard)),
device=False)
if not second_user:
self.accept_tos_checkbox.enable()
self.get_started_button.click_until_presence_of_element(self.generate_key_button)
self.i_m_new_in_status_button.click_until_presence_of_element(self.generate_key_button)
self.generate_key_button.click()
self.next_button.click_until_absense_of_element(self.element_by_translation_id("intro-wizard-title2"))
@ -192,14 +195,6 @@ class SignInView(BaseView):
self.confirm_your_password_input.set_value(password)
self.next_button.click()
# Old UI
# self.maybe_later_button.wait_for_visibility_of_element(30)
# if enable_notifications:
# self.enable_notifications_button.click()
# else:
# self.maybe_later_button.click_until_presence_of_element(self.lets_go_button)
# self.lets_go_button.click_until_absense_of_element(self.lets_go_button)
# self.profile_button.wait_for_visibility_of_element(30)
self.chats_tab.wait_for_visibility_of_element(30)

View File

@ -122,6 +122,7 @@
"bug-report-submit-email": "Submit by email with logs archive",
"bug-report-submit-gh-issue": "Submit a GitHub issue without logs",
"bug-report-too-short-description": "Description is too short",
"by-continuing-you-accept": "By continuing you accept our ",
"camera-access-error": "To grant the required camera permission, please go to your system settings and make sure that Status > Camera is selected.",
"can-not-add-yourself": "That's you, to start a chat choose someone else",
"cancel": "Cancel",
@ -152,6 +153,7 @@
"chat-key": "Chat key",
"chat-name": "Chat name",
"chat-settings": "Chat settings",
"chat-with-friends": "Chat privately with friends",
"chats": "Chats",
"check-your-recovery-phrase": "Check your seed phrase",
"choose-authentication-method": "Choose an authentication method",
@ -202,6 +204,7 @@
"invite": "Invite",
"invite-contacts": "Invite people from contacts list",
"create-channel": "Create a channel",
"im-new-to-status": "I'm new to status",
"import-community": "Import a community",
"import-community-title": "Import a community",
"name-your-community": "Name your community",
@ -463,6 +466,7 @@
"disabled": "Disabled",
"disconnected": "Chat offline",
"discover": "Discover",
"discover-web3": "Discover web3",
"dismiss": "Dismiss",
"discover-communities": "Discover Communities",
"done": "Done",
@ -591,6 +595,7 @@
"ethereum-node-started-incorrectly-description": "Ethereum node was started with incorrect configuration, application will be stopped to recover from that condition. Configured network id = {{network-id}}, actual = {{fetched-network-id}}",
"ethereum-node-started-incorrectly-title": "Ethereum node started incorrectly",
"etherscan-lookup": "Look up on Etherscan",
"explore-the-decentralized-web": "Explore and interact with the decentralized web",
"export-account": "Export account",
"export-key": "Export private key",
"community-private-key": "Community private key",
@ -709,6 +714,7 @@
"join-a-community": "or join a community",
"join-open-community": "Join Community",
"joined-community": "You joined “{{community}}”",
"join-decentralised-communities": "Join Decentralized Communities",
"http-gateway-error": "Oops, request failed!",
"sign-request-failed": "Could not sign message",
"invite-friends": "Invite friends",
@ -1042,6 +1048,7 @@
"or": "OR",
"outgoing": "Outgoing",
"outgoing-transaction": "Outgoing transaction",
"own-your-crypto": "Own your crypto",
"pair": "Pair devices",
"pair-card": "Pair to this device",
"pair-code": "Pair code",
@ -1062,6 +1069,7 @@
"pairing-new-installation-detected-title": "New device detected",
"pairing-no-info": "No info",
"pairing-please-set-a-name": "Please set a name for your device.",
"participate-in-the-metaverse": "Participate in the truly free metaverse",
"passphrase": "Passphrase",
"password": "Password",
"password-description": "At least 6 characters. Your password protects your keys. You need it to unlock Status and transact.",
@ -1365,6 +1373,7 @@
"update": "Update",
"url": "URL",
"usd-currency": "USD",
"use-the-multichain-wallet": "Use the leading multi-chain self-custodial wallet",
"use-valid-contact-code": "Please enter or scan a valid chat key or username",
"validation-amount-invalid-number": "Amount is not a valid number",
"validation-amount-is-too-precise": "Amount is too precise. Max number of decimals is {{decimals}}.",
@ -1417,6 +1426,7 @@
"welcome-community-blank-message": "Your channels will appear here. To create a new channel, click on the ⊕ button and select \"Create a channel\"",
"welcome-community-blank-message-edit-chats": "Your channels will appear here. To create a new channel, go back to the community screen, click on the ⊕ button and select \"Create a channel\"",
"welcome-blank-community-message": "Your communities will appear here.",
"with-full-encryption": "With full metadata privacy and e2e encryption",
"fetch-community": "Fetch community",
"fetching-community": "Fetching community...",
"seed-phrase-placeholder": "Seed phrase...",
@ -1441,6 +1451,7 @@
"You": "You",
"you": "you",
"you-already-have-an-asset": "You already have an asset {{value}}",
"you-already-use-status": "You already use Status",
"you-are-all-set": "Youre all set!",
"you-are-all-set-description": "If you lose your phone, you can now access your funds and chat key using your seed phrase",
"you-can-change-account": "You can change the account name and color to what you wish",