implemented profile onboarding

Signed-off-by: Andrey Shovkoplyas <motor4ik@gmail.com>
This commit is contained in:
Andrey Shovkoplyas 2018-03-16 19:55:12 +03:00
parent 9533d158f7
commit 946a44d257
No known key found for this signature in database
GPG Key ID: EAAB7C8622D860A4
40 changed files with 1096 additions and 540 deletions

View File

@ -1,3 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
<path opacity=".4" fill="white" d="M12,14.8284271 L8.46236609,11.2907932 C8.07770436,10.9061315 7.44077682,10.9023689 7.05025253,11.2928932 C6.65700558,11.6861402 6.65878803,12.3156423 7.04815252,12.7050068 L11.2949932,16.9518475 C11.4869793,17.1438336 11.741806,17.240936 11.9974742,17.2414902 C12.2565137,17.2439683 12.5106708,17.1461835 12.7050068,16.9518475 L16.9518475,12.7050068 C17.3365092,12.320345 17.3402718,11.6834175 16.9497475,11.2928932 C16.5565005,10.8996463 15.9269984,10.9014287 15.5376339,11.2907932 L12,14.8284271 Z" />
<path fill="" d="M12,14.8284271 L8.46236609,11.2907932 C8.07770436,10.9061315 7.44077682,10.9023689 7.05025253,11.2928932 C6.65700558,11.6861402 6.65878803,12.3156423 7.04815252,12.7050068 L11.2949932,16.9518475 C11.4869793,17.1438336 11.741806,17.240936 11.9974742,17.2414902 C12.2565137,17.2439683 12.5106708,17.1461835 12.7050068,16.9518475 L16.9518475,12.7050068 C17.3365092,12.320345 17.3402718,11.6834175 16.9497475,11.2928932 C16.5565005,10.8996463 15.9269984,10.9014287 15.5376339,11.2907932 L12,14.8284271 Z" />
</svg>

Before

Width:  |  Height:  |  Size: 631 B

After

Width:  |  Height:  |  Size: 613 B

View File

@ -1,3 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" x="12" y="12" width="24" height="24" viewBox="0 0 24 24">
<path opacity=".4" fill="white" d="M12,9 C11.7440777,9 11.4881554,9.09763107 11.2928932,9.29289322 L7.29289322,13.2928932 C6.90236893,13.6834175 6.90236893,14.3165825 7.29289322,14.7071068 C7.68341751,15.0976311 8.31658249,15.0976311 8.70710678,14.7071068 L12,11.4142136 L15.2928932,14.7071068 C15.6834175,15.0976311 16.3165825,15.0976311 16.7071068,14.7071068 C17.0976311,14.3165825 17.0976311,13.6834175 16.7071068,13.2928932 L12.7071068,9.29289322 C12.5118446,9.09763107 12.2559223,9 12,9 Z" />
<path fill="" d="M12,9 C11.7440777,9 11.4881554,9.09763107 11.2928932,9.29289322 L7.29289322,13.2928932 C6.90236893,13.6834175 6.90236893,14.3165825 7.29289322,14.7071068 C7.68341751,15.0976311 8.31658249,15.0976311 8.70710678,14.7071068 L12,11.4142136 L15.2928932,14.7071068 C15.6834175,15.0976311 16.3165825,15.0976311 16.7071068,14.7071068 C17.0976311,14.3165825 17.0976311,13.6834175 16.7071068,13.2928932 L12.7071068,9.29289322 C12.5118446,9.09763107 12.2559223,9 12,9 Z" />
</svg>

Before

Width:  |  Height:  |  Size: 606 B

After

Width:  |  Height:  |  Size: 588 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 KiB

View File

@ -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}

View File

@ -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)))))

View File

@ -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,6 +69,7 @@
accounts [:get-accounts]
contact [:get-in [:contacts/contacts @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)
@ -84,4 +87,4 @@
:public? public?
:sync-state sync-state}]
[last-activity {:online-text (online-text contact chat-id)
:sync-state sync-state}])]))
:sync-state sync-state}])]]))

View File

@ -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}])

View File

@ -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}}})

View File

@ -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))

View File

@ -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")})
:welcome-image (js/require "./resources/images/ui/welcome-image.png")
:lock (js/require "./resources/images/ui/lock.png")})

View File

@ -131,6 +131,27 @@
:send-message "Send message"
:testnet-text "Youre on the {{testnet}} Testnet. Do not send real ETH or SNT to your address"
:mainnet-text "Youre 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 cant help get it back if you loose it"
:your-data-belongs-to-you-description "This means Status cant 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 "Youre 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"
@ -309,7 +330,6 @@
: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"
@ -434,8 +454,6 @@
: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"

View File

@ -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

View File

@ -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}]]))

View File

@ -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})

View File

@ -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"))

View File

@ -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})

View File

@ -6,8 +6,9 @@
(defn text-input-with-label [{:keys [label error style height] :as props}]
[react/view
(when label
[react/text {:style styles/label}
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)])])

View File

@ -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})

View File

@ -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}]

View File

@ -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)))

View File

@ -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
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)
(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]))))))
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})))

View File

@ -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)

View File

@ -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})

View File

@ -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,6 +15,7 @@
(views/defview toolbar-content-dapp [contact-identity]
(views/letsubs [contact [:contact-by-identity contact-identity]]
[react/view
[react/view styles/toolbar-content-dapp
[chat-icon.screen/dapp-icon-browser contact 36]
[react/view styles/dapp-name
@ -25,10 +25,11 @@
:accessibility-label :dapp-name-text}
(:name contact)]
[react/text {:style styles/dapp-text}
(i18n/label :t/dapp)]]]))
(i18n/label :t/dapp)]]]]))
(defn toolbar-content [{:keys [url] :as browser}]
(let [url-text (atom nil)]
[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)])
@ -41,7 +42,7 @@
;;TODO .reload doesn't work, implement later
#_[react/touchable-highlight {:on-press #(when @webview (.reload @webview))}
[react/view
[vector-icons/icon :icons/refresh]]]]))
[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 %)

View File

@ -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

View File

@ -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]]

View File

@ -28,3 +28,10 @@
(defn tab-icon [active?]
{:color (if active? styles/color-blue4 styles/color-gray4)})
(def counter-container
{:position :absolute
:top 4})
(def counter
{:margin-left 18})

View File

@ -9,38 +9,47 @@
[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]
(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?
@ -48,12 +57,12 @@
accessibility-label
(assoc :accessibility-label accessibility-label))
[react/view
[content active?]]])
[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]]

View File

@ -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])]
(if icon-content
icon-content
(when active?
[vector-icons/icon :icons/forward {:color colors/gray}])]])
[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?)}]])

View File

@ -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?]))

View File

@ -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}))))

View File

@ -9,3 +9,6 @@
:source source
:value value})))
(defmethod navigation/preload-data! :backup-seed
[db]
(assoc db :my-profile/seed {:step :intro}))

View File

@ -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})

View File

@ -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])]))

View File

@ -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)))

View File

@ -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})

View File

@ -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]]])))

View File

@ -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]]

View File

@ -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

View File

@ -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))