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
This commit is contained in:
parent
6c2b437e62
commit
972e1eee01
|
@ -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)
|
||||
|
||||
|
|
|
@ -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)))
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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?])
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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})
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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]]]]]))
|
||||
|
||||
|
|
Loading…
Reference in New Issue