Album Reactions (#15208)

* feat: album actions
This commit is contained in:
Omar Basem 2023-03-02 20:11:17 +04:00 committed by GitHub
parent 11b6702939
commit 4dfd3af7f5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 120 additions and 138 deletions

View File

@ -5,6 +5,8 @@
[react-native.fast-image :as fast-image] [react-native.fast-image :as fast-image]
[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.text.view :as text]
[utils.re-frame :as rf])) [utils.re-frame :as rf]))
(def rectangular-style-count 3) (def rectangular-style-count 3)
@ -16,7 +18,7 @@
{:width (second size-arr) :height (first size-arr) :album-style album-style})) {:width (second size-arr) :height (first size-arr) :album-style album-style}))
(defn album-message (defn album-message
[message] [{:keys [albumize?] :as message} context on-long-press]
(let [shared-element-id (rf/sub [:shared-element-id]) (let [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))
@ -27,42 +29,48 @@
;; (portrait or landscape) ;; (portrait or landscape)
portrait? (and (= images-count rectangular-style-count) (= album-style :portrait)) portrait? (and (= images-count rectangular-style-count) (= album-style :portrait))
text (:text (:content first-image))] text (:text (:content first-image))]
[:<> (if (and albumize? (> images-count 1))
;; This text comp is temporary. Should later use [:<>
;; `status-im2.contexts.chat.messages.content.text.view` (when (not= text "placeholder")
(when (not= text "placeholder") [quo/text {:style {:margin-bottom 10}} text]) [rn/view {:style {:margin-bottom 10}} [text/text-content first-image context]])
[rn/view [rn/view
{:style (style/album-container portrait?)} {:style (style/album-container portrait?)}
(map-indexed (map-indexed
(fn [index item] (fn [index item]
(let [images-size-key (if (< images-count constants/max-album-photos) images-count :default) (let [images-size-key (if (< images-count constants/max-album-photos) images-count :default)
size (get-in constants/album-image-sizes [images-size-key index]) size (get-in constants/album-image-sizes [images-size-key index])
dimensions (if (not= images-count rectangular-style-count) dimensions (if (not= images-count rectangular-style-count)
{:width size :height size} {:width size :height size}
(find-size size album-style))] (find-size size album-style))]
[rn/touchable-opacity [rn/touchable-opacity
{:key (:message-id item) {:key (:message-id item)
:active-opacity 1 :active-opacity 1
;; issue: https://github.com/status-im/status-mobile/issues/14995 ;; issue: https://github.com/status-im/status-mobile/issues/14995
:on-long-press #(js/alert "Action drawer for albums is not supported yet") :on-long-press #(on-long-press message context)
:on-press (fn [] :on-press (fn []
(rf/dispatch [:chat.ui/update-shared-element-id (:message-id item)]) (rf/dispatch [:chat.ui/update-shared-element-id (:message-id item)])
(js/setTimeout #(rf/dispatch [:navigate-to :lightbox (js/setTimeout #(rf/dispatch [:navigate-to :lightbox
{:messages (:album message) :index index}]) {:messages (:album message)
100))} :index index}])
[fast-image/fast-image 100))}
{:style (style/image dimensions index portrait? images-count) [fast-image/fast-image
:source {:uri (:image (:content item))} {:style (style/image dimensions index portrait? images-count)
:native-ID (when (and (= shared-element-id (:message-id item)) :source {:uri (:image (:content item))}
(< index constants/max-album-photos)) :native-ID (when (and (= shared-element-id (:message-id item))
:shared-element)}] (< index constants/max-album-photos))
(when (and (> images-count constants/max-album-photos) :shared-element)}]
(= index (- constants/max-album-photos 1))) (when (and (> images-count constants/max-album-photos)
[rn/view (= index (- constants/max-album-photos 1)))
{:style style/overlay} [rn/view
[quo/text {:style style/overlay}
{:weight :bold [quo/text
:size :heading-2 {:weight :bold
:style {:color colors/white}} :size :heading-2
(str "+" (- images-count (dec constants/max-album-photos)))]])])) :style {:color colors/white}}
(:album message))]])) (str "+" (- images-count (dec constants/max-album-photos)))]])]))
(:album message))]]
[:<>
(map-indexed
(fn [index item]
[image/image-message index item context #(on-long-press message context)])
(:album message))])))

View File

@ -1,25 +1,19 @@
(ns status-im2.contexts.chat.messages.content.image.view (ns status-im2.contexts.chat.messages.content.image.view
(:require (:require
[quo2.core :as quo]
[react-native.core :as rn] [react-native.core :as rn]
[react-native.fast-image :as fast-image] [react-native.fast-image :as fast-image]
[status-im2.constants :as constants] [status-im2.constants :as constants]
[utils.re-frame :as rf])) [utils.re-frame :as rf]
[status-im2.contexts.chat.messages.content.text.view :as text]))
(defn calculate-dimensions (defn calculate-dimensions
[width height] [width height]
(let [max-width (if (> width height) (* 2 constants/image-size) (* 1.5 constants/image-size)) (let [max-width (if (> width height) (* 2 constants/image-size) (* 1.5 constants/image-size))
max-height (if (> width height) (* 1.5 constants/image-size) (* 2 constants/image-size))] max-height (if (> width height) (* 1.5 constants/image-size) (* 2 constants/image-size))]
(if (> height width) {:width (min width max-width) :height (min height max-height)}))
(let [calculated-height (* (min height max-height) (/ (max width max-width) width))
calculated-width (* (max width max-width) (/ (min height max-height) height))]
{:width calculated-width :height calculated-height})
(let [calculated-height (* (max height max-height) (/ (min width max-width) width))
calculated-width (* (min width max-width) (/ (max height max-height) height))]
{:width calculated-width :height calculated-height}))))
(defn image-message (defn image-message
[_ {:keys [content image-width image-height message-id] :as message} context on-long-press] [index {:keys [content image-width image-height message-id] :as message} context on-long-press]
(let [dimensions (calculate-dimensions (or image-width 1000) (or image-height 1000)) (let [dimensions (calculate-dimensions (or image-width 1000) (or image-height 1000))
text (:text content)] text (:text content)]
(fn [] (fn []
@ -27,15 +21,15 @@
[rn/touchable-opacity [rn/touchable-opacity
{:active-opacity 1 {:active-opacity 1
:key message-id :key message-id
:on-long-press #(on-long-press message context) :style {:margin-top (when (> index 0) 20)}
:on-long-press on-long-press
:on-press (fn [] :on-press (fn []
(rf/dispatch [:chat.ui/update-shared-element-id message-id]) (rf/dispatch [:chat.ui/update-shared-element-id message-id])
(js/setTimeout #(rf/dispatch [:navigate-to :lightbox (js/setTimeout #(rf/dispatch [:navigate-to :lightbox
{:messages [message] :index 0}]) {:messages [message] :index 0}])
100))} 100))}
;; This text comp is temporary. Should later use (when (and (not= text "placeholder") (= index 0))
;; `status-im2.contexts.chat.messages.content.text.view` [rn/view {:style {:margin-bottom 10}} [text/text-content message context]])
(when (not= text "placeholder") [quo/text {:style {:margin-bottom 10}} text])
[fast-image/fast-image [fast-image/fast-image
{:source {:uri (:image content)} {:source {:uri (:image content)}
:style (merge dimensions {:border-radius 12}) :style (merge dimensions {:border-radius 12})

View File

@ -6,10 +6,8 @@
[status-im2.contexts.chat.messages.drawers.view :as drawers])) [status-im2.contexts.chat.messages.drawers.view :as drawers]))
(defn message-reactions-row (defn message-reactions-row
[chat-id message-id messages-ids] [chat-id message-id]
(let [reactions (if messages-ids (let [reactions (rf/sub [:chats/message-reactions message-id chat-id])]
(mapcat #(rf/sub [:chats/message-reactions % chat-id]) messages-ids)
(rf/sub [:chats/message-reactions message-id chat-id]))]
(when (seq reactions) (when (seq reactions)
[rn/view {:margin-left 52 :margin-bottom 12 :flex-direction :row} [rn/view {:margin-left 52 :margin-bottom 12 :flex-direction :row}
(for [{:keys [own emoji-id quantity emoji-reaction-id] :as emoji-reaction} reactions] (for [{:keys [own emoji-id quantity emoji-reaction-id] :as emoji-reaction} reactions]
@ -30,12 +28,8 @@
:accessibility-label (str "emoji-reaction-" emoji-id)}]]) :accessibility-label (str "emoji-reaction-" emoji-id)}]])
[quo/add-reaction [quo/add-reaction
{:on-press (fn [] {:on-press (fn []
;; issue: https://github.com/status-im/status-mobile/issues/14995 (rf/dispatch [:dismiss-keyboard])
(if messages-ids (rf/dispatch
(js/alert "Reactions for albums is not yet supported") [:bottom-sheet/show-sheet
(do {:content (fn [] [drawers/reactions
(rf/dispatch [:dismiss-keyboard]) {:chat-id chat-id :message-id message-id}])}]))}]])))
(rf/dispatch
[:bottom-sheet/show-sheet
{:content (fn [] [drawers/reactions
{:chat-id chat-id :message-id message-id}])}]))))}]])))

View File

@ -76,12 +76,6 @@
constants/content-type-contact-request [not-implemented/not-implemented constants/content-type-contact-request [not-implemented/not-implemented
[old-message/system-contact-request message-data]]))) [old-message/system-contact-request message-data]])))
(defn message-on-long-press
[message-data context]
(rf/dispatch [:dismiss-keyboard])
(rf/dispatch [:bottom-sheet/show-sheet
{:content (drawers/reactions-and-actions message-data context)}]))
(defn on-long-press (defn on-long-press
[message-data context] [message-data context]
(rf/dispatch [:dismiss-keyboard]) (rf/dispatch [:dismiss-keyboard])
@ -102,7 +96,7 @@
outgoing (if (= content-type constants/content-type-album) outgoing (if (= content-type constants/content-type-album)
(:outgoing first-image) (:outgoing first-image)
outgoing) outgoing)
context (assoc context :on-long-press #(message-on-long-press message-data context)) context (assoc context :on-long-press #(on-long-press message-data context))
response-to (:response-to content)] response-to (:response-to content)]
[rn/touchable-highlight [rn/touchable-highlight
{:accessibility-label (if (and outgoing (= outgoing-status :sending)) {:accessibility-label (if (and outgoing (= outgoing-status :sending))
@ -154,9 +148,7 @@
[status/status outgoing-status])]]]])))]) [status/status outgoing-status])]]]])))])
(defn message-with-reactions (defn message-with-reactions
[{:keys [pinned-by mentioned in-pinned-view? content-type [{:keys [pinned-by mentioned in-pinned-view? content-type last-in-group? message-id] :as message-data}
last-in-group? message-id messages-ids]
:as message-data}
{:keys [chat-id] :as context}] {:keys [chat-id] :as context}]
[rn/view [rn/view
{:style (style/message-container in-pinned-view? pinned-by mentioned last-in-group?) {:style (style/message-container in-pinned-view? pinned-by mentioned last-in-group?)
@ -168,4 +160,4 @@
content-type) content-type)
[system-message-content message-data] [system-message-content message-data]
[user-message-content message-data context]) [user-message-content message-data context])
[reactions/message-reactions-row chat-id message-id messages-ids]]) [reactions/message-reactions-row chat-id message-id]])

View File

@ -20,7 +20,8 @@
(assoc message-data :pinned message-not-pinned?)])))) (assoc message-data :pinned message-not-pinned?)]))))
(defn get-actions (defn get-actions
[{:keys [outgoing content pinned outgoing-status deleted? deleted-for-me?] :as message-data} [{:keys [outgoing content pinned outgoing-status deleted? deleted-for-me? content-type]
:as message-data}
{:keys [edit-enabled show-input? community? can-delete-message-for-everyone? {:keys [edit-enabled show-input? community? can-delete-message-for-everyone?
message-pin-enabled group-chat group-admin?]}] message-pin-enabled group-chat group-admin?]}]
(concat (concat
@ -36,7 +37,7 @@
:label (i18n/label :t/message-reply) :label (i18n/label :t/message-reply)
:icon :i/reply :icon :i/reply
:id :reply}]) :id :reply}])
(when-not (or deleted? deleted-for-me?) (when (and (not (or deleted? deleted-for-me?)) (not= (get content :text) "placeholder"))
[{:type :main [{:type :main
:on-press #(react/copy-to-clipboard :on-press #(react/copy-to-clipboard
(components.reply/get-quoted-text-with-mentions (components.reply/get-quoted-text-with-mentions
@ -44,7 +45,8 @@
:label (i18n/label :t/copy-text) :label (i18n/label :t/copy-text)
:icon :i/copy :icon :i/copy
:id :copy}]) :id :copy}])
(when message-pin-enabled ;; pinning images are temporarily disabled
(when (and message-pin-enabled (not= content-type constants/content-type-image))
[{:type :main [{:type :main
:on-press #(pin-message message-data) :on-press #(pin-message message-data)
:label (i18n/label (if pinned :label (i18n/label (if pinned
@ -126,18 +128,22 @@
icon]])))])) icon]])))]))
(defn reactions-and-actions (defn reactions-and-actions
[{:keys [message-id outgoing-status deleted? deleted-for-me?] :as message-data} [message-data
{:keys [chat-id] :as context}] {:keys [chat-id] :as context}]
(fn [] (fn []
(let [actions (get-actions message-data context) (let [data (if (contains? message-data :album-id)
main-actions (filter #(= (:type %) :main) actions) (first (:album message-data))
danger-actions (filter #(= (:type %) :danger) actions) message-data)
admin-actions (filter #(= (:type %) :admin) actions)] {:keys [message-id deleted? deleted-for-me?]} data
outgoing-status (:outgoing-status data)
actions (get-actions data context)
main-actions (filter #(= (:type %) :main) actions)
danger-actions (filter #(= (:type %) :danger) actions)
admin-actions (filter #(= (:type %) :admin) actions)]
[:<> [:<>
;; REACTIONS ;; REACTIONS
(when (and (not= outgoing-status :sending) (not (or deleted? deleted-for-me?))) (when (and (not= outgoing-status :sending) (not (or deleted? deleted-for-me?)))
[reactions {:chat-id chat-id :message-id message-id}]) [reactions {:chat-id chat-id :message-id message-id}])
;; MAIN ACTIONS ;; MAIN ACTIONS
[rn/view {:style {:padding-horizontal 8}} [rn/view {:style {:padding-horizontal 8}}
(for [action main-actions] (for [action main-actions]

View File

@ -19,7 +19,7 @@
so we bucket both in 1999-12-31" so we bucket both in 1999-12-31"
[{:keys [acc last-timestamp last-datemark]} {:keys [whisper-timestamp datemark] :as msg}] [{:keys [acc last-timestamp last-datemark]} {:keys [whisper-timestamp datemark] :as msg}]
(cond (cond
(empty? acc) ; initial element (empty? acc) ; initial element
{:last-timestamp whisper-timestamp {:last-timestamp whisper-timestamp
:last-datemark datemark :last-datemark datemark
:acc (conj acc msg)} :acc (conj acc msg)}
@ -112,39 +112,27 @@
(defn albumize-messages (defn albumize-messages
[messages] [messages]
(get (get
(reduce (reduce (fn [{:keys [messages albums]} message]
(fn [{:keys [messages albums]} message] (let [album-id (:album-id message)
(let [album-id (:album-id message) albums (cond-> albums album-id (update album-id conj message))
;; check if this image is the first image in an album (which is not albumized yet) messages (if album-id
add-text? (when (and album-id (not (:albumize? message)) (> (count (get albums album-id)) 0)) (conj (filterv #(not= album-id (:album-id %)) messages)
(not (some #(= false %) {:album (get albums album-id)
(mapv #(< (:timestamp message) (:timestamp %)) :album-id album-id
(get albums album-id))))) :albumize? (:albumize? message)
albums (cond-> albums album-id (update album-id conj message)) :message-id (:message-id message)
;; keep text of the first album image only :deleted? (:deleted? message)
message (if (or add-text? (<= (count (get albums album-id)) 1)) :deleted-for-me? (:deleted-for-me? message)
message :deleted-by (:deleted-by message)
(assoc-in message [:content :text] nil)) :from (:from message)
messages (if (and (> (count (get albums album-id)) 1) (:albumize? message)) :timestamp-str (:timestamp-str message)
(conj (filterv #(not= album-id (:album-id %)) messages) :content-type constants/content-type-album})
{:album (get albums album-id) (conj messages message))]
:album-id album-id {:messages messages
:albumize? (:albumize? message) :albums albums}))
:messages-ids (mapv :message-id (get albums album-id)) {:messages []
:message-id album-id :albums {}}
:content-type constants/content-type-album}) messages)
;; remove text of other images in an album
(if add-text?
(conj (mapv #(when (= (:album-id %) album-id)
(assoc-in % [:content :text] nil))
messages)
message)
(conj messages message)))]
{:messages messages
:albums albums}))
{:messages []
:albums {}}
messages)
:messages)) :messages))
(re-frame/reg-sub (re-frame/reg-sub

View File

@ -7,25 +7,25 @@
[utils.re-frame :as rf])) [utils.re-frame :as rf]))
(def messages-state (def messages-state
[{:message-id "0x111" :album-id "abc" :albumize? true} [{:message-id "0x111" :album-id "abc" :albumize? true :from :xyz :timestamp-str "14:00"}
{:message-id "0x222" :album-id "abc" :albumize? true} {:message-id "0x222" :album-id "abc" :albumize? true :from :xyz :timestamp-str "14:00"}
{:message-id "0x333" :album-id "abc" :albumize? true} {:message-id "0x333" :album-id "abc" :albumize? true :from :xyz :timestamp-str "14:00"}
{:message-id "0x444" :album-id "abc" :albumize? true} {:message-id "0x444" :album-id "abc" :albumize? true :from :xyz :timestamp-str "14:00"}])
{:message-id "0x555" :album-id "edf" :timestamp 10 :content {:text "Wassup!"}}
{:message-id "0x666" :album-id "edf" :timestamp 20 :content {:text "Wassup!"}}])
(def messages-albumized-state (def messages-albumized-state
[{:album [{:message-id "0x444" :album-id "abc" :albumize? true} [{:album [{:message-id "0x444" :album-id "abc" :albumize? true :from :xyz :timestamp-str "14:00"}
{:message-id "0x333" :album-id "abc" :albumize? true} {:message-id "0x333" :album-id "abc" :albumize? true :from :xyz :timestamp-str "14:00"}
{:message-id "0x222" :album-id "abc" :albumize? true} {:message-id "0x222" :album-id "abc" :albumize? true :from :xyz :timestamp-str "14:00"}
{:message-id "0x111" :album-id "abc" :albumize? true}] {:message-id "0x111" :album-id "abc" :albumize? true :from :xyz :timestamp-str "14:00"}]
:album-id "abc" :album-id "abc"
:albumize? true :albumize? true
:message-id "abc" :message-id "0x444"
:messages-ids ["0x444" "0x333" "0x222" "0x111"] :deleted? nil
:content-type constants/content-type-album} :deleted-for-me? nil
{:message-id "0x555" :album-id "edf" :timestamp 10 :content {:text "Wassup!"}} :deleted-by nil
{:message-id "0x666" :album-id "edf" :timestamp 20 :content {:text nil}}]) :from :xyz
:timestamp-str "14:00"
:content-type constants/content-type-album}])
(deftest albumize-messages (deftest albumize-messages
(testing "Finding albums in the messages list" (testing "Finding albums in the messages list"