From 7bb7b48db7e2b3be1fcfb21cfbbe975765615c97 Mon Sep 17 00:00:00 2001 From: Gheorghe Pinzaru Date: Tue, 4 Aug 2020 12:16:20 +0300 Subject: [PATCH] Fix chat text input animation lags Remove picker panel from input Clean pan state Fixes #11031 Do not use pan state on android iOs panel switching do not change height Revert "Remove picker panel from input" This reverts commit 0ab2c1544b70f8c491424376268f572696bd284a. Change reactions space keeper method Audio Do not use accessory view on Android Signed-off-by: Gheorghe Pinzaru --- .../ui/screens/chat/components/accessory.cljs | 95 +++++++++++++------ .../ui/screens/chat/components/input.cljs | 11 ++- .../ui/screens/chat/components/style.cljs | 3 +- .../ui/screens/chat/message/message.cljs | 10 +- src/status_im/ui/screens/chat/views.cljs | 63 +++++++----- 5 files changed, 119 insertions(+), 63 deletions(-) diff --git a/src/status_im/ui/screens/chat/components/accessory.cljs b/src/status_im/ui/screens/chat/components/accessory.cljs index e09baf1701..a9f7f01a31 100644 --- a/src/status_im/ui/screens/chat/components/accessory.cljs +++ b/src/status_im/ui/screens/chat/components/accessory.cljs @@ -11,23 +11,29 @@ [quo.components.safe-area :refer [use-safe-area]])) (def tabbar-height tabs.styles/minimized-tabs-height) +(def duration 250) (defn create-pan-responder [y pan-active] - (js->clj (.-panHandlers - ^js (.create - ^js rn/pan-responder - #js {:onPanResponderGrant (fn [] - (animated/set-value pan-active 1)) - :onPanResponderMove (fn [_ ^js state] - (animated/set-value y (.-moveY state))) - :onPanResponderRelease (fn [] - (js/setTimeout - #(animated/set-value y 0) - 100)) - :onPanResponderEnd (fn [] - (animated/set-value pan-active 0))})))) + (when-not platform/android? + (js->clj (.-panHandlers + ^js (.create + ^js rn/pan-responder + #js {:onPanResponderGrant (fn [] + (animated/set-value pan-active 1)) + :onPanResponderMove (fn [_ ^js state] + (animated/set-value y (.-moveY state))) + :onPanResponderRelease (fn [] + (animated/set-value pan-active 0) + (js/setTimeout + #(animated/set-value y 0) + 100)) + :onPanResponderTerminate (fn [] + (animated/set-value pan-active 0) + (js/setTimeout + #(animated/set-value y 0) + 100))}))))) -(def view +(def ios-view (reagent/adapt-react-class (react/memo (fn [props] @@ -39,7 +45,6 @@ children :children} (bean/bean props) {keyboard-height :height keyboard-max-height :max-height - duration :duration keyboard-end-position :end-position} (use-keyboard-dimension) {:keys [bottom]} (use-safe-area) {on-layout :on-layout @@ -64,15 +69,13 @@ :easing (:keyboard animated/easings)}) 0 panel-on-screen)) - [duration panel-on-screen]) + [panel-on-screen]) delta-y (animated/clamp (animated/add drag-diff animated-y) max-delta 0) - on-update (react/callback - (fn [] - (when on-update-inset - (on-update-inset (+ bar-height panel-height)))) - [panel-height bar-height]) + on-update (fn [] + (when on-update-inset + (on-update-inset (+ bar-height panel-height)))) children (react/get-children children)] - (react/effect! on-update) + (react/effect! on-update [panel-height bar-height]) (animated/code! (fn [] (when has-panel @@ -86,12 +89,7 @@ (animated/delay (animated/set anim-visible (if visible 1 0)) (if visible delay 0))) - [visible keyboard-max-height duration]) - (rn/use-back-handler - (fn [] - (when visible - (on-close)) - visible)) + [visible keyboard-max-height delay]) (reagent/as-element [animated/view {:style {:position :absolute :left 0 @@ -104,3 +102,44 @@ [rn/view {:style {:flex 1 :height (when (pos? panel-height) panel-height)}} (second children)]])))))) + +(def android-view + (reagent/adapt-react-class + (react/memo + (fn [props] + (let [{on-update-inset :onUpdateInset + on-close :onClose + has-panel :hasPanel + children :children} (bean/bean props) + {keyboard-max-height :max-height} (use-keyboard-dimension) + {:keys [bottom]} (use-safe-area) + {on-layout :on-layout + bar-height :height} (rn/use-layout) + + visible has-panel + panel-on-screen (* -1 (- keyboard-max-height bottom tabbar-height)) + max-delta (min 0 (if has-panel panel-on-screen 0)) + panel-height (* -1 max-delta) + on-update (fn [] + (when on-update-inset + (on-update-inset (+ bar-height panel-height)))) + children (react/get-children children)] + (react/effect! on-update [panel-height bar-height]) + (rn/use-back-handler + (fn [] + (when visible + (on-close)) + visible)) + (reagent/as-element + [animated/view {:style {:position :absolute + :left 0 + :right 0 + :bottom 0 + :background-color (:ui-background @colors/theme)}} + [rn/view {:on-layout on-layout} + (first children)] + [rn/view {:style {:flex 1 + :height (when (pos? panel-height) panel-height)}} + (second children)]])))))) + +(def view (if platform/android? android-view ios-view)) diff --git a/src/status_im/ui/screens/chat/components/input.cljs b/src/status_im/ui/screens/chat/components/input.cljs index 75e3b31237..40590ef08a 100644 --- a/src/status_im/ui/screens/chat/components/input.cljs +++ b/src/status_im/ui/screens/chat/components/input.cljs @@ -15,8 +15,7 @@ [clojure.string :as string])) (def panel->icons {:extensions :main-icons/commands - :images :main-icons/photo - :audio :main-icons/speech}) + :images :main-icons/photo}) (defn touchable-icon [{:keys [panel active set-active accessibility-label]}] [pressable/pressable {:type :scale @@ -38,6 +37,8 @@ [icons/icon :main-icons/keyboard (styles/icon false)] [icons/icon :main-icons/stickers (styles/icon false)])]]) +;; TODO(Ferossgp): Move this into audio panel. +;; Instead of not changing panel we can show a placeholder with no permission (defn- request-record-audio-permission [set-active panel] (re-frame/dispatch [:request-permissions @@ -58,9 +59,9 @@ (input-focus) (request-record-audio-permission set-active panel))} [rn/view {:style (styles/in-input-touchable-icon)} - [icons/icon - (panel->icons panel) - (styles/icon (= active panel))]]]) + (if (= active panel) + [icons/icon :main-icons/keyboard (styles/icon false)] + [icons/icon :main-icons/speech (styles/icon false)])]]) (defn send-button [{:keys [on-send-press]}] [pressable/pressable {:type :scale diff --git a/src/status_im/ui/screens/chat/components/style.cljs b/src/status_im/ui/screens/chat/components/style.cljs index 1cba33a325..88ec9edd02 100644 --- a/src/status_im/ui/screens/chat/components/style.cljs +++ b/src/status_im/ui/screens/chat/components/style.cljs @@ -104,7 +104,8 @@ (defn in-input-buttons [] {:flex-direction :row - :height 34}) + :height 34 + :overflow :hidden}) (defn send-icon-color [] colors/white) diff --git a/src/status_im/ui/screens/chat/message/message.cljs b/src/status_im/ui/screens/chat/message/message.cljs index 6e368c964b..096507a130 100644 --- a/src/status_im/ui/screens/chat/message/message.cljs +++ b/src/status_im/ui/screens/chat/message/message.cljs @@ -2,7 +2,6 @@ (:require [re-frame.core :as re-frame] [status-im.constants :as constants] [status-im.i18n :as i18n] - [quo.platform :as platform] [status-im.ui.components.colors :as colors] [status-im.ui.components.icons.vector-icons :as vector-icons] [status-im.ui.components.react :as react] @@ -341,17 +340,14 @@ [message-content-wrapper message [unknown-content-type message]]) -(defn chat-message [message set-active-panel] +(defn chat-message [message space-keeper] [reactions/with-reaction-picker {:message message :reactions @(re-frame/subscribe [:chats/message-reactions (:message-id message)]) :picker-on-open (fn [] - ;; NOTE(Ferossgp): Because of soft-input adjustResize there are some problems on android - (when (and platform/ios? (pos? @(re-frame/subscribe [:keyboard-height]))) - (set-active-panel :keep-space))) + (space-keeper true)) :picker-on-close (fn [] - (when platform/ios? - (set-active-panel nil))) + (space-keeper false)) :send-emoji (fn [{:keys [emoji-id]}] (re-frame/dispatch [::models.reactions/send-emoji-reaction {:message-id (:message-id message) diff --git a/src/status_im/ui/screens/chat/views.cljs b/src/status_im/ui/screens/chat/views.cljs index 64a0f5604a..96b90975a2 100644 --- a/src/status_im/ui/screens/chat/views.cljs +++ b/src/status_im/ui/screens/chat/views.cljs @@ -11,6 +11,7 @@ [status-im.ui.screens.chat.sheets :as sheets] [quo.animated :as animated] [quo.react-native :as rn] + [quo.platform :as platform] [status-im.ui.screens.chat.audio-message.views :as audio-message] [quo.react :as quo.react] [status-im.ui.screens.chat.message.message :as message] @@ -27,8 +28,7 @@ [status-im.ui.components.invite.chat :as invite.chat] [status-im.ui.screens.chat.components.accessory :as accessory] [status-im.ui.screens.chat.components.input :as components] - [status-im.ui.screens.chat.message.datemark :as message-datemark]) - (:require-macros [status-im.utils.views :refer [defview letsubs]])) + [status-im.ui.screens.chat.message.datemark :as message-datemark])) (defn topbar [current-chat] [topbar/topbar @@ -137,14 +137,16 @@ first-not-visible))))) (debounce/debounce-and-dispatch [:chat.ui/message-visibility-changed e] 5000)) -(defview messages-view - [{:keys [group-chat chat-id public?] :as chat} bottom-space pan-handler set-active-panel] - (letsubs [messages [:chats/current-chat-messages-stream] - no-messages? [:chats/current-chat-no-messages?] - current-public-key [:multiaccount/public-key]] +(defn messages-view + [{:keys [chat bottom-space pan-responder space-keeper]}] + (let [{:keys [group-chat chat-id public?]} chat + + messages @(re-frame/subscribe [:chats/current-chat-messages-stream]) + no-messages? @(re-frame/subscribe [:chats/current-chat-no-messages?]) + current-public-key @(re-frame/subscribe [:multiaccount/public-key])] [list/flat-list (merge - pan-handler + pan-responder {:key-fn #(or (:message-id %) (:value %)) :ref #(reset! messages-list-ref %) :header (when (and group-chat (not public?)) @@ -167,13 +169,13 @@ :group-chat group-chat :public? public? :current-public-key current-public-key) - set-active-panel]))) + space-keeper]))) :on-viewable-items-changed on-viewable-items-changed :on-end-reached #(re-frame/dispatch [:chat.ui/load-more-messages]) :on-scroll-to-index-failed #() ;;don't remove this - :content-container-style {:padding-top (+ @bottom-space 16) + :content-container-style {:padding-top (+ bottom-space 16) :padding-bottom 16} - :scrollIndicatorInsets {:top @bottom-space} + :scrollIndicatorInsets {:top bottom-space} :keyboardDismissMode "interactive" :keyboard-should-persist-taps :handled})])) @@ -190,20 +192,34 @@ nil)) (defn chat [] - (let [bottom-space (reagent/atom 0) - active-panel (reagent/atom nil) - position-y (animated/value 0) - pan-state (animated/value 0) - text-input-ref (quo.react/create-ref) - on-update (partial reset! bottom-space) - pan-responder (accessory/create-pan-responder position-y pan-state) + (let [bottom-space (reagent/atom 0) + panel-space (reagent/atom 0) + active-panel (reagent/atom nil) + position-y (animated/value 0) + pan-state (animated/value 0) + text-input-ref (quo.react/create-ref) + on-update (partial reset! panel-space) + pan-responder (accessory/create-pan-responder position-y pan-state) + space-keeper (fn [state] + ;; NOTE: Only iOs now because we use soft input resize screen on android + (when platform/ios? + (cond + (and state + (< @bottom-space @panel-space) + (not @active-panel)) + (reset! bottom-space @panel-space) + + (and (not state) + (< @panel-space @bottom-space)) + (do + (some-> ^js (quo.react/current-ref text-input-ref) .focus) + (reset! panel-space @bottom-space) + (reset! bottom-space 0))))) set-active-panel (fn [panel] (rn/configure-next (:ease-opacity-200 rn/custom-animations)) - (when (and (not panel) - (= :keep-space @active-panel)) - (some-> ^js (quo.react/current-ref text-input-ref) .focus)) (reset! active-panel panel) + (reagent/flush) (when panel (js/setTimeout #(react/dismiss-keyboard!) 100)))] (fn [] @@ -215,7 +231,10 @@ [react/view {:style {:flex 1}} (when-not group-chat [add-contact-bar chat-id]) - [messages-view current-chat bottom-space pan-responder set-active-panel]]] + [messages-view {:chat current-chat + :bottom-space (max @bottom-space @panel-space) + :pan-responder pan-responder + :space-keeper space-keeper}]]] (when show-input? [accessory/view {:y position-y :pan-state pan-state