refactor: zoomable component (#16022)

refactor: zoomable comp
This commit is contained in:
Omar Basem 2023-05-31 10:01:35 +04:00 committed by GitHub
parent e8b956d4f4
commit 2b701f9af0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 111 additions and 102 deletions

View File

@ -4,10 +4,8 @@
[quo2.foundations.colors :as colors] [quo2.foundations.colors :as colors]
[react-native.core :as rn] [react-native.core :as rn]
[react-native.gesture :as gesture] [react-native.gesture :as gesture]
[react-native.navigation :as navigation]
[react-native.orientation :as orientation] [react-native.orientation :as orientation]
[react-native.platform :as platform] [react-native.platform :as platform]
[react-native.reanimated :as reanimated]
[react-native.safe-area :as safe-area] [react-native.safe-area :as safe-area]
[reagent.core :as reagent] [reagent.core :as reagent]
[status-im2.contexts.chat.lightbox.animations :as anim] [status-im2.contexts.chat.lightbox.animations :as anim]
@ -83,27 +81,6 @@
(when (and enabled? (not= result orientation/landscape-right)) (when (and enabled? (not= result orientation/landscape-right))
(handle-orientation result props state animations)))))))) (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 (defn drag-gesture
[{:keys [pan-x pan-y background-color opacity layout]} x? set-full-height?] [{:keys [pan-x pan-y background-color opacity layout]} x? set-full-height?]
(-> (->

View File

@ -38,8 +38,7 @@
[message index _ {:keys [screen-width screen-height] :as args}] [message index _ {:keys [screen-width screen-height] :as args}]
[rn/view [rn/view
{:style (style/image (+ screen-width constants/separator-width) screen-height)} {:style (style/image (+ screen-width constants/separator-width) screen-height)}
[zoomable-image/zoomable-image message index args [:f> zoomable-image/zoomable-image message index args]
#(utils/toggle-opacity index args %)]
[rn/view {:style {:width constants/separator-width}}]]) [rn/view {:style {:width constants/separator-width}}]])
(defn lightbox-content (defn lightbox-content

View File

@ -1,8 +1,11 @@
(ns status-im2.contexts.chat.lightbox.zoomable-image.utils (ns status-im2.contexts.chat.lightbox.zoomable-image.utils
(:require (:require
[clojure.string :as string] [clojure.string :as string]
[react-native.navigation :as navigation]
[react-native.orientation :as orientation] [react-native.orientation :as orientation]
[react-native.platform :as platform] [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.zoomable-image.constants :as c]
[status-im2.contexts.chat.lightbox.animations :as anim] [status-im2.contexts.chat.lightbox.animations :as anim]
[utils.re-frame :as rf])) [utils.re-frame :as rf]))
@ -36,9 +39,9 @@
exit? exit?
{:keys [x-threshold-scale y-threshold-scale]} {:keys [x-threshold-scale y-threshold-scale]}
{:keys [scale saved-scale] :as animations} {: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) (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/animate scale value (if exit? 100 c/default-duration))
(anim/set-val saved-scale value) (anim/set-val saved-scale value)
(reset! pan-x-enabled? (> value x-threshold-scale)) (reset! pan-x-enabled? (> value x-threshold-scale))
@ -102,6 +105,27 @@
(when (and (= zoom-out-signal index) (> scale c/min-scale)) (when (and (= zoom-out-signal index) (> scale c/min-scale))
(rescale c/min-scale true))) (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 ;;; Dimensions
(defn get-dimensions (defn get-dimensions
"Calculates all required dimensions. Dimensions calculations are different on iOS and Android because landscape "Calculates all required dimensions. Dimensions calculations are different on iOS and Android because landscape
@ -162,3 +186,28 @@
(if (or (> focal max) (< focal min)) (if (or (> focal max) (< focal min))
(/ screen-size 2) (/ screen-size 2)
focal))) 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)})

View File

@ -4,7 +4,6 @@
[react-native.gesture :as gesture] [react-native.gesture :as gesture]
[react-native.platform :as platform] [react-native.platform :as platform]
[react-native.reanimated :as reanimated] [react-native.reanimated :as reanimated]
[reagent.core :as reagent]
[utils.re-frame :as rf] [utils.re-frame :as rf]
[oops.core :refer [oget]] [oops.core :refer [oget]]
[react-native.orientation :as orientation] [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 [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] {:keys [saved-scale scale pinch-x pinch-y pinch-x-start pinch-y-start pinch-x-max pinch-y-max]
:as animations} :as animations}
{:keys [focal-x focal-y] :as props} {:keys [focal-x focal-y] :as state}
rescale rescale
transparent? transparent?
toggle-opacity] toggle-opacity]
@ -135,7 +134,7 @@
(utils/center-x animations false)) (utils/center-x animations false))
(when (< (anim/get-val scale) y-threshold-scale) (when (< (anim/get-val scale) y-threshold-scale)
(utils/center-y animations false)))) (utils/center-y animations false))))
(finalize-pinch dimensions animations props))))) (finalize-pinch dimensions animations state)))))
(defn pan-x-gesture (defn pan-x-gesture
[{:keys [width screen-width x-threshold-scale]} [{:keys [width screen-width x-threshold-scale]}
@ -204,66 +203,16 @@
(anim/animate-decay pan-y velocity [lower-bound upper-bound]) (anim/animate-decay pan-y velocity [lower-bound upper-bound])
(anim/animate-decay pan-y-start velocity [lower-bound upper-bound])))))))) (anim/animate-decay pan-y-start velocity [lower-bound upper-bound]))))))))
(defn zoomable-image (defn- f-zoomable-image
[{:keys [image-width image-height content message-id]} index args on-tap] [dimensions animations state rescale curr-orientation content focused? index render-data]
[:f> (let [{:keys [transparent? set-full-height?]} render-data
(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) portrait? (= curr-orientation orientation/portrait)
dimensions (utils/get-dimensions on-tap #(utils/toggle-opacity index render-data portrait?)
(or image-width c/default-dimension) tap (tap-gesture on-tap)
(or image-height c/default-duration) double-tap (double-tap-gesture dimensions animations rescale transparent? on-tap)
curr-orientation pinch (pinch-gesture dimensions animations state rescale transparent? on-tap)
args) pan-x (pan-x-gesture dimensions animations state rescale)
animations {:scale (anim/use-val c/min-scale) pan-y (pan-y-gesture dimensions animations state rescale)
: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 composed-gestures (gesture/exclusive
(gesture/simultaneous pinch pan-x pan-y) (gesture/simultaneous pinch pan-x pan-y)
(gesture/exclusive double-tap tap))] (gesture/exclusive double-tap tap))]
@ -276,4 +225,39 @@
[reanimated/fast-image [reanimated/fast-image
{:source {:uri (:image content)} {:source {:uri (:image content)}
:native-ID (when focused? :shared-element) :native-ID (when focused? :shared-element)
:style (style/image dimensions animations (:border-value args))}]]]))]))]) :style (style/image dimensions animations (:border-value render-data))}]]]))
(defn zoomable-image
[{: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]))