diff --git a/resources/images/ui/intro1.png b/resources/images/ui/intro1.png new file mode 100644 index 00000000000..2dc713333e9 Binary files /dev/null and b/resources/images/ui/intro1.png differ diff --git a/resources/images/ui/intro2.png b/resources/images/ui/intro2.png new file mode 100644 index 00000000000..b6b967ea87b Binary files /dev/null and b/resources/images/ui/intro2.png differ diff --git a/resources/images/ui/intro3.png b/resources/images/ui/intro3.png new file mode 100644 index 00000000000..885733f73b0 Binary files /dev/null and b/resources/images/ui/intro3.png differ diff --git a/resources/images/ui/sample-key.png b/resources/images/ui/sample-key.png new file mode 100644 index 00000000000..6ba485e18a8 Binary files /dev/null and b/resources/images/ui/sample-key.png differ diff --git a/src/status_im/accounts/create/core.cljs b/src/status_im/accounts/create/core.cljs index 3acaefbc171..5e785e539de 100644 --- a/src/status_im/accounts/create/core.cljs +++ b/src/status_im/accounts/create/core.cljs @@ -144,6 +144,34 @@ (dissoc :password :password-confirm :name :error)))} (navigation/navigate-to-cofx :create-account nil))) +(fx/defn intro-wizard [{:keys [db] :as cofx}] + (fx/merge {:db (assoc db :intro-wizard {:step 1})} + (navigation/navigate-to-cofx :intro-wizard nil))) + +(fx/defn intro-step-back [{:keys [db] :as cofx}] + (let [step (get-in db [:intro-wizard :step])] + + (if (< 1 step) + (fx/merge {:db (assoc db :intro-wizard {:step (dec step)})} + (navigation/navigate-to-cofx :intro-wizard nil)) + + (fx/merge {:db (dissoc db :intro-wizard)} + (navigation/navigate-to-clean :intro nil))))) + +(fx/defn intro-step-forward [{:keys [db] :as cofx}] + (let [step (get-in db [:intro-wizard :step])] + + (if (= step 7) + (fx/merge {:db (dissoc db :intro-wizard)} + (navigation/navigate-to-cofx :welcome nil)) + (fx/merge {:db (assoc db :intro-wizard {:step (inc step)})} + (navigation/navigate-to-cofx :intro-wizard nil))))) + +(defn get-new-key-code [current-code digit] + (str current-code digit)) + +(fx/defn code-digit-pressed [{:keys [db] :as cofx} digit] + {:db (update-in db [:intro-wizard :key-code] get-new-key-code digit)}) ;;;; COFX (re-frame/reg-cofx diff --git a/src/status_im/events.cljs b/src/status_im/events.cljs index 89a43c4041d..9775526851c 100644 --- a/src/status_im/events.cljs +++ b/src/status_im/events.cljs @@ -250,6 +250,26 @@ (fn [cofx _] (hardwallet/navigate-to-authentication-method cofx))) +(handlers/register-handler-fx + :accounts.create.ui/intro-wizard + (fn [cofx _] + (accounts.create/intro-wizard cofx))) + +(handlers/register-handler-fx + :intro-wizard/step-back-pressed + (fn [cofx _] + (accounts.create/intro-step-back cofx))) + +(handlers/register-handler-fx + :intro-wizard/step-forward-pressed + (fn [cofx _] + (accounts.create/intro-step-forward cofx))) + +(handlers/register-handler-fx + :intro-wizard/code-digit-pressed + (fn [cofx [_ digit]] + (accounts.create/code-digit-pressed cofx digit))) + ;; accounts recover module (handlers/register-handler-fx diff --git a/src/status_im/init/core.cljs b/src/status_im/init/core.cljs index 9d99fd91d30..5db653e3519 100644 --- a/src/status_im/init/core.cljs +++ b/src/status_im/init/core.cljs @@ -139,22 +139,22 @@ (fx/defn initialize-views [cofx] (let [{{:accounts/keys [accounts] :as db} :db} cofx] - (if (empty? accounts) - (navigation/navigate-to-cofx cofx :intro nil) - (let [account-with-notification - (when-not platform/desktop? - (notifications/lookup-contact-pubkey-from-hash - cofx - (first (keys (:push-notifications/stored db))))) - selection-fn - (if (not-empty account-with-notification) - #(filter (fn [account] - (= account-with-notification - (:public-key account))) - %) - #(sort-by :last-sign-in > %)) - {:keys [address photo-path name]} (first (selection-fn (vals accounts)))] - (accounts.login/open-login cofx address photo-path name))))) + (if true #_(empty? accounts) + (navigation/navigate-to-cofx cofx :intro nil) + (let [account-with-notification + (when-not platform/desktop? + (notifications/lookup-contact-pubkey-from-hash + cofx + (first (keys (:push-notifications/stored db))))) + selection-fn + (if (not-empty account-with-notification) + #(filter (fn [account] + (= account-with-notification + (:public-key account))) + %) + #(sort-by :last-sign-in > %)) + {:keys [address photo-path name]} (first (selection-fn (vals accounts)))] + (accounts.login/open-login cofx address photo-path name))))) (fx/defn load-accounts-and-initialize-views "DB has been decrypted, load accounts and initialize-view" diff --git a/src/status_im/react_native/resources.cljs b/src/status_im/react_native/resources.cljs index eed19d8698d..9ab76929680 100644 --- a/src/status_im/react_native/resources.cljs +++ b/src/status_im/react_native/resources.cljs @@ -5,6 +5,10 @@ :empty-recent (js/require "./resources/images/ui/empty-recent.png") :analytics-image (js/require "./resources/images/ui/analytics-image.png") :welcome-image (js/require "./resources/images/ui/welcome-image.png") + :intro1 (js/require "./resources/images/ui/intro1.png") + :intro2 (js/require "./resources/images/ui/intro2.png") + :intro3 (js/require "./resources/images/ui/intro3.png") + :sample-key (js/require "./resources/images/ui/sample-key.png") :lock (js/require "./resources/images/ui/lock.png") :tribute-to-talk (js/require "./resources/images/ui/tribute-to-talk.png") :wallet-welcome (js/require "./resources/images/ui/wallet-welcome.png") diff --git a/src/status_im/subs.cljs b/src/status_im/subs.cljs index 2bb17998190..55fd90acfc8 100644 --- a/src/status_im/subs.cljs +++ b/src/status_im/subs.cljs @@ -1631,3 +1631,10 @@ :<- [:search/filter] (fn [[chats search-filter]] (apply-filter search-filter chats extract-chat-attributes))) + +;; INTRO WIZARD +(re-frame/reg-sub + :intro-wizard + (fn [db] + (:intro-wizard db))) + diff --git a/src/status_im/ui/components/numpad/styles.cljs b/src/status_im/ui/components/numpad/styles.cljs index d6276fd6e36..c146cfa6906 100644 --- a/src/status_im/ui/components/numpad/styles.cljs +++ b/src/status_im/ui/components/numpad/styles.cljs @@ -21,7 +21,7 @@ {:flex-direction :row}) (def number-pad - {:flex 1 + {;:flex 1 :align-items :center :margin-bottom 24 :min-height 292 diff --git a/src/status_im/ui/screens/home/styles.cljs b/src/status_im/ui/screens/home/styles.cljs index 04c894aa8c5..57ed4cb26c1 100644 --- a/src/status_im/ui/screens/home/styles.cljs +++ b/src/status_im/ui/screens/home/styles.cljs @@ -167,6 +167,13 @@ (def welcome-view {:flex 1}) +(def action-button-container + {:position :absolute + :align-items :center + :bottom (+ tabs.styles/tabs-diff 6) + :width 40 + :height 40}) + (def welcome-image-container {:align-items :center :margin-top 42}) @@ -182,13 +189,6 @@ :margin-horizontal 32 :color colors/gray}) -(def action-button-container - {:position :absolute - :align-items :center - :bottom (+ tabs.styles/tabs-diff 6) - :width 40 - :height 40}) - (def action-button {:width 40 :height 40 diff --git a/src/status_im/ui/screens/intro/styles.cljs b/src/status_im/ui/screens/intro/styles.cljs index 1e97da08323..6baa7f8eac0 100644 --- a/src/status_im/ui/screens/intro/styles.cljs +++ b/src/status_im/ui/screens/intro/styles.cljs @@ -7,9 +7,59 @@ :padding-horizontal 30}) (def intro-logo-container - {:flex 1 + {;:flex 1 :align-items :center - :justify-content :center}) + :justify-content :center + ;:margin-bottom 110 +}) + +(def welcome-image-container + {:align-items :center + :margin-top 42}) + +(def intro-button + {:margin-vertical 8 + :padding-horizontal 32 + :align-self :center + :justify-content :center + :align-items :center}) + +(def wizard-title + {:font-size 22 + :line-height 28 + :text-align :center + :font-weight "600" + :margin-bottom 16}) + +(def wizard-text + {:font-size 15 + :line-height 22 + :color colors/gray + :text-align :center}) + +(def welcome-text + {:typography :header + :margin-top 32 + :text-align :center}) + +(def welcome-text-bottom-note + {:font-size 12 + :line-height 14 + :color colors/gray + :text-align :center}) + +(def wizard-bottom-note + {:font-size 15 + :line-height 22 + :margin-top 20 + :color colors/gray + :text-align :center}) + +(def welcome-text-description + {:margin-top 8 + :text-align :center + :margin-horizontal 32 + :color colors/gray}) (def intro-logo {:size 111 @@ -31,4 +81,4 @@ (def bottom-button-container {:margin-bottom 6 - :margin-top 38}) + :margin-top 16}) diff --git a/src/status_im/ui/screens/intro/views.cljs b/src/status_im/ui/screens/intro/views.cljs index f5753e67acc..a8c18619e36 100644 --- a/src/status_im/ui/screens/intro/views.cljs +++ b/src/status_im/ui/screens/intro/views.cljs @@ -2,28 +2,147 @@ (:require-macros [status-im.utils.views :refer [defview letsubs]]) (:require [status-im.ui.components.react :as react] [re-frame.core :as re-frame] + [status-im.react-native.resources :as resources] + [taoensso.timbre :as log] + [status-im.ui.components.colors :as colors] + [reagent.core :as r] + [status-im.ui.components.toolbar.actions :as actions] [status-im.ui.components.common.common :as components.common] + [status-im.ui.components.numpad.views :as numpad] [status-im.ui.screens.intro.styles :as styles] + [status-im.ui.components.toolbar.view :as toolbar] [status-im.i18n :as i18n] [status-im.ui.components.status-bar.view :as status-bar] [status-im.ui.screens.privacy-policy.views :as privacy-policy])) +(def margin 24) + +(defn intro-viewer [slides window-width] + (let [view-width (- window-width (* 2 margin)) + scroll-x (r/atom 0) + scroll-view-ref (atom nil) + max-width (* view-width (dec (count slides)))] + (fn [] + [react/view {:style {:margin-horizontal 32 + :align-items :center}} + [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 + :on-scroll #(let [x (.-nativeEvent.contentOffset.x %) + _ (log/info "#scroll" x view-width)] + (cond (> x max-width) + (.scrollTo @scroll-view-ref (clj->js {:x 0})) + (< x 0) + (.scrollTo @scroll-view-ref (clj->js {:x max-width})) + :else (reset! scroll-x x))) + :style {:width view-width + :margin-vertical 32}} + (for [s slides] + ^{:key (:title s)} + [react/view {:style {:width view-width}} + [react/view {:style styles/intro-logo-container} + [components.common/image-contain + {:container-style {}} + {:image (:image s) :width view-width :height view-width}]] + [react/i18n-text {:style styles/wizard-title :key (:title s)}] + [react/i18n-text {:style styles/wizard-text + :key (:text s)}]])] + [react/view {:style {:flex-direction :row + :justify-content :space-between + :align-items :center + :height 6 + :width (+ 6 (* (+ 6 10) (dec (count slides))))}} + (doall + (for [i (range (count slides))] + ^{:key i} + [react/view {:style {:background-color + (if (= i (/ @scroll-x view-width)) colors/blue (colors/alpha colors/blue 0.2)) + :width 6 :height 6 + :border-radius 3}}]))]]))) + (defview intro [] - [react/view {:style styles/intro-view} - [status-bar/status-bar {:flat? true}] - [react/view {:style styles/intro-logo-container} - [components.common/logo styles/intro-logo]] - [react/i18n-text {:style styles/intro-text - :key :intro-text}] - [react/view - [react/i18n-text {:style styles/intro-text-description - :key :intro-text-description}]] - [react/view styles/buttons-container - [components.common/button {:button-style {:flex-direction :row} - :on-press #(re-frame/dispatch [:accounts.create.ui/create-new-account-button-pressed]) - :label (i18n/label :t/create-account)}] - [react/view styles/bottom-button-container - [components.common/button {:on-press #(re-frame/dispatch [:accounts.recover.ui/recover-account-button-pressed]) - :label (i18n/label :t/already-have-account) - :background? false}]] - [privacy-policy/privacy-policy-button]]]) + (letsubs [;{window-width :width window-height :height} [:dimensions/window] + window-width [:dimensions/window-width]] + [react/view {:style styles/intro-view} + [status-bar/status-bar {:flat? true}] + [intro-viewer [{:image (:intro1 resources/ui) + :title :intro-title1 + :text :intro-text1} + {:image (:intro2 resources/ui) + :title :intro-title2 + :text :intro-text2} + {:image (:intro3 resources/ui) + :title :intro-title3 + :text :intro-text3}] window-width] + [react/view {:flex 1}] + [react/view styles/buttons-container + [components.common/button {:button-style {:flex-direction :row} + :on-press #(re-frame/dispatch [:accounts.create.ui/intro-wizard]) + :label (i18n/label :t/get-started)}] + [react/view styles/bottom-button-container + [components.common/button {:on-press #(re-frame/dispatch [:accounts.recover.ui/recover-account-button-pressed]) + :label (i18n/label :t/access-key) + :background? false}]] + [react/i18n-text {:style styles/welcome-text-bottom-note :key :intro-privacy-policy-note}] + #_[privacy-policy/privacy-policy-button]]])) + +(defn generate-key [] + [components.common/image-contain + {:container-style {}} + {:image (:sample-key resources/ui) + :width 154 :height 140}]) + +(defn choose-key []) + +(defn select-key-storage []) + +(defn create-code [] + [react/view + [numpad/number-pad {:on-press #(re-frame/dispatch [:intro-wizard/code-digit-pressed %])}] + [react/text {:style styles/wizard-bottom-note} (i18n/label :t/you-will-need-this-code)]]) + +(defn confirm-code [] + [react/view + [numpad/number-pad {:on-press #(re-frame/dispatch [:intro-wizard/code-digit-pressed %])}] + [react/text {:style styles/wizard-bottom-note} (i18n/label :t/you-will-need-this-code)]]) + +(defn enable-fingerprint []) + +(defn enable-notifications []) + +(defview wizard [] + (letsubs [{:keys [step]} [:intro-wizard]] + [react/view {:style {:flex 1}} + [toolbar/toolbar + nil + (when-not (= :finish step) + (toolbar/nav-button + (actions/back #(re-frame/dispatch + [:intro-wizard/step-back-pressed])))) + nil] + [react/view {:style {:flex 1 + :margin-horizontal 32 + :justify-content :space-between}} + [react/view {:style {:margin-top 16}} + + [react/text {:style styles/wizard-title} (i18n/label (keyword (str "intro-wizard-title" step)))] + [react/text {:style styles/wizard-text} (i18n/label (keyword (str "intro-wizard-text" step)))]] + (case step + 1 [generate-key] + 2 [choose-key] + 3 [select-key-storage] + 4 [create-code] + 5 [confirm-code] + 6 [enable-fingerprint] + 7 [enable-notifications]) + [react/view {:style {:margin-bottom 32}} + [components.common/button {:button-style styles/intro-button + ;:disabled? disable-button? + :on-press #(re-frame/dispatch + [:intro-wizard/step-forward-pressed]) + :label (i18n/label :generate-a-key)}] + [react/text {:style styles/wizard-bottom-note} + (i18n/label :t/this-will-take-few-seconds)]]]])) diff --git a/src/status_im/ui/screens/routing/intro_login_stack.cljs b/src/status_im/ui/screens/routing/intro_login_stack.cljs index 6a8285816c7..bf761de0879 100644 --- a/src/status_im/ui/screens/routing/intro_login_stack.cljs +++ b/src/status_im/ui/screens/routing/intro_login_stack.cljs @@ -8,6 +8,7 @@ :recover :accounts :intro + :intro-wizard :hardwallet-authentication-method :hardwallet-connect :enter-pin-login @@ -37,6 +38,6 @@ (defn intro-stack [] (-> (login-stack :intro) - (update :screens conj :intro) + (update :screens conj :intro :intro-wizard) (assoc :name :intro-stack) (assoc :config {:initialRouteName :intro}))) diff --git a/src/status_im/ui/screens/routing/screens.cljs b/src/status_im/ui/screens/routing/screens.cljs index 9214e47bf05..f7d483e812b 100644 --- a/src/status_im/ui/screens/routing/screens.cljs +++ b/src/status_im/ui/screens/routing/screens.cljs @@ -169,6 +169,7 @@ :currency-settings currency-settings/currency-settings :backup-seed profile.seed/backup-seed :tribute-to-talk tr-to-talk/tribute-to-talk + :intro-wizard intro/wizard :reset-card hardwallet.settings/reset-card :keycard-settings hardwallet.settings/keycard-settings :mobile-network-settings mobile-network-settings/mobile-network-settings diff --git a/translations/en.json b/translations/en.json index c4ab2f0a910..fb6fb15bb4a 100644 --- a/translations/en.json +++ b/translations/en.json @@ -150,6 +150,28 @@ "other": "{{count}} members" }, "intro-message1": "Welcome to Status!\nTap this message to set your password and get started.", + "intro-title1": "Private, secure communication", + "intro-text1": "An open source platform to securely chat and transact on the Ethereum blockchain", + "intro-title2": "Secure crypto wallet", + "intro-text2": "Your account has been set up. Please dont forget to backup your recovery phrase in your profile", + "intro-title3": "Decentralized apps", + "intro-text3": "Your account has been set up. Please dont forget to backup your recovery phrase in your profile", + "intro-privacy-policy-note": "Status does not collect, share or sell any personal data. By continuing you agree with the privacy policy", + "intro-wizard-title1": "Get yourself a key first", + "intro-wizard-text1": "Your identity is secure by design. You get a locally generated cryptographic keypair", + "intro-wizard-title2": "Choose a key and name", + "intro-wizard-text2": "This name is your identity in Status. It can’t be changed once you choose one. ", + "intro-wizard-title3": "Select key storage", + "intro-wizard-text3": "Status does not store your key on any server.", + "intro-wizard-title4": "Create a 6-digit code", + "intro-wizard-text4": "To secure and encrypt your key", + "intro-wizard-title5": "Confirm the code", + "intro-wizard-title6": "Enable fingerprint", + "intro-wizard-text6": "Make it easy to sign and send transactions by enambling fingerprint signing", + "intro-wizard-title7": "Enable notifications", + "intro-wizard-text7": "Status will notify you about new messages and transactions. You can edit your notification preferences later in settings", + "generate-a-key": "Generate a key", + "you-will-need-this-code": "You'll need this code to open Status and sign transactions", "not-implemented": "!not implemented", "new-contact": "New contact", "datetime-second": { @@ -915,6 +937,7 @@ "view-my-wallet": "View my wallet", "share-my-profile": "Share my profile", "get-started": "Get started", + "access-key": "Access key", "tribute-to-talk": "Tribute to talk", "tribute-to-talk-desc": "Monetize your attention by requiring SNT for new people to start a chat", "tribute-to-talk-set-snt-amount": "Set the amount of SNT required for new people to start a chat",