Images Album (#14635)

* feat: images album
This commit is contained in:
Omar Basem 2023-01-05 16:31:32 +04:00 committed by GitHub
parent bb4a4207df
commit 951fd43d10
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 179 additions and 21 deletions

View File

@ -83,7 +83,7 @@
:optimizations :simple :optimizations :simple
:target :node-test :target :node-test
;; When running tests without a REPL you can uncomment below line to `make test-watch` a specific file ;; When running tests without a REPL you can uncomment below line to `make test-watch` a specific file
;; :ns-regexp "status-im.chat.models-test$" ;;:ns-regexp "status-im2.subs.subs-test$"
:main :main
status-im.test-runner/main status-im.test-runner/main
;; set :ui-driven to true to let shadow-cljs inject node-repl ;; set :ui-driven to true to let shadow-cljs inject node-repl

View File

@ -184,9 +184,11 @@
(defn build-image-messages (defn build-image-messages
[{db :db} chat-id] [{db :db} chat-id]
(let [images (get-in db [:chat/inputs chat-id :metadata :sending-image])] (let [images (get-in db [:chat/inputs chat-id :metadata :sending-image])
album-id (str (random-uuid))]
(mapv (fn [[_ {:keys [uri]}]] (mapv (fn [[_ {:keys [uri]}]]
{:chat-id chat-id {:chat-id chat-id
:album-id album-id
:content-type constants/content-type-image :content-type constants/content-type-image
:image-path (utils/safe-replace uri #"file://" "") :image-path (utils/safe-replace uri #"file://" "")
:text (i18n/label :t/update-to-see-image {"locale" "en"})}) :text (i18n/label :t/update-to-see-image {"locale" "en"})})

View File

@ -4,9 +4,9 @@
[status-im.constants :as constants] [status-im.constants :as constants]
[status-im.data-store.chats :as data-store.chats] [status-im.data-store.chats :as data-store.chats]
[status-im.data-store.messages :as data-store.messages] [status-im.data-store.messages :as data-store.messages]
[utils.re-frame :as rf]
[status-im2.contexts.activity-center.events :as activity-center] [status-im2.contexts.activity-center.events :as activity-center]
[taoensso.timbre :as log])) [taoensso.timbre :as log]
[utils.re-frame :as rf]))
(defn cursor->clock-value (defn cursor->clock-value
[^js cursor] [^js cursor]
@ -101,6 +101,17 @@
:on-success #(re-frame/dispatch :on-success #(re-frame/dispatch
[::mark-all-read-in-community-successful %])}]})) [::mark-all-read-in-community-successful %])}]}))
;; For example, when a user receives a list of 4 image messages while inside the chat screen we
;; shouldn't group the images into albums. When the user exists the chat screen then enters the
;; chat screen again, we now need to group the images into albums (like WhatsApp). The albumize?
;; boolean is used to know whether we need to group these images into albums now or not. The
;; album-id can't be used for this because it will always be there.
(defn mark-album
[message]
(if (:album-id message)
(assoc message :albumize? true)
message))
(rf/defn messages-loaded (rf/defn messages-loaded
"Loads more messages for current chat" "Loads more messages for current chat"
{:events [::messages-loaded]} {:events [::messages-loaded]}
@ -131,8 +142,8 @@
current-clock-value (get-in db current-clock-value (get-in db
[:pagination-info chat-id [:pagination-info chat-id
:cursor-clock-value]) :cursor-clock-value])
clock-value (when cursor clock-value (when cursor (cursor->clock-value cursor))
(cursor->clock-value cursor))] new-messages (map mark-album new-messages)]
{:dispatch [:chat/add-senders-to-chat-users (vals senders)] {:dispatch [:chat/add-senders-to-chat-users (vals senders)]
:db (-> db :db (-> db
(update-in [:pagination-info chat-id :cursor-clock-value] (update-in [:pagination-info chat-id :cursor-clock-value]

View File

@ -20,7 +20,8 @@
from from
outgoing outgoing
whisper-timestamp whisper-timestamp
deleted-for-me?]}] deleted-for-me?
albumize?]}]
(-> {:whisper-timestamp whisper-timestamp (-> {:whisper-timestamp whisper-timestamp
:from from :from from
:one-to-one? (= constants/message-type-one-to-one message-type) :one-to-one? (= constants/message-type-one-to-one message-type)
@ -32,7 +33,8 @@
:clock-value clock-value :clock-value clock-value
:type :message :type :message
:message-id message-id :message-id message-id
:outgoing (boolean outgoing)} :outgoing (boolean outgoing)
:albumize? albumize?}
add-datemark add-datemark
add-timestamp)) add-timestamp))

View File

@ -35,6 +35,7 @@
:audioDurationMs :audio-duration-ms :audioDurationMs :audio-duration-ms
:deleted :deleted? :deleted :deleted?
:deletedForMe :deleted-for-me? :deletedForMe :deleted-for-me?
:albumId :album-id
:new :new?}) :new :new?})
(update :quoted-message (update :quoted-message

View File

@ -5,6 +5,7 @@
(defn build-message (defn build-message
[{:keys [chat-id [{:keys [chat-id
album-id
text text
response-to response-to
ens-name ens-name
@ -15,6 +16,7 @@
sticker sticker
content-type]}] content-type]}]
{:chatId chat-id {:chatId chat-id
:albumId album-id
:text text :text text
:responseTo response-to :responseTo response-to
:ensName ens-name :ensName ens-name

View File

@ -15,6 +15,7 @@
(def ^:const content-type-contact-request 11) ;; TODO: temp, will be removed (def ^:const content-type-contact-request 11) ;; TODO: temp, will be removed
(def ^:const content-type-gif 12) (def ^:const content-type-gif 12)
(def ^:const content-type-link 13) (def ^:const content-type-link 13)
(def ^:const content-type-album 14)
(def ^:const contact-request-state-none 0) (def ^:const contact-request-state-none 0)
(def ^:const contact-request-state-mutual 1) (def ^:const contact-request-state-mutual 1)
@ -200,3 +201,20 @@
(def ^:const delete-message-undo-time-limit-ms 4000) (def ^:const delete-message-undo-time-limit-ms 4000)
(def ^:const delete-message-for-me-undo-time-limit-ms 4000) (def ^:const delete-message-for-me-undo-time-limit-ms 4000)
(def ^:const album-image-sizes
{4 {0 146
1 146
2 146
3 146}
5 {0 146
1 146
2 97
3 97
4 97}
:default {0 146
1 146
2 72.5
3 72.5
4 72.5
5 72.5}})

View File

@ -0,0 +1,22 @@
(ns status-im2.contexts.chat.messages.content.album.style
(:require [quo2.foundations.colors :as colors]))
(def album-container
{:flex-direction :row
:flex-wrap :wrap
:overflow :hidden})
(defn image
[size index]
{:width size
:height size
:margin-left (when (and (not= index 0) (not= index 2)) 1)
:margin-bottom (when (< index 2) 1)})
(def overlay
{:position :absolute
:width 73
:height 73
:background-color colors/neutral-80-opa-60
:justify-content :center
:align-items :center})

View File

@ -0,0 +1,51 @@
(ns status-im2.contexts.chat.messages.content.album.view
(:require [quo2.core :as quo]
[quo2.foundations.colors :as colors]
[react-native.core :as rn]
[react-native.fast-image :as fast-image]
[status-im2.contexts.chat.messages.content.album.style :as style]
[status-im2.common.constants :as constants]))
(def max-display-count 6)
(defn border-tlr
[index]
(when (= index 0) 12))
(defn border-trr
[index]
(when (= index 1) 12))
(defn border-blr
[index count]
(when (and (= index 2) (> count 2)) 12))
(defn border-brr
[index count]
(when (and (= index (- (min count max-display-count) 1)) (> count 2)) 12))
(defn album-message
[message]
[rn/view
{:style style/album-container}
(map-indexed
(fn [index item]
(let [images-count (count (:album message))
images-size-key (if (< images-count 6) images-count :default)
size (get-in constants/album-image-sizes [images-size-key index])]
[rn/view {:key (:message-id item)}
[fast-image/fast-image
{:style (merge (style/image size index)
{:border-top-left-radius (border-tlr index)
:border-top-right-radius (border-trr index)
:border-bottom-left-radius (border-blr index images-count)
:border-bottom-right-radius (border-brr index images-count)})
:source {:uri (:image (:content item))}}]
(when (and (> images-count max-display-count) (= index (- max-display-count 1)))
[rn/view
{:style (merge style/overlay {:border-bottom-right-radius (border-brr index images-count)})}
[quo/text
{:weight :bold
:size :heading-2
:style {:color colors/white}} (str "+" (- images-count 5))]])]))
(:album message))])

View File

@ -10,6 +10,7 @@
[status-im2.contexts.chat.messages.content.reactions.view :as reactions] [status-im2.contexts.chat.messages.content.reactions.view :as reactions]
[status-im2.contexts.chat.messages.content.status.view :as status] [status-im2.contexts.chat.messages.content.status.view :as status]
[status-im2.contexts.chat.messages.content.system.text.view :as system.text] [status-im2.contexts.chat.messages.content.system.text.view :as system.text]
[status-im2.contexts.chat.messages.content.album.view :as album]
[quo2.core :as quo] [quo2.core :as quo]
[utils.re-frame :as rf] [utils.re-frame :as rf]
[status-im.ui2.screens.chat.messages.message :as old-message] [status-im.ui2.screens.chat.messages.message :as old-message]
@ -94,6 +95,8 @@
constants/content-type-audio [not-implemented/not-implemented constants/content-type-audio [not-implemented/not-implemented
[old-message/audio message-data]] [old-message/audio message-data]]
constants/content-type-album [album/album-message message-data]
[not-implemented/not-implemented [content.unknown/unknown-content message-data]]) [not-implemented/not-implemented [content.unknown/unknown-content message-data]])
[status/status message-data]]]]])) [status/status message-data]]]]]))

View File

@ -147,9 +147,10 @@
(re-frame/reg-sub (re-frame/reg-sub
:chats/sending-image :chats/sending-image
:<- [:chats/current-chat-inputs] :<- [:chats/current-chat-id]
(fn [{:keys [metadata]}] :<- [:chat/inputs]
(:sending-image metadata))) (fn [[chat-id inputs]]
(get-in inputs [chat-id :metadata :sending-image])))
(re-frame/reg-sub (re-frame/reg-sub
:chats/timeline-chat-input-text :chats/timeline-chat-input-text

View File

@ -102,6 +102,25 @@
(fn [messages] (fn [messages]
(empty? messages))) (empty? messages)))
(defn albumize-messages
[messages]
(get (reduce (fn [{:keys [messages albums]} message]
(let [album-id (when (:albumize? message) (:album-id message))
albums (cond-> albums album-id (update album-id conj message))
messages (if (and album-id (> (count (get albums album-id)) 3))
(conj (filterv #(not= album-id (:album-id %)) messages)
{:album (get albums album-id)
:album-id album-id
:message-id album-id
:content-type constants/content-type-album})
(conj messages message))]
{:messages messages
:albums albums}))
{:messages []
:albums {}}
messages)
:messages))
(re-frame/reg-sub (re-frame/reg-sub
:chats/raw-chat-messages-stream :chats/raw-chat-messages-stream
(fn [[_ chat-id] _] (fn [[_ chat-id] _]
@ -126,7 +145,8 @@
(datetime/timestamp) (datetime/timestamp)
chat-type chat-type
joined joined
loading-messages?)))))) loading-messages?)
(albumize-messages))))))
;;we want to keep data unchanged so react doesn't change component when we leave screen ;;we want to keep data unchanged so react doesn't change component when we leave screen
(def memo-profile-messages-stream (atom nil)) (def memo-profile-messages-stream (atom nil))

View File

@ -0,0 +1,25 @@
(ns status-im2.subs.chat.messages-test
(:require [cljs.test :refer [deftest is testing]]
[status-im2.subs.chat.messages :as messages]
[status-im2.common.constants :as constants]))
(def messages-state
[{:message-id "0x111" :album-id "abc" :albumize? true}
{:message-id "0x222" :album-id "abc" :albumize? true}
{:message-id "0x333" :album-id "abc" :albumize? true}
{:message-id "0x444" :album-id "abc" :albumize? true}
{:message-id "0x555" :album-id "efg" :albumize? true}])
(def messages-albumized-state
[{:album [{:message-id "0x444" :album-id "abc" :albumize? true}
{:message-id "0x333" :album-id "abc" :albumize? true}
{:message-id "0x222" :album-id "abc" :albumize? true}
{:message-id "0x111" :album-id "abc" :albumize? true}]
:album-id "abc"
:message-id "abc"
:content-type constants/content-type-album}
{:message-id "0x555" :album-id "efg" :albumize? true}])
(deftest albumize-messages
(testing "Finding albums in the messages list"
(is (= (messages/albumize-messages messages-state) messages-albumized-state))))