Implement collectible image preview (#18449)

* Lightbox refactored and moved to common screens that can be reused
* Events renamed
* Unify constants namespace importing
This commit is contained in:
Volodymyr Kozieiev 2024-01-25 10:58:12 +00:00 committed by GitHub
parent ed7463132a
commit 8999b2b9e8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
26 changed files with 343 additions and 250 deletions

View File

@ -1,4 +1,4 @@
(ns status-im.contexts.chat.messenger.lightbox.animations
(ns status-im.common.lightbox.animations
(:require
[react-native.reanimated :as reanimated]))

View File

@ -1,25 +1,25 @@
(ns status-im.contexts.chat.messenger.lightbox.bottom-view
(ns status-im.common.lightbox.bottom-view
(:require
[quo.foundations.colors :as colors]
[react-native.core :as rn]
[react-native.platform :as platform]
[react-native.reanimated :as reanimated]
[status-im.contexts.chat.messenger.lightbox.animations :as anim]
[status-im.contexts.chat.messenger.lightbox.constants :as c]
[status-im.contexts.chat.messenger.lightbox.style :as style]
[status-im.contexts.chat.messenger.lightbox.text-sheet.view :as text-sheet]
[status-im.common.lightbox.animations :as anim]
[status-im.common.lightbox.constants :as constants]
[status-im.common.lightbox.style :as style]
[status-im.common.lightbox.text-sheet.view :as text-sheet]
[utils.re-frame :as rf]))
(defn get-small-item-layout
[_ index]
#js
{:length c/small-image-size
:offset (* (+ c/small-image-size 8) index)
{:length constants/small-image-size
:offset (* (+ constants/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)
(let [size (if (= @scroll-index index) constants/focused-image-size constants/small-image-size)
size-value (anim/use-val size)
{:keys [scroll-index-lock? small-list-ref flat-list-ref]}
props]
@ -27,7 +27,7 @@
[rn/touchable-opacity
{:active-opacity 1
:on-press (fn []
(rf/dispatch [:chat.ui/zoom-out-signal @scroll-index])
(rf/dispatch [:lightbox/zoom-out-signal @scroll-index])
(reset! scroll-index-lock? true)
(js/setTimeout #(reset! scroll-index-lock? false) 500)
(js/setTimeout
@ -38,9 +38,9 @@
(.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)]))}
(rf/dispatch [:lightbox/update-animation-shared-element-id (:id item)]))}
[reanimated/fast-image
{:source {:uri (:image (:content item))}
{:source {:uri (:image item)}
:style (reanimated/apply-animations-to-style {:width size-value
:height size-value}
{:border-radius 10})}]]))
@ -50,20 +50,26 @@
[:f> f-small-image item index _ render-data])
(defn bottom-view
[messages index scroll-index insets animations derived item-width props state transparent?]
(let [padding-horizontal (- (/ item-width 2) (/ c/focused-image-size 2))]
[{:keys [images index scroll-index insets animations derived item-width props state transparent?
bottom-text-component]}]
(let [padding-horizontal (- (/ item-width 2) (/ constants/focused-image-size 2))]
[reanimated/linear-gradient
{:colors [colors/neutral-100-opa-100 colors/neutral-100-opa-80 colors/neutral-100-opa-0]
:location [0.2 0.9]
:start {:x 0 :y 1}
:end {:x 0 :y 0}
:style (style/gradient-container insets animations derived transparent?)}
[text-sheet/view messages animations state props]
(when bottom-text-component
[text-sheet/view
{:overlay-opacity (:overlay-opacity animations)
:overlay-z-index (:overlay-z-index state)
:text-sheet-lock? (:text-sheet-lock? props)
:text-component bottom-text-component}])
[rn/flat-list
{:ref #(reset! (:small-list-ref props) %)
:key-fn :message-id
:style {:height c/small-list-height}
:data messages
:key-fn :id
:style {:height constants/small-list-height}
:data images
:render-fn small-image
:render-data {:scroll-index scroll-index
:props props}

View File

@ -1,4 +1,4 @@
(ns status-im.contexts.chat.messenger.lightbox.constants)
(ns status-im.common.lightbox.constants)
(def ^:const small-image-size 40)
(def ^:const focused-extra-size 16)

View File

@ -1,4 +1,4 @@
(ns status-im.contexts.chat.messenger.lightbox.effects
(ns status-im.common.lightbox.effects
(:require [react-native.blob :as blob]
[react-native.cameraroll :as cameraroll]
[react-native.fs :as fs]
@ -10,7 +10,7 @@
{:trusty platform/ios?
:path (str (fs/cache-dir) "/StatusIm_Image.jpeg")})
(rf/reg-fx :effects.chat/share-image
(rf/reg-fx :effects.lightbox/share-image
(fn [uri]
(blob/fetch uri
config
@ -20,7 +20,7 @@
#(fs/unlink downloaded-url)
#(fs/unlink downloaded-url))))))
(rf/reg-fx :effects.chat/save-image-to-gallery
(rf/reg-fx :effects.lightbox/save-image-to-gallery
(fn [[uri on-success]]
(blob/fetch uri
config

View File

@ -0,0 +1,37 @@
(ns status-im.common.lightbox.events
(:require [reagent.core :as reagent]
status-im.common.lightbox.effects
[utils.re-frame :as rf]))
(rf/reg-event-fx :lightbox/navigate-to-lightbox
(fn [{:keys [db]} [animation-shared-element-id screen-params]]
(reagent/next-tick #(rf/dispatch [:navigate-to :lightbox screen-params]))
{:db (assoc db :animation-shared-element-id animation-shared-element-id)}))
(rf/reg-event-fx :lightbox/update-animation-shared-element-id
(fn [{:keys [db]} [animation-shared-element-id]]
{:db (assoc db :animation-shared-element-id animation-shared-element-id)}))
(rf/reg-event-fx :lightbox/exit-lightbox-signal
(fn [{:keys [db]} [value]]
{:db (assoc db :lightbox/exit-signal value)}))
(rf/reg-event-fx :lightbox/zoom-out-signal
(fn [{:keys [db]} [value]]
{:db (assoc db :lightbox/zoom-out-signal value)}))
(rf/reg-event-fx :lightbox/orientation-change
(fn [{:keys [db]} [value]]
{:db (assoc db :lightbox/orientation value)}))
(rf/reg-event-fx :lightbox/lightbox-scale
(fn [{:keys [db]} [value]]
{:db (assoc db :lightbox/scale value)}))
(rf/reg-event-fx :lightbox/share-image
(fn [_ [uri]]
{:effects.lightbox/share-image uri}))
(rf/reg-event-fx :lightbox/save-image-to-gallery
(fn [_ [uri on-success]]
{:effects.lightbox/save-image-to-gallery [uri on-success]}))

View File

@ -1,9 +1,9 @@
(ns status-im.contexts.chat.messenger.lightbox.style
(ns status-im.common.lightbox.style
(:require
[quo.foundations.colors :as colors]
[react-native.platform :as platform]
[react-native.reanimated :as reanimated]
[status-im.contexts.chat.messenger.lightbox.constants :as c]))
[status-im.common.lightbox.constants :as constants]))
;;;; VIEW
(defn image
@ -33,7 +33,7 @@
{:position :absolute
:padding-horizontal 20
:top (if (or platform/ios? (not landscape?)) top-inset 0)
:height c/top-view-height
:height constants/top-view-height
:z-index 4
:flex-direction :row
:justify-content :space-between
@ -44,7 +44,7 @@
(defn top-gradient
[insets]
{:position :absolute
:height (+ c/top-view-height (:top insets) 0)
:height (+ constants/top-view-height (:top insets) 0)
:top (- (:top insets))
:left 0
:right 0})
@ -71,12 +71,12 @@
:display (if @transparent? :none :flex)
:bottom 0
:padding-bottom (:bottom insets)
:padding-top c/text-min-height
:padding-top constants/text-min-height
:z-index 3}))
(defn content-container
[padding-horizontal]
{:padding-vertical c/small-list-padding-vertical
{:padding-vertical constants/small-list-padding-vertical
:padding-horizontal padding-horizontal
:align-items :center
:justify-content :center})

View File

@ -1,8 +1,8 @@
(ns status-im.contexts.chat.messenger.lightbox.text-sheet.style
(ns status-im.common.lightbox.text-sheet.style
(:require
[quo.foundations.colors :as colors]
[react-native.reanimated :as reanimated]
[status-im.contexts.chat.messenger.lightbox.constants :as constants]))
[status-im.common.lightbox.constants :as constants]))
(defn sheet-container
[{:keys [height top]}]
@ -14,10 +14,10 @@
:right 0}))
(defn text-style
[expanding-message?]
[expandable-text?]
{:color colors/white
:margin-horizontal 20
:align-items (when-not expanding-message? :center)
:align-items (when-not expandable-text? :center)
:flex-grow 1})
(def bar-container

View File

@ -1,10 +1,10 @@
(ns status-im.contexts.chat.messenger.lightbox.text-sheet.utils
(ns status-im.common.lightbox.text-sheet.utils
(:require
[oops.core :as oops]
[react-native.gesture :as gesture]
[react-native.reanimated :as reanimated]
[reagent.core :as r]
[status-im.contexts.chat.messenger.lightbox.constants :as constants]))
[status-im.common.lightbox.constants :as constants]))
(defn- collapse-sheet
[{:keys [derived-value overlay-opacity saved-top expanded? overlay-z-index]}]
@ -16,10 +16,10 @@
(defn sheet-gesture
[{:keys [derived-value saved-top overlay-opacity gradient-opacity]}
expanded-height max-height full-height overlay-z-index expanded? dragging? expanding-message?]
expanded-height max-height full-height overlay-z-index expanded? dragging? expandable-text?]
(let [disable-gesture-update (r/atom false)]
(-> (gesture/gesture-pan)
(gesture/enabled expanding-message?)
(gesture/enabled expandable-text?)
(gesture/on-start (fn []
(reset! overlay-z-index 1)
(reset! dragging? true)

View File

@ -1,4 +1,4 @@
(ns status-im.contexts.chat.messenger.lightbox.text-sheet.view
(ns status-im.common.lightbox.text-sheet.view
(:require
[quo.foundations.colors :as colors]
[react-native.core :as rn]
@ -8,33 +8,31 @@
[react-native.reanimated :as reanimated]
[react-native.safe-area :as safe-area]
[reagent.core :as reagent]
[status-im.contexts.chat.messenger.lightbox.constants :as constants]
[status-im.contexts.chat.messenger.lightbox.text-sheet.style :as style]
[status-im.contexts.chat.messenger.lightbox.text-sheet.utils :as utils]
[status-im.contexts.chat.messenger.messages.content.text.view :as message-view]))
[status-im.common.lightbox.constants :as constants]
[status-im.common.lightbox.text-sheet.style :as style]
[status-im.common.lightbox.text-sheet.utils :as utils]))
(defn- text-sheet
[messages overlay-opacity overlay-z-index text-sheet-lock?]
[_]
(let [text-height (reagent/atom 0)
expanded? (reagent/atom false)
dragging? (atom false)]
(fn []
(let [{:keys [chat-id content]} (first messages)
insets (safe-area/get-insets)
window-height (:height (rn/get-window))
max-height (- window-height
constants/text-min-height
constants/top-view-height
(:bottom insets)
(when platform/ios? (:top insets)))
full-height (+ constants/bar-container-height
constants/text-margin
constants/line-height
@text-height)
expanded-height (min max-height full-height)
animations (utils/init-animations overlay-opacity)
derived (utils/init-derived-animations animations)
expanding-message? (> @text-height (* constants/line-height 2))]
(fn [{:keys [overlay-opacity overlay-z-index text-sheet-lock? text-component]}]
(let [insets (safe-area/get-insets)
window-height (:height (rn/get-window))
max-height (- window-height
constants/text-min-height
constants/top-view-height
(:bottom insets)
(when platform/ios? (:top insets)))
full-height (+ constants/bar-container-height
constants/text-margin
constants/line-height
@text-height)
expanded-height (min max-height full-height)
animations (utils/init-animations overlay-opacity)
derived (utils/init-derived-animations animations)
expandable-text? (> @text-height (* constants/line-height 2))]
[rn/view
[reanimated/linear-gradient
{:colors [colors/neutral-100-opa-0 colors/neutral-100]
@ -51,10 +49,10 @@
overlay-z-index
expanded?
dragging?
expanding-message?)}
expandable-text?)}
[gesture/gesture-detector
{:gesture (-> (gesture/gesture-tap)
(gesture/enabled (and expanding-message? (not @expanded?)))
(gesture/enabled (and expandable-text? (not @expanded?)))
(gesture/on-start (fn []
(utils/expand-sheet animations
expanded-height
@ -63,7 +61,7 @@
expanded?
text-sheet-lock?))))}
[reanimated/view {:style (style/sheet-container derived)}
(when expanding-message?
(when expandable-text?
[rn/view {:style style/bar-container}
[rn/view {:style style/bar}]])
[linear-gradient/linear-gradient
@ -72,19 +70,17 @@
:end {:x 0 :y 0}
:locations [0.7 0.8 1]
:style (style/bottom-gradient (:bottom insets))}]
[gesture/scroll-view
{:scroll-enabled false
:scroll-event-throttle 16
:bounces false
:style {:height (- max-height constants/bar-container-height)}
:content-container-style {:padding-top (when (not expanding-message?)
:content-container-style {:padding-top (when (not expandable-text?)
constants/bar-container-height)}}
[message-view/render-parsed-text
{:content content
:chat-id chat-id
:style-override (style/text-style expanding-message?)
:on-layout #(utils/on-layout % text-height)}]]]]]]))))
[rn/view {:on-layout #(utils/on-layout % text-height)}
text-component]]]]]]))))
(defn view
[messages {:keys [overlay-opacity]} {:keys [overlay-z-index]} {:keys [text-sheet-lock?]}]
[:f> text-sheet messages overlay-opacity overlay-z-index text-sheet-lock?])
[props]
[:f> text-sheet props])

View File

@ -1,4 +1,4 @@
(ns status-im.contexts.chat.messenger.lightbox.top-view
(ns status-im.common.lightbox.top-view
(:require
[quo.core :as quo]
[quo.foundations.colors :as colors]
@ -6,10 +6,9 @@
[react-native.orientation :as orientation]
[react-native.platform :as platform]
[react-native.reanimated :as reanimated]
[status-im.contexts.chat.messenger.lightbox.animations :as anim]
[status-im.contexts.chat.messenger.lightbox.constants :as c]
[status-im.contexts.chat.messenger.lightbox.style :as style]
[utils.datetime :as datetime]
[status-im.common.lightbox.animations :as anim]
[status-im.common.lightbox.constants :as constants]
[status-im.common.lightbox.style :as style]
[utils.i18n :as i18n]
[utils.re-frame :as rf]
[utils.url :as url]))
@ -17,7 +16,7 @@
(defn animate-rotation
[result screen-width screen-height insets
{:keys [rotate top-view-y top-view-x top-view-width top-view-bg]}]
(let [top-x (+ (/ c/top-view-height 2) (:top insets))]
(let [top-x (+ (/ constants/top-view-height 2) (:top insets))]
(cond
(= result orientation/landscape-left)
(do
@ -42,9 +41,9 @@
(anim/animate top-view-bg colors/neutral-100-opa-0)))))
(defn drawer
[messages index]
(let [{:keys [content]} (nth messages index)
uri (url/replace-port (:image content) (rf/sub [:mediaserver/port]))]
[images index]
(let [{:keys [image]} (nth images index)
uri (url/replace-port image (rf/sub [:mediaserver/port]))]
[quo/action-drawer
[[{:icon :i/save
:accessibility-label :save-image
@ -52,7 +51,7 @@
:on-press (fn []
(rf/dispatch [:hide-bottom-sheet])
(rf/dispatch
[:chat.ui/save-image-to-gallery
[:lightbox/save-image-to-gallery
uri
#(rf/dispatch [:toasts/upsert
{:id :random-id
@ -61,20 +60,19 @@
:text (i18n/label :t/photo-saved)}])]))}]]]))
(defn share-image
[messages index]
(let [{:keys [content]} (nth messages index)
uri (url/replace-port (:image content) (rf/sub [:mediaserver/port]))]
(rf/dispatch [:chat.ui/share-image uri])))
[images index]
(let [{:keys [image]} (nth images index)
uri (url/replace-port image (rf/sub [:mediaserver/port]))]
(rf/dispatch [:lightbox/share-image uri])))
(defn top-view
[messages insets index animations derived landscape? screen-width]
(let [{:keys [from timestamp]} (first messages)
[primary-name _] (rf/sub [:contacts/contact-two-names-by-identity from])
bg-color (if landscape?
colors/neutral-100-opa-70
colors/neutral-100-opa-0)
[images insets index animations derived landscape? screen-width]
(let [{:keys [description header]} (nth images @index)
bg-color (if landscape?
colors/neutral-100-opa-70
colors/neutral-100-opa-0)
{:keys [background-color opacity
overlay-opacity]} animations]
overlay-opacity]} animations]
[reanimated/view
{:style
(style/top-view-container (:top insets) screen-width bg-color landscape? animations derived)}
@ -92,7 +90,7 @@
(anim/animate opacity 0)
(anim/animate overlay-opacity 0)
(rf/dispatch (if platform/ios?
[:chat.ui/exit-lightbox-signal @index]
[:lightbox/exit-lightbox-signal @index]
[:navigate-back])))
:style style/close-container}
[quo/icon :close {:size 20 :color colors/white}]]
@ -100,22 +98,22 @@
[quo/text
{:weight :semi-bold
:size :paragraph-1
:style {:color colors/white}} primary-name]
:style {:color colors/white}} header]
[quo/text
{:weight :medium
:size :paragraph-2
:style {:color colors/neutral-40}} (when timestamp (datetime/to-short-str timestamp))]]]
:style {:color colors/neutral-40}} description]]]
[rn/view {:style style/top-right-buttons}
[rn/touchable-opacity
{:active-opacity 1
:accessibility-label :share-image
:on-press #(share-image messages @index)
:on-press #(share-image images @index)
:style (merge style/close-container {:margin-right 12})}
[quo/icon :share {:size 20 :color colors/white}]]
[rn/touchable-opacity
{:active-opacity 1
:accessibility-label :image-options
:on-press #(rf/dispatch [:show-bottom-sheet
{:content (fn [] [drawer messages @index])}])
{:content (fn [] [drawer images @index])}])
:style style/close-container}
[quo/icon :options {:size 20 :color colors/white}]]]]))

View File

@ -1,4 +1,4 @@
(ns status-im.contexts.chat.messenger.lightbox.utils
(ns status-im.common.lightbox.utils
(:require
[clojure.string :as string]
[oops.core :as oops]
@ -9,9 +9,9 @@
[react-native.platform :as platform]
[react-native.safe-area :as safe-area]
[reagent.core :as reagent]
[status-im.contexts.chat.messenger.lightbox.animations :as anim]
[status-im.contexts.chat.messenger.lightbox.constants :as constants]
[status-im.contexts.chat.messenger.lightbox.top-view :as top-view]
[status-im.common.lightbox.animations :as anim]
[status-im.common.lightbox.constants :as constants]
[status-im.common.lightbox.top-view :as top-view]
[utils.re-frame :as rf]
[utils.worklets.chat.messenger.lightbox :as worklet]))
@ -46,7 +46,7 @@
(if platform/ios? 250 100)))
(swap! timers assoc :mount-index-lock (js/setTimeout #(reset! scroll-index-lock? false) 300))
(fn []
(rf/dispatch [:chat.ui/zoom-out-signal nil])
(rf/dispatch [:lightbox/zoom-out-signal nil])
(when platform/android?
(rf/dispatch [:chat.ui/lightbox-scale 1]))
(clear-timers timers)))))
@ -69,7 +69,7 @@
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]))
(rf/dispatch [:lightbox/orientation-change result]))
(cond
landscape?
(orientation/lock-to-landscape "lightbox")
@ -148,11 +148,11 @@
:timers (atom {})})
(defn init-state
[messages index]
[images 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)] []))
{:data (reagent/atom (if (number? index) [(nth images index)] []))
:scroll-index (reagent/atom index)
:transparent? (reagent/atom false)
:set-full-height? (reagent/atom false)

View File

@ -1,4 +1,4 @@
(ns status-im.contexts.chat.messenger.lightbox.view
(ns status-im.common.lightbox.view
(:require
[clojure.string :as string]
[oops.core :as oops]
@ -9,13 +9,13 @@
[react-native.platform :as platform]
[react-native.reanimated :as reanimated]
[react-native.safe-area :as safe-area]
[status-im.contexts.chat.messenger.lightbox.animations :as anim]
[status-im.contexts.chat.messenger.lightbox.bottom-view :as bottom-view]
[status-im.contexts.chat.messenger.lightbox.constants :as constants]
[status-im.contexts.chat.messenger.lightbox.style :as style]
[status-im.contexts.chat.messenger.lightbox.top-view :as top-view]
[status-im.contexts.chat.messenger.lightbox.utils :as utils]
[status-im.contexts.chat.messenger.lightbox.zoomable-image.view :as zoomable-image]
[status-im.common.lightbox.animations :as anim]
[status-im.common.lightbox.bottom-view :as bottom-view]
[status-im.common.lightbox.constants :as constants]
[status-im.common.lightbox.style :as style]
[status-im.common.lightbox.top-view :as top-view]
[status-im.common.lightbox.utils :as utils]
[status-im.common.lightbox.zoomable-image.view :as zoomable-image]
[utils.re-frame :as rf]))
(defn get-item-layout
@ -33,9 +33,10 @@
(reset! scroll-index index)
(when @small-list-ref
(.scrollToIndex ^js @small-list-ref #js {:animated true :index index}))
(rf/dispatch [:chat.ui/update-shared-element-id (:message-id (oops/oget changed :item))]))))
(rf/dispatch [:lightbox/update-animation-shared-element-id
(:id (oops/oget changed :item))]))))
(defn image
(defn image-view
[message index _ {:keys [screen-width screen-height] :as args}]
[rn/view
{:style (style/image (+ screen-width constants/separator-width) screen-height)}
@ -43,30 +44,31 @@
[rn/view {:style {:width constants/separator-width}}]])
(defn lightbox-content
[props {:keys [data transparent? scroll-index set-full-height?] :as state}
animations derived messages index handle-items-changed]
(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)]
[{:keys [props state animations derived images index handle-items-changed bottom-text-component]}]
(let [{:keys [data transparent? scroll-index
set-full-height?]} state
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 messages insets scroll-index animations derived landscape?
[:f> top-view/top-view images insets scroll-index animations derived landscape?
screen-width])
[gesture/gesture-detector
{:gesture (utils/drag-gesture animations (and landscape? platform/ios?) set-full-height?)}
@ -92,7 +94,7 @@
:scroll-event-throttle 8
:style {:width (+ screen-width constants/separator-width)}
:data @data
:render-fn image
:render-fn image-view
:render-data {:opacity-value (:opacity animations)
:overlay-opacity (:overlay-opacity animations)
:border-value (:border animations)
@ -118,26 +120,47 @@
;; NOTE: not un-mounting bottom-view based on `transparent?` (like we do with the top-view
;; above), since we need to save the state of the text-sheet position. Instead, we use
;; the `:display` style property to hide the bottom-sheet.
(when (not landscape?)
[:f> bottom-view/bottom-view messages index scroll-index insets animations derived
item-width props state transparent?])]))
(when (and (not landscape?) (or bottom-text-component (> (count images) 1)))
[:f> bottom-view/bottom-view
{:images images
:index index
:scroll-index scroll-index
:insets insets
:animations animations
:derived derived
:item-width item-width
:props props
:state state
:transparent? transparent?
:bottom-text-component bottom-text-component}])]))
(defn- f-lightbox
[]
(let [{:keys [messages index]} (rf/sub [:get-screen-params])
props (utils/init-props)
state (utils/init-state messages index)
handle-items-changed (fn [e]
(on-viewable-items-changed e props state))]
(let [{:keys [images index bottom-text-component]} (rf/sub [:get-screen-params])
props
(utils/init-props)
state
(utils/init-state images index)
handle-items-changed
(fn [e]
(on-viewable-items-changed e props state))]
(fn []
(let [animations (utils/init-animations (count messages) index)
(let [animations (utils/init-animations (count images) index)
derived (utils/init-derived-animations animations)]
(anim/animate (:background-color animations) colors/neutral-100)
(reset! (:data state) messages)
(reset! (:data state) images)
(when platform/ios? ; issue: https://github.com/wix/react-native-navigation/issues/7726
(utils/orientation-change props state animations))
(utils/effect props animations index)
[:f> lightbox-content props state animations derived messages index handle-items-changed]))))
[:f> lightbox-content
{:props props
:state state
:animations animations
:derived derived
:images images
:index index
:handle-items-changed handle-items-changed
:bottom-text-component bottom-text-component}]))))
(defn lightbox
[]

View File

@ -1,4 +1,4 @@
(ns status-im.contexts.chat.messenger.lightbox.zoomable-image.constants)
(ns status-im.common.lightbox.zoomable-image.constants)
(def ^:const min-scale 1)

View File

@ -1,4 +1,4 @@
(ns status-im.contexts.chat.messenger.lightbox.zoomable-image.style
(ns status-im.common.lightbox.zoomable-image.style
(:require
[react-native.platform :as platform]
[react-native.reanimated :as reanimated]))

View File

@ -1,4 +1,4 @@
(ns status-im.contexts.chat.messenger.lightbox.zoomable-image.utils
(ns status-im.common.lightbox.zoomable-image.utils
(:require
[clojure.string :as string]
[react-native.core :as rn]
@ -6,8 +6,8 @@
[react-native.orientation :as orientation]
[react-native.platform :as platform]
[reagent.core :as reagent]
[status-im.contexts.chat.messenger.lightbox.animations :as anim]
[status-im.contexts.chat.messenger.lightbox.zoomable-image.constants :as constants]
[status-im.common.lightbox.animations :as anim]
[status-im.common.lightbox.zoomable-image.constants :as constants]
[utils.re-frame :as rf]))
;;; Helpers
@ -97,7 +97,7 @@
(rescale constants/min-scale true)
(js/setTimeout #(rf/dispatch [:navigate-back]) 70))
(rf/dispatch [:navigate-back]))
(js/setTimeout #(rf/dispatch [:chat.ui/exit-lightbox-signal nil]) 500)))
(js/setTimeout #(rf/dispatch [:lightbox/exit-lightbox-signal nil]) 500)))
(defn handle-zoom-out-signal
"Zooms out when pressing on another photo from the small bottom list"

View File

@ -1,4 +1,4 @@
(ns status-im.contexts.chat.messenger.lightbox.zoomable-image.view
(ns status-im.common.lightbox.zoomable-image.view
(:require
[oops.core :refer [oget]]
[react-native.core :as rn]
@ -7,10 +7,10 @@
[react-native.platform :as platform]
[react-native.reanimated :as reanimated]
[reagent.core :as r]
[status-im.contexts.chat.messenger.lightbox.animations :as anim]
[status-im.contexts.chat.messenger.lightbox.zoomable-image.constants :as c]
[status-im.contexts.chat.messenger.lightbox.zoomable-image.style :as style]
[status-im.contexts.chat.messenger.lightbox.zoomable-image.utils :as utils]
[status-im.common.lightbox.animations :as anim]
[status-im.common.lightbox.zoomable-image.constants :as c]
[status-im.common.lightbox.zoomable-image.style :as style]
[status-im.common.lightbox.zoomable-image.utils :as utils]
[utils.re-frame :as rf]
[utils.url :as url]))
@ -206,7 +206,7 @@
(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
[dimensions animations state rescale curr-orientation image focused? index render-data
image-dimensions-nil?]
(let [{:keys [transparent? set-full-height?]} render-data
portrait? (= curr-orientation orientation/portrait)
@ -229,7 +229,7 @@
(= curr-orientation orientation/portrait))}
[reanimated/fast-image
(merge
{:source {:uri (url/replace-port (:image content) (rf/sub [:mediaserver/port]))}
{:source {:uri (url/replace-port image (rf/sub [:mediaserver/port]))}
:native-ID (when focused? :shared-element)
:style (style/image dimensions animations render-data index)}
(when image-dimensions-nil?
@ -238,12 +238,12 @@
(defn zoomable-image
[]
(let [state (utils/init-state)]
(fn [{:keys [image-width image-height content message-id]} index render-data]
(let [shared-element-id (rf/sub [:shared-element-id])
(fn [{:keys [image-width image-height image id]} index render-data]
(let [animation-shared-element-id (rf/sub [:animation-shared-element-id])
exit-lightbox-signal (rf/sub [:lightbox/exit-signal])
zoom-out-signal (rf/sub [:lightbox/zoom-out-signal])
{:keys [set-full-height? curr-orientation]} render-data
focused? (= shared-element-id message-id)
focused? (= animation-shared-element-id id)
;; TODO - remove `image-dimensions` check, once
;; https://github.com/status-im/status-desktop/issues/10944 is fixed
image-dimensions-nil? (not (and image-width image-height))
@ -269,5 +269,5 @@
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?
[:f> f-zoomable-image dimensions animations state rescale curr-orientation image focused?
index render-data image-dimensions-nil?]))))

View File

@ -4,13 +4,12 @@
[legacy.status-im.chat.models.loading :as loading]
[legacy.status-im.data-store.chats :as chats-store]
[re-frame.core :as re-frame]
[reagent.core :as reagent]
status-im.common.lightbox.events
[status-im.common.muting.helpers :refer [format-mute-till]]
[status-im.constants :as constants]
[status-im.contexts.chat.contacts.events :as contacts-store]
status-im.contexts.chat.effects
[status-im.contexts.chat.messenger.composer.link-preview.events :as link-preview]
status-im.contexts.chat.messenger.lightbox.events
status-im.contexts.chat.messenger.messages.content.reactions.events
[status-im.contexts.chat.messenger.messages.delete-message-for-me.events :as delete-for-me]
[status-im.contexts.chat.messenger.messages.delete-message.events :as delete-message]
@ -394,38 +393,6 @@
[cofx chat-id]
(navigation/navigate-to cofx :chat-pinned-messages {:chat-id chat-id}))
(rf/defn update-shared-element-id
{:events [:chat.ui/update-shared-element-id]}
[{:keys [db]} shared-element-id]
{:db (assoc db :shared-element-id shared-element-id)})
(rf/defn navigate-to-lightbox
{:events [:chat.ui/navigate-to-lightbox]}
[{:keys [db]} shared-element-id screen-params]
(reagent/next-tick #(rf/dispatch [:navigate-to :lightbox screen-params]))
{:db (assoc db :shared-element-id shared-element-id)})
(rf/defn exit-lightbox-signal
{:events [:chat.ui/exit-lightbox-signal]}
[{:keys [db]} value]
{:db (assoc db :lightbox/exit-signal value)})
(rf/defn zoom-out-signal
{:events [:chat.ui/zoom-out-signal]}
[{:keys [db]} value]
{:db (assoc db :lightbox/zoom-out-signal value)})
(rf/defn orientation-change
{:events [:chat.ui/orientation-change]}
[{:keys [db]} value]
{:db (assoc db :lightbox/orientation value)})
(rf/defn lightbox-scale
{:events [:chat.ui/lightbox-scale]}
[{:keys [db]} value]
{:db (assoc db :lightbox/scale value)})
(rf/defn check-last-chat
{:events [:chat/check-last-chat]}
[{:keys [db]}]
@ -452,3 +419,15 @@
{:events [:chat.ui/scroll-to-bottom]}
[_]
{:effects.chat/scroll-to-bottom nil})
(rf/reg-event-fx :chat.ui/clear-sending-images
(fn [{:keys [db]}]
{:db (update-in db [:chat/inputs (:current-chat-id db) :metadata] assoc :sending-image {})}))
(rf/reg-event-fx :chat.ui/image-unselected
(fn [{:keys [db]} [original]]
(let [current-chat-id (:current-chat-id db)]
{:db (update-in db
[:chat/inputs current-chat-id :metadata :sending-image]
dissoc
(:uri original))})))

View File

@ -1,23 +0,0 @@
(ns status-im.contexts.chat.messenger.lightbox.events
(:require status-im.contexts.chat.messenger.lightbox.effects
[utils.re-frame :as rf]))
(rf/reg-event-fx :chat.ui/clear-sending-images
(fn [{:keys [db]}]
{:db (update-in db [:chat/inputs (:current-chat-id db) :metadata] assoc :sending-image {})}))
(rf/reg-event-fx :chat.ui/image-unselected
(fn [{:keys [db]} [original]]
(let [current-chat-id (:current-chat-id db)]
{:db (update-in db
[:chat/inputs current-chat-id :metadata :sending-image]
dissoc
(:uri original))})))
(rf/reg-event-fx :chat.ui/share-image
(fn [_ [uri]]
{:effects.chat/share-image uri}))
(rf/reg-event-fx :chat.ui/save-image-to-gallery
(fn [_ [uri on-success]]
{:effects.chat/save-image-to-gallery [uri on-success]}))

View File

@ -7,6 +7,8 @@
[status-im.constants :as constants]
[status-im.contexts.chat.messenger.messages.content.album.style :as style]
[status-im.contexts.chat.messenger.messages.content.image.view :as image]
[status-im.contexts.chat.messenger.messages.content.lightbox.utils :as lightbox-utils]
[status-im.contexts.chat.messenger.messages.content.lightbox.view :as lightbox]
[status-im.contexts.chat.messenger.messages.content.text.view :as text]
[utils.re-frame :as rf]
[utils.url :as url]))
@ -21,17 +23,19 @@
(defn album-message
[{:keys [albumize?] :as message} context on-long-press message-container-data]
(let [shared-element-id (rf/sub [:shared-element-id])
media-server-port (rf/sub [:mediaserver/port])
first-image (first (:album message))
album-style (if (> (:image-width first-image) (:image-height first-image))
:landscape
:portrait)
images-count (count (:album message))
(let [animation-shared-element-id (rf/sub [:animation-shared-element-id])
media-server-port (rf/sub [:mediaserver/port])
album-messages (:album message)
first-image (first album-messages)
album-style (if (> (:image-width first-image) (:image-height first-image))
:landscape
:portrait)
images-count (count album-messages)
;; album images are always square, except when we have 3 images, then they must be
;; rectangular
;; (portrait or landscape)
portrait? (and (= images-count rectangular-style-count) (= album-style :portrait))]
portrait? (and (= images-count rectangular-style-count)
(= album-style :portrait))]
(if (and albumize? (> images-count 1))
[:<>
[rn/view {:style {:margin-bottom 4}} [text/text-content first-image]]
@ -52,14 +56,21 @@
{:key (:message-id item)
:active-opacity 1
:on-long-press #(on-long-press message context)
:on-press #(rf/dispatch [:chat.ui/navigate-to-lightbox
(:message-id item)
{:messages (:album message)
:index index}])}
:on-press #(rf/dispatch
[:lightbox/navigate-to-lightbox
(:message-id item)
{:index index
:images (into []
(map
lightbox-utils/convert-message-to-lightbox-image
album-messages))
:bottom-text-component
[lightbox/bottom-text-for-lightbox
first-image]}])}
[fast-image/fast-image
{:style (style/image dimensions index portrait? images-count)
:source {:uri (url/replace-port (:image (:content item)) media-server-port)}
:native-ID (when (and (= shared-element-id (:message-id item))
:native-ID (when (and (= animation-shared-element-id (:message-id item))
(< index constants/max-album-photos))
:shared-element)}]
(when (and (> images-count constants/max-album-photos)
@ -71,11 +82,11 @@
:size :heading-2
:style {:color colors/white}}
(str "+" (- images-count (dec constants/max-album-photos)))]])]))
(:album message))]]
album-messages)]]
[:<>
(map-indexed
(fn [index item]
[:<> {:key (:message-id item)}
[image/image-message index item {:on-long-press #(on-long-press message context)}
message-container-data]])
(:album message))])))
album-messages)])))

View File

@ -4,6 +4,8 @@
[react-native.fast-image :as fast-image]
[react-native.safe-area :as safe-area]
[status-im.constants :as constants]
[status-im.contexts.chat.messenger.messages.content.lightbox.utils :as lightbox-utils]
[status-im.contexts.chat.messenger.messages.content.lightbox.view :as lightbox]
[status-im.contexts.chat.messenger.messages.content.text.view :as text]
[utils.re-frame :as rf]
[utils.url :as url]))
@ -39,7 +41,7 @@
image-height
max-container-width
max-container-height)
shared-element-id (rf/sub [:shared-element-id])
animation-shared-element-id (rf/sub [:animation-shared-element-id])
image-local-url (url/replace-port (:image content) (rf/sub [:mediaserver/port]))]
[:<>
(when (= index 0)
@ -48,13 +50,16 @@
{:active-opacity 1
:style {:margin-top 4}
:on-long-press on-long-press
:on-press #(rf/dispatch [:chat.ui/navigate-to-lightbox
:on-press #(rf/dispatch [:lightbox/navigate-to-lightbox
message-id
{:messages [message]
:index 0
:insets insets}])}
{:images [(lightbox-utils/convert-message-to-lightbox-image
message)]
:index 0
:insets insets
:bottom-text-component
[lightbox/bottom-text-for-lightbox message]}])}
[fast-image/fast-image
{:source {:uri image-local-url}
:style (merge dimensions {:border-radius 12})
:native-ID (when (= shared-element-id message-id) :shared-element)
:native-ID (when (= animation-shared-element-id message-id) :shared-element)
:accessibility-label :image-message}]]]))

View File

@ -0,0 +1,10 @@
(ns status-im.contexts.chat.messenger.messages.content.lightbox.style
(:require
[quo.foundations.colors :as colors]))
(defn bottom-text
[expandable-text?]
{:color colors/white
:margin-horizontal 20
:align-items (when-not expandable-text? :center)
:flex-grow 1})

View File

@ -0,0 +1,14 @@
(ns status-im.contexts.chat.messenger.messages.content.lightbox.utils
(:require
[utils.datetime :as datetime]
[utils.re-frame :as rf]))
(defn convert-message-to-lightbox-image
[{:keys [timestamp image-width image-height message-id from content]}]
(let [[primary-name _] (rf/sub [:contacts/contact-two-names-by-identity from])]
{:image (:image content)
:image-width image-width
:image-height image-height
:id message-id
:header primary-name
:description (when timestamp (datetime/to-short-str timestamp))}))

View File

@ -0,0 +1,18 @@
(ns status-im.contexts.chat.messenger.messages.content.lightbox.view
(:require
[oops.core :as oops]
[reagent.core :as reagent]
[status-im.common.lightbox.constants :as constants]
[status-im.contexts.chat.messenger.messages.content.lightbox.style :as style]
[status-im.contexts.chat.messenger.messages.content.text.view :as message-view]))
(defn bottom-text-for-lightbox
[_]
(let [text-height (reagent/atom 0)]
(fn [{:keys [content chat-id] :as _message}]
(let [expandable-text? (> @text-height (* constants/line-height 2))]
[message-view/render-parsed-text
{:content content
:chat-id chat-id
:style-override (style/bottom-text expandable-text?)
:on-layout #(reset! text-height (oops/oget % "nativeEvent.layout.height"))}]))))

View File

@ -75,12 +75,14 @@
(let [selected-tab (reagent/atom :overview)
on-tab-change #(reset! selected-tab %)]
(fn []
(let [collectible (rf/sub [:wallet/last-collectible-details])
(let [collectible (rf/sub [:wallet/last-collectible-details])
animation-shared-element-id (rf/sub [:animation-shared-element-id])
{:keys [collectible-data preview-url
collection-data]} collectible
collection-data id]} collectible
token-id (:token-id id)
{collection-image :image-url
collection-name :name} collection-data
{collectible-name :name} collectible-data]
collection-name :name} collection-data
{collectible-name :name} collectible-data]
[scroll-page/scroll-page
{:navigate-back? true
:height 148
@ -95,9 +97,26 @@
:picture preview-url}}
[rn/view {:style style/container}
[rn/view {:style style/preview-container}
[rn/image
{:source preview-url
:style style/preview}]]
[rn/touchable-opacity
{:active-opacity 1
:on-press #(rf/dispatch [:lightbox/navigate-to-lightbox
token-id
{:images [{:image preview-url
:image-width 300 ; collectibles don't have
; width/height but we need
; to pass something
:image-height 300 ; without it animation
; doesn't work smoothly and
; :border-radius not
; applied
:id token-id
:header collectible-name
:description collection-name}]
:index 0}])}
[rn/image
{:source preview-url
:style style/preview
:native-ID (when (= animation-shared-element-id token-id) :shared-element)}]]]
[header collectible-name collection-name collection-image]
[cta-buttons]
[quo/tabs

View File

@ -2,13 +2,13 @@
(:require
[legacy.status-im.ui.screens.screens :as old-screens]
[status-im.common.emoji-picker.view :as emoji-picker]
[status-im.common.lightbox.view :as lightbox]
[status-im.config :as config]
[status-im.contexts.chat.group-details.view :as group-details]
[status-im.contexts.chat.home.add-new-contact.scan.scan-profile-qr-page :as scan-profile-qr-page]
[status-im.contexts.chat.home.add-new-contact.views :as add-new-contact]
[status-im.contexts.chat.home.new-chat.view :as new-chat]
[status-im.contexts.chat.messenger.camera.view :as camera-screen]
[status-im.contexts.chat.messenger.lightbox.view :as lightbox]
[status-im.contexts.chat.messenger.messages.view :as chat]
[status-im.contexts.chat.messenger.photo-selector.view :as photo-selector]
[status-im.contexts.communities.actions.accounts-selection.view :as communities.accounts-selection]

View File

@ -23,7 +23,7 @@
;;view
(reg-root-key-sub :view-id :view-id)
(reg-root-key-sub :screen-params :navigation/screen-params)
(reg-root-key-sub :shared-element-id :shared-element-id)
(reg-root-key-sub :animation-shared-element-id :animation-shared-element-id)
;;bottom sheet
(reg-root-key-sub :bottom-sheet :bottom-sheet)