feat: new delete message (#14232)

This commit is contained in:
yqrashawn 2022-12-09 15:35:41 +08:00 committed by GitHub
parent 9be8a5b961
commit bd84a36582
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 471 additions and 293 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

View File

@ -1,11 +1,11 @@
(ns quo2.components.messages.system-message
(:require [react-native.core :as rn]
[react-native.reanimated :as reanimated]
[quo2.theme :as theme]
[quo2.components.avatars.icon-avatar :as icon-avatar]
(:require [quo2.components.avatars.icon-avatar :as icon-avatar]
[quo2.components.avatars.user-avatar :as user-avatar]
[quo2.components.markdown.text :as text]
[quo2.foundations.colors :as colors]
[quo2.theme :as theme]
[react-native.core :as rn]
[react-native.reanimated :as reanimated]
[utils.string :as utils]))
(def themes-landed {:pinned colors/primary-50-opa-5
@ -60,19 +60,19 @@
:flex-direction :row}
[rn/view {:align-items :center
:flex-direction :row}
[sm-icon {:icon :main-icons/delete16
[sm-icon {:icon :main-icons/delete
:color :danger
:opacity 5}]
[text/text {:size :paragraph-2
:style {:color (get-color :text)
:margin-right 5}}
(or label (:message-deleted labels))]
(or (get labels label) label (:message-deleted labels))]
[sm-timestamp timestamp-str]]])
(defmethod sm-render :added [{:keys [state mentions timestamp-str labels]}]
[rn/view {:align-items :center
:flex-direction :row}
[sm-icon {:icon :main-icons/add-user16
[sm-icon {:icon :main-icons/add-user
:color :primary
:opacity (if (= state :landed) 0 5)}]
[sm-user-avatar (:image (first mentions))]
@ -94,7 +94,7 @@
[rn/view {:flex-direction :row
:flex 1
:align-items :center}
[sm-icon {:icon :main-icons/pin16
[sm-icon {:icon :main-icons/pin
:color :primary
:opacity (if (= state :landed) 0 5)}]
[rn/view {:flex-direction :column

View File

@ -3,6 +3,8 @@
[taoensso.timbre :as log]
[status-im.multiaccounts.model :as multiaccounts.model]
[status-im.chat.models.message-list :as message-list]
[status-im.chat.models.delete-message :as delete-message]
[status-im.chat.models.delete-message-for-me :as delete-for-me]
[status-im.data-store.chats :as chats-store]
[status-im.mailserver.core :as mailserver]
[status-im.data-store.contacts :as contacts-store]
@ -212,6 +214,8 @@
(when-let [chat-id (:current-chat-id db)]
(chat.state/reset-visible-item)
(fx/merge cofx
(delete-for-me/sync-all)
(delete-message/send-all)
{:db (dissoc db :current-chat-id)}
(offload-messages chat-id))))

View File

@ -0,0 +1,109 @@
(ns status-im.chat.models.delete-message
(:require [re-frame.core :as re-frame]
[status-im.chat.models.message-list :as message-list]
[status-im.chat.models.pin-message :as models.pin-message]
[status-im.ethereum.json-rpc :as json-rpc]
[status-im.utils.datetime :as datetime]
[status-im.utils.fx :as fx]
[taoensso.encore :as enc]
[taoensso.timbre :as log]))
(defn- update-db-clear-undo-timer
[db chat-id message-id]
(when (get-in db [:messages chat-id message-id])
(update-in db
[:messages chat-id message-id]
dissoc
:deleted-undoable-till)))
(defn- update-db-delete-locally
"Delete message in re-frame db and set the undo timelimit"
[db chat-id message-id undo-time-limit-ms]
(when (get-in db [:messages chat-id message-id])
(update-in db
[:messages chat-id message-id]
assoc
:deleted? true
:deleted-undoable-till (+ (datetime/timestamp)
undo-time-limit-ms))))
(defn- update-db-undo-locally
"Restore deleted message if called within timelimit"
[db chat-id message-id]
(let [{:keys [deleted? deleted-undoable-till]}
(get-in db [:messages chat-id message-id])]
(if (and deleted?
(> deleted-undoable-till (datetime/timestamp)))
(update-in db
[:messages chat-id message-id]
dissoc
:deleted?
:deleted-undoable-till)
(update-db-clear-undo-timer db chat-id message-id))))
(defn- update-db-delete-locally-without-time-limit
"Delete message in re-frame db, used to handle received removed-messages"
[db chat-id message-id]
(when (get-in db [:messages chat-id message-id])
(update-in db [:messages chat-id message-id] assoc :deleted? true)))
(fx/defn delete
"Delete message now locally and broadcast after undo time limit timeout"
{:events [:chat.ui/delete-message]}
[{:keys [db]} {:keys [chat-id message-id]} undo-time-limit-ms]
(when (get-in db [:messages chat-id message-id])
(assoc
(message-list/rebuild-message-list
{:db (update-db-delete-locally db chat-id message-id undo-time-limit-ms)}
chat-id)
:utils/dispatch-later [{:dispatch [:chat.ui/delete-message-and-send
{:chat-id chat-id
:message-id message-id}]
:ms undo-time-limit-ms}])))
(fx/defn undo
{:events [:chat.ui/undo-delete-message]}
[{:keys [db]} {:keys [chat-id message-id]}]
(when (get-in db [:messages chat-id message-id])
(message-list/rebuild-message-list
{:db (update-db-undo-locally db chat-id message-id)}
chat-id)))
(fx/defn delete-and-send
{:events [:chat.ui/delete-message-and-send]}
[{:keys [db]} {:keys [message-id chat-id]}]
(when-let [message (get-in db [:messages chat-id message-id])]
(enc/assoc-when
{:db (update-db-clear-undo-timer db chat-id message-id)
::json-rpc/call [{:method "wakuext_deleteMessageAndSend"
:params [message-id]
:js-response true
:on-error #(log/error "failed to delete message " {:message-id message-id :error %})
:on-success #(re-frame/dispatch [:sanitize-messages-and-process-response %])}]}
:dispatch (and (get-in db [:pin-messages chat-id message-id])
[::models.pin-message/send-pin-message {:chat-id chat-id :message-id message-id :pinned false}]))))
(defn- filter-pending-send-messages
"traverse all messages find not yet synced deleted? messages"
[acc chat-id messages]
(->> messages
(filter (fn [[_ {:keys [deleted? deleted-undoable-till]}]] (and deleted? deleted-undoable-till)))
(map (fn [message] {:chat-id chat-id :message-id (first message)}))
(concat acc)))
(fx/defn send-all
"Get all deleted messages that not yet synced with status-go and send them"
{:events [:chat.ui/send-all-deleted-messages]}
[{:keys [db] :as cofx}]
(let [pending-send-messages (reduce-kv filter-pending-send-messages [] (:messages db))]
(apply fx/merge cofx (map delete-and-send pending-send-messages))))
(fx/defn delete-messages-localy
"Mark messages :deleted? localy in client"
{:events [:chat.ui/delete-messages-localy]}
[{:keys [db]} messages chat-id]
(let [new-db (->> messages
(filter #(get-in db [:messages chat-id (:message-id %)]))
(reduce #(update-db-delete-locally-without-time-limit %1 chat-id (:message-id %2)) db))]
(when new-db
(message-list/rebuild-message-list {:db new-db} chat-id))))

View File

@ -69,22 +69,20 @@
::json-rpc/call [{:method "wakuext_deleteMessageForMeAndSync"
:params [chat-id message-id]
:js-response true
:on-error #(log/error "failed to delete message for me " %)
:on-success #(re-frame/dispatch [:sanitize-messages-and-process-response
%])}]}))
(defn- chats-reducer
"traverse all messages find not yet synced deleted-for-me? messages, generate dispatch vector"
:on-error #(log/error "failed to delete message for me, message id: " {:message-id message-id :error %})
:on-success #(re-frame/dispatch [:sanitize-messages-and-process-response %])}]}))
(defn- filter-pending-sync-messages
"traverse all messages find not yet synced deleted-for-me? messages"
[acc chat-id messages]
(reduce-kv
(fn [inner-acc message-id {:keys [deleted-for-me? deleted-for-me-undoable-till]}]
(if (and deleted-for-me? deleted-for-me-undoable-till)
(conj inner-acc [:chat.ui/delete-message-for-me-and-sync chat-id message-id])
inner-acc))
acc
messages))
(->> messages
(filter (fn [[_ {:keys [deleted-for-me? deleted-for-me-undoable-till]}]] (and deleted-for-me? deleted-for-me-undoable-till)))
(map (fn [message] {:chat-id chat-id :message-id (first message)}))
(concat acc)))
(fx/defn sync-all
"Get all deleted-for-me messages that not yet synced with status-go and sync them"
{:events [:chat.ui/sync-all-deleted-for-me-messages]}
[{:keys [db]}]
{:dispatch-n (reduce-kv chats-reducer [] (:messages db))})
[{:keys [db] :as cofx}]
(let [pending-sync-messages (reduce-kv filter-pending-sync-messages [] (:messages db))]
(apply fx/merge cofx (map delete-and-sync pending-sync-messages))))

View File

@ -4,94 +4,97 @@
delete-message-for-me]
[status-im.utils.datetime :as datetime]))
(defonce mid "message-id")
(defonce cid "chat-id")
(def mid "message-id")
(def cid "chat-id")
(deftest delete-for-me
(with-redefs [datetime/timestamp (constantly 1)]
(let [db {:messages {cid {mid {:id mid :whisper-timestamp 1}}}}
message {:message-id mid :chat-id cid}]
(testing "delete for me"
(let [result-message (get-in (delete-message-for-me/delete {:db db} message 1000)
(testing "dispatch right db fx"
(let [result-message (get-in (delete-message-for-me/delete {:db db} message 1000)
[:db :messages cid mid])]
(is (= (:id result-message) mid))
(is (true? (:deleted-for-me? result-message)))
(is (= (:deleted-for-me-undoable-till result-message) 1001))))
(testing "should return nil if message not in db"
(is (= (delete-message-for-me/delete {:db {:messages []}} message 1000)
nil))))))
(is (= (:id result-message) mid))
(is (true? (:deleted-for-me? result-message)))
(is (= (:deleted-for-me-undoable-till result-message) 1001))))
(testing "return nil if message not in db"
(is (= (delete-message-for-me/delete {:db {:messages []}} message 1000)
nil)))))))
(deftest undo-delete-for-me
(let [db {:messages {cid {mid {:id mid :whisper-timestamp 1}}}}
message {:message-id mid :chat-id cid}]
(testing "undo delete for me in time"
(let [db (update-in db
[:messages cid mid]
assoc
:deleted-for-me? true
:deleted-for-me-undoable-till
(+ (datetime/timestamp) 1000))
result-message (get-in (delete-message-for-me/undo {:db db} message)
[:db :messages cid mid])]
(is (= (:id result-message) mid))
(is (nil? (:deleted-for-me? result-message)))
(is (nil? (:deleted-for-me-undoable-till result-message)))))
(testing "undo delete for me"
(testing "in time"
(let [db (update-in db
[:messages cid mid]
assoc
:deleted-for-me? true
:deleted-for-me-undoable-till
(+ (datetime/timestamp) 1000))
result-message (get-in (delete-message-for-me/undo {:db db} message)
[:db :messages cid mid])]
(is (= (:id result-message) mid))
(is (nil? (:deleted-for-me? result-message)))
(is (nil? (:deleted-for-me-undoable-till result-message)))))
(testing "remain deleted for me when undo delete for me late"
(let [db (update-in db
[:messages cid mid]
assoc
:deleted-for-me? true
:deleted-for-me-undoable-till (- (datetime/timestamp) 1000))
result-message (get-in (delete-message-for-me/undo {:db db} message) [:db :messages cid mid])]
(is (= (:id result-message) mid))
(is (nil? (:deleted-for-me-undoable-till result-message)))
(is (true? (:deleted-for-me? result-message)))))
(testing "remain deleted for me when undo after timelimit"
(let [db (update-in db
[:messages cid mid]
assoc
:deleted-for-me? true
:deleted-for-me-undoable-till (- (datetime/timestamp) 1000))
result-message (get-in (delete-message-for-me/undo {:db db} message) [:db :messages cid mid])]
(is (= (:id result-message) mid))
(is (nil? (:deleted-for-me-undoable-till result-message)))
(is (true? (:deleted-for-me? result-message)))))
(testing "should return nil if message not in db"
(is (= (delete-message-for-me/undo {:db {:messages []}} message)
nil)))))
(testing "return nil if message not in db"
(is (= (delete-message-for-me/undo {:db {:messages []}} message)
nil))))))
(deftest delete-for-me-and-sync
(let [db {:messages {cid {mid {:id mid}}}}
message {:message-id mid :chat-id cid}]
(testing "delete for me and sync"
(let [expected-db {:messages {cid {mid {:id mid}}}}
effects (delete-message-for-me/delete-and-sync {:db db} message)
result-db (:db effects)
rpc-calls (:status-im.ethereum.json-rpc/call effects)]
(is (= result-db expected-db))
(is (= (count rpc-calls) 1))
(is (= (-> rpc-calls
first
:method)
"wakuext_deleteMessageForMeAndSync"))
(is (= (-> rpc-calls
first
:params
count)
2))
(is (= (-> rpc-calls
first
:params
first)
cid))
(is (= (-> rpc-calls
first
:params
second)
mid))))
(testing "delete for me and sync, should clean undo timer"
(let [expected-db {:messages {cid {mid {:id mid}}}}
effects (delete-message-for-me/delete-and-sync
{:db (update-in db
[:messages cid mid
:deleted-for-me-undoable-till]
(constantly (datetime/timestamp)))}
message)
result-db (:db effects)]
(is (= result-db expected-db))))
(testing "should return nil if message not in db"
(is (= (delete-message-for-me/delete-and-sync {:db {:messages []}}
message)
nil)))))
(testing "dispatch right rpc call"
(let [expected-db {:messages {cid {mid {:id mid}}}}
effects (delete-message-for-me/delete-and-sync {:db db} message)
result-db (:db effects)
rpc-calls (:status-im.ethereum.json-rpc/call effects)]
(is (= result-db expected-db))
(is (= (count rpc-calls) 1))
(is (= (-> rpc-calls
first
:method)
"wakuext_deleteMessageForMeAndSync"))
(is (= (-> rpc-calls
first
:params
count)
2))
(is (= (-> rpc-calls
first
:params
first)
cid))
(is (= (-> rpc-calls
first
:params
second)
mid))))
(testing "clean undo timer"
(let [expected-db {:messages {cid {mid {:id mid}}}}
effects (delete-message-for-me/delete-and-sync
{:db (update-in db
[:messages cid mid
:deleted-for-me-undoable-till]
(constantly (datetime/timestamp)))}
message)
result-db (:db effects)]
(is (= result-db expected-db))))
(testing "return nil if message not in db"
(is (= (delete-message-for-me/delete-and-sync {:db {:messages []}}
message)
nil))))))

View File

@ -0,0 +1,95 @@
(ns status-im.chat.models.delete-message-test
(:require
[cljs.test :refer-macros [deftest is testing]]
[status-im.chat.models.delete-message :as delete-message]
[status-im.utils.datetime :as datetime]))
(def mid "message-id")
(def cid "chat-id")
(deftest delete
(with-redefs [datetime/timestamp (constantly 1)]
(let [db {:messages {cid {mid {:id mid :whisper-timestamp 1}}}}
message {:message-id mid :chat-id cid}]
(testing "delete"
(testing "dispatch right db fx"
(let [result-message (get-in (delete-message/delete {:db db} message 1000)
[:db :messages cid mid])]
(is (= (:id result-message) mid))
(is (true? (:deleted? result-message)))
(is (= (:deleted-undoable-till result-message) 1001))))
(testing "return nil if message not in db"
(is (= (delete-message/delete {:db {:messages []}} message 1000)
nil)))))))
(deftest undo-delete
(let [db {:messages {cid {mid {:id mid :whisper-timestamp 1}}}}
message {:message-id mid :chat-id cid}]
(testing "undo delete"
(testing "undo in time"
(let [db (update-in db
[:messages cid mid]
assoc
:deleted? true
:deleted-undoable-till
(+ (datetime/timestamp) 1000))
result-message (get-in (delete-message/undo {:db db} message)
[:db :messages cid mid])]
(is (= (:id result-message) mid))
(is (nil? (:deleted? result-message)))
(is (nil? (:deleted-undoable-till result-message)))))
(testing "remain deleted when undo after timelimit"
(let [db (update-in db
[:messages cid mid]
assoc
:deleted? true
:deleted-undoable-till (- (datetime/timestamp) 1000))
result-message (get-in (delete-message/undo {:db db} message) [:db :messages cid mid])]
(is (= (:id result-message) mid))
(is (nil? (:deleted-undoable-till result-message)))
(is (true? (:deleted? result-message)))))
(testing "return nil if message not in db"
(is (= (delete-message/undo {:db {:messages []}} message)
nil))))))
(deftest delete-and-send
(let [db {:messages {cid {mid {:id mid}}}}
message {:message-id mid :chat-id cid}]
(testing "delete and send"
(testing "dispatch right rpc call fx"
(let [expected-db {:messages {cid {mid {:id mid}}}}
effects (delete-message/delete-and-send {:db db} message)
result-db (:db effects)
rpc-calls (:status-im.ethereum.json-rpc/call effects)]
(is (= result-db expected-db))
(is (= (count rpc-calls) 1))
(is (= (-> rpc-calls
first
:method)
"wakuext_deleteMessageAndSend"))
(is (= (-> rpc-calls
first
:params
count)
1))
(is (= (-> rpc-calls
first
:params
first)
mid))))
(testing "clean undo timer"
(let [expected-db {:messages {cid {mid {:id mid}}}}
effects (delete-message/delete-and-send
{:db (update-in db
[:messages cid mid
:deleted-undoable-till]
(constantly (datetime/timestamp)))}
message)
result-db (:db effects)]
(is (= result-db expected-db))))
(testing "return nil if message not in db"
(is (= (delete-message/delete-and-send {:db {:messages []}}
message)
nil))))))

View File

@ -1,20 +1,20 @@
(ns status-im.chat.models.input
(:require [clojure.string :as string]
(:require ["emojilib" :as emojis]
[clojure.string :as string]
[goog.object :as object]
[re-frame.core :as re-frame]
[taoensso.timbre :as log]
[status-im.chat.constants :as chat.constants]
[status-im.ethereum.json-rpc :as json-rpc]
[status-im.chat.models :as chat]
[status-im.chat.models.mentions :as mentions]
[status-im.chat.models.message :as chat.message]
[status-im.chat.models.message-content :as message-content]
[status-im.constants :as constants]
[status-im.ethereum.json-rpc :as json-rpc]
[status-im.i18n.i18n :as i18n]
[status-im.utils.datetime :as datetime]
[status-im.utils.fx :as fx]
["emojilib" :as emojis]
[status-im.chat.models.mentions :as mentions]
[status-im.utils.utils :as utils]))
[status-im.utils.utils :as utils]
[taoensso.timbre :as log]))
(defn text->emoji
"Replaces emojis in a specified `text`"
@ -134,16 +134,6 @@
(update-in [:chat/inputs current-chat-id :metadata]
dissoc :sending-image))}))
(fx/defn soft-delete-message
"Does't delete from db, this is a soft delete"
{:events [:chat.ui/soft-delete-message]}
[{:keys [db] :as cofx} {:keys [message-id chat-id]}]
{::json-rpc/call [{:method "wakuext_deleteMessageAndSend"
:params [message-id]
:js-response true
:on-error #(log/error "failed to delete message message " %)
:on-success #(re-frame/dispatch [:sanitize-messages-and-process-response %])}]})
(fx/defn show-contact-request-input
"Sets reference to previous chat message and focuses on input"
{:events [:chat.ui/send-contact-request]}

View File

@ -1,20 +1,21 @@
(ns status-im.chat.models.message
(:require [status-im.chat.models :as chat-model]
(:require [clojure.string :as string]
[re-frame.core :as re-frame]
[status-im.chat.models :as chat-model]
[status-im.chat.models.delete-message :as delete-message]
[status-im.chat.models.loading :as chat.loading]
[status-im.chat.models.mentions :as mentions]
[status-im.chat.models.message-list :as message-list]
[status-im.constants :as constants]
[status-im.data-store.messages :as data-store.messages]
[status-im.ethereum.json-rpc :as json-rpc]
[status-im.transport.message.protocol :as protocol]
[status-im.utils.fx :as fx]
[taoensso.timbre :as log]
[status-im.chat.models.mentions :as mentions]
[clojure.string :as string]
[status-im.utils.types :as types]
[status-im.ui.screens.chat.state :as view.state]
[status-im.chat.models.loading :as chat.loading]
[status-im.utils.fx :as fx]
[status-im.utils.gfycat.core :as gfycat]
[status-im.utils.platform :as platform]
[status-im.utils.gfycat.core :as gfycat]))
[status-im.utils.types :as types]
[taoensso.timbre :as log]))
(defn- message-loaded?
[db chat-id message-id]
@ -181,15 +182,6 @@
:on-error #(log/error "failed to re-send message" %)}]}
(update-message-status chat-id message-id :sending)))
(fx/defn delete-message
"Deletes chat message, rebuild message-list"
{:events [:chat.ui/delete-message]}
[{:keys [db] :as cofx} chat-id message-id]
(fx/merge cofx
{:db (update-in db [:messages chat-id] dissoc message-id)}
(data-store.messages/delete-message message-id)
(message-list/rebuild-message-list chat-id)))
(fx/defn send-message
[cofx message]
(protocol/send-chat-messages cofx [message]))
@ -201,19 +193,23 @@
(fx/defn handle-removed-messages
{:events [::handle-removed-messages]}
[{:keys [db] :as cofx} removed-messages]
(let [mark-as-seen-fx (mapv (fn [removed-message]
(let [chat-id (:chatId removed-message)
message-id (:messageId removed-message)]
(data-store.messages/mark-messages-seen chat-id
[message-id]
#(re-frame/dispatch [:chat/decrease-unviewed-count chat-id %3])))) removed-messages)
(let [mark-as-deleted-fx (->> removed-messages
(map #(assoc % :message-id (:messageId %)))
(group-by :chatId)
(mapv (fn [[chat-id messages]] (delete-message/delete-messages-localy messages chat-id))))
mark-as-seen-fx (mapv (fn [removed-message]
(let [chat-id (:chatId removed-message)
message-id (:messageId removed-message)]
(data-store.messages/mark-messages-seen chat-id
[message-id]
#(re-frame/dispatch [:chat/decrease-unviewed-count chat-id %3]))))
removed-messages)
remove-messages-fx (fn [{:keys [db]}]
{:db (reduce (fn [acc current]
(update-in acc [:messages (:chatId current)] dissoc (:messageId current)))
db removed-messages)
:dispatch-n [[:get-activity-center-notifications]
{:dispatch-n [[:get-activity-center-notifications]
[:get-activity-center-notifications-count]]})]
(apply fx/merge cofx (conj mark-as-seen-fx remove-messages-fx))))
(apply fx/merge cofx (-> mark-as-deleted-fx
(concat mark-as-seen-fx)
(conj remove-messages-fx)))))
(comment
(handle-removed-messages

View File

@ -1,9 +1,7 @@
(ns status-im.chat.models.message-test
(:require [cljs.test :refer-macros [deftest is testing]]
[status-im.chat.models.message :as message]
[status-im.chat.models.message-list :as models.message-list]
[status-im.chat.models.loading :as loading]
[status-im.utils.datetime :as time]
[status-im.ui.screens.chat.state :as view.state]))
(deftest add-received-message-test
@ -130,54 +128,3 @@
{:chats {"a" {:deleted-at-clock-value 1}}}
"a"
0))))
(deftest delete-message
(with-redefs [time/day-relative (constantly "day-relative")
time/timestamp->time (constantly "timestamp")]
(let [cofx1 {:db {:messages {"chat-id" {0 {:message-id 0
:content "a"
:clock-value 0
:whisper-timestamp 0
:timestamp 0}
1 {:message-id 1
:content "b"
:clock-value 1
:whisper-timestamp 1
:timestamp 1}}}
:message-lists {"chat-id" [{:something :something}]}
:chats {"chat-id" {}}}}
cofx2 {:db {:messages {"chat-id" {0 {:message-id 0
:content "a"
:clock-value 0
:whisper-timestamp 1
:timestamp 1}}}
:message-list {"chat-id" [{:something :something}]}
:chats {"chat-id" {}}}}
fx1 (message/delete-message cofx1 "chat-id" 1)
fx2 (message/delete-message cofx2 "chat-id" 0)]
(testing "Deleting message deletes it along with all references"
(is (= '(0)
(keys (get-in fx1 [:db :messages "chat-id"]))))
(is (= [{:one-to-one? false
:message-id 0
:whisper-timestamp 0
:type :message
:display-photo? true
:system-message? false
:last-in-group? true
:datemark "day-relative"
:clock-value 0
:first-in-group? true
:from nil
:first-outgoing? false
:outgoing-seen? false
:timestamp-str "timestamp"
:first? true
:display-username? true
:outgoing false}]
(models.message-list/->seq
(get-in fx1 [:db :message-lists "chat-id"]))))
(is (= {}
(get-in fx2 [:db :messages "chat-id"])))
(is (= nil
(get-in fx2 [:db :message-lists "chat-id"])))))))

View File

@ -197,8 +197,6 @@
(def ^:const sticker-pack-status-pending 2)
(def ^:const sticker-pack-status-owned 3)
(def ^:const delete-message-for-me-undo-time-limit-ms 4000)
(def ^:const community-member-role-all 1)
(def ^:const community-member-role-manage-users 2)
(def ^:const community-member-role-moderator 3)

View File

@ -32,6 +32,7 @@
:quotedMessage :quoted-message
:outgoingStatus :outgoing-status
:audioDurationMs :audio-duration-ms
:deleted :deleted?
:deletedForMe :deleted-for-me?
:new :new?})

View File

@ -15,7 +15,6 @@
[status-im.ui.screens.views :as views]
[status-im.utils.fx :as fx]
[status-im.utils.platform :as platform]
[taoensso.encore :as enc]
[taoensso.timbre :as log]))
(def debug? ^boolean js/goog.DEBUG)
@ -439,6 +438,4 @@
:community
:else
:home))
:dispatch-n (enc/conj-when []
(and (= view-id :chat) [:chat.ui/sync-all-deleted-for-me-messages]))}))
:home))}))

View File

@ -177,7 +177,7 @@
:title (i18n/label :t/delete-message)
:icon :main-icons/delete
:accessibility-label :delete-transaccent-button
:on-press #(hide-sheet-and-dispatch [:chat.ui/delete-message chat-id message-id])}]]))
:on-press #(hide-sheet-and-dispatch [:chat.ui/delete-message-not-used-any-more chat-id message-id])}]]))
(defn image-long-press [{:keys [content identicon from outgoing cant-be-replied] :as message} hide]
(let [contact-name @(re-frame/subscribe [:contacts/contact-name-by-identity from])]

View File

@ -1,16 +1,17 @@
(ns status-im.ui2.screens.chat.components.reply
(:require [quo2.foundations.colors :as colors]
[status-im.ui.components.icons.icons :as icons]
(:require [clojure.string :as string]
[quo.react-native :as rn]
[status-im.constants :as constants]
[status-im.utils.handlers :refer [<sub >evt]]
[quo2.components.markdown.text :as quo2.text]
[status-im.ui.screens.chat.photos :as photos]
[quo2.components.buttons.button :as quo2.button]
[clojure.string :as string]
[quo2.components.icon :as quo2.icon]
[quo2.components.markdown.text :as quo2.text]
[quo2.foundations.colors :as colors]
[status-im.constants :as constants]
[status-im.ethereum.stateofus :as stateofus]
[status-im.i18n.i18n :as i18n]
[status-im.ui2.screens.chat.composer.style :as styles]))
[i18n.i18n :as i18n]
[status-im.ui.components.icons.icons :as icons]
[status-im.ui.screens.chat.photos :as photos]
[status-im.ui2.screens.chat.composer.style :as styles]
[status-im.utils.handlers :refer [<sub >evt]]))
(defn get-quoted-text-with-mentions [parsed-text]
(string/join
@ -43,8 +44,21 @@
(i18n/label :t/You))
(format-author username)))
; This component is also used for quoted pinned message as the UI is very similar
(defn reply-message [{:keys [from identicon content-type contentType parsed-text content]} in-chat-input? pin?]
(defn reply-deleted-message []
[rn/view {:style {:flex-direction :row
:align-items :center}}
[quo2.icon/icon :sad-face {:size 16}]
[quo2.text/text
{:number-of-lines 1
:size :label
:weight :regular
:accessibility-label :quoted-message
:style {:text-transform :none
:margin-left 4
:margin-top 2}}
(i18n/label :t/message-deleted)]])
(defn reply-message [{:keys [from identicon content-type contentType parsed-text content deleted? deleted-for-me?]} in-chat-input? pin?]
(let [contact-name (<sub [:contacts/contact-name-by-identity from])
current-public-key (<sub [:multiaccount/public-key])
content-type (or content-type contentType)]
@ -56,32 +70,35 @@
;;TODO quo2 icon should be used
[icons/icon :main-icons/connector {:color (colors/theme-colors colors/neutral-40 colors/neutral-60)
:container-style {:position :absolute :left 10 :bottom -4 :width 16 :height 16}}])
[rn/view {:style (styles/quoted-message pin?)}
[photos/member-photo from identicon 16]
[quo2.text/text {:weight :semi-bold
:size :paragraph-2
:number-of-lines 1
:style {:margin-left 4}}
(format-reply-author from contact-name current-public-key)]
[quo2.text/text
{:number-of-lines 1
:size :label
:weight :regular
:accessibility-label :quoted-message
:ellipsize-mode :tail
:style (merge
{:text-transform :none
:margin-left 4
:margin-top 2}
(when (or (= constants/content-type-image content-type)
(= constants/content-type-sticker content-type)
(= constants/content-type-audio content-type))
{:color (colors/theme-colors colors/neutral-50 colors/neutral-40)}))}
(case (or content-type contentType)
constants/content-type-image "Image"
constants/content-type-sticker "Sticker"
constants/content-type-audio "Audio"
(get-quoted-text-with-mentions (or parsed-text (:parsed-text content))))]]]
(if (or deleted? deleted-for-me?)
[rn/view {:style (styles/quoted-message pin?)}
[reply-deleted-message]]
[rn/view {:style (styles/quoted-message pin?)}
[photos/member-photo from identicon 16]
[quo2.text/text {:weight :semi-bold
:size :paragraph-2
:number-of-lines 1
:style {:margin-left 4}}
(format-reply-author from contact-name current-public-key)]
[quo2.text/text
{:number-of-lines 1
:size :label
:weight :regular
:accessibility-label :quoted-message
:ellipsize-mode :tail
:style (merge
{:text-transform :none
:margin-left 4
:margin-top 2}
(when (or (= constants/content-type-image content-type)
(= constants/content-type-sticker content-type)
(= constants/content-type-audio content-type))
{:color (colors/theme-colors colors/neutral-50 colors/neutral-40)}))}
(case (or content-type contentType)
constants/content-type-image "Image"
constants/content-type-sticker "Sticker"
constants/content-type-audio "Audio"
(get-quoted-text-with-mentions (or parsed-text (:parsed-text content))))]])]
(when in-chat-input?
[quo2.button/button {:width 24
:size 24

View File

@ -2,11 +2,16 @@
(:require [quo.core :as quo]
[quo.design-system.colors :as quo.colors]
[quo.react-native :as rn]
[quo2.components.avatars.user-avatar :as user-avatar]
[quo2.components.icon :as icons]
[quo2.components.markdown.text :as text]
[quo2.components.messages.system-message :as system-message]
[quo2.core :as quo2]
[quo2.foundations.colors :as colors]
[quo2.foundations.typography :as typography]
[re-frame.core :as re-frame]
[reagent.core :as reagent]
[status-im.chat.models.delete-message]
[status-im.chat.models.delete-message-for-me]
[status-im.chat.models.images :as images]
[status-im.chat.models.pin-message :as models.pin-message]
@ -19,7 +24,6 @@
[status-im.ui.components.fast-image :as fast-image]
[status-im.ui.components.list.views :as list]
[status-im.ui.components.react :as react]
[status-im.ui2.screens.chat.components.reaction-drawer :as reaction-drawer]
[status-im.ui.screens.chat.image.preview.views :as preview]
[status-im.ui.screens.chat.message.audio :as message.audio]
[status-im.ui.screens.chat.message.command :as message.command]
@ -32,17 +36,14 @@
[status-im.ui.screens.chat.styles.photos :as photos.style]
[status-im.ui.screens.chat.utils :as chat.utils]
[status-im.ui.screens.communities.icon :as communities.icon]
[status-im.ui2.screens.chat.components.reaction-drawer :as reaction-drawer]
[status-im.ui2.screens.chat.components.reply :as components.reply]
[status-im.utils.config :as config]
[utils.re-frame :as rf]
[status-im.utils.security :as security]
[quo2.components.icon :as icons]
[status-im.utils.datetime :as time]
[quo2.components.avatars.user-avatar :as user-avatar]
[quo2.components.markdown.text :as text]
[status-im.utils.security :as security]
[status-im.utils.utils :as utils]
[status-im2.contexts.chat.home.chat-list-item.view :as home.chat-list-item]
[quo2.core :as quo2])
[utils.re-frame :as rf])
(:require-macros [status-im.utils.views :refer [defview letsubs]]))
(defview mention-element [from]
@ -83,10 +84,12 @@
:accessibility-label :message-timestamp}
timestamp-str]])))
(defview quoted-message
[_ reply pin?]
[rn/view {:style (when-not pin? (style/quoted-message-container))}
[components.reply/reply-message reply false pin?]])
(defn quoted-message
[{:keys [message-id chat-id]} reply pin?]
(let [{:keys [deleted? deleted-for-me?]} (get @(re-frame/subscribe [:chats/chat-messages chat-id]) message-id)
reply (assoc reply :deleted? deleted? :deleted-for-me? deleted-for-me?)]
[rn/view {:style (when-not pin? (style/quoted-message-container))}
[components.reply/reply-message reply false pin?]]))
(defn system-text? [content-type]
(= content-type constants/content-type-system-text))
@ -251,7 +254,7 @@
(defview community-content [{:keys [community-id] :as message}]
(letsubs [{:keys [name description verified] :as community} [:communities/community community-id]
communities-enabled? [:communities/enabled?]]
communities-enabled? [:communities/enabled?]]
(when (and communities-enabled? community)
[rn/view {:style (assoc (style/message-wrapper message)
:margin-vertical 10
@ -304,29 +307,32 @@
(defn message-content-wrapper
"Author, userpic and delivery wrapper"
[{:keys [last-in-group? timestamp-str timestamp
deleted-for-me? deleted-for-me-undoable-till pinned from]
[{:keys [last-in-group? timestamp-str timestamp deleted? deleted-undoable-till
deleted-for-me? deleted-for-me-undoable-till pinned from chat-id]
:as message} content]
(let [response-to (:response-to (:content message))
display-name (first (rf/sub [:contacts/contact-two-names-by-identity from]))
contact (rf/sub [:contacts/contact-by-address from])
photo-path (when-not (empty? (:images contact)) (rf/sub [:chats/photo-path from]))
online? (rf/sub [:visibility-status-updates/online? from])]
(if deleted-for-me?
(if (or deleted? deleted-for-me?)
[system-message/system-message
{:type :deleted
:label (i18n/label :message-deleted-for-you)
:labels {:pinned-a-message (i18n/label :pinned-a-message)
:message-deleted (i18n/label :message-deleted)
:added (i18n/label :added)}
:label (if deleted? :message-deleted :message-deleted-for-you)
:labels {:pinned-a-message (i18n/label :pinned-a-message)
:message-deleted (i18n/label :message-deleted-for-everyone)
:message-deleted-for-you (i18n/label :message-deleted-for-you)
:added (i18n/label :added)}
:timestamp-str timestamp-str
:non-pressable? true
:animate-landing? (if deleted-for-me-undoable-till true false)}]
:animate-landing? (if (or deleted-undoable-till deleted-for-me-undoable-till)
true
false)}]
[rn/view {:style (style/message-wrapper message)
:pointer-events :box-none
:accessibility-label :chat-item}
(when (and (seq response-to) (:quoted-message message))
[quoted-message response-to (:quoted-message message)])
[quoted-message {:message-id response-to :chat-id chat-id} (:quoted-message message)])
[rn/view {:style (style/message-body)
:pointer-events :box-none}
[rn/view {:style {:width 40}}
@ -444,20 +450,20 @@
:label (i18n/label (if pinned (if community? :t/unpin-from-channel :t/unpin-from-chat) (if community? :t/pin-to-channel :t/pin-to-chat)))
:icon :i/pin
:id (if pinned :unpin :pin)}])
[{:type :danger
:on-press (fn []
(when pinned (pin-message message))
(re-frame/dispatch
[:chat.ui/delete-message-for-me message
constants/delete-message-for-me-undo-time-limit-ms]))
:label (i18n/label :t/delete-for-me)
:icon :i/delete
:id :delete-for-me}]
(when (and (or outgoing can-delete-message-for-everyone?) config/delete-message-enabled?)
(when-not pinned
[{:type :danger
:on-press (fn []
(when pinned (pin-message message))
(re-frame/dispatch [:chat.ui/soft-delete-message message]))
(re-frame/dispatch
[:chat.ui/delete-message-for-me message
config/delete-message-for-me-undo-time-limit-ms]))
:label (i18n/label :t/delete-for-me)
:icon :i/delete
:id :delete-for-me}])
(when (and (or outgoing can-delete-message-for-everyone?) config/delete-message-enabled?)
[{:type :danger
:on-press #(re-frame/dispatch [:chat.ui/delete-message
message
config/delete-message-undo-time-limit-ms])
:label (i18n/label :t/delete-for-everyone)
:icon :i/delete
:id :delete-for-all}]))))
@ -600,13 +606,15 @@
[{:type :danger
:on-press #(re-frame/dispatch
[:chat.ui/delete-message-for-me message
constants/delete-message-for-me-undo-time-limit-ms])
config/delete-message-undo-time-limit-ms])
:label (i18n/label :t/delete-for-me)
:icon :i/delete
:id :delete-for-me}]
(when (and outgoing config/delete-message-enabled?)
[{:type :danger
:on-press #(re-frame/dispatch [:chat.ui/soft-delete-message message])
:on-press #(re-frame/dispatch [:chat.ui/delete-message
message
config/delete-message-undo-time-limit-ms])
:label (i18n/label :t/delete-for-everyone)
:icon :i/delete
:id :delete}]))))]
@ -637,13 +645,15 @@
{:type :danger
:on-press #(re-frame/dispatch
[:chat.ui/delete-message-for-me message
constants/delete-message-for-me-undo-time-limit-ms])
config/delete-message-for-me-undo-time-limit-ms])
:label (i18n/label :t/delete-for-me)
:icon :i/delete
:id :delete-for-me}
(when (and outgoing config/delete-message-enabled?)
{:type :danger
:on-press #(re-frame/dispatch [:chat.ui/soft-delete-message message])
:on-press #(re-frame/dispatch [:chat.ui/delete-message
message
config/delete-message-undo-time-limit-ms])
:label (i18n/label :t/delete-for-everyone)
:icon :i/delete
:id :delete})]))]
@ -708,7 +718,7 @@
[message-content-wrapper message
[unknown-content-type message]])
(defn chat-message [{:keys [pinned pinned-by mentioned in-pinned-view? last-in-group?] :as message}]
(defn chat-message [{:keys [pinned pinned-by mentioned in-pinned-view? last-in-group? deleted? deleted-for-me?] :as message}]
(let [reactions @(re-frame/subscribe [:chats/message-reactions (:message-id message) (:chat-id message)])
own-reactions (reduce (fn [acc {:keys [emoji-id own]}]
(if own (conj acc emoji-id) acc))
@ -742,13 +752,14 @@
(when (or mentioned pinned last-in-group?) {:margin-top 8})
{:margin-horizontal 8})}
(when pinned
(when (and pinned (not (or deleted? deleted-for-me?)))
[rn/view {:style (style/pin-indicator-container)}
[pinned-by-indicator pinned-by]])
[->message message {:ref on-long-press
:modal false
:on-long-press on-open-drawer}]
[reaction-row/message-reactions message reactions nil on-emoji-press on-long-press]])) ;; TODO: pass on-open-drawer function
(when-not (or deleted? deleted-for-me?)
[reaction-row/message-reactions message reactions nil on-emoji-press on-long-press])])) ;; TODO: pass on-open-drawer function
(defn message-render-fn
[{:keys [outgoing whisper-timestamp] :as message}
@ -849,7 +860,7 @@
(style/message-timestamp-text))
:accessibility-label :message-timestamp}
timestamp-str]]
[quoted-message response-to (:quoted-message message) true]]]))
[quoted-message {:message-id response-to :chat-id chat-id} (:quoted-message message) true]]]))
(defmethod ->message constants/content-type-system-text [{:keys [content quoted-message] :as message}]
(if quoted-message

View File

@ -143,5 +143,8 @@
(def wallet-connect-project-id "87815d72a81d739d2a7ce15c2cfdefb3")
(def delete-message-undo-time-limit-ms 4000)
(def delete-message-for-me-undo-time-limit-ms 4000)
;;TODO for development only should be removed in status 2.0
(def new-ui-enabled? true)

View File

@ -1,10 +1,10 @@
(ns status-im2.subs.chat.messages
(:require [re-frame.core :as re-frame]
[status-im.chat.models.reactions :as models.reactions]
[status-im.chat.models.message-list :as models.message-list]
[status-im.chat.db :as chat.db]
[status-im.utils.datetime :as datetime]
[status-im.constants :as constants]))
[status-im.chat.models.message-list :as models.message-list]
[status-im.chat.models.reactions :as models.reactions]
[status-im.constants :as constants]
[status-im.utils.datetime :as datetime]))
(re-frame/reg-sub
:chats/chat-messages
@ -14,18 +14,26 @@
(re-frame/reg-sub
:chats/pinned
:<- [:messages/pin-messages]
(fn [pin-messages [_ chat-id]]
(get pin-messages chat-id {})))
(fn [[_ chat-id] _]
[(re-frame/subscribe [:messages/pin-messages])
(re-frame/subscribe [:chats/chat-messages chat-id])])
(fn [[pin-messages messages] [_ chat-id]]
(let [pin-messages (get pin-messages chat-id {})]
(reduce-kv (fn [acc message-id message]
(let [{:keys [deleted? deleted-for-me?]} (get messages message-id)]
(if (or deleted? deleted-for-me?)
acc
(assoc acc message-id message))))
{} pin-messages))))
(re-frame/reg-sub
:chats/pinned-sorted-list
:<- [:messages/pin-messages]
(fn [pin-messages [_ chat-id]]
(->>
(get pin-messages chat-id {})
vals
(sort-by :pinned-at <))))
(fn [[_ chat-id] _]
(re-frame/subscribe [:chats/pinned chat-id]))
(fn [pin-messages _]
(->> pin-messages
vals
(sort-by :pinned-at <))))
(re-frame/reg-sub
:chats/pin-modal

View File

@ -3,7 +3,7 @@
"_comment": "Instead use: scripts/update-status-go.sh <rev>",
"owner": "status-im",
"repo": "status-go",
"version": "v0.115.5",
"commit-sha1": "684e9654de4800df619ce593b5d331ebea9ed1a8",
"src-sha256": "0ci1s3w5jnf6sz2b6hn30gy14hp142gic006r6c3g5ln8y19wdbi"
"version": "v0.115.6",
"commit-sha1": "a97b5527305b9f23b15246c5489726f8cd758edd",
"src-sha256": "18x506xrxbpabfy0vn82bzyd0f6zmr5khclqbzfwymi5d1m35cjm"
}

View File

@ -876,6 +876,7 @@
"members-title": "Members",
"message": "Message",
"message-deleted": "Message deleted",
"message-deleted-for-everyone": "Message deleted for everyone",
"message-deleted-for-you": "Message deleted for you",
"message-not-sent": "Message not sent",
"message-options-cancel": "Cancel",