diff --git a/android/app/src/main/res/drawable-hdpi/bell.png b/android/app/src/main/res/drawable-hdpi/bell.png new file mode 100644 index 0000000000..76ee86e868 Binary files /dev/null and b/android/app/src/main/res/drawable-hdpi/bell.png differ diff --git a/android/app/src/main/res/drawable-hdpi/fingerprint.png b/android/app/src/main/res/drawable-hdpi/fingerprint.png new file mode 100644 index 0000000000..17137fe969 Binary files /dev/null and b/android/app/src/main/res/drawable-hdpi/fingerprint.png differ diff --git a/android/app/src/main/res/drawable-hdpi/keycard_logo.png b/android/app/src/main/res/drawable-hdpi/keycard_logo.png new file mode 100644 index 0000000000..0546b5458b Binary files /dev/null and b/android/app/src/main/res/drawable-hdpi/keycard_logo.png differ diff --git a/android/app/src/main/res/drawable-mdpi/bell.png b/android/app/src/main/res/drawable-mdpi/bell.png new file mode 100644 index 0000000000..986123f365 Binary files /dev/null and b/android/app/src/main/res/drawable-mdpi/bell.png differ diff --git a/android/app/src/main/res/drawable-mdpi/fingerprint.png b/android/app/src/main/res/drawable-mdpi/fingerprint.png new file mode 100644 index 0000000000..7120503e07 Binary files /dev/null and b/android/app/src/main/res/drawable-mdpi/fingerprint.png differ diff --git a/android/app/src/main/res/drawable-mdpi/keycard_logo.png b/android/app/src/main/res/drawable-mdpi/keycard_logo.png new file mode 100644 index 0000000000..a1ff5a7434 Binary files /dev/null and b/android/app/src/main/res/drawable-mdpi/keycard_logo.png differ diff --git a/android/app/src/main/res/drawable-xhdpi/bell.png b/android/app/src/main/res/drawable-xhdpi/bell.png new file mode 100644 index 0000000000..4f4602ebaf Binary files /dev/null and b/android/app/src/main/res/drawable-xhdpi/bell.png differ diff --git a/android/app/src/main/res/drawable-xhdpi/fingerprint.png b/android/app/src/main/res/drawable-xhdpi/fingerprint.png new file mode 100644 index 0000000000..af1c6a23c3 Binary files /dev/null and b/android/app/src/main/res/drawable-xhdpi/fingerprint.png differ diff --git a/android/app/src/main/res/drawable-xhdpi/keycard_logo.png b/android/app/src/main/res/drawable-xhdpi/keycard_logo.png new file mode 100644 index 0000000000..8b0ed150cd Binary files /dev/null and b/android/app/src/main/res/drawable-xhdpi/keycard_logo.png differ diff --git a/android/app/src/main/res/drawable-xxhdpi/bell.png b/android/app/src/main/res/drawable-xxhdpi/bell.png new file mode 100644 index 0000000000..bc17c822ad Binary files /dev/null and b/android/app/src/main/res/drawable-xxhdpi/bell.png differ diff --git a/android/app/src/main/res/drawable-xxhdpi/fingerprint.png b/android/app/src/main/res/drawable-xxhdpi/fingerprint.png new file mode 100644 index 0000000000..16dea45157 Binary files /dev/null and b/android/app/src/main/res/drawable-xxhdpi/fingerprint.png differ diff --git a/android/app/src/main/res/drawable-xxhdpi/keycard_logo.png b/android/app/src/main/res/drawable-xxhdpi/keycard_logo.png new file mode 100644 index 0000000000..0a0a5cc602 Binary files /dev/null and b/android/app/src/main/res/drawable-xxhdpi/keycard_logo.png differ diff --git a/android/app/src/main/res/drawable-xxxhdpi/bell.png b/android/app/src/main/res/drawable-xxxhdpi/bell.png new file mode 100644 index 0000000000..d2e5fc4157 Binary files /dev/null and b/android/app/src/main/res/drawable-xxxhdpi/bell.png differ diff --git a/android/app/src/main/res/drawable-xxxhdpi/fingerprint.png b/android/app/src/main/res/drawable-xxxhdpi/fingerprint.png new file mode 100644 index 0000000000..d27788ec13 Binary files /dev/null and b/android/app/src/main/res/drawable-xxxhdpi/fingerprint.png differ diff --git a/android/app/src/main/res/drawable-xxxhdpi/keycard_logo.png b/android/app/src/main/res/drawable-xxxhdpi/keycard_logo.png new file mode 100644 index 0000000000..722f3568c1 Binary files /dev/null and b/android/app/src/main/res/drawable-xxxhdpi/keycard_logo.png differ diff --git a/externs.js b/externs.js index cfcf544daa..d614a2aa53 100644 --- a/externs.js +++ b/externs.js @@ -187,6 +187,7 @@ var TopLevel = { "hide" : function () {}, "i18n" : function () {}, "ignoreWarnings" : function () {}, + "importOnboardingAccount": function () {}, "in" : function () {}, "index" : function () {}, "indexOf" : function () {}, @@ -465,6 +466,7 @@ var TopLevel = { "StackActions" : function () {}, "start" : function () {}, "startNode" : function () {}, + "startOnboarding": function () {}, "state" : function () {}, "Status" : function () {}, "status" : function () {}, @@ -518,6 +520,7 @@ var TopLevel = { "verifyPin" : function () {}, "Version" : function () {}, "version" : function () {}, + "vibrate" : function () {}, "View" : function () {}, "warn" : function () {}, "Web3" : function () {}, diff --git a/ios/StatusIm/Images.xcassets/bell.imageset/Contents.json b/ios/StatusIm/Images.xcassets/bell.imageset/Contents.json new file mode 100644 index 0000000000..17224e6add --- /dev/null +++ b/ios/StatusIm/Images.xcassets/bell.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "bell_1x.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "bell_2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "bell_3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/ios/StatusIm/Images.xcassets/bell.imageset/bell_1x.png b/ios/StatusIm/Images.xcassets/bell.imageset/bell_1x.png new file mode 100644 index 0000000000..986123f365 Binary files /dev/null and b/ios/StatusIm/Images.xcassets/bell.imageset/bell_1x.png differ diff --git a/ios/StatusIm/Images.xcassets/bell.imageset/bell_2x.png b/ios/StatusIm/Images.xcassets/bell.imageset/bell_2x.png new file mode 100644 index 0000000000..4f4602ebaf Binary files /dev/null and b/ios/StatusIm/Images.xcassets/bell.imageset/bell_2x.png differ diff --git a/ios/StatusIm/Images.xcassets/bell.imageset/bell_3x.png b/ios/StatusIm/Images.xcassets/bell.imageset/bell_3x.png new file mode 100644 index 0000000000..bc17c822ad Binary files /dev/null and b/ios/StatusIm/Images.xcassets/bell.imageset/bell_3x.png differ diff --git a/ios/StatusIm/Images.xcassets/fingerprint.imageset/Contents.json b/ios/StatusIm/Images.xcassets/fingerprint.imageset/Contents.json new file mode 100644 index 0000000000..af6a125a04 --- /dev/null +++ b/ios/StatusIm/Images.xcassets/fingerprint.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "fingerprint_1x.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "fingerprint_2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "fingerprint_3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/ios/StatusIm/Images.xcassets/fingerprint.imageset/fingerprint_1x.png b/ios/StatusIm/Images.xcassets/fingerprint.imageset/fingerprint_1x.png new file mode 100644 index 0000000000..7120503e07 Binary files /dev/null and b/ios/StatusIm/Images.xcassets/fingerprint.imageset/fingerprint_1x.png differ diff --git a/ios/StatusIm/Images.xcassets/fingerprint.imageset/fingerprint_2x.png b/ios/StatusIm/Images.xcassets/fingerprint.imageset/fingerprint_2x.png new file mode 100644 index 0000000000..af1c6a23c3 Binary files /dev/null and b/ios/StatusIm/Images.xcassets/fingerprint.imageset/fingerprint_2x.png differ diff --git a/ios/StatusIm/Images.xcassets/fingerprint.imageset/fingerprint_3x.png b/ios/StatusIm/Images.xcassets/fingerprint.imageset/fingerprint_3x.png new file mode 100644 index 0000000000..16dea45157 Binary files /dev/null and b/ios/StatusIm/Images.xcassets/fingerprint.imageset/fingerprint_3x.png differ diff --git a/modules/react-native-status/android/src/main/java/im/status/ethereum/module/StatusModule.java b/modules/react-native-status/android/src/main/java/im/status/ethereum/module/StatusModule.java index 10feee6a77..59f7702413 100644 --- a/modules/react-native-status/android/src/main/java/im/status/ethereum/module/StatusModule.java +++ b/modules/react-native-status/android/src/main/java/im/status/ethereum/module/StatusModule.java @@ -660,6 +660,44 @@ class StatusModule extends ReactContextBaseJavaModule implements LifecycleEventL StatusThreadPoolExecutor.getInstance().execute(r); } + @ReactMethod + public void startOnboarding(final Integer n, final Integer mnemonicLength, final Callback callback) { + Log.d(TAG, "startOnboarding"); + if (!checkAvailability()) { + callback.invoke(false); + return; + } + Runnable r = new Runnable() { + @Override + public void run() { + String res = Statusgo.startOnboarding(n, mnemonicLength); + + callback.invoke(res); + } + }; + + StatusThreadPoolExecutor.getInstance().execute(r); + } + + @ReactMethod + public void importOnboardingAccount(final String id, final String password, final Callback callback) { + Log.d(TAG, "importOnboardingAccount"); + if (!checkAvailability()) { + callback.invoke(false); + return; + } + Runnable r = new Runnable() { + @Override + public void run() { + String res = Statusgo.importOnboardingAccount(id, password); + + callback.invoke(res); + } + }; + + StatusThreadPoolExecutor.getInstance().execute(r); + } + private String createIdentifier() { return UUID.randomUUID().toString(); } diff --git a/modules/react-native-status/ios/RCTStatus/RCTStatus.m b/modules/react-native-status/ios/RCTStatus/RCTStatus.m index f3b67f3534..16053ede2d 100644 --- a/modules/react-native-status/ios/RCTStatus/RCTStatus.m +++ b/modules/react-native-status/ios/RCTStatus/RCTStatus.m @@ -335,6 +335,29 @@ RCT_EXPORT_METHOD(recoverAccount:(NSString *)passphrase callback(@[result]); } +//////////////////////////////////////////////////////////////////// startOnboarding +RCT_EXPORT_METHOD(startOnboarding:(NSInteger)n + password:(NSInteger)mnemonicLength + callback:(RCTResponseSenderBlock)callback) { +#if DEBUG + NSLog(@"StartOnboarding() method called"); +#endif + NSString *result = StatusgoStartOnboarding(n, mnemonicLength); + callback(@[result]); +} + +//////////////////////////////////////////////////////////////////// importOnboardingAccount +RCT_EXPORT_METHOD(importOnboardingAccount:(NSString *)id + password:(NSString *)password + callback:(RCTResponseSenderBlock)callback) { +#if DEBUG + NSLog(@"ImportOnboardingAccount() method called"); +#endif + NSString *result = StatusgoImportOnboardingAccount(id, password); + callback(@[result]); +} + + //////////////////////////////////////////////////////////////////// login RCT_EXPORT_METHOD(login:(NSString *)address password:(NSString *)password @@ -449,6 +472,7 @@ RCT_EXPORT_METHOD(setSoftInputMode: (NSInteger) i) { RCT_EXPORT_METHOD(clearCookies) { NSHTTPCookie *cookie; NSHTTPCookieStorage *storage = [NSHTTPCookieStorage sharedHTTPCookieStorage]; + for (cookie in [storage cookies]) { [storage deleteCookie:cookie]; } diff --git a/resources/images/ui/intro1.png b/resources/images/ui/intro1.png new file mode 100644 index 0000000000..2dc713333e 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 0000000000..b6b967ea87 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 0000000000..885733f73b 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 0000000000..6ba485e18a 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 c95645a761..5aee910e26 100644 --- a/src/status_im/accounts/create/core.cljs +++ b/src/status_im/accounts/create/core.cljs @@ -17,7 +17,8 @@ [status-im.utils.identicon :as identicon] [status-im.utils.signing-phrase.core :as signing-phrase] [status-im.utils.types :as types] - [taoensso.timbre :as log] + [status-im.utils.utils :as utils] + [clojure.set :refer [map-invert]] [status-im.utils.fx :as fx] [status-im.node.core :as node] [status-im.ui.screens.mobile-network-settings.events :as mobile-network] @@ -26,26 +27,57 @@ (defn get-signing-phrase [cofx] (assoc cofx :signing-phrase (signing-phrase/generate))) +(def step-kw-to-num + {:generate-key 1 + :choose-key 2 + :select-key-storage 3 + :create-code 4 + :confirm-code 5 + :enable-fingerprint 6 + :enable-notifications 7}) + +(defn dec-step [step] + (let [inverted (map-invert step-kw-to-num)] + (inverted (dec (step-kw-to-num step))))) + +(defn inc-step [step] + (let [inverted (map-invert step-kw-to-num)] + (inverted (inc (step-kw-to-num step))))) + (defn get-status [cofx] (assoc cofx :status (rand-nth statuses/data))) -(defn create-account! [password] - (status/create-account - password - #(re-frame/dispatch [:accounts.create.callback/create-account-success (types/json->clj %) password]))) +(defn create-account! [{:keys [id password]}] + (if id + (status/import-onboarding-account + id + password + #(re-frame/dispatch [:accounts.create.callback/create-account-success (types/json->clj %) password])) + (status/create-account + password + #(re-frame/dispatch [:accounts.create.callback/create-account-success (types/json->clj %) password])))) ;;;; Handlers (defn create-account - [{:keys [db random-guid-generator] :as cofx}] - (fx/merge - cofx - {:db (-> db - (update :accounts/create assoc - :step :account-creating - :error nil) - (assoc :node/on-ready :create-account - :accounts/new-installation-id (random-guid-generator)))} - (node/initialize nil))) + [{:keys [db] :as cofx}] + (if (:intro-wizard db) + (fx/merge + cofx + {:accounts.create/create-account {:id (get-in db [:intro-wizard :selected-id]) + :password (or (get-in db [:accounts/create :password]) + (get-in db [:intro-wizard :key-code]))}}) + (fx/merge + cofx + {:db (-> db + (update :accounts/create assoc + :id (get-in db [:intro-wizard :selected-id]) + :password (or (get-in db [:accounts/create :password]) + (get-in db [:intro-wizard :key-code])) + :step :account-creating + :error nil) + (assoc :node/on-ready :create-account + :accounts/new-installation-id (random/guid)))} + (node/initialize nil)))) (fx/defn add-account "Takes db and new account, creates map of effects describing adding account to database and realm" @@ -59,44 +91,6 @@ {:db (assoc-in db [:accounts/accounts address] enriched-account) :data-store/base-tx [(accounts-store/save-account-tx enriched-account)]})) -(fx/defn on-account-created - [{:keys [signing-phrase - status - db] :as cofx} - {:keys [pubkey address mnemonic installation-id - keycard-instance-uid keycard-key-uid keycard-pairing keycard-paired-on]} - password - {:keys [seed-backed-up? login? new-account?] :or {login? true}}] - (let [normalized-address (utils.hex/normalize-hex address) - account {:public-key pubkey - :installation-id (or installation-id (get-in db [:accounts/new-installation-id])) - :address normalized-address - :name (gfycat/generate-gfy pubkey) - :status status - :signed-up? true - :desktop-notifications? false - :photo-path (identicon/identicon pubkey) - :signing-phrase signing-phrase - :seed-backed-up? seed-backed-up? - :mnemonic mnemonic - :keycard-instance-uid keycard-instance-uid - :keycard-key-uid keycard-key-uid - :keycard-pairing keycard-pairing - :keycard-paired-on keycard-paired-on - :settings (constants/default-account-settings) - :syncing-on-mobile-network? false - :remember-syncing-choice? false - :new-account? new-account?}] - (log/debug "account-created") - (when-not (string/blank? pubkey) - (fx/merge cofx - {:db (assoc db :accounts/login {:address normalized-address - :password password - :processing true})} - (add-account account) - (when login? - (accounts.login/user-login true)))))) - (defn reset-account-creation [{db :db}] {:db (update db :accounts/create assoc :step :enter-password @@ -145,6 +139,179 @@ (dissoc :password :password-confirm :name :error)))} (navigation/navigate-to-cofx :create-account nil))) +(fx/defn intro-wizard + {:events [:accounts.create.ui/intro-wizard]} + [{:keys [db] :as cofx}] + (fx/merge {:db (assoc db :intro-wizard {:step :generate-key + :weak-password? true + :encrypt-with-password? true} + :accounts/new-installation-id (random/guid))} + (navigation/navigate-to-cofx :intro-wizard nil))) + +(fx/defn intro-step-back + {:events [:intro-wizard/step-back-pressed]} + [{:keys [db] :as cofx}] + (let [step (get-in db [:intro-wizard :step])] + (if (not= :generate-key step) + (fx/merge {:db (cond-> (assoc-in db [:intro-wizard :step] (dec-step step)) + (#{:create-code :confirm-code} step) + (update :intro-wizard assoc :weak-password? true :key-code nil) + (= step :confirm-code) + (assoc-in [:intro-wizard :confirm-failure?] false))} + (navigation/navigate-to-cofx :intro-wizard nil)) + + (fx/merge {:db (dissoc db :intro-wizard)} + (navigation/navigate-to-clean :intro nil))))) + +(fx/defn exit-wizard [{:keys [db] :as cofx}] + (fx/merge {:db (dissoc db :intro-wizard) + :notifications/request-notifications-permissions nil} + (navigation/navigate-to-cofx :home nil))) + +(fx/defn init-key-generation [{:keys [db] :as cofx}] + (fx/merge + {:db (-> db + (assoc-in [:intro-wizard :generating-keys?] true) + (assoc :node/on-ready :start-onboarding))} + (node/initialize nil))) + +(fx/defn on-confirm-failure [{:keys [db] :as cofx}] + (do + (utils/vibrate) + {:db (assoc-in db [:intro-wizard :confirm-failure?] true)})) + +(defn confirm-failure? [db] + (let [step (get-in db [:intro-wizard :step])] + (and (= step :confirm-code) + (not (:accounts/login db)) + (get-in db [:intro-wizard :encrypt-with-password?]) + (not= (get-in db [:intro-wizard :stored-key-code]) (get-in db [:intro-wizard :key-code]))))) + +(fx/defn store-key-code [{:keys [db] :as cofx}] + (let [key-code (get-in db [:intro-wizard :key-code])] + {:db (update db :intro-wizard + assoc :stored-key-code key-code + :key-code nil + :step :confirm-code)})) + +(fx/defn intro-step-forward + {:events [:intro-wizard/step-forward-pressed]} + [{:keys [db] :as cofx} {:keys [skip?] :as opts}] + (let [step (get-in db [:intro-wizard :step])] + (cond (= step :enable-notifications) + (exit-wizard cofx) + + (= step :generate-key) + (init-key-generation cofx) + + (confirm-failure? db) + (on-confirm-failure cofx) + + (= step :create-code) + (store-key-code cofx) + + :else (fx/merge {:db (assoc-in db [:intro-wizard :step] + (inc-step step))} + (when (and (= step :confirm-code) + (not (:accounts/login db))) + (create-account cofx)))))) + +(fx/defn on-account-created + [{:keys [signing-phrase + status + db] :as cofx} + {:keys [pubkey address mnemonic installation-id + keycard-instance-uid keycard-key-uid keycard-pairing keycard-paired-on] :as result} + password + {:keys [seed-backed-up? login? new-account?] :or {login? true}}] + (let [normalized-address (utils.hex/normalize-hex address) + account {:public-key pubkey + :installation-id (or installation-id (get-in db [:accounts/new-installation-id])) + :address normalized-address + :name (gfycat/generate-gfy pubkey) + :status status + :signed-up? true + :desktop-notifications? false + :photo-path (identicon/identicon pubkey) + :signing-phrase signing-phrase + :seed-backed-up? seed-backed-up? + :mnemonic mnemonic + :keycard-instance-uid keycard-instance-uid + :keycard-key-uid keycard-key-uid + :keycard-pairing keycard-pairing + :keycard-paired-on keycard-paired-on + :settings (constants/default-account-settings) + :syncing-on-mobile-network? false + :remember-syncing-choice? false + :new-account? new-account?}] + (when-not (string/blank? pubkey) + (fx/merge cofx + {:db (assoc db :accounts/login {:address normalized-address + :password password + :processing true})} + (add-account account) + (when login? + (accounts.login/user-login true)) + (when (:intro-wizard db) + (intro-step-forward {})))))) + +(re-frame/reg-fx + :intro-wizard/start-onboarding + (fn [{:keys [n mnemonic-length]}] + (status/start-onboarding n mnemonic-length + #(re-frame/dispatch [:intro-wizard/on-keys-generated (types/json->clj %)])))) + +(fx/defn on-keys-generated + {:events [:intro-wizard/on-keys-generated]} + [{:keys [db] :as cofx} result] + (fx/merge + {:db (update db :intro-wizard + (fn [data] + (-> data + (dissoc :generating-keys?) + (assoc :accounts (:accounts result) + :selected-storage-type :default + :selected-id (-> result :accounts first :id) + :step :choose-key))))} + (navigation/navigate-to-cofx :intro-wizard nil))) + +(fx/defn on-key-selected + {:events [:intro-wizard/on-key-selected]} + [{:keys [db] :as cofx} id] + {:db (assoc-in db [:intro-wizard :selected-id] id)}) + +(fx/defn on-key-storage-selected + {:events [:intro-wizard/on-key-storage-selected]} + [{:keys [db] :as cofx} storage-type] + {:db (assoc-in db [:intro-wizard :selected-storage-type] storage-type)}) + +(fx/defn on-encrypt-with-password-pressed + {:events [:intro-wizard/on-encrypt-with-password-pressed]} + [{:keys [db] :as cofx}] + {:db (assoc-in db [:intro-wizard :encrypt-with-password?] true)}) + +(fx/defn on-learn-more-pressed + {:events [:intro-wizard/on-learn-more-pressed]} + [{:keys [db] :as cofx}] + {:db (assoc-in db [:intro-wizard :show-learn-more?] true)}) + +(defn get-new-key-code [current-code sym encrypt-with-password?] + (cond (or (= sym :remove) (= sym "Backspace")) + (subs current-code 0 (dec (count current-code))) + (and (not encrypt-with-password?) (= (count current-code) 6)) + current-code + (= (count sym) 1) + (str current-code sym) + :else current-code)) + +(fx/defn code-symbol-pressed + {:events [:intro-wizard/code-symbol-pressed]} + [{:keys [db] :as cofx} new-key-code] + (let [encrypt-with-password? (get-in db [:intro-wizard :encrypt-with-password?])] + {:db (update db :intro-wizard assoc :key-code new-key-code + :confirm-failure? false + :weak-password? (< (count new-key-code) 6))})) + ;;;; COFX (re-frame/reg-cofx diff --git a/src/status_im/init/core.cljs b/src/status_im/init/core.cljs index 058a2863ef..c402eb8fcb 100644 --- a/src/status_im/init/core.cljs +++ b/src/status_im/init/core.cljs @@ -166,6 +166,7 @@ :keys [accounts/accounts accounts/create networks/networks network network-status peers-count peers-summary view-id navigation-stack mailserver/mailservers + intro-wizard desktop/desktop hardwallet custom-fleets supported-biometric-auth device-UUID semaphores accounts/login] :node/keys [status on-ready] @@ -177,6 +178,7 @@ :view-id view-id :navigation-stack navigation-stack :node/status status + :intro-wizard intro-wizard :node/on-ready on-ready :accounts/create create :desktop/desktop (merge desktop (:desktop/desktop app-db)) @@ -201,20 +203,19 @@ (= view-id :create-account) (assoc-in [:accounts/create :step] :enter-name))})) -(defn login-only-events [cofx address stored-pns] +(defn login-only-events [{:keys [db] :as cofx} address stored-pns] (fx/merge cofx - (cond-> - {:notifications/request-notifications-permissions nil} - - platform/ios? - ;; on ios navigation state might be not initialized yet when - ;; navigate-to call happens. - ;; That's why it should be delayed a bit. - ;; TODO(rasom): revisit this later and find better solution - (assoc :dispatch-later - [{:ms 1 - :dispatch [:navigate-to :home]}])) - (when-not platform/ios? + (when-not (:intro-wizard db) + (cond-> {:notifications/request-notifications-permissions nil} + platform/ios? + ;; on ios navigation state might be not initialized yet when + ;; navigate-to call happens. + ;; That's why it should be delayed a bit. + ;; TODO(rasom): revisit this later and find better solution + (assoc :dispatch-later + [{:ms 1 + :dispatch [:navigate-to :home]}]))) + (when-not (or (:intro-wizard db) platform/ios?) (navigation/navigate-to-cofx :home nil)) (notifications/process-stored-event address stored-pns) (when platform/desktop? diff --git a/src/status_im/native_module/core.cljs b/src/status_im/native_module/core.cljs index 9354b2bfb9..c8479c4be8 100644 --- a/src/status_im/native_module/core.cljs +++ b/src/status_im/native_module/core.cljs @@ -22,6 +22,12 @@ (defn recover-account [passphrase password callback] (native-module/recover-account passphrase password callback)) +(defn start-onboarding [n mnemonic-length callback] + (native-module/start-onboarding n mnemonic-length callback)) + +(defn import-onboarding-account [id password callback] + (native-module/import-onboarding-account id password callback)) + (defn login [address password callback] (native-module/login address password callback)) diff --git a/src/status_im/native_module/impl/module.cljs b/src/status_im/native_module/impl/module.cljs index e2e56ac69f..481b5dcf5c 100644 --- a/src/status_im/native_module/impl/module.cljs +++ b/src/status_im/native_module/impl/module.cljs @@ -61,6 +61,14 @@ (when (and @node-started (status)) (.recoverAccount (status) passphrase password on-result))) +(defn start-onboarding [n mnemonic-length on-result] + (when (and @node-started (status)) + (.startOnboarding (status) n mnemonic-length on-result))) + +(defn import-onboarding-account [id password on-result] + (when (and @node-started (status)) + (.importOnboardingAccount (status) id password on-result))) + (defn login [address password on-result] (when (and @node-started (status)) (.login (status) address password on-result))) diff --git a/src/status_im/react_native/resources.cljs b/src/status_im/react_native/resources.cljs index bd435de676..e3885d8c81 100644 --- a/src/status_im/react_native/resources.cljs +++ b/src/status_im/react_native/resources.cljs @@ -6,6 +6,10 @@ :empty-recent (js-require/js-require "./resources/images/ui/empty-recent.png") :analytics-image (js-require/js-require "./resources/images/ui/analytics-image.png") :welcome-image (js-require/js-require "./resources/images/ui/welcome-image.png") + :intro1 (js-require/js-require "./resources/images/ui/intro1.png") + :intro2 (js-require/js-require "./resources/images/ui/intro2.png") + :intro3 (js-require/js-require "./resources/images/ui/intro3.png") + :sample-key (js-require/js-require "./resources/images/ui/sample-key.png") :lock (js-require/js-require "./resources/images/ui/lock.png") :tribute-to-talk (js-require/js-require "./resources/images/ui/tribute-to-talk.png") :wallet-welcome (js-require/js-require "./resources/images/ui/wallet-welcome.png") diff --git a/src/status_im/signals/core.cljs b/src/status_im/signals/core.cljs index 10de55341f..284a331630 100644 --- a/src/status_im/signals/core.cljs +++ b/src/status_im/signals/core.cljs @@ -37,14 +37,17 @@ [address password (:realm-error db)]})) :create-account (fn [_] - {:accounts.create/create-account (:password create)}) + {:accounts.create/create-account (select-keys create [:id :password])}) :recover-account (fn [{:keys [db]}] (let [{:keys [password passphrase]} (:accounts/recover db)] {:accounts.recover/recover-account [(security/mask-data passphrase) password]})) :create-keycard-account - (hardwallet/create-keycard-account))))) + (hardwallet/create-keycard-account) + :start-onboarding + (fn [] + {:intro-wizard/start-onboarding {:n 5 :mnemonic-length 12}}))))) (fx/defn status-node-stopped [{db :db}] diff --git a/src/status_im/subs.cljs b/src/status_im/subs.cljs index c87fd97485..8948691223 100644 --- a/src/status_im/subs.cljs +++ b/src/status_im/subs.cljs @@ -178,6 +178,9 @@ (reg-root-key-sub :signing/sign :signing/sign) (reg-root-key-sub :signing/edit-fee :signing/edit-fee) +;;intro-wizard +(reg-root-key-sub :intro-wizard :intro-wizard) + ;;GENERAL ============================================================================================================== (re-frame/reg-sub diff --git a/src/status_im/ui/components/typography.cljs b/src/status_im/ui/components/typography.cljs index a5ab379538..ece92fee6d 100644 --- a/src/status_im/ui/components/typography.cljs +++ b/src/status_im/ui/components/typography.cljs @@ -70,15 +70,17 @@ (assoc style :font-family default-font-family) (-> style (assoc :font-family - (str default-font-family "-" - (case font-weight - "400" (when-not (= font-style :italic) - "Regular") - "500" "Medium" - "600" "SemiBold" - "700" "Bold") - (when (= font-style :italic) - "Italic"))) + (if (= (:font-family style) "monospace") + (if platform/ios? "Menlo-Regular" "monospace") + (str default-font-family "-" + (case font-weight + "400" (when-not (= font-style :italic) + "Regular") + "500" "Medium" + "600" "SemiBold" + "700" "Bold") + (when (= font-style :italic) + "Italic")))) (dissoc :font-weight :font-style))))) (defn get-nested-style diff --git a/src/status_im/ui/screens/about_app/styles.cljs b/src/status_im/ui/screens/about_app/styles.cljs index 3d55e62df8..96256305f3 100644 --- a/src/status_im/ui/screens/about_app/styles.cljs +++ b/src/status_im/ui/screens/about_app/styles.cljs @@ -17,3 +17,9 @@ (def about-title-text {:font-size 20}) + +(def learn-more-title + {:typography :title-bold}) + +(def learn-more-text + {:color colors/gray}) diff --git a/src/status_im/ui/screens/about_app/views.cljs b/src/status_im/ui/screens/about_app/views.cljs index 987e3826be..814c3b50dc 100644 --- a/src/status_im/ui/screens/about_app/views.cljs +++ b/src/status_im/ui/screens/about_app/views.cljs @@ -1,11 +1,14 @@ (ns status-im.ui.screens.about-app.views - (:require-macros [status-im.utils.views :as views]) + (:require-macros [status-im.utils.views :as views] + [status-im.utils.views :refer [defview letsubs]]) (:require [status-im.ui.components.toolbar.view :as toolbar] [status-im.ui.components.react :as react] [status-im.ui.components.status-bar.view :as status-bar] + [status-im.ui.components.icons.vector-icons :as vector-icons] [status-im.ui.screens.about-app.styles :as styles] [status-im.i18n :as i18n] [status-im.transport.utils :as transport.utils] + [status-im.ui.components.colors :as colors] [status-im.ui.screens.profile.components.views :as profile.components] [re-frame.core :as re-frame])) @@ -20,6 +23,19 @@ nil 4)) +(defview learn-more-sheet [] + (letsubs [{:keys [title content]} [:bottom-sheet/options]] + [react/view {:style {:padding 16}} + [react/view {:style {:align-items :center :flex-direction :row :margin-bottom 16}} + [vector-icons/icon :main-icons/info {:color colors/blue + :container-style {:margin-right 13}}] + [react/text {:style styles/learn-more-title} title]] + [react/text {:style styles/learn-more-text} content]])) + +(def learn-more + {:content learn-more-sheet + :content-height 180}) + (defn peer-view [{:keys [enode]}] (let [[enode-id ip-address port] (transport.utils/extract-url-components enode)] ^{:key enode} diff --git a/src/status_im/ui/screens/intro/styles.cljs b/src/status_im/ui/screens/intro/styles.cljs index df7fd58c67..940617fb67 100644 --- a/src/status_im/ui/screens/intro/styles.cljs +++ b/src/status_im/ui/screens/intro/styles.cljs @@ -4,30 +4,102 @@ (def intro-view {:flex 1 - :padding-horizontal 30}) + :justify-content :flex-end + :padding-horizontal 30 + :margin-bottom 12}) (def intro-logo-container - {:flex 1 - :align-items :center + {:align-items :center :justify-content :center}) +(defn dot-selector [n] + (let [diameter 6 + interval 10] + {:flex-direction :row + :justify-content :space-between + :align-items :center + :height diameter + :width (+ diameter (* (+ diameter interval) + (dec n)))})) + +(defn dot [color selected?] + {:background-color (if selected? + color + (colors/alpha color 0.2)) + :width 6 + :height 6 + :border-radius 3}) + +(def welcome-image-container + {:align-items :center + :margin-top 42}) + +(def wizard-title + {:typography :header + :text-align :center + :margin-bottom 16}) + +(def wizard-text + {:color colors/gray + :text-align :center}) + +(def welcome-text + {:typography :header + :margin-top 32 + :text-align :center}) + +(def welcome-text-bottom-note + {:typography :caption + :color colors/gray + :text-align :center}) + +(defn list-item [selected?] + {:flex-direction :row + :align-items :center + :padding-left 16 + :padding-right 10 + :background-color (if selected? colors/blue-light colors/white) + :padding-vertical 10}) + +(def account-image + {:width 40 + :height 40 + :border-radius 20 + :border-width 1 + :border-color (colors/alpha colors/black 0.1)}) + +(def welcome-text-description + {:margin-top 8 + :text-align :center + :margin-horizontal 32 + :color colors/gray}) + (def intro-logo {:size 111}) -(defstyle intro-text - {:text-align :center - :font-weight "700" - :font-size 24}) - -(def intro-text-description - {:margin-top 8 - :margin-bottom 16 - :text-align :center - :color colors/gray}) +(defn password-text-input [width] + {:typography :header + :width width}) (def buttons-container - {:align-items :center}) + {:align-items :center + :margin-top 32}) + +(def bottom-button + {:padding-horizontal 24 + :justify-content :center + :align-items :center + :flex-direction :row}) (def bottom-button-container - {:margin-bottom 6 - :margin-top 38}) + {:margin-bottom 24 + :margin-top 16}) + +(def bottom-arrow + {:flex-direction :row + :justify-content :flex-end + :align-self :stretch + :padding-top 16 + :border-top-width 1 + :border-top-color colors/gray-lighter + :margin-right 20}) diff --git a/src/status_im/ui/screens/intro/views.cljs b/src/status_im/ui/screens/intro/views.cljs index f5753e67ac..6ff551a4fb 100644 --- a/src/status_im/ui/screens/intro/views.cljs +++ b/src/status_im/ui/screens/intro/views.cljs @@ -2,28 +2,297 @@ (: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] + [status-im.privacy-policy.core :as privacy-policy] + [status-im.accounts.create.core :refer [step-kw-to-num]] + [status-im.ui.components.icons.vector-icons :as vector-icons] + [status-im.utils.identicon :as identicon] + [status-im.ui.components.radio :as radio] + [taoensso.timbre :as log] + [status-im.utils.gfycat.core :as gfy] + [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.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])) + [status-im.ui.components.status-bar.view :as status-bar])) + +(defn dots-selector [{:keys [on-press n selected color]}] + [react/view {:style (styles/dot-selector n)} + (doall + (for [i (range n)] + ^{:key i} + [react/view {:style (styles/dot color (selected i))}]))]) + +(defn intro-viewer [slides window-width] + (let [margin 24 + 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 + :justify-content :flex-end}} + [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 %)] + (reset! scroll-x x)) + :style {:width view-width + :margin-vertical 32}} + (for [s slides] + ^{:key (:title s)} + [react/view {:style {:width view-width + :padding-horizontal 16}} + [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)}]])] + (let [selected (hash-set (/ @scroll-x view-width))] + [dots-selector {:selected selected :n (count slides) + :color colors/blue}])]))) (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 [: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 styles/buttons-container + [components.common/button {:button-style (assoc styles/bottom-button :margin-bottom 16) + :on-press #(re-frame/dispatch [:accounts.create.ui/intro-wizard]) + :label (i18n/label :t/get-started)}] + [components.common/button {:button-style (assoc styles/bottom-button :margin-bottom 24) + :on-press #(re-frame/dispatch [:accounts.recover.ui/recover-account-button-pressed]) + :label (i18n/label :t/access-key) + :background? false}] + [react/nested-text + {:style styles/welcome-text-bottom-note} + (i18n/label :t/intro-privacy-policy-note1) + [{:style (assoc styles/welcome-text-bottom-note :color colors/blue) + :on-press privacy-policy/open-privacy-policy-link!} + (i18n/label :t/intro-privacy-policy-note2)]]]])) + +(defn generate-key [] + [components.common/image-contain + {:container-style {:margin-horizontal 80}} + {:image (resources/get-image :sample-key) + :width 154 :height 140}]) + +(defn choose-key [{:keys [accounts selected-id] :as wizard-state} view-height] + [react/scroll-view {:content-container-style {:flex 1 + :justify-content :flex-end + ;; We have to align top account entry + ;; with top key storage entry on the next screen + :margin-bottom (if (< view-height 600) + -20 + (/ view-height 12))}} + (for [acc accounts] + (let [selected? (= (:id acc) selected-id)] + ^{:key (:pubkey acc)} + [react/touchable-highlight + {:on-press #(re-frame/dispatch [:intro-wizard/on-key-selected (:id acc)])} + [react/view {:style (styles/list-item selected?)} + + [react/image {:source {:uri (identicon/identicon (:pubkey acc))} + :style styles/account-image}] + [react/view {:style {:margin-horizontal 16 :flex 1 :justify-content :space-between}} + [react/text {:style (assoc styles/wizard-text :text-align :left + :color colors/black + :font-weight "500") + :number-of-lines 1 + :ellipsize-mode :middle} + (gfy/generate-gfy (:pubkey acc))] + [react/text {:style (assoc styles/wizard-text + :text-align :left + :font-family "monospace") + :number-of-lines 1 + :ellipsize-mode :middle} + (:pubkey acc)]] + [radio/radio selected?]]]))]) + +(defn storage-entry [{:keys [type icon title desc]} selected-storage-type] + (let [selected? (= type selected-storage-type)] + [react/view + [react/view {:style {:padding-top 14 :padding-bottom 4}} + [react/text {:style (assoc styles/wizard-text :text-align :left :margin-left 16)} + (i18n/label type)]] + [react/touchable-highlight + {:on-press #(re-frame/dispatch [:intro-wizard/on-key-storage-selected type])} + [react/view (assoc (styles/list-item selected?) + :align-items :flex-start + :padding-top 20 + :padding-bottom 12) + [vector-icons/icon icon {:color (if selected? colors/blue colors/gray) + :width 24 :height 24}] + [react/view {:style {:margin-horizontal 16 :flex 1}} + [react/text {:style (assoc styles/wizard-text :font-weight "500" :color colors/black :text-align :left)} + (i18n/label title)] + [react/view {:style {:min-height 4 :max-height 4}}] + [react/text {:style (assoc styles/wizard-text :text-align :left)} + (i18n/label desc)]] + [radio/radio selected?]]]])) + +(defn select-key-storage [{:keys [selected-storage-type] :as wizard-state} view-height] + (let [storage-types [{:type :default + :icon :main-icons/mobile + :title :this-device + :desc :this-device-desc} + {:type :advanced + :icon :main-icons/keycard-logo + :title :keycard + :desc :keycard-desc}]] + [react/view {:style {:flex 1 + :justify-content :flex-end + ;; We have to align top storage entry + ;; with top account entry on the previous screen + :margin-bottom (+ (- 300 232) (if (< view-height 600) + -20 + (/ view-height 12)))}} + [storage-entry (first storage-types) selected-storage-type] + [react/view {:style {:min-height 16 :max-height 16}}] + [storage-entry (second storage-types) selected-storage-type]])) + +(defn password-container [confirm-failure? view-width] + (let [horizontal-margin 16] + [react/view {:style {:flex 1 + :justify-content :space-between + :align-items :center :margin-horizontal horizontal-margin}} + [react/view {:style {:justify-content :center :flex 1}} + [react/text {:style (assoc styles/wizard-text :color colors/red + :margin-bottom 16)} + (if confirm-failure? (i18n/label :t/password_error1) " ")] + + [react/text-input {:secure-text-entry true + :auto-focus true + :text-align :center + :placeholder "" + :style (styles/password-text-input (- view-width (* 2 horizontal-margin))) + :on-change-text #(re-frame/dispatch [:intro-wizard/code-symbol-pressed %])}]] + [react/text {:style (assoc styles/wizard-text :margin-bottom 16)} (i18n/label :t/password-description)]])) + +(defn create-code [{:keys [confirm-failure?] :as wizard-state} view-width] + [password-container confirm-failure? view-width]) + +(defn confirm-code [{:keys [confirm-failure?] :as wizard-state} view-width] + [password-container confirm-failure? view-width]) + +(defn enable-fingerprint [] + [vector-icons/icon :main-icons/fingerprint + {:container-style {:align-items :center + :justify-content :center} + :width 76 :height 84}]) + +(defn enable-notifications [] + [vector-icons/icon :main-icons/bell {:container-style {:align-items :center + :justify-content :center} + :width 66 :height 64}]) + +(defn bottom-bar [{:keys [step generating-keys? weak-password? encrypt-with-password?] :as wizard-state}] + [react/view {:style {:margin-bottom (if (or (#{:choose-key :select-key-storage} step) + (and (#{:create-code :confirm-code} step) + encrypt-with-password?)) + 20 + 32) + :align-items :center}} + (cond generating-keys? + [react/activity-indicator {:animating true + :size :large}] + (#{:generate-key :enable-fingerprint :enable-notifications} step) + (let [label-kw (case step + :generate-key :generate-a-key + :enable-fingerprint :intro-wizard-title6 + :enable-notifications :intro-wizard-title7)] + [components.common/button {:button-style styles/bottom-button + :on-press #(re-frame/dispatch + [:intro-wizard/step-forward-pressed]) + :label (i18n/label label-kw)}]) + (and (#{:create-code :confirm-code} step) + (not encrypt-with-password?)) + [components.common/button {:button-style styles/bottom-button + :label (i18n/label :t/encrypt-with-password) + :on-press #(re-frame/dispatch [:intro-wizard/on-encrypt-with-password-pressed]) + :background? false}] + + :else + [react/view {:style styles/bottom-arrow} + [components.common/bottom-button {:on-press #(re-frame/dispatch + [:intro-wizard/step-forward-pressed]) + :disabled? (and (= step :create-code) weak-password?) + :forward? true}]]) + (when (#{:enable-fingerprint :enable-notifications} step) + [components.common/button {:button-style (assoc styles/bottom-button :margin-top 20) + :label (i18n/label :t/maybe-later) + :on-press #(re-frame/dispatch [:intro-wizard/step-forward-pressed {:skip? true}]) + :background? false}]) + (when (= :generate-key step) + [react/text {:style (assoc styles/wizard-text :margin-top 20)} + (i18n/label (if generating-keys? :t/generating-keys + :t/this-will-take-few-seconds))])]) + +(defn top-bar [{:keys [step encrypt-with-password?]}] + (let [hide-subtitle? (or (= step :confirm-code) + (and (#{:create-code :confirm-code} step) encrypt-with-password?))] + [react/view {:style {:margin-top 16 + :margin-horizontal 32}} + + [react/text {:style (cond-> styles/wizard-title + hide-subtitle? + (assoc :margin-bottom 0))} + (i18n/label (keyword (str "intro-wizard-title" (when (and (#{:create-code :confirm-code} step) encrypt-with-password?) + "-alt") (step-kw-to-num step))))] + (cond (#{:choose-key :select-key-storage} step) + ; Use nested text for the "Learn more" link + [react/nested-text {:style styles/wizard-text} + (str (i18n/label (keyword (str "intro-wizard-text" (step-kw-to-num step)))) " ") + [{:on-press #(re-frame/dispatch [:bottom-sheet/show-sheet :learn-more + {:title (i18n/label (if (= step :choose-key) :t/about-names-title :t/about-key-storage-title)) + :content (i18n/label (if (= step :choose-key) :t/about-names-content :t/about-key-storage-content))}]) + :style {:color colors/blue}} + (i18n/label :learn-more)]] + (not hide-subtitle?) + [react/text {:style styles/wizard-text} + (i18n/label (keyword (str "intro-wizard-text" (step-kw-to-num step))))] + :else nil)])) + +(defview wizard [] + (letsubs [{:keys [step generating-keys?] :as wizard-state} [:intro-wizard] + {view-height :height view-width :width} [:dimensions/window]] + [react/keyboard-avoiding-view {:style {:flex 1}} + [toolbar/toolbar + {:style {:border-bottom-width 0 + :margin-top 0}} + (when-not (#{:enable-fingerprint :enable-notifications} step) + (toolbar/nav-button + (actions/back #(re-frame/dispatch + [:intro-wizard/step-back-pressed])))) + nil] + [react/view {:style {:flex 1 + :justify-content :space-between}} + [top-bar wizard-state] + (case step + :generate-key [generate-key] + :choose-key [choose-key wizard-state view-height] + :select-key-storage [select-key-storage wizard-state view-height] + :create-code [create-code wizard-state view-width] + :confirm-code [confirm-code wizard-state view-width] + :enable-fingerprint [enable-fingerprint] + :enable-notifications [enable-notifications] + nil nil) + [bottom-bar wizard-state]]])) diff --git a/src/status_im/ui/screens/profile/user/views.cljs b/src/status_im/ui/screens/profile/user/views.cljs index c8c2943e56..d87daceb77 100644 --- a/src/status_im/ui/screens/profile/user/views.cljs +++ b/src/status_im/ui/screens/profile/user/views.cljs @@ -253,7 +253,7 @@ [react/view {:style styles/advanced-button-container-background} [react/view {:style styles/advanced-button-row} [react/text {:style styles/advanced-button-label} - (i18n/label :t/wallet-advanced)] + (i18n/label :t/advanced)] [icons/icon (if advanced? :main-icons/dropdown-up :main-icons/dropdown) {:color colors/blue}]]]]] (when advanced? [advanced-settings params on-show supported-biometric-auth])])) 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 1903926d29..074c2b31ac 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 @@ -52,6 +53,7 @@ (-> (login-stack :intro) (update :screens conj :intro + :intro-wizard :keycard-onboarding-intro :keycard-onboarding-start :keycard-onboarding-puk-code diff --git a/src/status_im/ui/screens/routing/screens.cljs b/src/status_im/ui/screens/routing/screens.cljs index 49e31b9bd6..89fa4cee4a 100644 --- a/src/status_im/ui/screens/routing/screens.cljs +++ b/src/status_im/ui/screens/routing/screens.cljs @@ -71,6 +71,7 @@ :recover recover/recover :accounts accounts/accounts :intro intro/intro + :intro-wizard intro/wizard :hardwallet-authentication-method hardwallet.authentication/hardwallet-authentication-method :hardwallet-connect hardwallet.connect/hardwallet-connect :hardwallet-connect-settings hardwallet.connect/hardwallet-connect diff --git a/src/status_im/ui/screens/views.cljs b/src/status_im/ui/screens/views.cljs index 173c13ac12..927ad4efe6 100644 --- a/src/status_im/ui/screens/views.cljs +++ b/src/status_im/ui/screens/views.cljs @@ -3,6 +3,7 @@ (:require [re-frame.core :refer [dispatch]] [status-im.utils.platform :refer [android?]] [status-im.utils.universal-links.core :as utils.universal-links] + [status-im.ui.screens.about-app.views :as about-app] [status-im.ui.components.react :as react] [status-im.ui.components.bottom-sheet.core :as bottom-sheet] [status-im.utils.navigation :as navigation] @@ -46,6 +47,9 @@ (= view :public-chat-actions) (merge home.sheet/public-chat-actions) + (= view :learn-more) + (merge about-app/learn-more) + (= view :private-chat-actions) (merge home.sheet/private-chat-actions) diff --git a/src/status_im/ui/screens/wallet/send/views.cljs b/src/status_im/ui/screens/wallet/send/views.cljs index a83a4d0876..5aad69e820 100644 --- a/src/status_im/ui/screens/wallet/send/views.cljs +++ b/src/status_im/ui/screens/wallet/send/views.cljs @@ -87,4 +87,4 @@ :scroll scroll :chain chain :all-tokens all-tokens - :network-status network-status}])) \ No newline at end of file + :network-status network-status}])) diff --git a/src/status_im/utils/utils.cljs b/src/status_im/utils/utils.cljs index b6610604b0..d5c2802165 100644 --- a/src/status_im/utils/utils.cljs +++ b/src/status_im/utils/utils.cljs @@ -19,6 +19,9 @@ (when on-dismiss (clj->js {:cancelable false}))))) +(defn vibrate [] + #_(.vibrate (.-Vibration rn-dependencies/react-native))) + (re-frame/reg-fx :utils/show-popup (fn [{:keys [title content on-dismiss]}] diff --git a/test/appium/tests/atomic/account_management/test_create_account.py b/test/appium/tests/atomic/account_management/test_create_account.py index 410c53e598..6812049dbd 100644 --- a/test/appium/tests/atomic/account_management/test_create_account.py +++ b/test/appium/tests/atomic/account_management/test_create_account.py @@ -41,7 +41,18 @@ class TestCreateAccount(SingleDeviceTestCase): if sign_in.ok_button.is_element_displayed(): sign_in.ok_button.click() sign_in.other_accounts_button.click() - sign_in.create_user() + sign_in.create_account_button.click() + sign_in.password_input.set_value(common_password) + sign_in.next_button.click() + sign_in.confirm_password_input.set_value(common_password) + sign_in.next_button.click() + + sign_in.element_by_text_part('Display name').wait_for_element(60) + username = 'user_%s' % get_current_time() + sign_in.name_input.set_value(username) + + sign_in.next_button.click() + sign_in.get_started_button.click() if sign_in.get_public_key() == public_key: pytest.fail('New account was not created') diff --git a/test/appium/tests/atomic/account_management/test_sign_in.py b/test/appium/tests/atomic/account_management/test_sign_in.py index caf7fe83ba..65fa02d0e1 100644 --- a/test/appium/tests/atomic/account_management/test_sign_in.py +++ b/test/appium/tests/atomic/account_management/test_sign_in.py @@ -13,12 +13,13 @@ class TestSignIn(SingleDeviceTestCase): @marks.critical def test_login_with_new_account(self): sign_in = SignInView(self.driver) - username = 'test_user' - sign_in.create_user(username=username) + sign_in.create_user() + profile = sign_in.profile_button.click() + default_username = profile.default_username_text.text self.driver.close_app() self.driver.launch_app() sign_in.accept_agreements() - if not sign_in.element_by_text(username).is_element_displayed(): + if not sign_in.element_by_text(default_username).is_element_displayed(): self.errors.append('Username is not shown while login') sign_in.sign_in() if not sign_in.home_button.is_element_displayed(): diff --git a/test/appium/tests/atomic/chats/test_chats_management.py b/test/appium/tests/atomic/chats/test_chats_management.py index 31b835ac6e..befce96bc0 100644 --- a/test/appium/tests/atomic/chats/test_chats_management.py +++ b/test/appium/tests/atomic/chats/test_chats_management.py @@ -230,7 +230,7 @@ class TestChatManagementMultipleDevice(MultipleDeviceTestCase): username_1 = 'user_%s' % get_current_time() message_before_block_1, message_before_block_2 = "Before block from %s" % device_1.driver.number, "Before block from %s" % device_2.driver.number message_after_block_2 = "After block from %s" % device_2.driver.number - home_1, home_2 = device_1.create_user(username=username_1), device_2.recover_access(basic_user['passphrase']) + home_1, home_2 = device_1.create_user(), device_2.recover_access(basic_user['passphrase']) # device 1, device 2: join to public chat and send several messages chat_name = device_1.get_public_chat_name() diff --git a/test/appium/tests/atomic/chats/test_public.py b/test/appium/tests/atomic/chats/test_public.py index 4d277df948..c60b2687fc 100644 --- a/test/appium/tests/atomic/chats/test_public.py +++ b/test/appium/tests/atomic/chats/test_public.py @@ -15,8 +15,7 @@ class TestPublicChatMultipleDevice(MultipleDeviceTestCase): def test_public_chat_messaging(self): self.create_drivers(2) device_1, device_2 = SignInView(self.drivers[0]), SignInView(self.drivers[1]) - username_1, username_2 = 'user_1', 'user_2' - home_1, home_2 = device_1.create_user(username=username_1), device_2.create_user(username=username_2) + home_1, home_2 = device_1.create_user(), device_2.create_user() profile_1 = home_1.profile_button.click() default_username_1 = profile_1.default_username_text.text profile_1.home_button.click() diff --git a/test/appium/views/sign_in_view.py b/test/appium/views/sign_in_view.py index 742e16864e..42ca632b16 100644 --- a/test/appium/views/sign_in_view.py +++ b/test/appium/views/sign_in_view.py @@ -6,20 +6,32 @@ from views.base_view import BaseView class AccountButton(BaseButton): - def __init__(self, driver): super(AccountButton, self).__init__(driver) self.locator = self.Locator.xpath_selector("//*[contains(@text,'0x')]") class PasswordInput(BaseEditBox): - def __init__(self, driver): super(PasswordInput, self).__init__(driver) self.locator = self.Locator.xpath_selector("//android.widget.TextView[@text='Password']" "/following-sibling::android.view.ViewGroup/android.widget.EditText") +class CreatePasswordInput(BaseEditBox): + def __init__(self, driver): + super(CreatePasswordInput, self).__init__(driver) + self.locator = self.Locator.xpath_selector("//android.widget.TextView[@text='Create a password']" + "/following-sibling::android.widget.EditText") + + +class ConfirmYourPasswordInput(BaseEditBox): + def __init__(self, driver): + super(ConfirmYourPasswordInput, self).__init__(driver) + self.locator = self.Locator.xpath_selector("//android.widget.TextView[@text='Confirm your password']" + "/following-sibling::android.widget.EditText") + + class SignInButton(BaseButton): def __init__(self, driver): @@ -48,12 +60,30 @@ class CreateAccountButton(BaseButton): self.locator = self.Locator.xpath_selector("//*[@text='Create account' or @text='Create new account']") +class GenerateKeyButton(BaseButton): + def __init__(self, driver): + super(GenerateKeyButton, self).__init__(driver) + self.locator = self.Locator.xpath_selector("//*[@text='Generate a key']") + + class IHaveAccountButton(RecoverAccessButton): def __init__(self, driver): super(IHaveAccountButton, self).__init__(driver) self.locator = self.Locator.xpath_selector("//*[@text='I already have an account']") +class AccessKeyButton(RecoverAccessButton): + def __init__(self, driver): + super(AccessKeyButton, self).__init__(driver) + self.locator = self.Locator.xpath_selector("//*[@text='Access key']") + + +class MaybeLaterButton(BaseButton): + def __init__(self, driver): + super(MaybeLaterButton, self).__init__(driver) + self.locator = self.Locator.xpath_selector("//*[@text='Maybe later']") + + class AddExistingAccountButton(RecoverAccessButton): def __init__(self, driver): super(AddExistingAccountButton, self).__init__(driver) @@ -106,25 +136,35 @@ class SignInView(BaseView): # new design self.create_account_button = CreateAccountButton(self.driver) self.i_have_account_button = IHaveAccountButton(self.driver) + self.access_key_button = AccessKeyButton(self.driver) + self.generate_key_button = GenerateKeyButton(self.driver) self.add_existing_account_button = AddExistingAccountButton(self.driver) self.confirm_password_input = ConfirmPasswordInput(self.driver) + self.create_password_input = CreatePasswordInput(self.driver) + self.confirm_your_password_input = ConfirmYourPasswordInput(self.driver) + self.maybe_later_button = MaybeLaterButton(self.driver) self.name_input = NameInput(self.driver) self.other_accounts_button = OtherAccountsButton(self.driver) self.privacy_policy_link = PrivacyPolicyLink(self.driver) - def create_user(self, username: str = '', password=common_password): - self.create_account_button.click() - self.password_input.set_value(password) - self.next_button.click() - self.confirm_password_input.set_value(password) - self.next_button.click() - - self.element_by_text_part('Display name').wait_for_element(60) - username = username if username else 'user_%s' % get_current_time() - self.name_input.set_value(username) - - self.next_button.click() + def create_user(self, password=common_password): self.get_started_button.click() + self.generate_key_button.click() + self.next_button.click() + self.next_button.click() + self.create_password_input.set_value(password) + self.next_button.click() + self.confirm_your_password_input.set_value(password) + self.next_button.click() + self.maybe_later_button.click() + self.maybe_later_button.click() + + # self.element_by_text_part('Display name').wait_for_element(60) + # username = username if username else 'user_%s' % get_current_time() + # self.name_input.set_value(username) + + # self.next_button.click() + # self.get_started_button.click() return self.get_home_view() def recover_access(self, passphrase: str, password: str = common_password): @@ -132,7 +172,7 @@ class SignInView(BaseView): self.other_accounts_button.click() recover_access_view = self.add_existing_account_button.click() else: - recover_access_view = self.i_have_account_button.click() + recover_access_view = self.access_key_button.click() recover_access_view.passphrase_input.click() recover_access_view.passphrase_input.set_value(passphrase) recover_access_view.password_input.click() diff --git a/test/cljs/status_im/test/accounts/create/core.cljs b/test/cljs/status_im/test/accounts/create/core.cljs index ded8fdb2a7..9488ec7526 100644 --- a/test/cljs/status_im/test/accounts/create/core.cljs +++ b/test/cljs/status_im/test/accounts/create/core.cljs @@ -1,5 +1,6 @@ (ns status-im.test.accounts.create.core (:require [cljs.test :refer-macros [deftest is testing]] + [status-im.utils.utils :as utils] [status-im.accounts.create.core :as models])) (deftest on-account-created @@ -11,23 +12,65 @@ :mnemonic "hello world"} "password" true)] - (is (= (:db result) - {:accounts/login {:address "7e92236392a850980d00d0cd2a4b92886bd7fe7b", :password "password", :processing true}, - :accounts/accounts {"7e92236392a850980d00d0cd2a4b92886bd7fe7b" - {:address "7e92236392a850980d00d0cd2a4b92886bd7fe7b", :mnemonic "hello world", :signing-phrase "", - :signed-up? true, :name "Dark Woozy Alligatorsnappingturtle", :desktop-notifications? false, - :settings {:wallet {:visible-tokens {:testnet #{:STT :HND}, - :mainnet #{:SNT}, - :rinkeby #{:MOKSHA :KDO}, :xdai #{}, :poa #{}}}}, - :networks nil, - :photo-path "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAMAAAC7IEhfAAADAFBMVEX////YjowAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACRS2MAAABAHRSTlP//wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKmfXxgAABnNJREFUeNoBaAaX+QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAQEBAQEBAQEBAQEBAQEBAQEBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAQEBAQEBAQEBAQEBAQEBAQEBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQEBAQEBAQEBAQEBAQEBAQEBAQEAAAAAAAAAAAAAAAAAAAAAAAAAAAABAQEBAQEBAQEBAQEBAQEBAQEBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAQEBAQEBAQEBAQEBAQEBAQEBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQEBAQEBAQEBAQEBAQEBAQEBAQEAAAAAAAAAAAAAAAAAAAAAAAAAAAABAQEBAQEBAQEBAQEBAQEBAQEBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAQEBAQEBAQEBAQEBAQEBAQEBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQEBAQAAAAAAAAAAAAAAAAEBAQEAAAAAAAAAAAAAAAAAAAAAAAAAAAABAQEBAAAAAAAAAAAAAAAAAQEBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAQEAAAAAAAAAAAAAAAABAQEBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQEBAQAAAAAAAAAAAAAAAAEBAQEAAAAAAAAAAAAAAAAAAAAAAAAAAAABAQEBAAAAAAEBAQEAAAAAAQEBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAQEAAAAAAQEBAQAAAAABAQEBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQEBAQAAAAABAQEBAAAAAAEBAQEAAAAAAAAAAAAAAAAAAAAAAAAAAAABAQEBAAAAAAEBAQEAAAAAAQEBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQEBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAQEBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAQEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQEBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAoRUBAbwwC1cAAAAASUVORK5CYII=", - :seed-backed-up? true, - :network "mainnet_rpc", - :public-key "04de2e21f1642ebee03b9aa4bf1936066124cc89967eaf269544c9b90c539fd5c980166a897d06dd4d3732b38116239f63c89395a8d73eac72881fab802010cb56", - :installation-id ""}}, - :network "mainnet_rpc", - :node/status :starting})) (is (= (keys result) - [:db - :node/start - :data-store/base-tx])))) + [:db :accounts.login/clear-web-data :data-store/change-account :data-store/base-tx])))) + +(deftest intro-step-back + (testing "Back from choose-key" + (let [db {:intro-wizard {:step :choose-key}} + result (get-in (models/intro-step-back {:db db}) [:db :intro-wizard])] + (is (= result {:step :generate-key})))) + + (testing "Back from create-code" + (let [db {:intro-wizard {:step :create-code :key-code "qwerty"}} + result (get-in (models/intro-step-back {:db db}) [:db :intro-wizard])] + (is (= result {:step :select-key-storage :key-code nil :weak-password? true})))) + + (testing "Back from confirm-code" + (let [db {:intro-wizard {:step :confirm-code :confirm-failure? true}} + result (get-in (models/intro-step-back {:db db}) [:db :intro-wizard])] + (is (= result {:step :create-code :key-code nil :confirm-failure? false :weak-password? true}))))) + +(deftest intro-step-forward + (testing "Forward from choose-key" + (let [db {:intro-wizard {:step :choose-key}} + ;; In this case intro-step-forward returns fx/merge result which is an fn + ;; to be invoked on cofx + result (get-in ((models/intro-step-forward {:db db}) {:db db}) [:db :intro-wizard])] + (is (= result {:step :select-key-storage})))) + + (testing "Forward from generate-key" + (let [db {:intro-wizard {:step :generate-key}} + result ((models/intro-step-forward {:db db}) {:db db})] + (is (= (select-keys (:db result) [:intro-wizard :node/on-ready]) {:intro-wizard {:step :generate-key :generating-keys? true} + :node/on-ready :start-onboarding})))) + + (testing "Forward from create-code" + (let [db {:intro-wizard {:step :create-code :key-code "qwerty"}} + result (get-in ((models/intro-step-forward {:db db}) {:db db}) [:db :intro-wizard])] + (is (= result {:step :confirm-code :key-code nil :stored-key-code "qwerty"})))) + + (testing "Forward from confirm-code (failure case)" + (with-redefs [utils/vibrate (fn [] "vibrating")] + (let [db {:intro-wizard {:step :confirm-code :key-code "abcdef" :encrypt-with-password? true :stored-key-code "qwerty"}} + result (get-in ((models/intro-step-forward {:db db}) {:db db}) [:db :intro-wizard])] + (is (= result {:step :confirm-code :key-code "abcdef" :confirm-failure? true + :encrypt-with-password? true + :stored-key-code "qwerty"})))))) + +(deftest on-keys-generated + (testing "Test merging of generated keys into app-db" + (let [db {:intro-wizard {:step :generate-key :generating-keys true}} + accounts [{:id "0x01"} + {:id "0x02"} + {:id "0x03"} + {:id "0x04"} + {:id "0x05"}] + result (get-in (models/on-keys-generated {:db db} {:accounts accounts}) [:db :intro-wizard])] + (is (= result) {:step :choose-key :accounts accounts :selected-storage-type :default :selected-id (-> accounts first :id)})))) + +(deftest get-new-key-code + (testing "Add new character to keycode" + (is (= "abcd" (models/get-new-key-code "abc" "d" true)))) + (testing "Remove trailing character from keycode" + (is (= "ab" (models/get-new-key-code "abc" :remove true))))) diff --git a/test/cljs/status_im/test/runner.cljs b/test/cljs/status_im/test/runner.cljs index 9fc76a3609..78f865df83 100644 --- a/test/cljs/status_im/test/runner.cljs +++ b/test/cljs/status_im/test/runner.cljs @@ -1,5 +1,6 @@ (ns status-im.test.runner (:require [doo.runner :refer-macros [doo-tests]] + [status-im.test.accounts.create.core] [status-im.test.accounts.recover.core] [status-im.test.browser.core] [status-im.test.browser.permissions] @@ -81,6 +82,7 @@ (set! goog.DEBUG false) (doo-tests + 'status-im.test.accounts.create.core 'status-im.test.accounts.recover.core 'status-im.test.browser.core 'status-im.test.browser.permissions @@ -151,4 +153,4 @@ 'status-im.test.utils.utils 'status-im.test.wallet.subs 'status-im.test.wallet.transactions - 'status-im.test.wallet.transactions.subs) \ No newline at end of file + 'status-im.test.wallet.transactions.subs) diff --git a/translations/el.json b/translations/el.json index 82d59b0650..e89bec621b 100644 --- a/translations/el.json +++ b/translations/el.json @@ -7,7 +7,7 @@ "amount": "Ποσό", "open": "Άνοιγμα", "close-app-title": "Προειδοποίηση!", - "wallet-advanced": "Προχωρημένη", + "advanced": "Προχωρημένη", "members-active": { "one": "1 μέλος", "other": "{{count}} μέλη", diff --git a/translations/en.json b/translations/en.json index 6a9f071c26..ac78e0768f 100644 --- a/translations/en.json +++ b/translations/en.json @@ -151,6 +151,41 @@ "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-note1": "Status does not collect, share or sell any personal data. By continuing you agree with the ", + "intro-privacy-policy-note2": "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": "Your key is stored locally. There is no copy. Only you have access.", + "intro-wizard-title4": "Create a 6-digit code", + "intro-wizard-title-alt4": "Create a password", + "intro-wizard-text4": "Secure and encrypt your key", + "intro-wizard-title5": "Confirm the code", + "intro-wizard-title-alt5": "Confirm your password", + "intro-wizard-title6": "Enable fingerprint", + "intro-wizard-text6": "Make it easy to sign and send transactions by enabling fingerprint signing", + "intro-wizard-title7": "Enable notifications", + "intro-wizard-text7": "Status will notify you about new messages. You can edit your notification preferences later in settings", + "generate-a-key": "Generate a key", + "generating-keys": "Generating keys...", + "you-will-need-this-code": "You'll need this code to open Status and sign transactions", + "this-device": "This device", + "this-device-desc": "Your key will be encrypted and securely stored", + "keycard-desc": "Android only. You will need to get a Keycard first", + "about-names-title": "About the names", + "about-names-content": "Your identity is secure and private by design. You get a locally generated cryptographic keypair. The name and image are a readable version of this. They are unique. Nobody can pretend to be you. Nobody sees your name unless you provide it.", + "about-key-storage-title": "About key storage", + "about-key-storage-content": "Status will never access your private key. Be sure to backup your Seed phrase. If you lose your phone it is the only way to access your keys.", + "encrypt-with-password": "Encrypt with password", + "maybe-later": "Maybe later", "not-implemented": "!not implemented", "new-contact": "New contact", "datetime-second": { @@ -249,7 +284,7 @@ "group-chat-admin-added": "*{{member}}* has been made admin", "group-chat-no-contacts": "You don't have any contacts yet.\nInvite your friends to start chatting", "agree-by-continuing": "By continuing you agree\n to our ", - "wallet-advanced": "Advanced", + "advanced": "Advanced", "currency-display-name-sos": "Somalia Shilling", "currency-display-name-zar": "South Africa Rand", "offline-messaging": "Mailserver", @@ -649,7 +684,7 @@ "wallet-asset": "Asset", "close-app-content": "The app will stop and close. When you reopen it, the selected network will be used", "logout-app-content": "The account will be logged out. When you log in again, the selected network will be used", - "password-description": "You'll need this password to open the app and confirm transactions.", + "password-description": "At least 6 characters. You'll need this password to open Status and confirm transactions", "currency-display-name-afn": "Afghanistan Afghani", "word-n-description": "In order to check if you have backed up your recovery phrase correctly, enter the word #{{number}} above.", "topic-name-error": "Use only lowercase letters (a to z), numbers & dashes (-). Do not use contact codes", @@ -704,6 +739,7 @@ "transactions-history": "Transaction history", "fetching-messages": "Fetching messages... ({{requests-left}} requests left)", "password_error1": "Passwords don't match.", + "passcode-error": "Passcodes don't match.", "your-contact-code": "Granting access authorizes this DApp to retrieve your contact code", "password-placeholder": "At least 6 characters", "clear-history-action": "Clear", @@ -919,6 +955,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", diff --git a/translations/es_419.json b/translations/es_419.json index 46115c7968..757a996dcb 100644 --- a/translations/es_419.json +++ b/translations/es_419.json @@ -647,7 +647,7 @@ "wallet": "Billetera", "wallet-add-asset": "Agregar activo", "wallet-address-from-clipboard": "Usar la dirección desde el portapapeles", - "wallet-advanced": "Avanzado", + "advanced": "Avanzado", "wallet-asset": "Activo", "wallet-assets": "Activos", "wallet-backup-recovery-description": "Esto te ayudará a mantener tus activos seguros", diff --git a/translations/fa.json b/translations/fa.json index 0c5fe9d7ff..f0e35b215c 100644 --- a/translations/fa.json +++ b/translations/fa.json @@ -652,7 +652,7 @@ "wallet": "کیف پول", "wallet-add-asset": "افزودن دارایی جدید", "wallet-address-from-clipboard": "از آدرسی که در کلیپ بورد ذخیره کردم استفاده کن", - "wallet-advanced": "پیشرفته", + "advanced": "پیشرفته", "wallet-asset": "دارایی", "wallet-assets": "دارایی ها", "wallet-backup-recovery-description": "این به شما کمک می کند که دارایی خود را امن نگه دارید", diff --git a/translations/ja.json b/translations/ja.json index 755ceee1b0..d4e3185c36 100644 --- a/translations/ja.json +++ b/translations/ja.json @@ -7,7 +7,7 @@ "amount": "金額", "open": "開く", "close-app-title": "警告!", - "wallet-advanced": "高度な", + "advanced": "高度な", "members-active": { "one": "1人がアクティブ", "other": "{{count}}人がアクティブ", diff --git a/translations/ko.json b/translations/ko.json index df9648a562..6498d17dbf 100644 --- a/translations/ko.json +++ b/translations/ko.json @@ -988,7 +988,7 @@ "wallet": "지갑", "wallet-add-asset": "자산 추가", "wallet-address-from-clipboard": "클립보드에서 가져오기", - "wallet-advanced": "고급 설정", + "advanced": "고급 설정", "wallet-asset": "자산", "wallet-assets": "자산", "wallet-backup-recovery-description": "자산을 안전하게 보호하세요", diff --git a/translations/ms.json b/translations/ms.json index 714f4a4ff0..2f8e4a5e19 100644 --- a/translations/ms.json +++ b/translations/ms.json @@ -701,7 +701,7 @@ "wallet": "Dompet", "wallet-add-asset": "Tambah aset", "wallet-address-from-clipboard": "Gunakan Alamat Daripada Papan Klip", - "wallet-advanced": "Lanjutan", + "advanced": "Lanjutan", "wallet-asset": "Aset", "wallet-assets": "Aset", "wallet-backup-recovery-description": "Ini akan membantu anda memastikan aset anda selamat", diff --git a/translations/ne.json b/translations/ne.json index cb04497f69..88ddee18ea 100644 --- a/translations/ne.json +++ b/translations/ne.json @@ -672,7 +672,7 @@ "wallet": "वालेट", "wallet-add-asset": "सम्पत्ति थप्नुहोस्", "wallet-address-from-clipboard": "Gunakan Alamat Daripada Papan Klip", - "wallet-advanced": "Lanjutan", + "advanced": "Lanjutan", "wallet-asset": "Aset", "wallet-assets": "Aset", "wallet-backup-recovery-description": "यसले तपाईंको सम्पत्ती सुरक्षित राख्न तपाईंलाई सहयोग गर्नेछ", diff --git a/translations/pl.json b/translations/pl.json index dd77d755e1..461e1394fd 100644 --- a/translations/pl.json +++ b/translations/pl.json @@ -631,7 +631,7 @@ "wallet": "Portfel", "wallet-add-asset": "Dodaj aktywo", "wallet-address-from-clipboard": "Użyj adresu ze schowka", - "wallet-advanced": "Zaawansowane", + "advanced": "Zaawansowane", "wallet-asset": "Aktywo", "wallet-assets": "Aktywa", "wallet-backup-recovery-description": "Pomoże Ci to zachować bezpieczeństwo Twoich zasobów", diff --git a/translations/ru.json b/translations/ru.json index c07f82edd9..348a7cc304 100644 --- a/translations/ru.json +++ b/translations/ru.json @@ -669,7 +669,7 @@ "wallet": "Кошелек", "wallet-add-asset": "Добавить актив", "wallet-address-from-clipboard": "Использовать Адрес Из Буфера Обмена", - "wallet-advanced": "Дополнительные параметры", + "advanced": "Дополнительные параметры", "wallet-asset": "Актив", "wallet-assets": "Активы", "wallet-backup-recovery-description": "Это поможет сохранить Ваши активы в безопасности", diff --git a/translations/zh_Hans_CN.json b/translations/zh_Hans_CN.json index 3c82505429..2609d3e83f 100644 --- a/translations/zh_Hans_CN.json +++ b/translations/zh_Hans_CN.json @@ -697,7 +697,7 @@ "wallet": "钱包", "wallet-add-asset": "添加资产", "wallet-address-from-clipboard": "使用剪贴板中的地址", - "wallet-advanced": "高级", + "advanced": "高级", "wallet-asset": "资产", "wallet-assets": "资产", "wallet-backup-recovery-description": "这有助于您保护资产安全",