[#11046] Add local contact names
Signed-off-by: andrey <motor4ik@gmail.com>
This commit is contained in:
parent
fea916590a
commit
4304a5dd97
|
@ -12,7 +12,8 @@
|
|||
[status-im.navigation :as navigation]
|
||||
[status-im.utils.fx :as fx]
|
||||
[status-im.waku.core :as waku]
|
||||
[taoensso.timbre :as log]))
|
||||
[taoensso.timbre :as log]
|
||||
[clojure.string :as string]))
|
||||
|
||||
(fx/defn load-contacts
|
||||
{:events [::contacts-loaded]}
|
||||
|
@ -59,7 +60,9 @@
|
|||
[{:keys [db]} contacts]
|
||||
{:db (update db :contacts/contacts
|
||||
#(reduce (fn [acc {:keys [public-key] :as contact}]
|
||||
(update acc public-key merge contact))
|
||||
(-> acc
|
||||
(update public-key merge contact)
|
||||
(assoc-in [public-key :nickname] (:nickname contact))))
|
||||
%
|
||||
contacts))})
|
||||
|
||||
|
@ -81,12 +84,15 @@
|
|||
|
||||
(fx/defn add-contact
|
||||
"Add a contact and set pending to false"
|
||||
[{:keys [db] :as cofx} public-key]
|
||||
[{:keys [db] :as cofx} public-key nickname]
|
||||
(when (not= (get-in db [:multiaccount :public-key]) public-key)
|
||||
(let [contact (-> (get-in db [:contacts/contacts public-key]
|
||||
(build-contact cofx public-key))
|
||||
(update :system-tags
|
||||
(fnil #(conj % :contact/added) #{})))]
|
||||
(let [contact (cond-> (get-in db [:contacts/contacts public-key]
|
||||
(build-contact cofx public-key))
|
||||
nickname
|
||||
(assoc :nickname nickname)
|
||||
:else
|
||||
(update :system-tags
|
||||
(fnil #(conj % :contact/added) #{})))]
|
||||
(fx/merge cofx
|
||||
{:db (dissoc db :contacts/new-identity)}
|
||||
(upsert-contact contact)
|
||||
|
@ -179,3 +185,13 @@
|
|||
:ens-verified true})}
|
||||
|
||||
(upsert-contact {:public-key public-key})))
|
||||
|
||||
(fx/defn update-nickname
|
||||
{:events [:contacts/update-nickname]}
|
||||
[{:keys [db] :as cofx} public-key nickname]
|
||||
(fx/merge cofx
|
||||
{:db (if (string/blank? nickname)
|
||||
(update-in db [:contacts/contacts public-key] dissoc :nickname)
|
||||
(assoc-in db [:contacts/contacts public-key :nickname] nickname))}
|
||||
(upsert-contact {:public-key public-key})
|
||||
(navigation/navigate-back)))
|
|
@ -3,7 +3,8 @@
|
|||
[clojure.string :as clojure.string]
|
||||
[status-im.ethereum.core :as ethereum]
|
||||
[status-im.utils.gfycat.core :as gfycat]
|
||||
[status-im.utils.identicon :as identicon]))
|
||||
[status-im.utils.identicon :as identicon]
|
||||
[status-im.multiaccounts.core :as multiaccounts]))
|
||||
|
||||
(defn public-key->new-contact [public-key]
|
||||
(let [alias (gfycat/generate-gfy public-key)]
|
||||
|
@ -135,7 +136,8 @@
|
|||
(assoc :pending? (pending? contact)
|
||||
:blocked? (blocked? contact)
|
||||
:active? (active? contact)
|
||||
:added? (contains? system-tags :contact/added))))
|
||||
:added? (contains? system-tags :contact/added))
|
||||
(multiaccounts/contact-with-names)))
|
||||
|
||||
(defn enrich-contacts
|
||||
[contacts]
|
||||
|
|
|
@ -32,18 +32,18 @@
|
|||
admins
|
||||
contacts
|
||||
current-multiaccount)
|
||||
[{:name "generated"
|
||||
:identicon "generated"
|
||||
:alias "generated"
|
||||
:admin? true
|
||||
:public-key "0x04fcf40c526b09ff9fb22f4a5dbd08490ef9b64af700870f8a0ba2133f4251d5607ed83cd9047b8c2796576bc83fa0de23a13a4dced07654b8ff137fe744047917"
|
||||
:system-tags #{}}
|
||||
{:alias "User A"
|
||||
:photo-path "photo2"
|
||||
[{:name "generated"
|
||||
:identicon "generated"
|
||||
:alias "generated"
|
||||
:admin? true
|
||||
:public-key "0x04fcf40c526b09ff9fb22f4a5dbd08490ef9b64af700870f8a0ba2133f4251d5607ed83cd9047b8c2796576bc83fa0de23a13a4dced07654b8ff137fe744047917"
|
||||
:system-tags #{}}
|
||||
{:alias "User A"
|
||||
:photo-path "photo2"
|
||||
:public-key "0x048a2f8b80c60f89a91b4c1316e56f75b087f446e7b8701ceca06a40142d8efe1f5aa36bd0fee9e248060a8d5207b43ae98bef4617c18c71e66f920f324869c09f"}
|
||||
{:last-updated 0
|
||||
:name "User B"
|
||||
:photo-path "photo1"
|
||||
:last-online 0
|
||||
:public-key "0x04985040682b77a32bb4bb58268a0719bd24ca4d07c255153fe1eb2ccd5883669627bd1a092d7cc76e8e4b9104327667b19dcda3ac469f572efabe588c38c1985f"
|
||||
:system-tags #{}}]))))))
|
||||
{:last-updated 0
|
||||
:name "User B"
|
||||
:photo-path "photo1"
|
||||
:last-online 0
|
||||
:public-key "0x04985040682b77a32bb4bb58268a0719bd24ca4d07c255153fe1eb2ccd5883669627bd1a092d7cc76e8e4b9104327667b19dcda3ac469f572efabe588c38c1985f"
|
||||
:system-tags #{}}]))))))
|
||||
|
|
|
@ -27,7 +27,8 @@
|
|||
:ensVerificationRetries :ens-verification-retries
|
||||
:lastENSClockValue :last-ens-clock-value
|
||||
:systemTags :system-tags
|
||||
:lastUpdated :last-updated})))
|
||||
:lastUpdated :last-updated
|
||||
:localNickname :nickname})))
|
||||
|
||||
(defn ->rpc [contact]
|
||||
(-> contact
|
||||
|
@ -41,7 +42,8 @@
|
|||
:photo-path :photoPath
|
||||
:tribute-to-talk :tributeToTalk
|
||||
:system-tags :systemTags
|
||||
:last-updated :lastUpdated})))
|
||||
:last-updated :lastUpdated
|
||||
:nickname :localNickname})))
|
||||
|
||||
(fx/defn fetch-contacts-rpc
|
||||
[cofx on-success]
|
||||
|
|
|
@ -816,7 +816,7 @@
|
|||
:contact.ui/add-to-contact-pressed
|
||||
[(re-frame/inject-cofx :random-id-generator)]
|
||||
(fn [cofx [_ public-key]]
|
||||
(contact/add-contact cofx public-key)))
|
||||
(contact/add-contact cofx public-key nil)))
|
||||
|
||||
(handlers/register-handler-fx
|
||||
:contact.ui/block-contact-confirmed
|
||||
|
@ -842,11 +842,11 @@
|
|||
(handlers/register-handler-fx
|
||||
:contact.ui/contact-code-submitted
|
||||
[(re-frame/inject-cofx :random-id-generator)]
|
||||
(fn [{{:contacts/keys [new-identity]} :db :as cofx} [_ new-contact?]]
|
||||
(fn [{{:contacts/keys [new-identity]} :db :as cofx} [_ new-contact? nickname]]
|
||||
(let [{:keys [public-key ens-name]} new-identity]
|
||||
(fx/merge cofx
|
||||
#(if new-contact?
|
||||
(contact/add-contact % public-key)
|
||||
(contact/add-contact % public-key nickname)
|
||||
(chat/start-chat % public-key))
|
||||
#(when new-contact?
|
||||
(navigation/navigate-back %))
|
||||
|
|
|
@ -8,7 +8,40 @@
|
|||
[status-im.utils.gfycat.core :as gfycat]
|
||||
[status-im.utils.identicon :as identicon]
|
||||
[status-im.utils.theme :as utils.theme]
|
||||
[status-im.theme.core :as theme]))
|
||||
[status-im.theme.core :as theme]
|
||||
[status-im.utils.utils :as utils]
|
||||
[clojure.string :as string]))
|
||||
|
||||
(defn contact-names
|
||||
"Returns map of all existing names for contact"
|
||||
[{:keys [name preferred-name alias public-key ens-verified nickname]}]
|
||||
(let [ens-name (or preferred-name
|
||||
name)]
|
||||
(cond-> {:nickname nickname
|
||||
:three-words-name (or alias (gfycat/generate-gfy public-key))}
|
||||
;; Preferred name is our own otherwise we make sure it's verified
|
||||
(or preferred-name (and ens-verified name))
|
||||
(assoc :ens-name (str "@" (or (stateofus/username ens-name) ens-name))))))
|
||||
|
||||
(defn contact-two-names
|
||||
"Returns vector of two names in next order nickname, ens name, three word name, public key"
|
||||
[{:keys [names public-key] :as contact} public-key?]
|
||||
(let [{:keys [nickname ens-name three-words-name]} (or names (contact-names contact))
|
||||
short-public-key (when public-key? (utils/get-shortened-address public-key))]
|
||||
(cond (not (string/blank? nickname))
|
||||
[nickname (or ens-name three-words-name short-public-key)]
|
||||
(not (string/blank? ens-name))
|
||||
[ens-name (or three-words-name short-public-key)]
|
||||
(not (string/blank? three-words-name))
|
||||
[three-words-name short-public-key]
|
||||
:else
|
||||
(when public-key?
|
||||
[short-public-key short-public-key]))))
|
||||
|
||||
(defn contact-with-names
|
||||
"Returns contact with :names map "
|
||||
[contact]
|
||||
(assoc contact :names (contact-names contact)))
|
||||
|
||||
(defn displayed-name
|
||||
"Use preferred name, name or alias in that order"
|
||||
|
|
|
@ -2,7 +2,8 @@
|
|||
(:require [reagent.core :as reagent]
|
||||
[status-im.ui.components.react :as react]
|
||||
[status-im.react-native.resources :as resources]
|
||||
[status-im.ui.components.colors :as colors]))
|
||||
[status-im.ui.components.colors :as colors]
|
||||
[re-frame.core :as re-frame]))
|
||||
|
||||
(def cnt (reagent/atom 0))
|
||||
(defonce cnt-prev (reagent/atom 0))
|
||||
|
@ -14,6 +15,7 @@
|
|||
(defn reload []
|
||||
(reset! warning? false)
|
||||
(reset! label "reloading UI")
|
||||
(re-frame/clear-subscription-cache!)
|
||||
(swap! cnt inc))
|
||||
|
||||
(defn build-competed []
|
||||
|
|
|
@ -1543,16 +1543,17 @@
|
|||
:<- [:contacts/contacts]
|
||||
:<- [:contacts/current-contact-identity]
|
||||
(fn [[contacts identity]]
|
||||
(or (contacts identity)
|
||||
(or (get contacts identity)
|
||||
(-> identity
|
||||
contact.db/public-key->new-contact
|
||||
contact.db/enrich-contact))))
|
||||
|
||||
(re-frame/reg-sub
|
||||
:contacts/contact-by-identity
|
||||
:<- [::contacts]
|
||||
:<- [:contacts/contacts]
|
||||
(fn [contacts [_ identity]]
|
||||
(get contacts identity)))
|
||||
(or (get contacts identity)
|
||||
(multiaccounts/contact-with-names {:public-key identity}))))
|
||||
|
||||
(re-frame/reg-sub
|
||||
:contacts/contact-added?
|
||||
|
@ -1562,27 +1563,23 @@
|
|||
(contact.db/added? contact)))
|
||||
|
||||
(re-frame/reg-sub
|
||||
:contacts/raw-contact-name-by-identity
|
||||
:contacts/contact-two-names-by-identity
|
||||
(fn [[_ identity] _]
|
||||
[(re-frame/subscribe [:contacts/contact-by-identity identity])])
|
||||
(fn [[db-contact] _]
|
||||
(if (and (:ens-verified db-contact) (seq (:name db-contact)))
|
||||
(str "@" (:name db-contact))
|
||||
(:alias db-contact))))
|
||||
[(re-frame/subscribe [:contacts/contact-by-identity identity])
|
||||
(re-frame/subscribe [:multiaccount])])
|
||||
(fn [[contact current-multiaccount] [_ identity]]
|
||||
(let [me? (= (:public-key current-multiaccount) identity)]
|
||||
(if me?
|
||||
[(or (:preferred-name current-multiaccount)
|
||||
(gfycat/generate-gfy identity))]
|
||||
(multiaccounts/contact-two-names contact false)))))
|
||||
|
||||
(re-frame/reg-sub
|
||||
:contacts/contact-name-by-identity
|
||||
(fn [[_ identity] _]
|
||||
[(re-frame/subscribe [:contacts/raw-contact-name-by-identity identity])
|
||||
(re-frame/subscribe [:multiaccount])])
|
||||
(fn [[contact-name current-multiaccount] [_ identity]]
|
||||
(let [me? (= (:public-key current-multiaccount) identity)]
|
||||
(if me?
|
||||
(or (:preferred-name current-multiaccount)
|
||||
(gfycat/generate-gfy identity))
|
||||
(or (stateofus/username contact-name)
|
||||
contact-name
|
||||
(gfycat/generate-gfy identity))))))
|
||||
[(re-frame/subscribe [:contacts/contact-two-names-by-identity identity])])
|
||||
(fn [[names] _]
|
||||
(first names)))
|
||||
|
||||
(re-frame/reg-sub
|
||||
:messages/quote-info
|
||||
|
@ -1612,8 +1609,7 @@
|
|||
:<- [::query-current-chat-contacts remove]
|
||||
(fn [contacts]
|
||||
(->> contacts
|
||||
(filter contact.db/added?)
|
||||
(sort-by (comp clojure.string/lower-case multiaccounts/displayed-name)))))
|
||||
(filter contact.db/added?))))
|
||||
|
||||
(re-frame/reg-sub
|
||||
:contacts/current-chat-contacts
|
||||
|
@ -1764,16 +1760,19 @@
|
|||
(string/lower-case (or alias
|
||||
(get-in contacts [chat-id :alias])
|
||||
(gfycat/generate-gfy chat-id)))
|
||||
"")]
|
||||
|
||||
"")
|
||||
nickname (get-in contacts [chat-id :nickname])]
|
||||
(or
|
||||
(string/includes? (string/lower-case (str name)) search-filter)
|
||||
(string/includes? (string/lower-case alias) search-filter)
|
||||
(when nickname
|
||||
(string/includes? (string/lower-case nickname) search-filter))
|
||||
(and
|
||||
(get-in contacts [chat-id :ens-verified])
|
||||
(string/includes? (string/lower-case
|
||||
(str (get-in contacts [chat-id :name])))
|
||||
search-filter)))))
|
||||
|
||||
(re-frame/reg-sub
|
||||
:search/filtered-chats
|
||||
:<- [:chats/active-chats]
|
||||
|
|
|
@ -1,8 +0,0 @@
|
|||
(ns status-im.ui.components.contact.contact
|
||||
(:require [status-im.ethereum.stateofus :as stateofus]
|
||||
[status-im.utils.gfycat.core :as gfycat]))
|
||||
|
||||
(defn format-name [{:keys [ens-verified name public-key]}]
|
||||
(if ens-verified
|
||||
(str "@" (or (stateofus/username name) name))
|
||||
(gfycat/generate-gfy public-key)))
|
|
@ -93,7 +93,7 @@
|
|||
(if-not validation-result
|
||||
(if new-contact?
|
||||
(fx/merge cofx
|
||||
(contact/add-contact chat-key)
|
||||
(contact/add-contact chat-key nil)
|
||||
(navigation/navigate-to-cofx :contacts-list {}))
|
||||
(chat/start-chat cofx chat-key))
|
||||
{:utils/show-popup {:title (i18n/label :t/unable-to-read-this-code)
|
||||
|
|
|
@ -13,17 +13,20 @@
|
|||
[status-im.ui.components.topbar :as topbar]
|
||||
[status-im.ui.screens.add-new.new-chat.styles :as styles]
|
||||
[status-im.utils.debounce :as debounce]
|
||||
[status-im.utils.utils :as utils])
|
||||
[status-im.utils.utils :as utils]
|
||||
[reagent.core :as reagent])
|
||||
(:require-macros [status-im.utils.views :as views]))
|
||||
|
||||
(defn- render-row [row _ _]
|
||||
[quo/list-item
|
||||
{:title (multiaccounts/displayed-name row)
|
||||
:icon [chat-icon/contact-icon-contacts-tab
|
||||
(multiaccounts/displayed-photo row)]
|
||||
:chevron true
|
||||
:on-press #(re-frame/dispatch [:chat.ui/start-chat
|
||||
(:public-key row)])}])
|
||||
(let [[first-name second-name] (multiaccounts/contact-two-names row false)]
|
||||
[quo/list-item
|
||||
{:title first-name
|
||||
:subtitle second-name
|
||||
:icon [chat-icon/contact-icon-contacts-tab
|
||||
(multiaccounts/displayed-photo row)]
|
||||
:chevron true
|
||||
:on-press #(re-frame/dispatch [:chat.ui/start-chat
|
||||
(:public-key row)])}]))
|
||||
|
||||
(defn- icon-wrapper [color icon]
|
||||
[react/view
|
||||
|
@ -36,7 +39,7 @@
|
|||
icon])
|
||||
|
||||
(defn- input-icon
|
||||
[state new-contact?]
|
||||
[state new-contact? entered-nickname]
|
||||
(let [icon (if new-contact? :main-icons/add :main-icons/arrow-right)]
|
||||
(case state
|
||||
:searching
|
||||
|
@ -45,7 +48,7 @@
|
|||
|
||||
:valid
|
||||
[react/touchable-highlight
|
||||
{:on-press #(debounce/dispatch-and-chill [:contact.ui/contact-code-submitted new-contact?] 3000)}
|
||||
{:on-press #(debounce/dispatch-and-chill [:contact.ui/contact-code-submitted new-contact? entered-nickname] 3000)}
|
||||
[icon-wrapper colors/blue
|
||||
[vector-icons/icon icon {:color colors/white-persist}]]]
|
||||
|
||||
|
@ -83,7 +86,7 @@
|
|||
(debounce/debounce-and-dispatch [:new-chat/set-new-identity %] 600))
|
||||
:on-submit-editing
|
||||
#(when (= state :valid)
|
||||
(debounce/dispatch-and-chill [:contact.ui/contact-code-submitted false] 3000))
|
||||
(debounce/dispatch-and-chill [:contact.ui/contact-code-submitted false nil] 3000))
|
||||
:placeholder (i18n/label :t/enter-contact-code)
|
||||
:show-cancel false
|
||||
:accessibility-label :enter-contact-code-input
|
||||
|
@ -91,7 +94,7 @@
|
|||
:return-key-type :go}]]
|
||||
[react/view {:justify-content :center
|
||||
:align-items :center}
|
||||
[input-icon state false]]]
|
||||
[input-icon state false nil]]]
|
||||
[react/view {:min-height 30 :justify-content :flex-end}
|
||||
[quo/text {:style styles/message
|
||||
:size :small
|
||||
|
@ -118,8 +121,20 @@
|
|||
:enableEmptySections true
|
||||
:keyboardShouldPersistTaps :always}]]))
|
||||
|
||||
(defn- nickname-input [entered-nickname]
|
||||
[quo/text-input
|
||||
{:on-change-text #(reset! entered-nickname %)
|
||||
:auto-capitalize :none
|
||||
:max-length 32
|
||||
:auto-focus false
|
||||
:accessibility-label :nickname-input
|
||||
:placeholder (i18n/label :t/add-nickname)
|
||||
:return-key-type :done
|
||||
:auto-correct false}])
|
||||
|
||||
(views/defview new-contact []
|
||||
(views/letsubs [{:keys [state ens-name public-key error]} [:contacts/new-identity]]
|
||||
(views/letsubs [{:keys [state ens-name public-key error]} [:contacts/new-identity]
|
||||
entered-nickname (reagent/atom "")]
|
||||
[react/view {:style {:flex 1}}
|
||||
[topbar/topbar
|
||||
{:title (i18n/label :t/new-contact)
|
||||
|
@ -142,7 +157,7 @@
|
|||
(debounce/debounce-and-dispatch [:new-chat/set-new-identity %] 600))
|
||||
:on-submit-editing
|
||||
#(when (= state :valid)
|
||||
(debounce/dispatch-and-chill [:contact.ui/contact-code-submitted true] 3000))
|
||||
(debounce/dispatch-and-chill [:contact.ui/contact-code-submitted true @entered-nickname] 3000))
|
||||
:placeholder (i18n/label :t/enter-contact-code)
|
||||
:show-cancel false
|
||||
:accessibility-label :enter-contact-code-input
|
||||
|
@ -150,12 +165,23 @@
|
|||
:return-key-type :go}]]
|
||||
[react/view {:justify-content :center
|
||||
:align-items :center}
|
||||
[input-icon state true]]]
|
||||
[react/view {:min-height 30 :justify-content :flex-end}
|
||||
[react/text {:style styles/message}
|
||||
[input-icon state true @entered-nickname]]]
|
||||
[react/view {:min-height 30 :justify-content :flex-end :margin-bottom 16}
|
||||
[quo/text {:style styles/message
|
||||
:size :small
|
||||
:align :center
|
||||
:color :secondary}
|
||||
(cond (= state :error)
|
||||
(get-validation-label error)
|
||||
(= state :valid)
|
||||
(str (when ens-name (str ens-name " • "))
|
||||
(utils/get-shortened-address public-key))
|
||||
:else "")]]]))
|
||||
:else "")]]
|
||||
[react/text {:style {:margin-horizontal 16 :color colors/gray}}
|
||||
(i18n/label :t/nickname-description)]
|
||||
[react/view {:padding 16}
|
||||
|
||||
[nickname-input entered-nickname]
|
||||
[react/text {:style {:align-self :flex-end :margin-top 16
|
||||
:color colors/gray}}
|
||||
(str (count @entered-nickname) " / 32")]]]))
|
|
@ -175,13 +175,13 @@
|
|||
(= outgoing-status :not-sent))
|
||||
[message-not-sent-text chat-id message-id]))
|
||||
|
||||
(defview message-author-name [from alias modal]
|
||||
(letsubs [contact-name [:contacts/raw-contact-name-by-identity from]]
|
||||
(chat.utils/format-author (or contact-name alias) modal)))
|
||||
(defview message-author-name [from modal]
|
||||
(letsubs [contact-with-names [:contacts/contact-by-identity from]]
|
||||
(chat.utils/format-author contact-with-names modal)))
|
||||
|
||||
(defn message-content-wrapper
|
||||
"Author, userpic and delivery wrapper"
|
||||
[{:keys [alias first-in-group? display-photo? identicon display-username?
|
||||
[{:keys [first-in-group? display-photo? identicon display-username?
|
||||
from outgoing]
|
||||
:as message} content {:keys [modal close-modal]}]
|
||||
[react/view {:style (style/message-wrapper message)
|
||||
|
@ -200,7 +200,7 @@
|
|||
[react/touchable-opacity {:style style/message-author-touchable
|
||||
:on-press #(do (when modal (close-modal))
|
||||
(re-frame/dispatch [:chat.ui/show-profile from]))}
|
||||
[message-author-name from alias modal]])
|
||||
[message-author-name from modal]])
|
||||
;;MESSAGE CONTENT
|
||||
[react/view
|
||||
content]]]
|
||||
|
|
|
@ -17,7 +17,11 @@
|
|||
(i18n/label-pluralize cnt :t/members-active))))]])
|
||||
|
||||
(defn one-to-one-name [from]
|
||||
@(re-frame.core/subscribe [:contacts/contact-name-by-identity from]))
|
||||
(let [[first-name _] @(re-frame.core/subscribe [:contacts/contact-two-names-by-identity from])]
|
||||
[react/text {:style st/chat-name-text
|
||||
:number-of-lines 1
|
||||
:accessibility-label :chat-name-text}
|
||||
first-name]))
|
||||
|
||||
(defn contact-indicator [contact-id]
|
||||
(let [added? @(re-frame/subscribe [:contacts/contact-added? contact-id])]
|
||||
|
@ -39,12 +43,12 @@
|
|||
[react/view {:margin-right 10}
|
||||
[chat-icon.screen/chat-icon-view-toolbar chat-id group-chat chat-name color]]
|
||||
[react/view {:style st/chat-name-view}
|
||||
[react/text {:style st/chat-name-text
|
||||
:number-of-lines 1
|
||||
:accessibility-label :chat-name-text}
|
||||
(if group-chat
|
||||
chat-name
|
||||
[one-to-one-name chat-id])]
|
||||
(if group-chat
|
||||
[react/text {:style st/chat-name-text
|
||||
:number-of-lines 1
|
||||
:accessibility-label :chat-name-text}
|
||||
chat-name]
|
||||
[one-to-one-name chat-id])
|
||||
(when-not group-chat
|
||||
[contact-indicator chat-id])
|
||||
(when group-chat
|
||||
|
|
|
@ -2,26 +2,31 @@
|
|||
(:require [status-im.ethereum.stateofus :as stateofus]
|
||||
[status-im.i18n :as i18n]
|
||||
[status-im.ui.components.react :as react]
|
||||
[status-im.ui.components.colors :as colors]))
|
||||
[status-im.ui.components.colors :as colors]
|
||||
[status-im.multiaccounts.core :as multiaccounts]))
|
||||
|
||||
(def ^:private reply-symbol "↪ ")
|
||||
|
||||
(defn format-author
|
||||
([contact-name] (format-author contact-name false))
|
||||
([contact-name modal]
|
||||
(if (= (aget contact-name 0) "@")
|
||||
(let [trimmed-name (subs contact-name 0 81)]
|
||||
[react/text {:number-of-lines 2
|
||||
:style {:color (if modal colors/white-persist colors/blue)
|
||||
:font-size 13
|
||||
:line-height 18
|
||||
:font-weight "500"}}
|
||||
(or (stateofus/username trimmed-name) trimmed-name)])
|
||||
[react/text {:style {:color (if modal colors/white-persist colors/gray)
|
||||
:font-size 12
|
||||
:line-height 18
|
||||
:font-weight "400"}}
|
||||
contact-name])))
|
||||
([contact] (format-author contact false))
|
||||
([{:keys [names] :as contact} modal]
|
||||
(let [{:keys [nickname ens-name]} names
|
||||
[first-name second-name] (multiaccounts/contact-two-names contact false)]
|
||||
(if (or nickname ens-name)
|
||||
[react/nested-text {:number-of-lines 2
|
||||
:style {:color (if modal colors/white-persist colors/blue)
|
||||
:font-size 13
|
||||
:line-height 18
|
||||
:font-weight "500"}}
|
||||
(subs first-name 0 81)
|
||||
(when nickname
|
||||
[{:style {:color colors/gray :font-weight "400"}}
|
||||
(str " " (subs second-name 0 81))])]
|
||||
[react/text {:style {:color (if modal colors/white-persist colors/gray)
|
||||
:font-size 12
|
||||
:line-height 18
|
||||
:font-weight "400"}}
|
||||
first-name]))))
|
||||
|
||||
(defn format-reply-author [from username current-public-key style]
|
||||
(let [contact-name (str reply-symbol username)]
|
||||
|
|
|
@ -73,7 +73,8 @@
|
|||
:default-chat-icon-text style/intro-header-icon-text
|
||||
:size 120}]]
|
||||
;; Chat title section
|
||||
[react/text {:style (style/intro-header-chat-name)} (if group-chat chat-name contact-name)]
|
||||
[react/text {:style (style/intro-header-chat-name)}
|
||||
(if group-chat chat-name contact-name)]
|
||||
;; Description section
|
||||
(if group-chat
|
||||
[chat.group/group-chat-description-container {:chat-id chat-id
|
||||
|
@ -89,9 +90,9 @@
|
|||
contact-name)])])
|
||||
|
||||
(defn chat-intro-one-to-one [{:keys [chat-id] :as opts}]
|
||||
(let [contact-name @(re-frame/subscribe
|
||||
[:contacts/contact-name-by-identity chat-id])]
|
||||
(chat-intro (assoc opts :contact-name contact-name))))
|
||||
(let [contact-names @(re-frame/subscribe
|
||||
[:contacts/contact-two-names-by-identity chat-id])]
|
||||
(chat-intro (assoc opts :contact-name (first contact-names)))))
|
||||
|
||||
(defn chat-intro-header-container
|
||||
[{:keys [group-chat
|
||||
|
|
|
@ -12,12 +12,14 @@
|
|||
(:require-macros [status-im.utils.views :refer [defview letsubs]]))
|
||||
|
||||
(defn contacts-list-item [{:keys [public-key] :as contact}]
|
||||
[quo/list-item
|
||||
{:title (multiaccounts/displayed-name contact)
|
||||
:icon [chat-icon.screen/contact-icon-contacts-tab
|
||||
(multiaccounts/displayed-photo contact)]
|
||||
:chevron true
|
||||
:on-press #(re-frame/dispatch [:chat.ui/show-profile public-key])}])
|
||||
(let [[first-name second-name] (multiaccounts/contact-two-names contact true)]
|
||||
[quo/list-item
|
||||
{:title first-name
|
||||
:subtitle second-name
|
||||
:icon [chat-icon.screen/contact-icon-contacts-tab
|
||||
(multiaccounts/displayed-photo contact)]
|
||||
:chevron true
|
||||
:on-press #(re-frame/dispatch [:chat.ui/show-profile public-key])}]))
|
||||
|
||||
(defn add-new-contact []
|
||||
[quo/list-item
|
||||
|
|
|
@ -628,7 +628,10 @@
|
|||
(views/defview my-name []
|
||||
(views/letsubs [contact-name [:multiaccount/preferred-name]]
|
||||
(when-not (string/blank? contact-name)
|
||||
(chat.utils/format-author (str "@" contact-name)))))
|
||||
(chat.utils/format-author {:names {:ens-name
|
||||
(str "@"
|
||||
(or (stateofus/username contact-name)
|
||||
contact-name))}}))))
|
||||
|
||||
(views/defview registered [names {:keys [preferred-name] :as account} _ registrations]
|
||||
[react/view {:style {:flex 1}}
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
[status-im.constants :as constants]
|
||||
[status-im.i18n :as i18n]
|
||||
[status-im.ui.components.chat-icon.screen :as chat-icon]
|
||||
[status-im.ui.components.contact.contact :as contact]
|
||||
[status-im.multiaccounts.core :as multiaccounts]
|
||||
[status-im.ui.components.keyboard-avoid-presentation
|
||||
:as
|
||||
|
@ -23,10 +22,12 @@
|
|||
(:require-macros [status-im.utils.views :as views]))
|
||||
|
||||
(defn- render-contact [row]
|
||||
[quo/list-item
|
||||
{:title (contact/format-name row)
|
||||
:icon [chat-icon/contact-icon-contacts-tab
|
||||
(multiaccounts/displayed-photo row)]}])
|
||||
(let [[first-name second-name] (multiaccounts/contact-two-names row false)]
|
||||
[quo/list-item
|
||||
{:title first-name
|
||||
:subtitle second-name
|
||||
:icon [chat-icon/contact-icon-contacts-tab
|
||||
(multiaccounts/displayed-photo row)]}]))
|
||||
|
||||
(defn- on-toggle [allow-new-users? checked? public-key]
|
||||
(cond
|
||||
|
@ -52,9 +53,11 @@
|
|||
|
||||
(defn- toggle-item []
|
||||
(fn [allow-new-users? subs-name {:keys [public-key] :as contact} on-toggle]
|
||||
(let [contact-selected? @(re-frame/subscribe [subs-name public-key])]
|
||||
(let [contact-selected? @(re-frame/subscribe [subs-name public-key])
|
||||
[first-name second-name] (multiaccounts/contact-two-names contact true)]
|
||||
[quo/list-item
|
||||
{:title (contact/format-name contact)
|
||||
{:title first-name
|
||||
:subtitle second-name
|
||||
:icon [chat-icon/contact-icon-contacts-tab
|
||||
(multiaccounts/displayed-photo contact)]
|
||||
:on-press #(on-toggle allow-new-users? contact-selected? public-key)
|
||||
|
@ -83,10 +86,12 @@
|
|||
|
||||
(defn filter-contacts [filter-text contacts]
|
||||
(let [lower-filter-text (string/lower-case (str filter-text))
|
||||
filter-fn (fn [{:keys [name alias]}]
|
||||
filter-fn (fn [{:keys [name alias nickname]}]
|
||||
(or
|
||||
(string/includes? (string/lower-case (str name)) lower-filter-text)
|
||||
(string/includes? (string/lower-case (str alias)) lower-filter-text)))]
|
||||
(string/includes? (string/lower-case (str alias)) lower-filter-text)
|
||||
(when nickname
|
||||
(string/includes? (string/lower-case (str nickname)) lower-filter-text))))]
|
||||
(if filter-text
|
||||
(filter filter-fn contacts)
|
||||
contacts)))
|
||||
|
|
|
@ -163,7 +163,7 @@
|
|||
;; This looks a bit odd, but I would like only to subscribe
|
||||
;; if it's a one-to-one. If wrapped in a component styling
|
||||
;; won't be applied correctly.
|
||||
@(re-frame/subscribe [:contacts/contact-name-by-identity chat-id]))]]
|
||||
(first @(re-frame/subscribe [:contacts/contact-two-names-by-identity chat-id])))]]
|
||||
[message-timestamp (if (pos? (:whisper-timestamp last-message))
|
||||
(:whisper-timestamp last-message)
|
||||
timestamp)]]
|
||||
|
|
|
@ -9,8 +9,13 @@
|
|||
[status-im.ui.components.react :as react]
|
||||
[status-im.ui.screens.profile.components.sheets :as sheets]
|
||||
[status-im.ui.screens.profile.contact.styles :as styles]
|
||||
[status-im.utils.gfycat.core :as gfy]
|
||||
[status-im.utils.utils :as utils])
|
||||
[status-im.utils.utils :as utils]
|
||||
[status-im.ui.components.topbar :as topbar]
|
||||
[status-im.ui.components.colors :as colors]
|
||||
[status-im.ui.components.toolbar :as toolbar]
|
||||
[status-im.ui.components.keyboard-avoid-presentation :as kb-presentation]
|
||||
[reagent.core :as reagent]
|
||||
[clojure.string :as string])
|
||||
(:require-macros [status-im.utils.views :as views]))
|
||||
|
||||
(defn actions
|
||||
|
@ -47,26 +52,47 @@
|
|||
; :content-height 150}
|
||||
; contact])
|
||||
|
||||
(defn render-detail [{:keys [public-key names name] :as detail}]
|
||||
[quo/list-item
|
||||
{:title (:three-words-name names)
|
||||
:subtitle [quo/text {:monospace true
|
||||
:color :secondary}
|
||||
(utils/get-shortened-address public-key)]
|
||||
:icon [chat-icon/contact-icon-contacts-tab
|
||||
(multiaccounts/displayed-photo detail)]
|
||||
:accessibility-label :profile-public-key
|
||||
:on-press #(re-frame/dispatch [:show-popover (merge {:view :share-chat-key
|
||||
:address public-key}
|
||||
(when (and (:ens-name names) name)
|
||||
{:ens-name name}))])
|
||||
:accessory [icons/icon :main-icons/share styles/contact-profile-detail-share-icon]}])
|
||||
|
||||
(defn profile-details [{:keys [alias public-key ens-name] :as contact}]
|
||||
(defn profile-details [contact]
|
||||
(when contact
|
||||
[react/view
|
||||
[quo/list-header
|
||||
[quo/text {:accessibility-label :profile-details
|
||||
:color :inherit}
|
||||
(i18n/label :t/profile-details)]]
|
||||
[quo/list-item
|
||||
{:title (or alias ens-name)
|
||||
:subtitle [quo/text {:monospace true
|
||||
:color :secondary}
|
||||
(utils/get-shortened-address public-key)]
|
||||
:icon [chat-icon/contact-icon-contacts-tab
|
||||
(multiaccounts/displayed-photo contact)]
|
||||
:accessibility-label :profile-public-key
|
||||
:on-press #(re-frame/dispatch [:show-popover {:view :share-chat-key
|
||||
:address public-key
|
||||
:ens-name ens-name}])
|
||||
:accessory [icons/icon :main-icons/share styles/contact-profile-detail-share-icon]}]]))
|
||||
[render-detail contact]]))
|
||||
|
||||
(defn render-chat-settings [{:keys [names]}]
|
||||
[quo/list-item
|
||||
{:title (i18n/label :t/nickname)
|
||||
:size :small
|
||||
:accessibility-label :profile-nickname-item
|
||||
:accessory :text
|
||||
:accessory-text (or (:nickname names) (i18n/label :t/none))
|
||||
:on-press #(re-frame/dispatch [:navigate-to :nickname])
|
||||
:chevron true}])
|
||||
|
||||
(defn chat-settings [contact]
|
||||
[react/view
|
||||
[quo/list-header
|
||||
[quo/text {:accessibility-label :chat-settings
|
||||
:color :inherit}
|
||||
(i18n/label :t/chat-settings)]]
|
||||
[render-chat-settings contact]])
|
||||
|
||||
;; TODO: List item
|
||||
(defn block-contact-action [{:keys [blocked? public-key]}]
|
||||
|
@ -84,10 +110,56 @@
|
|||
(i18n/label :t/unblock-contact)
|
||||
(i18n/label :t/block-contact))]])
|
||||
|
||||
(defn save-nickname [public-key nickname]
|
||||
(re-frame/dispatch [:contacts/update-nickname public-key nickname]))
|
||||
|
||||
(defn valid-nickname? [nickname]
|
||||
(not (string/blank? nickname)))
|
||||
|
||||
(defn- nickname-input [nickname entered-nickname public-key]
|
||||
[quo/text-input
|
||||
{:on-change-text #(reset! entered-nickname %)
|
||||
:on-submit-editing #(when (valid-nickname? @entered-nickname)
|
||||
(save-nickname public-key @entered-nickname))
|
||||
:auto-capitalize :none
|
||||
:auto-focus false
|
||||
:max-length 32
|
||||
:accessibility-label :nickname-input
|
||||
:default-value nickname
|
||||
:placeholder (i18n/label :t/nickname)
|
||||
:return-key-type :done
|
||||
:auto-correct false}])
|
||||
|
||||
(defn nickname-view [public-key {:keys [nickname ens-name three-words-name]}]
|
||||
(let [entered-nickname (reagent/atom nickname)]
|
||||
(fn []
|
||||
[kb-presentation/keyboard-avoiding-view {:style {:flex 1}}
|
||||
[topbar/topbar {:title (i18n/label :t/nickname)
|
||||
:subtitle (or ens-name three-words-name)
|
||||
:modal? true}]
|
||||
[react/view {:flex 1 :padding 16}
|
||||
[react/text {:style {:color colors/gray :margin-bottom 16}}
|
||||
(i18n/label :t/nickname-description)]
|
||||
[nickname-input nickname entered-nickname public-key]
|
||||
[react/text {:style {:align-self :flex-end :margin-top 16
|
||||
:color colors/gray}}
|
||||
(str (count @entered-nickname) " / 32")]]
|
||||
[toolbar/toolbar {:show-border? true
|
||||
:center
|
||||
[quo/button
|
||||
{:type :secondary
|
||||
:on-press #(save-nickname public-key @entered-nickname)}
|
||||
(i18n/label :t/done)]}]])))
|
||||
|
||||
(views/defview nickname []
|
||||
(views/letsubs [{:keys [public-key names]} [:contacts/current-contact]]
|
||||
[nickname-view public-key names]))
|
||||
|
||||
(views/defview profile []
|
||||
(views/letsubs [{:keys [ens-verified name public-key]
|
||||
(views/letsubs [{:keys [public-key name ens-verified]
|
||||
:as contact} [:contacts/current-contact]]
|
||||
(let [on-share #(re-frame/dispatch [:show-popover (merge
|
||||
(let [[first-name second-name] (multiaccounts/contact-two-names contact true)
|
||||
on-share #(re-frame/dispatch [:show-popover (merge
|
||||
{:view :share-chat-key
|
||||
:address public-key}
|
||||
(when (and ens-verified name)
|
||||
|
@ -104,13 +176,11 @@
|
|||
:accessibility-label :back-button
|
||||
:on-press #(re-frame/dispatch [:navigate-back])}]
|
||||
:extended-header (profile-header/extended-header
|
||||
{:on-press on-share
|
||||
:title (multiaccounts/displayed-name contact)
|
||||
:photo (multiaccounts/displayed-photo contact)
|
||||
{:on-press on-share
|
||||
:title first-name
|
||||
:photo (multiaccounts/displayed-photo contact)
|
||||
:monospace (not ens-verified)
|
||||
:subtitle (if (and ens-verified public-key)
|
||||
(gfy/generate-gfy public-key)
|
||||
public-key)})}
|
||||
:subtitle second-name})}
|
||||
|
||||
[react/view {:padding-top 12}
|
||||
(for [{:keys [label subtext accessibility-label icon action disabled?]} (actions contact)]
|
||||
|
@ -124,7 +194,6 @@
|
|||
:disabled disabled?
|
||||
:on-press action}]))]
|
||||
[react/view styles/contact-profile-details-container
|
||||
[profile-details (cond-> contact
|
||||
(and ens-verified name)
|
||||
(assoc :ens-name name))]]
|
||||
[profile-details contact]
|
||||
[chat-settings contact]]
|
||||
[block-contact-action contact]]]))))
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
[status-im.i18n :as i18n]
|
||||
[status-im.multiaccounts.core :as multiaccounts]
|
||||
[status-im.ui.components.chat-icon.screen :as chat-icon]
|
||||
[status-im.ui.components.contact.contact :as contact]
|
||||
[status-im.ui.components.list.views :as list]
|
||||
[status-im.ui.components.profile-header.view :as profile-header]
|
||||
[status-im.ui.components.react :as react]
|
||||
|
@ -16,56 +15,59 @@
|
|||
(:require-macros [status-im.utils.views :refer [defview letsubs]]))
|
||||
|
||||
(defn member-sheet [chat-id member us-admin?]
|
||||
[react/view
|
||||
[quo/list-item
|
||||
{:theme :accent
|
||||
:icon [chat-icon/contact-icon-contacts-tab
|
||||
(multiaccounts/displayed-photo member)]
|
||||
:title (contact/format-name member)
|
||||
:subtitle (i18n/label :t/view-profile)
|
||||
:accessibility-label :view-chat-details-button
|
||||
:chevron true
|
||||
:on-press #(chat.sheets/hide-sheet-and-dispatch
|
||||
[:chat.ui/show-profile
|
||||
(:public-key member)])}]
|
||||
(when (and us-admin?
|
||||
(not (:admin? member)))
|
||||
(let [[first-name _] (multiaccounts/contact-two-names member false)]
|
||||
[react/view
|
||||
[quo/list-item
|
||||
{:theme :accent
|
||||
:title (i18n/label :t/make-admin)
|
||||
:accessibility-label :make-admin
|
||||
:icon :main-icons/make-admin
|
||||
:on-press #(chat.sheets/hide-sheet-and-dispatch [:group-chats.ui/make-admin-pressed chat-id (:public-key member)])}])
|
||||
(when-not (:admin? member)
|
||||
[quo/list-item
|
||||
{:theme :accent
|
||||
:title (i18n/label :t/remove-from-chat)
|
||||
:accessibility-label :remove-from-chat
|
||||
:icon :main-icons/remove-contact
|
||||
:on-press #(chat.sheets/hide-sheet-and-dispatch [:group-chats.ui/remove-member-pressed chat-id (:public-key member)])}])])
|
||||
:icon [chat-icon/contact-icon-contacts-tab
|
||||
(multiaccounts/displayed-photo member)]
|
||||
:title first-name
|
||||
:subtitle (i18n/label :t/view-profile)
|
||||
:accessibility-label :view-chat-details-button
|
||||
:chevron true
|
||||
:on-press #(chat.sheets/hide-sheet-and-dispatch
|
||||
[:chat.ui/show-profile
|
||||
(:public-key member)])}]
|
||||
(when (and us-admin?
|
||||
(not (:admin? member)))
|
||||
[quo/list-item
|
||||
{:theme :accent
|
||||
:title (i18n/label :t/make-admin)
|
||||
:accessibility-label :make-admin
|
||||
:icon :main-icons/make-admin
|
||||
:on-press #(chat.sheets/hide-sheet-and-dispatch [:group-chats.ui/make-admin-pressed chat-id (:public-key member)])}])
|
||||
(when-not (:admin? member)
|
||||
[quo/list-item
|
||||
{:theme :accent
|
||||
:title (i18n/label :t/remove-from-chat)
|
||||
:accessibility-label :remove-from-chat
|
||||
:icon :main-icons/remove-contact
|
||||
:on-press #(chat.sheets/hide-sheet-and-dispatch [:group-chats.ui/remove-member-pressed chat-id (:public-key member)])}])]))
|
||||
|
||||
(defn render-member [chat-id {:keys [public-key] :as member} admin? current-user-identity]
|
||||
[quo/list-item
|
||||
(merge
|
||||
{:title (contact/format-name member)
|
||||
:accessibility-label :member-item
|
||||
:icon [chat-icon/contact-icon-contacts-tab
|
||||
(multiaccounts/displayed-photo member)]
|
||||
:on-press (when (not= public-key current-user-identity)
|
||||
#(re-frame/dispatch [:chat.ui/show-profile public-key]))}
|
||||
(when (:admin? member)
|
||||
{:accessory :text
|
||||
:accessory-text (i18n/label :t/group-chat-admin)})
|
||||
(when (and admin?
|
||||
(not (:admin? member))
|
||||
(not= public-key current-user-identity))
|
||||
{:accessory [quo/button {:on-press #(re-frame/dispatch [:bottom-sheet/show-sheet
|
||||
{:content (fn []
|
||||
[member-sheet chat-id member admin?])}])
|
||||
:type :icon
|
||||
:theme :icon
|
||||
:accessibility-label :menu-option}
|
||||
:main-icons/more]}))])
|
||||
(let [[first-name second-name] (multiaccounts/contact-two-names member false)]
|
||||
[quo/list-item
|
||||
(merge
|
||||
{:title first-name
|
||||
:subtitle second-name
|
||||
:accessibility-label :member-item
|
||||
:icon [chat-icon/contact-icon-contacts-tab
|
||||
(multiaccounts/displayed-photo member)]
|
||||
:on-press (when (not= public-key current-user-identity)
|
||||
#(re-frame/dispatch [:chat.ui/show-profile public-key]))}
|
||||
(when (:admin? member)
|
||||
{:accessory :text
|
||||
:accessory-text (i18n/label :t/group-chat-admin)})
|
||||
(when (and admin?
|
||||
(not (:admin? member))
|
||||
(not= public-key current-user-identity))
|
||||
{:accessory [quo/button {:on-press #(re-frame/dispatch [:bottom-sheet/show-sheet
|
||||
{:content (fn []
|
||||
[member-sheet chat-id member admin?])}])
|
||||
:type :icon
|
||||
:theme :icon
|
||||
:accessibility-label :menu-option}
|
||||
:main-icons/more]}))]))
|
||||
|
||||
(defview chat-group-members-view [chat-id admin? current-user-identity]
|
||||
(letsubs [members [:contacts/current-chat-contacts]]
|
||||
|
|
|
@ -23,7 +23,8 @@
|
|||
[quo.previews.main :as quo.preview]
|
||||
[status-im.utils.config :as config]
|
||||
[status-im.ui.screens.chat.image.preview.views :as image-preview]
|
||||
[status-im.ui.screens.notifications-settings.views :as notifications-settings]))
|
||||
[status-im.ui.screens.notifications-settings.views :as notifications-settings]
|
||||
[status-im.ui.screens.profile.contact.views :as contact]))
|
||||
|
||||
(defonce main-stack (navigation/create-stack))
|
||||
(defonce bottom-tabs (navigation/create-bottom-tabs))
|
||||
|
@ -82,6 +83,10 @@
|
|||
:transition :presentation-ios
|
||||
:insets {:bottom true}
|
||||
:component new-public-chat/new-public-chat}
|
||||
{:name :nickname
|
||||
:transition :presentation-ios
|
||||
:insets {:bottom true}
|
||||
:component contact/nickname}
|
||||
{:name :edit-group-chat-name
|
||||
:transition :presentation-ios
|
||||
:insets {:bottom true}
|
||||
|
|
|
@ -770,7 +770,6 @@
|
|||
"new-contract": "New Contract",
|
||||
"new-group": "New group",
|
||||
"new-group-chat": "New group chat",
|
||||
"add-members": "Add members",
|
||||
"new-network": "New network",
|
||||
"new-pin-description": "Enter new 6-digit passcode",
|
||||
"new-public-group-chat": "Join public chat",
|
||||
|
@ -1228,5 +1227,8 @@
|
|||
"audio": "Audio",
|
||||
"update-to-see-image": "Update to latest version to see a nice image here!",
|
||||
"update-to-listen-audio": "Update to latest version to listen to an audio message here!",
|
||||
"update-to-see-sticker": "Update to latest version to see a nice sticker here!"
|
||||
"update-to-see-sticker": "Update to latest version to see a nice sticker here!",
|
||||
"nickname": "Nickname",
|
||||
"add-nickname": "Add a nickname (optional)",
|
||||
"nickname-description": "Nicknames help you identify others in Status.\nOnly you can see the nicknames you’ve added"
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue