Remove chat / clear history

Signed-off-by: Andrea Maria Piana <andrea.maria.piana@gmail.com>
This commit is contained in:
Andrea Maria Piana 2018-05-14 17:32:44 +02:00
parent 97c96bbcea
commit 9cfb591068
No known key found for this signature in database
GPG Key ID: AA6CCA6DE0E06424
22 changed files with 444 additions and 177 deletions

View File

@ -342,38 +342,15 @@
(fn [cofx [chat]]
(models/upsert-chat chat cofx)))
(defn- remove-transport [chat-id {:keys [db] :as cofx}]
(let [{:keys [group-chat public?]} (get-in db [:chats chat-id])]
;; if this is private group chat, we have to broadcast leave and unsubscribe after that
(if (and group-chat (not public?))
(handlers-macro/merge-fx cofx (transport.message/send (group-chat/GroupLeave.) chat-id))
(handlers-macro/merge-fx cofx (transport/unsubscribe-from-chat chat-id)))))
(handlers/register-handler-fx
:leave-chat-and-navigate-home
[re-frame/trim-v]
(fn [cofx [chat-id]]
(defn remove-chat-and-navigate-home [cofx [chat-id]]
(handlers-macro/merge-fx cofx
(models/remove-chat chat-id)
(navigation/replace-view :home)
(remove-transport chat-id))))
(handlers/register-handler-fx
:leave-group-chat?
[re-frame/trim-v]
(fn [_ [chat-id]]
{:show-confirmation {:title (i18n/label :t/leave-confirmation)
:content (i18n/label :t/leave-group-chat-confirmation)
:confirm-button-text (i18n/label :t/leave)
:on-accept #(re-frame/dispatch [:leave-chat-and-navigate-home chat-id])}}))
(navigation/replace-view :home)))
(handlers/register-handler-fx
:remove-chat-and-navigate-home
[re-frame/trim-v]
(fn [cofx [chat-id]]
(handlers-macro/merge-fx cofx
(models/remove-chat chat-id)
(navigation/replace-view :home))))
remove-chat-and-navigate-home)
(handlers/register-handler-fx
:remove-chat-and-navigate-home?
@ -384,6 +361,19 @@
:confirm-button-text (i18n/label :t/delete)
:on-accept #(re-frame/dispatch [:remove-chat-and-navigate-home chat-id])}}))
(handlers/register-handler-fx
:clear-history
(fn [{{:keys [current-chat-id]} :db :as cofx} _]
(models/clear-history current-chat-id cofx)))
(handlers/register-handler-fx
:clear-history?
(fn [_ _]
{:show-confirmation {:title (i18n/label :t/clear-history-confirmation)
:content (i18n/label :t/clear-history-confirmation-content)
:confirm-button-text (i18n/label :t/clear)
:on-accept #(re-frame/dispatch [:clear-history])}}))
(handlers/register-handler-fx
:create-new-public-chat
[re-frame/trim-v]

View File

@ -1,9 +1,21 @@
(ns status-im.chat.models
(:require [status-im.ui.components.styles :as styles]
[status-im.utils.gfycat.core :as gfycat]
[status-im.transport.utils :as transport.utils]
[status-im.utils.clocks :as utils.clocks]
[status-im.transport.message.core :as transport.message]
[status-im.data-store.chats :as chats-store]
[status-im.transport.message.v1.group-chat :as transport.group-chat]
[status-im.utils.handlers-macro :as handlers-macro]
[status-im.data-store.messages :as messages-store]))
(defn multi-user-chat? [chat-id cofx]
(get-in cofx [:db :chats chat-id :group-chat]))
(defn group-chat? [chat-id cofx]
(and (multi-user-chat? chat-id cofx)
(not (get-in cofx [:db :chats chat-id :public?]))))
(defn set-chat-ui-props
"Updates ui-props in active chat by merging provided kvs into them"
[{:keys [current-chat-id] :as db} kvs]
@ -60,21 +72,50 @@
:group-admin admin
:contacts participants} cofx))
(defn new-update? [{:keys [added-to-at removed-at removed-from-at]} timestamp]
(and (> timestamp added-to-at)
(> timestamp removed-at)
(> timestamp removed-from-at)))
(defn clear-history [chat-id {:keys [db] :as cofx}]
(let [{:keys [messages
deleted-at-clock-value]} (get-in db [:chats chat-id])
last-message-clock-value (or (->> messages
vals
(sort-by (comp unchecked-negate :clock-value))
first
:clock-value)
deleted-at-clock-value
(utils.clocks/send 0))]
;; Necessary until we adjust merge-fx to cater for :txs
(-> (select-keys cofx [:data-store/tx :db])
(assoc-in [:db :chats chat-id :messages] {})
(assoc-in [:db :chats chat-id :message-groups] {})
(assoc-in [:db :chats chat-id :deleted-at-clock-value] last-message-clock-value)
(update :data-store/tx concat [(chats-store/clear-history-tx chat-id last-message-clock-value)
(messages-store/delete-messages-tx chat-id)]))))
(defn- remove-transport [chat-id {:keys [db] :as cofx}]
;; if this is private group chat, we have to broadcast leave and unsubscribe after that
(if (group-chat? chat-id cofx)
(transport.message/send (transport.group-chat/GroupLeave.) chat-id cofx)
(transport.utils/unsubscribe-from-chat chat-id cofx)))
(defn- deactivate-chat [chat-id {:keys [db now] :as cofx}]
(assoc-in {:db db
:data-store/tx [(chats-store/deactivate-chat-tx chat-id now)]}
[:db :chats chat-id :is-active] false))
;; TODO: There's a race condition here, as the removal of the filter (async)
;; is done at the same time as the removal of the chat, so a message
;; might come between and restore the chat. Multiple way to handle this
;; (remove chat only after the filter has been removed, probably the safest,
;; flag the chat to ignore new messages, change receive method for public/group chats)
;; For now to keep the code simplier and avoid significant changes, best to leave as it is.
(defn remove-chat [chat-id {:keys [db now] :as cofx}]
(let [{:keys [chat-id group-chat debug?]} (get-in db [:chats chat-id])]
(if debug?
(-> {:db db}
(update-in [:db :chats] dissoc chat-id)
(assoc :data-store/tx [(chats-store/delete-chat-tx chat-id)
(messages-store/delete-messages-tx chat-id)]))
(-> {:db db}
(assoc-in [:db :chats chat-id :is-active] false)
(assoc :data-store/tx [(chats-store/deactivate-chat-tx chat-id now)])))))
(letfn [(remove-transport-fx [chat-id cofx]
(when (multi-user-chat? chat-id cofx)
(remove-transport chat-id cofx)))]
(handlers-macro/merge-fx
cofx
(remove-transport-fx chat-id)
(deactivate-chat chat-id)
(clear-history chat-id))))
(defn bot-only-chat? [db chat-id]
(let [{:keys [group-chat contacts]} (get-in db [:chats chat-id])]

View File

@ -0,0 +1,77 @@
(ns status-im.chat.models.group-chat
(:require
[clojure.set :as set]
[status-im.i18n :as i18n]
[status-im.transport.utils :as transport.utils]
[status-im.ui.screens.group.core :as group]
[status-im.chat.models :as models.chat]
[status-im.transport.message.core :as message]
[status-im.transport.message.v1.group-chat :as transport.group-chat]
[status-im.utils.handlers-macro :as handlers-macro]
[status-im.chat.models.message :as models.message]))
(defn- participants-diff [existing-participants-set new-participants-set]
{:removed (set/difference existing-participants-set new-participants-set)
:added (set/difference new-participants-set existing-participants-set)})
(defn- prepare-system-message [admin-name added-participants removed-participants contacts]
(let [added-participants-names (map #(get-in contacts [% :name] %) added-participants)
removed-participants-names (map #(get-in contacts [% :name] %) removed-participants)]
(cond
(and (seq added-participants) (seq removed-participants))
(str admin-name " "
(i18n/label :t/invited) " " (apply str (interpose ", " added-participants-names))
" and "
(i18n/label :t/removed) " " (apply str (interpose ", " removed-participants-names)))
(seq added-participants)
(str admin-name " " (i18n/label :t/invited) " " (apply str (interpose ", " added-participants-names)))
(seq removed-participants)
(str admin-name " " (i18n/label :t/removed) " " (apply str (interpose ", " removed-participants-names))))))
(defn handle-group-admin-update [{:keys [chat-name participants]} chat-id signature {:keys [now db random-id] :as cofx}]
(let [me (:current-public-key db)]
;; we have to check if we already have a chat, or it's a new one
(if-let [{:keys [group-admin contacts] :as chat} (get-in db [:chats chat-id])]
;; update for existing group chat
(when (and (= signature group-admin) ;; make sure that admin is the one making changes
(not= (set contacts) (set participants))) ;; make sure it's actually changing something
(let [{:keys [removed added]} (participants-diff (set contacts) (set participants))
admin-name (or (get-in db [:contacts/contacts group-admin :name])
group-admin)]
(if (removed me) ;; we were removed
(handlers-macro/merge-fx cofx
(models.message/receive
(models.message/system-message chat-id random-id now
(str admin-name " " (i18n/label :t/removed-from-chat))))
(models.chat/upsert-chat {:chat-id chat-id
:removed-from-at now
:is-active false})
(transport.utils/unsubscribe-from-chat chat-id))
(handlers-macro/merge-fx cofx
(models.message/receive
(models.message/system-message chat-id random-id now
(prepare-system-message admin-name
added
removed
(:contacts/contacts db))))
(group/participants-added chat-id added)
(group/participants-removed chat-id removed)))))
;; first time we hear about chat -> create it if we are among participants
(when (get (set participants) me)
(models.chat/add-group-chat chat-id chat-name signature participants cofx)))))
(defn handle-group-leave [chat-id signature {:keys [db random-id now] :as cofx}]
(let [me (:current-public-key db)
participant-leaving-name (or (get-in db [:contacts/contacts signature :name])
signature)]
(when (and
(not= signature me)
(get-in db [:chats chat-id])) ;; chat is present
(handlers-macro/merge-fx cofx
(models.message/receive
(models.message/system-message chat-id random-id now
(str participant-leaving-name " " (i18n/label :t/left))))
(group/participants-removed chat-id #{signature})
(transport.group-chat/send-new-group-key nil chat-id)))))

View File

@ -106,10 +106,6 @@
(def ^:private- add-single-message (partial add-message false))
(def ^:private- add-batch-message (partial add-message true))
(defn- prepare-chat [chat-id {:keys [db now] :as cofx}]
(chat-model/upsert-chat {:chat-id chat-id
:timestamp now} cofx))
(defn- send-message-seen [chat-id message-id send-seen? cofx]
(when send-seen?
(transport/send (protocol/map->MessagesSeen {:message-ids #{message-id}}) chat-id cofx)))
@ -198,12 +194,18 @@
(#{:group-user-message :public-group-user-message} message-type))
(defn add-to-chat?
[{:keys [db]} {:keys [chat-id from message-id] :as message}]
[{:keys [db]} {:keys [chat-id
clock-value
from
message-id] :as message}]
(let [{:keys [chats current-public-key]} db
{:keys [messages not-loaded-message-ids]} (get chats chat-id)]
{:keys [deleted-at-clock-value
messages
not-loaded-message-ids]} (get chats chat-id)]
(when (not= from current-public-key)
(not (or (get messages message-id)
(get not-loaded-message-ids message-id))))))
(get not-loaded-message-ids message-id)
(>= deleted-at-clock-value clock-value))))))
(defn message-seen-by? [message user-pk]
(= :seen (get-in message [:user-statuses user-pk])))

View File

@ -18,24 +18,19 @@
{:label (i18n/label :t/delete-chat)
:action #(re-frame/dispatch [:remove-chat-and-navigate-home? chat-id group?])})
(defn- leave-group-chat [chat-id public?]
{:label (i18n/label (if public? :t/leave-public-chat :t/leave-group-chat))
:action #(re-frame/dispatch [:leave-group-chat? chat-id])})
(defn- chat-actions [chat-id]
[(view-profile chat-id)
(clear-history)
(delete-chat chat-id false)])
(defn- group-chat-actions [chat-id]
[(group-info chat-id)
(clear-history)
(delete-chat chat-id true)
(leave-group-chat chat-id false)])
(delete-chat chat-id true)])
(defn- public-chat-actions [chat-id]
[(clear-history)
(delete-chat chat-id true)
(leave-group-chat chat-id true)])
(delete-chat chat-id true)])
(defn actions [group-chat? chat-id public?]
(cond

View File

@ -3,6 +3,7 @@
[status-im.ui.components.react :as react]
[reagent.core :as reagent]
[status-im.native-module.core :as status]
status-im.transport.impl.receive
[taoensso.timbre :as log]
[status-im.utils.config :as config]
[status-im.react-native.js-dependencies :as js-dependencies]

View File

@ -37,6 +37,14 @@
(defn- get-chat-by-id [chat-id realm]
(core/single (core/get-by-field realm :chat :chat-id chat-id)))
(defn clear-history-tx
"Returns tx function for clearing the history of chat"
[chat-id deleted-at-clock-value]
(fn [realm]
(let [chat (get-chat-by-id chat-id realm)]
(doto chat
(aset "deleted-at-clock-value" deleted-at-clock-value)))))
(defn deactivate-chat-tx
"Returns tx function for deactivating chat"
[chat-id now]

View File

@ -1,6 +1,8 @@
(ns status-im.data-store.realm.schemas.account.core
(:require [status-im.data-store.realm.schemas.account.v1.core :as v1]
[status-im.data-store.realm.schemas.account.v2.core :as v2]))
(:require
[status-im.data-store.realm.schemas.account.v1.core :as v1]
[status-im.data-store.realm.schemas.account.v2.core :as v2]
[status-im.data-store.realm.schemas.account.v3.core :as v3]))
;; TODO(oskarth): Add failing test if directory vXX exists but isn't in schemas.
@ -10,5 +12,7 @@
:migration v1/migration}
{:schema v2/schema
:schemaVersion 2
:migration v2/migration}])
:migration v2/migration}
{:schema v3/schema
:schemaVersion 3
:migration v3/migration}])

View File

@ -0,0 +1,34 @@
(ns status-im.data-store.realm.schemas.account.v3.chat
(:require [status-im.ui.components.styles :refer [default-chat-color]]))
(def schema {:name :chat
:primaryKey :chat-id
:properties {:chat-id :string
:name :string
:color {:type :string
:default default-chat-color}
:group-chat {:type :bool
:indexed true}
:group-admin {:type :string
:optional true}
:is-active :bool
:timestamp :int
:contacts {:type "string[]"}
:removed-at {:type :int
:optional true}
:removed-from-at {:type :int
:optional true}
:deleted-at-clock-value {:type :int
:optional true}
:added-to-at {:type :int
:optional true}
:updated-at {:type :int
:optional true}
:message-overhead {:type :int
:default 0}
:contact-info {:type :string
:optional true}
:debug? {:type :bool
:default false}
:public? {:type :bool
:default false}}})

View File

@ -0,0 +1,27 @@
(ns status-im.data-store.realm.schemas.account.v3.core
(:require [status-im.data-store.realm.schemas.account.v3.chat :as chat]
[status-im.data-store.realm.schemas.account.v1.transport :as transport]
[status-im.data-store.realm.schemas.account.v1.contact :as contact]
[status-im.data-store.realm.schemas.account.v1.message :as message]
[status-im.data-store.realm.schemas.account.v1.request :as request]
[status-im.data-store.realm.schemas.account.v2.mailserver :as mailserver]
[status-im.data-store.realm.schemas.account.v1.user-status :as user-status]
[status-im.data-store.realm.schemas.account.v1.local-storage :as local-storage]
[status-im.data-store.realm.schemas.account.v1.browser :as browser]
[goog.object :as object]
[taoensso.timbre :as log]
[cljs.reader :as reader]
[clojure.string :as string]))
(def schema [chat/schema
transport/schema
contact/schema
message/schema
request/schema
mailserver/schema
user-status/schema
local-storage/schema
browser/schema])
(defn migration [old-realm new-realm]
(log/debug "migrating v3 account database: " old-realm new-realm))

View File

@ -296,7 +296,7 @@
:clear "Clear"
:clear-history "Clear history"
:clear-history-title "Clear history?"
:clear-group-history-confirmation "Are you sure you want to clear this group's chat history?"
:clear-history-confirmation-content "Are you sure you want to clear this chat history?"
:clear-history-confirmation "Clear history?"
:clear-history-action "Clear"
:mute-notifications "Mute notifications"

View File

@ -63,8 +63,6 @@
(fn [js-error js-message]
(re-frame/dispatch [:protocol/receive-whisper-message js-error js-message chat-id])))))
(def unsubscribe-from-chat transport.utils/unsubscribe-from-chat)
(defn stop-whisper
"Stops whisper protocol by removing all existing shh filters
It is necessary to remove the filters because status-go there isn't currently a logout feature in status-go

View File

@ -113,7 +113,7 @@
:group/unsubscribe-from-chat
[re-frame/trim-v]
(fn [cofx [chat-id]]
(transport/unsubscribe-from-chat chat-id cofx)))
(transport.utils/unsubscribe-from-chat chat-id cofx)))
(handlers/register-handler-fx
:group/send-new-sym-key

View File

@ -0,0 +1,34 @@
(ns status-im.transport.impl.receive
(:require
[status-im.chat.models.group-chat :as models.group-chat]
[status-im.ui.screens.contacts.core :as contacts]
[status-im.transport.message.core :as message]
[status-im.transport.message.v1.contact :as transport.contact]
[status-im.transport.message.v1.group-chat :as transport.group-chat]))
(extend-type transport.group-chat/GroupAdminUpdate
message/StatusMessage
(receive [this chat-id signature cofx]
(models.group-chat/handle-group-admin-update this chat-id signature cofx)))
(extend-type transport.group-chat/GroupLeave
message/StatusMessage
(receive [this chat-id signature cofx]
(models.group-chat/handle-group-leave chat-id signature cofx)))
(extend-type transport.contact/ContactRequest
message/StatusMessage
(receive [this chat-id signature cofx]
(contacts/receive-contact-request signature this cofx)))
(extend-type transport.contact/ContactRequestConfirmed
message/StatusMessage
(receive [this chat-id signature cofx]
(contacts/receive-contact-request-confirmation signature this cofx)))
(extend-type transport.contact/ContactUpdate
message/StatusMessage
(receive [this chat-id signature cofx]
(contacts/receive-contact-update chat-id
signature
this cofx)))

View File

@ -4,7 +4,6 @@
[status-im.transport.message.core :as message]
[status-im.transport.message.v1.protocol :as protocol]
[status-im.transport.utils :as transport.utils]
[status-im.ui.screens.contacts.core :as contacts]
[status-im.utils.handlers-macro :as handlers-macro]))
(defrecord NewContactKey [sym-key topic message]
@ -41,20 +40,14 @@
(handlers-macro/merge-fx cofx
{:shh/get-new-sym-keys [{:web3 (:web3 db)
:on-success on-success}]}
(protocol/init-chat chat-id topic))))
(receive [this chat-id signature {:keys [db] :as cofx}]
(contacts/receive-contact-request signature this cofx)))
(protocol/init-chat chat-id topic)))))
(defrecord ContactRequestConfirmed [name profile-image address fcm-token]
message/StatusMessage
(send [this chat-id cofx]
(handlers-macro/merge-fx cofx
(protocol/send {:chat-id chat-id
:payload this})))
(receive [this chat-id signature cofx]
(handlers-macro/merge-fx cofx
(contacts/receive-contact-request-confirmation signature
this))))
:payload this}))))
(defrecord ContactUpdate [name profile-image]
message/StatusMessage
@ -62,9 +55,4 @@
(let [public-keys (remove nil? (map :public-key (vals (:contacts/contacts db))))]
(handlers-macro/merge-fx cofx
(protocol/multi-send-by-pubkey {:public-keys public-keys
:payload this}))))
(receive [this chat-id signature cofx]
(handlers-macro/merge-fx cofx
(contacts/receive-contact-update chat-id
signature
this))))
:payload this})))))

View File

@ -1,13 +1,8 @@
(ns ^{:doc "Group chat API"}
status-im.transport.message.v1.group-chat
(:require [clojure.set :as set]
[re-frame.core :as re-frame]
(:require [re-frame.core :as re-frame]
[status-im.utils.handlers-macro :as handlers-macro]
[status-im.transport.message.core :as message]
[status-im.i18n :as i18n]
[status-im.ui.screens.group.core :as group]
[status-im.chat.models :as models.chat]
[status-im.chat.models.message :as models.message]
[status-im.transport.message.v1.protocol :as protocol]
[status-im.transport.utils :as transport.utils]))
@ -44,7 +39,7 @@
(= (get-in cofx [:db :chats chat-id :group-admin])
(get-in cofx [:db :current-public-key])))
(defn- send-new-group-key [message chat-id cofx]
(defn send-new-group-key [message chat-id cofx]
(when (user-is-group-admin? chat-id cofx)
{:shh/get-new-sym-keys [{:web3 (get-in cofx [:db :web3])
:on-success (fn [sym-key sym-key-id]
@ -55,83 +50,21 @@
:sym-key-id sym-key-id
:message message}]))}]}))
(defn- prepare-system-message [admin-name added-participants removed-participants contacts]
(let [added-participants-names (map #(get-in contacts [% :name] %) added-participants)
removed-participants-names (map #(get-in contacts [% :name] %) removed-participants)]
(cond
(and (seq added-participants) (seq removed-participants))
(str admin-name " "
(i18n/label :t/invited) " " (apply str (interpose ", " added-participants-names))
" and "
(i18n/label :t/removed) " " (apply str (interpose ", " removed-participants-names)))
(seq added-participants)
(str admin-name " " (i18n/label :t/invited) " " (apply str (interpose ", " added-participants-names)))
(seq removed-participants)
(str admin-name " " (i18n/label :t/removed) " " (apply str (interpose ", " removed-participants-names))))))
(defn- init-chat-if-new [chat-id cofx]
(if (nil? (get-in cofx [:db :transport/chats chat-id]))
(protocol/init-chat chat-id cofx)))
(defn- participants-diff [existing-participants-set new-participants-set]
{:removed (set/difference existing-participants-set new-participants-set)
:added (set/difference new-participants-set existing-participants-set)})
(defrecord GroupAdminUpdate [chat-name participants]
message/StatusMessage
(send [this chat-id cofx]
(handlers-macro/merge-fx cofx
(init-chat-if-new chat-id)
(send-new-group-key this chat-id)))
(receive [this chat-id signature {:keys [now db random-id] :as cofx}]
(let [me (:current-public-key db)]
;; we have to check if we already have a chat, or it's a new one
(if-let [{:keys [group-admin contacts] :as chat} (get-in db [:chats chat-id])]
;; update for existing group chat
(when (and (= signature group-admin) ;; make sure that admin is the one making changes
(not= (set contacts) (set participants))) ;; make sure it's actually changing something
(let [{:keys [removed added]} (participants-diff (set contacts) (set participants))
admin-name (or (get-in db [:contacts/contacts group-admin :name])
group-admin)]
(if (removed me) ;; we were removed
(handlers-macro/merge-fx cofx
(models.message/receive
(models.message/system-message chat-id random-id now
(str admin-name " " (i18n/label :t/removed-from-chat))))
(models.chat/upsert-chat {:chat-id chat-id
:removed-from-at now
:is-active false})
(transport.utils/unsubscribe-from-chat chat-id))
(handlers-macro/merge-fx cofx
(models.message/receive
(models.message/system-message chat-id random-id now
(prepare-system-message admin-name
added
removed
(:contacts/contacts db))))
(group/participants-added chat-id added)
(group/participants-removed chat-id removed)))))
;; first time we hear about chat -> create it if we are among participants
(when (get (set participants) me)
(models.chat/add-group-chat chat-id chat-name signature participants cofx))))))
(send-new-group-key this chat-id))))
(defrecord GroupLeave []
message/StatusMessage
(send [this chat-id cofx]
(protocol/send {:chat-id chat-id
:payload this
:success-event [::unsubscribe-from-chat chat-id]}
cofx))
(receive [this chat-id signature {:keys [db now random-id] :as cofx}]
(let [me (:current-public-key db)
participant-leaving-name (or (get-in db [:contacts/contacts signature :name])
signature)]
(when (get-in db [:chats chat-id]) ;; chat is present
(handlers-macro/merge-fx cofx
(models.message/receive
(models.message/system-message chat-id random-id now
(str participant-leaving-name " " (i18n/label :t/left))))
(group/participants-removed chat-id #{signature})
(send-new-group-key nil chat-id))))))
:success-event [:group/unsubscribe-from-chat chat-id]}
cofx)))

View File

@ -2,6 +2,7 @@
(:require [re-frame.core :as re-frame]
[status-im.i18n :as i18n]
[status-im.chat.models.message :as models.message]
[status-im.chat.models :as models.chat]
[status-im.ui.screens.navigation :as navigation]
[status-im.transport.message.v1.group-chat :as group-chat]
[status-im.transport.message.core :as transport]
@ -57,19 +58,3 @@
{:db (assoc-in db [:chats current-chat-id :name] new-chat-name)
:data-store/tx [(chats-store/save-chat-tx {:chat-id current-chat-id
:name new-chat-name})]}))
(handlers/register-handler-fx
:clear-history
(fn [{{:keys [current-chat-id] :as db} :db} _]
{:db (-> db
(assoc-in [:chats current-chat-id :messages] {})
(assoc-in [:chats current-chat-id :message-groups] {}))
:data-store/tx [(messages-store/hide-messages-tx current-chat-id)]}))
(handlers/register-handler-fx
:clear-history?
(fn [_ _]
{:show-confirmation {:title (i18n/label :t/clear-history-confirmation)
:content (i18n/label :t/clear-group-history-confirmation)
:confirm-button-text (i18n/label :t/clear)
:on-accept #(re-frame/dispatch [:clear-history])}}))

View File

@ -44,25 +44,17 @@
[{:label (i18n/label :t/clear-history)
:icon :icons/close
:action #(utils/show-confirmation (i18n/label :t/clear-history-title)
(i18n/label :t/clear-group-history-confirmation)
(i18n/label :t/clear-history-confirmation-content)
(i18n/label :t/clear-history-action)
(fn [] (re-frame/dispatch [:clear-history])))
:accessibility-label :clear-history-button}
{:label (i18n/label :t/delete-chat)
:icon :icons/delete
:action #(utils/show-confirmation (i18n/label :t/delete-chat-title)
(i18n/label :t/delete-group-chat-confirmation)
(i18n/label :t/delete)
(fn []
(re-frame/dispatch [:remove-chat-and-navigate-home chat-id])))
:accessibility-label :delete-chat-button}
{:label (i18n/label :t/leave-group)
:icon :icons/arrow-left
:action #(utils/show-confirmation (i18n/label :t/leave-group-title)
(i18n/label :t/leave-group-confirmation)
(i18n/label :t/leave-group-action)
(fn [] (re-frame/dispatch [:remove-chat-and-navigate-home chat-id])))
:accessibility-label :leave-chat-button}]))
:accessibility-label :delete-chat-button}]))
(defn contact-actions [contact]
[{:action #(re-frame/dispatch [:show-profile (:whisper-identity contact)])

View File

@ -1,5 +1,6 @@
(ns status-im.test.chat.models
(:require [cljs.test :refer-macros [deftest is testing]]
[status-im.utils.clocks :as utils.clocks]
[status-im.chat.models :as chat]))
(deftest upsert-chat-test
@ -100,3 +101,104 @@
(is (:group-chat chat)))
(testing "it does not sets the public flag"
(is (:public? chat)))))
(deftest clear-history-test
(let [chat-id "1"
cofx {:db {:chats {chat-id {:message-groups {:something "a"}
:messages {"1" {:clock-value 1}
"2" {:clock-value 10}
"3" {:clock-value 2}}}}}}]
(testing "it deletes all the messages"
(let [actual (chat/clear-history chat-id cofx)]
(is (= {} (get-in actual [:db :chats chat-id :messages])))))
(testing "it deletes all the message groups"
(let [actual (chat/clear-history chat-id cofx)]
(is (= {} (get-in actual [:db :chats chat-id :message-groups])))))
(testing "it sets a deleted-at-clock-value equal to the last message clock-value"
(let [actual (chat/clear-history chat-id cofx)]
(is (= 10 (get-in actual [:db :chats chat-id :deleted-at-clock-value])))))
(testing "it does not override the deleted-at-clock-value when there are no messages"
(let [actual (chat/clear-history chat-id
(update-in cofx
[:db :chats chat-id]
assoc
:messages {}
:deleted-at-clock-value 100))]
(is (= 100 (get-in actual [:db :chats chat-id :deleted-at-clock-value])))))
(testing "it set the deleted-at-clock-value to now the chat has no messages nor previous deleted-at"
(with-redefs [utils.clocks/send (constantly 42)]
(let [actual (chat/clear-history chat-id
(update-in cofx
[:db :chats chat-id]
assoc
:messages {}))]
(is (= 42 (get-in actual [:db :chats chat-id :deleted-at-clock-value]))))))
(testing "it adds the relevant transactions for realm"
(let [actual (chat/clear-history chat-id cofx)]
(is (:data-store/tx actual))
(is (= 2 (count (:data-store/tx actual))))))))
(deftest remove-chat-test
(let [chat-id "1"
cofx {:db {:transport/chats {chat-id {}}
:chats {chat-id {:messages {"1" {:clock-value 1}
"2" {:clock-value 10}
"3" {:clock-value 2}}}}}}]
(testing "it deletes all the messages"
(let [actual (chat/remove-chat chat-id cofx)]
(is (= {} (get-in actual [:db :chats chat-id :messages])))))
(testing "it sets a deleted-at-clock-value equal to the last message clock-value"
(let [actual (chat/remove-chat chat-id cofx)]
(is (= 10 (get-in actual [:db :chats chat-id :deleted-at-clock-value])))))
(testing "it sets the chat as inactive"
(let [actual (chat/remove-chat chat-id cofx)]
(is (= false (get-in actual [:db :chats chat-id :is-active])))))
(testing "it removes it from transport if it's a public chat"
(let [actual (chat/remove-chat chat-id
(update-in
cofx
[:db :chats chat-id]
assoc
:group-chat true
:public? true))]
(is (not (get-in actual [:db :transport/chats chat-id])))))
(testing "it sends a leave group request if it's a group-chat"
(let [actual (chat/remove-chat chat-id
(assoc-in
cofx
[:db :chats chat-id :group-chat]
true))]
(is (:shh/post actual))
(testing "it does not remove transport, only after send is successful"
(is (get-in actual [:db :transport/chats chat-id])))))
(testing "it does not remove it from transport if it's a one-to-one"
(let [actual (chat/remove-chat chat-id cofx)]
(is (get-in actual [:db :transport/chats chat-id]))))
(testing "it adds the relevant transactions for realm"
(let [actual (chat/remove-chat chat-id cofx)]
(is (:data-store/tx actual))
(is (= 3 (count (:data-store/tx actual))))))))
(deftest multi-user-chat?
(let [chat-id "1"]
(testing "it returns true if it's a group chat"
(let [cofx {:db {:chats {chat-id {:group-chat true}}}}]
(is (chat/multi-user-chat? chat-id cofx))))
(testing "it returns true if it's a public chat"
(let [cofx {:db {:chats {chat-id {:public? true :group-chat true}}}}]
(is (chat/multi-user-chat? chat-id cofx))))
(testing "it returns false if it's a 1-to-1 chat"
(let [cofx {:db {:chats {chat-id {}}}}]
(is (not (chat/multi-user-chat? chat-id cofx)))))))
(deftest group-chat?
(let [chat-id "1"]
(testing "it returns true if it's a group chat"
(let [cofx {:db {:chats {chat-id {:group-chat true}}}}]
(is (chat/group-chat? chat-id cofx))))
(testing "it returns false if it's a public chat"
(let [cofx {:db {:chats {chat-id {:public? true :group-chat true}}}}]
(is (not (chat/group-chat? chat-id cofx)))))
(testing "it returns false if it's a 1-to-1 chat"
(let [cofx {:db {:chats {chat-id {}}}}]
(is (not (chat/group-chat? chat-id cofx)))))))

View File

@ -9,22 +9,44 @@
(is (message/add-to-chat? {:db {:chats {"a" {}}}}
{:message-id "message-id"
:from "a"
:clock-value 1
:chat-id "a"})))
(testing "it returns false when from is the same as pk"
(is (not (message/add-to-chat? {:db {:current-public-key "pk"
:chats {"a" {}}}}
{:message-id "message-id"
:from "pk"
:clock-value 1
:chat-id "a"}))))
(testing "it returns false when it's already in the loaded message"
(is (not (message/add-to-chat? {:db {:chats {"a" {:messages {"message-id" {}}}}}}
{:message-id "message-id"
:from "a"
:clock-value 1
:chat-id "a"}))))
(testing "it returns false when it's already in the not-loaded-message-ids"
(is (not (message/add-to-chat? {:db {:chats {"a" {:not-loaded-message-ids #{"message-id"}}}}}
{:message-id "message-id"
:from "a"
:clock-value 1
:chat-id "a"}))))
(testing "it returns false when the clock-value is the same as the deleted-clock-value in chat"
(is (not (message/add-to-chat? {:db {:chats {"a" {:deleted-at-clock-value 1}}}}
{:message-id "message-id"
:from "a"
:clock-value 1
:chat-id "a"}))))
(testing "it returns true when the clock-value is greater than the deleted-clock-value in chat"
(is (message/add-to-chat? {:db {:chats {"a" {:deleted-at-clock-value 1}}}}
{:message-id "message-id"
:from "a"
:clock-value 2
:chat-id "a"})))
(testing "it returns false when the clock-value is less than the deleted-clock-value in chat"
(is (not (message/add-to-chat? {:db {:chats {"a" {:deleted-at-clock-value 1}}}}
{:message-id "message-id"
:from "a"
:clock-value 0
:chat-id "a"})))))
(deftest receive-send-seen

View File

@ -22,6 +22,7 @@
[status-im.test.protocol.web3.inbox]
[status-im.test.utils.utils]
[status-im.test.utils.money]
[status-im.test.utils.handlers-macro]
[status-im.test.utils.clocks]
[status-im.test.utils.inbox]
[status-im.test.utils.ethereum.eip681]
@ -66,6 +67,7 @@
'status-im.test.transport.inbox
'status-im.test.protocol.web3.inbox
'status-im.test.utils.utils
'status-im.test.utils.handlers-macro
'status-im.test.utils.money
'status-im.test.utils.clocks
'status-im.test.utils.inbox

View File

@ -0,0 +1,32 @@
(ns status-im.test.utils.handlers-macro
(:require [cljs.test :refer-macros [deftest is testing]]
[status-im.utils.handlers-macro :as m]))
(deftest merge-fx
(letfn [(add-b [cofx]
(assoc-in cofx [:db :b] "b"))
(add-c [cofx]
(assoc-in cofx [:db :c] "c"))
(add-tx [tx cofx]
(assoc cofx :data-store/tx [tx]))]
(testing "it updates db correctly"
(let [actual (m/merge-fx {:db {:a "a"}}
(add-b)
(add-c))]
(is (= {:db {:a "a"
:b "b"
:c "c"}} actual))))
(testing "it updates db correctly when a fn don't update it"
(let [empty-fn (constantly nil)
actual (m/merge-fx {:db {:a "a"}}
(add-b)
(empty-fn)
(add-c))]
(is (= {:db {:a "a"
:b "b"
:c "c"}} actual))))
#_(testing "it updates data-store/tx correctly"
(let [actual (m/merge-fx {:data-store/tx [1]}
(add-tx 2)
(add-tx 3))]
(is (= {:data-store/tx [1 2 3]} actual))))))