[#8585] Add watch only account

This commit is contained in:
Andrey Shovkoplyas 2019-11-18 10:29:30 +01:00
parent 15d9e7be86
commit 4bffdede81
No known key found for this signature in database
GPG Key ID: EAAB7C8622D860A4
18 changed files with 213 additions and 117 deletions

View File

@ -181,7 +181,7 @@
(reg-root-key-sub :intro-wizard-state :intro-wizard)
(reg-root-key-sub :popover/popover :popover/popover)
(reg-root-key-sub :generate-account :generate-account)
(reg-root-key-sub :add-account :add-account)
(reg-root-key-sub :keycard :hardwallet)
@ -511,6 +511,12 @@
(fn [multiaccounts]
(> (count multiaccounts) 1)))
(re-frame/reg-sub
:accounts-without-watch-only
:<- [:multiaccount]
(fn [macc]
(filter #(not= (:type %) :watch) (:accounts macc))))
;;CHAT ==============================================================================================================
(re-frame/reg-sub

View File

@ -1,12 +1,15 @@
(ns status-im.ui.components.toolbar
(:require [status-im.ui.components.react :as react]
[status-im.ui.components.button :as button]))
[status-im.ui.components.button :as button]
[status-im.ui.components.colors :as colors]))
(defn toolbar [{:keys [center left right]}]
(defn toolbar [{:keys [center left right show-border?]}]
(if center
[react/view {:height 52 :align-items :center :justify-content :center}
[react/view (merge {:height 52 :align-items :center :justify-content :center}
(when show-border? {:border-top-width 1 :border-top-color colors/gray-lighter}))
[button/button center]]
[react/view {:height 52 :align-items :center :flex-direction :row}
[react/view (merge {:height 52 :align-items :center :flex-direction :row}
(when show-border? {:border-top-width 1 :border-top-color colors/gray-lighter}))
(when left
[button/button left])
[react/view {:flex 1}]

View File

@ -98,7 +98,7 @@
(views/defview select-account []
(views/letsubs [height [:dimensions/window-height]
{:keys [accounts]} [:multiaccount]
accounts [:accounts-without-watch-only]
{:keys [name color] :as dapps-account} [:dapps-account]]
[react/view {:position :absolute
:z-index 2

View File

@ -86,7 +86,7 @@
(views/defview navigation [url can-go-back? can-go-forward? dapps-account]
(views/letsubs [height [:dimensions/window-height]
{:keys [accounts]} [:multiaccount]]
accounts [:accounts-without-watch-only]]
[react/view styles/navbar
[react/touchable-highlight {:on-press #(re-frame/dispatch [:browser.ui/previous-page-button-pressed])
:disabled (not can-go-back?)

View File

@ -311,4 +311,4 @@
::collectibles
:registry/registry
::two-pane-ui-enabled?
::generate-account]))
::add-account]))

View File

@ -14,7 +14,6 @@
[status-im.ui.components.list.views :as list]
[status-im.ui.components.radio :as radio]
[status-im.ui.components.react :as react]
[status-im.ui.components.status-bar.view :as status-bar]
[status-im.ui.components.toolbar.actions :as actions]
[status-im.ui.components.toolbar.view :as toolbar]
[status-im.ui.screens.chat.message.message :as message]

View File

@ -545,14 +545,11 @@
(defview wizard-enter-phrase []
(letsubs [wizard-state [:intro-wizard/enter-phrase]]
[react/keyboard-avoiding-view {:style {:flex 1}}
[toolbar/toolbar
{:style {:border-bottom-width 0
:margin-top 16}}
[toolbar/toolbar {:style {:border-bottom-width 0}}
(toolbar/nav-button
(actions/back #(re-frame/dispatch [:intro-wizard/navigate-back])))
nil]
[react/view {:style {:flex 1
:justify-content :space-between}}
[react/view {:style {:flex 1 :justify-content :space-between}}
[top-bar {:step :enter-phrase}]
[enter-phrase wizard-state]
[bottom-bar (merge {:step :enter-phrase

View File

@ -8,6 +8,7 @@
:new-public-chat :default
:wallet-account :default
:add-new-account :default
:add-watch-account :default
:add-new-account-password :default
:about-app :default
:help-center :default

View File

@ -182,6 +182,7 @@
:welcome [:modal home/welcome]
:keycard-welcome keycard/welcome
:add-new-account add-account/add-account
:add-watch-account add-account/add-watch-account
:add-new-account-password add-account/password
:account-added account-settings/account-added
:account-settings account-settings/account-settings})

View File

@ -5,6 +5,7 @@
:screens [:wallet
:wallet-account
:add-new-account
:add-watch-account
:add-new-account-password
:account-added
:account-settings

View File

@ -39,7 +39,7 @@
[icons/icon icon {:color colors/white}]
[react/text {:style {:margin-left 8 :color colors/white}} label]]]])
(views/defview account-card [{:keys [address color] :as account}]
(views/defview account-card [{:keys [address color type] :as account}]
(views/letsubs [currency [:wallet/currency]
portfolio-value [:account-portfolio-value address]
window-width [:dimensions/window-width]]
@ -62,10 +62,13 @@
:accessibility-label :share-wallet-address-icon}]]]
[react/view {:height 52 :background-color colors/black-transparent-20
:border-bottom-right-radius 8 :border-bottom-left-radius 8 :flex-direction :row}
[button
(i18n/label :t/wallet-send)
:main-icons/send
#(re-frame/dispatch [:wallet/prepare-transaction-from-wallet account])]
(if (= type :watch)
[react/view {:flex 1 :align-items :center :justify-content :center}
[react/text {:style {:margin-left 8 :color colors/white}} "Watch-only"]]
[button
(i18n/label :t/wallet-send)
:main-icons/send
#(re-frame/dispatch [:wallet/prepare-transaction-from-wallet account])])
[react/view {:style styles/divider}]
[button
(i18n/label :t/receive)

View File

@ -33,7 +33,7 @@
:type :secondary}}]]))
(defview account-added []
(letsubs [{:keys [account]} [:generate-account]]
(letsubs [{:keys [account]} [:add-account]]
[react/keyboard-avoiding-view {:flex 1}
[react/scroll-view {:keyboard-should-persist-taps :handled
:style {:margin-top 70 :flex 1}}
@ -51,20 +51,22 @@
{:label (i18n/label :t/account-name)
:auto-focus false
:default-value (:name account)
:on-change-text #(re-frame/dispatch [:set-in [:generate-account :account :name] %])}]
:placeholder (i18n/label :t/account-name)
:on-change-text #(re-frame/dispatch [:set-in [:add-account :account :name] %])}]
[react/text {:style {:margin-top 30}} (i18n/label :t/account-color)]
[react/touchable-highlight
{:on-press #(re-frame/dispatch [:show-popover
{:view [colors-popover (:color account)
(fn [new-color]
(re-frame/dispatch [:set-in [:generate-account :account :color] new-color])
(re-frame/dispatch [:set-in [:add-account :account :color] new-color])
(re-frame/dispatch [:hide-popover]))]
:style {:max-height "60%"}}])}
[react/view {:height 52 :margin-top 12 :background-color (:color account) :border-radius 8
:align-items :flex-end :justify-content :center :padding-right 12}
[icons/icon :main-icons/dropdown {:color colors/white}]]]]]
[toolbar/toolbar
{:right {:type :next
{:show-border? true
:right {:type :next
:label (i18n/label :t/finish)
:on-press #(re-frame/dispatch [:wallet.accounts/save-generated-account])
:disabled? (string/blank? (:name account))}}]]))
@ -77,7 +79,7 @@
value)])
(defview account-settings []
(letsubs [{:keys [address color path] :as account} [:current-account]
(letsubs [{:keys [address color path type] :as account} [:current-account]
new-account (reagent/atom nil)]
[react/keyboard-avoiding-view {:flex 1}
[topbar/toolbar {}
@ -109,13 +111,15 @@
:border-radius 8
:align-items :flex-end :justify-content :center :padding-right 12}
[icons/icon :main-icons/dropdown {:color colors/white}]]]
[property (i18n/label :t/type) (i18n/label :t/on-status-tree)]
[property (i18n/label :t/type) (case type :watch "Watch-only" (i18n/label :t/on-status-tree))]
[property (i18n/label :t/wallet-address)
[copyable-text/copyable-text-view
{:copied-text address}
[react/text {:style {:margin-top 6 :font-family "monospace"}} address]]]
[property (i18n/label :t/derivation-path)
[copyable-text/copyable-text-view
{:copied-text path}
[react/text {:style {:margin-top 6 :font-family "monospace"}} path]]]
[property (i18n/label :t/storage) (i18n/label :t/this-device)]]]]))
(when-not (= type :watch)
[property (i18n/label :t/derivation-path)
[copyable-text/copyable-text-view
{:copied-text path}
[react/text {:style {:margin-top 6 :font-family "monospace"}} path]]])
(when-not (= type :watch)
[property (i18n/label :t/storage) (i18n/label :t/this-device)])]]]))

View File

@ -35,14 +35,15 @@
:accessibility-label :wallet-backup-recovery-title
:on-press #(hide-sheet-and-dispatch [:navigate-to :backup-seed])}])]))
(defn send-receive [account]
(defn send-receive [account type]
[react/view
[list-item/list-item
{:theme :action
:title :t/wallet-send
:icon :main-icons/send
:accessibility-label :send-transaction-button
:on-press #(hide-sheet-and-dispatch [:wallet/prepare-transaction-from-wallet account])}]
(when-not (= type :watch)
[list-item/list-item
{:theme :action
:title :t/wallet-send
:icon :main-icons/send
:accessibility-label :send-transaction-button
:on-press #(hide-sheet-and-dispatch [:wallet/prepare-transaction-from-wallet account])}])
[list-item/list-item
{:theme :action
:title :t/receive
@ -63,7 +64,7 @@
{:theme :action
:title :t/add-a-watch-account
:icon :main-icons/watch
:disabled? true}]])
:on-press #(hide-sheet-and-dispatch [:wallet.accounts/start-adding-new-account {:type :watch}])}]])
(defn account-settings []
[react/view

View File

@ -17,16 +17,17 @@
(def state (reagent/atom {:tab :assets}))
(views/defview account-card [{:keys [name color address] :as account}]
(views/defview account-card [{:keys [name color address type] :as account}]
(views/letsubs [currency [:wallet/currency]
portfolio-value [:account-portfolio-value address]]
[react/touchable-highlight {:on-press #(re-frame/dispatch [:navigate-to :wallet-account account])
:on-long-press #(re-frame/dispatch [:bottom-sheet/show-sheet
{:content (fn [] [sheets/send-receive account])
:content-height 130}])}
[react/touchable-highlight
{:on-press #(re-frame/dispatch [:navigate-to :wallet-account account])
:on-long-press #(re-frame/dispatch [:bottom-sheet/show-sheet
{:content (fn [] [sheets/send-receive account type])
:content-height 130}])}
[react/view {:style (styles/card color)}
[react/view {:flex-direction :row :align-items :center :justify-content :space-between}
[react/nested-text {:style {:color colors/white-transparent :font-weight "500"}}
[react/nested-text {:style {:color colors/white-transparent :font-weight "500" :flex-shrink 1}}
[{:style {:color colors/white}} portfolio-value]
" "
(:code currency)]

View File

@ -1,26 +1,28 @@
(ns status-im.ui.screens.wallet.add-new.views
(:require-macros [status-im.utils.views :refer [defview letsubs]])
(:require [status-im.ui.components.react :as react]
[status-im.ui.components.toolbar.view :as toolbar]
[status-im.ui.components.toolbar.view :as topbar]
[status-im.i18n :as i18n]
[re-frame.core :as re-frame]
[status-im.ui.components.colors :as colors]
[status-im.ui.components.list-item.views :as list-item]
[status-im.ui.components.common.common :as components.common]
[reagent.core :as reagent]
[cljs.spec.alpha :as spec]
[status-im.multiaccounts.db :as multiaccounts.db]))
[status-im.multiaccounts.db :as multiaccounts.db]
[status-im.ui.components.toolbar :as toolbar]
[status-im.ui.components.styles :as components.styles]
[status-im.ethereum.core :as ethereum]))
(defn add-account []
[react/view {:flex 1}
[toolbar/toolbar {:transparent? true} toolbar/default-nav-back nil]
[topbar/simple-toolbar]
[react/scroll-view {:keyboard-should-persist-taps :handled
:style {:flex 1}}
[react/view {:align-items :center :padding-horizontal 40}
[react/text {:style {:typography :header :margin-top 16}} (i18n/label :t/add-an-account)]
[react/view {:align-items :center :padding-horizontal 40 :margin-bottom 52}
[react/text {:style {:typography :header :margin-top 16}}
(i18n/label :t/add-an-account)]
[react/text {:style {:color colors/gray :text-align :center :margin-top 16 :line-height 22}}
(i18n/label :t/add-account-description)]]
[react/view {:height 52}]
[list-item/list-item
{:type :section-header
:title :t/default}]
@ -29,40 +31,83 @@
:theme :action
:icon :main-icons/add
:accessories [:chevron]
:on-press
#(re-frame/dispatch
[:navigate-to :add-new-account-password])}]]])
:on-press #(re-frame/dispatch [:wallet.accounts/start-adding-new-account {:type :generate}])}]
[list-item/list-item
{:type :section-header
:container-margin-top 24
:title "Advanced"}]
[list-item/list-item
{:title "Enter a seed phrase"
:theme :action
:icon :main-icons/add
:accessories [:chevron]
:disabled? true
:on-press #(re-frame/dispatch [:wallet.accounts/start-adding-new-account {:type :seed}])}]
[list-item/list-item
{:title "Enter a private key"
:theme :action
:icon :main-icons/add
:accessories [:chevron]
:disabled? true
:on-press #(re-frame/dispatch [:wallet.accounts/start-adding-new-account {:type :key}])}]]])
(def input-container
{:flex-direction :row
:align-items :center
:border-radius components.styles/border-radius
:height 52
:margin 16
:padding-horizontal 16
:background-color colors/gray-lighter})
(defview add-watch-account []
(letsubs [{:keys [address]} [:add-account]]
[react/keyboard-avoiding-view {:flex 1}
[topbar/simple-toolbar]
[react/view {:flex 1
:justify-content :space-between
:align-items :center :margin-horizontal 16}
[react/view
[react/text {:style {:typography :header :margin-top 16}} "Add a watch-only address"]
[react/text {:style {:color colors/gray :text-align :center :margin-vertical 16}}
"Enter the address to watch"]]
[react/view {:align-items :center :flex 1 :flex-direction :row}
[react/text-input {:auto-focus true
:multiline true
:text-align :center
:placeholder "Enter address"
:style {:typography :header :flex 1}
:on-change-text #(re-frame/dispatch [:set-in [:add-account :address] %])}]]]
[toolbar/toolbar
{:show-border? true
:right {:type :next
:label "Next"
:on-press #(re-frame/dispatch [:wallet.accounts/add-watch-account])
:disabled? (not (ethereum/address? address))}}]]))
(defview password []
(letsubs [{:keys [error]} [:generate-account]
(letsubs [{:keys [error]} [:add-account]
entered-password (reagent/atom "")]
[react/keyboard-avoiding-view {:style {:flex 1}}
[toolbar/toolbar {:transparent? true} toolbar/default-nav-back nil]
[react/view {:flex 1}
[react/view {:style {:flex 1
:justify-content :space-between
:align-items :center :margin-horizontal 16}}
[react/text {:style {:typography :header :margin-top 16}} (i18n/label :t/enter-your-password)]
[react/view {:style {:justify-content :center :flex 1}}
[react/text-input {:secure-text-entry true
:auto-focus true
:text-align :center
:placeholder ""
:style {:typography :header}
:on-change-text #(reset! entered-password %)}]
(when error
[react/text {:style {:text-align :center :color colors/red :margin-top 76}} error])]
[react/text {:style {:color colors/gray :text-align :center :margin-bottom 16}}
(i18n/label :t/to-encrypt-enter-password)]]
[react/view {:style {:flex-direction :row
:justify-content :flex-end
:align-self :stretch
:padding-vertical 16
:border-top-width 1
:border-top-color colors/gray-lighter
:padding-right 12}}
[components.common/bottom-button {:label (i18n/label :t/generate-account)
:on-press #(re-frame/dispatch
[:wallet.accounts/generate-new-account @entered-password])
:disabled? (not (spec/valid? ::multiaccounts.db/password @entered-password))
:forward? true}]]]]))
[topbar/simple-toolbar]
[react/view {:flex 1
:justify-content :space-between
:align-items :center :margin-horizontal 16}
[react/text {:style {:typography :header :margin-top 16}} (i18n/label :t/enter-your-password)]
[react/view {:justify-content :center :flex 1}
[react/text-input {:secure-text-entry true
:auto-focus true
:text-align :center
:placeholder ""
:style {:typography :header}
:on-change-text #(reset! entered-password %)}]
(when error
[react/text {:style {:text-align :center :color colors/red :margin-top 76}} error])]
[react/text {:style {:color colors/gray :text-align :center :margin-bottom 16}}
(i18n/label :t/to-encrypt-enter-password)]]
[toolbar/toolbar
{:show-border? true
:right {:type :next
:label :t/generate-account
:on-press #(re-frame/dispatch [:wallet.accounts/generate-new-account @entered-password])
:disabled? (not (spec/valid? ::multiaccounts.db/password @entered-password))}}]]))

View File

@ -17,4 +17,4 @@
(defmethod navigation/preload-data! :add-new-account
[db [event]]
(dissoc db :generate-account))
(dissoc db :add-account))

View File

@ -27,8 +27,9 @@
:on-press #(re-frame/dispatch [:wallet.send/set-field field account])}]))
(views/defview accounts-list [field]
(views/letsubs [{:keys [accounts]} [:multiaccount]]
[list/flat-list {:data accounts
(views/letsubs [{:keys [accounts]} [:multiaccount]
accounts-whithout-watch [:accounts-without-watch-only]]
[list/flat-list {:data (if (= :to field) accounts accounts-whithout-watch)
:key-fn :address
:render-fn (render-account field)}]))

View File

@ -42,11 +42,11 @@
(let [{:keys [public-key address]}
(get (types/json->clj result) (keyword path))]
(re-frame/dispatch [::account-generated
{:name (str "Account " path-num)
:address address
{:name (str "Account " path-num)
:address address
:public-key public-key
:path (str constants/path-wallet-root "/" path-num)
:color (rand-nth colors/account-colors)}])))))))))))))
:path (str constants/path-wallet-root "/" path-num)
:color (rand-nth colors/account-colors)}])))))))))))))
(fx/defn set-symbol-request
{:events [:wallet.accounts/share]}
@ -57,42 +57,41 @@
{:events [:wallet.accounts/generate-new-account]}
[{:keys [db]} password]
(let [wallet-root-address (get-in db [:multiaccount :wallet-root-address])
path-num (inc (get-in db [:multiaccount :latest-derived-path]))]
(when-not (get-in db [:generate-account :step])
{:db (assoc-in db [:generate-account :step] :generating)
::generate-account {:derivation-info (if wallet-root-address
;; Use the walllet-root-address for stored on disk keys
;; This needs to be the RELATIVE path to the key used to derive
{:path (str "m/" path-num)
:address wallet-root-address}
;; Fallback on the master account for keycards, use the absolute path
{:path (str constants/path-wallet-root "/" path-num)
:address (get-in db [:multiaccount :address])})
:path-num path-num
:hashed-password (ethereum/sha3 password)}})))
path-num (inc (get-in db [:multiaccount :latest-derived-path]))]
(when-not (get-in db [:add-account :step])
{:db (assoc-in db [:add-account :step] :generating)
::generate-account {:derivation-info (if wallet-root-address
;; Use the walllet-root-address for stored on disk keys
;; This needs to be the RELATIVE path to the key used to derive
{:path (str "m/" path-num)
:address wallet-root-address}
;; Fallback on the master account for keycards, use the absolute path
{:path (str constants/path-wallet-root "/" path-num)
:address (get-in db [:multiaccount :address])})
:path-num path-num
:hashed-password (ethereum/sha3 password)}})))
(fx/defn generate-new-account-error
{:events [::generate-new-account-error]}
[{:keys [db]} password]
{:db (assoc db
:generate-account
:add-account
{:error (i18n/label :t/add-account-incorrect-password)})})
(fx/defn account-generated
{:events [::account-generated]}
[{:keys [db] :as cofx} account]
(fx/merge cofx
{:db (assoc db :generate-account {:account account
:step :generated})}
{:db (update db :add-account assoc :account account :step :generated)}
(navigation/navigate-to-cofx :account-added nil)))
(fx/defn save-account
{:events [:wallet.accounts/save-account]}
[{:keys [db] :as cofx} account {:keys [name color]}]
(let [{:keys [accounts]} (:multiaccount db)
new-account (cond-> account
name (assoc :name name)
color (assoc :color color))
new-account (cond-> account
name (assoc :name name)
color (assoc :color color))
new-accounts (replace {account new-account} accounts)]
(multiaccounts.update/multiaccount-update cofx {:accounts new-accounts} nil)))
@ -100,14 +99,48 @@
{:events [:wallet.accounts/save-generated-account]}
[{:keys [db] :as cofx}]
(let [{:keys [accounts latest-derived-path]} (:multiaccount db)
{:keys [account]} (:generate-account db)]
{:keys [account path type]} (:add-account db)]
(when account
(fx/merge cofx
{::json-rpc/call [{:method "accounts_saveAccounts"
:params [[account]]
{::json-rpc/call [{:method "accounts_saveAccounts"
:params [[account]]
:on-success #()}]
:db (dissoc db :generate-account)}
(multiaccounts.update/multiaccount-update {:accounts (conj accounts account)
:latest-derived-path (inc latest-derived-path)} nil)
:db (dissoc db :add-account)}
(multiaccounts.update/multiaccount-update
(merge {:accounts (conj accounts account)}
(when (= type :generate)
{:latest-derived-path (max (int path) latest-derived-path)}))
nil)
(wallet/update-balances nil)
(navigation/navigate-to-cofx :wallet nil)))))
(navigation/navigate-to-cofx :wallet nil)))))
(fx/defn start-adding-new-account
{:events [:wallet.accounts/start-adding-new-account]}
[{:keys [db] :as cofx} {:keys [type] :as add-account}]
(let [screen (case type :generate :add-new-account-password :watch :add-watch-account)]
(fx/merge cofx
{:db (assoc db :add-account add-account)}
(navigation/navigate-to-cofx screen nil))))
(fx/defn enter-phrase-next-pressed
{:events [:wallet.accounts/enter-phrase-next-pressed]}
[{:keys [db] :as cofx}]
(fx/merge cofx
{:db (-> db
(dissoc :intro-wizard)
(assoc-in [:add-account :seed] (get-in db [:intro-wizard :passphrase])))}
(navigation/navigate-to-cofx :add-new-account-password nil)))
(fx/defn add-watch-account
{:events [:wallet.accounts/add-watch-account]}
[{:keys [db] :as cofx}]
(let [address (get-in db [:add-account :address])]
(fx/merge cofx
{:db (assoc-in db [:add-account :account]
{:name ""
:address (eip55/address->checksum (ethereum/normalized-hex address))
:public-key nil
:path ""
:type :watch
:color (rand-nth colors/account-colors)})}
(navigation/navigate-to-cofx :account-added nil))))