Onboarding sign-in

Signed-off-by: Vitaliy Vlasov <siphiuel@gmail.com>
This commit is contained in:
Vitaliy Vlasov 2019-07-09 14:37:18 +03:00
parent b78bb456a4
commit 433f840f76
No known key found for this signature in database
GPG Key ID: A7D57C347F2B2964
19 changed files with 189 additions and 130 deletions

View File

@ -3,4 +3,4 @@
(def figwheel-urls {:android "ws://localhost:3449/figwheel-ws",
:ios "ws://192.168.0.9:3449/figwheel-ws",
:desktop "ws://localhost:3449/figwheel-ws"}
)
)

View File

@ -141,17 +141,19 @@
(fx/defn intro-wizard
{:events [:accounts.create.ui/intro-wizard]}
[{:keys [db] :as cofx}]
[{:keys [db] :as cofx} first-time-setup?]
(fx/merge {:db (assoc db :intro-wizard {:step :generate-key
:weak-password? true
:encrypt-with-password? true}
:encrypt-with-password? true
:first-time-setup? first-time-setup?}
: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])]
(let [step (get-in db [:intro-wizard :step])
first-time-setup? (get-in db [:intro-wizard :first-time-setup?])]
(if (not= :generate-key step)
(fx/merge {:db (cond-> (assoc-in db [:intro-wizard :step] (dec-step step))
(#{:create-code :confirm-code} step)
@ -161,7 +163,7 @@
(navigation/navigate-to-cofx :intro-wizard nil))
(fx/merge {:db (dissoc db :intro-wizard)}
(navigation/navigate-to-clean :intro nil)))))
(navigation/navigate-to-clean (if first-time-setup? :intro :accounts) nil)))))
(fx/defn exit-wizard [{:keys [db] :as cofx}]
(fx/merge {:db (dissoc db :intro-wizard)}
@ -196,26 +198,31 @@
(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)
(let [step (get-in db [:intro-wizard :step])
first-time-setup? (get-in db [:intro-wizard :first-time-setup?])]
(cond (confirm-failure? db)
(on-confirm-failure cofx)
(or (= step :enable-notifications)
(and (not first-time-setup?) (= step :confirm-code)
(:accounts/login db)))
(fx/merge cofx
(when-not skip? {:notifications/request-notifications-permissions nil})
(when (and (= step :enable-notifications) (not skip?))
{:notifications/request-notifications-permissions nil})
exit-wizard)
(= 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))))))
(and (= step :confirm-code)
(not (:accounts/login db)))
(create-account cofx)
:else {:db (assoc-in db [:intro-wizard :step]
(inc-step step))})))
(fx/defn on-account-created
[{:keys [signing-phrase

View File

@ -301,11 +301,12 @@
:keychain/get-user-password [address
#(re-frame/dispatch [:accounts.login.callback/get-user-password-success % address])]})
(fx/defn open-login [{:keys [db] :as cofx} address photo-path name]
(fx/defn open-login [{:keys [db] :as cofx} address photo-path name public-key]
(let [keycard-account? (get-in db [:accounts/accounts address :keycard-instance-uid])]
(fx/merge cofx
{:db (-> db
(update :accounts/login assoc
:public-key public-key
:address address
:photo-path photo-path
:name name)

View File

@ -341,8 +341,8 @@
(handlers/register-handler-fx
:accounts.login.ui/account-selected
(fn [cofx [_ address photo-path name]]
(accounts.login/open-login cofx address photo-path name)))
(fn [cofx [_ address photo-path name public-key]]
(accounts.login/open-login cofx address photo-path name public-key)))
(handlers/register-handler-fx
:accounts.login.callback/get-user-password-success

View File

@ -388,7 +388,7 @@
(fx/merge cofx
{:db (assoc-in db [:hardwallet :flow] :create)}
(navigation/navigate-to-cofx :hardwallet-authentication-method nil))
(accounts.create/navigate-to-create-account-screen cofx)))
(accounts.create/intro-wizard cofx false)))
(fx/defn navigate-to-recover-method
[{:keys [db] :as cofx}]

View File

@ -151,8 +151,8 @@
(: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)))))
{:keys [address public-key photo-path name]} (first (selection-fn (vals accounts)))]
(accounts.login/open-login cofx address photo-path name public-key)))))
(fx/defn load-accounts-and-initialize-views
"DB has been decrypted, load accounts and initialize-view"

View File

@ -26,5 +26,9 @@
(defn error [label?]
{:bottom-value (if label? 20 0)
:color colors/red-light
:container-style {:shadow-offset {:width 0 :height 1}
:shadow-radius 6
:shadow-opacity 1
:shadow-color colors/gray
:elevation 2}
:font-size 12})

View File

@ -15,15 +15,25 @@
:align-items :center
:justify-content :center})
(def sign-you-in
(def processing
{:margin-top 16
:font-size 13})
:color colors/gray})
(def bottom-button
{:padding-horizontal 24
:justify-content :center
:align-items :center
:align-self :center
:flex-direction :row})
(def bottom-button-container
{:flex-direction :row
:margin-horizontal 12
:margin-vertical 15
:align-items :center})
{:flex-direction :row
:padding-horizontal 12
:padding-vertical 8
:border-top-width 1
:border-top-color colors/gray-lighter
:justify-content :space-between
:align-items :center})
(def login-badge
{:align-items :center})
@ -31,18 +41,21 @@
(def login-badge-image-size 56)
(def login-badge-name
{:margin-top 8})
{:margin-top 10
:text-align :center
:font-weight "500"})
(def login-badge-pubkey
{:margin-top 4
:text-align :center
:color colors/gray
:font-family "monospace"})
(def password-container
{:margin-top 24
:android {:margin-top 11
:padding-top 13}})
(def save-password-checkbox-container
{:margin-top 0
:flex-direction :row
:align-items :center})
(def save-password-unavailable
{:margin-top 8
:width "100%"

View File

@ -2,14 +2,16 @@
(:require-macros [status-im.utils.views :refer [defview letsubs]])
(:require [clojure.string :as string]
[status-im.ui.screens.accounts.styles :as ast]
[status-im.ui.screens.profile.components.views :as profile.components]
[status-im.ui.components.colors :as colors]
[status-im.ui.components.text-input.view :as text-input]
[status-im.ui.components.status-bar.view :as status-bar]
[status-im.ui.components.toolbar.view :as toolbar]
[status-im.ui.components.checkbox.view :as checkbox]
[status-im.ui.components.toolbar.actions :as act]
[status-im.ui.screens.accounts.login.styles :as styles]
[status-im.ui.components.react :as react]
[status-im.i18n :as i18n]
[status-im.utils.utils :as utils]
[status-im.ui.components.react :as components]
[status-im.ui.components.common.common :as components.common]
[status-im.ui.screens.chat.photos :as photos]
@ -19,10 +21,11 @@
(defn login-toolbar [can-navigate-back?]
[toolbar/toolbar
nil
{:style {:border-bottom-width 0
:margin-top 0}}
(when can-navigate-back?
[toolbar/nav-button act/default-back])
[toolbar/content-title (i18n/label :t/sign-in-to-status)]])
[toolbar/nav-button (act/back #(re-frame/dispatch [:navigate-to-clean :accounts]))])
nil])
(defn login-account [password-text-input]
(.blur password-text-input)
@ -43,16 +46,19 @@
:else
:t/unknown-status-go-error))
(defn account-login-badge [photo-path name]
(defn account-login-badge [photo-path name public-key]
[react/view styles/login-badge
[photos/photo photo-path {:size styles/login-badge-image-size}]
[react/view
[react/text {:style styles/login-badge-name
:numberOfLines 1}
name]]])
[react/text {:style styles/login-badge-name
:ellipsize-mode :middle
:numberOfLines 1}
name]
[react/text {:style styles/login-badge-pubkey}
(utils/get-shortened-address public-key)]]])
(defview login []
(letsubs [{:keys [photo-path name error processing save-password? can-save-password?]} [:accounts/login]
(letsubs [{:keys [photo-path name public-key error processing save-password? can-save-password?] :as account} [:accounts/login]
can-navigate-back? [:can-navigate-back?]
password-text-input (atom nil)
sign-in-enabled? [:sign-in-enabled?]
@ -62,51 +68,51 @@
[login-toolbar can-navigate-back?]
[react/scroll-view styles/login-view
[react/view styles/login-badge-container
[account-login-badge photo-path name]
[account-login-badge photo-path name public-key]
[react/view {:style styles/password-container
:important-for-accessibility :no-hide-descendants}
[text-input/text-input-with-label
{:label (i18n/label :t/password)
:placeholder (i18n/label :t/password)
:ref #(reset! password-text-input %)
:auto-focus (= view-id :login)
:on-submit-editing (when sign-in-enabled?
#(login-account @password-text-input))
:on-change-text #(do
(re-frame/dispatch [:set-in [:accounts/login :password]
(security/mask-data %)])
(re-frame/dispatch [:set-in [:accounts/login :error] ""]))
{:placeholder (i18n/label :t/enter-your-password)
:ref #(reset! password-text-input %)
:auto-focus (= view-id :login)
:accessibility-label :password-input
:on-submit-editing (when sign-in-enabled?
#(login-account @password-text-input))
:on-change-text #(do
(re-frame/dispatch [:set-in [:accounts/login :password]
(security/mask-data %)])
(re-frame/dispatch [:set-in [:accounts/login :error] ""]))
:secure-text-entry true
:error (when (not-empty error) (i18n/label (error-key error)))}]]
(when-not platform/desktop?
;; saving passwords is unavailable on Desktop
[react/view {:style styles/save-password-checkbox-container}
(if (and platform/android? (not can-save-password?))
;; on Android, there is much more reasons for the password save to be unavailable,
;; so we don't show the checkbox whatsoever but put a label explaining why it happenned.
[react/i18n-text {:style styles/save-password-unavailable-android
:key :save-password-unavailable-android}]
[profile.components/settings-switch-item
{:label-kw (if can-save-password?
:t/save-password
:t/save-password-unavailable)
:active? can-save-password?
:value save-password?
:action-fn #(re-frame/dispatch [:set-in [:accounts/login :save-password?] %])}])])]]
(if (and platform/android? (not can-save-password?))
;; on Android, there is much more reasons for the password save to be unavailable,
;; so we don't show the checkbox whatsoever but put a label explaining why it happenned.
[react/i18n-text {:style styles/save-password-unavailable-android
:key :save-password-unavailable-android}]
[react/view {:style {:flex-direction :row
:align-items :center
:justify-content :flex-start}}
[checkbox/checkbox {:checked? save-password?
:style {:padding-left 0 :padding-right 10}
:on-value-change #(re-frame/dispatch [:set-in [:accounts/login :save-password?] %])}]
[react/text (i18n/label :t/save-password)]]))]]
(when processing
[react/view styles/processing-view
[components/activity-indicator {:animating true}]
[react/i18n-text {:style styles/sign-you-in :key :sign-you-in}]])
(when-not processing
[react/view {:style styles/bottom-button-container}
(when-not can-navigate-back?
[components.common/bottom-button
{:label (i18n/label :t/other-accounts)
:on-press #(re-frame/dispatch [:navigate-to-clean :accounts])}])
[react/view {:style {:flex 1}}]
[components.common/bottom-button
{:forward? true
:label (i18n/label :t/sign-in)
:disabled? (not sign-in-enabled?)
:on-press #(login-account @password-text-input)}]])]))
[react/i18n-text {:style styles/processing :key :processing}]])
[react/view {:style styles/bottom-button-container}
[components.common/button
{:label (i18n/label :t/access-key)
:button-style styles/bottom-button
:background? false
:on-press #(re-frame/dispatch [:accounts.recover.ui/recover-account-button-pressed])}]
[components.common/button
{:label (i18n/label :t/submit)
:button-style styles/bottom-button
:label-style {:color (if (or (not sign-in-enabled?) processing) colors/gray colors/blue)}
:background? true
:disabled? (or (not sign-in-enabled?) processing)
:on-press #(login-account @password-text-input)}]]]))

View File

@ -8,8 +8,9 @@
(def accounts-container
{:flex 1
:padding-horizontal 16
:background-color colors/gray-lighter})
:margin-top 24
:margin-bottom 16
:justify-content :space-between})
(def bottom-actions-container
{:margin-bottom 16})
@ -21,17 +22,15 @@
:font-size 17})
(defstyle accounts-list-container
{:flex 1
:margin-top 16
:margin-bottom 16})
{:flex 1
:padding-bottom 8})
(defstyle account-view
{:background-color :white
:flex-direction :row
:align-items :center
:padding-horizontal 16
:height 64
:border-radius 8})
:height 64})
(def account-badge-text-view
{:margin-left 16
@ -39,12 +38,18 @@
:flex-shrink 1})
(def account-badge-text
{:font-size 17})
{:font-weight "500"})
(def account-badge-pub-key-text
{:font-size 14
:color colors/gray
:margin-top 4})
{:font-family "monospace"
:color colors/gray})
(def bottom-button
{:padding-horizontal 24
:justify-content :center
:align-items :center
:align-self :center
:flex-direction :row})
(def bottom-button-container
{:margin-top 14

View File

@ -4,6 +4,7 @@
[re-frame.core :as re-frame]
[status-im.ui.screens.chat.photos :as photos]
[status-im.ui.screens.accounts.styles :as styles]
[status-im.utils.utils :as utils]
[status-im.ui.components.list.views :as list]
[status-im.ui.components.status-bar.view :as status-bar]
[status-im.ui.components.react :as react]
@ -15,41 +16,39 @@
[status-im.ui.screens.privacy-policy.views :as privacy-policy]))
(defn account-view [{:keys [address photo-path name public-key keycard-instance-uid]}]
[react/touchable-highlight {:on-press #(re-frame/dispatch [:accounts.login.ui/account-selected address photo-path name])}
[react/touchable-highlight {:on-press #(re-frame/dispatch [:accounts.login.ui/account-selected address photo-path name public-key])}
[react/view styles/account-view
[photos/photo photo-path {:size styles/account-image-size}]
[react/view styles/account-badge-text-view
[react/view {:flex-direction :row}
[react/text {:style styles/account-badge-text
:numberOfLines 1}
[react/text {:style styles/account-badge-text
:ellipsize-mode :middle
:numberOfLines 1}
name]
(when keycard-instance-uid
[icons/icon :main-icons/keycard {:color colors/blue
:container-style {:margin-left 7}}])]
[react/text {:style styles/account-badge-pub-key-text
:ellipsize-mode :middle
:numberOfLines 1}
public-key]]
[react/text {:style styles/account-badge-pub-key-text}
(utils/get-shortened-address public-key)]]
[react/view {:flex 1}]
[icons/icon :main-icons/next {:color (colors/alpha colors/black 0.4)}]]])
[icons/icon :main-icons/next {:color (colors/alpha colors/gray 0.4)}]]])
(defview accounts []
(letsubs [accounts [:accounts/accounts]]
[react/view styles/accounts-view
[status-bar/status-bar]
[toolbar/toolbar nil nil
[toolbar/content-title (i18n/label :t/sign-in-to-status)]]
[react/text {:style {:typography :header :margin-top 24 :text-align :center}}
(i18n/label :t/unlock)]
[react/view styles/accounts-container
[react/view styles/accounts-list-container
[list/flat-list {:data (vals accounts)
:key-fn :address
:render-fn (fn [account] [account-view account])
:separator [react/view {:height 12}]}]]
:render-fn (fn [account] [account-view account])}]]
[react/view
[components.common/button {:on-press #(re-frame/dispatch [:accounts.create.ui/create-new-account-button-pressed])
:label (i18n/label :t/create-new-account)}]
:button-style styles/bottom-button
:label (i18n/label :t/generate-a-new-key)}]
[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/add-existing-account)
:background? false}]]
[privacy-policy/privacy-policy-button]]]]))
:label (i18n/label :t/access-key)
:background? false}]]]]]))

View File

@ -21,7 +21,7 @@
;; initial state of app-db
(def app-db {:keyboard-height 0
:tab-bar-visible? true
:navigation-stack '()
:navigation-stack '(:accounts)
:contacts/contacts {}
:pairing/installations {}
:contact-recovery/pop-up #{}

View File

@ -4,6 +4,7 @@
[re-frame.core :as re-frame]
[status-im.react-native.resources :as resources]
[status-im.privacy-policy.core :as privacy-policy]
[status-im.utils.utils :as utils]
[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]
@ -76,7 +77,7 @@
: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])
:on-press #(re-frame/dispatch [:accounts.create.ui/intro-wizard true])
: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])
@ -121,10 +122,8 @@
(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)]]
:font-family "monospace")}
(utils/get-shortened-address (:pubkey acc))]]
[radio/radio selected?]]]))])
(defn storage-entry [{:keys [type icon title desc]} selected-storage-type]

View File

@ -72,6 +72,13 @@
:onPress on-accept
:accessibility-label :yes-button})))))
(defn get-shortened-address
"Takes first and last 4 digits from address including leading 0x
and adds unicode ellipsis in between"
[address]
(when address
(str (subs address 0 6) "\u2026" (subs address (- (count address) 4) (count address)))))
;; background-timer
(defn set-timeout [cb ms]

View File

@ -40,19 +40,16 @@ class TestCreateAccount(SingleDeviceTestCase):
profile.logout()
if sign_in.ok_button.is_element_displayed():
sign_in.ok_button.click()
sign_in.other_accounts_button.click()
sign_in.create_account_button.click()
sign_in.password_input.set_value(common_password)
sign_in.back_button.click()
sign_in.generate_new_key_button.click()
sign_in.generate_key_button.click()
sign_in.next_button.click()
sign_in.confirm_password_input.set_value(common_password)
sign_in.next_button.click()
sign_in.create_password_input.set_value(common_password)
sign_in.next_button.click()
sign_in.confirm_your_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')

View File

@ -230,7 +230,7 @@ class SendTransactionView(BaseView):
def sign_transaction(self, sender_password: str = common_password):
self.sign_with_password.click()
self.enter_password_input.send_keys(sender_password)
self.sign_button.click()
self.sign_button.click_until_presence_of_element(self.ok_button)
self.ok_button.click()
def get_transaction_fee_total(self):

View File

@ -14,6 +14,12 @@ class AccountButton(BaseButton):
class PasswordInput(BaseEditBox):
def __init__(self, driver):
super(PasswordInput, self).__init__(driver)
self.locator = self.Locator.accessibility_id("password-input")
class RecoverAccountPasswordInput(BaseEditBox):
def __init__(self, driver):
super(RecoverAccountPasswordInput, self).__init__(driver)
self.locator = self.Locator.xpath_selector("//android.widget.TextView[@text='Password']"
"/following-sibling::android.view.ViewGroup/android.widget.EditText")
@ -36,7 +42,7 @@ class SignInButton(BaseButton):
def __init__(self, driver):
super(SignInButton, self).__init__(driver)
self.locator = self.Locator.xpath_selector("//android.widget.TextView[@text='Sign in' or @text='SIGN IN']")
self.locator = self.Locator.xpath_selector("//android.widget.TextView[@text='Sign in' or @text='Submit']")
def navigate(self):
from views.home_view import HomeView
@ -66,6 +72,12 @@ class GenerateKeyButton(BaseButton):
self.locator = self.Locator.xpath_selector("//*[@text='Generate a key']")
class GenerateNewKeyButton(BaseButton):
def __init__(self, driver):
super(GenerateNewKeyButton, self).__init__(driver)
self.locator = self.Locator.xpath_selector("//*[@text='Generate a new key']")
class IHaveAccountButton(RecoverAccessButton):
def __init__(self, driver):
super(IHaveAccountButton, self).__init__(driver)
@ -130,6 +142,8 @@ class SignInView(BaseView):
self.account_button = AccountButton(self.driver)
self.password_input = PasswordInput(self.driver)
self.recover_account_password_input = RecoverAccountPasswordInput(self.driver)
self.sign_in_button = SignInButton(self.driver)
self.recover_access_button = RecoverAccessButton(self.driver)
@ -138,6 +152,7 @@ class SignInView(BaseView):
self.i_have_account_button = IHaveAccountButton(self.driver)
self.access_key_button = AccessKeyButton(self.driver)
self.generate_key_button = GenerateKeyButton(self.driver)
self.generate_new_key_button = GenerateNewKeyButton(self.driver)
self.add_existing_account_button = AddExistingAccountButton(self.driver)
self.confirm_password_input = ConfirmPasswordInput(self.driver)
self.create_password_input = CreatePasswordInput(self.driver)
@ -175,8 +190,8 @@ class SignInView(BaseView):
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()
recover_access_view.password_input.set_value(password)
recover_access_view.recover_account_password_input.click()
recover_access_view.recover_account_password_input.set_value(password)
recover_access_view.sign_in_button.click_until_presence_of_element(recover_access_view.home_button)
return self.get_home_view()

View File

@ -33,7 +33,7 @@
:account/account {:public-key nil}
:push-notifications/stored {"0x04d2e59a7501a7bc5bc8bf973a0ab95d06225e2b0f53d5f6be719d857c579bdc1b809bfbe0e8393343f9a5b63a9a90a1a58329c6d1c286d6929f01aaa5472ca9c2"
"0x045db1fdb16c4721ddf32e892c5156d9c7a7445482b84ccd41131eb7970f9d623629d86763c5c2a542ae372815125c27eb73535d583d3285bdbfa16ba37f42e2de"}}
:dispatch [:ui/open-login "bd36cd64e2621b054a3b7464ff1b3c4c304880e7" "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAMAAAC7IEhfAAADAFBMVEX" "Bob"]}
:dispatch [:ui/open-login "bd36cd64e2621b054a3b7464ff1b3c4c304880e7" "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAMAAAC7IEhfAAADAFBMVEX" "Bob" nil]}
(notifications/handle-push-notification-open {:db {:accounts/accounts {"bd36cd64e2621b054a3b7464ff1b3c4c304880e7" {:address "bd36cd64e2621b054a3b7464ff1b3c4c304880e7"
:photo-path "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAMAAAC7IEhfAAADAFBMVEX"
:name "Bob"

View File

@ -175,6 +175,8 @@
"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",
"generate-a-new-key": "Generate a new key",
"enter-your-password": "Enter your password...",
"generating-keys": "Generating keys...",
"you-will-need-this-code": "You'll need this code to open Status and sign transactions",
"this-device": "This device",
@ -186,6 +188,7 @@
"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",
"unlock": "Unlock",
"not-implemented": "!not implemented",
"new-contact": "New contact",
"datetime-second": {
@ -290,7 +293,10 @@
"offline-messaging": "Mailserver",
"currency-display-name-nad": "Namibia Dollar",
"creating-your-account": "Creating your account...",
"save-password": "Save password until logout",
"save-password": "Save password",
"submit": "Submit",
"recover-key": "Recover key",
"processing": "Processing...",
"currency-display-name-kes": "Kenyan Shilling",
"view-etheremon": "View in Etheremon",
"wallet-transaction-fee-details": "Gas limit caps the units of gas spent on the transaction. Gas price sets the price per unit of gas. Increasing gas price can make your transaction faster.",