From 972e1eee01f23de121e2e30abf0d8922da1c82b7 Mon Sep 17 00:00:00 2001 From: Icaro Motta Date: Wed, 1 Nov 2023 13:31:14 -0300 Subject: [PATCH] Composer - Fix conflicting touchable and gesture (#17680) Fix the unreliable swipe behavior of link previews in the chat composer. In many occasions, we noticed the horizontal scroll wasn't being triggered properly or not at all. In other occasions, the swipe gesture would incorrectly register a press on the parent `Touchable` component. Fixes https://github.com/status-im/status-mobile/issues/16599 --- .../contexts/chat/composer/constants.cljs | 10 ++- .../contexts/chat/composer/effects.cljs | 26 ++------ .../contexts/chat/composer/gesture.cljs | 3 +- .../chat/composer/gradients/style.cljs | 15 +++-- .../chat/composer/gradients/view.cljs | 23 ++++--- .../contexts/chat/composer/handlers.cljs | 28 ++++---- .../contexts/chat/composer/images/style.cljs | 8 +-- .../chat/composer/link_preview/style.cljs | 9 ++- .../contexts/chat/composer/mentions/view.cljs | 25 +++---- .../contexts/chat/composer/style.cljs | 35 +++++----- .../contexts/chat/composer/utils.cljs | 65 ++++++++----------- .../contexts/chat/composer/view.cljs | 21 +++--- 12 files changed, 125 insertions(+), 143 deletions(-) diff --git a/src/status_im2/contexts/chat/composer/constants.cljs b/src/status_im2/contexts/chat/composer/constants.cljs index ef24109dfc..d101cea05a 100644 --- a/src/status_im2/contexts/chat/composer/constants.cljs +++ b/src/status_im2/contexts/chat/composer/constants.cljs @@ -17,9 +17,15 @@ (def ^:const empty-opacity 0.7) -(def ^:const images-container-height 76) +(def ^:const images-padding-top 12) +(def ^:const images-padding-bottom 8) +(def ^:const images-container-height + (+ actions-container-height images-padding-top images-padding-bottom)) -(def ^:const links-container-height 76) +(def ^:const links-padding-top 12) +(def ^:const links-padding-bottom 8) +(def ^:const links-container-height + (+ actions-container-height links-padding-top links-padding-bottom)) (def ^:const reply-container-height 32) diff --git a/src/status_im2/contexts/chat/composer/effects.cljs b/src/status_im2/contexts/chat/composer/effects.cljs index 4160a98a7e..9ff63c82a7 100644 --- a/src/status_im2/contexts/chat/composer/effects.cljs +++ b/src/status_im2/contexts/chat/composer/effects.cljs @@ -168,20 +168,13 @@ (defn link-previews [{:keys [sending-links?]} {:keys [text-value maximized?]} - {:keys [height saved-height last-height]} + {:keys [height saved-height]} {:keys [link-previews?]}] (rn/use-effect (fn [] (if-not @maximized? - (let [value (if link-previews? - constants/links-container-height - (- constants/links-container-height))] - (when (not= @sending-links? link-previews?) - (reanimated/animate height (+ (reanimated/get-shared-value saved-height) value)) - (reanimated/set-shared-value saved-height - (+ (reanimated/get-shared-value saved-height) value)) - (reanimated/set-shared-value last-height - (+ (reanimated/get-shared-value last-height) value)))) + (when (not= @sending-links? link-previews?) + (reanimated/animate height (reanimated/get-shared-value saved-height))) (let [curr-text @text-value] (reset! text-value (str @text-value " ")) (js/setTimeout #(reset! text-value curr-text) 100))) @@ -191,7 +184,7 @@ (defn use-images [{:keys [sending-images? input-ref]} {:keys [text-value maximized?]} - {:keys [container-opacity height saved-height last-height]} + {:keys [container-opacity height saved-height]} {:keys [images]}] (rn/use-effect (fn [] @@ -200,15 +193,8 @@ (when (and (not @sending-images?) (seq images) @input-ref) (.focus ^js @input-ref)) (if-not @maximized? - (let [value (if (seq images) - constants/images-container-height - (- constants/images-container-height))] - (when (not= @sending-images? (boolean (seq images))) - (reanimated/animate height (+ (reanimated/get-shared-value saved-height) value)) - (reanimated/set-shared-value saved-height - (+ (reanimated/get-shared-value saved-height) value)) - (reanimated/set-shared-value last-height - (+ (reanimated/get-shared-value last-height) value)))) + (when (not= @sending-images? (boolean (seq images))) + (reanimated/animate height (reanimated/get-shared-value saved-height))) (let [curr-text @text-value] (reset! text-value (str @text-value " ")) (js/setTimeout #(reset! text-value curr-text) 100))) diff --git a/src/status_im2/contexts/chat/composer/gesture.cljs b/src/status_im2/contexts/chat/composer/gesture.cljs index 6a127a39c5..3d786ad845 100644 --- a/src/status_im2/contexts/chat/composer/gesture.cljs +++ b/src/status_im2/contexts/chat/composer/gesture.cljs @@ -59,7 +59,6 @@ [{:keys [input-ref] :as props} {:keys [gesture-enabled?] :as state} {:keys [height saved-height last-height opacity background-y container-opacity] :as animations} - {:keys [images link-previews?]} {:keys [max-height lines] :as dimensions} keyboard-shown] (let [expanding? (atom true) @@ -82,7 +81,7 @@ (gesture/on-update (fn [event] (let [translation (oops/oget event "translationY") - min-height (utils/get-min-height lines images link-previews?) + min-height (utils/get-min-height lines) new-height (- (reanimated/get-shared-value saved-height) translation) bounded-height (utils.number/value-in-range new-height min-height max-height)] (when keyboard-shown diff --git a/src/status_im2/contexts/chat/composer/gradients/style.cljs b/src/status_im2/contexts/chat/composer/gradients/style.cljs index 5163246bd6..54796ba3e2 100644 --- a/src/status_im2/contexts/chat/composer/gradients/style.cljs +++ b/src/status_im2/contexts/chat/composer/gradients/style.cljs @@ -4,24 +4,27 @@ [react-native.reanimated :as reanimated] [status-im2.contexts.chat.composer.constants :as constants])) -(defn top-gradient-style - [opacity z-index] +(defn- top-gradient-style + [opacity z-index showing-extra-space?] (reanimated/apply-animations-to-style {:opacity opacity} - {:height 80 + {:height (when (pos-int? z-index) 80) :position :absolute :z-index z-index - :top 0 + :top (+ constants/bar-container-height + (if showing-extra-space? + constants/edit-container-height + 0)) :left 0 :right 0})) (defn top-gradient - [opacity z-index] + [opacity z-index showing-extra-space?] {:colors [(colors/theme-colors colors/white-opa-0 colors/neutral-95-opa-0) (colors/theme-colors colors/white colors/neutral-95)] :start {:x 0 :y 1} :end {:x 0 :y 0} - :style (top-gradient-style opacity z-index)}) + :style (top-gradient-style opacity z-index showing-extra-space?)}) (def bottom-gradient-style {:height constants/line-height diff --git a/src/status_im2/contexts/chat/composer/gradients/view.cljs b/src/status_im2/contexts/chat/composer/gradients/view.cljs index 383956ed85..165b24c069 100644 --- a/src/status_im2/contexts/chat/composer/gradients/view.cljs +++ b/src/status_im2/contexts/chat/composer/gradients/view.cljs @@ -3,22 +3,25 @@ [react-native.core :as rn] [react-native.linear-gradient :as linear-gradient] [react-native.reanimated :as reanimated] - [status-im2.contexts.chat.composer.gradients.style :as style])) - + [status-im2.contexts.chat.composer.gradients.style :as style] + [utils.re-frame :as rf])) (defn f-view [{:keys [input-ref]} {:keys [gradient-z-index]} {:keys [gradient-opacity]} show-bottom-gradient?] - [:<> - [reanimated/linear-gradient (style/top-gradient gradient-opacity @gradient-z-index)] - (when show-bottom-gradient? - [rn/touchable-without-feedback - {:on-press #(when @input-ref (.focus ^js @input-ref)) - :accessibility-label :bottom-gradient} - [linear-gradient/linear-gradient (style/bottom-gradient)]])]) + (let [showing-extra-space? (boolean (or (rf/sub [:chats/edit-message]) + (rf/sub [:chats/reply-message])))] + [:<> + [reanimated/linear-gradient + (style/top-gradient gradient-opacity @gradient-z-index showing-extra-space?)] + (when show-bottom-gradient? + [rn/pressable + {:on-press #(when @input-ref (.focus ^js @input-ref)) + :accessibility-label :bottom-gradient} + [linear-gradient/linear-gradient (style/bottom-gradient)]])])) + (defn view [props state animations show-bottom-gradient?] [:f> f-view props state animations show-bottom-gradient?]) - diff --git a/src/status_im2/contexts/chat/composer/handlers.cljs b/src/status_im2/contexts/chat/composer/handlers.cljs index 4dc9e6a15f..dd58dd4d5f 100644 --- a/src/status_im2/contexts/chat/composer/handlers.cljs +++ b/src/status_im2/contexts/chat/composer/handlers.cljs @@ -43,14 +43,12 @@ {:keys [images link-previews? reply]}] (when-not @recording? (let [lines (utils/calc-lines (- @content-height constants/extra-content-offset)) - min-height (utils/get-min-height lines images link-previews?) + min-height (utils/get-min-height lines) reopen-height (utils/calc-reopen-height text-value min-height max-height content-height - saved-height - images - link-previews?)] + saved-height)] (reset! focused? false) (rf/dispatch [:chat.ui/set-input-focused false]) (reanimated/set-shared-value last-height reopen-height) @@ -72,21 +70,19 @@ [event {:keys [maximized? lock-layout? text-value]} {:keys [height saved-height opacity background-y]} - {:keys [images link-previews?]} {:keys [content-height window-height max-height]} keyboard-shown] (when keyboard-shown - (let [event-size (oops/oget event "nativeEvent.contentSize.height") - content-size (+ event-size constants/extra-content-offset) - lines (utils/calc-lines event-size) - content-size (if (or (= lines 1) (empty? @text-value)) - constants/input-height - (if (= lines 2) constants/multiline-minimized-height content-size)) - new-height (utils.number/value-in-range content-size - constants/input-height - max-height) - bottom-content-height (utils/calc-bottom-content-height images link-previews?) - new-height (min (+ new-height bottom-content-height) max-height)] + (let [event-size (oops/oget event "nativeEvent.contentSize.height") + content-size (+ event-size constants/extra-content-offset) + lines (utils/calc-lines event-size) + content-size (if (or (= lines 1) (empty? @text-value)) + constants/input-height + (if (= lines 2) constants/multiline-minimized-height content-size)) + new-height (utils.number/value-in-range content-size + constants/input-height + max-height) + new-height (min new-height max-height)] (reset! content-height content-size) (when (utils/update-height? content-size height max-height maximized?) (reanimated/animate height new-height) diff --git a/src/status_im2/contexts/chat/composer/images/style.cljs b/src/status_im2/contexts/chat/composer/images/style.cljs index 1ee9987da0..5657a39cdd 100644 --- a/src/status_im2/contexts/chat/composer/images/style.cljs +++ b/src/status_im2/contexts/chat/composer/images/style.cljs @@ -1,10 +1,11 @@ (ns status-im2.contexts.chat.composer.images.style (:require - [quo.foundations.colors :as colors])) + [quo.foundations.colors :as colors] + [status-im2.contexts.chat.composer.constants :as constants])) (def image-container - {:padding-top 12 - :padding-bottom 8 + {:padding-top constants/images-padding-top + :padding-bottom constants/images-padding-bottom :padding-right 12}) (defn remove-photo-container @@ -32,4 +33,3 @@ {:width 56 :height 56 :border-radius 12}) - diff --git a/src/status_im2/contexts/chat/composer/link_preview/style.cljs b/src/status_im2/contexts/chat/composer/link_preview/style.cljs index fb9a4ddbff..068a13d65b 100644 --- a/src/status_im2/contexts/chat/composer/link_preview/style.cljs +++ b/src/status_im2/contexts/chat/composer/link_preview/style.cljs @@ -1,13 +1,12 @@ -(ns status-im2.contexts.chat.composer.link-preview.style) +(ns status-im2.contexts.chat.composer.link-preview.style + (:require [status-im2.contexts.chat.composer.constants :as constants])) (def padding-horizontal 20) -(def preview-list-padding-top 12) -(def preview-list-padding-bottom 8) (def preview-height 56) (def preview-list - {:padding-top preview-list-padding-top - :padding-bottom preview-list-padding-bottom + {:padding-top constants/links-padding-top + :padding-bottom constants/links-padding-bottom :margin-horizontal (- padding-horizontal) ;; Keep a high index, otherwise the parent gesture detector used by the ;; composer grabs the initiating gesture event. diff --git a/src/status_im2/contexts/chat/composer/mentions/view.cljs b/src/status_im2/contexts/chat/composer/mentions/view.cljs index 2b0134abbd..56f2abbcd9 100644 --- a/src/status_im2/contexts/chat/composer/mentions/view.cljs +++ b/src/status_im2/contexts/chat/composer/mentions/view.cljs @@ -31,18 +31,19 @@ (defn- f-view [suggestions-atom props state animations max-height cursor-pos images link-previews? reply edit] - (let [suggestions (rf/sub [:chat/mention-suggestions]) - opacity (reanimated/use-shared-value (if (seq suggestions) 1 0)) - size (count suggestions) - data {:keyboard-height @(:kb-height state) - :insets (safe-area/get-insets) - :curr-height (reanimated/get-shared-value (:height animations)) - :window-height (:height (rn/get-window)) - :images images - :link-previews? link-previews? - :reply reply - :edit edit} - mentions-pos (utils/calc-suggestions-position cursor-pos max-height size state data)] + (let [suggestions (rf/sub [:chat/mention-suggestions]) + opacity (reanimated/use-shared-value (if (seq suggestions) 1 0)) + size (count suggestions) + data {:keyboard-height @(:kb-height state) + :insets (safe-area/get-insets) + :curr-height (reanimated/get-shared-value (:height animations)) + :window-height (:height (rn/get-window)) + :images images + :link-previews? link-previews? + :reply reply + :edit edit} + mentions-pos + (utils/calc-suggestions-position cursor-pos max-height size state data images link-previews?)] (rn/use-effect (fn [] (if (seq suggestions) diff --git a/src/status_im2/contexts/chat/composer/style.cljs b/src/status_im2/contexts/chat/composer/style.cljs index e61a593e4a..8b5daa321e 100644 --- a/src/status_im2/contexts/chat/composer/style.cljs +++ b/src/status_im2/contexts/chat/composer/style.cljs @@ -7,6 +7,8 @@ [react-native.reanimated :as reanimated] [status-im2.contexts.chat.composer.constants :as constants])) +(def border-top-radius 20) + (defn shadow [focused? theme] (if platform/ios? @@ -27,8 +29,8 @@ (reanimated/apply-animations-to-style {:opacity container-opacity} (merge - {:border-top-left-radius 20 - :border-top-right-radius 20 + {:border-top-left-radius border-top-radius + :border-top-right-radius border-top-radius :padding-horizontal 20 :background-color (colors/theme-colors colors/white colors/neutral-95 theme) :z-index 3 @@ -55,7 +57,8 @@ [height max-height] (reanimated/apply-animations-to-style {:height height} - {:max-height max-height})) + {:max-height max-height + :z-index 1})) (defn input-view [{:keys [recording?]}] @@ -68,19 +71,17 @@ (defn input-text [{:keys [saved-emoji-kb-extra-height]} {:keys [focused? maximized?]} - {:keys [link-previews? images]} {:keys [max-height theme]}] - (merge typography/paragraph-1 - {:color (colors/theme-colors :black :white theme) - :text-align-vertical :top - :position (if @saved-emoji-kb-extra-height :relative :absolute) - :top 0 - :left 0 - :right (when (or focused? platform/ios?) 0) - :max-height (- max-height - (if link-previews? constants/links-container-height 0) - (if (seq images) constants/images-container-height 0)) - :padding-bottom (when @maximized? 0)})) + (assoc typography/paragraph-1 + :color (colors/theme-colors :black :white theme) + :text-align-vertical :top + :position (if @saved-emoji-kb-extra-height :relative :absolute) + :top 0 + :left 0 + :right (when (or focused? platform/ios?) 0) + :max-height max-height + :padding-bottom (when @maximized? 0))) + (defn background [opacity background-y window-height] (reanimated/apply-animations-to-style @@ -102,8 +103,8 @@ :left 0 :right 0 :bottom 0 - :border-top-right-radius 20 - :border-top-left-radius 20 + :border-top-right-radius border-top-radius + :border-top-left-radius border-top-radius :overflow :hidden})) (defn blur-view diff --git a/src/status_im2/contexts/chat/composer/utils.cljs b/src/status_im2/contexts/chat/composer/utils.cljs index 3bc9e5d196..5cc2adcf58 100644 --- a/src/status_im2/contexts/chat/composer/utils.cljs +++ b/src/status_im2/contexts/chat/composer/utils.cljs @@ -57,43 +57,40 @@ (defn calc-top-content-height [reply? edit?] - (let [height (if reply? constants/reply-container-height 0) - height (if edit? (+ height constants/edit-container-height) height)] - height)) + (cond-> 0 + reply? (+ constants/reply-container-height) + edit? (+ constants/edit-container-height))) (defn calc-bottom-content-height [images link-previews?] - (let [height (if (seq images) constants/images-container-height 0) - height (if link-previews? (+ height constants/links-container-height) height)] - height)) + (cond-> 0 + (seq images) (+ constants/images-container-height) + link-previews? (+ constants/links-container-height))) (defn calc-reopen-height - [text-value min-height max-height content-height saved-height images link-previews?] + [text-value min-height max-height content-height saved-height] (if (empty? @text-value) min-height - (let [bottom-content-height (calc-bottom-content-height images link-previews?) - input-height (min (+ @content-height bottom-content-height) - (reanimated/get-shared-value saved-height))] + (let [input-height (min @content-height + (reanimated/get-shared-value saved-height))] (min max-height input-height)))) (defn get-min-height - [lines images link-previews?] - (let [input-height (if (> lines 1) - constants/multiline-minimized-height - constants/input-height) - bottom-content-height (calc-bottom-content-height images link-previews?)] - (+ input-height bottom-content-height))) + [lines] + (if (> lines 1) + constants/multiline-minimized-height + constants/input-height)) (defn calc-max-height - [{:keys [reply edit]} window-height kb-height insets] - (let [margin-top (if platform/ios? (:top insets) (+ 10 (:top insets))) - max-height (- window-height - margin-top - kb-height - constants/bar-container-height - constants/actions-container-height) - max-height (- max-height (calc-top-content-height reply edit))] - max-height)) + [{:keys [reply edit images link-previews?]} window-height kb-height insets] + (let [margin-top (if platform/ios? (:top insets) (+ 10 (:top insets)))] + (- window-height + margin-top + kb-height + constants/bar-container-height + constants/actions-container-height + (calc-top-content-height reply edit) + (calc-bottom-content-height images link-previews?)))) (defn empty-input? [text images link-previews? reply? audio?] @@ -124,21 +121,12 @@ sub-text-lines-in-view (- sub-text-lines scrolled-lines)] (* sub-text-lines-in-view constants/line-height))) -(defn calc-shell-neg-y - [insets maximized? extra-height] - (let [padding 12 - neg-y (if @maximized? -50 0)] - (- (+ constants/bar-container-height - constants/actions-container-height - (:bottom insets) - padding - extra-height - neg-y)))) - (defn calc-suggestions-position [cursor-pos max-height size {:keys [maximized?]} - {:keys [insets curr-height window-height keyboard-height reply edit]}] + {:keys [insets curr-height window-height keyboard-height reply edit]} + images + link-previews?] (let [base (+ constants/composer-default-height (:bottom insets) 8) base (+ base (- curr-height constants/input-height)) base (+ base (calc-top-content-height reply edit)) @@ -152,7 +140,8 @@ (+ constants/actions-container-height (:bottom insets)) (+ constants/actions-container-height (:bottom insets) (- max-height cursor-pos) 18)) (if (< (+ base container-height) view-height) - base + (let [bottom-content-height (calc-bottom-content-height images link-previews?)] + (+ base bottom-content-height)) (+ constants/actions-container-height (:bottom insets) (- curr-height cursor-pos) 18))))) (defn init-props diff --git a/src/status_im2/contexts/chat/composer/view.cljs b/src/status_im2/contexts/chat/composer/view.cljs index 7272a42aeb..d3c6c8bce0 100644 --- a/src/status_im2/contexts/chat/composer/view.cljs +++ b/src/status_im2/contexts/chat/composer/view.cljs @@ -86,7 +86,7 @@ [sub-view/shell-button state scroll-to-bottom-fn show-floating-scroll-down-button?] [gesture/gesture-detector {:gesture - (drag-gesture/drag-gesture props state animations subscriptions dimensions keyboard-shown)} + (drag-gesture/drag-gesture props state animations dimensions keyboard-shown)} [reanimated/view {:style (style/sheet-container insets state animations theme) :on-layout #(handler/layout % state blur-height)} @@ -97,7 +97,9 @@ [edit/view state]]) [reanimated/touchable-opacity {:active-opacity 1 - :on-press (when @(:input-ref props) #(.focus ^js @(:input-ref props))) + :on-press (fn [] + (when-let [ref @(:input-ref props)] + (.focus ^js ref))) :style (style/input-container (:height animations) max-height) :accessibility-label :message-input-container} [rn/selectable-text-input @@ -113,7 +115,6 @@ :on-content-size-change #(handler/content-size-change % state animations - subscriptions dimensions (or keyboard-shown (:edit subscriptions))) @@ -122,23 +123,21 @@ :on-selection-change #(handler/selection-change % props state) :on-selection #(selection/on-selection % props state) :keyboard-appearance (quo.theme/theme-value :light :dark) - :max-height max-height :max-font-size-multiplier 1 :multiline true :placeholder (i18n/label :t/type-something) :placeholder-text-color (colors/theme-colors colors/neutral-30 colors/neutral-50) :style (style/input-text props state - subscriptions {:max-height max-height :theme theme}) :max-length constants/max-text-size - :accessibility-label :chat-message-input}]] - (when chat-screen-loaded? - [:<> - [gradients/view props state animations show-bottom-gradient?] - [link-preview/view] - [images/images-list]])] + :accessibility-label :chat-message-input}]]] + (when chat-screen-loaded? + [:<> + [gradients/view props state animations show-bottom-gradient?] + [link-preview/view] + [images/images-list]]) [:f> actions/view props state animations window-height insets scroll-to-bottom-fn subscriptions]]]]]))