Fix: jumpy composer (#16051)

* fix: jumpy composer
This commit is contained in:
Omar Basem 2023-05-31 18:12:48 +04:00 committed by GitHub
parent fb4d484d7d
commit 9960a5c958
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 142 additions and 83 deletions

View File

@ -14,7 +14,8 @@
[status-im2.contexts.chat.composer.actions.style :as style]))
(defn send-message
[{:keys [text-value focused? maximized?]}
[{:keys [sending-images? sending-links?]}
{:keys [text-value focused? maximized?]}
{:keys [height saved-height last-height opacity background-y container-opacity]}
window-height]
(reanimated/animate height constants/input-height)
@ -32,10 +33,12 @@
(rf/dispatch [:chat.ui/set-chat-input-text nil])
(reset! maximized? false)
(reset! text-value "")
(reset! sending-links? false)
(reset! sending-images? false)
(messages.list/scroll-to-bottom))
(defn f-send-button
[{:keys [text-value] :as state}
[props {:keys [text-value] :as state}
animations window-height images?
btn-opacity z-index]
(rn/use-effect (fn []
@ -54,14 +57,14 @@
{:icon true
:size 32
:accessibility-label :send-message-button
:on-press #(send-message state animations window-height)}
:on-press #(send-message props state animations window-height)}
:i/arrow-up]])
(defn send-button
[state animations window-height images?]
[props {:keys [text-value] :as state} animations window-height images?]
(let [btn-opacity (reanimated/use-shared-value 0)
z-index (reagent/atom 0)]
[:f> f-send-button state animations window-height images? btn-opacity z-index]))
z-index (reagent/atom (if (and (empty? @text-value) (not images?)) 0 1))]
[:f> f-send-button props state animations window-height images? btn-opacity z-index]))
(defn audio-button
[{:keys [record-reset-fn input-ref]}
@ -197,6 +200,6 @@
[image-button props animations insets]
[reaction-button]
[format-button]]
[:f> send-button state animations window-height images]
[:f> send-button props state animations window-height images]
(when (and (not edit) (not images))
[audio-button props state animations])])

View File

@ -61,18 +61,6 @@
(when-not (string/blank? text)
(rf/dispatch [:link-preview/unfurl-urls text]))))
(defn images-effect
[{:keys [sending-images? input-ref]}
{:keys [container-opacity]}
images?]
(when images?
(reanimated/animate container-opacity 1))
(when (and (not @sending-images?) images? @input-ref)
(.focus ^js @input-ref)
(reset! sending-images? true))
(when-not images?
(reset! sending-images? false)))
(defn audio-effect
[{:keys [recording? gesture-enabled?]}
{:keys [container-opacity]}
@ -95,26 +83,9 @@
(.remove ^js @keyboard-hide-listener)
(.remove ^js @keyboard-frame-listener))
(defn max-height-effect
[{:keys [focused?]}
{:keys [max-height]}
{:keys [height saved-height last-height]}]
(rn/use-effect
(fn []
;; Some subscriptions can arrive after the composer if focused (esp. link
;; previews), so we need to react to changes in `max-height` outside of the
;; `on-focus` handler.
(when @focused?
(let [new-height (min max-height (reanimated/get-shared-value last-height))]
(reanimated/set-shared-value last-height new-height)
(reanimated/animate height new-height)
(reanimated/set-shared-value saved-height new-height))))
[max-height @focused?]))
(defn initialize
[props state animations {:keys [max-height] :as dimensions}
{:keys [chat-input images audio] :as subs}]
(max-height-effect state dimensions animations)
{:keys [chat-input audio] :as subs}]
(rn/use-effect
(fn []
(maximized-effect state animations dimensions chat-input)
@ -122,7 +93,6 @@
(layout-effect state)
(kb-default-height-effect state)
(background-effect state animations dimensions chat-input)
(images-effect props animations images)
(link-preview-effect state)
(audio-effect state animations audio)
(empty-effect state animations subs)
@ -186,6 +156,56 @@
(rf/dispatch [:mention/on-change-text input-text])))
[input-text]))
(defn link-previews
[{:keys [sending-links?]}
{:keys [text-value maximized?]}
{:keys [height saved-height last-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))))
(let [curr-text @text-value]
(reset! text-value (str @text-value " "))
(js/setTimeout #(reset! text-value curr-text) 10)))
(reset! sending-links? link-previews?))
[link-previews?]))
(defn images
[{:keys [sending-images? input-ref]}
{:keys [text-value maximized?]}
{:keys [container-opacity height saved-height last-height]}
{:keys [images]}]
(rn/use-effect
(fn []
(when images
(reanimated/animate container-opacity 1))
(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))))
(let [curr-text @text-value]
(reset! text-value (str @text-value " "))
(js/setTimeout #(reset! text-value curr-text) 10)))
(reset! sending-images? (boolean (seq images))))
[(boolean (seq images))]))
(defn did-mount
[{:keys [selectable-input-ref input-ref selection-manager]}]
(rn/use-effect

View File

@ -58,6 +58,7 @@
[{: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)
@ -80,7 +81,7 @@
(gesture/on-update
(fn [event]
(let [translation (oops/oget event "translationY")
min-height (utils/get-min-height lines)
min-height (utils/get-min-height lines images link-previews?)
new-height (- (reanimated/get-shared-value saved-height) translation)
bounded-height (utils/bounded-val new-height min-height max-height)]
(when keyboard-shown

View File

@ -15,10 +15,13 @@
(defn focus
[{:keys [input-ref] :as props}
{:keys [text-value focused? lock-selection? saved-cursor-position gradient-z-index]}
{:keys [last-height opacity background-y gradient-opacity container-opacity] :as animations}
{:keys [height saved-height last-height opacity background-y gradient-opacity container-opacity]
:as animations}
{:keys [max-height] :as dimensions}]
(reset! focused? true)
(rf/dispatch [:chat.ui/set-input-focused true])
(reanimated/animate height (reanimated/get-shared-value last-height))
(reanimated/set-shared-value saved-height (reanimated/get-shared-value last-height))
(reanimated/animate container-opacity 1)
(when (> (reanimated/get-shared-value last-height) (* constants/background-threshold max-height))
(reanimated/animate opacity 1)
@ -40,8 +43,14 @@
{: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)
reopen-height (utils/calc-reopen-height text-value min-height content-height saved-height)]
min-height (utils/get-min-height lines images link-previews?)
reopen-height (utils/calc-reopen-height text-value
min-height
max-height
content-height
saved-height
images
link-previews?)]
(reset! focused? false)
(rf/dispatch [:chat.ui/set-input-focused false])
(reanimated/set-shared-value last-height reopen-height)
@ -63,16 +72,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/bounded-val content-size constants/input-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/bounded-val 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)]
(reset! content-height content-size)
(when (utils/update-height? content-size height max-height maximized?)
(reanimated/animate height new-height)
@ -87,7 +99,7 @@
(when (= (reanimated/get-shared-value opacity) 1)
(reanimated/animate opacity 0)
(js/setTimeout #(reanimated/set-shared-value background-y (- window-height)) 300)))
(rf/dispatch [:chat.ui/set-input-content-height new-height])
(rf/dispatch [:chat.ui/set-input-content-height content-size])
(reset! lock-layout? (> lines 2)))))
(defn scroll

View File

@ -32,7 +32,9 @@
(if (seq images) constants/images-container-height 0)))
[images])
[reanimated/view
{:style (reanimated/apply-animations-to-style {:height height} {:margin-horizontal -20})}
{:style (reanimated/apply-animations-to-style {:height height}
{:margin-horizontal -20
:z-index 1})}
[gesture/flat-list
{:key-fn first
:render-fn image

View File

@ -23,7 +23,7 @@
(let [previews (rf/sub [:chats/link-previews-unfurled])
height (use-animated-height (boolean (seq previews)))]
[reanimated/view
{:style (reanimated/apply-animations-to-style {:height height} {})}
{:style (reanimated/apply-animations-to-style {:height height} {:z-index 1})}
[quo/url-preview-list
{:key-fn :url
:preview-width (- (:width (rn/get-window))

View File

@ -63,7 +63,9 @@
(defn input-text
[{:keys [saved-emoji-kb-extra-height]}
{:keys [focused? maximized?]}]
{:keys [focused? maximized?]}
{:keys [link-previews? images]}
max-height]
(merge typography/paragraph-1
{:color (colors/theme-colors :black :white)
:text-align-vertical :top
@ -71,6 +73,9 @@
: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)}))
(defn background
[opacity background-y window-height]

View File

@ -28,12 +28,12 @@
(defn- f-shell-button
[{:keys [maximized?]} {:keys [height]} {:keys [images link-previews? reply edit]}]
(let [insets (safe-area/get-insets)
extra-height (utils/calc-extra-content-height images link-previews? reply edit)
extra-height (utils/calc-top-content-height reply edit)
translate-y (reanimated/use-shared-value
(utils/calc-shell-neg-y insets maximized? extra-height))]
(rn/use-effect
(fn []
(let [extra-height (utils/calc-extra-content-height images link-previews? reply edit)]
(let [extra-height (utils/calc-top-content-height reply edit)]
(reanimated/animate translate-y
(utils/calc-shell-neg-y insets maximized? extra-height))))
[@maximized? images link-previews? reply edit])

View File

@ -14,16 +14,6 @@
[val min-val max-val]
(max min-val (min val max-val)))
(defn get-min-height
[lines]
(if (> lines 1) constants/multiline-minimized-height constants/input-height))
(defn calc-reopen-height
[text-value min-height content-height saved-height]
(if (empty? @text-value)
min-height
(Math/min @content-height (reanimated/get-shared-value saved-height))))
(defn update-height?
[content-size height max-height maximized?]
(when-not @maximized?
@ -64,23 +54,44 @@
[height]
(Math/floor (/ height constants/line-height)))
(defn calc-extra-content-height
[images? link-previews? reply? edit?]
(let [height (if images? constants/images-container-height 0)
height (if link-previews? (+ height constants/links-container-height) height)
height (if reply? (+ height constants/reply-container-height) height)
(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))
(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))
(defn calc-reopen-height
[text-value min-height max-height content-height saved-height images link-previews?]
(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))]
(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)))
(defn calc-max-height
[{:keys [images link-previews? reply edit]} window-height kb-height insets]
[{: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-extra-content-height images link-previews? reply edit))]
max-height (- max-height (calc-top-content-height reply edit))]
max-height))
(defn empty-input?
@ -126,10 +137,10 @@
(defn calc-suggestions-position
[cursor-pos max-height size
{:keys [maximized?]}
{:keys [insets curr-height window-height keyboard-height images link-previews? reply edit]}]
{:keys [insets curr-height window-height keyboard-height reply edit]}]
(let [base (+ constants/composer-default-height (:bottom insets) 8)
base (+ base (- curr-height constants/input-height))
base (+ base (calc-extra-content-height images link-previews? reply edit))
base (+ base (calc-top-content-height reply edit))
view-height (- window-height keyboard-height (:top insets))
container-height (bounded-val
(* (/ constants/mentions-max-height 4) size)
@ -153,6 +164,7 @@
:emoji-kb-extra-height (atom nil)
:saved-emoji-kb-extra-height (atom nil)
:sending-images? (atom false)
:sending-links? (atom false)
:record-reset-fn (atom nil)
:scroll-y (atom 0)
:selection-event (atom nil)
@ -191,9 +203,10 @@
(defn init-animations
[{:keys [input-text images link-previews? reply audio]}
lines content-height max-height opacity background-y]
(let [initial-height (if (> lines 1)
constants/multiline-minimized-height
constants/input-height)]
(let [initial-height (if (> lines 1)
constants/multiline-minimized-height
constants/input-height)
bottom-content-height 0]
{:gradient-opacity (reanimated/use-shared-value 0)
:container-opacity (reanimated/use-shared-value
(if (empty-input?
@ -210,7 +223,7 @@
initial-height)
:last-height (reanimated/use-shared-value
(bounded-val
@content-height
(+ @content-height bottom-content-height)
constants/input-height
max-height))
:opacity opacity

View File

@ -62,6 +62,8 @@
(effects/reply props animations subs)
(effects/update-input-mention props state subs)
(effects/edit-mentions props state subs)
(effects/link-previews props state animations subs)
(effects/images props state animations subs)
[:<>
[sub-view/shell-button state animations subs]
[mentions/view props state animations max-height cursor-pos
@ -70,7 +72,7 @@
(:reply subs)
(:edit subs)]
[gesture/gesture-detector
{:gesture (drag-gesture/drag-gesture props state animations dimensions keyboard-shown)}
{:gesture (drag-gesture/drag-gesture props state animations subs dimensions keyboard-shown)}
[reanimated/view
{:style (style/sheet-container insets state animations)
:on-layout #(handler/layout % state blur-height)}
@ -94,6 +96,7 @@
:on-content-size-change #(handler/content-size-change %
state
animations
subs
dimensions
(or keyboard-shown (:edit subs)))
:on-scroll #(handler/scroll % props state animations dimensions)
@ -106,12 +109,12 @@
:multiline true
:placeholder (i18n/label :t/type-something)
:placeholder-text-color (colors/theme-colors colors/neutral-40 colors/neutral-50)
:style (style/input-text props state)
:style (style/input-text props state subs max-height)
:max-length constants/max-text-size
:accessibility-label :chat-message-input}]]
[gradients/view props state animations show-bottom-gradient?]]
[link-preview/view]
[images/images-list]
[gradients/view props state animations show-bottom-gradient?]
[link-preview/view]
[images/images-list]]
[actions/view props state animations window-height insets subs]]]]))
(defn composer