From 2b701f9af0c884f08b02c513751990c7e63c02d2 Mon Sep 17 00:00:00 2001 From: Omar Basem Date: Wed, 31 May 2023 10:01:35 +0400 Subject: [PATCH] refactor: zoomable component (#16022) refactor: zoomable comp --- .../contexts/chat/lightbox/utils.cljs | 23 --- .../contexts/chat/lightbox/view.cljs | 3 +- .../chat/lightbox/zoomable_image/utils.cljs | 53 ++++++- .../chat/lightbox/zoomable_image/view.cljs | 134 ++++++++---------- 4 files changed, 111 insertions(+), 102 deletions(-) diff --git a/src/status_im2/contexts/chat/lightbox/utils.cljs b/src/status_im2/contexts/chat/lightbox/utils.cljs index 5f56bb0ce5..7a96899851 100644 --- a/src/status_im2/contexts/chat/lightbox/utils.cljs +++ b/src/status_im2/contexts/chat/lightbox/utils.cljs @@ -4,10 +4,8 @@ [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] @@ -83,27 +81,6 @@ (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?] (-> diff --git a/src/status_im2/contexts/chat/lightbox/view.cljs b/src/status_im2/contexts/chat/lightbox/view.cljs index 6d12bdd0ea..f311b91490 100644 --- a/src/status_im2/contexts/chat/lightbox/view.cljs +++ b/src/status_im2/contexts/chat/lightbox/view.cljs @@ -38,8 +38,7 @@ [message index _ {:keys [screen-width screen-height] :as args}] [rn/view {:style (style/image (+ screen-width constants/separator-width) screen-height)} - [zoomable-image/zoomable-image message index args - #(utils/toggle-opacity index args %)] + [:f> zoomable-image/zoomable-image message index args] [rn/view {:style {:width constants/separator-width}}]]) (defn lightbox-content diff --git a/src/status_im2/contexts/chat/lightbox/zoomable_image/utils.cljs b/src/status_im2/contexts/chat/lightbox/zoomable_image/utils.cljs index c1ad2bb284..5077a43e3b 100644 --- a/src/status_im2/contexts/chat/lightbox/zoomable_image/utils.cljs +++ b/src/status_im2/contexts/chat/lightbox/zoomable_image/utils.cljs @@ -1,8 +1,11 @@ (ns status-im2.contexts.chat.lightbox.zoomable-image.utils (:require [clojure.string :as string] + [react-native.navigation :as navigation] [react-native.orientation :as orientation] [react-native.platform :as platform] + [react-native.reanimated :as reanimated] + [reagent.core :as reagent] [status-im2.contexts.chat.lightbox.zoomable-image.constants :as c] [status-im2.contexts.chat.lightbox.animations :as anim] [utils.re-frame :as rf])) @@ -36,9 +39,9 @@ exit? {:keys [x-threshold-scale y-threshold-scale]} {:keys [scale saved-scale] :as animations} - {:keys [pan-x-enabled? pan-y-enabled?] :as props}] + {:keys [pan-x-enabled? pan-y-enabled?] :as state}] (when (= value c/min-scale) - (reset-values exit? animations props)) + (reset-values exit? animations state)) (anim/animate scale value (if exit? 100 c/default-duration)) (anim/set-val saved-scale value) (reset! pan-x-enabled? (> value x-threshold-scale)) @@ -102,6 +105,27 @@ (when (and (= zoom-out-signal index) (> scale c/min-scale)) (rescale c/min-scale true))) +(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)))) + ;;; Dimensions (defn get-dimensions "Calculates all required dimensions. Dimensions calculations are different on iOS and Android because landscape @@ -162,3 +186,28 @@ (if (or (> focal max) (< focal min)) (/ screen-size 2) focal))) + +;;; INITIALIZATIONS +(defn init-animations + [] + {:scale (anim/use-val c/min-scale) + :saved-scale (anim/use-val c/min-scale) + :pan-x-start (anim/use-val c/init-offset) + :pan-x (anim/use-val c/init-offset) + :pan-y-start (anim/use-val c/init-offset) + :pan-y (anim/use-val c/init-offset) + :pinch-x-start (anim/use-val c/init-offset) + :pinch-x (anim/use-val c/init-offset) + :pinch-y-start (anim/use-val c/init-offset) + :pinch-y (anim/use-val c/init-offset) + :pinch-x-max (anim/use-val js/Infinity) + :pinch-y-max (anim/use-val js/Infinity) + :rotate (anim/use-val c/init-rotation) + :rotate-scale (anim/use-val c/min-scale)}) + +(defn init-state + [] + {:pan-x-enabled? (reagent/atom false) + :pan-y-enabled? (reagent/atom false) + :focal-x (reagent/atom nil) + :focal-y (reagent/atom nil)}) diff --git a/src/status_im2/contexts/chat/lightbox/zoomable_image/view.cljs b/src/status_im2/contexts/chat/lightbox/zoomable_image/view.cljs index c8e3d56af1..c6abced6f7 100644 --- a/src/status_im2/contexts/chat/lightbox/zoomable_image/view.cljs +++ b/src/status_im2/contexts/chat/lightbox/zoomable_image/view.cljs @@ -4,7 +4,6 @@ [react-native.gesture :as gesture] [react-native.platform :as platform] [react-native.reanimated :as reanimated] - [reagent.core :as reagent] [utils.re-frame :as rf] [oops.core :refer [oget]] [react-native.orientation :as orientation] @@ -80,7 +79,7 @@ [{:keys [width height screen-height screen-width x-threshold-scale y-threshold-scale] :as dimensions} {:keys [saved-scale scale pinch-x pinch-y pinch-x-start pinch-y-start pinch-x-max pinch-y-max] :as animations} - {:keys [focal-x focal-y] :as props} + {:keys [focal-x focal-y] :as state} rescale transparent? toggle-opacity] @@ -135,7 +134,7 @@ (utils/center-x animations false)) (when (< (anim/get-val scale) y-threshold-scale) (utils/center-y animations false)))) - (finalize-pinch dimensions animations props))))) + (finalize-pinch dimensions animations state))))) (defn pan-x-gesture [{:keys [width screen-width x-threshold-scale]} @@ -204,76 +203,61 @@ (anim/animate-decay pan-y velocity [lower-bound upper-bound]) (anim/animate-decay pan-y-start velocity [lower-bound upper-bound])))))))) +(defn- f-zoomable-image + [dimensions animations state rescale curr-orientation content focused? index render-data] + (let [{:keys [transparent? set-full-height?]} render-data + portrait? (= curr-orientation orientation/portrait) + on-tap #(utils/toggle-opacity index render-data portrait?) + tap (tap-gesture on-tap) + double-tap (double-tap-gesture dimensions animations rescale transparent? on-tap) + pinch (pinch-gesture dimensions animations state rescale transparent? on-tap) + pan-x (pan-x-gesture dimensions animations state rescale) + pan-y (pan-y-gesture dimensions animations state rescale) + composed-gestures (gesture/exclusive + (gesture/simultaneous pinch pan-x pan-y) + (gesture/exclusive double-tap tap))] + [gesture/gesture-detector {:gesture composed-gestures} + [reanimated/view + {:style (style/container dimensions + animations + @set-full-height? + (= curr-orientation orientation/portrait))} + [reanimated/fast-image + {:source {:uri (:image content)} + :native-ID (when focused? :shared-element) + :style (style/image dimensions animations (:border-value render-data))}]]])) + (defn zoomable-image - [{:keys [image-width image-height content message-id]} index args on-tap] - [:f> - (fn [] - (let [{:keys [transparent? set-full-height?]} args - shared-element-id (rf/sub [:shared-element-id]) - exit-lightbox-signal (rf/sub [:lightbox/exit-signal]) - zoom-out-signal (rf/sub [:lightbox/zoom-out-signal]) - focused? (= shared-element-id message-id) - curr-orientation (or (rf/sub [:lightbox/orientation]) - orientation/portrait) - portrait? (= curr-orientation orientation/portrait) - dimensions (utils/get-dimensions - (or image-width c/default-dimension) - (or image-height c/default-duration) - curr-orientation - args) - animations {:scale (anim/use-val c/min-scale) - :saved-scale (anim/use-val c/min-scale) - :pan-x-start (anim/use-val c/init-offset) - :pan-x (anim/use-val c/init-offset) - :pan-y-start (anim/use-val c/init-offset) - :pan-y (anim/use-val c/init-offset) - :pinch-x-start (anim/use-val c/init-offset) - :pinch-x (anim/use-val c/init-offset) - :pinch-y-start (anim/use-val c/init-offset) - :pinch-y (anim/use-val c/init-offset) - :pinch-x-max (anim/use-val js/Infinity) - :pinch-y-max (anim/use-val js/Infinity) - :rotate (anim/use-val c/init-rotation) - :rotate-scale (anim/use-val c/min-scale)} - props {:pan-x-enabled? (reagent/atom false) - :pan-y-enabled? (reagent/atom false) - :focal-x (reagent/atom nil) - :focal-y (reagent/atom nil)} - rescale (fn [value exit?] - (utils/rescale-image value - exit? - dimensions - animations - props))] - (rn/use-effect (fn [] - (js/setTimeout #(reset! set-full-height? true) 500))) - (when platform/ios? - (utils/handle-orientation-change curr-orientation focused? dimensions animations props) - (utils/handle-exit-lightbox-signal exit-lightbox-signal - index - (anim/get-val (:scale animations)) - rescale - set-full-height?)) - (utils/handle-zoom-out-signal zoom-out-signal index (anim/get-val (:scale animations)) rescale) - [:f> - (fn [] - (let [tap (tap-gesture #(on-tap portrait?)) - double-tap - (double-tap-gesture dimensions animations rescale transparent? #(on-tap portrait?)) - pinch - (pinch-gesture dimensions animations props rescale transparent? #(on-tap portrait?)) - pan-x (pan-x-gesture dimensions animations props rescale) - pan-y (pan-y-gesture dimensions animations props rescale) - composed-gestures (gesture/exclusive - (gesture/simultaneous pinch pan-x pan-y) - (gesture/exclusive double-tap tap))] - [gesture/gesture-detector {:gesture composed-gestures} - [reanimated/view - {:style (style/container dimensions - animations - @set-full-height? - (= curr-orientation orientation/portrait))} - [reanimated/fast-image - {:source {:uri (:image content)} - :native-ID (when focused? :shared-element) - :style (style/image dimensions animations (:border-value args))}]]]))]))]) + [{:keys [image-width image-height content message-id]} index render-data] + (let [state (utils/init-state) + shared-element-id (rf/sub [:shared-element-id]) + exit-lightbox-signal (rf/sub [:lightbox/exit-signal]) + zoom-out-signal (rf/sub [:lightbox/zoom-out-signal]) + curr-orientation (or (rf/sub [:lightbox/orientation]) + orientation/portrait) + {:keys [set-full-height?]} render-data + focused? (= shared-element-id message-id) + dimensions (utils/get-dimensions + (or image-width c/default-dimension) + (or image-height c/default-duration) + curr-orientation + render-data) + animations (utils/init-animations) + rescale (fn [value exit?] + (utils/rescale-image value + exit? + dimensions + animations + state))] + (rn/use-effect (fn [] + (js/setTimeout #(reset! set-full-height? true) 500))) + (when platform/ios? + (utils/handle-orientation-change curr-orientation focused? dimensions animations state) + (utils/handle-exit-lightbox-signal exit-lightbox-signal + index + (anim/get-val (:scale animations)) + rescale + set-full-height?)) + (utils/handle-zoom-out-signal zoom-out-signal index (anim/get-val (:scale animations)) rescale) + [:f> f-zoomable-image dimensions animations state rescale curr-orientation content focused? + index render-data]))