parent
24eaec80bd
commit
0cc631ce2e
|
@ -130,6 +130,7 @@
|
||||||
|
|
||||||
;;Solid
|
;;Solid
|
||||||
(def black "#000000")
|
(def black "#000000")
|
||||||
|
(def black-opa-0 (alpha black 0))
|
||||||
(def onboarding-header-black "#000716")
|
(def onboarding-header-black "#000716")
|
||||||
|
|
||||||
;;;;Primary
|
;;;;Primary
|
||||||
|
|
|
@ -15,60 +15,59 @@
|
||||||
:offset (* (+ c/small-image-size 8) index)
|
:offset (* (+ c/small-image-size 8) index)
|
||||||
:index 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
|
(defn small-image
|
||||||
[item index _ {:keys [scroll-index atoms]}]
|
[item index _ render-data]
|
||||||
[:f>
|
[:f> f-small-image item index _ render-data])
|
||||||
(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})}]]))])
|
|
||||||
|
|
||||||
(defn bottom-view
|
(defn bottom-view
|
||||||
[messages index scroll-index insets animations derived item-width atoms]
|
[messages index scroll-index insets animations derived item-width props]
|
||||||
[:f>
|
(let [text (get-in (first messages) [:content :text])
|
||||||
(fn []
|
padding-horizontal (- (/ item-width 2) (/ c/focused-image-size 2))]
|
||||||
(let [text (get-in (first messages) [:content :text])
|
[reanimated/linear-gradient
|
||||||
padding-horizontal (- (/ item-width 2) (/ c/focused-image-size 2))]
|
{:colors [:black :transparent]
|
||||||
[reanimated/linear-gradient
|
:start {:x 0 :y 1}
|
||||||
{:colors [:black :transparent]
|
:end {:x 0 :y 0}
|
||||||
:start {:x 0 :y 1}
|
:style (style/gradient-container insets animations derived)}
|
||||||
:end {:x 0 :y 0}
|
[rn/text {:style style/text-style} text]
|
||||||
:style (style/gradient-container insets animations derived)}
|
[rn/flat-list
|
||||||
[rn/text {:style style/text-style} text]
|
{:ref #(reset! (:small-list-ref props) %)
|
||||||
[rn/flat-list
|
:key-fn :message-id
|
||||||
{:ref #(reset! (:small-list-ref atoms) %)
|
:style {:height c/small-list-height}
|
||||||
:key-fn :message-id
|
:data messages
|
||||||
:style {:height c/small-list-height}
|
:render-fn small-image
|
||||||
:data messages
|
:render-data {:scroll-index scroll-index
|
||||||
:render-fn small-image
|
:props props}
|
||||||
:render-data {:scroll-index scroll-index
|
:horizontal true
|
||||||
:atoms atoms}
|
:shows-horizontal-scroll-indicator false
|
||||||
:horizontal true
|
:get-item-layout get-small-item-layout
|
||||||
:shows-horizontal-scroll-indicator false
|
:separator [rn/view {:style {:width 8}}]
|
||||||
:get-item-layout get-small-item-layout
|
:initial-scroll-index index
|
||||||
:separator [rn/view {:style {:width 8}}]
|
:content-container-style (style/content-container padding-horizontal)}]]))
|
||||||
:initial-scroll-index index
|
|
||||||
:content-container-style (style/content-container padding-horizontal)}]]))])
|
|
||||||
|
|
||||||
|
|
|
@ -11,3 +11,7 @@
|
||||||
(def ^:const small-list-padding-vertical 12)
|
(def ^:const small-list-padding-vertical 12)
|
||||||
|
|
||||||
(def ^:const top-view-height 56)
|
(def ^:const top-view-height 56)
|
||||||
|
|
||||||
|
(def ^:const separator-width 16)
|
||||||
|
|
||||||
|
(def ^:const drag-threshold 100)
|
||||||
|
|
|
@ -67,9 +67,7 @@
|
||||||
:opacity opacity}
|
:opacity opacity}
|
||||||
{:position :absolute
|
{:position :absolute
|
||||||
:bottom 0
|
:bottom 0
|
||||||
:padding-bottom (if platform/ios?
|
:padding-bottom (:bottom insets)
|
||||||
(:bottom insets)
|
|
||||||
(+ (:bottom insets) c/small-list-padding-vertical c/focused-extra-size))
|
|
||||||
:z-index 3}))
|
:z-index 3}))
|
||||||
|
|
||||||
(defn content-container
|
(defn content-container
|
||||||
|
|
|
@ -41,49 +41,47 @@
|
||||||
|
|
||||||
(defn top-view
|
(defn top-view
|
||||||
[{:keys [from timestamp]} insets index animations derived landscape? screen-width]
|
[{:keys [from timestamp]} insets index animations derived landscape? screen-width]
|
||||||
[:f>
|
(let [display-name (first (rf/sub [:contacts/contact-two-names-by-identity
|
||||||
(fn []
|
from]))
|
||||||
(let [display-name (first (rf/sub [:contacts/contact-two-names-by-identity
|
bg-color (if landscape?
|
||||||
from]))
|
colors/neutral-100-opa-70
|
||||||
bg-color (if landscape?
|
colors/neutral-100-opa-0)
|
||||||
colors/neutral-100-opa-70
|
{:keys [background-color opacity]} animations]
|
||||||
colors/neutral-100-opa-0)
|
[reanimated/view
|
||||||
{:keys [background-color opacity]} animations]
|
{:style
|
||||||
[reanimated/view
|
(style/top-view-container (:top insets) screen-width bg-color landscape? animations derived)}
|
||||||
{:style
|
[reanimated/linear-gradient
|
||||||
(style/top-view-container (:top insets) screen-width bg-color landscape? animations derived)}
|
{:colors [(colors/alpha "#000000" 0.8) :transparent]
|
||||||
[reanimated/linear-gradient
|
:start {:x 0 :y 0}
|
||||||
{:colors [(colors/alpha "#000000" 0.8) :transparent]
|
:end {:x 0 :y 1}
|
||||||
:start {:x 0 :y 0}
|
:style (style/top-gradient insets)}]
|
||||||
:end {:x 0 :y 1}
|
[rn/view
|
||||||
:style (style/top-gradient insets)}]
|
{:style {:flex-direction :row
|
||||||
[rn/view
|
:align-items :center}}
|
||||||
{:style {:flex-direction :row
|
[rn/touchable-opacity
|
||||||
:align-items :center}}
|
{:on-press (fn []
|
||||||
[rn/touchable-opacity
|
(anim/animate background-color :transparent)
|
||||||
{:on-press (fn []
|
(anim/animate opacity 0)
|
||||||
(anim/animate background-color :transparent)
|
(rf/dispatch (if platform/ios?
|
||||||
(anim/animate opacity 0)
|
[:chat.ui/exit-lightbox-signal @index]
|
||||||
(rf/dispatch (if platform/ios?
|
[:navigate-back])))
|
||||||
[:chat.ui/exit-lightbox-signal @index]
|
:style style/close-container}
|
||||||
[:navigate-back])))
|
[quo/icon :close {:size 20 :color colors/white}]]
|
||||||
:style style/close-container}
|
[rn/view {:style {:margin-left 12}}
|
||||||
[quo/icon :close {:size 20 :color colors/white}]]
|
[quo/text
|
||||||
[rn/view {:style {:margin-left 12}}
|
{:weight :semi-bold
|
||||||
[quo/text
|
:size :paragraph-1
|
||||||
{:weight :semi-bold
|
:style {:color colors/white}} display-name]
|
||||||
:size :paragraph-1
|
[quo/text
|
||||||
:style {:color colors/white}} display-name]
|
{:weight :medium
|
||||||
[quo/text
|
:size :paragraph-2
|
||||||
{:weight :medium
|
:style {:color colors/neutral-40}} (datetime/to-short-str timestamp)]]]
|
||||||
:size :paragraph-2
|
[rn/view {:style style/top-right-buttons}
|
||||||
:style {:color colors/neutral-40}} (datetime/to-short-str timestamp)]]]
|
[rn/touchable-opacity
|
||||||
[rn/view {:style style/top-right-buttons}
|
{:active-opacity 1
|
||||||
[rn/touchable-opacity
|
:style (merge style/close-container {:margin-right 12})}
|
||||||
{:active-opacity 1
|
[quo/icon :share {:size 20 :color colors/white}]]
|
||||||
:style (merge style/close-container {:margin-right 12})}
|
[rn/touchable-opacity
|
||||||
[quo/icon :share {:size 20 :color colors/white}]]
|
{:active-opacity 1
|
||||||
[rn/touchable-opacity
|
:style style/close-container}
|
||||||
{:active-opacity 1
|
[quo/icon :options {:size 20 :color colors/white}]]]]))
|
||||||
:style style/close-container}
|
|
||||||
[quo/icon :options {:size 20 :color colors/white}]]]]))])
|
|
||||||
|
|
|
@ -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)})
|
|
@ -1,80 +1,31 @@
|
||||||
(ns status-im2.contexts.chat.lightbox.view
|
(ns status-im2.contexts.chat.lightbox.view
|
||||||
(:require
|
(:require
|
||||||
[clojure.string :as string]
|
[clojure.string :as string]
|
||||||
[quo2.foundations.colors :as colors]
|
|
||||||
[react-native.core :as rn]
|
[react-native.core :as rn]
|
||||||
[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.reanimated :as reanimated]
|
||||||
|
[react-native.safe-area :as safe-area]
|
||||||
[status-im2.contexts.chat.lightbox.animations :as anim]
|
[status-im2.contexts.chat.lightbox.animations :as anim]
|
||||||
[status-im2.contexts.chat.lightbox.style :as style]
|
[status-im2.contexts.chat.lightbox.style :as style]
|
||||||
[utils.re-frame :as rf]
|
[utils.re-frame :as rf]
|
||||||
[reagent.core :as reagent]
|
|
||||||
[react-native.gesture :as gesture]
|
[react-native.gesture :as gesture]
|
||||||
[status-im2.contexts.chat.lightbox.zoomable-image.view :as zoomable-image]
|
[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.top-view :as top-view]
|
||||||
[status-im2.contexts.chat.lightbox.bottom-view :as bottom-view]
|
[status-im2.contexts.chat.lightbox.bottom-view :as bottom-view]
|
||||||
[utils.worklets.lightbox :as worklet]
|
[oops.core :refer [oget]]
|
||||||
[oops.core :refer [oget]]))
|
[status-im2.contexts.chat.lightbox.utils :as utils]
|
||||||
|
[status-im2.contexts.chat.lightbox.constants :as constants]))
|
||||||
(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))))
|
|
||||||
|
|
||||||
(defn get-item-layout
|
(defn get-item-layout
|
||||||
[_ index item-width]
|
[_ 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
|
(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?
|
(when-not @scroll-index-lock?
|
||||||
(let [changed (-> e (oget :changed) first)
|
(let [changed (-> e (oget :changed) first)
|
||||||
index (oget changed :index)]
|
index (oget changed :index)]
|
||||||
|
@ -86,160 +37,88 @@
|
||||||
(defn image
|
(defn image
|
||||||
[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 seperator-width) screen-height)}
|
{:style (style/image (+ screen-width constants/separator-width) screen-height)}
|
||||||
[zoomable-image/zoomable-image message index args
|
[zoomable-image/zoomable-image message index args
|
||||||
#(toggle-opacity index args %)]
|
#(utils/toggle-opacity index args %)]
|
||||||
[rn/view {:style {:width seperator-width}}]])
|
[rn/view {:style {:width constants/separator-width}}]])
|
||||||
|
|
||||||
(defn drag-gesture
|
(defn lightbox-content
|
||||||
[{:keys [pan-x pan-y background-color opacity layout]} x? set-full-height?]
|
[props {:keys [data transparent? scroll-index set-full-height?]} animations derived messages index
|
||||||
(->
|
callback]
|
||||||
(gesture/gesture-pan)
|
(let [insets (safe-area/get-insets)
|
||||||
(gesture/enabled true)
|
window (rn/get-window)
|
||||||
(gesture/max-pointers 1)
|
window-width (:width window)
|
||||||
(gesture/on-start #(reset! set-full-height? false))
|
window-height (if platform/android?
|
||||||
(gesture/on-update (fn [e]
|
(+ (:height window) (:top insets))
|
||||||
(let [translation (if x? (oget e "translationX") (oget e "translationY"))
|
(:height window))
|
||||||
progress (Math/abs (/ translation drag-threshold))]
|
curr-orientation (or (rf/sub [:lightbox/orientation]) orientation/portrait)
|
||||||
(anim/set-val (if x? pan-x pan-y) translation)
|
landscape? (string/includes? curr-orientation orientation/landscape)
|
||||||
(anim/set-val opacity (- 1 progress))
|
horizontal? (or platform/android? (not landscape?))
|
||||||
(anim/set-val layout (* progress -20)))))
|
inverted? (and platform/ios? (= curr-orientation orientation/landscape-right))
|
||||||
(gesture/on-end (fn [e]
|
screen-width (if (or platform/ios? (= curr-orientation orientation/portrait))
|
||||||
(if (> (Math/abs (if x? (oget e "translationX") (oget e "translationY")))
|
window-width
|
||||||
drag-threshold)
|
window-height)
|
||||||
(do
|
screen-height (if (or platform/ios? (= curr-orientation orientation/portrait))
|
||||||
(anim/animate background-color "rgba(0,0,0,0)")
|
window-height
|
||||||
(anim/animate opacity 0)
|
window-width)
|
||||||
(rf/dispatch [:navigate-back]))
|
item-width (if (and landscape? platform/ios?) screen-height screen-width)]
|
||||||
(do
|
[reanimated/view
|
||||||
#(reset! set-full-height? true)
|
{:style (reanimated/apply-animations-to-style {:background-color (:background-color animations)}
|
||||||
(anim/animate (if x? pan-x pan-y) 0)
|
{:height screen-height})}
|
||||||
(anim/animate opacity 1)
|
(when-not @transparent?
|
||||||
(anim/animate layout 0)))))))
|
[: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
|
(defn lightbox
|
||||||
[]
|
[]
|
||||||
[:f>
|
(let [screen-params (rf/sub [:get-screen-params])]
|
||||||
(fn []
|
[:f> f-lightbox screen-params]))
|
||||||
;; 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])]))]))])
|
|
||||||
|
|
|
@ -3,7 +3,6 @@
|
||||||
[quo2.foundations.colors :as colors]
|
[quo2.foundations.colors :as colors]
|
||||||
[react-native.core :as rn]
|
[react-native.core :as rn]
|
||||||
[react-native.fast-image :as fast-image]
|
[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.contexts.chat.messages.content.album.style :as style]
|
||||||
[status-im2.constants :as constants]
|
[status-im2.constants :as constants]
|
||||||
[status-im2.contexts.chat.messages.content.image.view :as image]
|
[status-im2.contexts.chat.messages.content.image.view :as image]
|
||||||
|
@ -20,8 +19,7 @@
|
||||||
|
|
||||||
(defn album-message
|
(defn album-message
|
||||||
[{:keys [albumize?] :as message} context on-long-press]
|
[{:keys [albumize?] :as message} context on-long-press]
|
||||||
(let [insets (safe-area/get-insets)
|
(let [shared-element-id (rf/sub [:shared-element-id])
|
||||||
shared-element-id (rf/sub [:shared-element-id])
|
|
||||||
first-image (first (:album message))
|
first-image (first (:album message))
|
||||||
album-style (if (> (:image-width first-image) (:image-height first-image))
|
album-style (if (> (:image-width first-image) (:image-height first-image))
|
||||||
:landscape
|
:landscape
|
||||||
|
@ -53,8 +51,7 @@
|
||||||
:on-press #(rf/dispatch [:chat.ui/navigate-to-lightbox
|
:on-press #(rf/dispatch [:chat.ui/navigate-to-lightbox
|
||||||
(:message-id item)
|
(:message-id item)
|
||||||
{:messages (:album message)
|
{:messages (:album message)
|
||||||
:index index
|
:index index}])}
|
||||||
:insets insets}])}
|
|
||||||
[fast-image/fast-image
|
[fast-image/fast-image
|
||||||
{:style (style/image dimensions index portrait? images-count)
|
{:style (style/image dimensions index portrait? images-count)
|
||||||
:source {:uri (:image (:content item))}
|
:source {:uri (:image (:content item))}
|
||||||
|
|
Loading…
Reference in New Issue