diff --git a/src/react_native/core.cljs b/src/react_native/core.cljs index c1d9676be1..2feea4ef5c 100644 --- a/src/react_native/core.cljs +++ b/src/react_native/core.cljs @@ -103,6 +103,8 @@ (if (fn? ret) ret js/undefined)) (bean/->js deps)))) +(def use-callback react/useCallback) + (defn use-effect-once [effect-fn] (use-effect effect-fn)) diff --git a/src/react_native/gesture.cljs b/src/react_native/gesture.cljs index 00593227de..4d08fe94ff 100644 --- a/src/react_native/gesture.cljs +++ b/src/react_native/gesture.cljs @@ -1,6 +1,8 @@ (ns react-native.gesture - (:require ["react-native-gesture-handler" :refer (GestureDetector Gesture gestureHandlerRootHOC)] - [reagent.core :as reagent])) + (:require ["react-native-gesture-handler" :refer + (GestureDetector Gesture gestureHandlerRootHOC TapGestureHandler State)] + [reagent.core :as reagent] + [oops.core :refer [oget]])) (def gesture-detector (reagent/adapt-react-class GestureDetector)) (def gesture-handler-root-hoc gestureHandlerRootHOC) @@ -12,3 +14,14 @@ (defn on-start [^js pan handler] (.onStart pan handler)) (defn on-end [^js pan handler] (.onEnd pan handler)) + +(def tap-gesture-handler + (reagent/adapt-react-class TapGestureHandler)) + +(def states + {:began (oget State "BEGAN") + :active (oget State "ACTIVE") + :cancelled (oget State "CANCELLED") + :end (oget State "END") + :failed (oget State "FAILED") + :undetermined (oget State "UNDETERMINED")}) diff --git a/src/status_im2/contexts/chat/lightbox/style.cljs b/src/status_im2/contexts/chat/lightbox/style.cljs index 85c3994e2f..4c2003697e 100644 --- a/src/status_im2/contexts/chat/lightbox/style.cljs +++ b/src/status_im2/contexts/chat/lightbox/style.cljs @@ -1,20 +1,23 @@ (ns status-im2.contexts.chat.lightbox.style - (:require [quo2.foundations.colors :as colors])) + (:require [quo2.foundations.colors :as colors] + [react-native.platform :as platform] + [react-native.reanimated :as reanimated])) -(defn container-view - [padding-top] +(def container-view {:background-color :black - :height "100%" - :padding-top padding-top}) + :height "100%"}) (defn top-view-container - [top-inset] - {:position :absolute - :left 20 - :top (+ 12 top-inset) - :z-index 1 - :flex-direction :row - :width "100%"}) + [top-inset opacity] + (reanimated/apply-animations-to-style + {:opacity opacity} + {:position :absolute + :left 20 + :top (if platform/ios? (+ 12 top-inset) 12) + :z-index 4 + :flex-direction :row + :width "100%"})) + (def close-container {:width 32 @@ -26,15 +29,18 @@ (def top-right-buttons {:position :absolute - :right 20 + :right 40 :flex-direction :row}) (defn gradient-container - [insets] - {:width "100%" - ;:height (+ (:bottom insets) 65) - :position :absolute - :bottom (:bottom insets)}) + [insets opacity] + (reanimated/apply-animations-to-style + {:opacity opacity} + {:width "100%" + :position :absolute + :bottom 0 + :padding-bottom (:bottom insets) + :z-index 3})) (def text-style {:color colors/white diff --git a/src/status_im2/contexts/chat/lightbox/view.cljs b/src/status_im2/contexts/chat/lightbox/view.cljs index fe3a67a85a..2f98279574 100644 --- a/src/status_im2/contexts/chat/lightbox/view.cljs +++ b/src/status_im2/contexts/chat/lightbox/view.cljs @@ -1,139 +1,191 @@ (ns status-im2.contexts.chat.lightbox.view - (:require [quo2.core :as quo] - [quo2.foundations.colors :as colors] - [react-native.core :as rn] - [react-native.fast-image :as fast-image] - [utils.re-frame :as rf] - [react-native.safe-area :as safe-area] - [reagent.core :as reagent] - [oops.core :as oops] - [status-im2.contexts.chat.lightbox.style :as style] - [utils.datetime :as datetime] - [react-native.linear-gradient :as linear-gradient])) + (:require + [quo2.core :as quo] + [quo2.foundations.colors :as colors] + [react-native.core :as rn] + [react-native.fast-image :as fast-image] + [react-native.reanimated :as reanimated] + [utils.re-frame :as rf] + [react-native.safe-area :as safe-area] + [reagent.core :as reagent] + [status-im2.contexts.chat.lightbox.style :as style] + [utils.datetime :as datetime] + [react-native.gesture :as gesture] + [oops.core :refer [oget]])) (def flat-list-ref (atom nil)) (def small-list-ref (atom nil)) (def small-image-size 40) +(def focused-image-size 56) + +(defn toggle-opacity + [opacity-value border-value transparent?] + (let [opacity (reanimated/get-shared-value opacity-value)] + (reanimated/set-shared-value opacity-value (reanimated/with-timing (if (= opacity 1) 0 1))) + (reanimated/set-shared-value border-value (reanimated/with-timing (if (= opacity 1) 0 12))) + (reset! transparent? (not @transparent?)))) (defn image - [message] - (let [shared-element-id (rf/sub [:shared-element-id]) - window-width (:width (rn/get-window)) - height (* (or (:image-height message) 1000) - (/ window-width (or (:image-width message) 1000)))] - [fast-image/fast-image - {:source {:uri (:image (:content message))} - :style {:width window-width - :height height - :border-radius 12} - :native-ID (when (= shared-element-id (:message-id message)) :shared-element)}])) + [message opacity-value border-value transparent?] + [:f> + (fn [] + (let [shared-element-id (rf/sub [:shared-element-id]) + width (:width (rn/get-window)) + height (* (or (:image-height message) 1000) + (/ width (or (:image-width message) 1000)))] + [gesture/tap-gesture-handler + {:on-handler-state-change (fn [e] + (when (= (oget e "nativeEvent.state") (:active gesture/states)) + (toggle-opacity opacity-value border-value transparent?)))} + [rn/view {:style {:flex-direction :row}} + [reanimated/view + {:style (reanimated/apply-animations-to-style + {:border-radius border-value} + {:overflow :hidden})} + [fast-image/fast-image + {:source {:uri (:image (:content message))} + :style {:width width + :height height} + :native-ID (when (= shared-element-id (:message-id message)) :shared-element)}]] + [rn/view {:style {:width 16}}]]]))]) + (defn get-item-layout [_ index] (let [window-width (:width (rn/get-window))] - #js {:length window-width :offset (* window-width index) :index index})) + #js {:length window-width :offset (* (+ window-width 16) index) :index index})) (defn get-small-item-layout [_ index] #js {:length small-image-size :offset (* (+ small-image-size 8) index) :index index}) (defn on-viewable-items-changed - [e] - (let [changed (-> e (oops/oget :changed) first)] - (.scrollToIndex ^js @small-list-ref #js {:animated true :index (oops/oget changed :index)}) - (rf/dispatch [:chat.ui/update-shared-element-id (:message-id (oops/oget changed :item))]))) + [e scroll-index] + (let [changed (-> e (oget :changed) first) + index (oget changed :index)] + (reset! scroll-index index) + (.scrollToIndex ^js @small-list-ref #js {:animated true :index index}) + (rf/dispatch [:chat.ui/update-shared-element-id (:message-id (oget changed :item))]))) (defn top-view - [{:keys [from timestamp]} insets] - (let [display-name (first (rf/sub [:contacts/contact-two-names-by-identity from]))] - [rn/view - {:style (style/top-view-container (:top insets))} - [rn/touchable-opacity - {:active-opacity 1 - :on-press #(rf/dispatch [:navigate-back]) - :style style/close-container} - [quo/icon :close {:size 20 :color colors/white}]] - [rn/view {:style {:margin-left 12}} - [quo/text - {:weight :semi-bold - :size :paragraph-1 - :style {:color colors/white}} display-name] - [quo/text - {:weight :medium - :size :paragraph-2 - :style {:color colors/neutral-40}} (datetime/to-short-str timestamp)]] - [rn/view {:style style/top-right-buttons} - [rn/touchable-opacity - {:active-opacity 1 - :on-press #(js/alert "to be implemented") - :style (merge style/close-container {:margin-right 12})} - [quo/icon :share {:size 20 :color colors/white}]] - [rn/touchable-opacity - {:active-opacity 1 - :on-press #(js/alert "to be implemented") - :style style/close-container} - [quo/icon :options {:size 20 :color colors/white}]]]])) - + [{:keys [from timestamp]} insets opacity-value transparent?] + [:f> + (fn [] + (let [display-name (first (rf/sub [:contacts/contact-two-names-by-identity from]))] + [reanimated/view + {:style (style/top-view-container (:top insets) opacity-value)} + [rn/touchable-opacity + {:on-press #(when-not @transparent? (rf/dispatch [:navigate-back])) + :style style/close-container} + [quo/icon :close {:size 20 :color colors/white}]] + [rn/view {:style {:margin-left 12}} + [quo/text + {:weight :semi-bold + :size :paragraph-1 + :style {:color colors/white}} display-name] + [quo/text + {:weight :medium + :size :paragraph-2 + :style {:color colors/neutral-40}} (datetime/to-short-str timestamp)]] + [rn/view {:style style/top-right-buttons} + [rn/touchable-opacity + {:active-opacity 1 + :on-press #(js/alert "to be implemented") + :style (merge style/close-container {:margin-right 12})} + [quo/icon :share {:size 20 :color colors/white}]] + [rn/touchable-opacity + {:active-opacity 1 + :on-press #(js/alert "to be implemented") + :style style/close-container} + [quo/icon :options {:size 20 :color colors/white}]]]]))]) (defn small-image - [item] - [fast-image/fast-image - {:source {:uri (:image (:content item))} - :style {:width small-image-size - :height small-image-size - :border-radius 10}}]) + [item index scroll-index] + [:f> + (fn [] + (let [size (if (= @scroll-index index) focused-image-size small-image-size) + size-value (reanimated/use-shared-value size)] + (reanimated/set-shared-value size-value (reanimated/with-timing size)) + [rn/touchable-opacity + {:active-opacity 1 + :on-press (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}))} + [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 - [messages insets] - (let [text (get-in (first messages) [:content :text]) - padding-horizontal (- (/ (:width (rn/get-window)) 2) (/ small-image-size 2))] - [linear-gradient/linear-gradient - {:colors [:black :transparent] - :start {:x 0 :y 1} - :end {:x 0 :y 0} - :style (style/gradient-container insets)} - [rn/text - {:style style/text-style} text] - [rn/flat-list - {:ref #(reset! small-list-ref %) - :key-fn :message-id - :data messages - :render-fn small-image - :horizontal true - :get-item-layout get-small-item-layout - :separator [rn/view {:style {:width 8}}] - :content-container-style {:padding-vertical 12 - :padding-horizontal padding-horizontal}}]])) + [messages index scroll-index insets opacity-value] + [:f> + (fn [] + (let [text (get-in (first messages) [:content :text]) + padding-horizontal (- (/ (:width (rn/get-window)) 2) (/ focused-image-size 2))] + [reanimated/linear-gradient + {:colors [:black :transparent] + :start {:x 0 :y 1} + :end {:x 0 :y 0} + :style (style/gradient-container insets opacity-value)} + [rn/text + {:style style/text-style} text] + [rn/flat-list + {:ref #(reset! small-list-ref %) + :key-fn :message-id + :style {:height 68} + :data messages + :render-fn (fn [item index] [small-image item index scroll-index]) + :horizontal true + :get-item-layout get-small-item-layout + :separator [rn/view {:style {:width 8}}] + :initial-scroll-index index + :content-container-style {:padding-vertical 12 + :padding-horizontal padding-horizontal + :align-items :center + :justify-content :center}}]]))]) + (defn lightbox [] - (let [{:keys [messages index]} (rf/sub [:get-screen-params]) - ;; 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 [(nth messages index)])] - (reset! data messages) - ;; We use setTimeout to enqueue `scrollToIndex` until the `data` has been updated. - (js/setTimeout #(do - (.scrollToIndex ^js @flat-list-ref #js {:animated false :index index}) - (.scrollToIndex ^js @small-list-ref #js {:animated false :index index})) - 0) - [safe-area/consumer - (fn [insets] - [rn/view - {:style (style/container-view (:top insets))} - [top-view (first messages) insets] - [rn/flat-list - {:ref #(reset! flat-list-ref %) - :key-fn :message-id - :data @data - :render-fn image - :horizontal true - :paging-enabled true - :get-item-layout get-item-layout - :viewability-config {:view-area-coverage-percent-threshold 50} - :on-viewable-items-changed on-viewable-items-changed - :content-container-style {:justify-content :center - :align-items :center}}] - [bottom-view messages insets]])])) + [:f> + (fn [] + (let [{:keys [messages index]} (rf/sub [:get-screen-params]) + ;; 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 [(nth messages index)]) + scroll-index (reagent/atom index) + transparent? (reagent/atom false) + opacity-value (reanimated/use-shared-value 1) + border-value (reanimated/use-shared-value 12) + window-width (:width (rn/get-window))] + (reset! data messages) + [safe-area/consumer + (fn [insets] + [:f> + (fn [] + ;; We use setTimeout to enqueue `scrollToIndex` until the `data` has been updated. + (js/setTimeout #(.scrollToIndex ^js @flat-list-ref #js {:animated false :index index}) 0) + [rn/view {:style style/container-view} + [top-view (first messages) insets opacity-value transparent?] + [rn/flat-list + {:ref #(reset! flat-list-ref %) + :key-fn :message-id + :style {:width (+ window-width 16)} + :data @data + :render-fn (fn [item] [image item opacity-value border-value + transparent?]) + :horizontal true + :paging-enabled true + :get-item-layout get-item-layout + :viewability-config {:view-area-coverage-percent-threshold 50} + :on-viewable-items-changed (rn/use-callback (fn [e] + (on-viewable-items-changed e + scroll-index))) + :content-container-style {:justify-content :center + :align-items :center}}] + [bottom-view messages index scroll-index insets opacity-value]])])]))]) + diff --git a/src/status_im2/contexts/chat/messages/content/album/view.cljs b/src/status_im2/contexts/chat/messages/content/album/view.cljs index ec2e1291f3..fe79d2aaf5 100644 --- a/src/status_im2/contexts/chat/messages/content/album/view.cljs +++ b/src/status_im2/contexts/chat/messages/content/album/view.cljs @@ -10,31 +10,6 @@ (def rectangular-style-count 3) -(defn border-tlr - [index] - (when (= index 0) 12)) - -(defn border-trr - [index count album-style] - (when (or (and (= index 1) (not= count rectangular-style-count)) - (and (= index 0) (= count rectangular-style-count) (= album-style :landscape)) - (and (= index 1) (= count rectangular-style-count) (= album-style :portrait))) - 12)) - -(defn border-blr - [index count album-style] - (when (or (and (= index 0) (< count rectangular-style-count)) - (and (= index 2) (> count rectangular-style-count)) - (and (= index 1) (= count rectangular-style-count) (= album-style :landscape)) - (and (= index 0) (= count rectangular-style-count) (= album-style :portrait))) - 12)) - -(defn border-brr - [index count] - (when (or (and (= index 1) (< count rectangular-style-count)) - (and (= index (- (min count constants/max-album-photos) 1)) (> count 2))) - 12)) - (defn find-size [size-arr album-style] (if (= album-style :landscape) @@ -79,8 +54,7 @@ (when (and (> images-count constants/max-album-photos) (= index (- constants/max-album-photos 1))) [rn/view - {:style (merge style/overlay - {:border-bottom-right-radius (border-brr index images-count)})} + {:style style/overlay} [quo/text {:weight :bold :size :heading-2 diff --git a/src/status_im2/contexts/chat/photo_selector/album_selector/view.cljs b/src/status_im2/contexts/chat/photo_selector/album_selector/view.cljs index ad611f0e5c..0534b0e89c 100644 --- a/src/status_im2/contexts/chat/photo_selector/album_selector/view.cljs +++ b/src/status_im2/contexts/chat/photo_selector/album_selector/view.cljs @@ -21,7 +21,12 @@ {:source {:uri uri} :style style/cover}] [rn/view {:style {:margin-left 12}} - [quo/text {:weight :medium} title] + [quo/text + {:weight :medium + :ellipsize-mode :tail + :number-of-lines 1 + :style {:margin-right 50}} + title] [quo/text {:size :paragraph-2 :style {:color (colors/theme-colors colors/neutral-50 colors/neutral-40)}} @@ -40,6 +45,10 @@ {:label title :container-style style/divider}])) +(defn key-fn + [item index] + (str (:title item) index)) + (defn album-selector [] [:f> @@ -61,4 +70,4 @@ :render-section-header-fn section-header :style {:margin-top 12} :content-container-style {:padding-bottom 40} - :key-fn :title}]]))]) + :key-fn key-fn}]]))]) diff --git a/src/status_im2/contexts/chat/photo_selector/style.cljs b/src/status_im2/contexts/chat/photo_selector/style.cljs index 54fe25dbc9..98bb5f5375 100644 --- a/src/status_im2/contexts/chat/photo_selector/style.cljs +++ b/src/status_im2/contexts/chat/photo_selector/style.cljs @@ -29,8 +29,7 @@ :position :absolute :right 20}) -(defn camera-button-container - [] +(def camera-button-container {:background-color (colors/theme-colors colors/neutral-10 colors/neutral-80) :width 32 :height 32 @@ -82,4 +81,3 @@ :border-radius 8 :top 8 :right 8}) - diff --git a/src/status_im2/contexts/chat/photo_selector/view.cljs b/src/status_im2/contexts/chat/photo_selector/view.cljs index 37252737a6..a91eece60c 100644 --- a/src/status_im2/contexts/chat/photo_selector/view.cljs +++ b/src/status_im2/contexts/chat/photo_selector/view.cljs @@ -80,15 +80,23 @@ (inc (utils/first-index #(= (:uri item) (:uri %)) @selected))])]) (defn album-title - [photos? selected-album] + [photos? selected-album selected temporary-selected] [rn/touchable-opacity {:style (style/title-container) :active-opacity 1 :accessibility-label :album-title - :on-press #(rf/dispatch (if photos? - [:open-modal :album-selector] - [:navigate-back]))} - [quo/text {:weight :medium} selected-album] + :on-press (fn [] + (if photos? + (do + (reset! temporary-selected @selected) + (rf/dispatch [:open-modal :album-selector])) + (rf/dispatch [:navigate-back])))} + [quo/text + {:weight :medium + :ellipsize-mode :tail + :number-of-lines 1 + :style {:max-width 150}} + selected-album] [rn/view {:style (style/chevron-container)} [quo/icon (if photos? :i/chevron-down :i/chevron-up) {:color (colors/theme-colors colors/neutral-100 colors/white)}]]]) @@ -96,41 +104,47 @@ (defn photo-selector [] [:f> - (fn [] - (let [selected-images (rf/sub [:chats/sending-image]) - selected-album (or (rf/sub [:camera-roll/selected-album]) (i18n/label :t/recent)) - selected (reagent/atom [])] - (rn/use-effect - (fn [] - (rf/dispatch [:chat.ui/camera-roll-get-photos 20 nil selected-album]) - (if selected-images - (reset! selected (vec (vals selected-images))) - (reset! selected []))) - [selected-album]) - [safe-area/consumer - (fn [insets] - (let [window-width (:width (rn/get-window)) - camera-roll-photos (rf/sub [:camera-roll/photos]) - end-cursor (rf/sub [:camera-roll/end-cursor]) - loading? (rf/sub [:camera-roll/loading-more]) - has-next-page? (rf/sub [:camera-roll/has-next-page])] - [rn/view {:style {:flex 1}} - [rn/view - {:style style/buttons-container} - [album-title true selected-album] - [clear-button selected]] - [rn/flat-list - {:key-fn identity - :render-fn image - :render-data {:window-width window-width :selected selected} - :data camera-roll-photos - :num-columns 3 - :content-container-style {:width "100%" - :padding-bottom (+ (:bottom insets) 100) - :padding-top 80} - :on-end-reached #(rf/dispatch [:camera-roll/on-end-reached end-cursor - selected-album loading? - has-next-page?])}] - [bottom-gradient selected-images insets selected]]))]))]) - - + (let [temporary-selected (reagent/atom [])] ; used when switching albums + (fn [] + (let [selected (reagent/atom []) ; currently selected + selected-images (rf/sub [:chats/sending-image]) ; already selected and dispatched + selected-album (or (rf/sub [:camera-roll/selected-album]) (i18n/label :t/recent))] + (rn/use-effect + (fn [] + (rf/dispatch [:chat.ui/camera-roll-get-photos 20 nil selected-album]) + (if (seq selected-images) + (reset! selected (vec (vals selected-images))) + (reset! selected @temporary-selected))) + [selected-album]) + [safe-area/consumer + (fn [insets] + (let [window-width (:width (rn/get-window)) + camera-roll-photos (rf/sub [:camera-roll/photos]) + end-cursor (rf/sub [:camera-roll/end-cursor]) + loading? (rf/sub [:camera-roll/loading-more]) + has-next-page? (rf/sub [:camera-roll/has-next-page])] + [rn/view {:style {:flex 1}} + [rn/view + {:style style/buttons-container} + (when platform/android? + [rn/touchable-opacity + {:active-opacity 1 + :on-press #(rf/dispatch [:navigate-back]) + :style style/camera-button-container} + [quo/icon :i/close + {:size 20 :color (colors/theme-colors colors/black colors/white)}]]) + [album-title true selected-album selected temporary-selected] + [clear-button selected]] + [rn/flat-list + {:key-fn identity + :render-fn image + :render-data {:window-width window-width :selected selected} + :data camera-roll-photos + :num-columns 3 + :content-container-style {:width "100%" + :padding-bottom (+ (:bottom insets) 100) + :padding-top 80} + :on-end-reached #(rf/dispatch [:camera-roll/on-end-reached end-cursor + selected-album loading? + has-next-page?])}] + [bottom-gradient selected-images insets selected]]))])))]) diff --git a/status-go-version.json b/status-go-version.json index eb5d7686df..4c74afc64c 100644 --- a/status-go-version.json +++ b/status-go-version.json @@ -3,7 +3,7 @@ "_comment": "Instead use: scripts/update-status-go.sh ", "owner": "status-im", "repo": "status-go", - "version": "v0.126.1", - "commit-sha1": "cb624b0cc14d95e46754210c9153f8a0444d2845", - "src-sha256": "1wrjlzwqj7qzsvh2aba4fjfp29ckx416lkxsk3c7las88ji93wzn" + "version": "v0.128.0", + "commit-sha1": "82596b2b67f82ae6329ad88e34be6240d08c8ff1", + "src-sha256": "17n461i2cglvs66rpqxdxpka4k9ld9ds2ha6scdli2nrfg9jb4q3" }