From c1c8c210e84dcd29fe558748715058f6ca72ad36 Mon Sep 17 00:00:00 2001 From: Alexander Date: Thu, 7 Sep 2023 13:13:16 +0200 Subject: [PATCH] Top bar stability & animation improvements (#17170) * Top bar stability improvements Style updates Style updates, bugfixes * Display name update * Style fix --- .../contexts/chat/composer/actions/view.cljs | 33 +++--- .../contexts/chat/composer/sub_view.cljs | 15 ++- .../contexts/chat/composer/view.cljs | 27 +++-- .../contexts/chat/messages/list/view.cljs | 97 +++++----------- .../chat/messages/navigation/style.cljs | 10 +- .../chat/messages/navigation/view.cljs | 67 +++++------ .../contexts/chat/messages/view.cljs | 109 ++++++++++++++---- 7 files changed, 193 insertions(+), 165 deletions(-) diff --git a/src/status_im2/contexts/chat/composer/actions/view.cljs b/src/status_im2/contexts/chat/composer/actions/view.cljs index ba8e5ab060..07eba55e01 100644 --- a/src/status_im2/contexts/chat/composer/actions/view.cljs +++ b/src/status_im2/contexts/chat/composer/actions/view.cljs @@ -9,7 +9,6 @@ [reagent.core :as reagent] [status-im2.common.alert.events :as alert] [status-im2.contexts.chat.composer.constants :as comp-constants] - [status-im2.contexts.chat.messages.list.view :as messages.list] [status-im2.common.device-permissions :as device-permissions] [utils.i18n :as i18n] [utils.re-frame :as rf] @@ -20,7 +19,9 @@ [{:keys [sending-images? sending-links?]} {:keys [text-value focused? maximized?]} {:keys [height saved-height last-height opacity background-y container-opacity]} - window-height edit] + window-height + edit + scroll-to-bottom-fn] (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) @@ -38,14 +39,13 @@ (reset! text-value "") (reset! sending-links? false) (reset! sending-images? false) - (when-not (some? edit) - (messages.list/scroll-to-bottom))) + (when (and (not (some? edit)) scroll-to-bottom-fn) + (scroll-to-bottom-fn))) (defn f-send-button - [props {:keys [text-value] :as state} - animations window-height images? - btn-opacity z-index edit] - (let [customization-color (rf/sub [:profile/customization-color])] + [props state animations window-height images? btn-opacity scroll-to-bottom-fn z-index edit] + (let [{:keys [text-value]} state + customization-color (rf/sub [:profile/customization-color])] (rn/use-effect (fn [] (if (or (seq @text-value) images?) (when (or (not= @z-index 1) (not= (reanimated/get-shared-value btn-opacity) 1)) @@ -60,17 +60,19 @@ [reanimated/view {:style (style/send-button btn-opacity @z-index)} [quo/button - {:icon-only? true - :size 32 + {:icon-only? true + :size 32 :customization-color customization-color :accessibility-label :send-message-button - :on-press #(send-message props state animations window-height edit)} + :on-press #(send-message props state animations window-height edit scroll-to-bottom-fn)} :i/arrow-up]])) (defn send-button - [props {:keys [text-value] :as state} animations window-height images? edit btn-opacity] + [props {:keys [text-value] :as state} animations window-height images? edit btn-opacity + scroll-to-bottom-fn] (let [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 edit])) + [:f> f-send-button props state animations window-height images? btn-opacity scroll-to-bottom-fn + z-index edit])) (defn disabled-audio-button [opacity] @@ -214,7 +216,7 @@ :icon :i/format}]) (defn view - [props state animations window-height insets {:keys [edit images]}] + [props state animations window-height insets scroll-to-bottom-fn {:keys [edit images]}] (let [send-btn-opacity (reanimated/use-shared-value 0) audio-btn-opacity (reanimated/interpolate send-btn-opacity [0 1] [1 0])] [rn/view {:style style/actions-container} @@ -225,7 +227,8 @@ [image-button props animations insets] [reaction-button] [format-button]] - [:f> send-button props state animations window-height images edit send-btn-opacity] + [:f> send-button props state animations window-height images edit send-btn-opacity + scroll-to-bottom-fn] (when (and (not edit) (not images)) ;; TODO(alwx): needs to be replaced with an `audio-button` later. ;; See https://github.com/status-im/status-mobile/issues/16084 for more details. diff --git a/src/status_im2/contexts/chat/composer/sub_view.cljs b/src/status_im2/contexts/chat/composer/sub_view.cljs index 808e76c158..7ee5a6d7b3 100644 --- a/src/status_im2/contexts/chat/composer/sub_view.cljs +++ b/src/status_im2/contexts/chat/composer/sub_view.cljs @@ -6,7 +6,6 @@ [status-im2.config :as config] [react-native.reanimated :as reanimated] [status-im2.contexts.chat.composer.style :as style] - [status-im2.contexts.chat.messages.list.view :as messages.list] [utils.i18n :as i18n] [utils.re-frame :as rf])) @@ -25,16 +24,16 @@ [:f> f-blur-view layout-height focused?]) (defn- f-shell-button - [{:keys [focused?]}] + [{:keys [focused?]} scroll-to-bottom-fn show-floating-scroll-down-button?] (let [customization-color (rf/sub [:profile/customization-color]) - hide-shell? (or @focused? @messages.list/show-floating-scroll-down-button) + hide-shell? (or @focused? @show-floating-scroll-down-button?) y-shell (reanimated/use-shared-value (if hide-shell? 35 0)) opacity (reanimated/use-shared-value (if hide-shell? 0 1))] (rn/use-effect (fn [] (reanimated/animate opacity (if hide-shell? 0 1)) (reanimated/animate y-shell (if hide-shell? 35 0))) - [@focused? @messages.list/show-floating-scroll-down-button]) + [@focused? @show-floating-scroll-down-button?]) [:<> [reanimated/view {:style (style/shell-button y-shell opacity)} @@ -48,11 +47,11 @@ :label (i18n/label :t/jump-to) :style {:align-self :center}}} {}]] - (when @messages.list/show-floating-scroll-down-button + (when @show-floating-scroll-down-button? [quo/floating-shell-button - {:scroll-to-bottom {:on-press messages.list/scroll-to-bottom}} + {:scroll-to-bottom {:on-press scroll-to-bottom-fn}} style/scroll-to-bottom-button])])) (defn shell-button - [state animations subscriptions] - [:f> f-shell-button state animations subscriptions]) + [state scroll-to-bottom-fn show-floating-scroll-down-button?] + [:f> f-shell-button state scroll-to-bottom-fn show-floating-scroll-down-button?]) diff --git a/src/status_im2/contexts/chat/composer/view.cljs b/src/status_im2/contexts/chat/composer/view.cljs index 009a0f784c..2d31a0d43f 100644 --- a/src/status_im2/contexts/chat/composer/view.cljs +++ b/src/status_im2/contexts/chat/composer/view.cljs @@ -26,7 +26,13 @@ [quo2.theme :as theme])) (defn sheet-component - [{:keys [insets window-height blur-height opacity background-y]} props state] + [{:keys [insets + scroll-to-bottom-fn + show-floating-scroll-down-button? + window-height + blur-height + opacity + background-y]} props state] (let [{:keys [chat-screen-loaded?] :as subscriptions} (utils/init-subs) content-height (reagent/atom (or (:input-content-height @@ -76,7 +82,7 @@ (:edit subscriptions)]) [rn/view {:style style/composer-sheet-and-jump-to-container} - [sub-view/shell-button state] + [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)} @@ -127,20 +133,23 @@ [gradients/view props state animations show-bottom-gradient?] [link-preview/view] [images/images-list]])] - [:f> actions/view props state animations window-height insets subscriptions]]]]])) + [:f> actions/view props state animations window-height insets scroll-to-bottom-fn + subscriptions]]]]])) (defn composer - [insets] + [{:keys [insets scroll-to-bottom-fn show-floating-scroll-down-button?]}] (let [window-height (:height (rn/get-window)) opacity (reanimated/use-shared-value 0) background-y (reanimated/use-shared-value (- window-height)) blur-height (reanimated/use-shared-value (+ constants/composer-default-height (:bottom insets))) - extra-params {:insets insets - :window-height window-height - :blur-height blur-height - :opacity opacity - :background-y background-y} + extra-params {:insets insets + :window-height window-height + :scroll-to-bottom-fn scroll-to-bottom-fn + :show-floating-scroll-down-button? show-floating-scroll-down-button? + :blur-height blur-height + :opacity opacity + :background-y background-y} props (utils/init-props) state (utils/init-state)] [rn/view (when platform/ios? {:style {:z-index 1}}) diff --git a/src/status_im2/contexts/chat/messages/list/view.cljs b/src/status_im2/contexts/chat/messages/list/view.cljs index 391738b2a7..5af2be001d 100644 --- a/src/status_im2/contexts/chat/messages/list/view.cljs +++ b/src/status_im2/contexts/chat/messages/list/view.cljs @@ -1,25 +1,25 @@ (ns status-im2.contexts.chat.messages.list.view - (:require [oops.core :as oops] - [quo2.core :as quo] - [quo2.foundations.colors :as colors] - [react-native.background-timer :as background-timer] - [react-native.core :as rn] - [react-native.hooks :as hooks] - [react-native.platform :as platform] - [react-native.reanimated :as reanimated] - [react-native.safe-area :as safe-area] - [reagent.core :as reagent] - [status-im.ui.screens.chat.group :as chat.group] - [status-im.ui.screens.chat.message.gap :as message.gap] - [status-im2.constants :as constants] - [status-im2.contexts.chat.messages.content.view :as message] - [status-im2.contexts.chat.messages.list.state :as state] - [status-im2.contexts.chat.messages.list.style :as style] - [status-im2.contexts.shell.jump-to.constants :as jump-to.constants] - [status-im2.contexts.chat.composer.constants :as composer.constants] - [status-im2.contexts.chat.messages.navigation.style :as navigation.style] - [utils.i18n :as i18n] - [utils.re-frame :as rf])) + (:require + [oops.core :as oops] + [quo2.core :as quo] + [quo2.foundations.colors :as colors] + [react-native.background-timer :as background-timer] + [react-native.core :as rn] + [react-native.hooks :as hooks] + [react-native.platform :as platform] + [react-native.reanimated :as reanimated] + [reagent.core :as reagent] + [status-im.ui.screens.chat.group :as chat.group] + [status-im.ui.screens.chat.message.gap :as message.gap] + [status-im2.constants :as constants] + [status-im2.contexts.chat.messages.content.view :as message] + [status-im2.contexts.chat.messages.list.state :as state] + [status-im2.contexts.chat.messages.list.style :as style] + [status-im2.contexts.chat.composer.constants :as composer.constants] + [status-im2.contexts.chat.messages.navigation.style :as navigation.style] + [status-im2.contexts.shell.jump-to.constants :as jump-to.constants] + [utils.i18n :as i18n] + [utils.re-frame :as rf])) (defonce ^:const threshold-percentage-to-show-floating-scroll-down-button 75) (defonce ^:const loading-indicator-extra-spacing 250) @@ -27,10 +27,11 @@ (defonce ^:const scroll-animation-input-range [50 125]) (defonce ^:const min-message-height 32) +(defonce extra-keyboard-height (reagent/atom 0)) (defonce messages-list-ref (atom nil)) (defonce messages-view-height (reagent/atom 0)) (defonce messages-view-header-height (reagent/atom 0)) -(defonce show-floating-scroll-down-button (reagent/atom false)) +(defonce show-floating-scroll-down-button? (reagent/atom false)) (defn list-key-fn [{:keys [message-id value]}] (or message-id value)) (defn list-ref [ref] (reset! messages-list-ref ref)) @@ -48,9 +49,9 @@ threshold-height (* (/ layout-height 100) threshold-percentage-to-show-floating-scroll-down-button) reached-threshold? (> y threshold-height)] - (when (not= reached-threshold? @show-floating-scroll-down-button) + (when (not= reached-threshold? @show-floating-scroll-down-button?) (rn/configure-next (:ease-in-ease-out rn/layout-animation-presets)) - (reset! show-floating-scroll-down-button reached-threshold?)))) + (reset! show-floating-scroll-down-button? reached-threshold?)))) (defn on-viewable-items-changed [e] @@ -296,10 +297,9 @@ :on-viewable-items-changed on-viewable-items-changed :on-content-size-change (fn [_ y] ;; NOTE(alwx): here we set the initial value of `scroll-y` - ;; which is needed because by default the chat is scrolled to - ;; the - ;; bottom - ;; and no initial `on-scroll` event is getting triggered + ;; which is needed because by default the chat is + ;; scrolled to the bottom and no initial `on-scroll` + ;; event is getting triggered (let [scroll-y-shared (reanimated/get-shared-value scroll-y) content-height-shared (reanimated/get-shared-value @@ -360,44 +360,3 @@ [quo/skeleton-list {:content :messages :parent-height content-height}]]))) - -(defn f-messages-list - [{:keys [chat cover-bg-color header-comp footer-comp]}] - (let [insets (safe-area/get-insets) - scroll-y (reanimated/use-shared-value 0) - content-height (reanimated/use-shared-value 0) - {:keys [keyboard-height keyboard-shown]} (hooks/use-keyboard)] - (rn/use-effect - (fn [] - (if keyboard-shown - (reanimated/set-shared-value scroll-y - (+ (reanimated/get-shared-value scroll-y) - keyboard-height)) - (reanimated/set-shared-value scroll-y - (- (reanimated/get-shared-value scroll-y) - keyboard-height)))) - [keyboard-shown keyboard-height]) - ;; Note - Don't pass `behavior :height` to keyboard avoiding view, - ;; It breaks composer - https://github.com/status-im/status-mobile/issues/16595 - [rn/keyboard-avoiding-view - {:style (style/keyboard-avoiding-container insets) - :keyboard-vertical-offset (- (:bottom insets))} - - (when header-comp - [header-comp - {:scroll-y scroll-y}]) - - [message-list-content-view - {:chat chat - :insets insets - :scroll-y scroll-y - :content-height content-height - :cover-bg-color cover-bg-color - :keyboard-shown? keyboard-shown}] - - (when footer-comp - [footer-comp {:insets insets}])])) - -(defn messages-list - [props] - [:f> f-messages-list props]) diff --git a/src/status_im2/contexts/chat/messages/navigation/style.cljs b/src/status_im2/contexts/chat/messages/navigation/style.cljs index 82c24570a5..3786a1c67c 100644 --- a/src/status_im2/contexts/chat/messages/navigation/style.cljs +++ b/src/status_im2/contexts/chat/messages/navigation/style.cljs @@ -52,8 +52,14 @@ {:opacity animation}) blur-view)) -(def navigation-view - {:z-index 1}) +(defn navigation-view + [loaded?] + {:z-index 1 + :top 0 + :right 0 + :left 0 + :position :absolute + :opacity (if loaded? 1 0)}) (def header-container {:position :absolute diff --git a/src/status_im2/contexts/chat/messages/navigation/view.cljs b/src/status_im2/contexts/chat/messages/navigation/view.cljs index a9e80b85cf..13c2cfda30 100644 --- a/src/status_im2/contexts/chat/messages/navigation/view.cljs +++ b/src/status_im2/contexts/chat/messages/navigation/view.cljs @@ -9,52 +9,38 @@ [react-native.reanimated :as reanimated] [status-im2.common.home.actions.view :as actions] [status-im2.config :as config] - [status-im2.constants :as constants] [status-im2.contexts.chat.messages.navigation.style :as style] [status-im2.contexts.chat.messages.pin.banner.view :as pin.banner] [utils.i18n :as i18n] [utils.re-frame :as rf])) (defn f-view - [{:keys [scroll-y]}] - (let [{:keys [group-chat chat-id chat-name emoji - chat-type] - :as chat} (rf/sub [:chats/current-chat-chat-view]) - chat-screen-loaded? (rf/sub [:shell/chat-screen-loaded?]) - all-loaded? (when chat-screen-loaded? - (rf/sub [:chats/all-loaded? (:chat-id chat)])) - display-name (cond - (= chat-type constants/one-to-one-chat-type) - (first (rf/sub [:contacts/contact-two-names-by-identity chat-id])) - (= chat-type constants/community-chat-type) - (str (when emoji (str emoji " ")) "# " chat-name) - :else (str emoji chat-name)) - online? (rf/sub [:visibility-status-updates/online? chat-id]) - photo-path (rf/sub [:chats/photo-path chat-id]) - opacity-animation (reanimated/interpolate scroll-y - [style/navigation-bar-height - (+ style/navigation-bar-height 30)] - [0 1] - {:extrapolateLeft "clamp" - :extrapolateRight "extend"}) - banner-opacity-animation (reanimated/interpolate scroll-y - [(+ style/navigation-bar-height 150) - (+ style/navigation-bar-height 200)] - [0 1] - {:extrapolateLeft "clamp" - :extrapolateRight "extend"}) - translate-animation (reanimated/interpolate scroll-y - [(+ style/navigation-bar-height 25) - (+ style/navigation-bar-height 100)] - [50 0] - {:extrapolateLeft "clamp" - :extrapolateRight "clamp"}) - title-opacity-animation (reanimated/interpolate scroll-y - [0 50] - [0 1] - {:extrapolateLeft "clamp" - :extrapolateRight "clamp"})] - [rn/view {:style style/navigation-view} + [{:keys [scroll-y chat chat-screen-loaded? all-loaded? display-name online? photo-path]}] + (let [{:keys [group-chat chat-id]} chat + opacity-animation (reanimated/interpolate scroll-y + [style/navigation-bar-height + (+ style/navigation-bar-height 30)] + [0 1] + {:extrapolateLeft "clamp" + :extrapolateRight "extend"}) + banner-opacity-animation (reanimated/interpolate scroll-y + [(+ style/navigation-bar-height 150) + (+ style/navigation-bar-height 200)] + [0 1] + {:extrapolateLeft "clamp" + :extrapolateRight "extend"}) + translate-animation (reanimated/interpolate scroll-y + [(+ style/navigation-bar-height 25) + (+ style/navigation-bar-height 100)] + [50 0] + {:extrapolateLeft "clamp" + :extrapolateRight "clamp"}) + title-opacity-animation (reanimated/interpolate scroll-y + [0 50] + [0 1] + {:extrapolateLeft "clamp" + :extrapolateRight "clamp"})] + [rn/view {:style (style/navigation-view chat-screen-loaded?)} [reanimated/view {:style (style/animated-background-view all-loaded? opacity-animation nil)}] @@ -64,6 +50,7 @@ :blur-type (colors/theme-colors :light :dark) :blur-radius (if platform/ios? 20 10) :style {:flex 1}}]] + [rn/view {:style style/header-container} [rn/touchable-opacity {:active-opacity 1 diff --git a/src/status_im2/contexts/chat/messages/view.cljs b/src/status_im2/contexts/chat/messages/view.cljs index b99fc1944c..1b09b564eb 100644 --- a/src/status_im2/contexts/chat/messages/view.cljs +++ b/src/status_im2/contexts/chat/messages/view.cljs @@ -1,26 +1,91 @@ (ns status-im2.contexts.chat.messages.view - (:require [status-im2.contexts.chat.composer.view :as composer] - [status-im2.contexts.chat.messages.contact-requests.bottom-drawer :as - contact-requests.bottom-drawer] - [status-im2.contexts.chat.messages.list.view :as messages.list] - [status-im2.contexts.chat.messages.navigation.view :as messages.navigation] - [utils.re-frame :as rf])) + (:require + [react-native.core :as rn] + [react-native.hooks :as hooks] + [react-native.reanimated :as reanimated] + [react-native.safe-area :as safe-area] + [reagent.core :as reagent] + [status-im2.constants :as constants] + [status-im2.contexts.chat.messages.contact-requests.bottom-drawer :as contact-requests.bottom-drawer] + [status-im2.contexts.chat.messages.list.view :as list.view] + [status-im2.contexts.chat.messages.list.style :as style] + [status-im2.contexts.chat.composer.view :as composer.view] + [status-im2.contexts.chat.messages.navigation.view :as messages.navigation] + [utils.re-frame :as rf])) + +(defonce extra-keyboard-height (reagent/atom 0)) + +(defn f-chat + [] + (let [insets (safe-area/get-insets) + scroll-y (reanimated/use-shared-value 0) + content-height (reanimated/use-shared-value 0) + {:keys [keyboard-height keyboard-shown]} (hooks/use-keyboard) + {:keys [chat-id + contact-request-state + group-chat + able-to-send-message? + chat-type + chat-name + emoji] + :as chat} (rf/sub [:chats/current-chat-chat-view]) + chat-screen-loaded? (rf/sub [:shell/chat-screen-loaded?]) + all-loaded? (when chat-screen-loaded? + (rf/sub [:chats/all-loaded? (:chat-id chat)])) + display-name (cond + (= chat-type constants/one-to-one-chat-type) + (first (rf/sub + [:contacts/contact-two-names-by-identity + chat-id])) + (= chat-type constants/community-chat-type) + (str (when emoji (str emoji " ")) "# " chat-name) + :else (str emoji chat-name)) + online? (rf/sub [:visibility-status-updates/online? chat-id]) + photo-path (rf/sub [:chats/photo-path chat-id])] + (rn/use-effect + (fn [] + ;; If keyboard is shown then adjust `scroll-y` + (when (and keyboard-shown (> keyboard-height 0)) + (reanimated/set-shared-value scroll-y + (+ (reanimated/get-shared-value scroll-y) + keyboard-height)) + (reset! extra-keyboard-height keyboard-height)) + ;; If keyboard is not shown then subtract the keyboard height from `scroll-y` value + (when-not keyboard-shown + (reanimated/set-shared-value scroll-y + (- (reanimated/get-shared-value scroll-y) + @extra-keyboard-height)))) + [keyboard-shown keyboard-height]) + ;; Note - Don't pass `behavior :height` to keyboard avoiding view, + ;; It breaks composer - https://github.com/status-im/status-mobile/issues/16595 + [rn/keyboard-avoiding-view + {:style (style/keyboard-avoiding-container insets) + :keyboard-vertical-offset (- (:bottom insets))} + + [list.view/message-list-content-view + {:chat chat + :insets insets + :scroll-y scroll-y + :content-height content-height + :cover-bg-color :turquoise + :keyboard-shown? keyboard-shown}] + + [messages.navigation/navigation-view + {:scroll-y scroll-y + :chat chat + :chat-screen-loaded? chat-screen-loaded? + :all-loaded? all-loaded? + :display-name display-name + :online? online? + :photo-path photo-path}] + + (if able-to-send-message? + [:f> composer.view/composer + {:insets insets + :scroll-to-bottom-fn list.view/scroll-to-bottom + :show-floating-scroll-down-button? list.view/show-floating-scroll-down-button?}] + [contact-requests.bottom-drawer/view chat-id contact-request-state group-chat])])) (defn chat [] - (let [{:keys [chat-id - contact-request-state - group-chat - able-to-send-message?] - :as current-chat} (rf/sub [:chats/current-chat-chat-view])] - [messages.list/messages-list - {:cover-bg-color :turquoise - :chat current-chat - :header-comp (fn [{:keys [scroll-y]}] - [messages.navigation/navigation-view {:scroll-y scroll-y}]) - :footer-comp (when (some? able-to-send-message?) - (fn [{:keys [insets]}] - (if-not able-to-send-message? - [contact-requests.bottom-drawer/view chat-id contact-request-state - group-chat] - [:f> composer/composer insets])))}])) + [:f> f-chat])