Improved Start New Private Chat interface

Signed-off-by: Brian Sztamfater <brian@status.im>
This commit is contained in:
Brian Sztamfater 2021-03-29 19:46:50 -03:00 committed by Brian Sztamfater
parent eea5b4eb0f
commit 31ba933917
No known key found for this signature in database
GPG Key ID: 59EB921E0706B48F
4 changed files with 134 additions and 60 deletions

View File

@ -83,7 +83,7 @@
[icons/icon icon {:color icon-color}]])]))) [icons/icon icon {:color icon-color}]])])))
(defn title-column (defn title-column
[{:keys [title text-color subtitle subtitle-max-lines [{:keys [title text-color subtitle subtitle-max-lines subtitle-secondary
title-accessibility-label size text-size title-text-weight title-accessibility-label size text-size title-text-weight
right-side-present?]}] right-side-present?]}]
[rn/view {:style (merge (:tiny spacing/padding-horizontal) [rn/view {:style (merge (:tiny spacing/padding-horizontal)
@ -105,14 +105,37 @@
:size text-size} :size text-size}
title] title]
title) title)
(if (string? subtitle) (if (string? subtitle-secondary)
[text/text {:weight :regular [rn/view {:flex-direction :row}
:color :secondary [text/text {:style {:max-width "56.5%"}
:ellipsize-mode :tail :weight :regular
:number-of-lines subtitle-max-lines :color :secondary
:size text-size} :ellipsize-mode :tail
subtitle] :number-of-lines subtitle-max-lines
subtitle)] :size text-size}
subtitle]
[text/text {:style {:width "7%" :text-align :center}
:weight :regular
:color :secondary
:ellipsize-mode :middle
:number-of-lines subtitle-max-lines
:size text-size}
"•"]
[text/text {:style {:max-width "36.5%"}
:weight :regular
:color :secondary
:ellipsize-mode :middle
:number-of-lines subtitle-max-lines
:size text-size}
subtitle-secondary]]
(if (string? subtitle)
[text/text {:weight :regular
:color :secondary
:ellipsize-mode :tail
:number-of-lines subtitle-max-lines
:size text-size}
subtitle]
subtitle))]
title title
(if (string? title) (if (string? title)
@ -170,7 +193,7 @@
(defn list-item (defn list-item
[{:keys [theme accessory disabled subtitle-max-lines icon icon-container-style [{:keys [theme accessory disabled subtitle-max-lines icon icon-container-style
left-side-alignment left-side-alignment
title subtitle active on-press on-long-press chevron size text-size title subtitle subtitle-secondary active on-press on-long-press chevron size text-size
accessory-text accessibility-label title-accessibility-label accessory-text accessibility-label title-accessibility-label
haptic-feedback haptic-type error animated animated-accessory? title-text-weight] haptic-feedback haptic-type error animated animated-accessory? title-text-weight]
:or {subtitle-max-lines 1 :or {subtitle-max-lines 1
@ -223,6 +246,7 @@
:text-size text-size :text-size text-size
:subtitle subtitle :subtitle subtitle
:subtitle-max-lines subtitle-max-lines :subtitle-max-lines subtitle-max-lines
:subtitle-secondary subtitle-secondary
:right-side-present? (or accessory chevron)}] :right-side-present? (or accessory chevron)}]
[right-side {:chevron chevron [right-side {:chevron chevron
:active active :active active

View File

@ -13,17 +13,22 @@
[status-im.ui.components.topbar :as topbar] [status-im.ui.components.topbar :as topbar]
[status-im.utils.debounce :as debounce] [status-im.utils.debounce :as debounce]
[status-im.utils.utils :as utils] [status-im.utils.utils :as utils]
[reagent.core :as reagent]) [reagent.core :as reagent]
[quo.react-native :as rn]
[clojure.string :as string]
[status-im.ui.components.invite.views :as invite]
[status-im.ethereum.ens :as ens]
[quo.platform :as platform]
[status-im.transport.filters.core :as filters]
[status-im.utils.identicon :as identicon])
(:require-macros [status-im.utils.views :as views])) (:require-macros [status-im.utils.views :as views]))
(defn- render-row [row] (defn- render-row [row]
(let [[first-name second-name] (multiaccounts/contact-two-names row false)] (let [first-name (first (multiaccounts/contact-two-names row false))]
[quo/list-item [quo/list-item
{:title first-name {:title first-name
:subtitle second-name
:icon [chat-icon/contact-icon-contacts-tab :icon [chat-icon/contact-icon-contacts-tab
(multiaccounts/displayed-photo row)] (multiaccounts/displayed-photo row)]
:chevron true
:on-press #(re-frame/dispatch [:chat.ui/start-chat :on-press #(re-frame/dispatch [:chat.ui/start-chat
(:public-key row)])}])) (:public-key row)])}]))
@ -57,13 +62,33 @@
(defn get-validation-label [value] (defn get-validation-label [value]
(case value (case value
:invalid :invalid
(i18n/label :t/user-not-found) (i18n/label :t/profile-not-found)
:yourself :yourself
(i18n/label :t/can-not-add-yourself))) (i18n/label :t/can-not-add-yourself)))
(defn search-contacts [filter-text {:keys [name alias nickname]}]
(or
(string/includes? (string/lower-case (str name)) filter-text)
(string/includes? (string/lower-case (str alias)) filter-text)
(when nickname
(string/includes? (string/lower-case (str nickname)) filter-text))))
(defn filter-contacts [filter-text contacts]
(let [lower-filter-text (string/lower-case filter-text)]
(if filter-text
(filter (partial search-contacts lower-filter-text) contacts)
contacts)))
(defn is-valid-username? [username]
(let [is-chat-key? (and (filters/is-public-key? username)
(= (count username) 132))
is-ens? (ens/valid-eth-name-prefix? username)]
(or is-chat-key? is-ens?)))
(views/defview new-chat [] (views/defview new-chat []
(views/letsubs [contacts [:contacts/active] (views/letsubs [contacts [:contacts/active]
{:keys [state ens-name public-key error]} [:contacts/new-identity]] {:keys [state ens-name public-key error]} [:contacts/new-identity]
search-value (reagent/atom "")]
[react/view {:style {:flex 1}} [react/view {:style {:flex 1}}
[topbar/topbar [topbar/topbar
{:title (i18n/label :t/new-chat) {:title (i18n/label :t/new-chat)
@ -76,11 +101,11 @@
:handler :contact/qr-code-scanned}])}]}] :handler :contact/qr-code-scanned}])}]}]
[react/view {:flex-direction :row [react/view {:flex-direction :row
:padding 16} :padding 16}
[react/view {:flex 1 [react/view {:flex 1}
:padding-right 16}
[quo/text-input [quo/text-input
{:on-change-text {:on-change-text
#(do #(do
(reset! search-value %)
(re-frame/dispatch [:set-in [:contacts/new-identity :state] :searching]) (re-frame/dispatch [:set-in [:contacts/new-identity :state] :searching])
(debounce/debounce-and-dispatch [:new-chat/set-new-identity %] 600)) (debounce/debounce-and-dispatch [:new-chat/set-new-identity %] 600))
:on-submit-editing :on-submit-editing
@ -90,35 +115,59 @@
:show-cancel false :show-cancel false
:accessibility-label :enter-contact-code-input :accessibility-label :enter-contact-code-input
:auto-capitalize :none :auto-capitalize :none
:return-key-type :go}]] :return-key-type :go
[react/view {:justify-content :center :monospace true}]]]
:align-items :center} [react/view (if (and
[input-icon state false nil]]] (= (count contacts) 0)
[react/view {:min-height 30 :justify-content :flex-end} (= @search-value ""))
[quo/text {:style {:margin-horizontal 16} {:flex 1}
:size :small {:justify-content :flex-end})
:align :center (if (and
:color :secondary} (= (count contacts) 0)
(cond (= state :error) (= @search-value ""))
(get-validation-label error) [react/view {:flex 1
:align-items :center
(= state :valid) :padding-horizontal 58
(str (if ens-name :padding-top 160}
ens-name [quo/text {:size :base
(gfycat/generate-gfy public-key)) :align :center
" • ") :color :secondary}
(i18n/label :t/you-dont-have-contacts-invite-friends)]
:else "") [invite/button]]
(when (= state :valid) [list/flat-list {:data (filter-contacts @search-value contacts)
[quo/text {:monospace true :key-fn :address
:size :inherit :render-fn render-row
:color :inherit} :enableEmptySections true
(utils/get-shortened-address public-key)])]] :keyboardShouldPersistTaps :always}])]
[list/flat-list {:data contacts (when-not (= @search-value "")
:key-fn :address [react/view
:render-fn render-row [quo/text {:style {:margin-horizontal 16
:enableEmptySections true :margin-vertical 14}
:keyboardShouldPersistTaps :always}]])) :size :base
:align :left
:color :secondary}
(i18n/label :t/non-contacts)]
(when (and (= state :searching)
(is-valid-username? @search-value))
[rn/activity-indicator {:color colors/gray
:size (if platform/android? :large :small)}])
(if (= state :valid)
[quo/list-item
(merge
{:title (or ens-name (gfycat/generate-gfy public-key))
:subtitle (if ens-name (gfycat/generate-gfy public-key) (utils/get-shortened-address public-key))
:icon [chat-icon/contact-icon-contacts-tab
(identicon/identicon public-key)]
:on-press #(re-frame/dispatch [:chat.ui/start-chat public-key])}
(when ens-name {:subtitle-secondary public-key}))]
[quo/text {:style {:margin-horizontal 16}
:size :base
:align :center
:color :secondary}
(if (is-valid-username? @search-value)
(when (= state :error)
(get-validation-label error))
(i18n/label :t/invalid-username-or-key))])])]))
(defn- nickname-input [entered-nickname] (defn- nickname-input [entered-nickname]
[quo/text-input [quo/text-input

View File

@ -95,13 +95,13 @@ class TestChatManagement(SingleDeviceTestCase):
chat.public_key_edit_box.clear() chat.public_key_edit_box.clear()
chat.public_key_edit_box.set_value(invalid_chat_key) chat.public_key_edit_box.set_value(invalid_chat_key)
chat.confirm() chat.confirm()
if not home.element_by_translation_id("user-not-found").is_element_displayed(): if not home.element_by_translation_id("profile-not-found").is_element_displayed():
self.errors.append('Error is not shown for invalid public key') self.errors.append('Error is not shown for invalid public key')
home.just_fyi("Check that valid ENS is resolved") home.just_fyi("Check that valid ENS is resolved")
chat.public_key_edit_box.clear() chat.public_key_edit_box.clear()
chat.public_key_edit_box.set_value(ens_user_ropsten['ens']) chat.public_key_edit_box.set_value(ens_user_ropsten['ens'])
resolved_ens = chat.get_resolved_chat_key('%s.stateofus.eth' % ens_user_ropsten['ens'], ens_user_ropsten['public_key']) resolved_ens = '%s.stateofus.eth' % ens_user_ropsten['ens']
if not chat.element_by_text(resolved_ens).is_element_displayed(10): if not chat.element_by_text(resolved_ens).is_element_displayed(10):
self.errors.append('ENS name is not resolved after pasting chat key') self.errors.append('ENS name is not resolved after pasting chat key')
home.back_button.click() home.back_button.click()
@ -116,8 +116,7 @@ class TestChatManagement(SingleDeviceTestCase):
chat.public_key_edit_box.paste_text_from_clipboard() chat.public_key_edit_box.paste_text_from_clipboard()
if chat.public_key_edit_box.text != public_key: if chat.public_key_edit_box.text != public_key:
self.errors.append('Public key is not pasted from clipboard') self.errors.append('Public key is not pasted from clipboard')
expected_resolved_name = chat.get_resolved_chat_key(basic_user['username'], public_key) if not chat.element_by_text(basic_user['username']).is_element_displayed():
if not chat.element_by_text(expected_resolved_name).is_element_displayed():
self.errors.append('3 random-name is not resolved after pasting chat key') self.errors.append('3 random-name is not resolved after pasting chat key')
chat.public_key_edit_box.click() chat.public_key_edit_box.click()
chat.confirm_until_presence_of_element(chat.chat_message_input) chat.confirm_until_presence_of_element(chat.chat_message_input)
@ -585,10 +584,9 @@ class TestChatManagementMultipleDevice(MultipleDeviceTestCase):
'Check that user is added to contacts below "Start new chat" and you redirected to 1-1 on tap') 'Check that user is added to contacts below "Start new chat" and you redirected to 1-1 on tap')
home_1.plus_button.click() home_1.plus_button.click()
home_1.start_new_chat_button.click() home_1.start_new_chat_button.click()
for name in (nickname, username_2): if not home_1.element_by_text(nickname).is_element_displayed():
if not home_1.element_by_text(name).is_element_displayed(): home_1.driver.fail('List of contacts below "Start new chat" does not contain added user')
home_1.driver.fail('List of contacts below "Start new chat" does not contain added user') home_1.element_by_text(nickname).click()
home_1.element_by_text(username_2).click()
if not chat_1.chat_message_input.is_element_displayed(): if not chat_1.chat_message_input.is_element_displayed():
home_1.driver.fail('No redirect to 1-1 chat if tap on Contact below "Start new chat"') home_1.driver.fail('No redirect to 1-1 chat if tap on Contact below "Start new chat"')
for element in (chat_1.chat_message_input, chat_1.element_by_text(nickname)): for element in (chat_1.chat_message_input, chat_1.element_by_text(nickname)):
@ -599,7 +597,7 @@ class TestChatManagementMultipleDevice(MultipleDeviceTestCase):
device_1.just_fyi('Remove user from contacts') device_1.just_fyi('Remove user from contacts')
chat_1.profile_button.click() chat_1.profile_button.click()
userprofile = profile_1.open_contact_from_profile(username_2) userprofile = profile_1.open_contact_from_profile(nickname)
userprofile.remove_from_contacts.click() userprofile.remove_from_contacts.click()
if userprofile.remove_from_contacts.is_element_displayed(): if userprofile.remove_from_contacts.is_element_displayed():
self.errors.append("'Remove from contacts' is not changed to 'Add to contacts'") self.errors.append("'Remove from contacts' is not changed to 'Add to contacts'")
@ -608,7 +606,7 @@ class TestChatManagementMultipleDevice(MultipleDeviceTestCase):
device_1.just_fyi('Check that user is removed from contact list in profile') device_1.just_fyi('Check that user is removed from contact list in profile')
userprofile.back_button.click() userprofile.back_button.click()
if profile_1.element_by_text(username_2).is_element_displayed(): if profile_1.element_by_text(nickname).is_element_displayed():
self.errors.append('List of contacts in profile contains removed user') self.errors.append('List of contacts in profile contains removed user')
profile_1.home_button.click(desired_view='chat') profile_1.home_button.click(desired_view='chat')
if not chat_1.add_to_contacts.is_element_displayed(): if not chat_1.add_to_contacts.is_element_displayed():

View File

@ -503,7 +503,7 @@
"enter-a-private-key": "Enter a private key", "enter-a-private-key": "Enter a private key",
"enter-a-seed-phrase": "Enter a seed phrase", "enter-a-seed-phrase": "Enter a seed phrase",
"enter-address": "Enter address", "enter-address": "Enter address",
"enter-contact-code": "Enter ENS username or chat key", "enter-contact-code": "ENS (vitalik94) or chat key (0x04…)",
"enter-pair-code": "Enter your pairing code", "enter-pair-code": "Enter your pairing code",
"pair-code-placeholder": "Pair code...", "pair-code-placeholder": "Pair code...",
"enter-pair-code-description": "Pairing code was displayed to you during the Keycard setup", "enter-pair-code-description": "Pairing code was displayed to you during the Keycard setup",
@ -640,6 +640,7 @@
"invalid-number": "Invalid number", "invalid-number": "Invalid number",
"invalid-pairing-password": "Invalid pairing password", "invalid-pairing-password": "Invalid pairing password",
"invalid-range": "Invalid format, must be between {{min}} and {{max}}", "invalid-range": "Invalid format, must be between {{min}} and {{max}}",
"invalid-username-or-key": "Invalid username or chat key",
"join-me": "Hey join me on Status: {{url}}", "join-me": "Hey join me on Status: {{url}}",
"join-a-community": "or join a community", "join-a-community": "or join a community",
"http-gateway-error": "Oops, request failed!", "http-gateway-error": "Oops, request failed!",
@ -1304,6 +1305,7 @@
"you-are-all-set-description": "If you lose your phone, you can now access your funds and chat key using your seed phrase", "you-are-all-set-description": "If you lose your phone, you can now access your funds and chat key using your seed phrase",
"you-can-change-account": "You can change the account name and color to what you wish", "you-can-change-account": "You can change the account name and color to what you wish",
"you-dont-have-stickers": "You dont have any stickers yet", "you-dont-have-stickers": "You dont have any stickers yet",
"you-dont-have-contacts-invite-friends": "You dont have any contacts yet.\nInvite your friends to start chatting.",
"your-contact-code": "Granting access authorizes this DApp to retrieve your chat key", "your-contact-code": "Granting access authorizes this DApp to retrieve your chat key",
"your-data-belongs-to-you": "If you lose your seed phrase you lose your data and funds", "your-data-belongs-to-you": "If you lose your seed phrase you lose your data and funds",
"your-data-belongs-to-you-description": "If you lose access, for example by losing your phone, you can only access your keys with your seed phrase. No one, but you has your seed phrase. Write it down. Keep it safe", "your-data-belongs-to-you-description": "If you lose access, for example by losing your phone, you can only access your keys with your seed phrase. No one, but you has your seed phrase. Write it down. Keep it safe",
@ -1338,7 +1340,7 @@
"add-seed-account": "Add account with a seed phrase", "add-seed-account": "Add account with a seed phrase",
"account-exists-title": "Account already exists", "account-exists-title": "Account already exists",
"add-private-key-account": "Add account from private key", "add-private-key-account": "Add account from private key",
"user-not-found": "User not found", "profile-not-found": "Profile not found",
"waku-bloom-filter-mode": "Waku bloom filter mode", "waku-bloom-filter-mode": "Waku bloom filter mode",
"appearance": "Appearance", "appearance": "Appearance",
"preference": "Preference", "preference": "Preference",
@ -1496,5 +1498,6 @@
"rpc-usage-reset": "Reset", "rpc-usage-reset": "Reset",
"rpc-usage-filter": "Filter methods", "rpc-usage-filter": "Filter methods",
"rpc-usage-copy": "Copy", "rpc-usage-copy": "Copy",
"community-message-preview": "Invitation to join {{community-name}}" "community-message-preview": "Invitation to join {{community-name}}",
"non-contacts": "Non contacts"
} }