Compare commits
1
Commits
test-pr
...
issue-14166
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3c507be976 |
@@ -1,312 +1,123 @@
|
||||
(ns status-im.ui.screens.add-new.new-chat.views
|
||||
(:require [re-frame.core :as re-frame]
|
||||
[status-im.i18n.i18n :as i18n]
|
||||
[status-im.multiaccounts.core :as multiaccounts]
|
||||
[status-im.ui.components.chat-icon.screen :as chat-icon]
|
||||
[quo.design-system.colors :as colors]
|
||||
[status-im.ui.components.icons.icons :as icons]
|
||||
[quo.core :as quo]
|
||||
[status-im.utils.gfycat.core :as gfycat]
|
||||
[status-im.qr-scanner.core :as qr-scanner]
|
||||
[status-im.ui.components.list.views :as list]
|
||||
[status-im.ui.components.react :as react]
|
||||
[status-im.ui.components.topbar :as topbar]
|
||||
[status-im.utils.debounce :as debounce]
|
||||
[status-im.utils.utils :as utils]
|
||||
[reagent.core :as reagent]
|
||||
[quo.react-native :as rn]
|
||||
[clojure.string :as string]
|
||||
[status-im.ethereum.ens :as ens]
|
||||
[quo.platform :as platform]
|
||||
[status-im.utils.identicon :as identicon]
|
||||
[status-im.ui.components.keyboard-avoid-presentation :as kb-presentation]
|
||||
[status-im.ui.components.animation :as animation]
|
||||
[status-im.ui.screens.chat.photos :as photos]
|
||||
[status-im.utils.db :as utils.db]
|
||||
[status-im.ui.components.invite.views :as invite])
|
||||
(:require-macros [status-im.utils.views :as views]))
|
||||
[status-im.utils.core :as utils]
|
||||
[status-im.ui.components.list.views :as list]
|
||||
[status-im.ui.components.invite.events :as invite.events]
|
||||
[quo.theme :as theme]
|
||||
[quo2.components.buttons.button :as button]
|
||||
[quo2.components.avatars.icon-avatar :as icon-avatar]
|
||||
[quo2.components.markdown.text :as text]
|
||||
[quo2.foundations.colors :as colors]))
|
||||
|
||||
(defn- render-row [row]
|
||||
(let [first-name (first (multiaccounts/contact-two-names row false))]
|
||||
[quo/list-item
|
||||
{:title first-name
|
||||
:icon [chat-icon/contact-icon-contacts-tab
|
||||
(multiaccounts/displayed-photo row)]
|
||||
:on-press #(re-frame/dispatch [:chat.ui/start-chat
|
||||
(:public-key row)])}]))
|
||||
(defn chat-no-contacts []
|
||||
[rn/view {:top 128
|
||||
:height 302
|
||||
:padding 12
|
||||
:flex-direction :column
|
||||
:justify-content :space-around
|
||||
:align-items :center}
|
||||
[rn/view {:style {:height 120
|
||||
:width 120
|
||||
:background-color colors/neutral-40}}]
|
||||
[text/text {:size :paragraph-1
|
||||
:weight :semi-bold
|
||||
:style {:color colors/neutral-100}}
|
||||
(i18n/label :you-have-no-contacts)]
|
||||
[text/text {:size :paragraph-2
|
||||
:style {:color colors/neutral-100}}
|
||||
(i18n/label :invite-your-friends)]
|
||||
[button/button {:size 32 :on-press
|
||||
#(re-frame/dispatch [::invite.events/share-link nil])}
|
||||
(i18n/label :invite-friends)]
|
||||
[button/button {:type :grey :size 32 :on-press #(%)}
|
||||
(i18n/label :add-a-contact)]])
|
||||
|
||||
(defn- icon-wrapper [color icon]
|
||||
[react/view
|
||||
{:style {:width 32
|
||||
:height 32
|
||||
:border-radius 25
|
||||
:align-items :center
|
||||
:justify-content :center
|
||||
:background-color color}}
|
||||
icon])
|
||||
(defn contact-item [contact]
|
||||
[rn/touchable-opacity
|
||||
[:<>
|
||||
[rn/view
|
||||
;; [icon-avatar/icon-avatar {:size :medium
|
||||
;; :icon icon
|
||||
;; :color color
|
||||
;; :opacity opacity}]
|
||||
|
||||
(defn- input-icon
|
||||
[state new-contact? entered-nickname blocked?]
|
||||
(let [icon (if new-contact? :main-icons/add :main-icons/arrow-right)]
|
||||
(cond
|
||||
(= state :searching)
|
||||
[icon-wrapper colors/gray
|
||||
[react/activity-indicator {:color colors/white-persist}]]
|
||||
[text/text (:nickname contact)]
|
||||
]]])
|
||||
|
||||
(and (= state :valid) (not blocked?))
|
||||
[react/touchable-highlight
|
||||
{:on-press #(debounce/dispatch-and-chill [:contact.ui/contact-code-submitted new-contact? entered-nickname] 3000)}
|
||||
[icon-wrapper colors/blue
|
||||
[icons/icon icon {:color colors/white-persist}]]]
|
||||
(defn contact-list-key-fn [contact]
|
||||
(:public-key contact))
|
||||
|
||||
:else
|
||||
[icon-wrapper colors/gray
|
||||
[icons/icon icon {:color colors/white-persist}]])))
|
||||
(defn render-fn [contact]
|
||||
[contact-item contact])
|
||||
|
||||
(defn get-validation-label [value]
|
||||
(case value
|
||||
:invalid
|
||||
(i18n/label :t/profile-not-found)
|
||||
:yourself
|
||||
(i18n/label :t/can-not-add-yourself)))
|
||||
(defn chat-contacts-exist [contacts account]
|
||||
;; show contact list
|
||||
[rn/view {:top 128
|
||||
:height 302
|
||||
:padding 12
|
||||
:flex-direction :column
|
||||
:justify-content :space-around
|
||||
:align-items :center}
|
||||
;; user-avatar
|
||||
;; for status: either group-avatar or verified check
|
||||
[list/flat-list
|
||||
{:key-fn contact-list-key-fn
|
||||
;; :getItemLayout get-item-layout
|
||||
;; :on-end-reached #(%)
|
||||
;; :keyboard-should-persist-taps :always
|
||||
:data contacts
|
||||
:render-fn render-fn}]
|
||||
])
|
||||
|
||||
(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))))
|
||||
;; (merge (when key-fn {:keyExtractor (memo-wrap-key-fn key-fn)})
|
||||
;; (when render-fn {:renderItem (memo-wrap-render-fn render-fn render-data)})
|
||||
;; (when separator {:ItemSeparatorComponent (memo-separator-fn separator default-separator?)})
|
||||
;; (when empty-component {:ListEmptyComponent (memo-as-element empty-component)})
|
||||
;; (when header {:ListHeaderComponent (memo-as-element header)})
|
||||
;; (when footer {:ListFooterComponent (memo-as-element footer)}))
|
||||
|
||||
(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)))
|
||||
(def x (atom {}))
|
||||
(defn new-chat []
|
||||
(let [contacts @(re-frame/subscribe [:contacts/active])
|
||||
account @(re-frame/subscribe [:multiaccount])]
|
||||
(swap! x assoc :contacts contacts)
|
||||
(swap! x assoc :account account)
|
||||
[rn/view
|
||||
[rn/view {:height 120
|
||||
:flex-direction :column}
|
||||
[rn/view {:height 20}]
|
||||
[rn/view {:flex-direction :row
|
||||
:align-items :flex-start
|
||||
:justify-content :flex-start
|
||||
:padding-left 20
|
||||
:height 44}
|
||||
[rn/view {:justify-content :center
|
||||
:align-items :center
|
||||
:background-color colors/neutral-10
|
||||
:width 32
|
||||
:height 32
|
||||
:border-radius 10}
|
||||
[rn/touchable-opacity {:on-press #(re-frame/dispatch [:navigate-back])}
|
||||
[icons/icon :main-icons2/close]]]]
|
||||
[rn/view {:flex 1
|
||||
:flex-direction :row
|
||||
:align-items :center
|
||||
:padding-left 20}
|
||||
[text/text {:size :heading-1
|
||||
:weight :semi-bold
|
||||
:style {:color colors/neutral-100}} (i18n/label :new-chat)]]]
|
||||
(if (seq? contacts)
|
||||
(chat-contacts-exist contacts account)
|
||||
(chat-no-contacts))]))
|
||||
|
||||
(defn is-public-key? [k]
|
||||
(and
|
||||
(string? k)
|
||||
(string/starts-with? k "0x")))
|
||||
(defn new-contact [] (fn [] [rn/view]))
|
||||
|
||||
(defn is-valid-username? [username]
|
||||
(let [is-chat-key? (and (is-public-key? username)
|
||||
(= (count username) 132))
|
||||
is-ens? (ens/valid-eth-name-prefix? username)]
|
||||
(or is-chat-key? is-ens?)))
|
||||
|
||||
(defn translate-anim [translate-y-value translate-y-anim-value]
|
||||
(animation/start
|
||||
(animation/timing translate-y-anim-value {:toValue translate-y-value
|
||||
:duration 200
|
||||
:useNativeDriver true})))
|
||||
(views/defview new-chat []
|
||||
(views/letsubs [contacts [:contacts/active]
|
||||
{:keys [state ens-name public-key error]} [:contacts/new-identity]
|
||||
search-value (reagent/atom "")
|
||||
account @(re-frame/subscribe [:multiaccount])
|
||||
on-share #(re-frame/dispatch [:show-popover
|
||||
{:view :share-chat-key
|
||||
:address (account :public-key)
|
||||
:ens-name (account :preferred-name)}])
|
||||
my-profile-button-anim-y (animation/create-value 0)
|
||||
keyboard-show-listener (atom nil)
|
||||
keyboard-hide-listener (atom nil)
|
||||
on-keyboard-show (fn []
|
||||
;; 42 is the bottom position so we translate it by 32 pts to leave 10 as margin
|
||||
(translate-anim 32 my-profile-button-anim-y))
|
||||
on-keyboard-hide (fn []
|
||||
(translate-anim 0 my-profile-button-anim-y))
|
||||
keyboard-show-event (if platform/android? "keyboardDidShow" "keyboardWillShow")
|
||||
keyboard-hide-event (if platform/android? "keyboardDidHide" "keyboardWillHide")]
|
||||
{:component-did-mount
|
||||
(fn [_]
|
||||
(reset! keyboard-show-listener (.addListener react/keyboard keyboard-show-event on-keyboard-show))
|
||||
(reset! keyboard-hide-listener (.addListener react/keyboard keyboard-hide-event on-keyboard-hide)))
|
||||
:component-will-unmount
|
||||
(fn []
|
||||
(some-> ^js @keyboard-show-listener .remove)
|
||||
(some-> ^js @keyboard-hide-listener .remove))}
|
||||
[kb-presentation/keyboard-avoiding-view {:style {:flex 1}}
|
||||
[react/view {:style {:flex 1}}
|
||||
[topbar/topbar
|
||||
{:title (i18n/label :t/new-chat)
|
||||
:modal? true
|
||||
:right-accessories
|
||||
[{:icon :qr
|
||||
:accessibility-label :scan-contact-code-button
|
||||
:on-press #(re-frame/dispatch [::qr-scanner/scan-code
|
||||
{:title (i18n/label :t/new-chat)
|
||||
:handler :contact/qr-code-scanned}])}]}]
|
||||
[react/view {:flex-direction :row
|
||||
:padding 16}
|
||||
[react/view {:flex 1}
|
||||
[quo/text-input
|
||||
{:on-change-text
|
||||
#(do
|
||||
(reset! search-value %)
|
||||
(re-frame/dispatch [:set-in [:contacts/new-identity :state] :empty])
|
||||
(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 nil] 3000))
|
||||
:placeholder (i18n/label :t/enter-contact-code)
|
||||
:show-cancel false
|
||||
:accessibility-label :enter-contact-code-input
|
||||
:auto-capitalize :none
|
||||
:return-key-type :go
|
||||
:monospace true
|
||||
:auto-correct false}]]]
|
||||
[react/scroll-view {:style {:flex 1}
|
||||
:keyboard-dismiss-mode :on-drag
|
||||
:keyboard-should-persist-taps :handled}
|
||||
[react/view (when (and
|
||||
(= (count contacts) 0)
|
||||
(= @search-value ""))
|
||||
{:flex 1})
|
||||
(if (and
|
||||
(= (count contacts) 0)
|
||||
(= @search-value ""))
|
||||
[react/view {:flex 1
|
||||
:align-items :center
|
||||
:padding-horizontal 58
|
||||
:padding-top 160}
|
||||
[quo/text {:size :base
|
||||
:align :center
|
||||
:color :secondary}
|
||||
(i18n/label :t/you-dont-have-contacts-invite-friends)]
|
||||
[invite/button]]
|
||||
[list/flat-list {:data (filter-contacts @search-value contacts)
|
||||
:key-fn :address
|
||||
:render-fn render-row}])]
|
||||
(when-not (= @search-value "")
|
||||
[react/view
|
||||
[quo/text {:style {:margin-horizontal 16
|
||||
:margin-vertical 14}
|
||||
: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 #(do
|
||||
(debounce/dispatch-and-chill [:contact.ui/contact-code-submitted false] 3000)
|
||||
(re-frame/dispatch [:search/home-filter-changed nil]))}
|
||||
(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))])])]
|
||||
(when-not (and
|
||||
(= (count contacts) 0)
|
||||
(= @search-value ""))
|
||||
[react/animated-view {:style {:height 36
|
||||
:width 124
|
||||
:position :absolute
|
||||
:bottom 42
|
||||
:transform [{:translateY my-profile-button-anim-y}]
|
||||
:align-self :center}}
|
||||
[react/touchable-opacity {:style {:padding-horizontal 2
|
||||
:height 36
|
||||
:width 124
|
||||
:background-color colors/blue
|
||||
:border-radius 18
|
||||
:elevation 4
|
||||
:shadow-offset {:width 0 :height 4}
|
||||
:shadow-color "rgba(0, 34, 51, 0.16)"
|
||||
:shadow-radius 4
|
||||
:shadow-opacity 1}
|
||||
:on-press on-share}
|
||||
[react/view {:style {:flex 1
|
||||
:flex-direction :row
|
||||
:align-items :center}}
|
||||
[photos/photo
|
||||
(multiaccounts/displayed-photo account)
|
||||
{:size 32
|
||||
:accessibility-label :current-account-photo}]
|
||||
[quo/text {:size :base
|
||||
:weight :medium
|
||||
:color :inverse
|
||||
:style {:margin-left 6}}
|
||||
(i18n/label :t/my-profile)]]]])]]))
|
||||
|
||||
(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}])
|
||||
|
||||
(defn new-contact []
|
||||
(let [entered-nickname (reagent/atom "")]
|
||||
(fn []
|
||||
(let [{:keys [state ens-name public-key error]} @(re-frame/subscribe [:contacts/new-identity])
|
||||
blocked? (and
|
||||
(utils.db/valid-public-key? (or public-key ""))
|
||||
@(re-frame/subscribe [:contacts/contact-blocked? public-key]))]
|
||||
[react/view {:style {:flex 1}}
|
||||
[topbar/topbar
|
||||
{:title (i18n/label :t/new-contact)
|
||||
:modal? true
|
||||
:right-accessories
|
||||
[{:icon :qr
|
||||
:accessibility-label :scan-contact-code-button
|
||||
:on-press #(re-frame/dispatch [::qr-scanner/scan-code
|
||||
{:title (i18n/label :t/new-contact)
|
||||
:handler :contact/qr-code-scanned
|
||||
:new-contact? true
|
||||
:nickname @entered-nickname}])}]}]
|
||||
[react/view {:flex-direction :row
|
||||
:padding 16}
|
||||
[react/view {:flex 1
|
||||
:padding-right 16}
|
||||
[quo/text-input
|
||||
{:on-change-text
|
||||
#(do
|
||||
(re-frame/dispatch [:set-in [:contacts/new-identity :state] :searching])
|
||||
(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 @entered-nickname] 3000))
|
||||
:placeholder (i18n/label :t/enter-contact-code)
|
||||
:show-cancel false
|
||||
:accessibility-label :enter-contact-code-input
|
||||
:auto-capitalize :none
|
||||
:return-key-type :go}]]
|
||||
[react/view {:justify-content :center
|
||||
:align-items :center}
|
||||
[input-icon state true @entered-nickname blocked?]]]
|
||||
[react/view {:min-height 30 :justify-content :flex-end :margin-bottom 16}
|
||||
[quo/text {:style {:margin-horizontal 16}
|
||||
: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 "")]]
|
||||
[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")]]]))))
|
||||
(comment
|
||||
;; open new-chat
|
||||
(re-frame/dispatch [:open-modal :new-chat])
|
||||
;; close it
|
||||
(re-frame/dispatch [:navigate-back])
|
||||
)
|
||||
|
||||
@@ -594,14 +594,15 @@
|
||||
|
||||
;;MODALS
|
||||
|
||||
;[Chat] New Chat
|
||||
;; [Chat] New Chat
|
||||
{:name :new-chat
|
||||
:on-focus [::new-chat.events/new-chat-focus]
|
||||
;;TODO accessories
|
||||
:options {:topBar {:visible false}}
|
||||
:component new-chat/new-chat}
|
||||
:component new-chat/new-chat
|
||||
}
|
||||
|
||||
;[Chat] New Public chat
|
||||
;; [Chat] New Public chat
|
||||
{:name :new-public-chat
|
||||
:insets {:bottom true}
|
||||
:options {:topBar {:title {:text (i18n/label :t/new-public-group-chat)}}}
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
"add-an-account": "Add an account",
|
||||
"add-bootnode": "Add bootnode",
|
||||
"add-contact": "Add contact",
|
||||
"add-a-contact": "Add a contact",
|
||||
"add-custom-token": "Add custom token",
|
||||
"add-mailserver": "Add Status node",
|
||||
"add-members": "Add members",
|
||||
@@ -197,6 +198,7 @@
|
||||
"community-share-title": "Share",
|
||||
"invite": "Invite",
|
||||
"invite-contacts": "Invite people from contacts list",
|
||||
"invite-your-friends": "Invite your friends and family to Status",
|
||||
"create-channel": "Create a channel",
|
||||
"import-community": "Import a community",
|
||||
"import-community-title": "Import a community",
|
||||
@@ -947,6 +949,7 @@
|
||||
"no": "No",
|
||||
"no-collectibles": "No collectibles available",
|
||||
"no-contacts": "No contacts yet",
|
||||
"you-have-no-contacts": "You have no contacts",
|
||||
"no-keycard-applet-on-card": "No Keycard applet on card",
|
||||
"no-messages": "No messages",
|
||||
"no-pairing-slots-available": "This card is already paired to 5 devices and cannot pair to this one. Please use one of the paired devices, log in with this card and free up pairing slots on the card",
|
||||
|
||||
Reference in New Issue
Block a user