From 0cc631ce2e962c0b6f361d8d2d5fda5a10a41e6b Mon Sep 17 00:00:00 2001 From: Omar Basem Date: Wed, 24 May 2023 22:32:31 +0400 Subject: [PATCH] refactor: lightbox screen (#15996) * refactor: lightbox screen --- src/quo2/foundations/colors.cljs | 1 + .../contexts/chat/lightbox/bottom_view.cljs | 107 ++++--- .../contexts/chat/lightbox/constants.cljs | 4 + .../contexts/chat/lightbox/style.cljs | 4 +- .../contexts/chat/lightbox/top_view.cljs | 90 +++--- .../contexts/chat/lightbox/utils.cljs | 166 ++++++++++ .../contexts/chat/lightbox/view.cljs | 299 ++++++------------ .../chat/messages/content/album/view.cljs | 7 +- 8 files changed, 360 insertions(+), 318 deletions(-) create mode 100644 src/status_im2/contexts/chat/lightbox/utils.cljs diff --git a/src/quo2/foundations/colors.cljs b/src/quo2/foundations/colors.cljs index 48613ad399..c6dcd4ca08 100644 --- a/src/quo2/foundations/colors.cljs +++ b/src/quo2/foundations/colors.cljs @@ -130,6 +130,7 @@ ;;Solid (def black "#000000") +(def black-opa-0 (alpha black 0)) (def onboarding-header-black "#000716") ;;;;Primary diff --git a/src/status_im2/contexts/chat/lightbox/bottom_view.cljs b/src/status_im2/contexts/chat/lightbox/bottom_view.cljs index 58f2569070..ed88633637 100644 --- a/src/status_im2/contexts/chat/lightbox/bottom_view.cljs +++ b/src/status_im2/contexts/chat/lightbox/bottom_view.cljs @@ -15,60 +15,59 @@ :offset (* (+ c/small-image-size 8) index) :index index}) +(defn- f-small-image + [item index _ {:keys [scroll-index props]}] + (let [size (if (= @scroll-index index) c/focused-image-size c/small-image-size) + size-value (anim/use-val size) + {:keys [scroll-index-lock? small-list-ref flat-list-ref]} + props] + (anim/animate size-value size) + [rn/touchable-opacity + {:active-opacity 1 + :on-press (fn [] + (rf/dispatch [:chat.ui/zoom-out-signal @scroll-index]) + (reset! scroll-index-lock? true) + (js/setTimeout #(reset! scroll-index-lock? false) 500) + (js/setTimeout + (fn [] + (reset! scroll-index index) + (.scrollToIndex ^js @small-list-ref + #js {:animated true :index index}) + (.scrollToIndex ^js @flat-list-ref + #js {:animated true :index index})) + (if platform/ios? 50 150)) + (rf/dispatch [:chat.ui/update-shared-element-id (:message-id item)]))} + [reanimated/fast-image + {:source {:uri (:image (:content item))} + :style (reanimated/apply-animations-to-style {:width size-value + :height size-value} + {:border-radius 10})}]])) + (defn small-image - [item index _ {:keys [scroll-index atoms]}] - [:f> - (fn [] - (let [size (if (= @scroll-index index) c/focused-image-size c/small-image-size) - size-value (anim/use-val size) - {:keys [scroll-index-lock? small-list-ref - flat-list-ref]} atoms] - (anim/animate size-value size) - [rn/touchable-opacity - {:active-opacity 1 - :on-press (fn [] - (rf/dispatch [:chat.ui/zoom-out-signal @scroll-index]) - (reset! scroll-index-lock? true) - (js/setTimeout #(reset! scroll-index-lock? false) 500) - (js/setTimeout - (fn [] - (reset! scroll-index index) - (.scrollToIndex ^js @small-list-ref - #js {:animated true :index index}) - (.scrollToIndex ^js @flat-list-ref - #js {:animated true :index index})) - (if platform/ios? 50 150)) - (rf/dispatch [:chat.ui/update-shared-element-id (:message-id item)]))} - [reanimated/fast-image - {:source {:uri (:image (:content item))} - :style (reanimated/apply-animations-to-style {:width size-value - :height size-value} - {:border-radius 10})}]]))]) + [item index _ render-data] + [:f> f-small-image item index _ render-data]) (defn bottom-view - [messages index scroll-index insets animations derived item-width atoms] - [:f> - (fn [] - (let [text (get-in (first messages) [:content :text]) - padding-horizontal (- (/ item-width 2) (/ c/focused-image-size 2))] - [reanimated/linear-gradient - {:colors [:black :transparent] - :start {:x 0 :y 1} - :end {:x 0 :y 0} - :style (style/gradient-container insets animations derived)} - [rn/text {:style style/text-style} text] - [rn/flat-list - {:ref #(reset! (:small-list-ref atoms) %) - :key-fn :message-id - :style {:height c/small-list-height} - :data messages - :render-fn small-image - :render-data {:scroll-index scroll-index - :atoms atoms} - :horizontal true - :shows-horizontal-scroll-indicator false - :get-item-layout get-small-item-layout - :separator [rn/view {:style {:width 8}}] - :initial-scroll-index index - :content-container-style (style/content-container padding-horizontal)}]]))]) - + [messages index scroll-index insets animations derived item-width props] + (let [text (get-in (first messages) [:content :text]) + padding-horizontal (- (/ item-width 2) (/ c/focused-image-size 2))] + [reanimated/linear-gradient + {:colors [:black :transparent] + :start {:x 0 :y 1} + :end {:x 0 :y 0} + :style (style/gradient-container insets animations derived)} + [rn/text {:style style/text-style} text] + [rn/flat-list + {:ref #(reset! (:small-list-ref props) %) + :key-fn :message-id + :style {:height c/small-list-height} + :data messages + :render-fn small-image + :render-data {:scroll-index scroll-index + :props props} + :horizontal true + :shows-horizontal-scroll-indicator false + :get-item-layout get-small-item-layout + :separator [rn/view {:style {:width 8}}] + :initial-scroll-index index + :content-container-style (style/content-container padding-horizontal)}]])) diff --git a/src/status_im2/contexts/chat/lightbox/constants.cljs b/src/status_im2/contexts/chat/lightbox/constants.cljs index 4bc85f7f93..f7b0072cd6 100644 --- a/src/status_im2/contexts/chat/lightbox/constants.cljs +++ b/src/status_im2/contexts/chat/lightbox/constants.cljs @@ -11,3 +11,7 @@ (def ^:const small-list-padding-vertical 12) (def ^:const top-view-height 56) + +(def ^:const separator-width 16) + +(def ^:const drag-threshold 100) diff --git a/src/status_im2/contexts/chat/lightbox/style.cljs b/src/status_im2/contexts/chat/lightbox/style.cljs index eda442c51c..f2f6e47309 100644 --- a/src/status_im2/contexts/chat/lightbox/style.cljs +++ b/src/status_im2/contexts/chat/lightbox/style.cljs @@ -67,9 +67,7 @@ :opacity opacity} {:position :absolute :bottom 0 - :padding-bottom (if platform/ios? - (:bottom insets) - (+ (:bottom insets) c/small-list-padding-vertical c/focused-extra-size)) + :padding-bottom (:bottom insets) :z-index 3})) (defn content-container diff --git a/src/status_im2/contexts/chat/lightbox/top_view.cljs b/src/status_im2/contexts/chat/lightbox/top_view.cljs index ad95d7277d..d95612d3d9 100644 --- a/src/status_im2/contexts/chat/lightbox/top_view.cljs +++ b/src/status_im2/contexts/chat/lightbox/top_view.cljs @@ -41,49 +41,47 @@ (defn top-view [{:keys [from timestamp]} insets index animations derived landscape? screen-width] - [:f> - (fn [] - (let [display-name (first (rf/sub [:contacts/contact-two-names-by-identity - from])) - bg-color (if landscape? - colors/neutral-100-opa-70 - colors/neutral-100-opa-0) - {:keys [background-color opacity]} animations] - [reanimated/view - {:style - (style/top-view-container (:top insets) screen-width bg-color landscape? animations derived)} - [reanimated/linear-gradient - {:colors [(colors/alpha "#000000" 0.8) :transparent] - :start {:x 0 :y 0} - :end {:x 0 :y 1} - :style (style/top-gradient insets)}] - [rn/view - {:style {:flex-direction :row - :align-items :center}} - [rn/touchable-opacity - {:on-press (fn [] - (anim/animate background-color :transparent) - (anim/animate opacity 0) - (rf/dispatch (if platform/ios? - [:chat.ui/exit-lightbox-signal @index] - [:navigate-back]))) - :style style/close-container} - [quo/icon :close {:size 20 :color colors/white}]] - [rn/view {:style {:margin-left 12}} - [quo/text - {:weight :semi-bold - :size :paragraph-1 - :style {:color colors/white}} display-name] - [quo/text - {:weight :medium - :size :paragraph-2 - :style {:color colors/neutral-40}} (datetime/to-short-str timestamp)]]] - [rn/view {:style style/top-right-buttons} - [rn/touchable-opacity - {:active-opacity 1 - :style (merge style/close-container {:margin-right 12})} - [quo/icon :share {:size 20 :color colors/white}]] - [rn/touchable-opacity - {:active-opacity 1 - :style style/close-container} - [quo/icon :options {:size 20 :color colors/white}]]]]))]) + (let [display-name (first (rf/sub [:contacts/contact-two-names-by-identity + from])) + bg-color (if landscape? + colors/neutral-100-opa-70 + colors/neutral-100-opa-0) + {:keys [background-color opacity]} animations] + [reanimated/view + {:style + (style/top-view-container (:top insets) screen-width bg-color landscape? animations derived)} + [reanimated/linear-gradient + {:colors [(colors/alpha "#000000" 0.8) :transparent] + :start {:x 0 :y 0} + :end {:x 0 :y 1} + :style (style/top-gradient insets)}] + [rn/view + {:style {:flex-direction :row + :align-items :center}} + [rn/touchable-opacity + {:on-press (fn [] + (anim/animate background-color :transparent) + (anim/animate opacity 0) + (rf/dispatch (if platform/ios? + [:chat.ui/exit-lightbox-signal @index] + [:navigate-back]))) + :style style/close-container} + [quo/icon :close {:size 20 :color colors/white}]] + [rn/view {:style {:margin-left 12}} + [quo/text + {:weight :semi-bold + :size :paragraph-1 + :style {:color colors/white}} display-name] + [quo/text + {:weight :medium + :size :paragraph-2 + :style {:color colors/neutral-40}} (datetime/to-short-str timestamp)]]] + [rn/view {:style style/top-right-buttons} + [rn/touchable-opacity + {:active-opacity 1 + :style (merge style/close-container {:margin-right 12})} + [quo/icon :share {:size 20 :color colors/white}]] + [rn/touchable-opacity + {:active-opacity 1 + :style style/close-container} + [quo/icon :options {:size 20 :color colors/white}]]]])) diff --git a/src/status_im2/contexts/chat/lightbox/utils.cljs b/src/status_im2/contexts/chat/lightbox/utils.cljs new file mode 100644 index 0000000000..5f56bb0ce5 --- /dev/null +++ b/src/status_im2/contexts/chat/lightbox/utils.cljs @@ -0,0 +1,166 @@ +(ns status-im2.contexts.chat.lightbox.utils + (:require + [clojure.string :as string] + [quo2.foundations.colors :as colors] + [react-native.core :as rn] + [react-native.gesture :as gesture] + [react-native.navigation :as navigation] + [react-native.orientation :as orientation] + [react-native.platform :as platform] + [react-native.reanimated :as reanimated] + [react-native.safe-area :as safe-area] + [reagent.core :as reagent] + [status-im2.contexts.chat.lightbox.animations :as anim] + [status-im2.contexts.chat.lightbox.top-view :as top-view] + [utils.re-frame :as rf] + [oops.core :refer [oget]] + [status-im2.contexts.chat.lightbox.constants :as constants] + [utils.worklets.lightbox :as worklet])) + + +(defn effect + [{:keys [flat-list-ref scroll-index-lock?]} {:keys [opacity layout border]} index] + (rn/use-effect (fn [] + (reagent/next-tick (fn [] + (when @flat-list-ref + (.scrollToIndex ^js @flat-list-ref + #js {:animated false :index index})))) + (js/setTimeout (fn [] + (anim/animate opacity 1) + (anim/animate layout 0) + (anim/animate border 12)) + (if platform/ios? 250 100)) + (js/setTimeout #(reset! scroll-index-lock? false) 300) + (fn [] + (rf/dispatch [:chat.ui/zoom-out-signal nil]) + (when platform/android? + (rf/dispatch [:chat.ui/lightbox-scale 1])))))) + +(defn handle-orientation + [result {:keys [flat-list-ref]} {:keys [scroll-index]} animations] + (let [insets (safe-area/get-insets) + window (rn/get-window) + window-width (:width window) + window-height (:height window) + window-height (if platform/android? + (+ window-height (:top insets)) + window-height) + screen-width (if (or platform/ios? (= result orientation/portrait)) + window-width + window-height) + screen-height (if (or platform/ios? (= result orientation/portrait)) + window-height + window-width) + landscape? (string/includes? result orientation/landscape) + item-width (if (and landscape? platform/ios?) screen-height screen-width)] + (when (or landscape? (= result orientation/portrait)) + (rf/dispatch [:chat.ui/orientation-change result])) + (cond + landscape? + (orientation/lock-to-landscape "lightbox") + (= result orientation/portrait) + (orientation/lock-to-portrait "lightbox")) + (js/setTimeout + (fn [] + (when @flat-list-ref + (.scrollToOffset + ^js @flat-list-ref + #js {:animated false :offset (* (+ item-width constants/separator-width) @scroll-index)}))) + 100) + (when platform/ios? + (top-view/animate-rotation result screen-width screen-height insets animations)))) + +(defn orientation-change + [props state animations] + (orientation/use-device-orientation-change + (fn [result] + (if platform/ios? + (handle-orientation result props state animations) + ;; `use-device-orientation-change` will always be called on Android, so need to check + (orientation/get-auto-rotate-state + (fn [enabled?] + ;; RNN does not support landscape-right + (when (and enabled? (not= result orientation/landscape-right)) + (handle-orientation result props state animations)))))))) + +(defn toggle-opacity + [index {:keys [opacity-value border-value transparent? props]} portrait?] + (let [{:keys [small-list-ref]} props + opacity (reanimated/get-shared-value opacity-value)] + (if (= opacity 1) + (do + (when platform/ios? + ;; status-bar issue: https://github.com/status-im/status-mobile/issues/15343 + (js/setTimeout #(navigation/merge-options "lightbox" {:statusBar {:visible false}}) 75)) + (anim/animate opacity-value 0) + (js/setTimeout #(reset! transparent? (not @transparent?)) 400)) + (do + (reset! transparent? (not @transparent?)) + (js/setTimeout #(anim/animate opacity-value 1) 50) + (js/setTimeout #(when @small-list-ref + (.scrollToIndex ^js @small-list-ref #js {:animated false :index index})) + 100) + (when (and platform/ios? portrait?) + (js/setTimeout #(navigation/merge-options "lightbox" {:statusBar {:visible true}}) 150)))) + (anim/animate border-value (if (= opacity 1) 0 12)))) + +(defn drag-gesture + [{:keys [pan-x pan-y background-color opacity layout]} x? set-full-height?] + (-> + (gesture/gesture-pan) + (gesture/enabled true) + (gesture/max-pointers 1) + (gesture/on-start #(reset! set-full-height? false)) + (gesture/on-update (fn [e] + (let [translation (if x? (oget e "translationX") (oget e "translationY")) + progress (Math/abs (/ translation constants/drag-threshold))] + (anim/set-val (if x? pan-x pan-y) translation) + (anim/set-val opacity (- 1 progress)) + (anim/set-val layout (* progress -20))))) + (gesture/on-end (fn [e] + (if (> (Math/abs (if x? (oget e "translationX") (oget e "translationY"))) + constants/drag-threshold) + (do + (anim/animate background-color "rgba(0,0,0,0)") + (anim/animate opacity 0) + (rf/dispatch [:navigate-back])) + (do + #(reset! set-full-height? true) + (anim/animate (if x? pan-x pan-y) 0) + (anim/animate opacity 1) + (anim/animate layout 0))))))) + +(defn init-props + [] + {:flat-list-ref (atom nil) + :small-list-ref (atom nil) + :scroll-index-lock? (atom true)}) + +(defn init-state + [messages index] + ;; The initial value of data is the image that was pressed (and not the whole album) in order + ;; for the transition animation to execute properly, otherwise it would animate towards + ;; outside the screen (even if we have `initialScrollIndex` set). + {:data (reagent/atom (if (number? index) [(nth messages index)] [])) + :scroll-index (reagent/atom index) + :transparent? (reagent/atom false) + :set-full-height? (reagent/atom false)}) + +(defn init-animations + [] + {:background-color (anim/use-val colors/black-opa-0) + :border (anim/use-val (if platform/ios? 0 12)) + :opacity (anim/use-val 0) + :rotate (anim/use-val "0deg") + :layout (anim/use-val -10) + :top-view-y (anim/use-val 0) + :top-view-x (anim/use-val 0) + :top-view-width (anim/use-val (:width (rn/get-window))) + :top-view-bg (anim/use-val colors/neutral-100-opa-0) + :pan-y (anim/use-val 0) + :pan-x (anim/use-val 0)}) + +(defn init-derived-animations + [{:keys [layout]}] + {:top-layout (worklet/info-layout layout true) + :bottom-layout (worklet/info-layout layout false)}) diff --git a/src/status_im2/contexts/chat/lightbox/view.cljs b/src/status_im2/contexts/chat/lightbox/view.cljs index 8c64224f4b..6d12bdd0ea 100644 --- a/src/status_im2/contexts/chat/lightbox/view.cljs +++ b/src/status_im2/contexts/chat/lightbox/view.cljs @@ -1,80 +1,31 @@ (ns status-im2.contexts.chat.lightbox.view (:require [clojure.string :as string] - [quo2.foundations.colors :as colors] [react-native.core :as rn] - [react-native.navigation :as navigation] [react-native.orientation :as orientation] [react-native.platform :as platform] [react-native.reanimated :as reanimated] + [react-native.safe-area :as safe-area] [status-im2.contexts.chat.lightbox.animations :as anim] [status-im2.contexts.chat.lightbox.style :as style] [utils.re-frame :as rf] - [reagent.core :as reagent] [react-native.gesture :as gesture] [status-im2.contexts.chat.lightbox.zoomable-image.view :as zoomable-image] [status-im2.contexts.chat.lightbox.top-view :as top-view] [status-im2.contexts.chat.lightbox.bottom-view :as bottom-view] - [utils.worklets.lightbox :as worklet] - [oops.core :refer [oget]])) - -(def ^:const seperator-width 16) - -(def ^:const drag-threshold 100) - -(defn toggle-opacity - [index {:keys [opacity-value border-value transparent? atoms]} portrait?] - (let [{:keys [small-list-ref]} atoms - opacity (reanimated/get-shared-value opacity-value)] - (if (= opacity 1) - (do - (when platform/ios? - ;; status-bar issue: https://github.com/status-im/status-mobile/issues/15343 - (js/setTimeout #(navigation/merge-options "lightbox" {:statusBar {:visible false}}) 75)) - (anim/animate opacity-value 0) - (js/setTimeout #(reset! transparent? (not @transparent?)) 400)) - (do - (reset! transparent? (not @transparent?)) - (js/setTimeout #(anim/animate opacity-value 1) 50) - (js/setTimeout #(when @small-list-ref - (.scrollToIndex ^js @small-list-ref #js {:animated false :index index})) - 100) - (when (and platform/ios? portrait?) - (js/setTimeout #(navigation/merge-options "lightbox" {:statusBar {:visible true}}) 150)))) - (anim/animate border-value (if (= opacity 1) 0 12)))) - -(defn handle-orientation - [result index window-width window-height animations insets {:keys [flat-list-ref]}] - (let [screen-width (if (or platform/ios? (= result orientation/portrait)) - window-width - window-height) - screen-height (if (or platform/ios? (= result orientation/portrait)) - window-height - window-width) - landscape? (string/includes? result orientation/landscape) - item-width (if (and landscape? platform/ios?) screen-height screen-width) - timeout (if platform/ios? 50 100)] - (when (or landscape? (= result orientation/portrait)) - (rf/dispatch [:chat.ui/orientation-change result])) - (cond - landscape? - (orientation/lock-to-landscape "lightbox") - (= result orientation/portrait) - (orientation/lock-to-portrait "lightbox")) - (js/setTimeout #(when @flat-list-ref - (.scrollToOffset - ^js @flat-list-ref - #js {:animated false :offset (* (+ item-width seperator-width) @index)})) - timeout) - (when platform/ios? - (top-view/animate-rotation result screen-width screen-height insets animations)))) + [oops.core :refer [oget]] + [status-im2.contexts.chat.lightbox.utils :as utils] + [status-im2.contexts.chat.lightbox.constants :as constants])) (defn get-item-layout [_ index item-width] - #js {:length item-width :offset (* (+ item-width seperator-width) index) :index index}) + #js + {:length item-width + :offset (* (+ item-width constants/separator-width) index) + :index index}) (defn on-viewable-items-changed - [e scroll-index {:keys [scroll-index-lock? small-list-ref]}] + [e {:keys [scroll-index-lock? small-list-ref]} {:keys [scroll-index]}] (when-not @scroll-index-lock? (let [changed (-> e (oget :changed) first) index (oget changed :index)] @@ -86,160 +37,88 @@ (defn image [message index _ {:keys [screen-width screen-height] :as args}] [rn/view - {:style (style/image (+ screen-width seperator-width) screen-height)} + {:style (style/image (+ screen-width constants/separator-width) screen-height)} [zoomable-image/zoomable-image message index args - #(toggle-opacity index args %)] - [rn/view {:style {:width seperator-width}}]]) + #(utils/toggle-opacity index args %)] + [rn/view {:style {:width constants/separator-width}}]]) -(defn drag-gesture - [{:keys [pan-x pan-y background-color opacity layout]} x? set-full-height?] - (-> - (gesture/gesture-pan) - (gesture/enabled true) - (gesture/max-pointers 1) - (gesture/on-start #(reset! set-full-height? false)) - (gesture/on-update (fn [e] - (let [translation (if x? (oget e "translationX") (oget e "translationY")) - progress (Math/abs (/ translation drag-threshold))] - (anim/set-val (if x? pan-x pan-y) translation) - (anim/set-val opacity (- 1 progress)) - (anim/set-val layout (* progress -20))))) - (gesture/on-end (fn [e] - (if (> (Math/abs (if x? (oget e "translationX") (oget e "translationY"))) - drag-threshold) - (do - (anim/animate background-color "rgba(0,0,0,0)") - (anim/animate opacity 0) - (rf/dispatch [:navigate-back])) - (do - #(reset! set-full-height? true) - (anim/animate (if x? pan-x pan-y) 0) - (anim/animate opacity 1) - (anim/animate layout 0))))))) +(defn lightbox-content + [props {:keys [data transparent? scroll-index set-full-height?]} animations derived messages index + callback] + (let [insets (safe-area/get-insets) + window (rn/get-window) + window-width (:width window) + window-height (if platform/android? + (+ (:height window) (:top insets)) + (:height window)) + curr-orientation (or (rf/sub [:lightbox/orientation]) orientation/portrait) + landscape? (string/includes? curr-orientation orientation/landscape) + horizontal? (or platform/android? (not landscape?)) + inverted? (and platform/ios? (= curr-orientation orientation/landscape-right)) + screen-width (if (or platform/ios? (= curr-orientation orientation/portrait)) + window-width + window-height) + screen-height (if (or platform/ios? (= curr-orientation orientation/portrait)) + window-height + window-width) + item-width (if (and landscape? platform/ios?) screen-height screen-width)] + [reanimated/view + {:style (reanimated/apply-animations-to-style {:background-color (:background-color animations)} + {:height screen-height})} + (when-not @transparent? + [:f> top-view/top-view (first messages) insets scroll-index animations derived landscape? + screen-width]) + [gesture/gesture-detector + {:gesture (utils/drag-gesture animations (and landscape? platform/ios?) set-full-height?)} + [reanimated/view + {:style (reanimated/apply-animations-to-style + {:transform [{:translateY (:pan-y animations)} + {:translateX (:pan-x animations)}]} + {})} + [gesture/flat-list + {:ref #(reset! (:flat-list-ref props) %) + :key-fn :message-id + :style {:width (+ screen-width constants/separator-width)} + :data @data + :render-fn image + :render-data {:opacity-value (:opacity animations) + :border-value (:border animations) + :transparent? transparent? + :set-full-height? set-full-height? + :screen-height screen-height + :screen-width screen-width + :window-height window-height + :window-width window-width + :props props} + :horizontal horizontal? + :inverted inverted? + :paging-enabled true + :get-item-layout (fn [_ index] (get-item-layout _ index item-width)) + :viewability-config {:view-area-coverage-percent-threshold 50 + :wait-for-interaction true} + :shows-vertical-scroll-indicator false + :shows-horizontal-scroll-indicator false + :on-viewable-items-changed callback}]]] + (when (and (not @transparent?) (not landscape?)) + [:f> bottom-view/bottom-view messages index scroll-index insets animations derived + item-width props])])) + +(defn- f-lightbox + [{:keys [messages index]}] + (let [props (utils/init-props) + state (utils/init-state messages index)] + (fn [{:keys [messages index]}] + (let [animations (utils/init-animations) + derived (utils/init-derived-animations animations) + callback (fn [e] + (on-viewable-items-changed e props state))] + (anim/animate (:background-color animations) "rgba(0,0,0,1)") + (reset! (:data state) messages) + (utils/orientation-change props state animations) + (utils/effect props animations index) + [:f> lightbox-content props state animations derived messages index callback])))) (defn lightbox [] - [:f> - (fn [] - ;; we get `insets` from `screen-params` because trying to consume it from - ;; lightbox screen causes lots of problems - (let [{:keys [messages index insets]} (rf/sub [:get-screen-params]) - atoms {:flat-list-ref (atom nil) - :small-list-ref (atom nil) - :scroll-index-lock? (atom true)} - ;; The initial value of data is the image that was pressed (and not the whole album) in order - ;; for the transition animation to execute properly, otherwise it would animate towards - ;; outside the screen (even if we have `initialScrollIndex` set). - data (reagent/atom (if (number? index) [(nth messages index)] [])) - scroll-index (reagent/atom index) - transparent? (reagent/atom false) - set-full-height? (reagent/atom false) - window (rf/sub [:dimensions/window]) - window-width (:width window) - window-height (:height window) - window-height (if platform/android? - (+ window-height (:top insets)) - window-height) - animations {:background-color (anim/use-val "rgba(0,0,0,0)") - :border (anim/use-val (if platform/ios? 0 12)) - :opacity (anim/use-val 0) - :rotate (anim/use-val "0deg") - :layout (anim/use-val -10) - :top-view-y (anim/use-val 0) - :top-view-x (anim/use-val 0) - :top-view-width (anim/use-val window-width) - :top-view-bg (anim/use-val colors/neutral-100-opa-0) - :pan-y (anim/use-val 0) - :pan-x (anim/use-val 0)} - derived {:top-layout (worklet/info-layout (:layout animations) - true) - :bottom-layout (worklet/info-layout (:layout animations) - false)} - callback (fn [e] - (on-viewable-items-changed e scroll-index atoms))] - (anim/animate (:background-color animations) "rgba(0,0,0,1)") - (reset! data messages) - (orientation/use-device-orientation-change - (fn [result] - (if platform/ios? - (handle-orientation result scroll-index window-width window-height animations insets atoms) - ;; `use-device-orientation-change` will always be called on Android, so need to check - (orientation/get-auto-rotate-state - (fn [enabled?] - ;; RNN does not support landscape-right - (when (and enabled? (not= result orientation/landscape-right)) - (handle-orientation result - scroll-index - window-width - window-height - animations - insets - atoms))))))) - (rn/use-effect (fn [] - (when @(:flat-list-ref atoms) - (.scrollToIndex ^js @(:flat-list-ref atoms) - #js {:animated false :index index})) - (js/setTimeout (fn [] - (anim/animate (:opacity animations) 1) - (anim/animate (:layout animations) 0) - (anim/animate (:border animations) 12)) - (if platform/ios? 250 100)) - (js/setTimeout #(reset! (:scroll-index-lock? atoms) false) 300) - (fn [] - (rf/dispatch [:chat.ui/zoom-out-signal nil]) - (when platform/android? - (rf/dispatch [:chat.ui/lightbox-scale 1]))))) - [:f> - (fn [] - (let [curr-orientation (or (rf/sub [:lightbox/orientation]) orientation/portrait) - landscape? (string/includes? curr-orientation orientation/landscape) - horizontal? (or platform/android? (not landscape?)) - inverted? (and platform/ios? (= curr-orientation orientation/landscape-right)) - screen-width (if (or platform/ios? (= curr-orientation orientation/portrait)) - window-width - window-height) - screen-height (if (or platform/ios? (= curr-orientation orientation/portrait)) - window-height - window-width) - item-width (if (and landscape? platform/ios?) screen-height screen-width)] - [reanimated/view - {:style (reanimated/apply-animations-to-style {:background-color (:background-color - animations)} - {:height screen-height})} - (when-not @transparent? - [top-view/top-view (first messages) insets scroll-index animations derived landscape? - screen-width]) - [gesture/gesture-detector - {:gesture (drag-gesture animations (and landscape? platform/ios?) set-full-height?)} - [reanimated/view - {:style (reanimated/apply-animations-to-style - {:transform [{:translateY (:pan-y animations)} - {:translateX (:pan-x animations)}]} - {})} - [gesture/flat-list - {:ref #(reset! (:flat-list-ref atoms) %) - :key-fn :message-id - :style {:width (+ screen-width seperator-width)} - :data @data - :render-fn image - :render-data {:opacity-value (:opacity animations) - :border-value (:border animations) - :transparent? transparent? - :set-full-height? set-full-height? - :screen-height screen-height - :screen-width screen-width - :window-height window-height - :window-width window-width - :atoms atoms} - :horizontal horizontal? - :inverted inverted? - :paging-enabled true - :get-item-layout (fn [_ index] (get-item-layout _ index item-width)) - :viewability-config {:view-area-coverage-percent-threshold 50 - :wait-for-interaction true} - :shows-vertical-scroll-indicator false - :shows-horizontal-scroll-indicator false - :on-viewable-items-changed callback}]]] - (when (and (not @transparent?) (not landscape?)) - [bottom-view/bottom-view messages index scroll-index insets animations derived - item-width atoms])]))]))]) + (let [screen-params (rf/sub [:get-screen-params])] + [:f> f-lightbox screen-params])) diff --git a/src/status_im2/contexts/chat/messages/content/album/view.cljs b/src/status_im2/contexts/chat/messages/content/album/view.cljs index 269004b40c..997a96a301 100644 --- a/src/status_im2/contexts/chat/messages/content/album/view.cljs +++ b/src/status_im2/contexts/chat/messages/content/album/view.cljs @@ -3,7 +3,6 @@ [quo2.foundations.colors :as colors] [react-native.core :as rn] [react-native.fast-image :as fast-image] - [react-native.safe-area :as safe-area] [status-im2.contexts.chat.messages.content.album.style :as style] [status-im2.constants :as constants] [status-im2.contexts.chat.messages.content.image.view :as image] @@ -20,8 +19,7 @@ (defn album-message [{:keys [albumize?] :as message} context on-long-press] - (let [insets (safe-area/get-insets) - shared-element-id (rf/sub [:shared-element-id]) + (let [shared-element-id (rf/sub [:shared-element-id]) first-image (first (:album message)) album-style (if (> (:image-width first-image) (:image-height first-image)) :landscape @@ -53,8 +51,7 @@ :on-press #(rf/dispatch [:chat.ui/navigate-to-lightbox (:message-id item) {:messages (:album message) - :index index - :insets insets}])} + :index index}])} [fast-image/fast-image {:style (style/image dimensions index portrait? images-count) :source {:uri (:image (:content item))}