diff --git a/resources/images/icons2/16x16/sad-face@2x.png b/resources/images/icons2/16x16/sad-face@2x.png new file mode 100644 index 0000000000..7d1c01eedf Binary files /dev/null and b/resources/images/icons2/16x16/sad-face@2x.png differ diff --git a/resources/images/icons2/16x16/sad-face@3x.png b/resources/images/icons2/16x16/sad-face@3x.png new file mode 100644 index 0000000000..4d9d75d2b7 Binary files /dev/null and b/resources/images/icons2/16x16/sad-face@3x.png differ diff --git a/src/quo2/components/messages/system_message.cljs b/src/quo2/components/messages/system_message.cljs index 82113b6144..ef6f51f0cf 100644 --- a/src/quo2/components/messages/system_message.cljs +++ b/src/quo2/components/messages/system_message.cljs @@ -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 diff --git a/src/status_im/chat/models.cljs b/src/status_im/chat/models.cljs index 15856c5a67..bf7ccba309 100644 --- a/src/status_im/chat/models.cljs +++ b/src/status_im/chat/models.cljs @@ -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)))) diff --git a/src/status_im/chat/models/delete_message.cljs b/src/status_im/chat/models/delete_message.cljs new file mode 100644 index 0000000000..976be11803 --- /dev/null +++ b/src/status_im/chat/models/delete_message.cljs @@ -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)))) diff --git a/src/status_im/chat/models/delete_message_for_me.cljs b/src/status_im/chat/models/delete_message_for_me.cljs index a275747343..6cb010e94d 100644 --- a/src/status_im/chat/models/delete_message_for_me.cljs +++ b/src/status_im/chat/models/delete_message_for_me.cljs @@ -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)))) diff --git a/src/status_im/chat/models/delete_message_for_me_test.cljs b/src/status_im/chat/models/delete_message_for_me_test.cljs index 2e583745a5..830bbae5b6 100644 --- a/src/status_im/chat/models/delete_message_for_me_test.cljs +++ b/src/status_im/chat/models/delete_message_for_me_test.cljs @@ -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)))))) diff --git a/src/status_im/chat/models/delete_message_test.cljs b/src/status_im/chat/models/delete_message_test.cljs new file mode 100644 index 0000000000..77fa42a316 --- /dev/null +++ b/src/status_im/chat/models/delete_message_test.cljs @@ -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)))))) diff --git a/src/status_im/chat/models/input.cljs b/src/status_im/chat/models/input.cljs index 453ab26951..5a1bf718b4 100644 --- a/src/status_im/chat/models/input.cljs +++ b/src/status_im/chat/models/input.cljs @@ -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]} diff --git a/src/status_im/chat/models/message.cljs b/src/status_im/chat/models/message.cljs index 4199e4cd0a..0ef78dad50 100644 --- a/src/status_im/chat/models/message.cljs +++ b/src/status_im/chat/models/message.cljs @@ -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 diff --git a/src/status_im/chat/models/message_test.cljs b/src/status_im/chat/models/message_test.cljs index fa3731b94c..baa8c30dc9 100644 --- a/src/status_im/chat/models/message_test.cljs +++ b/src/status_im/chat/models/message_test.cljs @@ -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"]))))))) diff --git a/src/status_im/constants.cljs b/src/status_im/constants.cljs index 970f1ad11e..eaac0b62be 100644 --- a/src/status_im/constants.cljs +++ b/src/status_im/constants.cljs @@ -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) diff --git a/src/status_im/data_store/messages.cljs b/src/status_im/data_store/messages.cljs index f7f805c420..334f9f0172 100644 --- a/src/status_im/data_store/messages.cljs +++ b/src/status_im/data_store/messages.cljs @@ -32,6 +32,7 @@ :quotedMessage :quoted-message :outgoingStatus :outgoing-status :audioDurationMs :audio-duration-ms + :deleted :deleted? :deletedForMe :deleted-for-me? :new :new?}) diff --git a/src/status_im/navigation/core.cljs b/src/status_im/navigation/core.cljs index d11343d9e7..1146d52048 100644 --- a/src/status_im/navigation/core.cljs +++ b/src/status_im/navigation/core.cljs @@ -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))})) diff --git a/src/status_im/ui/screens/chat/sheets.cljs b/src/status_im/ui/screens/chat/sheets.cljs index c4b394cc8d..687933e5ec 100644 --- a/src/status_im/ui/screens/chat/sheets.cljs +++ b/src/status_im/ui/screens/chat/sheets.cljs @@ -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])] diff --git a/src/status_im/ui2/screens/chat/components/reply.cljs b/src/status_im/ui2/screens/chat/components/reply.cljs index 5b9ee199d7..89a57e6299 100644 --- a/src/status_im/ui2/screens/chat/components/reply.cljs +++ b/src/status_im/ui2/screens/chat/components/reply.cljs @@ -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 [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 [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 (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 diff --git a/src/status_im/utils/config.cljs b/src/status_im/utils/config.cljs index 8c6682bf7f..4630e0915c 100644 --- a/src/status_im/utils/config.cljs +++ b/src/status_im/utils/config.cljs @@ -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) diff --git a/src/status_im2/subs/chat/messages.cljs b/src/status_im2/subs/chat/messages.cljs index 297bc8f158..8d1e169983 100644 --- a/src/status_im2/subs/chat/messages.cljs +++ b/src/status_im2/subs/chat/messages.cljs @@ -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 diff --git a/status-go-version.json b/status-go-version.json index 3231aec623..715895bb09 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.115.5", - "commit-sha1": "684e9654de4800df619ce593b5d331ebea9ed1a8", - "src-sha256": "0ci1s3w5jnf6sz2b6hn30gy14hp142gic006r6c3g5ln8y19wdbi" + "version": "v0.115.6", + "commit-sha1": "a97b5527305b9f23b15246c5489726f8cd758edd", + "src-sha256": "18x506xrxbpabfy0vn82bzyd0f6zmr5khclqbzfwymi5d1m35cjm" } diff --git a/translations/en.json b/translations/en.json index 51d83bd886..a7e42f67dd 100644 --- a/translations/en.json +++ b/translations/en.json @@ -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",