diff --git a/src/js/worklets/chat/messenger/composer.js b/src/js/worklets/chat/messenger/composer.js index 7600d7a1c862..3ac2febe6fbb 100644 --- a/src/js/worklets/chat/messenger/composer.js +++ b/src/js/worklets/chat/messenger/composer.js @@ -1,4 +1,4 @@ -import { useDerivedValue, withTiming } from 'react-native-reanimated'; +import { useDerivedValue, withTiming, withDelay } from 'react-native-reanimated'; export function scrollDownButtonOpacity(chatListScrollY, isComposerFocused, windowHeight) { return useDerivedValue(function () { @@ -24,3 +24,26 @@ export function jumpToButtonPosition(scrollDownButtonOpacity, isComposerFocused) return withTiming(scrollDownButtonOpacity.value == 1 || isComposerFocused.value ? 35 : 0); }); } + +export function composerContainerOpacity(isComposerFocused, isEmptyInput, emptyOpacity) { + return useDerivedValue(function () { + 'worklet'; + return isEmptyInput.value && !isComposerFocused.value + ? withDelay(300, withTiming(emptyOpacity, { duration: 0 })) + : 1; + }); +} + +export function blurContainerElevation(isComposerFocused, isEmptyInput) { + return useDerivedValue(function () { + 'worklet'; + return isEmptyInput.value && !isComposerFocused.value ? 10 : 0; + }); +} + +export function composerElevation(isComposerFocused, isEmptyInput) { + return useDerivedValue(function () { + 'worklet'; + return isEmptyInput.value && !isComposerFocused.value ? 0 : 10; + }); +} diff --git a/src/status_im/contexts/chat/messenger/composer/actions/view.cljs b/src/status_im/contexts/chat/messenger/composer/actions/view.cljs index 5a3e97cecf5a..4d7497695367 100644 --- a/src/status_im/contexts/chat/messenger/composer/actions/view.cljs +++ b/src/status_im/contexts/chat/messenger/composer/actions/view.cljs @@ -17,16 +17,14 @@ (defn send-message "Minimize composer, animate-out background overlay, clear input and flush state" [{:keys [sending-images? sending-links?]} - {:keys [text-value focused? maximized?]} - {:keys [height saved-height last-height opacity background-y container-opacity]} + {:keys [text-value maximized?]} + {:keys [height saved-height last-height opacity background-y]} window-height edit] (reanimated/animate height comp-constants/input-height) (reanimated/set-shared-value saved-height comp-constants/input-height) (reanimated/set-shared-value last-height comp-constants/input-height) (reanimated/animate opacity 0) - (when-not @focused? - (js/setTimeout #(reanimated/animate container-opacity comp-constants/empty-opacity) 300)) (js/setTimeout #(reanimated/set-shared-value background-y (- window-height)) 300) @@ -84,8 +82,7 @@ (defn audio-button [{:keys [record-reset-fn input-ref]} - {:keys [record-permission? recording? gesture-enabled? focused?]} - {:keys [container-opacity]}] + {:keys [record-permission? recording? gesture-enabled? focused?]}] (let [audio (rf/sub [:chats/sending-audio])] [rn/view {:style (style/record-audio-container) @@ -97,8 +94,7 @@ :on-start-recording (fn [] (rf/dispatch [:chat.ui/set-recording true]) (reset! recording? true) - (reset! gesture-enabled? false) - (reanimated/animate container-opacity 1)) + (reset! gesture-enabled? false)) :audio-file audio :on-lock (fn [] (rf/dispatch [:chat.ui/set-recording false])) @@ -110,9 +106,7 @@ (reset! recording? false) (reset! gesture-enabled? true) (rf/dispatch [:chat/send-audio file-path duration]) - (if-not @focused? - (reanimated/animate container-opacity - comp-constants/empty-opacity) + (when @focused? (js/setTimeout #(when @input-ref (.focus ^js @input-ref)) 300)) (rf/dispatch [:chat.ui/set-input-audio nil])) @@ -121,9 +115,7 @@ (rf/dispatch [:chat.ui/set-recording false]) (reset! recording? false) (reset! gesture-enabled? true) - (if-not @focused? - (reanimated/animate container-opacity - comp-constants/empty-opacity) + (when @focused? (js/setTimeout #(when @input-ref (.focus ^js @input-ref)) 300)) diff --git a/src/status_im/contexts/chat/messenger/composer/effects.cljs b/src/status_im/contexts/chat/messenger/composer/effects.cljs index cd112f552786..bc3853d049da 100644 --- a/src/status_im/contexts/chat/messenger/composer/effects.cljs +++ b/src/status_im/contexts/chat/messenger/composer/effects.cljs @@ -70,19 +70,17 @@ (defn audio-effect [{:keys [recording? gesture-enabled?]} - {:keys [container-opacity]} audio] (when (and audio (not @recording?)) (reset! recording? true) - (reset! gesture-enabled? false) - (reanimated/animate container-opacity 1))) + (reset! gesture-enabled? false))) (defn empty-effect - [{:keys [focused?]} - {:keys [container-opacity]} + [{:keys [empty-input?]} {:keys [input-text images link-previews? reply audio]}] - (when (and (not @focused?) (utils/empty-input? input-text images link-previews? reply audio)) - (reanimated/animate-delay container-opacity constants/empty-opacity 200))) + (reanimated/set-shared-value + empty-input? + (utils/empty-input? input-text images link-previews? reply audio))) (defn component-will-unmount [{:keys [keyboard-show-listener keyboard-hide-listener keyboard-frame-listener]}] @@ -100,8 +98,8 @@ (kb-default-height-effect state) (background-effect state animations dimensions chat-input) (link-preview-effect state) - (audio-effect state animations audio) - (empty-effect state animations subscriptions) + (audio-effect state audio) + (empty-effect animations subscriptions) (kb/add-kb-listeners props state animations dimensions) #(component-will-unmount props)) [max-height]) @@ -160,13 +158,10 @@ (defn use-reply [{:keys [input-ref]} - {:keys [container-opacity]} {:keys [reply]} chat-screen-layout-calculations-complete?] (rn/use-effect (fn [] - (when reply - (reanimated/animate container-opacity 1)) (when (and reply @input-ref (reanimated/get-shared-value chat-screen-layout-calculations-complete?)) (js/setTimeout #(.focus ^js @input-ref) 600))) [(:message-id reply)])) @@ -203,12 +198,10 @@ (defn use-images [{:keys [sending-images? input-ref]} {:keys [text-value maximized?]} - {:keys [container-opacity height saved-height]} + {:keys [height saved-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? diff --git a/src/status_im/contexts/chat/messenger/composer/gesture.cljs b/src/status_im/contexts/chat/messenger/composer/gesture.cljs index ea8f93cae2e9..00b8d7a96499 100644 --- a/src/status_im/contexts/chat/messenger/composer/gesture.cljs +++ b/src/status_im/contexts/chat/messenger/composer/gesture.cljs @@ -57,7 +57,7 @@ (defn drag-gesture [{:keys [input-ref] :as props} {:keys [gesture-enabled?] :as state} - {:keys [height saved-height last-height opacity background-y container-opacity] :as animations} + {:keys [height saved-height last-height opacity background-y] :as animations} {:keys [max-height lines] :as dimensions} keyboard-shown] (let [expanding? (atom true) @@ -68,7 +68,6 @@ (if-not keyboard-shown (do ; focus and end (when (< (oops/oget event "velocityY") constants/velocity-threshold) - (reanimated/set-shared-value container-opacity 1) (reanimated/set-shared-value last-height max-height) (maximize state animations dimensions)) (when @input-ref diff --git a/src/status_im/contexts/chat/messenger/composer/handlers.cljs b/src/status_im/contexts/chat/messenger/composer/handlers.cljs index 397fb2892b91..1bd54a911c5a 100644 --- a/src/status_im/contexts/chat/messenger/composer/handlers.cljs +++ b/src/status_im/contexts/chat/messenger/composer/handlers.cljs @@ -16,8 +16,8 @@ (defn focus "Animate to the `saved-height`, display background-overlay if needed, and set cursor position" [{:keys [input-ref] :as props} - {:keys [text-value focused? lock-selection? saved-cursor-position composer-focused? maximized?]} - {:keys [height saved-height last-height opacity background-y container-opacity] + {:keys [text-value focused? lock-selection? saved-cursor-position maximized?]} + {:keys [height saved-height last-height opacity background-y composer-focused?] :as animations} {:keys [max-height] :as dimensions}] (reanimated/set-shared-value composer-focused? true) @@ -27,7 +27,6 @@ new-height (min max-height last-height-value)] (reanimated/animate height new-height) (reanimated/set-shared-value saved-height new-height) - (reanimated/animate container-opacity 1) (when (> last-height-value (* constants/background-threshold max-height)) (reset! maximized? true) (reanimated/animate opacity 1) @@ -42,10 +41,9 @@ (defn blur "Save the current height, minimize the composer, animate-out the background, and save cursor position" [{:keys [text-value focused? lock-selection? cursor-position saved-cursor-position gradient-z-index - maximized? recording? composer-focused?]} - {:keys [height saved-height last-height gradient-opacity container-opacity opacity background-y]} - {:keys [content-height max-height window-height]} - {:keys [images link-previews? reply]}] + maximized? recording?]} + {:keys [height saved-height last-height gradient-opacity opacity background-y composer-focused?]} + {:keys [content-height max-height window-height]}] (when-not @recording? (let [lines (utils/calc-lines (- @content-height constants/extra-content-offset)) min-height (utils/get-min-height lines) @@ -62,8 +60,6 @@ (reanimated/set-shared-value saved-height min-height) (reanimated/animate opacity 0) (js/setTimeout #(reanimated/set-shared-value background-y (- window-height)) 300) - (when (utils/empty-input? @text-value images link-previews? reply nil) - (reanimated/animate container-opacity constants/empty-opacity)) (reanimated/animate gradient-opacity 0) (reset! lock-selection? true) (reset! saved-cursor-position @cursor-position) @@ -164,8 +160,3 @@ (let [{:keys [start end text-input-handle]} @selection-event] (selection/update-selection text-input-handle start end) (reset! selection-event nil))))) - -(defn layout - [event state blur-height] - (when (utils/update-blur-height? event state blur-height) - (reanimated/set-shared-value blur-height (oops/oget event "nativeEvent.layout.height")))) diff --git a/src/status_im/contexts/chat/messenger/composer/style.cljs b/src/status_im/contexts/chat/messenger/composer/style.cljs index c2e42f7743b5..c52fbee52104 100644 --- a/src/status_im/contexts/chat/messenger/composer/style.cljs +++ b/src/status_im/contexts/chat/messenger/composer/style.cljs @@ -10,13 +10,12 @@ (def border-top-radius 20) (defn shadow - [focused? theme] - (if platform/ios? + [theme] + (when platform/ios? {:shadow-radius 20 :shadow-opacity (colors/theme-colors 0.1 0.7 theme) :shadow-color colors/neutral-100 - :shadow-offset {:width 0 :height (colors/theme-colors -4 -8 theme)}} - {:elevation (if @focused? 10 0)})) + :shadow-offset {:width 0 :height (colors/theme-colors -4 -8 theme)}})) (def composer-sheet-and-jump-to-container {:position :absolute @@ -25,9 +24,10 @@ :right 0}) (defn sheet-container - [insets {:keys [focused?]} {:keys [container-opacity]} theme] + [insets {:keys [container-opacity composer-elevation]} theme] (reanimated/apply-animations-to-style - {:opacity container-opacity} + {:opacity container-opacity + :elevation composer-elevation} (merge {:border-top-left-radius border-top-radius :border-top-right-radius border-top-radius @@ -35,7 +35,7 @@ :background-color (colors/theme-colors colors/white colors/neutral-95 theme) :z-index 3 :padding-bottom (:bottom insets)} - (shadow focused? theme)))) + (shadow theme)))) (def bar-container {:height constants/bar-container-height @@ -95,17 +95,16 @@ :background-color colors/neutral-95-opa-70})) (defn blur-container - [height focused?] - (reanimated/apply-animations-to-style - {:height height} + [composer-default-height {:keys [blur-container-elevation]}] + [{:elevation blur-container-elevation} {:position :absolute - :elevation (if-not @focused? 10 0) :left 0 :right 0 :bottom 0 + :height composer-default-height :border-top-right-radius border-top-radius :border-top-left-radius border-top-radius - :overflow :hidden})) + :overflow :hidden}]) (defn blur-view [theme] diff --git a/src/status_im/contexts/chat/messenger/composer/sub_view.cljs b/src/status_im/contexts/chat/messenger/composer/sub_view.cljs index 6d2ae2491cd7..daeb8c1bb9b4 100644 --- a/src/status_im/contexts/chat/messenger/composer/sub_view.cljs +++ b/src/status_im/contexts/chat/messenger/composer/sub_view.cljs @@ -1,7 +1,6 @@ (ns status-im.contexts.chat.messenger.composer.sub-view (:require [quo.core :as quo] - [react-native.blur :as blur] [react-native.core :as rn] [react-native.reanimated :as reanimated] [status-im.contexts.chat.messenger.composer.style :as style] @@ -14,15 +13,6 @@ [rn/view {:style style/bar-container} [rn/view {:style (style/bar theme)}]]) -(defn f-blur-view - [{:keys [layout-height focused? theme]}] - [reanimated/view {:style (style/blur-container layout-height focused?)} - [blur/view (style/blur-view theme)]]) - -(defn blur-view - [props] - [:f> f-blur-view props]) - (defn- f-shell-button [{:keys [composer-focused?]} chat-list-scroll-y window-height] (let [customization-color (rf/sub [:profile/customization-color]) @@ -52,5 +42,5 @@ scroll-down-button-opacity]])) (defn shell-button - [state chat-list-scroll-y window-height] - [:f> f-shell-button state chat-list-scroll-y window-height]) + [shared-values chat-list-scroll-y window-height] + [:f> f-shell-button shared-values chat-list-scroll-y window-height]) diff --git a/src/status_im/contexts/chat/messenger/composer/utils.cljs b/src/status_im/contexts/chat/messenger/composer/utils.cljs index 7d325a2eaa7c..fb3feef68d95 100644 --- a/src/status_im/contexts/chat/messenger/composer/utils.cljs +++ b/src/status_im/contexts/chat/messenger/composer/utils.cljs @@ -1,7 +1,6 @@ (ns status-im.contexts.chat.messenger.composer.utils (:require [clojure.string :as string] - [oops.core :as oops] [react-native.core :as rn] [react-native.platform :as platform] [react-native.reanimated :as reanimated] @@ -9,7 +8,8 @@ [status-im.contexts.chat.messenger.composer.constants :as constants] [status-im.contexts.chat.messenger.composer.selection :as selection] [utils.number] - [utils.re-frame :as rf])) + [utils.re-frame :as rf] + [utils.worklets.chat.messenger.composer :as worklets])) (defn bounded-val [v min-v max-v] @@ -44,12 +44,6 @@ (or @maximized? (> new-height (* constants/background-threshold max-height)))) -(defn update-blur-height? - [event {:keys [lock-layout? focused?]} layout-height] - (or (not @lock-layout?) - (not @focused?) - (> (reanimated/get-shared-value layout-height) (oops/oget event "nativeEvent.layout.height")))) - (defn calc-lines [height] (Math/floor (/ height constants/line-height))) @@ -193,8 +187,7 @@ :record-permission? (reagent/atom true) :recording? (reagent/atom false) :first-level? (reagent/atom true) - :menu-items (reagent/atom selection/first-level-menu-items) - :composer-focused? (reanimated/use-shared-value false)}) + :menu-items (reagent/atom selection/first-level-menu-items)}) (defn init-subs [] @@ -209,31 +202,36 @@ :input-text (:input-text chat-input) :input-content-height (:input-content-height chat-input)})) +(defn init-shared-values + [] + (let [composer-focused? (reanimated/use-shared-value false) + empty-input-shared-value? (reanimated/use-shared-value true)] + {:composer-focused? composer-focused? + :empty-input? empty-input-shared-value? + :container-opacity (worklets/composer-container-opacity composer-focused? + empty-input-shared-value? + constants/empty-opacity) + :blur-container-elevation (worklets/blur-container-elevation composer-focused? + empty-input-shared-value?) + :composer-elevation (worklets/composer-elevation composer-focused? + empty-input-shared-value?)})) + (defn init-animations - [{:keys [input-text images link-previews? reply audio]} - lines content-height max-height opacity background-y] + [lines content-height max-height opacity background-y shared-values] (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? - input-text - images - link-previews? - reply - audio) - 0.7 - 1)) - :height (reanimated/use-shared-value - initial-height) - :saved-height (reanimated/use-shared-value - initial-height) - :last-height (reanimated/use-shared-value - (utils.number/value-in-range - (+ @content-height bottom-content-height) - constants/input-height - max-height)) - :opacity opacity - :background-y background-y})) + (assoc shared-values + :gradient-opacity (reanimated/use-shared-value 0) + :height (reanimated/use-shared-value + initial-height) + :saved-height (reanimated/use-shared-value + initial-height) + :last-height (reanimated/use-shared-value + (utils.number/value-in-range + (+ @content-height bottom-content-height) + constants/input-height + max-height)) + :opacity opacity + :background-y background-y))) diff --git a/src/status_im/contexts/chat/messenger/composer/view.cljs b/src/status_im/contexts/chat/messenger/composer/view.cljs index 237ecab97383..ddeb1676100c 100644 --- a/src/status_im/contexts/chat/messenger/composer/view.cljs +++ b/src/status_im/contexts/chat/messenger/composer/view.cljs @@ -2,6 +2,7 @@ (:require [quo.foundations.colors :as colors] [quo.theme :as quo.theme] + [react-native.blur :as blur] [react-native.core :as rn] [react-native.gesture :as gesture] [react-native.hooks :as hooks] @@ -32,11 +33,10 @@ [{:keys [insets chat-list-scroll-y chat-screen-layout-calculations-complete? - window-height - blur-height opacity background-y - theme]} props state] + theme + window-height]} props state shared-values] (let [subscriptions (utils/init-subs) content-height (reagent/atom (or (:input-content-height ; Actual text height subscriptions) @@ -53,12 +53,12 @@ ;; Maximum number of lines that can be displayed when composer in maximized max-lines (utils/calc-lines max-height) animations (utils/init-animations - subscriptions lines content-height max-height opacity - background-y) + background-y + shared-values) dimensions {:content-height content-height :max-height max-height :window-height window-height @@ -76,7 +76,7 @@ dimensions subscriptions) (effects/use-edit props state subscriptions chat-screen-layout-calculations-complete?) - (effects/use-reply props animations subscriptions chat-screen-layout-calculations-complete?) + (effects/use-reply props subscriptions chat-screen-layout-calculations-complete?) (effects/update-input-mention props state subscriptions) (effects/link-previews props state animations subscriptions) (effects/use-images props state animations subscriptions) @@ -88,13 +88,12 @@ (:edit subscriptions)] [rn/view {:style style/composer-sheet-and-jump-to-container} - [sub-view/shell-button state chat-list-scroll-y window-height] + [sub-view/shell-button shared-values chat-list-scroll-y window-height] [gesture/gesture-detector {:gesture (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)} + {:style (style/sheet-container insets animations theme)} [sub-view/bar] [:<> [reply/view state (:input-ref props)] @@ -116,7 +115,7 @@ {:ref #(reset! (:input-ref props) %) :default-value @(:text-value state) :on-focus #(handler/focus props state animations dimensions) - :on-blur #(handler/blur state animations dimensions subscriptions) + :on-blur #(handler/blur state animations dimensions) :on-content-size-change #(handler/content-size-change % state animations @@ -146,29 +145,24 @@ (defn f-composer [props] - (let [window-height (:height (rn/get-window)) - theme (quo.theme/use-theme-value) - opacity (reanimated/use-shared-value 0) - background-y (reanimated/use-shared-value (- window-height)) ; Y position of background - ; overlay - blur-height (reanimated/use-shared-value (+ constants/composer-default-height - (:bottom (:insets props)))) - extra-params (assoc props - :window-height window-height - :blur-height blur-height - :opacity opacity - :background-y background-y - :theme theme) - props (utils/init-non-reactive-state) - state (utils/init-reactive-state)] + (let [theme (quo.theme/use-theme-value) + opacity (reanimated/use-shared-value 0) + window-height (:height (rn/get-window)) + background-y (reanimated/use-shared-value (- window-height)) + composer-default-height (+ constants/composer-default-height (:bottom (:insets props))) + shared-values (utils/init-shared-values) + extra-params (assoc props + :window-height window-height + :opacity opacity + :background-y background-y + :theme theme) + props (utils/init-non-reactive-state) + state (utils/init-reactive-state)] [rn/view (when platform/ios? {:style {:z-index 1}}) - [reanimated/view {:style (style/background opacity background-y window-height)}] ; background - ; overlay - [sub-view/blur-view - {:layout-height blur-height - :focused? (:focused? state) - :theme theme}] - [:f> sheet-component extra-params props state]])) + [reanimated/view {:style (style/background opacity background-y window-height)}] + [reanimated/view {:style (style/blur-container composer-default-height shared-values)} + [blur/view (style/blur-view theme)]] + [:f> sheet-component extra-params props state shared-values]])) (defn composer [props] diff --git a/src/utils/worklets/chat/messenger/composer.cljs b/src/utils/worklets/chat/messenger/composer.cljs index 91b1282ed805..2d7dfc463763 100644 --- a/src/utils/worklets/chat/messenger/composer.cljs +++ b/src/utils/worklets/chat/messenger/composer.cljs @@ -13,3 +13,15 @@ (defn jump-to-button-position [scroll-down-button-opacity-sv composer-focused?] (.jumpToButtonPosition ^js worklets scroll-down-button-opacity-sv composer-focused?)) + +(defn composer-container-opacity + [composer-focused? empty-input? empty-opacity] + (.composerContainerOpacity ^js worklets composer-focused? empty-input? empty-opacity)) + +(defn blur-container-elevation + [composer-focused? empty-input?] + (.blurContainerElevation ^js worklets composer-focused? empty-input?)) + +(defn composer-elevation + [composer-focused? empty-input?] + (.composerElevation ^js worklets composer-focused? empty-input?))