[Fix #8067] Added screens for ENS preferred name selection

Signed-off-by: Julien Eluard <julien.eluard@gmail.com>
This commit is contained in:
Julien Eluard 2019-06-20 16:11:24 +02:00
parent cd567656d6
commit 3b7e92230b
No known key found for this signature in database
GPG Key ID: 6FD7DB5437FCBEF6
21 changed files with 208 additions and 86 deletions

View File

@ -1,15 +1,24 @@
(ns status-im.accounts.core
(:require [re-frame.core :as re-frame]
[status-im.accounts.update.core :as accounts.update]
[status-im.ethereum.stateofus :as stateofus]
[status-im.i18n :as i18n]
[status-im.native-module.core :as native-module]
[status-im.ui.screens.navigation :as navigation]
[status-im.utils.build :as build]
[status-im.utils.config :as config]
[status-im.utils.fx :as fx]
[status-im.utils.gfycat.core :as gfycat]
[status-im.utils.platform :as platform]
[status-im.utils.utils :as utils]))
(defn displayed-name [{:keys [ens-name nickname public-key]}]
(or nickname
(when ens-name
(let [username (stateofus/username ens-name)]
(str "@" (or username ens-name))))
(gfycat/generate-gfy public-key)))
(re-frame/reg-fx
::chaos-mode-changed
(fn [on]

View File

@ -3,6 +3,7 @@
[clojure.string :as string]
[status-im.chat.commands.core :as commands]
[status-im.chat.commands.input :as commands.input]
[status-im.accounts.core :as accounts]
[status-im.contact.db :as contact.db]
[status-im.group-chats.db :as group-chats.db]
[status-im.mailserver.constants :as mailserver.constants]
@ -36,7 +37,7 @@
random-name (gfycat/generate-gfy chat-id)]
(-> chat
(assoc :contact contact
:chat-name (or contact-name random-name)
:chat-name (accounts/displayed-name contact)
:name contact-name
:random-name random-name)
(update :tags clojure.set/union (:tags contact))))))

View File

@ -344,7 +344,7 @@
(and group-chat (not public?))
(assoc :message-type :group-user-message)))
(def ^:private transport-keys [:content :content-type :message-type :clock-value :timestamp])
(def ^:private transport-keys [:content :content-type :message-type :clock-value :timestamp :name])
(defn remove-icon
"Coin's icon's resource is represented as a function,

View File

@ -248,3 +248,6 @@
{:type :string :optional true}))
(def v24 (update v23 :properties merge {:usernames {:type "string[]" :optional true}}))
(def v25 (update v24 :properties merge {:preferred-name {:type :string :optional true}
:show-name? {:type :bool :optional true}}))

View File

@ -118,6 +118,11 @@
extension/v12
account/v24])
(def v30 [network/v1
bootnode/v4
extension/v12
account/v25])
;; put schemas ordered by version
(def schemas [{:schema v1
:schemaVersion 1
@ -205,4 +210,7 @@
:migration (constantly nil)}
{:schema v29
:schemaVersion 29
:migration (constantly nil)}
{:schema v30
:schemaVersion 30
:migration (constantly nil)}])

View File

@ -116,14 +116,34 @@
{:db (-> (update-in db [:ens/registration :custom-domain?] not)
(empty-username-candidate))})
(fx/defn save-preferred-name
{:events [:ens/save-preferred-name]}
[{:keys [db] :as cofx} name]
(fx/merge (assoc-in cofx [:db :account/account :preferred-name] name)
(accounts.update/account-update cofx
{:preferred-name name})))
(fx/defn save-username
{:events [:ens/save-username]}
[{:keys [db] :as cofx} custom-domain? username]
(let [name (fullname custom-domain? username)
db (update-in db [:account/account :usernames] #((fnil conj []) %1 %2) name)]
(accounts.update/account-update cofx
{:usernames (get-in db [:account/account :usernames])}
{:success-event [:ens/set-state username :saved]})))
(let [name (fullname custom-domain? username)
new-db (update-in db [:account/account :usernames] #((fnil conj []) %1 %2) name)
names (get-in new-db [:account/account :usernames])]
(fx/merge {:db new-db}
(accounts.update/account-update cofx
{:usernames names}
{:success-event [:ens/set-state username :saved]})
(when (= 1 (count names))
;; First name, save it as default
(save-preferred-name cofx name)))))
(fx/defn switch-show-username
{:events [:ens/switch-show-username]}
[{:keys [db] :as cofx} _]
(let [new-db (update-in db [:account/account :show-name?] not)]
(fx/merge {:db new-db}
(accounts.update/account-update cofx
{:show-name? (get-in new-db [:account/account :show-name?])}))))
(fx/defn on-registration-failure
{:events [:ens/on-registration-failure]}

View File

@ -70,7 +70,7 @@
(defn coordinates [public-key]
(when-let [hex (naked-address public-key)]
(when (= public-key-length (count (subs hex 2)))
{:x (normalized-address (subs hex 1 65))
{:x (normalized-address (subs hex 2 66))
:y (normalized-address (subs hex 66))})))
(defn address? [s]

View File

@ -142,7 +142,7 @@
:outputs ["bytes32" "bytes32"]
:on-success
(fn [[x y]]
(when-let [public-key (uncompressed-public-key x y)]
(let [public-key (uncompressed-public-key x y)]
(when-not (= public-key default-key)
(cb public-key))))}))

View File

@ -7,7 +7,7 @@
(str username "." domain))
(defn username [name]
(when (string/ends-with? name domain)
(when (and name (string/ends-with? name domain))
(first (string/split name "."))))
(def registrars

View File

@ -4,6 +4,7 @@
[status-im.react-native.js-dependencies :as rn]
[taoensso.timbre :as log]
[status-im.i18n :as i18n]
[status-im.accounts.core :as accounts]
[status-im.accounts.model :as accounts.model]
[status-im.contact.db :as contact.db]
[status-im.chat.models :as chat-model]
@ -150,7 +151,7 @@
(defn- get-contact-name [{:keys [db] :as cofx} from]
(if (accounts.model/logged-in? cofx)
(:name (hash->contact from (-> db :contacts/contacts vals)))
(accounts/displayed-name (hash->contact from (-> db :contacts/contacts vals)))
(anonymize-pubkey from)))
(defn- build-notification [{:keys [title body decoded-payload]}]

View File

@ -1847,13 +1847,25 @@
(fn [account]
(:usernames account)))
(re-frame/reg-sub
:ens/preferred-name
:<- [:account/account]
(fn [account]
(:preferred-name account)))
(re-frame/reg-sub
:ens/show?
:<- [:account/account]
(fn [account]
(:show-name? account)))
(re-frame/reg-sub
:ens.registration/screen
:<- [:ens/registration]
:<- [:ens.stateofus/registrar]
:<- [:account/account]
(fn [[{:keys [custom-domain? username-candidate] :as ens} registrar {:keys [address public-key]}] _]
{:state (get-in ens [:states username-candidate])
{:state (or (get-in ens [:states username-candidate]) :initial)
:username username-candidate
:custom-domain? (or custom-domain? false)
:contract registrar
@ -1870,6 +1882,18 @@
:address address
:public-key public-key})))
(re-frame/reg-sub
:ens.main/screen
:<- [:account/usernames]
:<- [:account/account]
:<- [:ens/preferred-name]
:<- [:ens/show?]
(fn [[names account preferred-name show?] _]
{:names names
:account account
:preferred-name preferred-name
:show? (or show? false)}))
;;SIGNING =============================================================================================================
(re-frame/reg-sub

View File

@ -122,8 +122,7 @@
value ""
active? true
style {}}}]
{:pre [(or icon image-source activity-indicator)
text
{:pre [text
(or (nil? accessibility-label) (keyword? accessibility-label))]}
[react/touchable-highlight
{:on-press action-fn

View File

@ -215,19 +215,19 @@
(if outgoing-status
[text-status outgoing-status]))))))
(defview message-author-name [from message-username]
(defview message-author-name [from message-username name]
(letsubs [username [:contacts/contact-name-by-identity from]]
(chat.utils/format-author from (or username message-username) style/message-author-name)))
(chat.utils/format-author from (or username message-username) style/message-author-name name)))
(defn message-body
[{:keys [last-in-group?
display-photo?
display-username?
message-type
from
outgoing
modal?
username] :as message} content]
username
name] :as message} content]
[react/view (style/group-message-wrapper message)
[react/view (style/message-body message)
(when display-photo?
@ -236,11 +236,11 @@
[react/touchable-highlight {:on-press #(when-not modal? (re-frame/dispatch [:chat.ui/show-profile from]))}
[react/view
[photos/member-photo from]]])])
[react/view (style/group-message-view outgoing message-type display-photo?)
[react/view (style/group-message-view outgoing display-photo?)
(when display-username?
[react/touchable-opacity {:on-press #(re-frame/dispatch [:chat.ui/show-profile from])}
[message-author-name from username]])
[react/view {:style (style/timestamp-content-wrapper outgoing message-type)}
[message-author-name from username name]])
[react/view {:style (style/timestamp-content-wrapper outgoing)}
content]]]
[react/view (style/delivery-status outgoing)
[message-delivery-status message]]])

View File

@ -70,11 +70,11 @@
{:margin-right 64})
(last-message-padding message)))
(defn timestamp-content-wrapper [outgoing message-type]
(defn timestamp-content-wrapper [outgoing]
{:flex-direction (if outgoing :row-reverse :row)})
(defn group-message-view
[outgoing message-type display-photo?]
[outgoing display-photo?]
(let [align (if outgoing :flex-end :flex-start)]
(merge {:flex-direction :column
:max-width (if platform/desktop? 500 320)

View File

@ -1,31 +1,28 @@
(ns status-im.ui.screens.chat.utils
(:require [re-frame.core :as re-frame]
[status-im.ethereum.stateofus :as stateofus]
[status-im.utils.gfycat.core :as gfycat]
[status-im.utils.platform :as platform]
[status-im.i18n :as i18n]
[status-im.constants :as constants]
[status-im.utils.core :as core-utils]
[status-im.ui.components.react :as react]
[status-im.ui.components.colors :as colors]
[status-im.utils.http :as http]))
(defn format-author [from username style]
;; TODO: We defensively generate the name for now, to be revisited when new protocol is defined
[react/nested-text {:style (style false)
:number-of-lines 1
:ellipsize-mode :tail}
(when username
[{:style (style true)
:number-of-lines 1
:ellipsize-mode :tail}
(str username " • ")])
(gfycat/generate-gfy from)])
(defn format-author [from username style ens-name]
(cond
ens-name
[react/text {:style {:color colors/blue :font-size 13 :font-weight "500"}}
(str "@" (or (stateofus/username ens-name) ens-name))]
:else
[react/text {:style {:color colors/gray :font-size 12 :font-weight "400"}}
(gfycat/generate-gfy from)]))
(defn format-reply-author [from username current-public-key style]
(or (and (= from current-public-key)
[react/text {:style (style true)}
(i18n/label :t/You)])
(format-author from username style)))
(format-author from username style nil)))
(def ^:private styling->prop
{:bold {:style {:font-weight "700"}}

View File

@ -56,10 +56,13 @@
[status-im.ui.components.common.common :as components.common]
[status-im.ui.components.icons.vector-icons :as vector-icons]
[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.components.toolbar.view :as toolbar]
[status-im.ui.screens.chat.message.message :as message]
[status-im.ui.screens.profile.components.views :as profile.components])
(:require-macros [status-im.utils.views :as views]))
@ -124,7 +127,7 @@
:icon :main-icons/close
:icon-color colors/gray
:hide-chevron? true}]
[react/view {:style {:margin-top 10}}
[react/view {:style {:margin-top 18}}
[list/big-list-item {:text (i18n/label :t/ens-release-username)
:text-color colors/gray
:text-style {:font-weight "500"}
@ -205,7 +208,7 @@
icon])
(defn- input-action [{:keys [state custom-domain? username]}]
(if (= :connected state)
(if (= state :connected)
;; Already registered, just need to save the contact
[:ens/save-username custom-domain? username]
[:ens/set-state username :registering]))
@ -487,35 +490,78 @@
[button {:on-press #(re-frame/dispatch [:navigate-to :ens-register])
:label (i18n/label :t/get-started)}]]])
(defn- registered [names]
[react/scroll-view {:style {:flex 1}}
(defn- name-item [{:keys [name action hide-chevron?]}]
(let [stateofus-username (stateofus/username name)
s (or stateofus-username name)]
[list/big-list-item {:text s
:subtext (when stateofus-username stateofus/domain)
:action-fn action
:icon :main-icons/username
:hide-chevron? hide-chevron?}]))
(defn- name-list [names preferred-name]
[react/view {:style {:flex 1 :margin-top 16}}
[react/view {:style {:margin-horizontal 16 :align-items :center :justify-content :center}}
[react/nested-text
{:style {:color colors/gray}}
(i18n/label :t/ens-displayed-with)
[{:style {:color colors/black :text-align :center}}
(str "\n@" preferred-name)]]]
[react/view {:style {:flex 1 :margin-top 8}}
[list/big-list-item {:text (i18n/label :t/ens-add-username)
:action-fn #(re-frame/dispatch [:navigate-to :ens-register])
:icon :main-icons/add}]]
[react/view {:style {:margin-top 22}}
[react/text {:style {:color colors/gray :margin-horizontal 16}}
(i18n/label :t/ens-your-usernames)]
(if (seq names)
[react/view {:style {:margin-top 8}}
(for [name names]
^{:key name}
[react/view
(let [stateofus-username (stateofus/username name)
s (or stateofus-username name)]
[list/big-list-item {:text s
:subtext (when stateofus-username stateofus/domain)
:action-fn #(re-frame/dispatch [:ens/navigate-to-name name])
:icon :main-icons/username}])])]
[react/text {:style {:color colors/gray :font-size 15}}
(i18n/label :t/ens-no-usernames)])]])
[react/scroll-view {:style {:flex 1}}
[react/view {:style {:flex 1}}
(for [name names]
(let [action #(do (re-frame/dispatch [:ens/save-preferred-name name])
(re-frame/dispatch [:bottom-sheet/hide-sheet]))]
^{:key name}
[react/touchable-highlight {:on-press action}
[react/view {:style {:flex 1 :flex-direction :row :align-items :center :justify-content :center :margin-right 16}}
[react/view {:style {:flex 1}}
[name-item {:name name :hide-chevron? true :action action}]]
[radio/radio (= name preferred-name)]]]))]]]])
(defn- registered [names {:keys [preferred-name address public-key name]} show?]
[react/view {:style {:flex 1}}
[react/scroll-view
[react/view {:style {:margin-top 8}}
[list/big-list-item {:text (i18n/label :t/ens-add-username)
:action-fn #(re-frame/dispatch [:navigate-to :ens-register])
:icon :main-icons/add}]]
[react/view {:style {:margin-top 22 :margin-bottom 8}}
[react/text {:style {:color colors/gray :margin-horizontal 16}}
(i18n/label :t/ens-your-usernames)]
(if (seq names)
[react/view {:style {:margin-top 8}}
(for [name names]
^{:key name}
[name-item {:name name :action #(re-frame/dispatch [:ens/navigate-to-name name])}])]
[react/text {:style {:color colors/gray :font-size 15}}
(i18n/label :t/ens-no-usernames)])]
[react/view {:style {:padding-top 22 :border-color colors/gray-lighter :border-top-width 1}}
[react/text {:style {:color colors/gray :margin-horizontal 16}}
(i18n/label :t/ens-chat-settings)]
(when (> (count names) 1)
[profile.components/settings-item {:label-kw :ens-primary-username
:value preferred-name
:action-fn #(re-frame/dispatch [:bottom-sheet/show-sheet
{:content (fn [] (name-list names preferred-name))
:content-height (+ 72 (* (min 4 (count names)) 64))}])}])
[profile.components/settings-switch-item {:label-kw :ens-show-username
:action-fn #(re-frame/dispatch [:ens/switch-show-username])
:value show?}]]
(let [message (merge {:from public-key :last-in-group? true :display-username? true :display-photo? true :username name
:content {:text (i18n/label :t/ens-test-message)} :content-type "text/plain" :timestamp-str "9:41 AM"}
(when show?
{:name preferred-name}))]
[message/message-body message
[message/text-message message]])]])
(views/defview main []
(views/letsubs [names [:account/usernames]]
(views/letsubs [{:keys [names account preferred-name show?]} [:ens.main/screen]]
[react/view {:style {:flex 1}}
[status-bar/status-bar {:type :main}]
[toolbar/simple-toolbar
(i18n/label :t/ens-usernames)]
(if (seq names)
[registered names]
[registered names account show?]
[welcome])]))

View File

@ -46,11 +46,12 @@
{:margin-left 16})
(def settings-item
{:padding-horizontal 16
:flex 1
:flex-direction :row
:align-items :center
:height 52})
{:padding-left 16
:padding-right 8
:flex 1
:flex-direction :row
:align-items :center
:height 52})
(def settings-item-icon
{:background-color colors/blue-light

View File

@ -1,6 +1,8 @@
(ns status-im.ui.screens.profile.components.views
(:require [clojure.string :as string]
[re-frame.core :as re-frame]
[status-im.accounts.core :as accounts]
[status-im.ethereum.stateofus :as stateofus]
[status-im.i18n :as i18n]
[status-im.ui.components.chat-icon.screen :as chat-icon.screen]
[status-im.ui.components.colors :as colors]
@ -38,7 +40,7 @@
[react/view styles/profile-header-name-container
[react/text {:style styles/profile-name-text
:number-of-lines 1}
name]
(accounts/displayed-name contact)]
(when (and public-key (not= generated-name name))
[react/text {:style styles/profile-three-words
:number-of-lines 1}
@ -101,7 +103,7 @@
(if icon-content
icon-content
(when (and active? (not hide-arrow?))
[vector-icons/icon :main-icons/next {:color colors/gray}]))]])
[vector-icons/icon :main-icons/next {:color colors/gray-transparent-40}]))]])
(defn settings-switch-item
[{:keys [label-kw value action-fn active?]

View File

@ -276,21 +276,22 @@
:accessory-value active-contacts-count
:action-fn #(re-frame/dispatch [:navigate-to :contacts-list])}])
(defn- ens-item [ens {:keys [registrar] :as props}]
[list.views/big-list-item
(let [enabled? (not (nil? registrar))]
(merge
{:text (or ens (i18n/label :t/ens-usernames))
:subtext (if enabled?
(if ens (i18n/label :t/ens-your-your-name) (i18n/label :t/ens-usernames-details))
(i18n/label :t/ens-network-restriction))
:icon :main-icons/username
:accessibility-label :ens-button}
(if enabled?
{:action-fn #(re-frame/dispatch [:navigate-to :ens-main props])}
{:icon-color colors/gray
:active? false
:hide-chevron? (not enabled?)})))])
(defn- ens-item [name {:keys [registrar] :as props}]
[react/view {:style {:margin-top 8}}
[list.views/big-list-item
(let [enabled? (not (nil? registrar))]
(merge
{:text (or name (i18n/label :t/ens-usernames))
:subtext (if enabled?
(if name (i18n/label :t/ens-your-your-name) (i18n/label :t/ens-usernames-details))
(i18n/label :t/ens-network-restriction))
:icon :main-icons/username
:accessibility-label :ens-button}
(if enabled?
{:action-fn #(re-frame/dispatch [:navigate-to :ens-main props])}
{:icon-color colors/gray
:active? false
:hide-chevron? (not enabled?)})))]])
(defn tribute-to-talk-item
[opts]
@ -310,7 +311,7 @@
[view]]]))
(defview my-profile []
(letsubs [{:keys [public-key photo-path] :as current-account} [:account/account]
(letsubs [{:keys [public-key photo-path preferred-name] :as current-account} [:account/account]
editing? [:my-profile/editing?]
extensions [:extensions/profile]
changed-account [:my-profile/profile]
@ -351,7 +352,7 @@
profile-icon-options)
:on-change-text-event :my-profile/update-name}]]
[share-profile-item (dissoc current-account :mnemonic)]
[ens-item nil {:registrar stateofus-registrar}]
[ens-item preferred-name {:registrar stateofus-registrar}]
[contacts-list-item active-contacts-count]
(when tribute-to-talk
[tribute-to-talk-item tribute-to-talk])

View File

@ -7,3 +7,8 @@
(is (= (ethereum/chain-id->chain-keyword 3) :testnet))
(is (= (ethereum/chain-id->chain-keyword 4) :rinkeby))
(is (= (ethereum/chain-id->chain-keyword 5777) :custom)))
(deftest coordinates
(is (= {:x "0x46fa4851f3cccd01e3b8d96c130c00bf812502354939eacf06a68fa519ebcbd1"
:y "0xeb08bebe7403856c0d9686210b9b2e324aa0179747bbba56d53f304a002f31c3"}
(ethereum/coordinates "0x0446fa4851f3cccd01e3b8d96c130c00bf812502354939eacf06a68fa519ebcbd1eb08bebe7403856c0d9686210b9b2e324aa0179747bbba56d53f304a002f31c3"))))

View File

@ -1136,8 +1136,13 @@
"ens-release-username": "Release username",
"ens-your-usernames": "Your usernames",
"ens-no-usernames": "You don't have any username connected",
"ens-chat-settings": "Chat settings",
"ens-primary-username": "Primary username",
"ens-show-username": "Show my ENS username in chats",
"ens-understand": "I understand that my wallet address will be publicly connected to my username.",
"ens-transaction-pending": "Transaction pending...",
"ens-test-message": "Hey",
"ens-displayed-with": "Your messages are displayed to others with",
"add-account" : "Add account",
"add-an-account" : "Add an account",
"back-up-your-seed-phrase" : "Back up your seed phrase",