From 946a44d257bb3c2a76e128cf519e7c83b245b84c Mon Sep 17 00:00:00 2001 From: Andrey Shovkoplyas Date: Fri, 16 Mar 2018 19:55:12 +0300 Subject: [PATCH] implemented profile onboarding Signed-off-by: Andrey Shovkoplyas --- resources/icons/down.svg | 2 +- resources/icons/up.svg | 2 +- resources/images/ui/lock.png | Bin 0 -> 83947 bytes src/status_im/chat/screen.cljs | 5 +- src/status_im/chat/subs.cljs | 19 +- src/status_im/chat/views/toolbar_content.cljs | 41 +- .../data_store/realm/schemas/base/core.cljs | 8 +- .../realm/schemas/base/v8/account.cljs | 29 + .../realm/schemas/base/v8/core.cljs | 10 + src/status_im/react_native/resources.cljs | 3 +- src/status_im/translations/en.cljs | 812 +++++++++--------- src/status_im/ui/components/colors.cljs | 1 + .../ui/components/common/common.cljs | 13 + .../ui/components/common/styles.cljs | 25 + src/status_im/ui/components/react.cljs | 1 + .../ui/components/text_input/styles.cljs | 4 +- .../ui/components/text_input/view.cljs | 7 +- .../ui/components/toolbar/styles.cljs | 5 + src/status_im/ui/components/toolbar/view.cljs | 14 +- src/status_im/ui/screens/accounts/db.cljs | 6 +- src/status_im/ui/screens/accounts/events.cljs | 25 +- .../ui/screens/accounts/recover/events.cljs | 1 + src/status_im/ui/screens/browser/styles.cljs | 6 +- src/status_im/ui/screens/browser/views.cljs | 60 +- src/status_im/ui/screens/db.cljs | 3 +- .../ui/screens/home/views/inner_item.cljs | 11 +- .../ui/screens/main_tabs/styles.cljs | 9 +- src/status_im/ui/screens/main_tabs/views.cljs | 37 +- .../ui/screens/profile/components/views.cljs | 20 +- src/status_im/ui/screens/profile/db.cljs | 16 +- src/status_im/ui/screens/profile/events.cljs | 32 +- .../ui/screens/profile/navigation.cljs | 3 + .../ui/screens/profile/seed/styles.cljs | 154 ++++ .../ui/screens/profile/seed/views.cljs | 146 ++++ src/status_im/ui/screens/profile/subs.cljs | 9 + .../ui/screens/profile/user/styles.cljs | 24 + .../ui/screens/profile/user/views.cljs | 64 +- src/status_im/ui/screens/subs.cljs | 3 +- src/status_im/ui/screens/views.cljs | 2 + src/status_im/ui/screens/wallet/subs.cljs | 4 + 40 files changed, 1096 insertions(+), 540 deletions(-) create mode 100644 resources/images/ui/lock.png create mode 100644 src/status_im/data_store/realm/schemas/base/v8/account.cljs create mode 100644 src/status_im/data_store/realm/schemas/base/v8/core.cljs create mode 100644 src/status_im/ui/screens/profile/seed/styles.cljs create mode 100644 src/status_im/ui/screens/profile/seed/views.cljs create mode 100644 src/status_im/ui/screens/profile/subs.cljs diff --git a/resources/icons/down.svg b/resources/icons/down.svg index fb0ed31ea1..9ad7430a6d 100644 --- a/resources/icons/down.svg +++ b/resources/icons/down.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/resources/icons/up.svg b/resources/icons/up.svg index bc16ee31da..90b2386d7c 100644 --- a/resources/icons/up.svg +++ b/resources/icons/up.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/resources/images/ui/lock.png b/resources/images/ui/lock.png new file mode 100644 index 0000000000000000000000000000000000000000..5972b6f3593092f39d224e738dec4c6e1956be6b GIT binary patch literal 83947 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�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 X?X7%}UNLgS0e)1JG!#qaE#Ce=r3i8B literal 0 HcmV?d00001 diff --git a/src/status_im/chat/screen.cljs b/src/status_im/chat/screen.cljs index 38ccc93961..4fdab9cb4d 100644 --- a/src/status_im/chat/screen.cljs +++ b/src/status_im/chat/screen.cljs @@ -50,12 +50,11 @@ :options (actions/actions group-chat? chat-id public?)})) (defview chat-toolbar [public?] - (letsubs [accounts [:get-accounts] - {:keys [group-chat name chat-id]} [:get-current-chat]] + (letsubs [{:keys [group-chat name chat-id]} [:get-current-chat]] [react/view [status-bar/status-bar] [toolbar/platform-agnostic-toolbar {} - toolbar/default-nav-back + toolbar/nav-back-count [toolbar-content/toolbar-content-view] [toolbar/actions [{:icon :icons/options :icon-opts {:color :black} diff --git a/src/status_im/chat/subs.cljs b/src/status_im/chat/subs.cljs index 7a8c6e1825..b68d8e38c8 100644 --- a/src/status_im/chat/subs.cljs +++ b/src/status_im/chat/subs.cljs @@ -52,15 +52,18 @@ platform/ios? kb-height :default 0))) -(defn active-chats [[_ chat]] - ;;TODO (andrey) console should be shown in dev mode only, will be done soon - (and (:is-active chat))) ;(not= const/console-chat-id (:chat-id chat)))) +(defn active-chats [dev-mode?] + (fn [[_ chat]] + (and (:is-active chat) + (or dev-mode? + (not= const/console-chat-id (:chat-id chat)))))) (reg-sub :get-active-chats :<- [:get-chats] - (fn [chats] - (into {} (filter active-chats chats)))) + :<- [:get-current-account] + (fn [[chats {:keys [dev-mode?]}]] + (into {} (filter (active-chats dev-mode?) chats)))) (reg-sub :get-chat @@ -324,3 +327,9 @@ (fn [db [_ key type]] (let [chat-id (subscribe [:get-current-chat-id])] (get-in db [:chat-animations @chat-id key type])))) + +(reg-sub + :get-chats-unread-messages-number + :<- [:get-active-chats] + (fn [chats _] + (apply + (map #(count (:unviewed-messages %)) (vals chats))))) \ No newline at end of file diff --git a/src/status_im/chat/views/toolbar_content.cljs b/src/status_im/chat/views/toolbar_content.cljs index a501becaf8..2bdc468153 100644 --- a/src/status_im/chat/views/toolbar_content.cljs +++ b/src/status_im/chat/views/toolbar_content.cljs @@ -9,7 +9,9 @@ [status-im.utils.datetime :as time] [status-im.utils.platform :refer [platform-specific]] [status-im.utils.gfycat.core :refer [generate-gfy]] - [status-im.constants :refer [console-chat-id]])) + [status-im.constants :refer [console-chat-id]] + [status-im.ui.components.common.common :as components.common] + [status-im.ui.components.styles :as common.styles])) (defn- online-text [contact chat-id] (cond @@ -67,21 +69,22 @@ accounts [:get-accounts] contact [:get-in [:contacts/contacts @chat-id]] sync-state [:sync-state]] - [react/view (st/chat-name-view (or (empty? accounts) - show-actions?)) - (let [chat-name (if (string/blank? name) - (generate-gfy public-key) - (or (i18n/get-contact-translated chat-id :name name) - (i18n/label :t/chat-name)))] - [react/text {:style st/chat-name-text - :number-of-lines 1 - :font :toolbar-title} - (if public? - (str "#" chat-name) - chat-name)]) - (if group-chat - [group-last-activity {:contacts contacts - :public? public? - :sync-state sync-state}] - [last-activity {:online-text (online-text contact chat-id) - :sync-state sync-state}])])) + [react/view common.styles/flex + [react/view (st/chat-name-view (or (empty? accounts) + show-actions?)) + (let [chat-name (if (string/blank? name) + (generate-gfy public-key) + (or (i18n/get-contact-translated chat-id :name name) + (i18n/label :t/chat-name)))] + [react/text {:style st/chat-name-text + :number-of-lines 1 + :font :toolbar-title} + (if public? + (str "#" chat-name) + chat-name)]) + (if group-chat + [group-last-activity {:contacts contacts + :public? public? + :sync-state sync-state}] + [last-activity {:online-text (online-text contact chat-id) + :sync-state sync-state}])]])) diff --git a/src/status_im/data_store/realm/schemas/base/core.cljs b/src/status_im/data_store/realm/schemas/base/core.cljs index f140d9021a..6ba8ede945 100644 --- a/src/status_im/data_store/realm/schemas/base/core.cljs +++ b/src/status_im/data_store/realm/schemas/base/core.cljs @@ -5,7 +5,8 @@ [status-im.data-store.realm.schemas.base.v4.core :as v4] [status-im.data-store.realm.schemas.base.v5.core :as v5] [status-im.data-store.realm.schemas.base.v6.core :as v6] - [status-im.data-store.realm.schemas.base.v7.core :as v7])) + [status-im.data-store.realm.schemas.base.v7.core :as v7] + [status-im.data-store.realm.schemas.base.v8.core :as v8])) ;; put schemas ordered by version (def schemas [{:schema v1/schema @@ -28,4 +29,7 @@ :migration v6/migration} {:schema v7/schema :schemaVersion 7 - :migration v7/migration}]) + :migration v7/migration} + {:schema v8/schema + :schemaVersion 8 + :migration v8/migration}]) diff --git a/src/status_im/data_store/realm/schemas/base/v8/account.cljs b/src/status_im/data_store/realm/schemas/base/v8/account.cljs new file mode 100644 index 0000000000..07ca6bfa5f --- /dev/null +++ b/src/status_im/data_store/realm/schemas/base/v8/account.cljs @@ -0,0 +1,29 @@ +(ns status-im.data-store.realm.schemas.base.v8.account) + +(def schema {:name :account + :primaryKey :address + :properties {:address :string + :public-key :string + :updates-public-key {:type :string + :optional true} + :updates-private-key {:type :string + :optional true} + :name {:type :string :optional true} + :email {:type :string :optional true} + :status {:type :string :optional true} + :debug? {:type :bool :default false} + :photo-path :string + :signing-phrase {:type :string} + :mnemonic {:type :string} + :last-updated {:type :int :default 0} + :last-sign-in {:type :int :default 0} + :signed-up? {:type :bool + :default false} + :network :string + :networks {:type :list + :objectType :network} + :wnode :string + :settings {:type :string} + :sharing-usage-data? {:type :bool :default false} + :dev-mode? {:type :bool :default false} + :seed-backed-up? {:type :bool :default false}}}) \ No newline at end of file diff --git a/src/status_im/data_store/realm/schemas/base/v8/core.cljs b/src/status_im/data_store/realm/schemas/base/v8/core.cljs new file mode 100644 index 0000000000..63e2ba42b1 --- /dev/null +++ b/src/status_im/data_store/realm/schemas/base/v8/core.cljs @@ -0,0 +1,10 @@ +(ns status-im.data-store.realm.schemas.base.v8.core + (:require [status-im.data-store.realm.schemas.base.v4.network :as network] + [status-im.data-store.realm.schemas.base.v8.account :as account] + [taoensso.timbre :as log])) + +(def schema [network/schema + account/schema]) + +(defn migration [old-realm new-realm] + (log/debug "migrating v8 base database: " old-realm new-realm)) diff --git a/src/status_im/react_native/resources.cljs b/src/status_im/react_native/resources.cljs index 99c146ef20..5d5fbdde0e 100644 --- a/src/status_im/react_native/resources.cljs +++ b/src/status_im/react_native/resources.cljs @@ -24,4 +24,5 @@ {:empty-hashtags (js/require "./resources/images/ui/empty-hashtags.png") :empty-recent (js/require "./resources/images/ui/empty-recent.png") :analytics-image (js/require "./resources/images/ui/analytics-image.png") - :welcome-image (js/require "./resources/images/ui/welcome-image.png")}) \ No newline at end of file + :welcome-image (js/require "./resources/images/ui/welcome-image.png") + :lock (js/require "./resources/images/ui/lock.png")}) \ No newline at end of file diff --git a/src/status_im/translations/en.cljs b/src/status_im/translations/en.cljs index fb9695753c..13d3f70742 100644 --- a/src/status_im/translations/en.cljs +++ b/src/status_im/translations/en.cljs @@ -3,470 +3,488 @@ (def translations { ;;common - :members-title "Members" - :not-implemented "!not implemented" - :chat-name "Chat name" - :notifications-title "Notifications and sounds" - :offline "Offline" - :connection-problem "Messages connection problem" - :search-for "Search for..." - :cancel "Cancel" - :next "Next" - :open "Open" - :description "Description" - :enter-url "Enter URL" - :open-dapp "Open ÐApp" - :url "URL" - :type-a-message "Type a message..." - :type-a-command "Start typing a command..." - :error "Error" - :unknown-status-go-error "Unknown status-go error" - :node-unavailable "No ethereum node running" - :add "Add" - :yes "Yes" - :no "No" + :members-title "Members" + :not-implemented "!not implemented" + :chat-name "Chat name" + :notifications-title "Notifications and sounds" + :offline "Offline" + :connection-problem "Messages connection problem" + :search-for "Search for..." + :cancel "Cancel" + :next "Next" + :open "Open" + :description "Description" + :enter-url "Enter URL" + :open-dapp "Open ÐApp" + :url "URL" + :type-a-message "Type a message..." + :type-a-command "Start typing a command..." + :error "Error" + :unknown-status-go-error "Unknown status-go error" + :node-unavailable "No ethereum node running" + :add "Add" + :yes "Yes" + :no "No" - :camera-access-error "To grant the required camera permission, please go to your system settings and make sure that Status > Camera is selected." - :photos-access-error "To grant the required photos permission, please go to your system settings and make sure that Status > Photos is selected." + :camera-access-error "To grant the required camera permission, please go to your system settings and make sure that Status > Camera is selected." + :photos-access-error "To grant the required photos permission, please go to your system settings and make sure that Status > Photos is selected." ;;sign in - :intro-text "Status is an open source decentralized chat and Ethereum browser" - :intro-text-description "Status is built with the help of the community to help you use all the benefits of decentralized web in your mobile phone" - :create-account "Create account" - :already-have-account "I already have an account" - :creating-your-account "Creating your account on the blockchain. We can't touch it, no one can, except for you!" - :password-placeholder "Type your password" - :password-placeholder2 "Confirm your password" - :name-placeholder "Enter your full name…" - :password_error1 "Password confirmation doesn't match password." - :password-description "You'll need this password to open the app, confirm transactions and whenever you need to regain access on a new device or install." - :name-description "This will be the name everybody who uses Status will see. You can change it later." - :other-accounts "Other accounts" - :sign-you-in "Signing you in…" + :intro-text "Status is an open source decentralized chat and Ethereum browser" + :intro-text-description "Status is built with the help of the community to help you use all the benefits of decentralized web in your mobile phone" + :create-account "Create account" + :already-have-account "I already have an account" + :creating-your-account "Creating your account on the blockchain. We can't touch it, no one can, except for you!" + :password-placeholder "Type your password" + :password-placeholder2 "Confirm your password" + :name-placeholder "Enter your full name…" + :password_error1 "Password confirmation doesn't match password." + :password-description "You'll need this password to open the app, confirm transactions and whenever you need to regain access on a new device or install." + :name-description "This will be the name everybody who uses Status will see. You can change it later." + :other-accounts "Other accounts" + :sign-you-in "Signing you in…" - :help-improve "Help improve Status \n by sharing usage data" - :help-improve-description "We strive to collect only what we need to understand how and where we can improve Status" - :share-usage-data "Share usage data" - :dont-want-to-share "No, i don't want to share" + :help-improve "Help improve Status \n by sharing usage data" + :help-improve-description "We strive to collect only what we need to understand how and where we can improve Status" + :share-usage-data "Share usage data" + :dont-want-to-share "No, i don't want to share" ;;drawer - :switch-users "Switch users" - :logout-title "Log out?" - :logout-are-you-sure "Are you sure you want\nto log out?" - :logout "Log out" - :current-network "Current network" + :switch-users "Switch users" + :logout-title "Log out?" + :logout-are-you-sure "Are you sure you want\nto log out?" + :logout "Log out" + :current-network "Current network" ;;home - :home "Home" - :no-recent-chats "There are no recent Chats or DApps here yet.\nUse the “Plus” button to see the list of Dapps or discover people to chat with" - :welcome-to-status "Welcome to Status" - :welcome-to-status-description "Here you can chat with people in a secure private chat, browse and interact with DApps. Use the “Plus” icon above to explore Status" + :home "Home" + :no-recent-chats "There are no recent Chats or DApps here yet.\nUse the “Plus” button to see the list of Dapps or discover people to chat with" + :welcome-to-status "Welcome to Status" + :welcome-to-status-description "Here you can chat with people in a secure private chat, browse and interact with DApps. Use the “Plus” icon above to explore Status" ;;chat - :is-typing "is typing" - :and-you "and you" - :search-chat "Search chat" - :members {:one "1 member" - :other "{{count}} members" - :zero "no members"} - :members-active {:one "1 member" - :other "{{count}} members" - :zero "no members"} - :public-group-status "Public" - :active-online "Online" - :active-unknown "Unknown" - :available "Available" - :no-messages "No messages" - :suggestions-requests "Requests" - :suggestions-commands "Commands" - :faucet-success "Faucet request has been received" - :faucet-error "Faucet request error" + :is-typing "is typing" + :and-you "and you" + :search-chat "Search chat" + :members {:one "1 member" + :other "{{count}} members" + :zero "no members"} + :members-active {:one "1 member" + :other "{{count}} members" + :zero "no members"} + :public-group-status "Public" + :active-online "Online" + :active-unknown "Unknown" + :available "Available" + :no-messages "No messages" + :suggestions-requests "Requests" + :suggestions-commands "Commands" + :faucet-success "Faucet request has been received" + :faucet-error "Faucet request error" ;;sync - :sync-in-progress "Syncing..." - :sync-synced "In sync" + :sync-in-progress "Syncing..." + :sync-synced "In sync" ;;messages - :status-sending "Sending..." - :status-pending "Pending" - :status-sent "Sent" - :status-seen-by-everyone "Seen by everyone" - :status-seen "Seen" - :status-delivered "Delivered" - :status-failed "Failed" + :status-sending "Sending..." + :status-pending "Pending" + :status-sent "Sent" + :status-seen-by-everyone "Seen by everyone" + :status-seen "Seen" + :status-delivered "Delivered" + :status-failed "Failed" ;;datetime - :datetime-ago-format "{{number}} {{time-intervals}} {{ago}}" - :datetime-second {:one "second" - :other "seconds"} - :datetime-minute {:one "minute" - :other "minutes"} - :datetime-hour {:one "hour" - :other "hours"} - :datetime-day {:one "day" - :other "days"} - :datetime-ago "ago" - :datetime-yesterday "yesterday" - :datetime-today "today" + :datetime-ago-format "{{number}} {{time-intervals}} {{ago}}" + :datetime-second {:one "second" + :other "seconds"} + :datetime-minute {:one "minute" + :other "minutes"} + :datetime-hour {:one "hour" + :other "hours"} + :datetime-day {:one "day" + :other "days"} + :datetime-ago "ago" + :datetime-yesterday "yesterday" + :datetime-today "today" ;;profile - :profile "Profile" - :view-profile "View profile" - :edit-profile "Edit profile" - :main-currency "Main currency" - :message "Message" - :notifications "Notifications" - :not-specified "Not specified" - :public-key "Public key" - :phone-number "Phone number" - :share-contact-code "Share my contact code" - :update-status "Update your status..." - :add-a-status "Add a status..." - :status-prompt "Set your status. Using #hastags will help others discover you and talk about what's on your mind" - :contact-code "Contact code" - :add-to-contacts "Add to contacts" - :in-contacts "In contacts" - :remove-from-contacts "Remove from contacts" - :start-conversation "Start conversation" - :send-message "Send message" - :testnet-text "You’re on the {{testnet}} Testnet. Do not send real ETH or SNT to your address" - :mainnet-text "You’re on the Mainnet. Real ETH will be sent" + :profile "Profile" + :view-profile "View profile" + :edit-profile "Edit profile" + :main-currency "Main currency" + :message "Message" + :notifications "Notifications" + :not-specified "Not specified" + :public-key "Public key" + :phone-number "Phone number" + :share-contact-code "Share my contact code" + :update-status "Update your status..." + :add-a-status "Add a status..." + :status-prompt "Set your status. Using #hastags will help others discover you and talk about what's on your mind" + :contact-code "Contact code" + :add-to-contacts "Add to contacts" + :in-contacts "In contacts" + :remove-from-contacts "Remove from contacts" + :start-conversation "Start conversation" + :send-message "Send message" + :testnet-text "You’re on the {{testnet}} Testnet. Do not send real ETH or SNT to your address" + :mainnet-text "You’re on the Mainnet. Real ETH will be sent" + :dev-mode "Development mode" + :backup-your-seed "Backup your Seed Phrase" + + ;;seed + :your-data-belongs-to-you "Your Data and Funds belong to you. We can’t help get it back if you loose it" + :your-data-belongs-to-you-description "This means Status can’t help you get it back if you lose it. You are in charge of the secuty of all your data" + :ok-continue "Ok, continue" + :your-seed-phrase "Your Seed Phrase" + :your-seed-phrase-description "This is your seed phrase. You use it to prove that this is your wallet. You only get to see it once! Copy it on paper and keep it in a secure place. You need it when you loose or reinstall your wallet." + :enter-word "Enter word" + :check-your-seed "Check your Seed Phrase" + :wrong-word "Wrong word" + :are-you-sure? "Are you sure?" + :are-you-sure-description "You will not be able to see the whole seed phrase again" + :you-are-all-set "You’re all set!" + :you-are-all-set-description "Now if you loose your phone you can restore your account and wallet using the Seed Phrase and password" + :ok-got-it "Ok, got it" + :backup-seed-phrase "Backup Seed Phrase" + :step-i-of-n "Step {{step}} of {{number}}" + :word-n-description "In order to check if have backed up your seed phrase correctly, enter the word #{{number}} above" + :word-n "Word #{{number}}" ;;make_photo - :image-source-title "Edit picture" - :image-source-make-photo "Capture" - :image-source-gallery "Select from gallery" + :image-source-title "Edit picture" + :image-source-make-photo "Capture" + :image-source-gallery "Select from gallery" ;;sharing - :sharing-copy-to-clipboard "Copy to clipboard" - :sharing-share "Share..." - :sharing-cancel "Cancel" + :sharing-copy-to-clipboard "Copy to clipboard" + :sharing-share "Share..." + :sharing-cancel "Cancel" - :browsing-title "Browse" - :browsing-open-in-web-browser "Open in web browser" - :browsing-open-in-browser "Open in browser" - :browsing-cancel "Cancel" + :browsing-title "Browse" + :browsing-open-in-web-browser "Open in web browser" + :browsing-open-in-browser "Open in browser" + :browsing-cancel "Cancel" ;;sign-up - :contacts-syncronized "Your contacts have been synchronized" - :confirmation-code (str "Thanks! We've sent you a text message with a confirmation " - "code. Please provide that code to confirm your phone number") - :incorrect-code (str "Sorry the code was incorrect, please enter it again") - :phew-here-is-your-passphrase "Phew, that was hard. Here is your passphrase, *write this down and keep it safe!* You will need it to recover your account." - :here-is-your-passphrase "Here is your passphrase, *write this down and keep this safe!* You will need it to recover your account." - :here-is-your-signing-phrase "Here is your signing phrase. You will use it to verify your transactions. *Write it down and keep it safe!*" - :phone-number-required "Tap here to validate your phone number & I'll find your friends." - :shake-your-phone "Found a bug or have a suggestion? Just ~shake~ your phone!" - :intro-status "Chat with me to setup your account and change your settings." - :intro-message1 "Welcome to Status!\nTap this message to set your password and get started." - :account-generation-message "Gimmie a sec, I gotta do some crazy math to generate your account!" - :move-to-internal-failure-message "We need to move some important files from external to internal storage. To do this, we need your permission. We won't be using external storage in future versions." - :debug-enabled "Debug server has been launched! You can now execute *status-dev-cli scan* to find the server from your computer on the same network." + :contacts-syncronized "Your contacts have been synchronized" + :confirmation-code (str "Thanks! We've sent you a text message with a confirmation " + "code. Please provide that code to confirm your phone number") + :incorrect-code (str "Sorry the code was incorrect, please enter it again") + :phew-here-is-your-passphrase "Phew, that was hard. Here is your passphrase, *write this down and keep it safe!* You will need it to recover your account." + :here-is-your-passphrase "Here is your passphrase, *write this down and keep this safe!* You will need it to recover your account." + :here-is-your-signing-phrase "Here is your signing phrase. You will use it to verify your transactions. *Write it down and keep it safe!*" + :phone-number-required "Tap here to validate your phone number & I'll find your friends." + :shake-your-phone "Found a bug or have a suggestion? Just ~shake~ your phone!" + :intro-status "Chat with me to setup your account and change your settings." + :intro-message1 "Welcome to Status!\nTap this message to set your password and get started." + :account-generation-message "Gimmie a sec, I gotta do some crazy math to generate your account!" + :move-to-internal-failure-message "We need to move some important files from external to internal storage. To do this, we need your permission. We won't be using external storage in future versions." + :debug-enabled "Debug server has been launched! You can now execute *status-dev-cli scan* to find the server from your computer on the same network." ;;phone types - :phone-e164 "International 1" - :phone-international "International 2" - :phone-national "National" - :phone-significant "Significant" + :phone-e164 "International 1" + :phone-international "International 2" + :phone-national "National" + :phone-significant "Significant" ;;chats - :new "New" - :new-chat "New chat" - :start-new-chat "Start new chat" - :start-group-chat "Start group chat" - :invite-friends "Invite friends" - :get-status-at "Get Status at http://status.im?refCode={{address}}" - :chats "Chats" - :delete-chat "Delete chat" - :group-chat "Group chat" - :group-info "Group info" - :delete-chat-confirmation "Are you sure you want to delete this chat?" - :delete-group-chat-confirmation "Are you sure you want to delete this group chat?" - :new-group-chat "New group chat" - :new-public-group-chat "Join public chat" - :public-chat "Public chat" - :edit-chats "Edit chats" - :search-chats "Search chats" - :empty-topic "Empty topic" - :topic-format "Wrong format [a-z0-9\\-]+" - :public-group-topic "Topic" - :set-a-topic "Set a topic" - :empty-chat-description "There are no messages \nin this chat yet" + :new "New" + :new-chat "New chat" + :start-new-chat "Start new chat" + :start-group-chat "Start group chat" + :invite-friends "Invite friends" + :get-status-at "Get Status at http://status.im?refCode={{address}}" + :chats "Chats" + :delete-chat "Delete chat" + :group-chat "Group chat" + :group-info "Group info" + :delete-chat-confirmation "Are you sure you want to delete this chat?" + :delete-group-chat-confirmation "Are you sure you want to delete this group chat?" + :new-group-chat "New group chat" + :new-public-group-chat "Join public chat" + :public-chat "Public chat" + :edit-chats "Edit chats" + :search-chats "Search chats" + :empty-topic "Empty topic" + :topic-format "Wrong format [a-z0-9\\-]+" + :public-group-topic "Topic" + :set-a-topic "Set a topic" + :empty-chat-description "There are no messages \nin this chat yet" ;;discover - :discover "Discover" - :none "None" - :search-tags "Type your search tags here" - :popular-tags "Popular #hashtags" - :recent "Recent statuses" - :no-statuses-found "No statuses found" - :chat "Chat" - :all "All" - :public-chats "Public chats" - :soon "Soon" - :public-chat-user-count "{{count}} people" - :dapps "ÐApps" - :dapp-profile "ÐApp profile" - :no-statuses-discovered "No statuses discovered" - :no-statuses-discovered-body "When somebody posts\na status you will see it here." - :no-hashtags-discovered-title "No #hashtags discovered" - :no-hashtags-discovered-body "When a #hashtag becomes\npopular you will see it here." + :discover "Discover" + :none "None" + :search-tags "Type your search tags here" + :popular-tags "Popular #hashtags" + :recent "Recent statuses" + :no-statuses-found "No statuses found" + :chat "Chat" + :all "All" + :public-chats "Public chats" + :soon "Soon" + :public-chat-user-count "{{count}} people" + :dapps "ÐApps" + :dapp-profile "ÐApp profile" + :no-statuses-discovered "No statuses discovered" + :no-statuses-discovered-body "When somebody posts\na status you will see it here." + :no-hashtags-discovered-title "No #hashtags discovered" + :no-hashtags-discovered-body "When a #hashtag becomes\npopular you will see it here." ;;settings - :settings "Settings" + :settings "Settings" ;;contacts - :contacts "Contacts" - :new-contact "New contact" - :delete-contact "Delete contact" - :delete-contact-confirmation "This contact will be removed from your contacts" - :remove-from-group "Remove from group" - :edit-contacts "Edit contacts" - :search-contacts "Search contacts" - :contacts-group-new-chat "Start new chat" - :choose-from-contacts "Choose from contacts" - :no-contacts "No contacts yet" - :show-qr "Show QR code" - :qr-code-public-key-hint "Share this code to \nstart chatting" - :enter-address "Enter address" - :enter-contact-code "Enter contact code" - :more "more" + :contacts "Contacts" + :new-contact "New contact" + :delete-contact "Delete contact" + :delete-contact-confirmation "This contact will be removed from your contacts" + :remove-from-group "Remove from group" + :edit-contacts "Edit contacts" + :search-contacts "Search contacts" + :contacts-group-new-chat "Start new chat" + :choose-from-contacts "Choose from contacts" + :no-contacts "No contacts yet" + :show-qr "Show QR code" + :qr-code-public-key-hint "Share this code to \nstart chatting" + :enter-address "Enter address" + :enter-contact-code "Enter contact code" + :more "more" ;;group-settings - :remove "Remove" - :save "Save" - :create "Create" - :delete "Delete" - :delete-confirmation "Delete?" - :leave "Leave" - :leave-confirmation "Leave?" - :clear "Clear" - :clear-history "Clear history" - :clear-history-title "Clear history?" - :clear-group-history-confirmation "Are you sure you want to clear this group's chat history?" - :clear-history-confirmation "Clear history?" - :clear-history-action "Clear" - :mute-notifications "Mute notifications" - :leave-group-action "Leave" - :leave-group-title "Leave?" - :leave-group-confirmation "Are you sure you want to leave this group?" - :leave-chat "Leave chat" - :leave-group-chat-confirmation "Are you sure you want to leave this group?" - :leave-group-chat "Leave group chat" - :leave-group "Leave group" - :remove-from-chat "Remove from chat" - :delete-chat-title "Delete chat?" - :leave-public-chat "Leave public chat" - :chat-settings "Chat settings" - :edit "Edit" - :add-members "Add members" + :remove "Remove" + :save "Save" + :create "Create" + :delete "Delete" + :delete-confirmation "Delete?" + :leave "Leave" + :leave-confirmation "Leave?" + :clear "Clear" + :clear-history "Clear history" + :clear-history-title "Clear history?" + :clear-group-history-confirmation "Are you sure you want to clear this group's chat history?" + :clear-history-confirmation "Clear history?" + :clear-history-action "Clear" + :mute-notifications "Mute notifications" + :leave-group-action "Leave" + :leave-group-title "Leave?" + :leave-group-confirmation "Are you sure you want to leave this group?" + :leave-chat "Leave chat" + :leave-group-chat-confirmation "Are you sure you want to leave this group?" + :leave-group-chat "Leave group chat" + :leave-group "Leave group" + :remove-from-chat "Remove from chat" + :delete-chat-title "Delete chat?" + :leave-public-chat "Leave public chat" + :chat-settings "Chat settings" + :edit "Edit" + :add-members "Add members" ;;commands - :chat-send-eth "{{amount}} ETH" + :chat-send-eth "{{amount}} ETH" ;;new-group - :new-group "New group" - :reorder-groups "Reorder groups" - :edit-group "Edit group" - :delete-group "Delete group" - :delete-group-confirmation "This group will be removed from your groups. This will not affect your contacts" - :delete-group-prompt "This will not affect your contacts" - :contact-s {:one "contact" - :other "contacts"} + :new-group "New group" + :reorder-groups "Reorder groups" + :edit-group "Edit group" + :delete-group "Delete group" + :delete-group-confirmation "This group will be removed from your groups. This will not affect your contacts" + :delete-group-prompt "This will not affect your contacts" + :contact-s {:one "contact" + :other "contacts"} ;;protocol - :received-invitation "received chat invitation" - :removed-from-chat "removed you from group chat" - :left "left" - :invited "invited" - :removed "removed" - :You "You" + :received-invitation "received chat invitation" + :removed-from-chat "removed you from group chat" + :left "left" + :invited "invited" + :removed "removed" + :You "You" ;;new-contact - :add-new-contact "Add new contact" - :scan-qr "Scan QR code" - :name "Name" - :address-explication "Your public key is used to generate your address on Ethereum and is a series of numbers and letters. You can find it easily in your profile" - :use-valid-contact-code "Please enter a valid contact code" - :enter-valid-public-key "Please enter a valid public key or scan a QR code" - :contact-already-added "The contact has already been added" - :can-not-add-yourself "You can't add yourself" - :unknown-address "Unknown address" + :add-new-contact "Add new contact" + :scan-qr "Scan QR code" + :name "Name" + :address-explication "Your public key is used to generate your address on Ethereum and is a series of numbers and letters. You can find it easily in your profile" + :use-valid-contact-code "Please enter a valid contact code" + :enter-valid-public-key "Please enter a valid public key or scan a QR code" + :contact-already-added "The contact has already been added" + :can-not-add-yourself "You can't add yourself" + :unknown-address "Unknown address" ;;login - :connect "Connect" - :address "Address" - :password "Password" - :sign-in-to-status "Sign in to Status" - :sign-in "Sign in" - :sign-in-to-another "Sign in to another account" - :wrong-password "Wrong password" - :enter-password "Enter password" + :connect "Connect" + :address "Address" + :password "Password" + :sign-in-to-status "Sign in to Status" + :sign-in "Sign in" + :sign-in-to-another "Sign in to another account" + :wrong-password "Wrong password" + :enter-password "Enter password" ;;recover - :passphrase "Passphrase" - :recover "Recover" - :twelve-words-in-correct-order "12 words in correct order" - :enter-12-words "Enter the 12 words of your seed phrase" - + :passphrase "Passphrase" + :recover "Recover" + :twelve-words-in-correct-order "12 words in correct order" + :enter-12-words "Enter the 12 words of your seed phrase" ;;accounts - :recover-access "Recover access" - :create-new-account "Create new account" - :add-existing-account "Add existing account" + :recover-access "Recover access" + :create-new-account "Create new account" + :add-existing-account "Add existing account" ;;wallet-qr-code - :done "Done" + :done "Done" ;;validation - :invalid-phone "Invalid phone number" - :amount "Amount" + :invalid-phone "Invalid phone number" + :amount "Amount" ;;transactions - :confirm "Confirm" - :transaction "Transaction" - :unsigned-transaction-expired "Unsigned transaction expired" - :status "Status" - :recipient "Recipient" - :specify-recipient "Specify recipient..." - :recipient-code "Enter recipient address" - :recent-recipients "Recent recipients" - :to "To" - :from "From" - :data "Data" - :got-it "Got it" - :block "Block" - :hash "Hash" - :gas-limit "Gas limit" - :gas-price "Gas price" - :gas-used "Gas used" - :cost-fee "Cost/Fee" - :nonce "Nonce" - :confirmations "Confirmations" - :confirmations-helper-text "Please wait for at least 12 confirmations to make sure your transaction is processed securely" - :copy-transaction-hash "Copy transaction hash" - :open-on-etherscan "Open on Etherscan.io" - :incoming "Incoming" - :outgoing "Outgoing" - :pending "Pending" - :postponed "Postponed" + :confirm "Confirm" + :transaction "Transaction" + :unsigned-transaction-expired "Unsigned transaction expired" + :status "Status" + :recipient "Recipient" + :specify-recipient "Specify recipient..." + :recipient-code "Enter recipient address" + :recent-recipients "Recent recipients" + :to "To" + :from "From" + :data "Data" + :got-it "Got it" + :block "Block" + :hash "Hash" + :gas-limit "Gas limit" + :gas-price "Gas price" + :gas-used "Gas used" + :cost-fee "Cost/Fee" + :nonce "Nonce" + :confirmations "Confirmations" + :confirmations-helper-text "Please wait for at least 12 confirmations to make sure your transaction is processed securely" + :copy-transaction-hash "Copy transaction hash" + :open-on-etherscan "Open on Etherscan.io" + :incoming "Incoming" + :outgoing "Outgoing" + :pending "Pending" + :postponed "Postponed" ;;webview - :web-view-error "oops, error" + :web-view-error "oops, error" ;;testfairy warning - :testfairy-title "Warning!" - :testfairy-message "You are using an app installed from a nightly build. For testing purposes this build includes session recording if wifi connection is used, so all your interactions with this app is saved (as video and logs) and might be used by our development team to investigate possible issues. Saved video/logs do not include your passwords. Recording is done only if the app is installed from a nightly build. Nothing is recorded if the app is installed from PlayStore or TestFlight." + :testfairy-title "Warning!" + :testfairy-message "You are using an app installed from a nightly build. For testing purposes this build includes session recording if wifi connection is used, so all your interactions with this app is saved (as video and logs) and might be used by our development team to investigate possible issues. Saved video/logs do not include your passwords. Recording is done only if the app is installed from a nightly build. Nothing is recorded if the app is installed from PlayStore or TestFlight." ;; wallet - :wallet "Wallet" - :wallets "Wallets" - :your-wallets "Your wallets" - :main-wallet "Main Wallet" - :wallet-error "Error loading data" - :wallet-send "Send" - :wallet-send-token "Send {{symbol}}" - :wallet-request "Request" - :wallet-exchange "Exchange" - :wallet-asset "Asset" - :wallet-assets "Assets" - :wallet-add-asset "Add asset" - :wallet-total-value "Total value" - :wallet-settings "Wallet settings" - :wallet-manage-assets "Manage Assets" - :signing-phrase-description "Sign the transaction by entering your password. Make sure that the words above match your secret signing phrase" - :wallet-insufficient-funds "Insufficient funds" - :receive "Receive" - :request-qr-legend "Share this code to receive assets" - :send-request "Send request" - :send-transaction-request "Send a transaction request" - :share "Share" - :eth "ETH" - :gwei "Gwei" - :currency "Currency" - :usd-currency "USD" - :amount-placeholder "Specify amount..." - :transactions "Transactions" - :transaction-details "Transaction details" - :transaction-failed "Transaction failed" - :transactions-sign "Sign" - :transactions-sign-all "Sign all" - :transactions-sign-transaction "Sign transaction" - :transactions-sign-later "Sign later" - :transactions-delete "Delete transaction" - :transactions-delete-content "Transaction will be removed from 'Unsigned' list" - :transactions-history "History" - :transactions-unsigned "Unsigned" - :transactions-history-empty "No transactions in your history yet" - :transactions-unsigned-empty "You don't have any unsigned transactions" - :transactions-filter-title "Filter history" - :transactions-filter-tokens "Tokens" - :transactions-filter-type "Type" - :transactions-filter-select-all "Select all" - :view-transaction-details "View transaction details" - :transaction-description "Please wait for at least 12 confirmations to make sure your transaction is processed securely" - :transaction-sent "Transaction sent" - :transaction-moved-text "The transaction will remain in the 'Unsigned' list for the next 5 mins" - :transaction-moved-title "Transaction moved" - :sign-later-title "Sign transaction later?" - :sign-later-text "Check the transaction history to sign this transaction" - :not-applicable "Not applicable for unsigned transactions" - :send-transaction "Send transaction" - :new-request "New request" - :request-transaction "Request transaction" - :receive-transaction "Receive transaction" - :new-transaction "New Transaction" - :transaction-history "Transaction History" + :wallet "Wallet" + :wallets "Wallets" + :your-wallets "Your wallets" + :main-wallet "Main Wallet" + :wallet-error "Error loading data" + :wallet-send "Send" + :wallet-send-token "Send {{symbol}}" + :wallet-request "Request" + :wallet-exchange "Exchange" + :wallet-asset "Asset" + :wallet-assets "Assets" + :wallet-add-asset "Add asset" + :wallet-total-value "Total value" + :wallet-settings "Wallet settings" + :wallet-manage-assets "Manage Assets" + :signing-phrase-description "Sign the transaction by entering your password. Make sure that the words above match your secret signing phrase" + :wallet-insufficient-funds "Insufficient funds" + :receive "Receive" + :request-qr-legend "Share this code to receive assets" + :send-request "Send request" + :send-transaction-request "Send a transaction request" + :share "Share" + :eth "ETH" + :gwei "Gwei" + :currency "Currency" + :usd-currency "USD" + :amount-placeholder "Specify amount..." + :transactions "Transactions" + :transaction-details "Transaction details" + :transaction-failed "Transaction failed" + :transactions-sign "Sign" + :transactions-sign-all "Sign all" + :transactions-sign-transaction "Sign transaction" + :transactions-sign-later "Sign later" + :transactions-delete "Delete transaction" + :transactions-delete-content "Transaction will be removed from 'Unsigned' list" + :transactions-history "History" + :transactions-unsigned "Unsigned" + :transactions-history-empty "No transactions in your history yet" + :transactions-unsigned-empty "You don't have any unsigned transactions" + :transactions-filter-title "Filter history" + :transactions-filter-tokens "Tokens" + :transactions-filter-type "Type" + :transactions-filter-select-all "Select all" + :view-transaction-details "View transaction details" + :transaction-description "Please wait for at least 12 confirmations to make sure your transaction is processed securely" + :transaction-sent "Transaction sent" + :transaction-moved-text "The transaction will remain in the 'Unsigned' list for the next 5 mins" + :transaction-moved-title "Transaction moved" + :sign-later-title "Sign transaction later?" + :sign-later-text "Check the transaction history to sign this transaction" + :not-applicable "Not applicable for unsigned transactions" + :send-transaction "Send transaction" + :new-request "New request" + :request-transaction "Request transaction" + :receive-transaction "Receive transaction" + :new-transaction "New Transaction" + :transaction-history "Transaction History" ;; Wallet Send - :wallet-choose-recipient "Choose Recipient" - :wallet-choose-from-contacts "Choose From Contacts" - :wallet-address-from-clipboard "Use Address From Clipboard" - :wallet-invalid-address "Invalid address: \n {{data}}" - :wallet-invalid-chain-id "Network does not match: \n {{data}} but current chain is {{chain}}" - :wallet-browse-photos "Browse Photos" - :wallet-advanced "Advanced" - :wallet-transaction-fee "Transaction Fee" - :wallet-transaction-fee-details "Gas limit is the amount of gas to send with your transaction. Increasing this number will not get your transaction processed faster" - :wallet-transaction-total-fee "Total Fee" - :validation-amount-invalid-number "Amount is not a valid number" - :validation-amount-is-too-precise "Amount is too precise. The smallest unit you can send is 1 Wei (1x10^-18 ETH)" - :scan-qr-code "Scan a QR code with a wallet address" - :reset-default "Reset to default" - - + :wallet-choose-recipient "Choose Recipient" + :wallet-choose-from-contacts "Choose From Contacts" + :wallet-address-from-clipboard "Use Address From Clipboard" + :wallet-invalid-address "Invalid address: \n {{data}}" + :wallet-invalid-chain-id "Network does not match: \n {{data}} but current chain is {{chain}}" + :wallet-browse-photos "Browse Photos" + :wallet-advanced "Advanced" + :wallet-transaction-fee "Transaction Fee" + :wallet-transaction-fee-details "Gas limit is the amount of gas to send with your transaction. Increasing this number will not get your transaction processed faster" + :wallet-transaction-total-fee "Total Fee" + :validation-amount-invalid-number "Amount is not a valid number" + :validation-amount-is-too-precise "Amount is too precise. The smallest unit you can send is 1 Wei (1x10^-18 ETH)" + :scan-qr-code "Scan a QR code with a wallet address" + :reset-default "Reset to default" ;; network settings - :new-network "New network" - :add-network "Add network" - :add-new-network "Add new network" - :add-wnode "Add mailserver" - :existing-networks "Existing networks" + :new-network "New network" + :add-network "Add network" + :add-new-network "Add new network" + :add-wnode "Add mailserver" + :existing-networks "Existing networks" ;; TODO(dmitryn): come up with better description/naming. Suggested namings: Mailbox and Master Node - :existing-wnodes "Existing mailservers" - :add-json-file "Add a JSON file" - :paste-json-as-text "Paste JSON as text" - :paste-json "Paste JSON" - :specify-rpc-url "Specify a RPC URL" - :edit-network-config "Edit network config" - :connected "Connected" - :process-json "Process JSON" - :error-processing-json "Error processing JSON" - :rpc-url "RPC URL" - :network "Network" - :remove-network "Remove network" - :network-settings "Network settings" - :offline-messaging-settings "Offline messages settings" - :edit-network-warning "Be careful, editing the network data may disable this network for you" - :connecting-requires-login "Connecting to another network requires login" - :close-app-title "Warning!" - :close-app-content "The app will stop and close. When you reopen it, the selected network will be used" - :close-app-button "Confirm" - :connect-wnode-content "Connect to {{name}}?" + :existing-wnodes "Existing mailservers" + :add-json-file "Add a JSON file" + :paste-json-as-text "Paste JSON as text" + :paste-json "Paste JSON" + :specify-rpc-url "Specify a RPC URL" + :edit-network-config "Edit network config" + :connected "Connected" + :process-json "Process JSON" + :error-processing-json "Error processing JSON" + :rpc-url "RPC URL" + :network "Network" + :remove-network "Remove network" + :network-settings "Network settings" + :offline-messaging-settings "Offline messages settings" + :edit-network-warning "Be careful, editing the network data may disable this network for you" + :connecting-requires-login "Connecting to another network requires login" + :close-app-title "Warning!" + :close-app-content "The app will stop and close. When you reopen it, the selected network will be used" + :close-app-button "Confirm" + :connect-wnode-content "Connect to {{name}}?" ;; browser - :browser "Browser" - :enter-dapp-url "Enter a ÐApp URL" - :dapp "ÐApp" - :selected "Selected" - :selected-dapps "Selected ÐApps"}) + :browser "Browser" + :enter-dapp-url "Enter a ÐApp URL" + :dapp "ÐApp" + :selected "Selected" + :selected-dapps "Selected ÐApps"}) diff --git a/src/status_im/ui/components/colors.cljs b/src/status_im/ui/components/colors.cljs index 00a8a01202..4611344861 100644 --- a/src/status_im/ui/components/colors.cljs +++ b/src/status_im/ui/components/colors.cljs @@ -11,6 +11,7 @@ (def gray-icon "#6e777e") ;; Used for forward icon in accounts (def gray-light "#e8ebec") ;; Used as divider color (def gray-lighter "#eef2f5") ;; Used as a background or shadow +(def gray-border "#ececf0") (def blue "#4360df") ;; Used as main wallet color, and ios home add button (def red "#ff2d55") ;; Used to highlight errors or "dangerous" actions (def red-light "#ffe5ea") ;; error tooltip diff --git a/src/status_im/ui/components/common/common.cljs b/src/status_im/ui/components/common/common.cljs index 0c795a4af9..d611cb89e5 100644 --- a/src/status_im/ui/components/common/common.cljs +++ b/src/status_im/ui/components/common/common.cljs @@ -88,3 +88,16 @@ :style styles/button-label} label]]]) +(defn counter + ([value] (counter nil value)) + ([{:keys [size] :or {size 18}} value] + [react/view {:style (styles/counter-container size)} + [react/text {:style (styles/counter-label size)} + value]])) + +(defn image-contain + ([source] (image-contain nil source)) + ([{:keys [style]} source] + [react/view {:style (merge styles/image-contain + style)} + [react/image {:source source :resizeMode :contain :style styles/image-contain-image}]])) \ No newline at end of file diff --git a/src/status_im/ui/components/common/styles.cljs b/src/status_im/ui/components/common/styles.cljs index a175a69bd5..4f7dcb6b51 100644 --- a/src/status_im/ui/components/common/styles.cljs +++ b/src/status_im/ui/components/common/styles.cljs @@ -148,3 +148,28 @@ :letter-spacing -0.2 :text-align :center :color colors/blue}) + +(defn counter-container [size] + {:width size + :height size + :border-radius (/ size 2) + :background-color colors/blue + :align-items :center + :justify-content :center}) + +(defn counter-label [size] + {:font-size (/ size 2) + :letter-spacing -0.2 + :text-align :center + :color colors/white}) + +(def image-contain + {:flex-direction :row + :flex 1 + :align-items :center + :justify-content :center + :flex-wrap :wrap}) + +(def image-contain-image + {:flex-direction :row + :flex 1}) diff --git a/src/status_im/ui/components/react.cljs b/src/status_im/ui/components/react.cljs index 1369d7b536..c39745af4e 100644 --- a/src/status_im/ui/components/react.cljs +++ b/src/status_im/ui/components/react.cljs @@ -46,6 +46,7 @@ (def text-class (get-class "Text")) (def text-input-class (get-class "TextInput")) (def image (get-class "Image")) +(def switch (get-class "Switch")) (def check-box (get-class "CheckBox")) (def touchable-without-feedback (get-class "TouchableWithoutFeedback")) diff --git a/src/status_im/ui/components/text_input/styles.cljs b/src/status_im/ui/components/text_input/styles.cljs index 8a9f4d3467..77418229ae 100644 --- a/src/status_im/ui/components/text_input/styles.cljs +++ b/src/status_im/ui/components/text_input/styles.cljs @@ -21,7 +21,7 @@ :color colors/black :padding 0}) -(def error - {:bottom-value -20 +(defn error [label?] + {:bottom-value (if label? -20 0) :color colors/red-light :font-size 12}) \ No newline at end of file diff --git a/src/status_im/ui/components/text_input/view.cljs b/src/status_im/ui/components/text_input/view.cljs index 2be1dccbc6..57686104c3 100644 --- a/src/status_im/ui/components/text_input/view.cljs +++ b/src/status_im/ui/components/text_input/view.cljs @@ -6,8 +6,9 @@ (defn text-input-with-label [{:keys [label error style height] :as props}] [react/view - [react/text {:style styles/label} - label] + (when label + [react/text {:style styles/label} + label]) [react/view {:style (styles/input-container height)} [react/text-input (merge @@ -17,4 +18,4 @@ :auto-capitalize :none} (dissoc props :style :height))]] (when error - [tooltip/tooltip error styles/error])]) + [tooltip/tooltip error (styles/error label)])]) diff --git a/src/status_im/ui/components/toolbar/styles.cljs b/src/status_im/ui/components/toolbar/styles.cljs index a410082bb8..f79f1adfad 100644 --- a/src/status_im/ui/components/toolbar/styles.cljs +++ b/src/status_im/ui/components/toolbar/styles.cljs @@ -84,3 +84,8 @@ ;;TODO(goranjovic) - Breaks the toolbar title into new line on smaller screens ;;e.g. see Discover > Popular hashtags on iPhone 5s (def ios-content-item {:position :absolute :right 40 :left 40}) + +(def counter-container + {:position :absolute + :top 19 + :right 2}) diff --git a/src/status_im/ui/components/toolbar/view.cljs b/src/status_im/ui/components/toolbar/view.cljs index b059083c9e..79a070bd7a 100644 --- a/src/status_im/ui/components/toolbar/view.cljs +++ b/src/status_im/ui/components/toolbar/view.cljs @@ -1,4 +1,5 @@ (ns status-im.ui.components.toolbar.view + (:require-macros [status-im.utils.views :refer [defview letsubs]]) (:require [re-frame.core :as re-frame] [status-im.i18n :as i18n] [status-im.ui.components.icons.vector-icons :as vector-icons] @@ -8,7 +9,8 @@ [status-im.ui.components.toolbar.actions :as actions] [status-im.ui.components.toolbar.styles :as styles] [status-im.utils.platform :as platform] - [status-im.utils.core :as utils])) + [status-im.utils.core :as utils] + [status-im.ui.components.common.common :as components.common])) ;; Navigation item @@ -26,6 +28,14 @@ [nav-item (merge {:style styles/nav-item-button} props) [vector-icons/icon icon icon-opts]]) +(defview nav-button-with-count [props] + (letsubs [unread-messages-number [:get-chats-unread-messages-number]] + [react/view + [nav-button props] + (when (pos? unread-messages-number) + [react/view styles/counter-container + [components.common/counter unread-messages-number]])])) + (defn nav-text ([text] (nav-text nil text)) ([{:keys [handler] :as props} text] @@ -41,6 +51,8 @@ (def default-nav-back [nav-button actions/default-back]) +(def nav-back-count [nav-button-with-count actions/default-back]) + (defn default-done "Renders a touchable icon on Android or a label or iOS." [{:keys [icon] :as props}] diff --git a/src/status_im/ui/screens/accounts/db.cljs b/src/status_im/ui/screens/accounts/db.cljs index fe5da47c53..7eae134fae 100644 --- a/src/status_im/ui/screens/accounts/db.cljs +++ b/src/status_im/ui/screens/accounts/db.cljs @@ -28,7 +28,10 @@ (spec/def :account/wnode (spec/nilable string?)) (spec/def :account/settings (spec/nilable (spec/map-of keyword? any?))) (spec/def :account/signing-phrase :global/not-empty-string) +(spec/def :account/mnemonic (spec/nilable string?)) (spec/def :account/sharing-usage-data? (spec/nilable boolean?)) +(spec/def :account/dev-mode? (spec/nilable boolean?)) +(spec/def :account/seed-backed-up? (spec/nilable boolean?)) (spec/def :accounts/account (allowed-keys :req-un [:account/name :account/address :account/public-key @@ -37,7 +40,8 @@ :account/updates-private-key :account/updates-public-key :account/email :account/signed-up? :account/network :account/networks :account/settings :account/wnode - :account/last-sign-in :account/sharing-usage-data?])) + :account/last-sign-in :account/sharing-usage-data? :account/dev-mode? + :account/seed-backed-up? :account/mnemonic])) (spec/def :accounts/accounts (spec/nilable (spec/map-of :account/address :accounts/account))) diff --git a/src/status_im/ui/screens/accounts/events.cljs b/src/status_im/ui/screens/accounts/events.cljs index 63de61bb6b..eac8f0bbea 100644 --- a/src/status_im/ui/screens/accounts/events.cljs +++ b/src/status_im/ui/screens/accounts/events.cljs @@ -119,6 +119,7 @@ :updates-private-key (:private keypair) :photo-path (identicon pubkey) :signing-phrase signing-phrase + :mnemonic mnemonic :settings {:wallet {:visible-tokens {:testnet #{:STT} :mainnet #{:SNT}}}}}] (log/debug "account-created") (when-not (str/blank? pubkey) @@ -158,13 +159,18 @@ "Takes effects (containing :db) + new account fields, adds all effects necessary for account update." [{{:accounts/keys [accounts current-account-id] :as db} :db :as fx} new-account-fields] (let [current-account (get accounts current-account-id) - new-account (merge current-account new-account-fields)] - (-> fx - (assoc-in [:db :accounts/accounts current-account-id] new-account) - (assoc ::save-account new-account - ::broadcast-account-update (merge (select-keys db [:current-public-key :web3]) - (select-keys new-account [:name :photo-path :status - :updates-public-key :updates-private-key])))))) + new-account (merge current-account new-account-fields) + broadcast-fields [:name :photo-path :status + :updates-public-key :updates-private-key]] + (cond-> fx + true + (assoc-in [:db :accounts/accounts current-account-id] new-account) + true + (assoc ::save-account new-account) + + (seq (clojure.set/intersection (set (keys new-account-fields)) (set broadcast-fields))) + (assoc ::broadcast-account-update (merge (select-keys db [:current-public-key :web3]) + (select-keys new-account broadcast-fields)))))) (handlers/register-handler-fx :account-update-keys @@ -210,3 +216,8 @@ :reset-account-creation (fn [{db :db} _] {:db (update db :accounts/create assoc :step :enter-password :password nil :password-confirm nil :error nil)})) + +(handlers/register-handler-fx + :switch-dev-mode + (fn [{db :db} [_ dev-mode]] + (account-update {:db db} {:dev-mode? dev-mode}))) \ No newline at end of file diff --git a/src/status_im/ui/screens/accounts/recover/events.cljs b/src/status_im/ui/screens/accounts/recover/events.cljs index 36d70f742e..e54e8eedae 100644 --- a/src/status_im/ui/screens/accounts/recover/events.cljs +++ b/src/status_im/ui/screens/accounts/recover/events.cljs @@ -39,6 +39,7 @@ :photo-path (identicon/identicon public-key) :updates-public-key public :updates-private-key private + :mnemonic "" :signed-up? true :signing-phrase phrase}] (when-not (string/blank? public-key) diff --git a/src/status_im/ui/screens/browser/styles.cljs b/src/status_im/ui/screens/browser/styles.cljs index 6711202632..9fcd449163 100644 --- a/src/status_im/ui/screens/browser/styles.cljs +++ b/src/status_im/ui/screens/browser/styles.cljs @@ -54,10 +54,10 @@ :background-color colors/gray-lighter :padding-horizontal 12 :android {:align-items :flex-start - :margin-left (if show-actions 66 18) + :margin-left (if show-actions 66 20) :padding-bottom 6} :ios {:align-items :center - :margin-horizontal 12}}) + :margin-horizontal 15}}) (defstyle url-input {:flex 1 @@ -67,4 +67,4 @@ (def toolbar-content-dapp {:flex-direction :row - :margin-horizontal 10}) + :margin-horizontal 15}) \ No newline at end of file diff --git a/src/status_im/ui/screens/browser/views.cljs b/src/status_im/ui/screens/browser/views.cljs index 98f9d4d71d..561bd30676 100644 --- a/src/status_im/ui/screens/browser/views.cljs +++ b/src/status_im/ui/screens/browser/views.cljs @@ -5,7 +5,6 @@ [status-im.ui.screens.browser.styles :as styles] [status-im.ui.components.status-bar.view :as status-bar] [status-im.ui.components.toolbar.view :as toolbar.view] - [status-im.chat.views.toolbar-content :as toolbar-content] [status-im.ui.components.webview-bridge :as components.webview-bridge] [status-im.utils.js-resources :as js-res] [status-im.ui.components.react :as components] @@ -16,32 +15,34 @@ (views/defview toolbar-content-dapp [contact-identity] (views/letsubs [contact [:contact-by-identity contact-identity]] - [react/view styles/toolbar-content-dapp - [chat-icon.screen/dapp-icon-browser contact 36] - [react/view styles/dapp-name - [react/text {:style styles/dapp-name-text - :number-of-lines 1 - :font :toolbar-title - :accessibility-label :dapp-name-text} - (:name contact)] - [react/text {:style styles/dapp-text} - (i18n/label :t/dapp)]]])) + [react/view + [react/view styles/toolbar-content-dapp + [chat-icon.screen/dapp-icon-browser contact 36] + [react/view styles/dapp-name + [react/text {:style styles/dapp-name-text + :number-of-lines 1 + :font :toolbar-title + :accessibility-label :dapp-name-text} + (:name contact)] + [react/text {:style styles/dapp-text} + (i18n/label :t/dapp)]]]])) (defn toolbar-content [{:keys [url] :as browser}] (let [url-text (atom nil)] - [react/view (styles/toolbar-content false) - [react/text-input {:on-change-text #(reset! url-text %) - :on-submit-editing #(re-frame/dispatch [:update-browser (assoc browser :url @url-text)]) - :auto-focus (not url) - :placeholder (i18n/label :t/enter-url) - :auto-capitalize :none - :auto-correct false - :default-value url - :style styles/url-input}] - ;;TODO .reload doesn't work, implement later - #_[react/touchable-highlight {:on-press #(when @webview (.reload @webview))} - [react/view - [vector-icons/icon :icons/refresh]]]])) + [react/view + [react/view (styles/toolbar-content false) + [react/text-input {:on-change-text #(reset! url-text %) + :on-submit-editing #(re-frame/dispatch [:update-browser (assoc browser :url @url-text)]) + :auto-focus (not url) + :placeholder (i18n/label :t/enter-url) + :auto-capitalize :none + :auto-correct false + :default-value url + :style styles/url-input}] + ;;TODO .reload doesn't work, implement later + #_[react/touchable-highlight {:on-press #(when @webview (.reload @webview))} + [react/view + [vector-icons/icon :icons/refresh]]]]])) (defn web-view-error [] (reagent/as-element @@ -54,7 +55,7 @@ [components/activity-indicator {:animating true}]])) (defn on-navigation-change [event browser] - (let [{:strs [loading url title canGoBack canGoForward]} (js->clj event)] + (let [{:strs [url title canGoBack canGoForward]} (js->clj event)] (when-not (= "about:blank" url) (re-frame/dispatch [:update-browser (assoc browser :url url :name title)])) (re-frame/dispatch [:update-browser-options {:can-go-back? canGoBack :can-go-forward? canGoForward}]))) @@ -64,14 +65,15 @@ {:keys [dapp? contact url] :as browser} [:get-current-browser] {:keys [can-go-back? can-go-forward?]} [:get :browser/options] extra-js [:web-view-extra-js] - rpc-url [:get :rpc-url]] + rpc-url [:get :rpc-url] + unread-messages-number [:get-chats-unread-messages-number]] [react/keyboard-avoiding-view styles/browser [status-bar/status-bar] [toolbar.view/toolbar {} - toolbar.view/default-nav-back + toolbar.view/nav-back-count (if dapp? - [toolbar-content-dapp contact] - [toolbar-content browser])] + [toolbar-content-dapp contact unread-messages-number] + [toolbar-content browser unread-messages-number])] (if url [components.webview-bridge/webview-bridge {:ref #(reset! webview %) diff --git a/src/status_im/ui/screens/db.cljs b/src/status_im/ui/screens/db.cljs index 9ee824b797..cf21f090cd 100644 --- a/src/status_im/ui/screens/db.cljs +++ b/src/status_im/ui/screens/db.cljs @@ -146,10 +146,11 @@ :accounts/current-account-id :accounts/recover :accounts/login - :my-profile/drawer :my-profile/profile :my-profile/default-name :my-profile/editing? + :my-profile/advanced? + :my-profile/seed :group-chat-profile/profile :group-chat-profile/editing? :networks/selected-network diff --git a/src/status_im/ui/screens/home/views/inner_item.cljs b/src/status_im/ui/screens/home/views/inner_item.cljs index f584895cad..b58dbe4e28 100644 --- a/src/status_im/ui/screens/home/views/inner_item.cljs +++ b/src/status_im/ui/screens/home/views/inner_item.cljs @@ -12,7 +12,8 @@ [status-im.utils.gfycat.core :as gfycat] [status-im.constants :as const] [status-im.ui.components.icons.vector-icons :as vector-icons] - [status-im.ui.components.chat-icon.screen :as chat-icon.screen])) + [status-im.ui.components.chat-icon.screen :as chat-icon.screen] + [status-im.ui.components.common.common :as components.common])) (defn message-content-text [{:keys [content] :as message}] [react/view styles/last-message-container @@ -58,10 +59,7 @@ (defview unviewed-indicator [chat-id] (letsubs [unviewed-messages-count [:unviewed-messages-count chat-id]] (when (pos? unviewed-messages-count) - [react/view styles/new-messages-container - [react/text {:style styles/new-messages-text - :font :medium} - unviewed-messages-count]]))) + [components.common/counter {:size 22} unviewed-messages-count]))) (defn chat-list-item-name [name group-chat? public? public-key] (let [private-group? (and group-chat? (not public?)) @@ -89,7 +87,6 @@ (letsubs [last-message [:get-last-message chat-id]] (let [name (or (i18n/get-contact-translated chat-id :name name) (gfycat/generate-gfy public-key))] - [react/view [react/touchable-highlight {:on-press #(re-frame/dispatch [:navigate-to-chat chat-id])} [react/view styles/chat-container [react/view styles/chat-icon-container @@ -103,7 +100,7 @@ [message-timestamp last-message]])] [react/view styles/item-lower-container [message-content-text last-message] - [unviewed-indicator chat-id]]]]]]))) + [unviewed-indicator chat-id]]]]]))) (defview home-list-browser-item-inner-view [{:keys [browser-id name url dapp? contact] :as browser}] (letsubs [contact' [:contact-by-identity contact]] diff --git a/src/status_im/ui/screens/main_tabs/styles.cljs b/src/status_im/ui/screens/main_tabs/styles.cljs index 856056f12a..f17110e0f3 100644 --- a/src/status_im/ui/screens/main_tabs/styles.cljs +++ b/src/status_im/ui/screens/main_tabs/styles.cljs @@ -27,4 +27,11 @@ styles/color-gray4)}) (defn tab-icon [active?] - {:color (if active? styles/color-blue4 styles/color-gray4)}) \ No newline at end of file + {:color (if active? styles/color-blue4 styles/color-gray4)}) + +(def counter-container + {:position :absolute + :top 4}) + +(def counter + {:margin-left 18}) \ No newline at end of file diff --git a/src/status_im/ui/screens/main_tabs/views.cljs b/src/status_im/ui/screens/main_tabs/views.cljs index 6cc1cfe841..e3eb551215 100644 --- a/src/status_im/ui/screens/main_tabs/views.cljs +++ b/src/status_im/ui/screens/main_tabs/views.cljs @@ -9,51 +9,60 @@ [status-im.ui.screens.home.views :as home] [status-im.ui.screens.wallet.views :as wallet] [status-im.ui.screens.main-tabs.styles :as styles] - [status-im.ui.screens.profile.user.views :as profile.user])) + [status-im.ui.screens.profile.user.views :as profile.user] + [status-im.ui.components.common.common :as components.common])) (def tabs-list-data [{:view-id :home :content {:title (i18n/label :t/home) :icon-inactive :icons/home :icon-active :icons/home-active} + :count-subscription :get-chats-unread-messages-number :accessibility-label :home-tab-button} {:view-id :wallet :content {:title (i18n/label :t/wallet) :icon-inactive :icons/wallet :icon-active :icons/wallet-active} + :count-subscription :get-wallet-unread-messages-number :accessibility-label :wallet-tab-button} {:view-id :my-profile :content {:title (i18n/label :t/profile) :icon-inactive :icons/profile :icon-active :icons/profile-active} + :count-subscription :get-profile-unread-messages-number :accessibility-label :profile-tab-button}]) (defn- tab-content [{:keys [title icon-active icon-inactive]}] - (fn [active?] + (fn [active? count] [react/view {:style styles/tab-container} (let [icon (if active? icon-active icon-inactive)] [react/view [vector-icons/icon icon (styles/tab-icon active?)]]) [react/view [react/text {:style (styles/tab-title active?)} - title]]])) + title]] + (when (pos? count) + [react/view styles/counter-container + [react/view styles/counter + [components.common/counter count]]])])) (def tabs-list (map #(update % :content tab-content) tabs-list-data)) -(defn- tab [view-id content active? accessibility-label] - [react/touchable-highlight - (cond-> {:style common.styles/flex - :disabled active? - :on-press #(re-frame/dispatch [:navigate-to-tab view-id])} - accessibility-label - (assoc :accessibility-label accessibility-label)) - [react/view - [content active?]]]) +(views/defview tab [view-id content active? accessibility-label count-subscription] + (views/letsubs [count [count-subscription]] + [react/touchable-highlight + (cond-> {:style common.styles/flex + :disabled active? + :on-press #(re-frame/dispatch [:navigate-to-tab view-id])} + accessibility-label + (assoc :accessibility-label accessibility-label)) + [react/view + [content active? count]]])) (defn tabs [current-view-id] [react/view {:style styles/tabs-container} - (for [{:keys [content view-id accessibility-label]} tabs-list] - ^{:key view-id} [tab view-id content (= view-id current-view-id) accessibility-label])]) + (for [{:keys [content view-id accessibility-label count-subscription]} tabs-list] + ^{:key view-id} [tab view-id content (= view-id current-view-id) accessibility-label count-subscription])]) (views/defview main-tabs [] (views/letsubs [view-id [:get :view-id]] diff --git a/src/status_im/ui/screens/profile/components/views.cljs b/src/status_im/ui/screens/profile/components/views.cljs index 47586f18e5..1acd1595b0 100644 --- a/src/status_im/ui/screens/profile/components/views.cljs +++ b/src/status_im/ui/screens/profile/components/views.cljs @@ -6,7 +6,6 @@ [status-im.ui.components.chat-icon.screen :as chat-icon.screen] [status-im.ui.screens.profile.components.styles :as styles] [status-im.ui.components.common.common :as common] - [status-im.ui.components.styles :as components.styles] [status-im.ui.components.icons.vector-icons :as vector-icons] [status-im.ui.components.colors :as colors] [clojure.string :as string])) @@ -63,7 +62,8 @@ [react/text {:style styles/settings-title} title]) -(defn settings-item [label-kw value action-fn active? & [accessibility-label]] +(defn settings-item [{:keys [label-kw value action-fn active? accessibility-label icon-content] + :or {value "" active? true}}] [react/touchable-highlight (cond-> {:on-press action-fn :disabled (not active?)} @@ -79,5 +79,17 @@ :number-of-lines 1 :uppercase? true} value])] - (when active? - [vector-icons/icon :icons/forward {:color colors/gray}])]]) + (if icon-content + icon-content + (when active? + [vector-icons/icon :icons/forward {:color colors/gray}]))]]) + +(defn settings-switch-item [{:keys [label-kw value action-fn active?] :or {active? true}}] + [react/view styles/settings-item + [react/view styles/settings-item-text-wrapper + [react/text {:style styles/settings-item-text} + (i18n/label label-kw)]] + [react/switch {:on-tint-color colors/blue + :value value + :on-value-change action-fn + :disabled (not active?)}]]) \ No newline at end of file diff --git a/src/status_im/ui/screens/profile/db.cljs b/src/status_im/ui/screens/profile/db.cljs index 65a7e9cc83..57c5e86596 100644 --- a/src/status_im/ui/screens/profile/db.cljs +++ b/src/status_im/ui/screens/profile/db.cljs @@ -1,13 +1,10 @@ (ns status-im.ui.screens.profile.db - (:require-macros [status-im.utils.db :refer [allowed-keys]]) (:require [cljs.spec.alpha :as spec] [clojure.string :as string] [status-im.chat.constants :as chat.constants] [status-im.constants :as constants] [status-im.utils.homoglyph :as homoglyph])) -(def account-profile-keys [:name :photo-path :status]) - (defn correct-name? [username] (when-let [username (some-> username (string/trim))] (every? false? @@ -15,11 +12,6 @@ (homoglyph/matches username constants/console-chat-id) (string/includes? username chat.constants/command-char)]))) -(defn correct-email? [email] - (let [pattern #"[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?"] - (or (string/blank? email) - (and (string? email) (re-matches pattern email))))) - (defn base64-encoded-image-path? [photo-path] (or (string/starts-with? photo-path "data:image/jpeg;base64,") (string/starts-with? photo-path "data:image/png;base64,"))) @@ -28,9 +20,9 @@ (spec/def :profile/status (spec/nilable string?)) (spec/def :profile/photo-path (spec/nilable base64-encoded-image-path?)) -;; EDIT PROFILE -(spec/def :my-profile/default-name string?) +(spec/def :my-profile/default-name (spec/nilable string?)) +(spec/def :my-profile/editing? (spec/nilable boolean?)) +(spec/def :my-profile/advanced? (spec/nilable boolean?)) +(spec/def :my-profile/seed (spec/nilable map?)) (spec/def :my-profile/profile (spec/keys :opt-un [::name :profile/status :profile/photo-path ::edit-status? ::valid-name?])) -(spec/def :my-profile/drawer (spec/keys :opt-un [::name :profile/status - ::edit-status? ::valid-name?])) diff --git a/src/status_im/ui/screens/profile/events.cljs b/src/status_im/ui/screens/profile/events.cljs index 78ab2e9f72..697d01c84e 100644 --- a/src/status_im/ui/screens/profile/events.cljs +++ b/src/status_im/ui/screens/profile/events.cljs @@ -34,7 +34,7 @@ (as-> fx (merge fx (input-events/select-chat-input-command (:db fx) send-command nil true))))))) -(defn get-current-account [{:keys [:accounts/current-account-id] :as db}] +(defn get-current-account [{:accounts/keys [current-account-id] :as db}] (get-in db [:accounts/accounts current-account-id])) (defn valid-name? [name] @@ -61,15 +61,7 @@ (get-in db [:accounts/accounts current-account-id :name])))) (defn clear-profile [fx] - (update fx :db dissoc :my-profile/profile :my-profile/drawer :my-profile/default-name :my-profile/editing?)) - -(handlers/register-handler-fx - :my-profile.drawer/save-name - (fn [{:keys [db now]} _] - (let [cleaned-name (clean-name db :my-profile/drawer)] - (-> (clear-profile {:db db}) - (accounts-events/account-update {:name cleaned-name - :last-updated now}))))) + (update fx :db dissoc :my-profile/profile :my-profile/default-name :my-profile/editing?)) (handlers/register-handler-fx :my-profile/start-editing-profile @@ -99,3 +91,23 @@ (fn [{:keys [db]} _] (-> {:db db} (update :db dissoc :group-chat-profile/editing?)))) + +(handlers/register-handler-fx + :my-profile/enter-two-random-words + (fn [{:keys [db]} []] + (let [{:keys [mnemonic]} (get-current-account db) + shuffled-mnemonic (shuffle (map-indexed vector (clojure.string/split mnemonic #" ")))] + {:db (assoc db :my-profile/seed {:step :first-word + :first-word (first shuffled-mnemonic) + :second-word (second shuffled-mnemonic)})}))) + +(handlers/register-handler-fx + :my-profile/set-step + (fn [{:keys [db]} [_ step]] + {:db (update db :my-profile/seed assoc :step step :error nil :word nil)})) + +(handlers/register-handler-fx + :my-profile/finish + (fn [{:keys [db]} _] + (-> {:db (update db :my-profile/seed assoc :step :finish :error nil :word nil)} + (accounts-events/account-update {:seed-backed-up? true})))) diff --git a/src/status_im/ui/screens/profile/navigation.cljs b/src/status_im/ui/screens/profile/navigation.cljs index ecbf3bed01..ee0ee9bebd 100644 --- a/src/status_im/ui/screens/profile/navigation.cljs +++ b/src/status_im/ui/screens/profile/navigation.cljs @@ -9,3 +9,6 @@ :source source :value value}))) +(defmethod navigation/preload-data! :backup-seed + [db] + (assoc db :my-profile/seed {:step :intro})) \ No newline at end of file diff --git a/src/status_im/ui/screens/profile/seed/styles.cljs b/src/status_im/ui/screens/profile/seed/styles.cljs new file mode 100644 index 0000000000..66d68fe270 --- /dev/null +++ b/src/status_im/ui/screens/profile/seed/styles.cljs @@ -0,0 +1,154 @@ +(ns status-im.ui.screens.profile.seed.styles + (:require [status-im.ui.components.colors :as colors])) + +(def intro-container + {:flex 1 + :align-items :center + :margin-horizontal 26}) + +(def intro-image + {:padding-vertical 25}) + +(def intro-text + {:text-align :center + :font-size 22 + :font-weight :bold + :line-height 28 + :letter-spacing -0.3}) + +(def intro-description + {:margin-top 8 + :font-size 14 + :line-height 21 + :letter-spacing -0.2 + :text-align :center + :color colors/gray}) + +(def intro-button + {:flex-direction :row + :margin-top 16 + :margin-bottom 32}) + +(def six-words-container + {:flex 1 + :padding 15}) + +(def six-word-row + {:flex-direction :row}) + +(def six-word-num + {:opacity 0.4 + :font-size 15 + :letter-spacing -0.2}) + +(def six-words-word + {:margin-left 16 + :font-size 15 + :letter-spacing -0.2}) + +(def six-words-separator + {:height 12}) + +(def twelve-words-container + {:flex 1 + :padding 16}) + +(def twelve-words-label + {:font-size 14 + :line-height 21 + :letter-spacing -0.2}) + +(def twelve-words-description + {:font-size 14 + :line-height 21 + :letter-spacing -0.2}) + +(def twelve-words-spacer + {:flex 1}) + +(def twelve-words-button-container + {:align-items :flex-end}) + +(def twelve-words-columns + {:margin-top 8 + :margin-bottom 16 + :flex-direction :row + :border-radius 8 + :background-color colors/white + :border-width 1 + :border-color colors/gray-border}) + +(def twelve-words-columns-separator + {:width 1 + :background-color colors/gray-border}) + +(def enter-word-container + {:flex 1 + :padding 16}) + +(def enter-word-row + {:flex-direction :row}) + +(def enter-word-label + {:font-size 14 + :line-height 21 + :letter-spacing -0.2}) + +(def enter-word-n + {:margin-left 8 + :font-size 14 + :line-height 21 + :letter-spacing -0.2 + :color colors/gray}) + +(def enter-word-n-description + {:font-size 14 + :line-height 21 + :letter-spacing -0.2 + :color colors/gray}) + +(def finish-container + {:flex 1 + :padding-horizontal 24 + :align-items :center}) + +(def finish-logo-container + {:flex 1 + :align-items :center + :justify-content :center}) + +(def ok-icon + {:color :white + :width 41 + :height 41}) + +(def finish-label + {:font-size 22 + :font-weight :bold + :line-height 30 + :letter-spacing -0.3 + :text-align :center}) + +(def finish-description + {:margin-top 8 + :font-size 14 + :line-height 20 + :text-align :center + :color colors/gray}) + +(def finish-button + {:flex-direction :row + :margin-top 16 + :margin-bottom 32}) + +(def backup-seed + {:font-weight :bold + :font-size 15 + :letter-spacing -0.2 + :text-align :center}) + +(def step-n + {:margin-top 5 + :font-size 14 + :text-align :center + :color colors/gray}) \ No newline at end of file diff --git a/src/status_im/ui/screens/profile/seed/views.cljs b/src/status_im/ui/screens/profile/seed/views.cljs new file mode 100644 index 0000000000..28c05beeff --- /dev/null +++ b/src/status_im/ui/screens/profile/seed/views.cljs @@ -0,0 +1,146 @@ +(ns status-im.ui.screens.profile.seed.views + (:require-macros [status-im.utils.views :refer [defview letsubs]]) + (:require [status-im.ui.components.react :as react] + [status-im.ui.components.status-bar.view :as status-bar] + [status-im.ui.components.toolbar.view :as toolbar] + [status-im.ui.components.toolbar.actions :as actions] + [status-im.ui.components.colors :as colors] + [status-im.react-native.resources :as resources] + [status-im.ui.components.common.common :as components.common] + [re-frame.core :as re-frame] + [status-im.ui.components.text-input.view :as text-input] + [status-im.ui.components.icons.vector-icons :as icons] + [status-im.ui.components.common.common :as components.common] + [status-im.ui.components.common.styles :as components.common.styles] + [clojure.string :as string] + [status-im.utils.utils :as utils] + [status-im.ui.screens.profile.seed.styles :as styles] + [status-im.i18n :as i18n] + [status-im.ui.components.styles :as common.styles])) + +(def steps-numbers + {:intro 1 + :12-words 1 + :first-word 2 + :second-word 3 + :finish 3}) + +(defn step-back [step] + (case step + (:intro :12-words) (re-frame/dispatch [:navigate-back]) + :first-word (re-frame/dispatch [:my-profile/set-step :12-words]) + :second-word (re-frame/dispatch [:my-profile/set-step :first-word]))) + +(defn intro [] + [react/view {:style styles/intro-container} + [components.common/image-contain {:style styles/intro-image} + (:lock resources/ui)] + [react/text {:style styles/intro-text} + (i18n/label :t/your-data-belongs-to-you)] + [react/text {:style styles/intro-description} + (i18n/label :t/your-data-belongs-to-you-description)] + [components.common/button {:style styles/intro-button + :on-press #(re-frame/dispatch [:set-in [:my-profile/seed :step] :12-words]) + :label (i18n/label :t/ok-continue)}]]) + +(defn six-words [words] + [react/view {:style styles/six-words-container} + (for [[i word] words] + ^{:key (str "word" i)} + [react/view + [react/view {:style styles/six-word-row} + [react/text {:style styles/six-word-num} + (inc i)] + [react/text {:style styles/six-words-word} + word]] + (when (not= i (first (last words))) + [react/view {:style styles/six-words-separator}])])]) + +(defn twelve-words [{:keys [mnemonic]}] + (let [mnemonic-vec (vec (map-indexed vector (clojure.string/split mnemonic #" ")))] + [react/view {:style styles/twelve-words-container} + [react/text {:style styles/twelve-words-label} + (i18n/label :t/your-seed-phrase)] + [react/view {:style styles/twelve-words-columns} + [six-words (subvec mnemonic-vec 0 6)] + [react/view {:style styles/twelve-words-columns-separator}] + [six-words (subvec mnemonic-vec 6 12)]] + [react/text {:style styles/twelve-words-description} + (i18n/label :t/your-seed-phrase-description)] + [react/view styles/twelve-words-spacer] + [react/view styles/twelve-words-button-container + [components.common/bottom-button + {:forward? true + :on-press #(re-frame/dispatch [:my-profile/enter-two-random-words])}]]])) + +(defview input [error] + [text-input/text-input-with-label + {:placeholder (i18n/label :t/enter-word) + :auto-focus true + :on-change-text #(re-frame/dispatch [:set-in [:my-profile/seed :word] %]) + :error error}]) + +(defn enter-word [step [idx word] error entered-word] + ^{:key word} + [react/view {:style styles/enter-word-container} + [react/view {:style styles/enter-word-row} + [react/text {:style styles/enter-word-label} + (i18n/label :t/check-your-seed)] + [react/text {:style styles/enter-word-n} + (i18n/label :t/word-n {:number (inc idx)})]] + [input error] + [react/text {:style styles/enter-word-n-description} + (i18n/label :t/word-n-description {:number (inc idx)})] + [react/view styles/twelve-words-spacer] + [react/view styles/twelve-words-button-container + [components.common/bottom-button + {:forward? (not= :second-word step) + :label (when (= :second-word step) (i18n/label :t/done)) + :disabled? (string/blank? entered-word) + :on-press (fn [_] + (cond (not= word entered-word) + (re-frame/dispatch [:set-in [:my-profile/seed :error] (i18n/label :t/wrong-word)]) + + (= :first-word step) + (re-frame/dispatch [:my-profile/set-step :second-word]) + + :else + (utils/show-question + (i18n/label :t/are-you-sure?) + (i18n/label :t/are-you-sure-description) + #(re-frame/dispatch [:my-profile/finish]))))}]]]) + +(defn finish [] + [react/view {:style styles/finish-container} + [react/view {:style styles/finish-logo-container} + [react/view {:style (components.common.styles/logo-container 80 true)} + [icons/icon :icons/ok styles/ok-icon]]] + [react/text {:style styles/finish-label} + (i18n/label :t/you-are-all-set)] + [react/text {:style styles/finish-description} + (i18n/label :t/you-are-all-set-description)] + [components.common/button {:style styles/finish-button + :on-press #(re-frame/dispatch [:navigate-back]) + :label (i18n/label :t/ok-got-it)}]]) + +(defview backup-seed [] + (letsubs [current-account [:get-current-account] + {:keys [step first-word second-word error word]} [:get :my-profile/seed]] + [react/keyboard-avoiding-view {:style common.styles/flex} + [status-bar/status-bar] + [toolbar/toolbar + nil + (when-not (#{:finish} step) + (toolbar/nav-button (actions/back #(step-back step)))) + [react/view + [react/text {:style styles/backup-seed} + (i18n/label :t/backup-seed-phrase)] + [react/text {:style styles/step-n} + (i18n/label :t/step-i-of-n {:step (steps-numbers step) :number 3})]]] + [components.common/separator] + (case step + :intro [intro] + :12-words [twelve-words current-account] + :first-word [enter-word step first-word error word] + :second-word [enter-word step second-word error word] + :finish [finish])])) \ No newline at end of file diff --git a/src/status_im/ui/screens/profile/subs.cljs b/src/status_im/ui/screens/profile/subs.cljs new file mode 100644 index 0000000000..47a795eb4d --- /dev/null +++ b/src/status_im/ui/screens/profile/subs.cljs @@ -0,0 +1,9 @@ +(ns status-im.ui.screens.profile.subs + (:require [re-frame.core :refer [reg-sub]] + [clojure.string :as string])) + +(reg-sub + :get-profile-unread-messages-number + :<- [:get-current-account] + (fn [{:keys [seed-backed-up? mnemonic]}] + (if (or seed-backed-up? (string/blank? mnemonic)) 0 1))) \ No newline at end of file diff --git a/src/status_im/ui/screens/profile/user/styles.cljs b/src/status_im/ui/screens/profile/user/styles.cljs index a74b8a41dc..81e5024185 100644 --- a/src/status_im/ui/screens/profile/user/styles.cljs +++ b/src/status_im/ui/screens/profile/user/styles.cljs @@ -45,3 +45,27 @@ (defstyle my-profile-info-container {:background-color colors/white}) + +(def advanced-button + {:margin-top 16 + :margin-bottom 12}) + +(def advanced-button-container + {:align-items :center + :justify-content :center}) + +(def advanced-button-container-background + {:padding-left 16 + :padding-right 12 + :padding-vertical 6 + :border-radius 18 + :background-color (colors/alpha colors/blue 0.1)}) + +(def advanced-button-row + {:flex-direction :row + :align-items :center}) + +(def advanced-button-label + {:font-size 15 + :letter-spacing -0.2 + :color colors/blue}) \ No newline at end of file diff --git a/src/status_im/ui/screens/profile/user/views.cljs b/src/status_im/ui/screens/profile/user/views.cljs index f5820f69af..a2a0433f40 100644 --- a/src/status_im/ui/screens/profile/user/views.cljs +++ b/src/status_im/ui/screens/profile/user/views.cljs @@ -11,15 +11,16 @@ [status-im.ui.components.qr-code-viewer.views :as qr-code-viewer] [status-im.ui.components.react :as react] [status-im.ui.components.status-bar.view :as status-bar] - [status-im.ui.components.styles :as components.styles] [status-im.ui.components.toolbar.view :as toolbar] [status-im.ui.screens.profile.components.views :as profile.components] [status-im.ui.screens.profile.components.styles :as profile.components.styles] [status-im.ui.screens.profile.user.styles :as styles] [status-im.utils.config :as config] [status-im.utils.platform :as platform] - [status-im.utils.utils :as utils])) - + [status-im.utils.utils :as utils] + [status-im.ui.components.icons.vector-icons :as icons] + [status-im.ui.components.common.common :as components.common] + [clojure.string :as string])) (defn my-profile-toolbar [] [toolbar/toolbar {} @@ -87,22 +88,24 @@ :accessibility-label :share-my-contact-code-button} [vector-icons/icon :icons/qr {:color colors/blue}]]]]) -(defn my-profile-settings [{:keys [network networks]}] +(defn my-profile-settings [{:keys [seed-backed-up? mnemonic]}] [react/view [profile.components/settings-title (i18n/label :t/settings)] - [profile.components/settings-item :t/main-currency "USD" #() false] + [profile.components/settings-item {:label-kw :t/main-currency + :value "USD" + :active? false}] [profile.components/settings-item-separator] - [profile.components/settings-item :t/notifications "" #(.openURL react/linking "app-settings://notification/status-im") true + [profile.components/settings-item {:label-kw :t/notifications + :action-fn #(.openURL react/linking "app-settings://notification/status-im")} :notifications-button] [profile.components/settings-item-separator] - [profile.components/settings-item :t/network (get-in networks [network :name]) - #(re-frame/dispatch [:navigate-to :network-settings]) true :network-button] - (when config/offline-inbox-enabled? - [profile.components/settings-item-separator]) - (when config/offline-inbox-enabled? - [profile.components/settings-item :t/offline-messaging-settings "" - #(re-frame/dispatch [:navigate-to :offline-messaging-settings]) true - :offline-messages-settings-button])]) + (when (and (not seed-backed-up?) (not (string/blank? mnemonic))) + [react/view + [profile.components/settings-item + {:label-kw :t/backup-your-seed + :action-fn #(re-frame/dispatch [:navigate-to :backup-seed]) + :icon-content [components.common/counter {:size 22} 1]}] + [profile.components/settings-item-separator]])]) (defn navigate-to-accounts [] ;; TODO(rasom): probably not the best place for this call @@ -124,6 +127,36 @@ :font (if platform/android? :medium :default)} (i18n/label :t/logout)]]]]) +(defview advanced [{:keys [network networks dev-mode?]}] + (letsubs [advanced? [:get :my-profile/advanced?]] + [react/view + [react/touchable-highlight {:on-press #(re-frame/dispatch [:set :my-profile/advanced? (not advanced?)]) + :style styles/advanced-button} + [react/view {:style styles/advanced-button-container} + [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)] + [icons/icon (if advanced? :icons/up :icons/down) {:color colors/blue}]]]]] + (when advanced? + [react/view + [profile.components/settings-item + {:label-kw :t/network + :value (get-in networks [network :name]) + :action-fn #(re-frame/dispatch [:navigate-to :network-settings]) + :accessibility-label :network-button}] + (when config/offline-inbox-enabled? + [profile.components/settings-item-separator]) + (when config/offline-inbox-enabled? + [profile.components/settings-item + {:label-kw :t/offline-messaging-settings + :action-fn #(re-frame/dispatch [:navigate-to :offline-messaging-settings]) + :accessibility-label :offline-messages-settings-button}]) + [profile.components/settings-item-separator] + [profile.components/settings-switch-item + {:label-kw :t/dev-mode + :value dev-mode? + :action-fn #(re-frame/dispatch [:switch-dev-mode %])}]])])) (defview my-profile [] (letsubs [{:keys [public-key] :as current-account} [:get-current-account] @@ -141,4 +174,5 @@ [share-contact-code current-account public-key]] [react/view styles/my-profile-info-container [my-profile-settings current-account]] - [logout]]]))) + [logout] + [advanced shown-account]]]))) \ No newline at end of file diff --git a/src/status_im/ui/screens/subs.cljs b/src/status_im/ui/screens/subs.cljs index 645d7a3765..47256a87e5 100644 --- a/src/status_im/ui/screens/subs.cljs +++ b/src/status_im/ui/screens/subs.cljs @@ -16,7 +16,8 @@ status-im.ui.screens.network-settings.subs status-im.ui.screens.browser.subs status-im.bots.subs - status-im.ui.screens.add-new.new-chat.subs)) + status-im.ui.screens.add-new.new-chat.subs + status-im.ui.screens.profile.subs)) (reg-sub :get (fn [db [_ k]] diff --git a/src/status_im/ui/screens/views.cljs b/src/status_im/ui/screens/views.cljs index 385dd8a291..e7194610d1 100644 --- a/src/status_im/ui/screens/views.cljs +++ b/src/status_im/ui/screens/views.cljs @@ -53,6 +53,7 @@ [status-im.ui.screens.intro.views :refer [intro]] [status-im.ui.screens.accounts.create.views :refer [create-account]] [status-im.ui.screens.usage-data.views :refer [usage-data]] + [status-im.ui.screens.profile.seed.views :refer [backup-seed]] [status-im.utils.config :as config])) ;;; defines hierarchy of views, when parent screen is opened children screens @@ -170,6 +171,7 @@ :recipient-qr-code recipient-qr-code :contact-code contact-code :profile-qr-viewer profile.user/qr-viewer + :backup-seed backup-seed [react/view [react/text (str "Unknown view: " view-id)]]) main-screen-view (create-main-screen-view view-id)] [main-screen-view common-styles/flex diff --git a/src/status_im/ui/screens/wallet/subs.cljs b/src/status_im/ui/screens/wallet/subs.cljs index 20722cee92..076bd110c3 100644 --- a/src/status_im/ui/screens/wallet/subs.cljs +++ b/src/status_im/ui/screens/wallet/subs.cljs @@ -44,3 +44,7 @@ (fn [wallet] (or (get-in wallet [:errors :balance-update]) (get-in wallet [:errors :prices-update])))) + +(reg-sub :get-wallet-unread-messages-number + (fn [db] + 0))