[#10385] [perfromance][chat] Optimize input field in chat
Signed-off-by: Andrey Shovkoplyas <motor4ik@gmail.com>
This commit is contained in:
parent
f280d5a90e
commit
f73754af34
|
@ -32,12 +32,12 @@
|
|||
"keyboardDidShow"
|
||||
(fn [e]
|
||||
(let [h (.. e -endCoordinates -height)]
|
||||
(dispatch [:set :keyboard-height h])
|
||||
(dispatch [:set :keyboard-max-height h]))))
|
||||
(dispatch-sync [:set :keyboard-height h])
|
||||
(dispatch-sync [:set :keyboard-max-height h]))))
|
||||
(.addListener react/keyboard
|
||||
"keyboardDidHide"
|
||||
(fn [_]
|
||||
(dispatch [:set :keyboard-height 0])))
|
||||
(dispatch-sync [:set :keyboard-height 0])))
|
||||
(.hide react/splash-screen)
|
||||
(.addEventListener react/app-state "change" app-state-change-handler)
|
||||
(.addEventListener rn-dependencies/react-native-languages "change" on-languages-change)
|
||||
|
|
|
@ -308,3 +308,8 @@
|
|||
(fx/merge (assoc-in cofx [:db :contacts/identity] identity)
|
||||
(contact.core/create-contact identity)
|
||||
(navigation/navigate-to-cofx :profile nil)))
|
||||
|
||||
(fx/defn input-on-focus
|
||||
{:events [:chat.ui/input-on-focus]}
|
||||
[{db :db}]
|
||||
{:db (set-chat-ui-props db {:input-bottom-sheet nil})})
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
"Set input text for current-chat. Takes db and input text and cofx
|
||||
as arguments and returns new fx. Always clear all validation messages."
|
||||
[{{:keys [current-chat-id] :as db} :db} new-input]
|
||||
{:db (assoc-in db [:chats current-chat-id :input-text] (text->emoji new-input))})
|
||||
{:db (assoc-in db [:chat/inputs current-chat-id :input-text] (text->emoji new-input))})
|
||||
|
||||
(defn- start-cooldown [{:keys [db]} cooldowns]
|
||||
{:dispatch-later [{:dispatch [:chat/disable-cooldown]
|
||||
|
@ -67,6 +67,12 @@
|
|||
(when-let [cmp-ref (get-in chat-ui-props [current-chat-id ref])]
|
||||
{::focus-rn-component cmp-ref}))
|
||||
|
||||
(fx/defn chat-input-clear
|
||||
"Returns fx for focusing on active chat input reference"
|
||||
[{{:keys [current-chat-id chat-ui-props]} :db} ref]
|
||||
(when-let [cmp-ref (get-in chat-ui-props [current-chat-id ref])]
|
||||
{::clear-rn-component cmp-ref}))
|
||||
|
||||
(fx/defn reply-to-message
|
||||
"Sets reference to previous chat message and focuses on input"
|
||||
[{:keys [db] :as cofx} message-id]
|
||||
|
@ -103,6 +109,7 @@
|
|||
:response-to message-id
|
||||
:ens-name preferred-name})
|
||||
(set-chat-input-text nil)
|
||||
(chat-input-clear :input-ref)
|
||||
(process-cooldown)))))
|
||||
|
||||
(fx/defn send-sticker-fx
|
||||
|
@ -117,15 +124,15 @@
|
|||
(fx/defn send-current-message
|
||||
"Sends message from current chat input"
|
||||
[{{:keys [current-chat-id] :as db} :db :as cofx}]
|
||||
(let [{:keys [input-text]} (get-in db [:chats current-chat-id])]
|
||||
(let [{:keys [input-text]} (get-in db [:chat/inputs current-chat-id])]
|
||||
(plain-text-message-fx input-text current-chat-id cofx)))
|
||||
|
||||
(fx/defn send-transaction-result
|
||||
{:events [:chat/send-transaction-result]}
|
||||
[cofx chat-id params result]
|
||||
[cofx chat-id params result])
|
||||
;;TODO: should be implemented on status-go side
|
||||
;;see https://github.com/status-im/team-core/blob/6c3d67d8e8bd8500abe52dab06a59e976ec942d2/rfc-001.md#status-gostatus-react-interface
|
||||
)
|
||||
|
||||
|
||||
;; effects
|
||||
|
||||
|
@ -136,3 +143,11 @@
|
|||
(.focus ref)
|
||||
(catch :default e
|
||||
(log/debug "Cannot focus the reference")))))
|
||||
|
||||
(re-frame/reg-fx
|
||||
::clear-rn-component
|
||||
(fn [ref]
|
||||
(try
|
||||
(.clear ref)
|
||||
(catch :default e
|
||||
(log/debug "Cannot clear the reference")))))
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
(s/def :chat/loaded-chats (s/nilable seq?))
|
||||
(s/def :chat/bot-db (s/nilable map?))
|
||||
(s/def :chat/cooldowns (s/nilable number?)) ; number of cooldowns given for spamming send button
|
||||
(s/def :chat/inputs (s/nilable map?))
|
||||
(s/def :chat/cooldown-enabled? (s/nilable boolean?))
|
||||
(s/def :chat/last-outgoing-message-sent-at (s/nilable number?))
|
||||
(s/def :chat/spam-messages-frequency (s/nilable number?)) ; number of consecutive spam messages sent
|
||||
|
|
|
@ -30,11 +30,11 @@
|
|||
"keyboardWillShow"
|
||||
(fn [e]
|
||||
(let [h (.. e -endCoordinates -height)]
|
||||
(dispatch [:set :keyboard-height h])
|
||||
(dispatch [:set :keyboard-max-height h]))))
|
||||
(dispatch-sync [:set :keyboard-height h])
|
||||
(dispatch-sync [:set :keyboard-max-height h]))))
|
||||
(.addListener react/keyboard
|
||||
"keyboardWillHide"
|
||||
#(dispatch [:set :keyboard-height 0]))
|
||||
#(dispatch-sync [:set :keyboard-height 0]))
|
||||
(.hide react/splash-screen)
|
||||
(.addEventListener react/app-state "change" app-state-change-handler)
|
||||
(.addEventListener rn-dependencies/react-native-languages "change" on-languages-change)
|
||||
|
|
|
@ -126,6 +126,7 @@
|
|||
(reg-root-key-sub :group-chat-profile/editing? :group-chat-profile/editing?)
|
||||
(reg-root-key-sub :group-chat-profile/profile :group-chat-profile/profile)
|
||||
(reg-root-key-sub :selected-participants :selected-participants)
|
||||
(reg-root-key-sub :chat/inputs :chat/inputs)
|
||||
|
||||
;;browser
|
||||
(reg-root-key-sub :browsers :browser/browsers)
|
||||
|
@ -607,26 +608,15 @@
|
|||
:else 272)))
|
||||
|
||||
(re-frame/reg-sub
|
||||
:chats/input-margin
|
||||
:chats/empty-chat-panel-height
|
||||
:<- [:keyboard-height]
|
||||
:<- [:chats/current-chat-ui-prop :input-bottom-sheet]
|
||||
(fn [[kb-height input-bottom-sheet]]
|
||||
(cond
|
||||
;; During the transition of bottom sheet and close of keyboard happens
|
||||
;; The heights are summed up and input grows too much
|
||||
(not (nil? input-bottom-sheet))
|
||||
0
|
||||
|
||||
(and platform/iphone-x? (> kb-height 0))
|
||||
(- kb-height tabs.styles/minimized-tabs-height 34)
|
||||
|
||||
platform/ios?
|
||||
(+ kb-height (- (if (> kb-height 0)
|
||||
(fn [kb-height]
|
||||
(if (and platform/ios? (pos? kb-height))
|
||||
(- kb-height
|
||||
tabs.styles/minimized-tabs-height
|
||||
(if platform/iphone-x? 34 0))
|
||||
0)))
|
||||
|
||||
:default 0)))
|
||||
|
||||
(re-frame/reg-sub
|
||||
:chats/active-chats
|
||||
:<- [:contacts/contacts]
|
||||
|
@ -706,9 +696,10 @@
|
|||
|
||||
(re-frame/reg-sub
|
||||
:chats/current-chat-input-text
|
||||
:<- [:chats/current-raw-chat]
|
||||
(fn [chat]
|
||||
(:input-text chat)))
|
||||
:<- [:chats/current-chat-id]
|
||||
:<- [:chat/inputs]
|
||||
(fn [[chat-id inputs]]
|
||||
(get-in inputs [chat-id :input-text])))
|
||||
|
||||
(re-frame/reg-sub
|
||||
:chats/current-chat
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
(ns status-im.ui.screens.chat.input.input
|
||||
(:require-macros [status-im.utils.views :refer [defview letsubs]])
|
||||
(:require [clojure.string :as string]
|
||||
[reagent.core :as reagent]
|
||||
[re-frame.core :as re-frame]
|
||||
[status-im.ui.screens.chat.styles.input.input :as style]
|
||||
[status-im.ui.screens.chat.styles.message.message :as message-style]
|
||||
|
@ -12,103 +11,26 @@
|
|||
[status-im.ui.components.colors :as colors]
|
||||
[status-im.ui.components.react :as react]
|
||||
[status-im.ui.components.icons.vector-icons :as vector-icons]
|
||||
[status-im.utils.platform :as platform]
|
||||
[status-im.utils.config :as config]
|
||||
[status-im.ui.screens.chat.stickers.views :as stickers]
|
||||
[status-im.ui.screens.chat.extensions.views :as extensions]))
|
||||
|
||||
(defview basic-text-input [{:keys [set-container-width-fn height single-line-input?]}]
|
||||
(letsubs [input-text [:chats/current-chat-input-text]
|
||||
cooldown-enabled? [:chats/cooldown-enabled?]]
|
||||
(defn basic-text-input [input-text cooldown-enabled?]
|
||||
[react/text-input
|
||||
(merge
|
||||
{:ref #(when % (re-frame/dispatch [:chat.ui/set-chat-ui-props {:input-ref %}]))
|
||||
:accessibility-label :chat-message-input
|
||||
:multiline (not single-line-input?)
|
||||
:multiline true
|
||||
:default-value (or input-text "")
|
||||
:editable (not cooldown-enabled?)
|
||||
:blur-on-submit false
|
||||
:on-focus #(re-frame/dispatch [:chat.ui/set-chat-ui-props {:input-bottom-sheet nil}])
|
||||
:on-submit-editing #(when single-line-input?
|
||||
(re-frame/dispatch [:chat.ui/send-current-message]))
|
||||
:on-layout #(set-container-width-fn (.-width (.-layout (.-nativeEvent %))))
|
||||
:on-focus #(re-frame/dispatch-sync [:chat.ui/input-on-focus])
|
||||
:on-change #(re-frame/dispatch [:chat.ui/set-chat-input-text (.-text (.-nativeEvent %))])
|
||||
:on-selection-change #(let [s (-> (.-nativeEvent %)
|
||||
(.-selection))
|
||||
end (.-end s)]
|
||||
(re-frame/dispatch [:chat.ui/set-chat-ui-props {:selection end}]))
|
||||
:style (style/input-view single-line-input?)
|
||||
:style style/input-view
|
||||
:placeholder (if cooldown-enabled?
|
||||
(i18n/label :cooldown/text-input-disabled)
|
||||
(i18n/label :t/type-a-message))
|
||||
:placeholder-text-color colors/gray
|
||||
:auto-capitalize :sentences}
|
||||
(when cooldown-enabled?
|
||||
{:placeholder (i18n/label :cooldown/text-input-disabled)}))]))
|
||||
|
||||
(defview basic-text-input-desktop [{:keys [set-container-width-fn height single-line-input? set-text state-text]}]
|
||||
(letsubs [inp-ref (atom nil)
|
||||
cooldown-enabled? [:chats/cooldown-enabled?]]
|
||||
[react/text-input
|
||||
(merge
|
||||
{:ref #(when % (do
|
||||
(reset! inp-ref %)
|
||||
(re-frame/dispatch [:chat.ui/set-chat-ui-props {:input-ref %}])))
|
||||
:accessibility-label :chat-message-input
|
||||
:multiline (not single-line-input?)
|
||||
:default-value @state-text
|
||||
:editable (not cooldown-enabled?)
|
||||
:blur-on-submit false
|
||||
:on-focus #(re-frame/dispatch [:chat.ui/set-chat-ui-props {:input-bottom-sheet nil}])
|
||||
:submit-shortcut {:key "Enter"}
|
||||
:on-submit-editing #(do
|
||||
(.clear @inp-ref)
|
||||
(.focus @inp-ref)
|
||||
(re-frame/dispatch [:chat.ui/set-chat-input-text @state-text])
|
||||
(re-frame/dispatch [:chat.ui/send-current-message])
|
||||
(set-text ""))
|
||||
:on-layout #(set-container-width-fn (.-width (.-layout (.-nativeEvent %))))
|
||||
:on-change #(do
|
||||
(set-text (.-text (.-nativeEvent %))))
|
||||
:on-end-editing #(re-frame/dispatch [:chat.ui/set-chat-input-text @state-text])
|
||||
:on-selection-change #(let [s (-> (.-nativeEvent %)
|
||||
(.-selection))
|
||||
end (.-end s)]
|
||||
(re-frame/dispatch [:chat.ui/set-chat-ui-props {:selection end}]))
|
||||
:style (style/input-view single-line-input?)
|
||||
:placeholder-text-color colors/gray
|
||||
:auto-capitalize :sentences}
|
||||
(when cooldown-enabled?
|
||||
{:placeholder (i18n/label :cooldown/text-input-disabled)}))]))
|
||||
|
||||
(defview invisible-input [{:keys [set-layout-width-fn value]}]
|
||||
(letsubs [input-text [:chats/current-chat-input-text]]
|
||||
[react/text {:style style/invisible-input-text
|
||||
:on-layout #(let [w (-> (.-nativeEvent %)
|
||||
(.-layout)
|
||||
(.-width))]
|
||||
(set-layout-width-fn w))}
|
||||
(or input-text "")]))
|
||||
|
||||
(defn get-options [type]
|
||||
(case (keyword type)
|
||||
:phone {:keyboard-type "phone-pad"}
|
||||
:password {:secure-text-entry true}
|
||||
:number {:keyboard-type "numeric"}
|
||||
nil))
|
||||
|
||||
(defview input-view [{:keys [single-line-input? set-text state-text]}]
|
||||
(let [component (reagent/current-component)
|
||||
set-layout-width-fn #(reagent/set-state component {:width %})
|
||||
set-container-width-fn #(reagent/set-state component {:container-width %})
|
||||
{:keys [width]} (reagent/state component)]
|
||||
[react/view {:style style/input-root}
|
||||
[react/animated-view {:style style/input-animated}
|
||||
[invisible-input {:set-layout-width-fn set-layout-width-fn}]
|
||||
(if platform/desktop?
|
||||
[basic-text-input-desktop {:set-container-width-fn set-container-width-fn
|
||||
:single-line-input? single-line-input?
|
||||
:set-text set-text
|
||||
:state-text state-text}]
|
||||
[basic-text-input {:set-container-width-fn set-container-width-fn
|
||||
:single-line-input? single-line-input?}])]]))
|
||||
:auto-capitalize :sentences}])
|
||||
|
||||
(defview reply-message [from alias message-text]
|
||||
(letsubs [{:keys [ens-name]} [:contacts/contact-name-by-identity from]
|
||||
|
@ -121,7 +43,6 @@
|
|||
(defview reply-message-view []
|
||||
(letsubs [{:keys [content from alias] :as message} [:chats/reply-message]]
|
||||
(when message
|
||||
[react/view {:style style/reply-message-container}
|
||||
[react/view {:style style/reply-message}
|
||||
[photos/member-photo from]
|
||||
[reply-message from alias (:text content)]
|
||||
|
@ -133,39 +54,23 @@
|
|||
[vector-icons/icon :main-icons/close {:container-style style/cancel-reply-icon
|
||||
:width 19
|
||||
:height 19
|
||||
:color colors/white}]]]]])))
|
||||
:color colors/white}]]]])))
|
||||
|
||||
(defview container []
|
||||
(letsubs [margin [:chats/input-margin]
|
||||
mainnet? [:mainnet?]
|
||||
(letsubs [mainnet? [:mainnet?]
|
||||
input-text [:chats/current-chat-input-text]
|
||||
result-box [:chats/current-chat-ui-prop :result-box]
|
||||
cooldown-enabled? [:chats/cooldown-enabled?]
|
||||
input-bottom-sheet [:chats/current-chat-ui-prop :input-bottom-sheet]
|
||||
one-to-one-chat? [:current-chat/one-to-one-chat?]
|
||||
state-text (reagent/atom "")]
|
||||
{:component-will-unmount #(when platform/desktop?
|
||||
(re-frame/dispatch [:chat.ui/set-chat-input-text @state-text]))
|
||||
|
||||
:component-did-mount #(when-not (string/blank? input-text) (reset! state-text input-text))}
|
||||
(let [single-line-input? (:singleLineInput result-box)
|
||||
set-text #(reset! state-text %)
|
||||
input-text-empty? (if platform/desktop?
|
||||
(string/blank? state-text)
|
||||
(string/blank? input-text))]
|
||||
[react/view {:style (style/root margin)}
|
||||
one-to-one-chat? [:current-chat/one-to-one-chat?]]
|
||||
(let [input-text-empty? (string/blank? (string/trim (or input-text "")))]
|
||||
[react/view {:style (style/root)}
|
||||
[reply-message-view]
|
||||
[react/view {:style style/input-container}
|
||||
[input-view {:single-line-input? single-line-input? :set-text set-text :state-text state-text}]
|
||||
[basic-text-input input-text cooldown-enabled?]
|
||||
(when (and input-text-empty? mainnet?)
|
||||
[stickers/button (= :stickers input-bottom-sheet)])
|
||||
(when (and one-to-one-chat? input-text-empty? (or config/commands-enabled? mainnet?))
|
||||
[extensions/button (= :extensions input-bottom-sheet)])
|
||||
(when-not input-text-empty?
|
||||
(if platform/desktop?
|
||||
[send-button/send-button-view {:input-text @state-text}
|
||||
#(do
|
||||
(re-frame/dispatch [:chat.ui/set-chat-input-text @state-text])
|
||||
(re-frame/dispatch [:chat.ui/send-current-message])
|
||||
(set-text ""))]
|
||||
[send-button/send-button-view {:input-text input-text}
|
||||
#(re-frame/dispatch [:chat.ui/send-current-message])]))]])))
|
||||
[send-button/send-button-view input-text-empty?
|
||||
#(re-frame/dispatch [:chat.ui/send-current-message])])]])))
|
||||
|
|
|
@ -1,21 +1,19 @@
|
|||
(ns status-im.ui.screens.chat.input.send-button
|
||||
(:require-macros [status-im.utils.views :refer [defview letsubs]])
|
||||
(:require [clojure.string :as string]
|
||||
[status-im.ui.screens.chat.styles.input.send-button :as style]
|
||||
(:require [status-im.ui.screens.chat.styles.input.send-button :as style]
|
||||
[status-im.ui.components.react :as react]
|
||||
[status-im.ui.components.icons.vector-icons :as vector-icons]
|
||||
[status-im.ui.components.colors :as colors]))
|
||||
|
||||
(defn sendable? [input-text disconnected? login-processing?]
|
||||
(let [trimmed (string/trim input-text)]
|
||||
(not (or (string/blank? trimmed)
|
||||
(defn sendable? [input-text-empty? disconnected? login-processing?]
|
||||
(not (or input-text-empty?
|
||||
login-processing?
|
||||
disconnected?))))
|
||||
disconnected?)))
|
||||
|
||||
(defview send-button-view [{:keys [input-text]} on-send-press]
|
||||
(defview send-button-view [input-text-empty? on-send-press]
|
||||
(letsubs [disconnected? [:disconnected?]
|
||||
{:keys [processing]} [:multiaccounts/login]]
|
||||
(when (sendable? input-text disconnected? processing)
|
||||
(when (sendable? input-text-empty? disconnected? processing)
|
||||
[react/touchable-highlight
|
||||
{:on-press on-send-press}
|
||||
[vector-icons/icon :main-icons/arrow-up
|
||||
|
|
|
@ -140,7 +140,6 @@
|
|||
(letsubs [selected-pack [:stickers/selected-pack]
|
||||
installed-packs [:stickers/installed-packs-vals]
|
||||
panel-height [:chats/chat-panel-height]
|
||||
|
||||
bottom-anim-value (anim/create-value @panel-height)
|
||||
alpha-value (anim/create-value 0)]
|
||||
{:component-did-mount #(show-panel-anim bottom-anim-value alpha-value)}
|
||||
|
|
|
@ -8,9 +8,8 @@
|
|||
(def border-height 1)
|
||||
(def max-input-height (* 5 min-input-height))
|
||||
|
||||
(defn root [margin-bottom]
|
||||
(defn root []
|
||||
{:background-color colors/white
|
||||
:margin-bottom margin-bottom
|
||||
:flex-direction :column
|
||||
:border-top-width border-height
|
||||
:border-top-color colors/gray-lighter})
|
||||
|
@ -78,13 +77,7 @@
|
|||
|
||||
(def input-container
|
||||
{:flex-direction :row
|
||||
:align-items :flex-end
|
||||
:padding-left 14})
|
||||
|
||||
(def input-root
|
||||
{:padding-top padding-vertical
|
||||
:padding-bottom padding-vertical
|
||||
:flex 1})
|
||||
:align-items :flex-end})
|
||||
|
||||
(def input-animated
|
||||
{:align-items :flex-start
|
||||
|
@ -93,16 +86,13 @@
|
|||
:min-height min-input-height
|
||||
:max-height max-input-height})
|
||||
|
||||
(styles/defn input-view [single-line-input?]
|
||||
(def input-view
|
||||
{:flex 1
|
||||
:padding-top 9
|
||||
:padding-bottom 5
|
||||
:padding-right 12
|
||||
:padding-top 12
|
||||
:padding-bottom 15
|
||||
:padding-horizontal 12
|
||||
:min-height min-input-height
|
||||
:max-height (if single-line-input?
|
||||
min-input-height
|
||||
max-input-height)
|
||||
:android {:padding-top 3}})
|
||||
:max-height max-input-height})
|
||||
|
||||
(def invisible-input-text
|
||||
{:position :absolute
|
||||
|
|
|
@ -136,6 +136,10 @@
|
|||
:on-scroll-to-index-failed #() ;;don't remove this
|
||||
:keyboard-should-persist-taps :handled}]))
|
||||
|
||||
(defview empty-bottom-sheet []
|
||||
(letsubs [input-bottom-sheet [:chats/empty-chat-panel-height]]
|
||||
[react/view {:height input-bottom-sheet}]))
|
||||
|
||||
(defview bottom-sheet []
|
||||
(letsubs [input-bottom-sheet [:chats/current-chat-ui-prop :input-bottom-sheet]]
|
||||
(case input-bottom-sheet
|
||||
|
@ -143,7 +147,7 @@
|
|||
[stickers/stickers-view]
|
||||
:extensions
|
||||
[extensions/extensions-view]
|
||||
nil)))
|
||||
[empty-bottom-sheet])))
|
||||
|
||||
(defview chat []
|
||||
(letsubs [{:keys [chat-id show-input? group-chat contact] :as current-chat}
|
||||
|
|
|
@ -42,6 +42,7 @@
|
|||
:mailserver/topics {}
|
||||
:mailserver/pending-requests 0
|
||||
:chat/cooldowns 0
|
||||
:chat/inputs {}
|
||||
:chat/cooldown-enabled? false
|
||||
:chat/last-outgoing-message-sent-at 0
|
||||
:chat/spam-messages-frequency 0
|
||||
|
@ -203,6 +204,7 @@
|
|||
:browser/options
|
||||
:navigation/screen-params
|
||||
:chat/cooldowns
|
||||
:chat/inputs
|
||||
:chat/cooldown-enabled?
|
||||
:chat/last-outgoing-message-sent-at
|
||||
:chat/spam-messages-frequency
|
||||
|
|
Loading…
Reference in New Issue