Move message processing to status-go and introduce protobuf

This commit moves all the processing of messages to status-go.

Messages are going arrive to status-react already saved an processed.

Receiving/sending/retrieving from db is now using the same identical
structure. The only processing left in status-react is to mark the
messages as seen and update the unviewed count locally (only
status-react knows whether the count should be updated).

Partially remove commands as well as won't be used anymore.

Signed-off-by: Andrea Maria Piana <andrea.maria.piana@gmail.com>
This commit is contained in:
Andrea Maria Piana 2019-11-26 14:15:19 +01:00
parent bee7804fd0
commit 78d694f52f
No known key found for this signature in database
GPG Key ID: AA6CCA6DE0E06424
44 changed files with 462 additions and 883 deletions

View File

@ -57,6 +57,8 @@ var TopLevel = {
"code" : function () {}, "code" : function () {},
"concat" : function () {}, "concat" : function () {},
"confirmMessagesProcessed" : function () {}, "confirmMessagesProcessed" : function () {},
"chats": function() {},
"rawMessages": function() {},
"messages": function() {}, "messages": function() {},
"discovery": function() {}, "discovery": function() {},
"dismiss": function() {}, "dismiss": function() {},

View File

@ -140,7 +140,7 @@ class NewMessageSignalHandler {
void handleNewMessageSignal(JSONObject newMessageSignal) { void handleNewMessageSignal(JSONObject newMessageSignal) {
try { try {
JSONArray chatsNewMessagesData = newMessageSignal.getJSONObject("event").getJSONArray("messages"); JSONArray chatsNewMessagesData = newMessageSignal.getJSONObject("event").getJSONArray("chats");
for (int i = 0; i < chatsNewMessagesData.length(); i++) { for (int i = 0; i < chatsNewMessagesData.length(); i++) {
try { try {
upsertChat(chatsNewMessagesData.getJSONObject(i)); upsertChat(chatsNewMessagesData.getJSONObject(i));
@ -148,6 +148,15 @@ class NewMessageSignalHandler {
Log.e(TAG, "JSON conversion failed: " + e.getMessage()); Log.e(TAG, "JSON conversion failed: " + e.getMessage());
} }
} }
JSONArray messagesNewMessagesData = newMessageSignal.getJSONObject("event").getJSONArray("messages");
for (int i = 0; i < messagesNewMessagesData.length(); i++) {
try {
upsertMessage(messagesNewMessagesData.getJSONObject(i));
} catch (JSONException e) {
Log.e(TAG, "JSON conversion failed: " + e.getMessage());
}
}
if(shouldRefreshNotifications) { if(shouldRefreshNotifications) {
refreshNotifications(); refreshNotifications();
shouldRefreshNotifications = false; shouldRefreshNotifications = false;
@ -173,72 +182,69 @@ class NewMessageSignalHandler {
return person; return person;
} }
private void upsertChat(JSONObject chatNewMessagesData) { private void upsertChat(JSONObject chatData) {
try { try {
JSONObject chatData = chatNewMessagesData.getJSONObject("chat");
// NOTE: this is an exemple of chatData // NOTE: this is an exemple of chatData
// {"chatId":"contact-discovery-3622","filterId":"c0239d63f830e8b25f4bf7183c8d207f355a925b89514a17068cae4898e7f193", // {"chatId":"contact-discovery-3622","filterId":"c0239d63f830e8b25f4bf7183c8d207f355a925b89514a17068cae4898e7f193",
// "symKeyId":"","oneToOne":true,"identity":"046599511623d7385b926ce709ac57d518dac10d451a81f75cd32c7fb4b1c...", // "symKeyId":"","oneToOne":true,"identity":"046599511623d7385b926ce709ac57d518dac10d451a81f75cd32c7fb4b1c...",
// "topic":"0xc446561b","discovery":false,"negotiated":false,"listen":true} // "topic":"0xc446561b","discovery":false,"negotiated":false,"listen":true}
boolean oneToOne = chatData.getBoolean("oneToOne"); int oneToOne = chatData.getInt("chatType");
// NOTE: for now we only notify one to one chats // NOTE: for now we only notify one to one chats
// TODO: also notifiy on mentions, keywords and group chats // TODO: also notifiy on mentions, keywords and group chats
// TODO: one would have to pass the ens name and keywords to notify on when instanciating the class as well // TODO: one would have to pass the ens name and keywords to notify on when instanciating the class as well
// as have a method to add new ones after the handler is instanciated // as have a method to add new ones after the handler is instanciated
if (oneToOne) { if (oneToOne == 1) {
JSONArray messagesData = chatNewMessagesData.getJSONArray("messages"); //JSONArray messagesData = chatNewMessagesData.getJSONArray("messages");
// there is no proper id for oneToOne chat in chatData so we peek into first message sig // there is no proper id for oneToOne chat in chatData so we peek into first message sig
// TODO: won't work for sync becaus it could be our own message // TODO: won't work for sync becaus it could be our own message
String id = messagesData.getJSONObject(0).getJSONObject("message").getString("sig"); String id = chatData.getString("id");
StatusChat chat = chats.get(id); StatusChat chat = chats.get(id);
// if the chat was not already there, we create one // if the chat was not already there, we create one
if (chat == null) { if (chat == null) {
chat = new StatusChat(id, oneToOne); chat = new StatusChat(id, true);
} }
ArrayList<StatusMessage> messages = chat.getMessages();
// parse the new messages
for (int j = 0; j < messagesData.length(); j++) {
StatusMessage message = createMessage(messagesData.getJSONObject(j));
if (message != null) {
messages.add(message);
}
}
if (!messages.isEmpty()) {
chat.setMessages(messages);
chats.put(id, chat); chats.put(id, chat);
shouldRefreshNotifications = true;
}
} }
} catch (JSONException e) { } catch (JSONException e) {
Log.e(TAG, "JSON conversion failed: " + e.getMessage()); Log.e(TAG, "JSON conversion failed: " + e.getMessage());
} }
} }
private StatusMessage createMessage(JSONObject messageData) {
try {
JSONObject metadata = messageData.getJSONObject("metadata"); private void upsertMessage(JSONObject messageData) {
JSONObject authorMetadata = metadata.getJSONObject("author"); try {
JSONArray payload = new JSONArray(messageData.getString("payload")); String chatId = messageData.getString("localChatId");
// NOTE: this is an exemple of payload we are currently working with StatusChat chat = chats.get(chatId);
// it is in the transit format, which is basically JSON if (chat == null) {
// refer to `transport.message.transit.cljs` on react side for details return;
// ["~#c4",["7","text/plain","~:public-group-user-message",157201130275201,1572011302752,["^ ","~:chat-id","test","~:text","7"]]]
if (payload.getString(0).equals("~#c4")) {
Person author = getPerson(authorMetadata.getString("publicKey"), authorMetadata.getString("identicon"), authorMetadata.getString("alias"));
JSONArray payloadContent = payload.getJSONArray(1);
String text = payloadContent.getString(0);
Double timestamp = payloadContent.getDouble(4);
return new StatusMessage(author, timestamp.longValue(), text);
}
} catch (JSONException e) {
Log.e(TAG, "JSON conversion failed: " + e.getMessage());
} }
return null;
StatusMessage message = createMessage(messageData);
if (message != null) {
chat.appendMessage(message);
chats.put(chatId, chat);
shouldRefreshNotifications = true;
}
}
catch (JSONException e) {
Log.e(TAG, "JSON conversion failed: " + e.getMessage());
}
}
private StatusMessage createMessage(JSONObject messageData) {
try {
Person author = getPerson(messageData.getString("from"), messageData.getString("identicon"), messageData.getString("alias"));
return new StatusMessage(author, messageData.getLong("whisperTimestamp"), messageData.getString("text"));
} catch (JSONException e) {
Log.e(TAG, "JSON conversion failed: " + e.getMessage());
}
return null;
} }
} }
@ -252,6 +258,7 @@ class StatusChat {
this.id = id; this.id = id;
this.oneToOne = oneToOne; this.oneToOne = oneToOne;
this.messages = new ArrayList<StatusMessage>(); this.messages = new ArrayList<StatusMessage>();
this.name = name;
} }
public String getId() { public String getId() {
@ -259,15 +266,23 @@ class StatusChat {
} }
public String getName() { public String getName() {
//TODO this should be improved as it would rename the chat
// after our own user if we were posting from another device //TODO this should be improved as it would rename the chat
// in 1-1 chats it should be the name of the user whose // after our own user if we were posting from another device
// key is different than ours // in 1-1 chats it should be the name of the user whose
return getLastMessage().getAuthor().getName().toString(); // key is different than ours
StatusMessage message = getLastMessage();
if (message == null) {
return "no-name";
}
return message.getAuthor().getName().toString();
} }
private StatusMessage getLastMessage() { private StatusMessage getLastMessage() {
if (messages.size() > 0) {
return messages.get(messages.size()-1); return messages.get(messages.size()-1);
}
return null;
} }
public long getTimestamp() { public long getTimestamp() {
@ -278,8 +293,8 @@ class StatusChat {
return messages; return messages;
} }
public void setMessages(ArrayList<StatusMessage> messages) { public void appendMessage(StatusMessage message) {
this.messages = messages; this.messages.add(message);
} }
public String getSummary() { public String getSummary() {

View File

@ -114,13 +114,6 @@
[{:keys [db] :as cofx} chat-id] [{:keys [db] :as cofx} chat-id]
(chats-store/save-chat cofx (get-in db [:chats chat-id]))) (chats-store/save-chat cofx (get-in db [:chats chat-id])))
(fx/defn save-chat-delayed
"Debounce saving the chat"
[_ chat-id]
{:dispatch-debounce [{:key :save-chat
:event [::save-chat chat-id]
:delay 500}]})
(fx/defn add-public-chat (fx/defn add-public-chat
"Adds new public group chat to db" "Adds new public group chat to db"
[cofx topic] [cofx topic]
@ -139,12 +132,9 @@
"Clears history of the particular chat" "Clears history of the particular chat"
[{:keys [db] :as cofx} chat-id] [{:keys [db] :as cofx} chat-id]
(let [{:keys [messages (let [{:keys [messages
last-message
deleted-at-clock-value]} (get-in db [:chats chat-id]) deleted-at-clock-value]} (get-in db [:chats chat-id])
last-message-clock-value (or (->> messages last-message-clock-value (or (:clock-value last-message)
vals
(sort-by (comp unchecked-negate :clock-value))
first
:clock-value)
deleted-at-clock-value deleted-at-clock-value
(utils.clocks/send 0))] (utils.clocks/send 0))]
(fx/merge (fx/merge
@ -152,9 +142,7 @@
{:db (update-in db [:chats chat-id] merge {:db (update-in db [:chats chat-id] merge
{:messages {} {:messages {}
:message-list nil :message-list nil
:last-message-content nil :last-message nil
:last-message-content-type nil
:last-message-timestamp nil
:unviewed-messages-count 0 :unviewed-messages-count 0
:deleted-at-clock-value last-message-clock-value})} :deleted-at-clock-value last-message-clock-value})}
(messages-store/delete-messages-by-chat-id chat-id) (messages-store/delete-messages-by-chat-id chat-id)
@ -204,13 +192,11 @@
[{:keys [db] :as cofx} {:keys [chat-id loaded-unviewed-messages-ids]}] [{:keys [db] :as cofx} {:keys [chat-id loaded-unviewed-messages-ids]}]
(let [{:keys [loaded-unviewed-messages-ids unviewed-messages-count]} (let [{:keys [loaded-unviewed-messages-ids unviewed-messages-count]}
(get-in db [:chats chat-id])] (get-in db [:chats chat-id])]
(upsert-chat {:db (update-in db [:chats chat-id] assoc
cofx :unviewed-messages-count (subtract-seen-messages
{:chat-id chat-id unviewed-messages-count
:unviewed-messages-count (subtract-seen-messages loaded-unviewed-messages-ids)
unviewed-messages-count :loaded-unviewed-messages-ids #{})}))
loaded-unviewed-messages-ids)
:loaded-unviewed-messages-ids #{}})))
(fx/defn mark-messages-seen (fx/defn mark-messages-seen
"Marks all unviewed loaded messages as seen in particular chat" "Marks all unviewed loaded messages as seen in particular chat"
@ -224,7 +210,7 @@
true)) true))
db db
loaded-unviewed-ids)} loaded-unviewed-ids)}
(messages-store/mark-messages-seen loaded-unviewed-ids) (messages-store/mark-messages-seen chat-id loaded-unviewed-ids)
(update-chats-unviewed-messages-count {:chat-id chat-id}) (update-chats-unviewed-messages-count {:chat-id chat-id})
(when platform/desktop? (when platform/desktop?
(update-dock-badge-label)))))) (update-dock-badge-label))))))

View File

@ -100,12 +100,9 @@
:content-type (if emoji? :content-type (if emoji?
constants/content-type-emoji constants/content-type-emoji
constants/content-type-text) constants/content-type-text)
:content (cond-> {:chat-id current-chat-id :text input-text
:text input-text} :response-to message-id
message-id :ens-name preferred-name})
(assoc :response-to message-id)
preferred-name
(assoc :name preferred-name))})
(set-chat-input-text nil) (set-chat-input-text nil)
(process-cooldown))))) (process-cooldown)))))
@ -114,10 +111,9 @@
(when-not (string/blank? hash) (when-not (string/blank? hash)
(chat.message/send-message cofx {:chat-id current-chat-id (chat.message/send-message cofx {:chat-id current-chat-id
:content-type constants/content-type-sticker :content-type constants/content-type-sticker
:content (cond-> {:chat-id current-chat-id :sticker {:hash hash
:hash hash :pack pack}
:pack pack :text "Update to latest version to see a nice sticker here!"})))
:text "Update to latest version to see a nice sticker here!"})})))
(fx/defn send-current-message (fx/defn send-current-message
"Sends message from current chat input" "Sends message from current chat input"

View File

@ -9,7 +9,7 @@
[status-im.chat.models.message-content :as message-content] [status-im.chat.models.message-content :as message-content]
[status-im.constants :as constants] [status-im.constants :as constants]
[status-im.contact.db :as contact.db] [status-im.contact.db :as contact.db]
[status-im.data-store.messages :as messages-store] [status-im.data-store.messages :as data-store.messages]
[status-im.ethereum.core :as ethereum] [status-im.ethereum.core :as ethereum]
[status-im.mailserver.core :as mailserver] [status-im.mailserver.core :as mailserver]
[status-im.native-module.core :as status] [status-im.native-module.core :as status]
@ -26,40 +26,20 @@
[status-im.utils.types :as types] [status-im.utils.types :as types]
[taoensso.timbre :as log])) [taoensso.timbre :as log]))
(defn- wrap-group-message
"Wrap a group message in a membership update"
[cofx chat-id message]
(when-let [chat (get-in cofx [:db :chats chat-id])]
(message.group-chat/map->GroupMembershipUpdate.
{:chat-id chat-id
:membership-updates (:membership-updates chat)
:message message})))
(defn- prepare-message (defn- prepare-message
[{:keys [content content-type] :as message} chat-id current-chat?] [{:keys [content content-type] :as message} chat-id current-chat?]
(cond-> message (cond-> message
current-chat? current-chat?
(assoc :seen true) (and (= constants/content-type-text content-type) (assoc :seen true)
(message-content/should-collapse?
(:text content)
(:line-count content)))
(and (= constants/content-type-text content-type)
(message-content/should-collapse?
(:text content)
(:line-count content)))
(assoc :should-collapse? true))) (assoc :should-collapse? true)))
(defn system-message? [{:keys [message-type]}] (defn system-message? [{:keys [message-type]}]
(= :system-message message-type)) (= constants/message-type-private-group-system-message message-type))
(defn add-outgoing-status
[{:keys [from outgoing-status] :as message} current-public-key]
(if (and (= from current-public-key)
(not (system-message? message)))
(assoc message
:outgoing true
;; We don't override outgoing-status if there, which means
;; that our device has sent the message, while if empty is coming
;; from a different device
:outgoing-status (or outgoing-status :sent))
message))
(defn build-desktop-notification (defn build-desktop-notification
[{:keys [db] :as cofx} {:keys [chat-id timestamp content from] :as message}] [{:keys [db] :as cofx} {:keys [chat-id timestamp content from] :as message}]
@ -81,16 +61,10 @@
(fx/defn add-message (fx/defn add-message
[{:keys [db] :as cofx} [{:keys [db] :as cofx}
{{:keys [chat-id message-id clock-value timestamp from] :as message} :message {{:keys [chat-id message-id timestamp from] :as message} :message
:keys [current-chat? batch?]}] :keys [current-chat?]}]
(let [current-public-key (multiaccounts.model/current-public-key cofx) (let [current-public-key (multiaccounts.model/current-public-key cofx)
prepared-message (-> message prepared-message (prepare-message message chat-id current-chat?)]
(prepare-message chat-id current-chat?)
(add-outgoing-status current-public-key))
chat-initialized?
(or
current-chat?
(get-in db [:chats chat-id :messages-initialized?]))]
(when (and platform/desktop? (when (and platform/desktop?
(not= from current-public-key) (not= from current-public-key)
(get-in db [:multiaccount :desktop-notifications?]) (get-in db [:multiaccount :desktop-notifications?])
@ -100,7 +74,6 @@
(fx/merge cofx (fx/merge cofx
{:db (cond-> {:db (cond->
(-> db (-> db
(update-in [:chats chat-id :last-clock-value] (partial utils.clocks/receive clock-value))
;; We should not be always adding to the list, as it does not make sense ;; We should not be always adding to the list, as it does not make sense
;; if the chat has not been initialized, but run into ;; if the chat has not been initialized, but run into
;; some troubles disabling it, so next time ;; some troubles disabling it, so next time
@ -110,25 +83,21 @@
(not= from current-public-key)) (not= from current-public-key))
(update-in [:chats chat-id :loaded-unviewed-messages-ids] (update-in [:chats chat-id :loaded-unviewed-messages-ids]
(fnil conj #{}) message-id))} (fnil conj #{}) message-id))}
#(messages-store/save-message % prepared-message)
(when (and platform/desktop? (when (and platform/desktop?
(not batch?)
(not (system-message? prepared-message))) (not (system-message? prepared-message)))
(chat-model/update-dock-badge-label))))) (chat-model/update-dock-badge-label)))))
(fx/defn add-received-message (fx/defn add-received-message
[{:keys [db] :as cofx} [{:keys [db] :as cofx}
{:keys [from message-id chat-id content metadata] :as message}] {:keys [from message-id chat-id content] :as message}]
(let [{:keys [current-chat-id view-id]} db (let [{:keys [current-chat-id view-id]} db
current-public-key (multiaccounts.model/current-public-key cofx)
current-chat? (and (or (= :chat view-id) current-chat? (and (or (= :chat view-id)
(= :chat-modal view-id)) (= :chat-modal view-id))
(= current-chat-id chat-id))] (= current-chat-id chat-id))]
(add-message cofx {:batch? true (fx/merge cofx
:message message (add-message {:message message
:metadata metadata :current-chat? current-chat?}))))
:current-chat? current-chat?})))
(defn- add-to-chat? (defn- add-to-chat?
[{:keys [db]} {:keys [chat-id clock-value message-id from]}] [{:keys [db]} {:keys [chat-id clock-value message-id from]}]
@ -140,43 +109,36 @@
(defn extract-chat-id [cofx {:keys [chat-id from message-type]}] (defn extract-chat-id [cofx {:keys [chat-id from message-type]}]
"Validate and return a valid chat-id" "Validate and return a valid chat-id"
(cond (cond
(and (= :group-user-message message-type) (and (= constants/message-type-private-group message-type)
(and (get-in cofx [:db :chats chat-id :contacts from]) (and (get-in cofx [:db :chats chat-id :contacts from])
(get-in cofx [:db :chats chat-id :members-joined (multiaccounts.model/current-public-key cofx)]))) chat-id (get-in cofx [:db :chats chat-id :members-joined (multiaccounts.model/current-public-key cofx)]))) chat-id
(and (= :public-group-user-message message-type) (and (= constants/message-type-public-group message-type)
(get-in cofx [:db :chats chat-id :public?])) chat-id (get-in cofx [:db :chats chat-id :public?])) chat-id
(and (= :user-message message-type) (and (= constants/message-type-one-to-one message-type)
(= (multiaccounts.model/current-public-key cofx) from)) chat-id (= (multiaccounts.model/current-public-key cofx) from)) chat-id
(= :user-message message-type) from)) (= constants/message-type-one-to-one message-type) from))
(defn calculate-unviewed-message-count (fx/defn update-unviewed-count
[{:keys [db] :as cofx} {:keys [chat-id from]}] [{:keys [now db] :as cofx} {:keys [chat-id
from
message-id] :as message}]
(let [{:keys [current-chat-id view-id]} db (let [{:keys [current-chat-id view-id]} db
chat-view? (or (= :chat view-id) chat-view? (or (= :chat view-id)
(= :chat-modal view-id)) (= :chat-modal view-id))
current-count (get-in db [:chats chat-id :unviewed-messages-count])] current-count (get-in db [:chats chat-id :unviewed-messages-count])]
(if (or (and chat-view? (= current-chat-id chat-id)) (cond
(= from (multiaccounts.model/current-public-key cofx))) (= from (multiaccounts.model/current-public-key cofx))
current-count ;; nothing to do
(inc current-count)))) nil
(fx/defn update-unviewed-count [{:keys [now db] :as cofx} {:keys [chat-id] :as message}] (and chat-view? (= current-chat-id chat-id))
{:db (update-in db [:chats chat-id] (fx/merge cofx
assoc (data-store.messages/mark-messages-seen current-chat-id [message-id]))
:is-active true
:timestamp now
:unviewed-messages-count (calculate-unviewed-message-count cofx message))})
(fx/defn update-last-message [{:keys [db]} {:keys [clock-value chat-id content timestamp content-type]}] :else
(let [last-chat-clock-value (get-in db [:chats chat-id :last-message-clock-value])]
;; We should also compare message-id in case of clashes, but not sure it's worth
(when (> clock-value last-chat-clock-value)
{:db (update-in db [:chats chat-id] {:db (update-in db [:chats chat-id]
assoc assoc
:last-message-clock-value clock-value :unviewed-messages-count (inc current-count))})))
:last-message-content content
:last-message-timestamp timestamp
:last-message-content-type content-type)})))
(fx/defn receive-one (fx/defn receive-one
[cofx message] [cofx message]
@ -184,128 +146,32 @@
(let [message-with-chat-id (assoc message :chat-id chat-id)] (let [message-with-chat-id (assoc message :chat-id chat-id)]
(when (add-to-chat? cofx message-with-chat-id) (when (add-to-chat? cofx message-with-chat-id)
(fx/merge cofx (fx/merge cofx
(chat-model/ensure-chat {:chat-id chat-id})
(add-received-message message-with-chat-id) (add-received-message message-with-chat-id)
(update-unviewed-count message-with-chat-id) (update-unviewed-count message-with-chat-id)
(chat-model/join-time-messages-checked chat-id) (chat-model/join-time-messages-checked chat-id)
(update-last-message message-with-chat-id)
(when platform/desktop? (when platform/desktop?
(chat-model/update-dock-badge-label)) (chat-model/update-dock-badge-label)))))))
;; And save chat
(chat-model/save-chat-delayed chat-id))))))
(defn system-message [{:keys [now] :as cofx} {:keys [clock-value chat-id content from]}]
(let [{:keys [last-clock-value]} (get-in cofx [:db :chats chat-id])
message {:chat-id chat-id
:from from
:timestamp now
:whisper-timestamp now
:clock-value (or clock-value
(utils.clocks/send last-clock-value))
:content content
:message-type :system-message
:content-type constants/content-type-status}]
(assoc message
:message-id (transport.utils/system-message-id message))))
(defn group-message? [{:keys [message-type]}]
(#{:group-user-message :public-group-user-message} message-type))
;;;; Send message ;;;; Send message
(fx/defn send
[{{:keys [peers-count]} :db :as cofx} chat-id message send-record]
(protocol/send send-record chat-id (assoc cofx :message message)))
(defn add-message-type [message {:keys [chat-id group-chat public?]}]
(cond-> message
(not group-chat)
(assoc :message-type :user-message)
(and group-chat public?)
(assoc :message-type :public-group-user-message)
(and group-chat (not public?))
(assoc :message-type :group-user-message)))
(def ^:private transport-keys [:content :content-type :message-type :clock-value :timestamp :name]) (def ^:private transport-keys [:content :content-type :message-type :clock-value :timestamp :name])
(defn remove-icon
"Coin's icon's resource is represented as a function,
can't be properly de/serialised and has to be removed."
[message]
(cond-> message
(get-in message [:content :params :coin :icon :source])
(update-in [:content :params :coin] dissoc :icon)))
(fx/defn add-message-with-id [cofx message chat-id]
(when (and message
(not (get-in cofx [:db :chats chat-id :messages (:message-id message)])))
(add-message cofx {:batch? false
:message message
:current-chat? (= (get-in cofx [:db :current-chat-id]) chat-id)})))
(fx/defn prepare-message-content [cofx chat-id message]
{::json-rpc/call
[{:method "shhext_prepareContent"
:params [(:content message)]
:on-success #(re-frame/dispatch [::prepared-message chat-id message %])
:on-failure #(log/error "failed to prepare content" %)}]})
(fx/defn prepared-message
{:events [::prepared-message]}
[{:keys [now] :as cofx} chat-id message content]
(let [message-with-content
(update message :content
assoc
:parsed-text (:parsedText content)
:line-count (:lineCount content)
:should-collapse? (message-content/should-collapse?
(:text content)
(:lineCount content))
:rtl? (:rtl content))
send-record (protocol/map->Message
(select-keys message-with-content transport-keys))
wrapped-record (if (= (:message-type send-record) :group-user-message)
(wrap-group-message cofx chat-id send-record)
send-record)]
(fx/merge cofx
(chat-model/upsert-chat
{:chat-id chat-id
:timestamp now
:last-message-timestamp (:timestamp message-with-content)
:last-message-content (:content message-with-content)
:last-message-content-type (:content-type message-with-content)
:last-clock-value (:clock-value message-with-content)})
(send chat-id message-with-content wrapped-record))))
(fx/defn upsert-and-send
[{:keys [now] :as cofx} {:keys [chat-id from] :as message}]
(let [message (remove-icon message)
message (assoc message :outgoing-status :sending)]
(prepare-message-content cofx chat-id message)))
(fx/defn update-message-status (fx/defn update-message-status
[{:keys [db] :as cofx} chat-id message-id status] [{:keys [db] :as cofx} chat-id message-id status]
(fx/merge cofx (fx/merge cofx
{:db (assoc-in db {:db (assoc-in db
[:chats chat-id :messages message-id :outgoing-status] [:chats chat-id :messages message-id :outgoing-status]
status)} status)}
(messages-store/update-outgoing-status message-id status))) (data-store.messages/update-outgoing-status message-id status)))
(fx/defn resend-message (fx/defn resend-message
[cofx chat-id message-id] [{:keys [db] :as cofx} chat-id message-id]
(let [message (get-in cofx [:db :chats chat-id :messages message-id]) (fx/merge cofx
send-record (-> message {::json-rpc/call [{:method "shhext_reSendChatMessage"
(select-keys transport-keys) :params [message-id]
(update :message-type keyword) :on-success #(log/debug "re-sent message successfully")
protocol/map->Message) :on-error #(log/error "failed to re-send message" %)}]}
(update-message-status chat-id message-id :sending)))
wrapped-record (if (= (:message-type send-record) :group-user-message)
(wrap-group-message cofx chat-id send-record)
send-record)]
(fx/merge cofx
(send chat-id message wrapped-record)
(update-message-status chat-id message-id :sending))))
(fx/defn rebuild-message-list (fx/defn rebuild-message-list
[{:keys [db]} chat-id] [{:keys [db]} chat-id]
@ -317,35 +183,26 @@
[{:keys [db] :as cofx} chat-id message-id] [{:keys [db] :as cofx} chat-id message-id]
(fx/merge cofx (fx/merge cofx
{:db (update-in db [:chats chat-id :messages] dissoc message-id)} {:db (update-in db [:chats chat-id :messages] dissoc message-id)}
(messages-store/delete-message message-id) (data-store.messages/delete-message message-id)
(rebuild-message-list chat-id))) (rebuild-message-list chat-id)))
(fx/defn handle-saved-system-messages
{:events [:messages/system-messages-saved]}
[cofx messages]
(apply fx/merge cofx (map #(add-message {:message %
:current-chat? true})
messages)))
(fx/defn add-system-messages [cofx messages] (fx/defn add-system-messages [cofx messages]
(let [messages-fx (map #(add-message (data-store.messages/save-system-messages cofx messages))
{:batch false
:message (system-message cofx %)
:current-chat? true})
messages)]
(apply fx/merge cofx messages-fx)))
(fx/defn send-message (fx/defn send-message
[{:keys [db now] :as cofx} {:keys [chat-id] :as message}] [{:keys [db now] :as cofx} {:keys [chat-id] :as message}]
(let [{:keys [chats]} db (let [{:keys [chats]} db
{:keys [last-clock-value] :as chat} (get chats chat-id)
message-data (-> message message-data (-> message
(assoc :from (multiaccounts.model/current-public-key cofx) (tribute-to-talk/add-transaction-hash db))]
:timestamp now (protocol/send-chat-message cofx message-data)))
:whisper-timestamp now
:clock-value (utils.clocks/send
last-clock-value))
(tribute-to-talk/add-transaction-hash db)
(add-message-type chat))]
(upsert-and-send cofx message-data)))
(fx/defn toggle-expand-message (fx/defn toggle-expand-message
[{:keys [db]} chat-id message-id] [{:keys [db]} chat-id message-id]
{:db (update-in db [:chats chat-id :messages message-id :expanded?] not)}) {:db (update-in db [:chats chat-id :messages message-id :expanded?] not)})
(fx/defn confirm-message-processed
[_ raw-message]
{:transport/confirm-messages-processed [raw-message]})

View File

@ -2,6 +2,7 @@
(:require (:require
[status-im.js-dependencies :as dependencies] [status-im.js-dependencies :as dependencies]
[taoensso.timbre :as log] [taoensso.timbre :as log]
[status-im.constants :as constants]
[status-im.utils.fx :as fx] [status-im.utils.fx :as fx]
[status-im.chat.db :as chat.db] [status-im.chat.db :as chat.db]
[status-im.utils.datetime :as time])) [status-im.utils.datetime :as time]))
@ -20,12 +21,13 @@
whisper-timestamp]}] whisper-timestamp]}]
(-> {:whisper-timestamp whisper-timestamp (-> {:whisper-timestamp whisper-timestamp
:from from :from from
:one-to-one? (= :user-message message-type) :one-to-one? (= constants/message-type-one-to-one message-type)
:system-message? (= :system-message message-type) :system-message? (= constants/message-type-private-group-system-message
message-type)
:clock-value clock-value :clock-value clock-value
:type :message :type :message
:message-id message-id :message-id message-id
:outgoing outgoing} :outgoing (boolean outgoing)}
add-datemark add-datemark
add-timestamp)) add-timestamp))
@ -65,7 +67,8 @@
We divide messages in groups. Messages are sorted descending so :first? is We divide messages in groups. Messages are sorted descending so :first? is
the most recent message, similarly :first-in-group? is the most recent message the most recent message, similarly :first-in-group? is the most recent message
in a group." in a group."
[{:keys [one-to-one? outgoing] :as current-message} [{:keys [system-message?
one-to-one? outgoing] :as current-message}
{:keys [outgoing-seen?] :as previous-message} {:keys [outgoing-seen?] :as previous-message}
next-message] next-message]
(let [last-in-group? (or (nil? next-message) (let [last-in-group? (or (nil? next-message)
@ -80,6 +83,7 @@
(not (same-group? current-message previous-message))) (not (same-group? current-message previous-message)))
:last-in-group? last-in-group? :last-in-group? last-in-group?
:display-username? (and last-in-group? :display-username? (and last-in-group?
(not system-message?)
(not outgoing) (not outgoing)
(not one-to-one?)) (not one-to-one?))
:display-photo? (display-photo? current-message)))) :display-photo? (display-photo? current-message))))
@ -100,10 +104,13 @@
(defn update-previous-message (defn update-previous-message
"If this is a new group, we mark the previous as the last one in the group" "If this is a new group, we mark the previous as the last one in the group"
[current-message {:keys [one-to-one? outgoing] :as previous-message}] [current-message {:keys [one-to-one?
system-message?
outgoing] :as previous-message}]
(let [last-in-group? (not (same-group? current-message previous-message))] (let [last-in-group? (not (same-group? current-message previous-message))]
(assoc previous-message (assoc previous-message
:display-username? (and last-in-group? :display-username? (and last-in-group?
(not system-message?)
(not outgoing) (not outgoing)
(not one-to-one?)) (not one-to-one?))
:last-in-group? last-in-group?))) :last-in-group? last-in-group?)))

View File

@ -7,10 +7,15 @@
(def ms-in-bg-for-require-bioauth 5000) (def ms-in-bg-for-require-bioauth 5000)
(def content-type-text "text/plain") (def content-type-text 0)
(def content-type-sticker "sticker") (def content-type-sticker 1)
(def content-type-status "status") (def content-type-status 2)
(def content-type-emoji "emoji") (def content-type-emoji 3)
(def message-type-one-to-one 0)
(def message-type-public-group 1)
(def message-type-private-group 2)
(def message-type-private-group-system-message 3)
(def desktop-content-types (def desktop-content-types
#{content-type-text content-type-emoji content-type-status}) #{content-type-text content-type-emoji content-type-status})

View File

@ -23,9 +23,7 @@
public-key public-key
{:keys [chat-id {:keys [chat-id
unviewed-messages-count unviewed-messages-count
last-message-content last-message]}]
last-message-timestamp
last-message-content-type]}]
(let [removed-messages-ids (keep (let [removed-messages-ids (keep
(fn [[message-id {:keys [from]}]] (fn [[message-id {:keys [from]}]]
(when (= from public-key) (when (= from public-key)
@ -41,9 +39,7 @@
(update-in [:chats chat-id] (update-in [:chats chat-id]
assoc assoc
:unviewed-messages-count unviewed-messages-count :unviewed-messages-count unviewed-messages-count
:last-message-content last-message-content :last-message last-message))]
:last-message-timestamp last-message-timestamp
:last-message-content-type last-message-content-type))]
{:db (update-in db [:chats chat-id :message-list] message-list/add-many (vals (get-in db [:chats chat-id :messages])))})) {:db (update-in db [:chats chat-id :message-list] message-list/add-many (vals (get-in db [:chats chat-id :messages])))}))
(fx/defn contact-blocked (fx/defn contact-blocked

View File

@ -115,7 +115,7 @@
(->> members (->> members
(map #(or (get all-contacts %) (map #(or (get all-contacts %)
(public-key->new-contact %))) (public-key->new-contact %)))
(sort-by (comp clojure.string/lower-case :name)) (sort-by (comp clojure.string/lower-case #(or (:name %) (:alias %))))
(map #(if (get admins (:public-key %)) (map #(if (get admins (:public-key %))
(assoc % :admin? true) (assoc % :admin? true)
%))))) %)))))

View File

@ -111,13 +111,11 @@
type->rpc type->rpc
marshal-members marshal-members
(update :membership-updates marshal-membership-updates) (update :membership-updates marshal-membership-updates)
(utils/update-if-present :last-message-content messages/prepare-content) (update :last-message messages/->rpc)
(clojure.set/rename-keys {:chat-id :id (clojure.set/rename-keys {:chat-id :id
:membership-updates :membershipUpdates :membership-updates :membershipUpdates
:unviewed-messages-count :unviewedMessagesCount :unviewed-messages-count :unviewedMessagesCount
:last-message-content :lastMessageContent :last-message :lastMessage
:last-message-content-type :lastMessageContentType
:last-message-timestamp :lastMessageTimestamp
:deleted-at-clock-value :deletedAtClockValue :deleted-at-clock-value :deletedAtClockValue
:is-active :active :is-active :active
:last-clock-value :lastClockValue}) :last-clock-value :lastClockValue})
@ -133,16 +131,13 @@
unmarshal-members unmarshal-members
(clojure.set/rename-keys {:id :chat-id (clojure.set/rename-keys {:id :chat-id
:membershipUpdates :membership-updates :membershipUpdates :membership-updates
:unviewedMessagesCount :unviewed-messages-count
:lastMessageContent :last-message-content
:lastMessageContentType :last-message-content-type
:lastMessageTimestamp :last-message-timestamp
:deletedAtClockValue :deleted-at-clock-value :deletedAtClockValue :deleted-at-clock-value
:unviewedMessagesCount :unviewed-messages-count
:lastMessage :last-message
:active :is-active :active :is-active
:lastClockValue :last-clock-value}) :lastClockValue :last-clock-value})
(update :membership-updates (partial unmarshal-membership-updates (:id chat))) (update :membership-updates (partial unmarshal-membership-updates (:id chat)))
(update :last-message-content utils/safe-read-message-content) (update :last-message #(when % (messages/<-rpc %)))
(update :last-clock-value utils.clocks/safe-timestamp)
(dissoc :chatType :members))) (dissoc :chatType :members)))
(fx/defn save-chat [cofx {:keys [chat-id] :as chat}] (fx/defn save-chat [cofx {:keys [chat-id] :as chat}]

View File

@ -10,55 +10,37 @@
[status-im.constants :as constants] [status-im.constants :as constants]
[status-im.utils.core :as utils])) [status-im.utils.core :as utils]))
(defn prepare-content [content] (defn ->rpc [{:keys [content] :as message}]
(if (string? content) (cond-> message
content content
(utils.types/clj->json content))) (assoc :text (:text content)
:sticker (:sticker content))
(defn ->rpc [message] :always
(-> message (clojure.set/rename-keys {:chat-id :chatId
(dissoc :dedup-id) :clock-value :clock})))
(update :message-type name)
(update :outgoing-status #(if % (name %) ""))
(utils/update-if-present :content prepare-content)
(clojure.set/rename-keys {:message-id :id
:whisper-timestamp :whisperTimestamp
:message-type :messageType
:chat-id :chatId
:content-type :contentType
:clock-value :clockValue
:outgoing-status :outgoingStatus})
(assoc :replyTo (get-in message [:content :response-to]))))
(defn update-quoted-message [message]
(let [parsed-content (utils/safe-read-message-content (get-in message [:quotedMessage :content]))]
(cond-> message
parsed-content
(assoc :quoted-message {:from (get-in message [:quotedMessage :from])
:text (:text parsed-content)})
:always
(dissoc message :quotedMessage))))
(defn <-rpc [message] (defn <-rpc [message]
(when-let [parsed-content (utils/safe-read-message-content (:content message))] (-> message
(let [outgoing-status (when-not (empty? (:outgoingStatus message)) (clojure.set/rename-keys {:id :message-id
(keyword (:outgoingStatus message)))] :whisperTimestamp :whisper-timestamp
:messageType :message-type
:localChatId :chat-id
:contentType :content-type
:clock :clock-value
:quotedMessage :quoted-message
:outgoingStatus :outgoing-status})
(-> message (update :outgoing-status keyword)
(update :messageType keyword) (assoc :content {:chat-id (:chatId message)
(update :outgoingStatus keyword) :text (:text message)
(assoc :content parsed-content :sticker (:sticker message)
:outgoingStatus outgoing-status :ens-name (:ensName message)
:outgoing outgoing-status) :line-count (:lineCount message)
(update-quoted-message) :parsed-text (:parsedText message)
(clojure.set/rename-keys {:id :message-id :rtl (:rtl message)
:whisperTimestamp :whisper-timestamp :response-to (:responseTo message)}
:messageType :message-type :outgoing (boolean (:outgoingStatus message)))
:chatId :chat-id (dissoc :ensName :chatId :text :rtl :responseTo :sticker :lineCount :parsedText)))
:contentType :content-type
:replyTo :reply-to
:clockValue :clock-value
:outgoingStatus :outgoing-status})))))
(defn update-outgoing-status-rpc [message-id status] (defn update-outgoing-status-rpc [message-id status]
{::json-rpc/call [{:method "shhext_updateMessageOutgoingStatus" {::json-rpc/call [{:method "shhext_updateMessageOutgoingStatus"
@ -66,12 +48,11 @@
:on-success #(log/debug "updated message outgoing stauts" message-id status) :on-success #(log/debug "updated message outgoing stauts" message-id status)
:on-failure #(log/error "failed to update message outgoing status" message-id status %)}]}) :on-failure #(log/error "failed to update message outgoing status" message-id status %)}]})
(defn save-messages-rpc [messages] (defn save-system-messages-rpc [messages]
(let [confirmations (keep :metadata messages)] (json-rpc/call {:method "shhext_addSystemMessages"
(json-rpc/call {:method "shhext_saveMessages" :params [(map ->rpc messages)]
:params [(map ->rpc messages)] :on-success #(re-frame/dispatch [:messages/system-messages-saved (map <-rpc %)])
:on-success #(re-frame/dispatch [:message/messages-persisted confirmations]) :on-failure #(log/error "failed to save messages" %)}))
:on-failure #(log/error "failed to save messages" %)})))
(defn messages-by-chat-id-rpc [chat-id cursor limit on-success] (defn messages-by-chat-id-rpc [chat-id cursor limit on-success]
{::json-rpc/call [{:method "shhext_chatMessages" {::json-rpc/call [{:method "shhext_chatMessages"
@ -80,9 +61,9 @@
(on-success (update result :messages #(map <-rpc %)))) (on-success (update result :messages #(map <-rpc %))))
:on-failure #(log/error "failed to get messages" %)}]}) :on-failure #(log/error "failed to get messages" %)}]})
(defn mark-seen-rpc [ids] (defn mark-seen-rpc [chat-id ids]
{::json-rpc/call [{:method "shhext_markMessagesSeen" {::json-rpc/call [{:method "shhext_markMessagesSeen"
:params [ids] :params [chat-id ids]
:on-success #(log/debug "successfully marked as seen") :on-success #(log/debug "successfully marked as seen")
:on-failure #(log/error "failed to get messages" %)}]}) :on-failure #(log/error "failed to get messages" %)}]})
@ -105,28 +86,12 @@
:on-failure #(log/error "failed to delete messages by chat-id" % chat-id)}]}) :on-failure #(log/error "failed to delete messages by chat-id" % chat-id)}]})
(re-frame/reg-fx (re-frame/reg-fx
::save-message ::save-system-message
(fn [messages] (fn [messages]
(save-messages-rpc messages))) (save-system-messages-rpc messages)))
(fx/defn save-messages [{:keys [db]}] (fx/defn save-system-messages [cofx messages]
(when-let [messages (vals (:messages/stored db))] {::save-system-message messages})
;; Pull message from database to pick up most recent changes, default to
;; stored one in case it has been offloaded
(let [hydrated-messages (map #(get-in db [:chats (-> % :content :chat-id) :messages (:message-id %)] %) messages)]
{:db (dissoc db :messages/stored)
::save-message hydrated-messages})))
(fx/defn handle-save-messages
{:events [::save-messages]}
[cofx]
(save-messages cofx))
(fx/defn save-message [{:keys [db]} {:keys [message-id] :as message}]
{:db (assoc-in db [:messages/stored message-id] message)
:dispatch-debounce [{:key :save-messages
:event [::save-messages]
:delay 500}]})
(fx/defn delete-message [cofx id] (fx/defn delete-message [cofx id]
(delete-message-rpc id)) (delete-message-rpc id))
@ -134,8 +99,8 @@
(fx/defn delete-messages-from [cofx author] (fx/defn delete-messages-from [cofx author]
(delete-messages-from-rpc author)) (delete-messages-from-rpc author))
(fx/defn mark-messages-seen [_ ids] (fx/defn mark-messages-seen [_ chat-id ids]
(mark-seen-rpc ids)) (mark-seen-rpc chat-id ids))
(fx/defn update-outgoing-status [cofx message-id status] (fx/defn update-outgoing-status [cofx message-id status]
(update-outgoing-status-rpc message-id status)) (update-outgoing-status-rpc message-id status))

View File

@ -291,8 +291,8 @@
(stateofus/valid-username? ens-name)))) (stateofus/valid-username? ens-name))))
(fx/defn verify-names-from-message [cofx {:keys [content]} signature] (fx/defn verify-names-from-message [cofx {:keys [content]} signature]
(when (should-be-verified? cofx (:name content) signature) (when (should-be-verified? cofx (:ens-name content) signature)
{::verify-names [{:name (:name content) {::verify-names [{:name (:ens-name content)
:publicKey (subs signature 2)}]})) :publicKey (subs signature 2)}]}))
(fx/defn verify-names-from-contact-request [cofx {:keys [name]} signature] (fx/defn verify-names-from-contact-request [cofx {:keys [name]} signature]

View File

@ -35,13 +35,15 @@
"shhext_sendPublicMessage" {} "shhext_sendPublicMessage" {}
"shhext_enableInstallation" {} "shhext_enableInstallation" {}
"shhext_disableInstallation" {} "shhext_disableInstallation" {}
"shhext_sendChatMessage" {}
"shhext_reSendChatMessage" {}
"shhext_getOurInstallations" {} "shhext_getOurInstallations" {}
"shhext_setInstallationMetadata" {} "shhext_setInstallationMetadata" {}
"shhext_loadFilters" {} "shhext_loadFilters" {}
"shhext_loadFilter" {} "shhext_loadFilter" {}
"shhext_removeFilters" {} "shhext_removeFilters" {}
"shhext_chats" {} "shhext_chats" {}
"shhext_saveMessages" {} "shhext_addSystemMessages" {}
"shhext_deleteMessagesFrom" {} "shhext_deleteMessagesFrom" {}
"shhext_deleteMessagesByChatID" {} "shhext_deleteMessagesByChatID" {}
"shhext_deleteMessage" {} "shhext_deleteMessage" {}

View File

@ -3,7 +3,7 @@
[re-frame.core :as re-frame] [re-frame.core :as re-frame]
[status-im.multiaccounts.core :as multiaccounts] [status-im.multiaccounts.core :as multiaccounts]
[status-im.data-store.messages :as data-store.messages] [status-im.data-store.messages :as data-store.messages]
[status-im.data-store.chats :as data-store.chats]
[status-im.multiaccounts.create.core :as multiaccounts.create] [status-im.multiaccounts.create.core :as multiaccounts.create]
[status-im.multiaccounts.login.core :as multiaccounts.login] [status-im.multiaccounts.login.core :as multiaccounts.login]
[status-im.multiaccounts.logout.core :as multiaccounts.logout] [status-im.multiaccounts.logout.core :as multiaccounts.logout]
@ -172,10 +172,7 @@
(handlers/register-handler-fx (handlers/register-handler-fx
:multiaccounts.logout.ui/logout-confirmed :multiaccounts.logout.ui/logout-confirmed
(fn [cofx _] (fn [cofx _]
(fx/merge (multiaccounts.logout/logout cofx)))
cofx
(data-store.messages/save-messages)
(multiaccounts.logout/logout))))
;; multiaccounts update module ;; multiaccounts update module
@ -551,8 +548,12 @@
(handlers/register-handler-fx (handlers/register-handler-fx
:chat.ui/resend-message :chat.ui/resend-message
(fn [cofx [_ chat-id message-id]] (fn [{:keys [db] :as cofx} [_ chat-id message-id]]
(chat.message/resend-message cofx chat-id message-id))) (let [message (get-in db [:chats chat-id :messages message-id])]
(fx/merge
cofx
(transport.message/set-message-envelope-hash chat-id message-id (:message-type message) 1)
(chat.message/resend-message chat-id message-id)))))
(handlers/register-handler-fx (handlers/register-handler-fx
:chat.ui/delete-message :chat.ui/delete-message
@ -616,16 +617,6 @@
(fn [cofx [_ chat-id message-id status]] (fn [cofx [_ chat-id message-id status]]
(chat.message/update-message-status cofx chat-id message-id status))) (chat.message/update-message-status cofx chat-id message-id status)))
(handlers/register-handler-fx
:message/messages-persisted
(fn [cofx [_ raw-messages]]
(apply fx/merge
cofx
(map
(fn [raw-message]
(chat.message/confirm-message-processed raw-message))
raw-messages))))
;; signal module ;; signal module
(handlers/register-handler-fx (handlers/register-handler-fx
@ -1204,13 +1195,20 @@
(fn [{:keys [db] :as cofx} [_ err]] (fn [{:keys [db] :as cofx} [_ err]]
(log/error :send-status-message-error err))) (log/error :send-status-message-error err)))
(fx/defn handle-update [cofx {:keys [chats messages] :as response}]
(let [chats (map data-store.chats/<-rpc chats)
messages (map data-store.messages/<-rpc messages)
message-fxs (map chat.message/receive-one messages)
chat-fxs (map #(chat/ensure-chat (dissoc % :unviewed-messages-count)) chats)]
(apply fx/merge cofx (concat chat-fxs message-fxs))))
(handlers/register-handler-fx (handlers/register-handler-fx
:transport/message-sent :transport/message-sent
(fn [cofx [_ chat-id message message-type message-id messages-count]] (fn [cofx [_ response messages-count]]
(fx/merge cofx (let [{:keys [localChatId id messageType]} (-> response :messages first)]
(when message (chat.message/add-message-with-id (assoc message :message-id message-id) chat-id)) (fx/merge cofx
(handle-update response)
(transport.message/set-message-envelope-hash chat-id message-id message-type messages-count)))) (transport.message/set-message-envelope-hash localChatId id messageType messages-count)))))
(handlers/register-handler-fx (handlers/register-handler-fx
:transport/contact-message-sent :transport/contact-message-sent

View File

@ -5,6 +5,7 @@
[clojure.string :as string] [clojure.string :as string]
[status-im.ethereum.json-rpc :as json-rpc] [status-im.ethereum.json-rpc :as json-rpc]
[re-frame.core :as re-frame] [re-frame.core :as re-frame]
[status-im.constants :as constants]
[status-im.chat.models.message-content :as message-content] [status-im.chat.models.message-content :as message-content]
[status-im.multiaccounts.core :as multiaccounts] [status-im.multiaccounts.core :as multiaccounts]
[status-im.multiaccounts.model :as multiaccounts.model] [status-im.multiaccounts.model :as multiaccounts.model]
@ -141,7 +142,7 @@
:success-event [:transport/message-sent :success-event [:transport/message-sent
chat-id chat-id
(:message cofx) (:message cofx)
:group-user-message] constants/message-type-private-group]
:payload payload}})))) :payload payload}}))))
(fx/defn handle-membership-update-received (fx/defn handle-membership-update-received
@ -374,7 +375,7 @@
(let [get-contact (partial models.contact/build-contact cofx) (let [get-contact (partial models.contact/build-contact cofx)
format-message (fn [contact text clock-value] format-message (fn [contact text clock-value]
{:chat-id chat-id {:chat-id chat-id
:content {:text text} :text text
:clock-value clock-value :clock-value clock-value
:from (:public-key contact)}) :from (:public-key contact)})
creator-contact (when creator (get-contact creator)) creator-contact (when creator (get-contact creator))
@ -465,43 +466,6 @@
(transport.filters/upsert-group-chat-topics) (transport.filters/upsert-group-chat-topics)
(transport.filters/load-members members))))) (transport.filters/load-members members)))))
(fx/defn prepared-message
{:events [::prepared-message]}
[{:keys [now] :as cofx}
chat-id message
content
sender-signature
whisper-timestamp
metadata]
(let [message-with-content
(update message :content
assoc
:parsed-text (:parsedText content)
:line-count (:lineCount content)
:should-collapse? (message-content/should-collapse?
(:text content)
(:lineCount content))
:rtl? (:rtl content))]
(protocol/receive message-with-content
chat-id
sender-signature
whisper-timestamp
(assoc cofx :metadata metadata))))
(fx/defn prepare-message-content
[cofx chat-id message sender-signature whisper-timestamp metadata]
{::json-rpc/call
[{:method "shhext_prepareContent"
:params [(:content message)]
:on-success #(re-frame/dispatch [::prepared-message
chat-id
message
%
sender-signature
whisper-timestamp
metadata])
:on-failure #(log/error "failed to prepare content" %)}]})
(fx/defn handle-membership-update (fx/defn handle-membership-update
"Upsert chat and receive message if valid" "Upsert chat and receive message if valid"
;; Care needs to be taken here as chat-id is not coming from a whisper filter ;; Care needs to be taken here as chat-id is not coming from a whisper filter
@ -532,18 +496,7 @@
:contacts (:contacts new-group)}) :contacts (:contacts new-group)})
(add-system-messages chat-id previous-chat new-group) (add-system-messages chat-id previous-chat new-group)
(set-up-filter chat-id previous-chat) (set-up-filter chat-id previous-chat))))))
#(when (and message
;; don't allow anything but group messages
(instance? protocol/Message message)
(= :group-user-message (:message-type message)))
(prepare-message-content
%
chat-id
message
sender-signature
whisper-timestamp
metadata)))))))
(defn handle-sign-success (defn handle-sign-success
"Upsert chat and send signed payload to group members" "Upsert chat and send signed payload to group members"

View File

@ -11,12 +11,12 @@
[status-im.multiaccounts.model :as multiaccounts.model] [status-im.multiaccounts.model :as multiaccounts.model]
[status-im.multiaccounts.update.core :as multiaccounts.update] [status-im.multiaccounts.update.core :as multiaccounts.update]
[status-im.native-module.core :as status] [status-im.native-module.core :as status]
[status-im.transport.message.protocol :as protocol]
[status-im.transport.utils :as transport.utils] [status-im.transport.utils :as transport.utils]
[status-im.ui.screens.mobile-network-settings.utils [status-im.ui.screens.mobile-network-settings.utils
:as :as
mobile-network-utils] mobile-network-utils]
[status-im.ui.screens.navigation :as navigation] [status-im.ui.screens.navigation :as navigation]
[status-im.utils.config :as config]
[status-im.utils.fx :as fx] [status-im.utils.fx :as fx]
[status-im.utils.handlers :as handlers] [status-im.utils.handlers :as handlers]
[status-im.utils.platform :as platform] [status-im.utils.platform :as platform]
@ -43,6 +43,14 @@
(defn connected? [db id] (defn connected? [db id]
(= (:mailserver/current-id db) id)) (= (:mailserver/current-id db) id))
(def whisper-opts
{;; time drift that is tolerated by whisper, in seconds
:whisper-drift-tolerance 10
;; ttl of 10 sec
:ttl 10
:powTarget config/pow-target
:powTime config/pow-time})
(defn fetch [db id] (defn fetch [db id]
(get-in db [:mailserver/mailservers (node/current-fleet-key db) id])) (get-in db [:mailserver/mailservers (node/current-fleet-key db) id]))
@ -297,8 +305,8 @@
(defn adjust-request-for-transit-time (defn adjust-request-for-transit-time
[from] [from]
(let [ttl (:ttl protocol/whisper-opts) (let [ttl (:ttl whisper-opts)
whisper-tolerance (:whisper-drift-tolerance protocol/whisper-opts) whisper-tolerance (:whisper-drift-tolerance whisper-opts)
adjustment (+ whisper-tolerance ttl) adjustment (+ whisper-tolerance ttl)
adjusted-from (- (max from adjustment) adjustment)] adjusted-from (- (max from adjustment) adjustment)]
(log/debug "Adjusting mailserver request" "from:" from (log/debug "Adjusting mailserver request" "from:" from

View File

@ -115,8 +115,11 @@
"return all the topics for this chat, including discovery topics if specified" "return all the topics for this chat, including discovery topics if specified"
[topics chat-id include-discovery?] [topics chat-id include-discovery?]
(reduce-kv (reduce-kv
(fn [acc topic {:keys [discovery? chat-ids]}] (fn [acc topic {:keys [negotiated?
(if (or (and discovery? discovery?
chat-ids]}]
(if (or (and (or discovery?
negotiated?)
include-discovery?) include-discovery?)
(chat-ids chat-id)) (chat-ids chat-id))
(conj acc topic) (conj acc topic)

View File

@ -4,6 +4,7 @@
[status-im.chat.models :as models.chat] [status-im.chat.models :as models.chat]
[status-im.contact.core :as contact] [status-im.contact.core :as contact]
[status-im.contact.db :as contact.db] [status-im.contact.db :as contact.db]
[taoensso.timbre :as log]
[status-im.ethereum.json-rpc :as json-rpc] [status-im.ethereum.json-rpc :as json-rpc]
[status-im.i18n :as i18n] [status-im.i18n :as i18n]
[status-im.multiaccounts.model :as multiaccounts.model] [status-im.multiaccounts.model :as multiaccounts.model]
@ -70,10 +71,6 @@
device-type utils.platform/os] device-type utils.platform/os]
(protocol/send (transport.pairing/PairInstallation. installation-id device-type installation-name nil) nil cofx))) (protocol/send (transport.pairing/PairInstallation. installation-id device-type installation-name nil) nil cofx)))
(fx/defn confirm-message-processed
[cofx confirmation]
{:transport/confirm-messages-processed [confirmation]})
(defn send-pair-installation (defn send-pair-installation
[cofx payload] [cofx payload]
(let [current-public-key (multiaccounts.model/current-public-key cofx)] (let [current-public-key (multiaccounts.model/current-public-key cofx)]
@ -321,8 +318,8 @@
(defn handle-sync-installation (defn handle-sync-installation
[{:keys [db] :as cofx} {:keys [contacts account chat]} sender] [{:keys [db] :as cofx} {:keys [contacts account chat]} sender]
(let [confirmation (:metadata cofx)] (let [confirmation (:metadata cofx)]
(if (= sender (multiaccounts.model/current-public-key cofx)) (when (= sender (multiaccounts.model/current-public-key cofx))
(let [on-success #(re-frame/dispatch [:message/messages-persisted [confirmation]]) (let [on-success #(log/debug "handled sync installation successfully")
new-contacts (when (seq contacts) new-contacts (when (seq contacts)
(vals (merge-contacts (:contacts/contacts db) (vals (merge-contacts (:contacts/contacts db)
((comp ensure-photo-path ((comp ensure-photo-path
@ -339,17 +336,15 @@
:on-success on-success}]} :on-success on-success}]}
#(when (:public? chat) #(when (:public? chat)
(models.chat/start-public-chat % (:chat-id chat) {:dont-navigate? true}))] (models.chat/start-public-chat % (:chat-id chat) {:dont-navigate? true}))]
contacts-fx))) contacts-fx))))))
(confirm-message-processed cofx confirmation))))
(defn handle-pair-installation (defn handle-pair-installation
[{:keys [db] :as cofx} {:keys [name installation-id [{:keys [db] :as cofx} {:keys [name installation-id
device-type]} timestamp sender] device-type]} timestamp sender]
(if (and (= sender (multiaccounts.model/current-public-key cofx)) (when (and (= sender (multiaccounts.model/current-public-key cofx))
(not= (get-in db [:multiaccount :installation-id]) installation-id)) (not= (get-in db [:multiaccount :installation-id]) installation-id))
{:pairing/set-installation-metadata [[installation-id {:name name {:pairing/set-installation-metadata [[installation-id {:name name
:deviceType device-type}]]} :deviceType device-type}]]}))
(confirm-message-processed cofx (:metadata cofx))))
(fx/defn update-installation [{:keys [db]} installation-id metadata] (fx/defn update-installation [{:keys [db]} installation-id metadata]
{:db (update-in db [:pairing/installations installation-id] {:db (update-in db [:pairing/installations installation-id]

View File

@ -62,6 +62,6 @@
"subscriptions.data" (ethereum.subscriptions/handle-signal cofx (js->clj event-js :keywordize-keys true)) "subscriptions.data" (ethereum.subscriptions/handle-signal cofx (js->clj event-js :keywordize-keys true))
"subscriptions.error" (ethereum.subscriptions/handle-error cofx (js->clj event-js :keywordize-keys true)) "subscriptions.error" (ethereum.subscriptions/handle-error cofx (js->clj event-js :keywordize-keys true))
"whisper.filter.added" (transport.filters/handle-negotiated-filter cofx (js->clj event-js :keywordize-keys true)) "whisper.filter.added" (transport.filters/handle-negotiated-filter cofx (js->clj event-js :keywordize-keys true))
"messages.new" (transport.message/receive-messages cofx event-js) "messages.new" (transport.message/process-response cofx event-js)
"wallet" (ethereum.subscriptions/new-wallet-event cofx (js->clj event-js :keywordize-keys true)) "wallet" (ethereum.subscriptions/new-wallet-event cofx (js->clj event-js :keywordize-keys true))
(log/debug "Event " type " not handled")))) (log/debug "Event " type " not handled"))))

View File

@ -60,7 +60,7 @@
(spec/def ::content-type #{constants/content-type-text (spec/def ::content-type #{constants/content-type-text
constants/content-type-emoji constants/content-type-emoji
constants/content-type-sticker}) constants/content-type-sticker})
(spec/def ::message-type #{:group-user-message :public-group-user-message :user-message}) (spec/def ::message-type #{constants/message-type-private-group constants/message-type-public-group constants/message-type-one-to-one})
(spec/def ::clock-value (spec/and pos-int? (spec/def ::clock-value (spec/and pos-int?
utils.clocks/safe-timestamp?)) utils.clocks/safe-timestamp?))
(spec/def ::timestamp (spec/nilable pos-int?)) (spec/def ::timestamp (spec/nilable pos-int?))

View File

@ -238,7 +238,7 @@
(fx/defn handle-negotiated-filter (fx/defn handle-negotiated-filter
"Check if it's a new filter, if so create an shh filter and process it" "Check if it's a new filter, if so create an shh filter and process it"
[{:keys [db] :as cofx} {:keys [filters]}] [{:keys [db] :as cofx} {:keys [filters]}]
(let [processed-filters (map responses->filters filters) (let [processed-filters (map #(responses->filters (assoc % :negotiated true)) filters)
new-filters (filter new-filters (filter
(partial not-loaded? db) (partial not-loaded? db)
processed-filters)] processed-filters)]

View File

@ -3,6 +3,10 @@
(:require [goog.object :as o] (:require [goog.object :as o]
[re-frame.core :as re-frame] [re-frame.core :as re-frame]
[status-im.chat.models.message :as models.message] [status-im.chat.models.message :as models.message]
[status-im.chat.models :as models.chat]
[status-im.data-store.messages :as data-store.messages]
[status-im.data-store.chats :as data-store.chats]
[status-im.constants :as constants]
[status-im.utils.handlers :as handlers] [status-im.utils.handlers :as handlers]
[status-im.ethereum.json-rpc :as json-rpc] [status-im.ethereum.json-rpc :as json-rpc]
[status-im.ethereum.core :as ethereum] [status-im.ethereum.core :as ethereum]
@ -12,77 +16,34 @@
[status-im.transport.message.transit :as transit] [status-im.transport.message.transit :as transit]
[status-im.transport.utils :as transport.utils] [status-im.transport.utils :as transport.utils]
[status-im.tribute-to-talk.whitelist :as whitelist] [status-im.tribute-to-talk.whitelist :as whitelist]
[status-im.ens.core :as ens]
[cljs-bean.core :as clj-bean] [cljs-bean.core :as clj-bean]
[status-im.utils.config :as config] [status-im.utils.config :as config]
[status-im.utils.fx :as fx] [status-im.utils.fx :as fx]
[taoensso.timbre :as log] [taoensso.timbre :as log]
[status-im.ethereum.json-rpc :as json-rpc])) [status-im.ethereum.json-rpc :as json-rpc]))
(def message-type-message 1) (fx/defn handle-raw-message
(defn build-content [content-js]
{:text (.-text content-js)
:line-count (.-lineCount content-js)
:parsed-text (clj-bean/->clj (.-parsedText content-js))
:name (.-name content-js)
:rtl? (.-rtl content-js)
:response-to (aget content-js "response-to")
:chat-id (.-chat_id content-js)})
(defn build-message [parsed-message-js]
(let [content (.-content parsed-message-js)
built-message
(protocol/Message.
(build-content content)
(.-content_type parsed-message-js)
(keyword (.-message_type parsed-message-js))
(.-clock parsed-message-js)
(.-timestamp parsed-message-js))]
built-message))
(defn handle-message
"Check if parsedMessage is present and of a supported type, if so
build a record using the right type. Otherwise defaults to transit
deserializing"
[message-js]
(if (and (.-parsedMessage message-js)
(= message-type-message (.-messageType message-js)))
(build-message (.-parsedMessage message-js))
(transit/deserialize (.-payload message-js))))
(fx/defn receive-message
"Receive message handles a new status-message. "Receive message handles a new status-message.
dedup-id is passed by status-go and is used to deduplicate messages at that layer. dedup-id is passed by status-go and is used to deduplicate messages at that layer.
Once a message has been successfuly processed, that id needs to be sent back Once a message has been successfuly processed, that id needs to be sent back
in order to stop receiving that message" in order to stop receiving that message"
[{:keys [db] :as cofx} now-in-s filter-chat-id message-js] [{:keys [db] :as cofx} raw-message-js]
(let [blocked-contacts (get db :contacts/blocked #{}) (let [timestamp (.-timestamp raw-message-js)
timestamp (.-timestamp (.-message message-js)) sig (.-from raw-message-js)
metadata-js (.-metadata message-js) payload (.-payload raw-message-js)]
metadata {:author {:publicKey (.-publicKey (.-author metadata-js)) (let [status-message (transit/deserialize payload)]
:alias (.-alias (.-author metadata-js)) (when (and sig
:identicon (.-identicon (.-author metadata-js))} status-message)
:dedupId (.-dedupId metadata-js) (try
:encryptionId (.-encryptionId metadata-js) (when-let [valid-message (protocol/validate status-message)]
:messageId (.-messageId metadata-js)} (protocol/receive
status-message (handle-message message-js) valid-message
sig (-> metadata :author :publicKey)] sig
(when (and sig sig
status-message timestamp
(not (blocked-contacts sig))) cofx))
(try (catch :default e nil)))))) ; ignore unknown message types
(when-let [valid-message (protocol/validate status-message)]
(protocol/receive
(assoc valid-message
:metadata metadata)
(or
filter-chat-id
(get-in valid-message [:content :chat-id])
sig)
sig
timestamp
(assoc cofx :metadata metadata)))
(catch :default e nil))))) ; ignore unknown message types
(defn- js-obj->seq [obj] (defn- js-obj->seq [obj]
;; Sometimes the filter will return a single object instead of a collection ;; Sometimes the filter will return a single object instead of a collection
@ -91,42 +52,48 @@
(aget obj i)) (aget obj i))
[obj])) [obj]))
(fx/defn handle-chat [cofx chat]
;; :unviewed-messages-count is managed by status-react, so we don't copy
;; over it
(models.chat/ensure-chat cofx (dissoc chat :unviewed-messages-count)))
(fx/defn handle-message-2 [cofx message]
(fx/merge cofx
(models.message/receive-one message)
(ens/verify-names-from-message message (:from message))))
(fx/defn process-response [cofx response-js]
(let [chats (.-chats response-js)
raw-messages (.-rawMessages response-js)
messages (.-messages response-js)]
(cond
(seq chats)
(let [chat (.pop chats)]
(fx/merge cofx
{:dispatch-later [{:ms 20 :dispatch [::process response-js]}]}
(handle-chat (-> chat (clj-bean/->clj) (data-store.chats/<-rpc)))))
(seq raw-messages)
(let [first-filter (aget raw-messages 0)
messages (.-messages first-filter)
first-message (.pop messages)]
;; Pop the empty array
(when (= (.-length messages) 0)
(.pop raw-messages))
(when first-message
(fx/merge cofx
{:dispatch-later [{:ms 20 :dispatch [::process response-js]}]}
(handle-raw-message first-message))))
(seq messages)
(let [message (.pop messages)]
(fx/merge cofx
{:dispatch-later [{:ms 20 :dispatch [::process response-js]}]}
(handle-message-2 (-> message (clj-bean/->clj) (data-store.messages/<-rpc))))))))
(handlers/register-handler-fx (handlers/register-handler-fx
::process ::process
(fn [cofx [_ messages now-in-s]] (fn [cofx [_ response-js]]
(let [[chat-id message] (first messages) (process-response cofx response-js)))
remaining-messages (rest messages)]
(if (seq remaining-messages)
(assoc
(receive-message cofx now-in-s chat-id message)
;; We dispatch later to let the UI thread handle events, without this
;; it will keep processing events ignoring user input.
:dispatch-later [{:ms 20 :dispatch [::process remaining-messages now-in-s]}])
(receive-message cofx now-in-s chat-id message)))))
(fx/defn receive-messages
"Initialize the ::process event, which will process messages one by one
dispatching later to itself"
[{:keys [now] :as cofx} event-js]
(let [now-in-s (quot now 1000)
events (reduce
(fn [acc message-specs]
(let [chat (.-chat message-specs)
messages (.-messages message-specs)
error (.-error message-specs)
chat-id (if (or (.-discovery chat)
(.-negotiated chat))
nil
(.-chatId chat))]
(if (seq messages)
(reduce (fn [acc m]
(conj acc [chat-id m]))
acc
messages)
acc)))
[]
(.-messages event-js))]
{:dispatch [::process events now-in-s]}))
(fx/defn remove-hash (fx/defn remove-hash
[{:keys [db] :as cofx} envelope-hash] [{:keys [db] :as cofx} envelope-hash]
@ -192,16 +159,3 @@
{:name name {:name name
:profile-image photo-path :profile-image photo-path
:address address})) :address address}))
(fx/defn resend-contact-request [cofx own-info chat-id {:keys [sym-key topic]}]
(protocol/send (contact/map->ContactRequest own-info)
chat-id cofx))
(re-frame/reg-fx
:transport/confirm-messages-processed
(fn [confirmations]
(when (seq confirmations)
(json-rpc/call {:method "shhext_confirmMessagesProcessedByID"
:params [confirmations]
:on-success #(log/debug "successfully confirmed messages")
:on-failure #(log/error "failed to confirm messages" %)}))))

View File

@ -2,32 +2,24 @@
status-im.transport.message.protocol status-im.transport.message.protocol
(:require [cljs.spec.alpha :as spec] (:require [cljs.spec.alpha :as spec]
[status-im.multiaccounts.model :as multiaccounts.model] [status-im.multiaccounts.model :as multiaccounts.model]
[re-frame.core :as re-frame]
[status-im.data-store.messages :as data-store.messages]
[status-im.ethereum.json-rpc :as json-rpc]
[status-im.constants :as constants] [status-im.constants :as constants]
[status-im.ethereum.core :as ethereum] [status-im.ethereum.core :as ethereum]
[status-im.transport.db :as transport.db] [status-im.transport.db :as transport.db]
[status-im.utils.pairing :as pairing.utils] [status-im.utils.pairing :as pairing.utils]
[status-im.transport.utils :as transport.utils] [status-im.transport.utils :as transport.utils]
[status-im.tribute-to-talk.whitelist :as whitelist] [status-im.tribute-to-talk.whitelist :as whitelist]
[status-im.utils.config :as config]
[status-im.utils.fx :as fx] [status-im.utils.fx :as fx]
[taoensso.timbre :as log])) [taoensso.timbre :as log]))
(defn discovery-topic-hash [] (transport.utils/get-topic constants/contact-discovery))
(defprotocol StatusMessage (defprotocol StatusMessage
"Protocol for the messages that are sent through the transport layer" "Protocol for the messages that are sent through the transport layer"
(send [this chat-id cofx] "Method producing all effects necessary for sending the message record") (send [this chat-id cofx] "Method producing all effects necessary for sending the message record")
(receive [this chat-id signature timestamp cofx] "Method producing all effects necessary for receiving the message record") (receive [this chat-id signature timestamp cofx] "Method producing all effects necessary for receiving the message record")
(validate [this] "Method returning the message if it is valid or nil if it is not")) (validate [this] "Method returning the message if it is valid or nil if it is not"))
(def whisper-opts
{;; time drift that is tolerated by whisper, in seconds
:whisper-drift-tolerance 10
;; ttl of 10 sec
:ttl 10
:powTarget config/pow-target
:powTime config/pow-time})
(defn send-public-message (defn send-public-message
"Sends the payload to topic" "Sends the payload to topic"
[cofx chat-id success-event payload] [cofx chat-id success-event payload]
@ -52,24 +44,28 @@
success-event success-event
payload)) payload))
(fx/defn send-chat-message [_ {:keys [chat-id
text
response-to
ens-name
message-type
sticker
content-type]
:as message}]
{::json-rpc/call [{:method "shhext_sendChatMessage"
:params [{:chatId chat-id
:text text
:responseTo response-to
:ensName ens-name
:sticker sticker
:contentType content-type}]
:on-success
#(re-frame/dispatch [:transport/message-sent % 1])
:on-failure #(log/error "failed to send a message" %)}]})
(defrecord Message [content content-type message-type clock-value timestamp] (defrecord Message [content content-type message-type clock-value timestamp]
StatusMessage StatusMessage
(send [this chat-id {:keys [message] :as cofx}] (send [this chat-id {:keys [message] :as cofx}])
(let [current-public-key (multiaccounts.model/current-public-key cofx)
params {:chat-id chat-id
:payload this
:success-event [:transport/message-sent
chat-id
message
message-type]}]
(case message-type
:public-group-user-message
(send-public-message cofx chat-id (:success-event params) this)
:user-message
(fx/merge cofx
(when (pairing.utils/has-paired-installations? cofx)
(send-direct-message current-public-key nil this))
(send-with-pubkey params)))))
(validate [this] (validate [this]
(if (spec/valid? :message/message this) (if (spec/valid? :message/message this)
this this

View File

@ -4,17 +4,6 @@
[status-im.ethereum.core :as ethereum] [status-im.ethereum.core :as ethereum]
[status-im.js-dependencies :as dependencies])) [status-im.js-dependencies :as dependencies]))
(defn system-message-id
[{:keys [from chat-id clock-value]}]
(ethereum/sha3 (str from chat-id clock-value)))
(defn message-id
"Get a message-id by appending the hex-encoded pk of the sender to the raw-payload.
We strip 0x from the payload so web3 understand that the whole thing is to be
decoded as raw bytes"
[from raw-payload]
(ethereum/sha3 (str from (subs raw-payload 2))))
(defn get-topic (defn get-topic
"Get the topic of a group chat or public chat from the chat-id" "Get the topic of a group chat or public chat from the chat-id"
[chat-id] [chat-id]

View File

@ -1,6 +1,7 @@
(ns status-im.tribute-to-talk.whitelist (ns status-im.tribute-to-talk.whitelist
(:require [status-im.contact.db :as contact.db] (:require [status-im.contact.db :as contact.db]
[status-im.data-store.contacts :as contacts-store] [status-im.data-store.contacts :as contacts-store]
[status-im.constants :as constants]
[status-im.tribute-to-talk.db :as tribute-to-talk.db] [status-im.tribute-to-talk.db :as tribute-to-talk.db]
[status-im.utils.fx :as fx])) [status-im.utils.fx :as fx]))
@ -61,7 +62,7 @@
along the message" along the message"
[{:keys [db] :as cofx} received-message-fx message-type tribute-transaction from] [{:keys [db] :as cofx} received-message-fx message-type tribute-transaction from]
;; if it is not a user-message or the user is whitelisted it passes ;; if it is not a user-message or the user is whitelisted it passes
(if (or (not= :user-message message-type) (if (or (not= constants/message-type-one-to-one message-type)
(contains? (:contacts/whitelist db) from)) (contains? (:contacts/whitelist db) from))
received-message-fx received-message-fx
;; if ttt is disabled it passes ;; if ttt is disabled it passes

View File

@ -52,11 +52,6 @@
:number-of-lines 5} :number-of-lines 5}
(or text (:text quote))]]))) (or text (:text quote))]])))
(defview message-content-status [{:keys [content]}]
[react/view style/status-container
[react/text {:style style/status-text}
(:text content)]])
(defn expand-button [expanded? chat-id message-id] (defn expand-button [expanded? chat-id message-id]
[react/text {:style style/message-expand-button [react/text {:style style/message-expand-button
:on-press #(re-frame/dispatch [:chat.ui/message-expand-toggled chat-id message-id])} :on-press #(re-frame/dispatch [:chat.ui/message-expand-toggled chat-id message-id])}
@ -103,6 +98,14 @@
(conj acc literal))) (conj acc literal)))
(defview message-content-status [{:keys [content]}]
[react/view style/status-container
[react/text {:style style/status-text}
(reduce
(fn [acc e] (render-inline (:text content) false acc e))
[react/text-class {:style style/status-text}]
(-> content :parsed-text peek :children))]])
(defn render-block [{:keys [chat-id message-id content (defn render-block [{:keys [chat-id message-id content
timestamp-str group-chat outgoing timestamp-str group-chat outgoing
current-public-key expanded?] :as message} current-public-key expanded?] :as message}
@ -184,7 +187,7 @@
[wrapper {:keys [content] :as message}] [wrapper {:keys [content] :as message}]
[wrapper message [wrapper message
[react/image {:style {:margin 10 :width 140 :height 140} [react/image {:style {:margin 10 :width 140 :height 140}
:source {:uri (contenthash/url (:hash content))}}]]) :source {:uri (contenthash/url (-> content :sticker :hash))}}]])
(defmethod message-content :default (defmethod message-content :default
[wrapper {:keys [content-type] :as message}] [wrapper {:keys [content-type] :as message}]
@ -220,7 +223,7 @@
[{:keys [chat-id message-id outgoing-status [{:keys [chat-id message-id outgoing-status
first-outgoing? first-outgoing?
content message-type] :as message}] content message-type] :as message}]
(when (not= :system-message message-type) (when (not= constants/message-type-private-group-system-message message-type)
(case outgoing-status (case outgoing-status
:sending [message-activity-indicator] :sending [message-activity-indicator]
:not-sent [message-not-sent-text chat-id message-id] :not-sent [message-not-sent-text chat-id message-id]
@ -267,25 +270,26 @@
(defn chat-message (defn chat-message
[{:keys [outgoing group-chat modal? current-public-key content-type content] :as message}] [{:keys [outgoing group-chat modal? current-public-key content-type content] :as message}]
[react/view (let [sticker (:sticker content)]
[react/touchable-highlight [react/view
{:on-press (fn [arg] [react/touchable-highlight
(if (and platform/desktop? (= "right" (.-button (.-nativeEvent arg)))) {:on-press (fn [arg]
(open-chat-context-menu message) (if (and platform/desktop? (= "right" (.-button (.-nativeEvent arg))))
(do (open-chat-context-menu message)
(when (and (= content-type constants/content-type-sticker) (:pack content)) (do
(re-frame/dispatch [:stickers/open-sticker-pack (:pack content)])) (when (and (= content-type constants/content-type-sticker) (:pack sticker))
(re-frame/dispatch [:chat.ui/set-chat-ui-props {:messages-focused? true (re-frame/dispatch [:stickers/open-sticker-pack (:pack sticker)]))
:input-bottom-sheet nil}]) (re-frame/dispatch [:chat.ui/set-chat-ui-props {:messages-focused? true
(when-not platform/desktop? :input-bottom-sheet nil}])
(react/dismiss-keyboard!))))) (when-not platform/desktop?
:on-long-press #(when (or (= content-type constants/content-type-text) (react/dismiss-keyboard!)))))
(= content-type constants/content-type-emoji)) :on-long-press #(when (or (= content-type constants/content-type-text)
(open-chat-context-menu message))} (= content-type constants/content-type-emoji))
[react/view {:accessibility-label :chat-item} (open-chat-context-menu message))}
(let [incoming-group (and group-chat (not outgoing))] [react/view {:accessibility-label :chat-item}
[message-content message-body (merge message (let [incoming-group (and group-chat (not outgoing))]
{:current-public-key current-public-key [message-content message-body (merge message
:group-chat group-chat {:current-public-key current-public-key
:modal? modal? :group-chat group-chat
:incoming-group incoming-group})])]]]) :modal? modal?
:incoming-group incoming-group})])]]]))

View File

@ -20,16 +20,13 @@
[{:keys [chat-id name group-chat [{:keys [chat-id name group-chat
color public? public-key color public? public-key
timestamp chat-name timestamp chat-name
last-message-content last-message]
last-message-timestamp
last-message-content-type]
:as chat-item}] :as chat-item}]
(views/letsubs [photo-path [:contacts/chat-photo chat-id] (views/letsubs [photo-path [:contacts/chat-photo chat-id]
unviewed-messages-count [:chats/unviewed-messages-count chat-id] unviewed-messages-count [:chats/unviewed-messages-count chat-id]
current-chat-id [:chats/current-chat-id]] current-chat-id [:chats/current-chat-id]]
(let [last-message {:content last-message-content (let [last-message (or last-message
:timestamp (if (pos? last-message-timestamp) last-message-timestamp timestamp) {:timestamp timestamp})
:content-type last-message-content-type}
name (or chat-name name (or chat-name
(gfycat/generate-gfy public-key)) (gfycat/generate-gfy public-key))
[unviewed-messages-label large?] [(utils/unread-messages-count unviewed-messages-count) true] [unviewed-messages-label large?] [(utils/unread-messages-count unviewed-messages-count) true]
@ -60,7 +57,7 @@
[react/text {:ellipsize-mode :tail [react/text {:ellipsize-mode :tail
:number-of-lines 1 :number-of-lines 1
:style styles/chat-last-message} :style styles/chat-last-message}
(or (:text last-message-content) (or (:text last-message)
(i18n/label :no-messages-yet))]))] (i18n/label :no-messages-yet))]))]
[react/view {:style styles/timestamp} [react/view {:style styles/timestamp}
[chat-item/message-timestamp (:timestamp last-message)] [chat-item/message-timestamp (:timestamp last-message)]

View File

@ -3,6 +3,7 @@
[reagent.core :as reagent] [reagent.core :as reagent]
[status-im.ens.core :as ens] [status-im.ens.core :as ens]
[status-im.ethereum.core :as ethereum] [status-im.ethereum.core :as ethereum]
[status-im.constants :as constants]
[status-im.ethereum.ens :as ethereum.ens] [status-im.ethereum.ens :as ethereum.ens]
[status-im.ethereum.stateofus :as stateofus] [status-im.ethereum.stateofus :as stateofus]
[status-im.i18n :as i18n] [status-im.i18n :as i18n]
@ -649,7 +650,7 @@
:action-fn #(re-frame/dispatch [::ens/switch-show-username]) :action-fn #(re-frame/dispatch [::ens/switch-show-username])
:value show?}]] :value show?}]]
(let [message {:content {:parsed-text [{:type "paragraph", :children [{:literal (i18n/label :t/ens-test-message)}]}]} (let [message {:content {:parsed-text [{:type "paragraph", :children [{:literal (i18n/label :t/ens-test-message)}]}]}
:content-type "text/plain" :content-type constants/content-type-text
:timestamp-str "9:41 AM"}] :timestamp-str "9:41 AM"}]
[react/view [react/view
[react/view {:padding-left 60} [react/view {:padding-left 60}

View File

@ -25,7 +25,7 @@
(= constants/content-type-sticker content-type) (= constants/content-type-sticker content-type)
[react/image {:style {:margin 1 :width 20 :height 20} [react/image {:style {:margin 1 :width 20 :height 20}
:source {:uri (contenthash/url (:hash content))}}] :source {:uri (contenthash/url (-> content :sticker :hash))}}]
(string/blank? (:text content)) (string/blank? (:text content))
[react/text {:style styles/last-message-text} [react/text {:style styles/last-message-text}
@ -36,14 +36,7 @@
:number-of-lines 1 :number-of-lines 1
:ellipsize-mode :tail :ellipsize-mode :tail
:accessibility-label :chat-message-text} :accessibility-label :chat-message-text}
(string/trim-newline (:text content))] (string/trim-newline (:text content))])])
:else
[react/text {:style styles/last-message-text
:number-of-lines 1
:ellipsize-mode :tail
:accessibility-label :chat-message-text}
content])])
(defn message-timestamp [timestamp] (defn message-timestamp [timestamp]
(when timestamp (when timestamp
@ -61,10 +54,8 @@
[chat-id chat-name [chat-id chat-name
color online group-chat color online group-chat
public? contact public? contact
last-message-timestamp
timestamp timestamp
last-message-content last-message]} home-item
last-message-content-type]} home-item
private-group? (and group-chat (not public?)) private-group? (and group-chat (not public?))
public-group? (and group-chat public?) public-group? (and group-chat public?)
truncated-chat-name (utils/truncate-str chat-name 30) truncated-chat-name (utils/truncate-str chat-name 30)
@ -81,14 +72,14 @@
:else nil) :else nil)
:title truncated-chat-name :title truncated-chat-name
:title-accessibility-label :chat-name-text :title-accessibility-label :chat-name-text
:title-row-accessory [message-timestamp (if (pos? last-message-timestamp) :title-row-accessory [message-timestamp (if (pos? (:whisper-timestamp last-message))
last-message-timestamp (:whisper-timestamp last-message)
timestamp)] timestamp)]
:subtitle :subtitle
(let [{:keys [tribute-status tribute-label]} (:tribute-to-talk contact)] (let [{:keys [tribute-status tribute-label]} (:tribute-to-talk contact)]
(if (not (#{:require :pending} tribute-status)) (if (not (#{:require :pending} tribute-status))
[message-content-text {:content last-message-content [message-content-text {:content (:content last-message)
:content-type last-message-content-type}] :content-type (:content-type last-message)}]
tribute-label)) tribute-label))
:subtitle-row-accessory [unviewed-indicator chat-id] :subtitle-row-accessory [unviewed-indicator chat-id]
:on-press #(do :on-press #(do

View File

@ -41,7 +41,7 @@
;; ;;
;; But what we can do, is to use our time to make a "bid", hoping that it will ;; But what we can do, is to use our time to make a "bid", hoping that it will
;; beat the current chat-timestamp. So our Lamport timestamp format is: ;; beat the current chat-timestamp. So our Lamport timestamp format is:
;; {unix-timestamp-ms}{2-digits-post-id} ;; {unix-timestamp-ms}
;; ;;
;; We always need to make sure we take the max value between the last-clock-value ;; We always need to make sure we take the max value between the last-clock-value
;; for the chat and the bid-timestamp. ;; for the chat and the bid-timestamp.
@ -70,13 +70,12 @@
;; http://amturing.acm.org/p558-lamport.pdf ;; http://amturing.acm.org/p558-lamport.pdf
(def one-month-in-ms (* 60 60 24 31 1000)) (def one-month-in-ms (* 60 60 24 31 1000))
(def post-id-digits 100)
(defn- ->timestamp-bid [] (defn- ->timestamp-bid []
(* (utils.datetime/timestamp) post-id-digits)) (utils.datetime/timestamp))
(defn max-timestamp [] (defn max-timestamp []
(* (+ one-month-in-ms (utils.datetime/timestamp)) post-id-digits)) (+ one-month-in-ms (utils.datetime/timestamp)))
; The timestamp has an upper limit of Number.MAX_SAFE_INTEGER ; The timestamp has an upper limit of Number.MAX_SAFE_INTEGER
; A malicious client could send a crafted message with timestamp = Number.MAX_SAFE_INTEGER ; A malicious client could send a crafted message with timestamp = Number.MAX_SAFE_INTEGER
; which effectively would DoS the chat, as any new message would get ; which effectively would DoS the chat, as any new message would get

View File

@ -12,10 +12,8 @@
cofx)) cofx))
(def ^:private mergeable-keys (def ^:private mergeable-keys
#{:dispatch-debounce #{:filters/load-filters
:filters/load-filters
:pairing/set-installation-metadata :pairing/set-installation-metadata
:status-im.data-store.messages/save-message
:status-im.ens.core/verify-names :status-im.ens.core/verify-names
:shh/send-direct-message :shh/send-direct-message
:shh/remove-filter :shh/remove-filter

View File

@ -88,31 +88,6 @@
(when address (when address
(get-shortened-address (eip55/address->checksum (ethereum/normalized-hex address))))) (get-shortened-address (eip55/address->checksum (ethereum/normalized-hex address)))))
;; debounce, taken from https://github.com/johnswanson/re-frame-debounce-fx
; {:dispatch-debounce {:key :search
; :event [:search value]
; :delay 250}}))
(def registered-keys (atom nil))
(defn dispatch-if-not-superceded [{:keys [key delay event time-received]}]
(when (= time-received (get @registered-keys key))
;; no new events on this key!
(re-frame/dispatch event)))
(defn dispatch-debounced [{:keys [delay] :as debounce}]
(js/setTimeout
(fn [] (dispatch-if-not-superceded debounce))
delay))
(re-frame/reg-fx
:dispatch-debounce
(fn dispatch-debounce [debounces]
(doseq [debounce debounces]
(let [ts (.getTime (js/Date.))]
(swap! registered-keys assoc (:key debounce) ts)
(dispatch-debounced (assoc debounce :time-received ts))))))
;; background-timer ;; background-timer
(defn set-timeout [cb ms] (defn set-timeout [cb ms]
@ -159,4 +134,4 @@
(let [decimal-part (get (string/split (str amount) ".") 1)] (let [decimal-part (get (string/split (str amount) ".") 1)]
(if (> (count decimal-part) places) (if (> (count decimal-part) places)
(gstring/format (str "%." places "f") amount) (gstring/format (str "%." places "f") amount)
(or (str amount) 0)))) (or (str amount) 0))))

View File

@ -2,7 +2,7 @@
"_comment": "DO NOT EDIT THIS FILE BY HAND. USE 'scripts/update-status-go.sh <tag>' instead", "_comment": "DO NOT EDIT THIS FILE BY HAND. USE 'scripts/update-status-go.sh <tag>' instead",
"owner": "status-im", "owner": "status-im",
"repo": "status-go", "repo": "status-go",
"version": "v0.35.1", "version": "v0.36.1",
"commit-sha1": "4769fb91c3247c72c21013bf36194843d8d72d18", "commit-sha1": "fd49b0140ebafdcec35b4da84685bcd8559a7dd9",
"src-sha256": "1b6050kvnvf69gqw5llb7gb5v25pq6vbhkf9nznk9ra2spasqqzy" "src-sha256": "1pqnvmldg93vbmmsvpr24pj87d2vx3cfm7rr9rgwdk469pd1hhhy"
} }

View File

@ -861,13 +861,14 @@ class TestProfileMultipleDevice(MultipleDeviceTestCase):
self.errors.append('Public chat "%s" doesn\'t appear on other device when devices are paired' self.errors.append('Public chat "%s" doesn\'t appear on other device when devices are paired'
% public_chat_before_sync_name) % public_chat_before_sync_name)
device_2_home.element_by_text(group_chat_name).click() # Disabling for now until GroupMembershipUpdate is also moved to status-go
device_2_group_chat = device_2_home.get_chat_view() #device_2_home.element_by_text(group_chat_name).click()
#device_2_group_chat = device_2_home.get_chat_view()
if not device_2_group_chat.chat_element_by_text(message_after_sync).is_element_displayed(): #if not device_2_group_chat.chat_element_by_text(message_after_sync).is_element_displayed():
self.errors.append('"%s" message in group chat is not synced' % message_after_sync) # self.errors.append('"%s" message in group chat is not synced' % message_after_sync)
device_2_group_chat.get_back_to_home_view() #device_2_group_chat.get_back_to_home_view()
device_2_home.profile_button.click() device_2_home.profile_button.click()
if not device_2_profile.profile_picture.is_element_image_equals_template('sauce_logo_red_profile.png'): if not device_2_profile.profile_picture.is_element_image_equals_template('sauce_logo_red_profile.png'):
self.errors.append('Profile picture was not updated after changing when devices are paired') self.errors.append('Profile picture was not updated after changing when devices are paired')
@ -951,4 +952,4 @@ class TestProfileMultipleDevice(MultipleDeviceTestCase):
if not profile_2.element_by_text('@' + user_1['ens']).is_element_displayed(): if not profile_2.element_by_text('@' + user_1['ens']).is_element_displayed():
self.errors.append('ENS username is not shown in contacts') self.errors.append('ENS username is not shown in contacts')
self.errors.verify_no_errors() self.errors.verify_no_errors()

View File

@ -5,19 +5,19 @@ from views.sign_in_view import SignInView
def return_left_chat_system_message(username): def return_left_chat_system_message(username):
return "*%s* left the group" % username return "%s left the group" % username
def return_created_chat_system_message(username, chat_name): def return_created_chat_system_message(username, chat_name):
return "*%s* created the group *%s*" % (username, chat_name) return "%s created the group %s" % (username, chat_name)
def return_joined_chat_system_message(username): def return_joined_chat_system_message(username):
return "*%s* has joined the group" % username return "%s has joined the group" % username
def return_made_admin_system_message(username): def return_made_admin_system_message(username):
return "*%s* has been made admin" % username return "%s has been made admin" % username
def create_users(driver_1, driver_2): def create_users(driver_1, driver_2):

View File

@ -65,9 +65,7 @@
(deftest clear-history-test (deftest clear-history-test
(let [chat-id "1" (let [chat-id "1"
cofx {:db {:chats {chat-id {:message-list [{:something "a"}] cofx {:db {:chats {chat-id {:message-list [{:something "a"}]
:messages {"1" {:clock-value 1} :last-message {:clock-value 10}
"2" {:clock-value 10}
"3" {:clock-value 2}}
:unviewed-messages-count 1}}}}] :unviewed-messages-count 1}}}}]
(testing "it deletes all the messages" (testing "it deletes all the messages"
(let [actual (chat/clear-history cofx chat-id)] (let [actual (chat/clear-history cofx chat-id)]
@ -85,7 +83,7 @@
(let [actual (chat/clear-history (update-in cofx (let [actual (chat/clear-history (update-in cofx
[:db :chats chat-id] [:db :chats chat-id]
assoc assoc
:messages {} :last-message nil
:deleted-at-clock-value 100) :deleted-at-clock-value 100)
chat-id)] chat-id)]
(is (= 100 (get-in actual [:db :chats chat-id :deleted-at-clock-value]))))) (is (= 100 (get-in actual [:db :chats chat-id :deleted-at-clock-value])))))
@ -94,7 +92,7 @@
(let [actual (chat/clear-history (update-in cofx (let [actual (chat/clear-history (update-in cofx
[:db :chats chat-id] [:db :chats chat-id]
assoc assoc
:messages {}) :last-message nil)
chat-id)] chat-id)]
(is (= 42 (get-in actual [:db :chats chat-id :deleted-at-clock-value])))))) (is (= 42 (get-in actual [:db :chats chat-id :deleted-at-clock-value]))))))
(testing "it adds the relevant rpc calls" (testing "it adds the relevant rpc calls"
@ -104,7 +102,8 @@
(deftest remove-chat-test (deftest remove-chat-test
(let [chat-id "1" (let [chat-id "1"
cofx {:db {:chats {chat-id {:messages {"1" {:clock-value 1} cofx {:db {:chats {chat-id {:last-message {:clock-value 10}
:messages {"1" {:clock-value 1}
"2" {:clock-value 10} "2" {:clock-value 10}
"3" {:clock-value 2}}}}}}] "3" {:clock-value 2}}}}}}]
(testing "it deletes all the messages" (testing "it deletes all the messages"

View File

@ -2,6 +2,7 @@
(:require [cljs.test :refer-macros [deftest is testing]] (:require [cljs.test :refer-macros [deftest is testing]]
[status-im.utils.gfycat.core :as gfycat] [status-im.utils.gfycat.core :as gfycat]
[status-im.utils.identicon :as identicon] [status-im.utils.identicon :as identicon]
[status-im.constants :as constants]
[status-im.utils.datetime :as time] [status-im.utils.datetime :as time]
[status-im.transport.message.protocol :as protocol] [status-im.transport.message.protocol :as protocol]
[status-im.chat.models.message-list :as models.message-list] [status-im.chat.models.message-list :as models.message-list]
@ -49,51 +50,17 @@
(testing "a message coming from you!" (testing "a message coming from you!"
(let [actual (message/receive-one {:db db} (let [actual (message/receive-one {:db db}
{:from "me" {:from "me"
:message-type :user-message :message-type constants/message-type-one-to-one
:timestamp 0 :timestamp 0
:whisper-timestamp 0 :whisper-timestamp 0
:message-id "id" :message-id "id"
:chat-id "chat-id" :chat-id "chat-id"
:outgoing true
:content "b" :content "b"
:clock-value 1}) :clock-value 1})
message (get-in actual [:db :chats "chat-id" :messages "id"])] message (get-in actual [:db :chats "chat-id" :messages "id"])]
(testing "it adds the message" (testing "it adds the message"
(is message)) (is message))))))
(testing "it marks the message as outgoing"
(is (= true (:outgoing message))))))))
(deftest receive-one-clock-value
(let [db {:multiaccount {:public-key "me"}
:view-id :chat
:current-chat-id "chat-id"
:chats {"chat-id" {:last-clock-value 10
:messages {}}}}]
(testing "a message with a higher clock value"
(let [actual (message/receive-one {:db db}
{:from "chat-id"
:message-type :user-message
:timestamp 0
:whisper-timestamp 0
:message-id "id"
:chat-id "chat-id"
:content "b"
:clock-value 12})
chat-clock-value (get-in actual [:db :chats "chat-id" :last-clock-value])]
(testing "it sets last-clock-value"
(is (= 12 chat-clock-value)))))
(testing "a message with a lower clock value"
(let [actual (message/receive-one {:db db}
{:from "chat-id"
:message-type :user-message
:timestamp 0
:whisper-timestamp 0
:message-id "id"
:chat-id "chat-id"
:content "b"
:clock-value 2})
chat-clock-value (get-in actual [:db :chats "chat-id" :last-clock-value])]
(testing "it sets last-clock-value"
(is (= 10 chat-clock-value)))))))
(deftest receive-group-chats (deftest receive-group-chats
(let [cofx {:db {:chats {"chat-id" {:contacts #{"present"} (let [cofx {:db {:chats {"chat-id" {:contacts #{"present"}
@ -104,21 +71,21 @@
cofx-without-member (update-in cofx [:db :chats "chat-id" :members-joined] disj "a") cofx-without-member (update-in cofx [:db :chats "chat-id" :members-joined] disj "a")
valid-message {:chat-id "chat-id" valid-message {:chat-id "chat-id"
:from "present" :from "present"
:message-type :group-user-message :message-type constants/message-type-private-group
:message-id "1" :message-id "1"
:clock-value 1 :clock-value 1
:whisper-timestamp 0 :whisper-timestamp 0
:timestamp 0} :timestamp 0}
bad-chat-id-message {:chat-id "bad-chat-id" bad-chat-id-message {:chat-id "bad-chat-id"
:from "present" :from "present"
:message-type :group-user-message :message-type constants/message-type-private-group
:message-id "1" :message-id "1"
:clock-value 1 :clock-value 1
:whisper-timestamp 0 :whisper-timestamp 0
:timestamp 0} :timestamp 0}
bad-from-message {:chat-id "chat-id" bad-from-message {:chat-id "chat-id"
:from "not-present" :from "not-present"
:message-type :group-user-message :message-type constants/message-type-private-group
:message-id "1" :message-id "1"
:clock-value 1 :clock-value 1
:whisper-timestamp 0 :whisper-timestamp 0
@ -139,14 +106,14 @@
:view-id :chat}} :view-id :chat}}
valid-message {:chat-id "chat-id" valid-message {:chat-id "chat-id"
:from "anyone" :from "anyone"
:message-type :public-group-user-message :message-type constants/message-type-public-group
:message-id "1" :message-id "1"
:clock-value 1 :clock-value 1
:whisper-timestamp 0 :whisper-timestamp 0
:timestamp 0} :timestamp 0}
bad-chat-id-message {:chat-id "bad-chat-id" bad-chat-id-message {:chat-id "bad-chat-id"
:from "present" :from "present"
:message-type :public-group-user-message :message-type constants/message-type-public-group
:message-id "1" :message-id "1"
:clock-value 1 :clock-value 1
:whisper-timestamp 0 :whisper-timestamp 0
@ -166,14 +133,14 @@
:view-id :chat}} :view-id :chat}}
valid-message {:chat-id "matching" valid-message {:chat-id "matching"
:from "matching" :from "matching"
:message-type :user-message :message-type constants/message-type-one-to-one
:message-id "1" :message-id "1"
:clock-value 1 :clock-value 1
:whisper-timestamp 0 :whisper-timestamp 0
:timestamp 0} :timestamp 0}
own-message {:chat-id "matching" own-message {:chat-id "matching"
:from "me" :from "me"
:message-type :user-message :message-type constants/message-type-one-to-one
:message-id "1" :message-id "1"
:clock-value 1 :clock-value 1
:whisper-timestamp 0 :whisper-timestamp 0
@ -181,7 +148,7 @@
bad-chat-id-message {:chat-id "bad-chat-id" bad-chat-id-message {:chat-id "bad-chat-id"
:from "not-matching" :from "not-matching"
:message-type :user-message :message-type constants/message-type-one-to-one
:message-id "1" :message-id "1"
:clock-value 1 :clock-value 1
:whisper-timestamp 0 :whisper-timestamp 0
@ -229,38 +196,15 @@
:clock-value 0 :clock-value 0
:first-in-group? true :first-in-group? true
:from nil :from nil
:first-outgoing? nil :first-outgoing? false
:outgoing-seen? nil :outgoing-seen? false
:timestamp-str "timestamp" :timestamp-str "timestamp"
:first? true :first? true
:display-username? true :display-username? true
:outgoing nil}] :outgoing false}]
(models.message-list/->seq (models.message-list/->seq
(get-in fx1 [:db :chats "chat-id" :message-list])))) (get-in fx1 [:db :chats "chat-id" :message-list]))))
(is (= {} (is (= {}
(get-in fx2 [:db :chats "chat-id" :messages]))) (get-in fx2 [:db :chats "chat-id" :messages])))
(is (= nil (is (= nil
(get-in fx2 [:db :chats "chat-id" :message-list]))))))) (get-in fx2 [:db :chats "chat-id" :message-list])))))))
(deftest add-outgoing-status
(testing "coming from us"
(testing "system-message"
(let [message (message/add-outgoing-status {:message-type :system-message
:from "us"} "us")]
(is (not (:outgoing message)))
(is (not (:outgoing-status message)))))
(testing "has already a an outgoing status"
(testing "it does not override it"
(let [message (message/add-outgoing-status {:outgoing-status :sending
:from "us"} "us")]
(is (:outgoing message))
(is (= :sending (:outgoing-status message))))))
(testing "does not have an outgoing status"
(testing "it sets it to sent"
(let [message (message/add-outgoing-status {:from "us"} "us")]
(is (:outgoing message))
(is (= :sent (:outgoing-status message)))))))
(testing "not coming from us"
(let [message (message/add-outgoing-status {:from "not-us"} "us")]
(is (not (:outgoing message)))
(is (not (:outgoing-status message))))))

View File

@ -27,8 +27,6 @@
:is-active true :is-active true
:messages {} :messages {}
:pagination-info {} :pagination-info {}
:last-message-content "content"
:last-message-content-type "type"
:chat-id "chat-id" :chat-id "chat-id"
:loaded-unviewed-messages-ids [] :loaded-unviewed-messages-ids []
:timestamp 2 :timestamp 2
@ -37,8 +35,7 @@
:color "color" :color "color"
:name "name" :name "name"
:chatType 3 :chatType 3
:lastMessageContent "content" :lastMessage nil
:lastMessageContentType "type"
:members #{{:id "a" :members #{{:id "a"
:admin true :admin true
:joined true} :joined true}
@ -90,9 +87,6 @@
:admin false :admin false
:joined false}] :joined false}]
:lastClockValue 10 :lastClockValue 10
:lastMessageContent "\"content\"" ;; goes through edn/read-string
:lastMessageContentType "type"
:membershipUpdates [{:type "chat-created" :membershipUpdates [{:type "chat-created"
:name "test" :name "test"
:clockValue 1 :clockValue 1
@ -111,11 +105,9 @@
expected-chat {:public? false expected-chat {:public? false
:group-chat true :group-chat true
:color "color" :color "color"
:last-message-content "content"
:last-message-content-type "type"
:contacts #{"a" "b" "c" "d"} :contacts #{"a" "b" "c" "d"}
:last-clock-value 10 :last-clock-value 10
:last-message nil
:admins #{"a" "b"} :admins #{"a" "b"}
:members-joined #{"a" "c"} :members-joined #{"a" "c"}
:name "name" :name "name"

View File

@ -6,60 +6,45 @@
(def chat-id "chat-id") (def chat-id "chat-id")
(def from "0x0424a68f89ba5fcd5e0640c1e1f591d561fa4125ca4e2a43592bc4123eca10ce064e522c254bb83079ba404327f6eafc01ec90a1444331fe769d3f3a7f90b0dde1") (def from "0x0424a68f89ba5fcd5e0640c1e1f591d561fa4125ca4e2a43592bc4123eca10ce064e522c254bb83079ba404327f6eafc01ec90a1444331fe769d3f3a7f90b0dde1")
(deftest message->rpc
(testing "message to rpc"
(let [message {:message-id message-id
:content {:chat-id chat-id
:response-to "id-2"
:text "hta"}
:whisper-timestamp 1
:dedup-id "ATIwMTkwODE0YTdkNWZhZGY1N2E0ZDU3MzUxZmJkNDZkZGM1ZTU4ZjRlYzUyYWYyMDA5NTc2NWYyYmIxOTQ2OTM3NGUwNjdiMvEpTIGEjHOTAyqsrN39wST4npnSAv1AR8jJWeubanjkoGIyJooD5RVRnx6ZMt+/JzBOD2hoZzlHQWA0bC6XbdU="
:outgoing-status :sending
:message-type :public-group-user-message
:clock-value 2
:from from
:chat-id chat-id
:content-type "text/plain"
:timestamp 3}
expected {:id message-id
:whisperTimestamp 1
:from from
:chatId chat-id
:replyTo "id-2"
:content "{\"chat-id\":\"chat-id\",\"response-to\":\"id-2\",\"text\":\"hta\"}"
:contentType "text/plain"
:messageType "public-group-user-message"
:clockValue 2
:timestamp 3
:outgoingStatus "sending"}]
(is (= expected (m/->rpc message))))))
(deftest message<-rpc (deftest message<-rpc
(testing "message to rpc" (testing "message to rpc"
(let [expected {:message-id message-id (let [expected {:message-id message-id
:content {:chat-id chat-id :content {:chat-id chat-id
:text "hta"} :sticker {:hash "hash" :pack 1}
:text "hta"
:line-count 1
:ens-name "ens-name"
:parsed-text "parsed-text"
:rtl false
:response-to "a"}
:whisper-timestamp 1 :whisper-timestamp 1
:outgoing-status :sending :outgoing-status :sending
:outgoing :sending :outgoing true
:message-type :public-group-user-message :message-type 0
:clock-value 2 :clock-value 2
:from from :from from
:chat-id chat-id :chat-id chat-id
:quoted-message {:from "from" :quoted-message {:from "from"
:text "reply"} :text "reply"}
:content-type "text/plain" :content-type 1
:timestamp 3} :timestamp 3}
message {:id message-id message {:id message-id
:whisperTimestamp 1 :whisperTimestamp 1
:parsedText "parsed-text"
:ensName "ens-name"
:localChatId chat-id
:from from :from from
:text "hta"
:rtl false
:chatId chat-id :chatId chat-id
:content "{\"chat-id\":\"chat-id\",\"text\":\"hta\"}" :lineCount 1
:contentType "text/plain" :sticker {:hash "hash" :pack 1}
:messageType "public-group-user-message" :contentType 1
:clockValue 2 :messageType 0
:clock 2
:responseTo "a"
:quotedMessage {:from "from" :quotedMessage {:from "from"
:content "{\"chat-id\":\"chat-id\",\"text\":\"reply\"}"} :text "reply"}
:timestamp 3 :timestamp 3
:outgoingStatus "sending"}] :outgoingStatus "sending"}]
(is (= expected (m/<-rpc message)))))) (is (= expected (m/<-rpc message))))))

View File

@ -57,14 +57,7 @@
(:membership-updates actual)))) (:membership-updates actual))))
(testing "it sets the right admins" (testing "it sets the right admins"
(is (= #{admin} (is (= #{admin}
(:admins actual)))) (:admins actual))))))
(testing "it adds a system message"
(is (= 3 (count (:messages actual)))))
(testing "it adds the right text"
(is (= ["group-chat-created"
"group-chat-member-added"
"group-chat-member-added"]
(map (comp :text :content) (sort-by :clock-value (vals (:messages actual)))))))))
(testing "a chat with the wrong id" (testing "a chat with the wrong id"
(let [bad-chat-id (str random-id member-2) (let [bad-chat-id (str random-id member-2)
actual (-> actual (->
@ -162,18 +155,7 @@
(testing "members are updated" (testing "members are updated"
(is (= #{member-1 member-2 member-4} (:contacts actual-chat)))) (is (= #{member-1 member-2 member-4} (:contacts actual-chat))))
(testing "the name is updated" (testing "the name is updated"
(is (= "new-name" (:name actual-chat)))) (is (= "new-name" (:name actual-chat))))))))))
(testing "it adds a system message"
(is (= 7 (count (:messages actual-chat)))))
(testing "it sets the right text"
(is (= ["group-chat-created"
"group-chat-member-added"
"group-chat-member-added"
"group-chat-admin-added"
"group-chat-member-added"
"group-chat-member-removed"
"group-chat-name-changed"]
(map (comp :text :content) (sort-by :clock-value (vals (:messages actual-chat)))))))))))))
(deftest build-group-test (deftest build-group-test
(testing "only adds" (testing "only adds"

View File

@ -36,7 +36,6 @@
[status-im.test.sign-in.flow] [status-im.test.sign-in.flow]
[status-im.test.stickers.core] [status-im.test.stickers.core]
[status-im.test.transport.core] [status-im.test.transport.core]
[status-im.test.transport.utils]
[status-im.test.tribute-to-talk.core] [status-im.test.tribute-to-talk.core]
[status-im.test.tribute-to-talk.db] [status-im.test.tribute-to-talk.db]
[status-im.test.tribute-to-talk.whitelist] [status-im.test.tribute-to-talk.whitelist]
@ -112,7 +111,6 @@
'status-im.test.signing.core 'status-im.test.signing.core
'status-im.test.signing.gas 'status-im.test.signing.gas
'status-im.test.transport.core 'status-im.test.transport.core
'status-im.test.transport.utils
'status-im.test.tribute-to-talk.core 'status-im.test.tribute-to-talk.core
'status-im.test.tribute-to-talk.db 'status-im.test.tribute-to-talk.db
'status-im.test.tribute-to-talk.whitelist 'status-im.test.tribute-to-talk.whitelist

View File

@ -1,11 +0,0 @@
(ns status-im.test.transport.utils
(:require [cljs.test :refer-macros [deftest is testing]]
[status-im.utils.fx :as fx]
[status-im.transport.utils :as transport]))
(deftest test-message-id
(testing "test"
(let [pk "0x03d0370306168850aa1f06a2f22c9a756c7dd00e35dd797fcdf351e53ff6ae7b9f"
payload "0x74657374"
expected-message-id "0x642b7f39873aab69d5aee686f4ed0ca02f82e025242ea57569a70640a94aea34"]
(is (= expected-message-id (transport/message-id pk payload))))))

View File

@ -1,6 +1,7 @@
(ns status-im.test.tribute-to-talk.whitelist (ns status-im.test.tribute-to-talk.whitelist
(:require [cljs.test :refer-macros [deftest testing is]] (:require [cljs.test :refer-macros [deftest testing is]]
[status-im.utils.gfycat.core :as gfycat] [status-im.utils.gfycat.core :as gfycat]
[status-im.constants :as constants]
[status-im.utils.identicon :as identicon] [status-im.utils.identicon :as identicon]
[status-im.tribute-to-talk.whitelist :as whitelist])) [status-im.tribute-to-talk.whitelist :as whitelist]))
@ -113,21 +114,21 @@
(whitelist/filter-message (whitelist/filter-message
(whitelist/enable-whitelist ttt-enabled-multiaccount) (whitelist/enable-whitelist ttt-enabled-multiaccount)
:unfiltered-fx :unfiltered-fx
:user-message constants/message-type-one-to-one
nil nil
"whitelisted because added")) "whitelisted because added"))
(testing "tribute to talk is disabled" (testing "tribute to talk is disabled"
(whitelist/filter-message (whitelist/filter-message
ttt-disabled-multiaccount ttt-disabled-multiaccount
:unfiltered-fx :unfiltered-fx
:user-message constants/message-type-one-to-one
nil nil
"public-key")) "public-key"))
(testing "user is not whitelisted but transaction is valid" (testing "user is not whitelisted but transaction is valid"
(let [result (whitelist/filter-message (let [result (whitelist/filter-message
ttt-enabled-multiaccount ttt-enabled-multiaccount
#(assoc % :message-received true) #(assoc % :message-received true)
:user-message constants/message-type-one-to-one
"transaction-hash-1" "transaction-hash-1"
sender-pk)] sender-pk)]
(is (contains? (get-in result [:db :contacts/whitelist]) (is (contains? (get-in result [:db :contacts/whitelist])

View File

@ -492,14 +492,14 @@
"got-it": "Got it", "got-it": "Got it",
"group-chat": "Group chat", "group-chat": "Group chat",
"group-chat-admin": "Admin", "group-chat-admin": "Admin",
"group-chat-admin-added": "*{{member}}* has been made admin", "group-chat-admin-added": "**{{member}}** has been made admin",
"group-chat-created": "*{{member}}* created the group *{{name}}*", "group-chat-created": "**{{member}}** created the group **{{name}}**",
"group-chat-decline-invitation": "Decline invitation", "group-chat-decline-invitation": "Decline invitation",
"group-chat-member-added": "*{{member}}* has been invited", "group-chat-member-added": "**{{member}}** has been invited",
"group-chat-member-joined": "*{{member}}* has joined the group", "group-chat-member-joined": "**{{member}}** has joined the group",
"group-chat-member-removed": "*{{member}}* left the group", "group-chat-member-removed": "**{{member}}** left the group",
"group-chat-members-count": "{{selected}}/{{max}} members", "group-chat-members-count": "{{selected}}/{{max}} members",
"group-chat-name-changed": "*{{member}}* changed the group's name to *{{name}}*", "group-chat-name-changed": "**{{member}}** changed the group's name to **{{name}}**",
"group-chat-no-contacts": "You don't have any contacts yet.\nInvite your friends to start chatting", "group-chat-no-contacts": "You don't have any contacts yet.\nInvite your friends to start chatting",
"group-info": "Group info", "group-info": "Group info",
"gwei": "Gwei", "gwei": "Gwei",