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 () {},
"concat" : function () {},
"confirmMessagesProcessed" : function () {},
"chats": function() {},
"rawMessages": function() {},
"messages": function() {},
"discovery": function() {},
"dismiss": function() {},

View File

@ -140,7 +140,7 @@ class NewMessageSignalHandler {
void handleNewMessageSignal(JSONObject newMessageSignal) {
try {
JSONArray chatsNewMessagesData = newMessageSignal.getJSONObject("event").getJSONArray("messages");
JSONArray chatsNewMessagesData = newMessageSignal.getJSONObject("event").getJSONArray("chats");
for (int i = 0; i < chatsNewMessagesData.length(); i++) {
try {
upsertChat(chatsNewMessagesData.getJSONObject(i));
@ -148,6 +148,15 @@ class NewMessageSignalHandler {
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) {
refreshNotifications();
shouldRefreshNotifications = false;
@ -173,72 +182,69 @@ class NewMessageSignalHandler {
return person;
}
private void upsertChat(JSONObject chatNewMessagesData) {
private void upsertChat(JSONObject chatData) {
try {
JSONObject chatData = chatNewMessagesData.getJSONObject("chat");
// NOTE: this is an exemple of chatData
// {"chatId":"contact-discovery-3622","filterId":"c0239d63f830e8b25f4bf7183c8d207f355a925b89514a17068cae4898e7f193",
// "symKeyId":"","oneToOne":true,"identity":"046599511623d7385b926ce709ac57d518dac10d451a81f75cd32c7fb4b1c...",
// "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
// 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
// as have a method to add new ones after the handler is instanciated
if (oneToOne) {
JSONArray messagesData = chatNewMessagesData.getJSONArray("messages");
if (oneToOne == 1) {
//JSONArray messagesData = chatNewMessagesData.getJSONArray("messages");
// 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
String id = messagesData.getJSONObject(0).getJSONObject("message").getString("sig");
String id = chatData.getString("id");
StatusChat chat = chats.get(id);
// if the chat was not already there, we create one
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);
shouldRefreshNotifications = true;
}
}
} catch (JSONException e) {
Log.e(TAG, "JSON conversion failed: " + e.getMessage());
}
}
private StatusMessage createMessage(JSONObject messageData) {
try {
JSONObject metadata = messageData.getJSONObject("metadata");
JSONObject authorMetadata = metadata.getJSONObject("author");
JSONArray payload = new JSONArray(messageData.getString("payload"));
// NOTE: this is an exemple of payload we are currently working with
// it is in the transit format, which is basically JSON
// refer to `transport.message.transit.cljs` on react side for details
// ["~#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());
private void upsertMessage(JSONObject messageData) {
try {
String chatId = messageData.getString("localChatId");
StatusChat chat = chats.get(chatId);
if (chat == null) {
return;
}
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.oneToOne = oneToOne;
this.messages = new ArrayList<StatusMessage>();
this.name = name;
}
public String getId() {
@ -259,15 +266,23 @@ class StatusChat {
}
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
// in 1-1 chats it should be the name of the user whose
// key is different than ours
return getLastMessage().getAuthor().getName().toString();
//TODO this should be improved as it would rename the chat
// after our own user if we were posting from another device
// in 1-1 chats it should be the name of the user whose
// key is different than ours
StatusMessage message = getLastMessage();
if (message == null) {
return "no-name";
}
return message.getAuthor().getName().toString();
}
private StatusMessage getLastMessage() {
if (messages.size() > 0) {
return messages.get(messages.size()-1);
}
return null;
}
public long getTimestamp() {
@ -278,8 +293,8 @@ class StatusChat {
return messages;
}
public void setMessages(ArrayList<StatusMessage> messages) {
this.messages = messages;
public void appendMessage(StatusMessage message) {
this.messages.add(message);
}
public String getSummary() {

View File

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

View File

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

View File

@ -9,7 +9,7 @@
[status-im.chat.models.message-content :as message-content]
[status-im.constants :as constants]
[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.mailserver.core :as mailserver]
[status-im.native-module.core :as status]
@ -26,40 +26,20 @@
[status-im.utils.types :as types]
[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
[{:keys [content content-type] :as message} chat-id current-chat?]
(cond-> message
current-chat?
(assoc :seen true) (and (= constants/content-type-text content-type)
(message-content/should-collapse?
(:text content)
(:line-count content)))
(assoc :seen true)
(and (= constants/content-type-text content-type)
(message-content/should-collapse?
(:text content)
(:line-count content)))
(assoc :should-collapse? true)))
(defn system-message? [{:keys [message-type]}]
(= :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))
(= constants/message-type-private-group-system-message message-type))
(defn build-desktop-notification
[{:keys [db] :as cofx} {:keys [chat-id timestamp content from] :as message}]
@ -81,16 +61,10 @@
(fx/defn add-message
[{:keys [db] :as cofx}
{{:keys [chat-id message-id clock-value timestamp from] :as message} :message
:keys [current-chat? batch?]}]
{{:keys [chat-id message-id timestamp from] :as message} :message
:keys [current-chat?]}]
(let [current-public-key (multiaccounts.model/current-public-key cofx)
prepared-message (-> message
(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?]))]
prepared-message (prepare-message message chat-id current-chat?)]
(when (and platform/desktop?
(not= from current-public-key)
(get-in db [:multiaccount :desktop-notifications?])
@ -100,7 +74,6 @@
(fx/merge cofx
{:db (cond->
(-> 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
;; if the chat has not been initialized, but run into
;; some troubles disabling it, so next time
@ -110,25 +83,21 @@
(not= from current-public-key))
(update-in [:chats chat-id :loaded-unviewed-messages-ids]
(fnil conj #{}) message-id))}
#(messages-store/save-message % prepared-message)
(when (and platform/desktop?
(not batch?)
(not (system-message? prepared-message)))
(chat-model/update-dock-badge-label)))))
(fx/defn add-received-message
[{: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
current-public-key (multiaccounts.model/current-public-key cofx)
current-chat? (and (or (= :chat view-id)
(= :chat-modal view-id))
(= current-chat-id chat-id))]
(add-message cofx {:batch? true
:message message
:metadata metadata
:current-chat? current-chat?})))
(fx/merge cofx
(add-message {:message message
:current-chat? current-chat?}))))
(defn- add-to-chat?
[{: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]}]
"Validate and return a valid chat-id"
(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])
(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
(and (= :user-message message-type)
(and (= constants/message-type-one-to-one message-type)
(= (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
[{:keys [db] :as cofx} {:keys [chat-id from]}]
(fx/defn update-unviewed-count
[{:keys [now db] :as cofx} {:keys [chat-id
from
message-id] :as message}]
(let [{:keys [current-chat-id view-id]} db
chat-view? (or (= :chat view-id)
(= :chat-modal view-id))
current-count (get-in db [:chats chat-id :unviewed-messages-count])]
(if (or (and chat-view? (= current-chat-id chat-id))
(= from (multiaccounts.model/current-public-key cofx)))
current-count
(inc current-count))))
(cond
(= from (multiaccounts.model/current-public-key cofx))
;; nothing to do
nil
(fx/defn update-unviewed-count [{:keys [now db] :as cofx} {:keys [chat-id] :as message}]
{:db (update-in db [:chats chat-id]
assoc
:is-active true
:timestamp now
:unviewed-messages-count (calculate-unviewed-message-count cofx message))})
(and chat-view? (= current-chat-id chat-id))
(fx/merge cofx
(data-store.messages/mark-messages-seen current-chat-id [message-id]))
(fx/defn update-last-message [{:keys [db]} {:keys [clock-value chat-id content timestamp content-type]}]
(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)
:else
{:db (update-in db [:chats chat-id]
assoc
:last-message-clock-value clock-value
:last-message-content content
:last-message-timestamp timestamp
:last-message-content-type content-type)})))
:unviewed-messages-count (inc current-count))})))
(fx/defn receive-one
[cofx message]
@ -184,128 +146,32 @@
(let [message-with-chat-id (assoc message :chat-id chat-id)]
(when (add-to-chat? cofx message-with-chat-id)
(fx/merge cofx
(chat-model/ensure-chat {:chat-id chat-id})
(add-received-message message-with-chat-id)
(update-unviewed-count message-with-chat-id)
(chat-model/join-time-messages-checked chat-id)
(update-last-message message-with-chat-id)
(when platform/desktop?
(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))
(chat-model/update-dock-badge-label)))))))
;;;; 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])
(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
[{:keys [db] :as cofx} chat-id message-id status]
(fx/merge cofx
{:db (assoc-in db
[:chats chat-id :messages message-id :outgoing-status]
status)}
(messages-store/update-outgoing-status message-id status)))
(data-store.messages/update-outgoing-status message-id status)))
(fx/defn resend-message
[cofx chat-id message-id]
(let [message (get-in cofx [:db :chats chat-id :messages message-id])
send-record (-> message
(select-keys transport-keys)
(update :message-type keyword)
protocol/map->Message)
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))))
[{:keys [db] :as cofx} chat-id message-id]
(fx/merge cofx
{::json-rpc/call [{:method "shhext_reSendChatMessage"
:params [message-id]
:on-success #(log/debug "re-sent message successfully")
:on-error #(log/error "failed to re-send message" %)}]}
(update-message-status chat-id message-id :sending)))
(fx/defn rebuild-message-list
[{:keys [db]} chat-id]
@ -317,35 +183,26 @@
[{:keys [db] :as cofx} chat-id message-id]
(fx/merge cofx
{: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)))
(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]
(let [messages-fx (map #(add-message
{:batch false
:message (system-message cofx %)
:current-chat? true})
messages)]
(apply fx/merge cofx messages-fx)))
(data-store.messages/save-system-messages cofx messages))
(fx/defn send-message
[{:keys [db now] :as cofx} {:keys [chat-id] :as message}]
(let [{:keys [chats]} db
{:keys [last-clock-value] :as chat} (get chats chat-id)
message-data (-> message
(assoc :from (multiaccounts.model/current-public-key cofx)
:timestamp now
: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)))
(tribute-to-talk/add-transaction-hash db))]
(protocol/send-chat-message cofx message-data)))
(fx/defn toggle-expand-message
[{:keys [db]} chat-id message-id]
{: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
[status-im.js-dependencies :as dependencies]
[taoensso.timbre :as log]
[status-im.constants :as constants]
[status-im.utils.fx :as fx]
[status-im.chat.db :as chat.db]
[status-im.utils.datetime :as time]))
@ -20,12 +21,13 @@
whisper-timestamp]}]
(-> {:whisper-timestamp whisper-timestamp
:from from
:one-to-one? (= :user-message message-type)
:system-message? (= :system-message message-type)
:one-to-one? (= constants/message-type-one-to-one message-type)
:system-message? (= constants/message-type-private-group-system-message
message-type)
:clock-value clock-value
:type :message
:message-id message-id
:outgoing outgoing}
:outgoing (boolean outgoing)}
add-datemark
add-timestamp))
@ -65,7 +67,8 @@
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
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}
next-message]
(let [last-in-group? (or (nil? next-message)
@ -80,6 +83,7 @@
(not (same-group? current-message previous-message)))
:last-in-group? last-in-group?
:display-username? (and last-in-group?
(not system-message?)
(not outgoing)
(not one-to-one?))
:display-photo? (display-photo? current-message))))
@ -100,10 +104,13 @@
(defn update-previous-message
"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))]
(assoc previous-message
:display-username? (and last-in-group?
(not system-message?)
(not outgoing)
(not one-to-one?))
:last-in-group? last-in-group?)))

View File

@ -7,10 +7,15 @@
(def ms-in-bg-for-require-bioauth 5000)
(def content-type-text "text/plain")
(def content-type-sticker "sticker")
(def content-type-status "status")
(def content-type-emoji "emoji")
(def content-type-text 0)
(def content-type-sticker 1)
(def content-type-status 2)
(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
#{content-type-text content-type-emoji content-type-status})

View File

@ -23,9 +23,7 @@
public-key
{:keys [chat-id
unviewed-messages-count
last-message-content
last-message-timestamp
last-message-content-type]}]
last-message]}]
(let [removed-messages-ids (keep
(fn [[message-id {:keys [from]}]]
(when (= from public-key)
@ -41,9 +39,7 @@
(update-in [:chats chat-id]
assoc
:unviewed-messages-count unviewed-messages-count
:last-message-content last-message-content
:last-message-timestamp last-message-timestamp
:last-message-content-type last-message-content-type))]
:last-message last-message))]
{: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

View File

@ -115,7 +115,7 @@
(->> members
(map #(or (get all-contacts %)
(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 %))
(assoc % :admin? true)
%)))))

View File

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

View File

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

View File

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

View File

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

View File

@ -3,7 +3,7 @@
[re-frame.core :as re-frame]
[status-im.multiaccounts.core :as multiaccounts]
[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.login.core :as multiaccounts.login]
[status-im.multiaccounts.logout.core :as multiaccounts.logout]
@ -172,10 +172,7 @@
(handlers/register-handler-fx
:multiaccounts.logout.ui/logout-confirmed
(fn [cofx _]
(fx/merge
cofx
(data-store.messages/save-messages)
(multiaccounts.logout/logout))))
(multiaccounts.logout/logout cofx)))
;; multiaccounts update module
@ -551,8 +548,12 @@
(handlers/register-handler-fx
:chat.ui/resend-message
(fn [cofx [_ chat-id message-id]]
(chat.message/resend-message cofx chat-id message-id)))
(fn [{:keys [db] :as 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
:chat.ui/delete-message
@ -616,16 +617,6 @@
(fn [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
(handlers/register-handler-fx
@ -1204,13 +1195,20 @@
(fn [{:keys [db] :as cofx} [_ 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
:transport/message-sent
(fn [cofx [_ chat-id message message-type message-id messages-count]]
(fx/merge cofx
(when message (chat.message/add-message-with-id (assoc message :message-id message-id) chat-id))
(transport.message/set-message-envelope-hash chat-id message-id message-type messages-count))))
(fn [cofx [_ response messages-count]]
(let [{:keys [localChatId id messageType]} (-> response :messages first)]
(fx/merge cofx
(handle-update response)
(transport.message/set-message-envelope-hash localChatId id messageType messages-count)))))
(handlers/register-handler-fx
:transport/contact-message-sent

View File

@ -5,6 +5,7 @@
[clojure.string :as string]
[status-im.ethereum.json-rpc :as json-rpc]
[re-frame.core :as re-frame]
[status-im.constants :as constants]
[status-im.chat.models.message-content :as message-content]
[status-im.multiaccounts.core :as multiaccounts]
[status-im.multiaccounts.model :as multiaccounts.model]
@ -141,7 +142,7 @@
:success-event [:transport/message-sent
chat-id
(:message cofx)
:group-user-message]
constants/message-type-private-group]
:payload payload}}))))
(fx/defn handle-membership-update-received
@ -374,7 +375,7 @@
(let [get-contact (partial models.contact/build-contact cofx)
format-message (fn [contact text clock-value]
{:chat-id chat-id
:content {:text text}
:text text
:clock-value clock-value
:from (:public-key contact)})
creator-contact (when creator (get-contact creator))
@ -465,43 +466,6 @@
(transport.filters/upsert-group-chat-topics)
(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
"Upsert chat and receive message if valid"
;; Care needs to be taken here as chat-id is not coming from a whisper filter
@ -532,18 +496,7 @@
:contacts (:contacts new-group)})
(add-system-messages chat-id previous-chat new-group)
(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)))))))
(set-up-filter chat-id previous-chat))))))
(defn handle-sign-success
"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.update.core :as multiaccounts.update]
[status-im.native-module.core :as status]
[status-im.transport.message.protocol :as protocol]
[status-im.transport.utils :as transport.utils]
[status-im.ui.screens.mobile-network-settings.utils
:as
mobile-network-utils]
[status-im.ui.screens.navigation :as navigation]
[status-im.utils.config :as config]
[status-im.utils.fx :as fx]
[status-im.utils.handlers :as handlers]
[status-im.utils.platform :as platform]
@ -43,6 +43,14 @@
(defn connected? [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]
(get-in db [:mailserver/mailservers (node/current-fleet-key db) id]))
@ -297,8 +305,8 @@
(defn adjust-request-for-transit-time
[from]
(let [ttl (:ttl protocol/whisper-opts)
whisper-tolerance (:whisper-drift-tolerance protocol/whisper-opts)
(let [ttl (:ttl whisper-opts)
whisper-tolerance (:whisper-drift-tolerance whisper-opts)
adjustment (+ whisper-tolerance ttl)
adjusted-from (- (max from adjustment) adjustment)]
(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"
[topics chat-id include-discovery?]
(reduce-kv
(fn [acc topic {:keys [discovery? chat-ids]}]
(if (or (and discovery?
(fn [acc topic {:keys [negotiated?
discovery?
chat-ids]}]
(if (or (and (or discovery?
negotiated?)
include-discovery?)
(chat-ids chat-id))
(conj acc topic)

View File

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

View File

@ -60,7 +60,7 @@
(spec/def ::content-type #{constants/content-type-text
constants/content-type-emoji
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?
utils.clocks/safe-timestamp?))
(spec/def ::timestamp (spec/nilable pos-int?))

View File

@ -238,7 +238,7 @@
(fx/defn handle-negotiated-filter
"Check if it's a new filter, if so create an shh filter and process it"
[{: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
(partial not-loaded? db)
processed-filters)]

View File

@ -3,6 +3,10 @@
(:require [goog.object :as o]
[re-frame.core :as re-frame]
[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.ethereum.json-rpc :as json-rpc]
[status-im.ethereum.core :as ethereum]
@ -12,77 +16,34 @@
[status-im.transport.message.transit :as transit]
[status-im.transport.utils :as transport.utils]
[status-im.tribute-to-talk.whitelist :as whitelist]
[status-im.ens.core :as ens]
[cljs-bean.core :as clj-bean]
[status-im.utils.config :as config]
[status-im.utils.fx :as fx]
[taoensso.timbre :as log]
[status-im.ethereum.json-rpc :as json-rpc]))
(def message-type-message 1)
(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
(fx/defn handle-raw-message
"Receive message handles a new status-message.
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
in order to stop receiving that message"
[{:keys [db] :as cofx} now-in-s filter-chat-id message-js]
(let [blocked-contacts (get db :contacts/blocked #{})
timestamp (.-timestamp (.-message message-js))
metadata-js (.-metadata message-js)
metadata {:author {:publicKey (.-publicKey (.-author metadata-js))
:alias (.-alias (.-author metadata-js))
:identicon (.-identicon (.-author metadata-js))}
:dedupId (.-dedupId metadata-js)
:encryptionId (.-encryptionId metadata-js)
:messageId (.-messageId metadata-js)}
status-message (handle-message message-js)
sig (-> metadata :author :publicKey)]
(when (and sig
status-message
(not (blocked-contacts sig)))
(try
(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
[{:keys [db] :as cofx} raw-message-js]
(let [timestamp (.-timestamp raw-message-js)
sig (.-from raw-message-js)
payload (.-payload raw-message-js)]
(let [status-message (transit/deserialize payload)]
(when (and sig
status-message)
(try
(when-let [valid-message (protocol/validate status-message)]
(protocol/receive
valid-message
sig
sig
timestamp
cofx))
(catch :default e nil)))))) ; ignore unknown message types
(defn- js-obj->seq [obj]
;; Sometimes the filter will return a single object instead of a collection
@ -91,42 +52,48 @@
(aget obj i))
[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
::process
(fn [cofx [_ messages now-in-s]]
(let [[chat-id message] (first messages)
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]}))
(fn [cofx [_ response-js]]
(process-response cofx response-js)))
(fx/defn remove-hash
[{:keys [db] :as cofx} envelope-hash]
@ -192,16 +159,3 @@
{:name name
:profile-image photo-path
: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
(:require [cljs.spec.alpha :as spec]
[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.ethereum.core :as ethereum]
[status-im.transport.db :as transport.db]
[status-im.utils.pairing :as pairing.utils]
[status-im.transport.utils :as transport.utils]
[status-im.tribute-to-talk.whitelist :as whitelist]
[status-im.utils.config :as config]
[status-im.utils.fx :as fx]
[taoensso.timbre :as log]))
(defn discovery-topic-hash [] (transport.utils/get-topic constants/contact-discovery))
(defprotocol StatusMessage
"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")
(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"))
(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
"Sends the payload to topic"
[cofx chat-id success-event payload]
@ -52,24 +44,28 @@
success-event
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]
StatusMessage
(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)))))
(send [this chat-id {:keys [message] :as cofx}])
(validate [this]
(if (spec/valid? :message/message this)
this

View File

@ -4,17 +4,6 @@
[status-im.ethereum.core :as ethereum]
[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
"Get the topic of a group chat or public chat from the chat-id"
[chat-id]

View File

@ -1,6 +1,7 @@
(ns status-im.tribute-to-talk.whitelist
(:require [status-im.contact.db :as contact.db]
[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.utils.fx :as fx]))
@ -61,7 +62,7 @@
along the message"
[{: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 (or (not= :user-message message-type)
(if (or (not= constants/message-type-one-to-one message-type)
(contains? (:contacts/whitelist db) from))
received-message-fx
;; if ttt is disabled it passes

View File

@ -52,11 +52,6 @@
:number-of-lines 5}
(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]
[react/text {:style style/message-expand-button
:on-press #(re-frame/dispatch [:chat.ui/message-expand-toggled chat-id message-id])}
@ -103,6 +98,14 @@
(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
timestamp-str group-chat outgoing
current-public-key expanded?] :as message}
@ -184,7 +187,7 @@
[wrapper {:keys [content] :as message}]
[wrapper message
[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
[wrapper {:keys [content-type] :as message}]
@ -220,7 +223,7 @@
[{:keys [chat-id message-id outgoing-status
first-outgoing?
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
:sending [message-activity-indicator]
:not-sent [message-not-sent-text chat-id message-id]
@ -267,25 +270,26 @@
(defn chat-message
[{:keys [outgoing group-chat modal? current-public-key content-type content] :as message}]
[react/view
[react/touchable-highlight
{:on-press (fn [arg]
(if (and platform/desktop? (= "right" (.-button (.-nativeEvent arg))))
(open-chat-context-menu message)
(do
(when (and (= content-type constants/content-type-sticker) (:pack content))
(re-frame/dispatch [:stickers/open-sticker-pack (:pack content)]))
(re-frame/dispatch [:chat.ui/set-chat-ui-props {:messages-focused? true
:input-bottom-sheet nil}])
(when-not platform/desktop?
(react/dismiss-keyboard!)))))
:on-long-press #(when (or (= content-type constants/content-type-text)
(= content-type constants/content-type-emoji))
(open-chat-context-menu message))}
[react/view {:accessibility-label :chat-item}
(let [incoming-group (and group-chat (not outgoing))]
[message-content message-body (merge message
{:current-public-key current-public-key
:group-chat group-chat
:modal? modal?
:incoming-group incoming-group})])]]])
(let [sticker (:sticker content)]
[react/view
[react/touchable-highlight
{:on-press (fn [arg]
(if (and platform/desktop? (= "right" (.-button (.-nativeEvent arg))))
(open-chat-context-menu message)
(do
(when (and (= content-type constants/content-type-sticker) (:pack sticker))
(re-frame/dispatch [:stickers/open-sticker-pack (:pack sticker)]))
(re-frame/dispatch [:chat.ui/set-chat-ui-props {:messages-focused? true
:input-bottom-sheet nil}])
(when-not platform/desktop?
(react/dismiss-keyboard!)))))
:on-long-press #(when (or (= content-type constants/content-type-text)
(= content-type constants/content-type-emoji))
(open-chat-context-menu message))}
[react/view {:accessibility-label :chat-item}
(let [incoming-group (and group-chat (not outgoing))]
[message-content message-body (merge message
{:current-public-key current-public-key
:group-chat group-chat
:modal? modal?
:incoming-group incoming-group})])]]]))

View File

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

View File

@ -3,6 +3,7 @@
[reagent.core :as reagent]
[status-im.ens.core :as ens]
[status-im.ethereum.core :as ethereum]
[status-im.constants :as constants]
[status-im.ethereum.ens :as ethereum.ens]
[status-im.ethereum.stateofus :as stateofus]
[status-im.i18n :as i18n]
@ -649,7 +650,7 @@
:action-fn #(re-frame/dispatch [::ens/switch-show-username])
:value show?}]]
(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"}]
[react/view
[react/view {:padding-left 60}

View File

@ -25,7 +25,7 @@
(= constants/content-type-sticker content-type)
[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))
[react/text {:style styles/last-message-text}
@ -36,14 +36,7 @@
:number-of-lines 1
:ellipsize-mode :tail
:accessibility-label :chat-message-text}
(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])])
(string/trim-newline (:text content))])])
(defn message-timestamp [timestamp]
(when timestamp
@ -61,10 +54,8 @@
[chat-id chat-name
color online group-chat
public? contact
last-message-timestamp
timestamp
last-message-content
last-message-content-type]} home-item
last-message]} home-item
private-group? (and group-chat (not public?))
public-group? (and group-chat public?)
truncated-chat-name (utils/truncate-str chat-name 30)
@ -81,14 +72,14 @@
:else nil)
:title truncated-chat-name
:title-accessibility-label :chat-name-text
:title-row-accessory [message-timestamp (if (pos? last-message-timestamp)
last-message-timestamp
:title-row-accessory [message-timestamp (if (pos? (:whisper-timestamp last-message))
(:whisper-timestamp last-message)
timestamp)]
:subtitle
(let [{:keys [tribute-status tribute-label]} (:tribute-to-talk contact)]
(if (not (#{:require :pending} tribute-status))
[message-content-text {:content last-message-content
:content-type last-message-content-type}]
[message-content-text {:content (:content last-message)
:content-type (:content-type last-message)}]
tribute-label))
:subtitle-row-accessory [unviewed-indicator chat-id]
: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
;; 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
;; for the chat and the bid-timestamp.
@ -70,13 +70,12 @@
;; http://amturing.acm.org/p558-lamport.pdf
(def one-month-in-ms (* 60 60 24 31 1000))
(def post-id-digits 100)
(defn- ->timestamp-bid []
(* (utils.datetime/timestamp) post-id-digits))
(utils.datetime/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
; 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

View File

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

View File

@ -88,31 +88,6 @@
(when 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
(defn set-timeout [cb ms]
@ -159,4 +134,4 @@
(let [decimal-part (get (string/split (str amount) ".") 1)]
(if (> (count decimal-part) places)
(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",
"owner": "status-im",
"repo": "status-go",
"version": "v0.35.1",
"commit-sha1": "4769fb91c3247c72c21013bf36194843d8d72d18",
"src-sha256": "1b6050kvnvf69gqw5llb7gb5v25pq6vbhkf9nznk9ra2spasqqzy"
"version": "v0.36.1",
"commit-sha1": "fd49b0140ebafdcec35b4da84685bcd8559a7dd9",
"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'
% public_chat_before_sync_name)
device_2_home.element_by_text(group_chat_name).click()
device_2_group_chat = device_2_home.get_chat_view()
# Disabling for now until GroupMembershipUpdate is also moved to status-go
#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():
self.errors.append('"%s" message in group chat is not synced' % message_after_sync)
#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)
device_2_group_chat.get_back_to_home_view()
#device_2_group_chat.get_back_to_home_view()
device_2_home.profile_button.click()
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')
@ -951,4 +952,4 @@ class TestProfileMultipleDevice(MultipleDeviceTestCase):
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.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):
return "*%s* left the group" % username
return "%s left the group" % username
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):
return "*%s* has joined the group" % username
return "%s has joined the group" % 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):

View File

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

View File

@ -2,6 +2,7 @@
(:require [cljs.test :refer-macros [deftest is testing]]
[status-im.utils.gfycat.core :as gfycat]
[status-im.utils.identicon :as identicon]
[status-im.constants :as constants]
[status-im.utils.datetime :as time]
[status-im.transport.message.protocol :as protocol]
[status-im.chat.models.message-list :as models.message-list]
@ -49,51 +50,17 @@
(testing "a message coming from you!"
(let [actual (message/receive-one {:db db}
{:from "me"
:message-type :user-message
:message-type constants/message-type-one-to-one
:timestamp 0
:whisper-timestamp 0
:message-id "id"
:chat-id "chat-id"
:outgoing true
:content "b"
:clock-value 1})
message (get-in actual [:db :chats "chat-id" :messages "id"])]
(testing "it adds the 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)))))))
(is message))))))
(deftest receive-group-chats
(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")
valid-message {:chat-id "chat-id"
:from "present"
:message-type :group-user-message
:message-type constants/message-type-private-group
:message-id "1"
:clock-value 1
:whisper-timestamp 0
:timestamp 0}
bad-chat-id-message {:chat-id "bad-chat-id"
:from "present"
:message-type :group-user-message
:message-type constants/message-type-private-group
:message-id "1"
:clock-value 1
:whisper-timestamp 0
:timestamp 0}
bad-from-message {:chat-id "chat-id"
:from "not-present"
:message-type :group-user-message
:message-type constants/message-type-private-group
:message-id "1"
:clock-value 1
:whisper-timestamp 0
@ -139,14 +106,14 @@
:view-id :chat}}
valid-message {:chat-id "chat-id"
:from "anyone"
:message-type :public-group-user-message
:message-type constants/message-type-public-group
:message-id "1"
:clock-value 1
:whisper-timestamp 0
:timestamp 0}
bad-chat-id-message {:chat-id "bad-chat-id"
:from "present"
:message-type :public-group-user-message
:message-type constants/message-type-public-group
:message-id "1"
:clock-value 1
:whisper-timestamp 0
@ -166,14 +133,14 @@
:view-id :chat}}
valid-message {:chat-id "matching"
:from "matching"
:message-type :user-message
:message-type constants/message-type-one-to-one
:message-id "1"
:clock-value 1
:whisper-timestamp 0
:timestamp 0}
own-message {:chat-id "matching"
:from "me"
:message-type :user-message
:message-type constants/message-type-one-to-one
:message-id "1"
:clock-value 1
:whisper-timestamp 0
@ -181,7 +148,7 @@
bad-chat-id-message {:chat-id "bad-chat-id"
:from "not-matching"
:message-type :user-message
:message-type constants/message-type-one-to-one
:message-id "1"
:clock-value 1
:whisper-timestamp 0
@ -229,38 +196,15 @@
:clock-value 0
:first-in-group? true
:from nil
:first-outgoing? nil
:outgoing-seen? nil
:first-outgoing? false
:outgoing-seen? false
:timestamp-str "timestamp"
:first? true
:display-username? true
:outgoing nil}]
:outgoing false}]
(models.message-list/->seq
(get-in fx1 [:db :chats "chat-id" :message-list]))))
(is (= {}
(get-in fx2 [:db :chats "chat-id" :messages])))
(is (= nil
(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
:messages {}
:pagination-info {}
:last-message-content "content"
:last-message-content-type "type"
:chat-id "chat-id"
:loaded-unviewed-messages-ids []
:timestamp 2
@ -37,8 +35,7 @@
:color "color"
:name "name"
:chatType 3
:lastMessageContent "content"
:lastMessageContentType "type"
:lastMessage nil
:members #{{:id "a"
:admin true
:joined true}
@ -90,9 +87,6 @@
:admin false
:joined false}]
:lastClockValue 10
:lastMessageContent "\"content\"" ;; goes through edn/read-string
:lastMessageContentType "type"
:membershipUpdates [{:type "chat-created"
:name "test"
:clockValue 1
@ -111,11 +105,9 @@
expected-chat {:public? false
:group-chat true
:color "color"
:last-message-content "content"
:last-message-content-type "type"
:contacts #{"a" "b" "c" "d"}
:last-clock-value 10
:last-message nil
:admins #{"a" "b"}
:members-joined #{"a" "c"}
:name "name"

View File

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

View File

@ -57,14 +57,7 @@
(:membership-updates actual))))
(testing "it sets the right admins"
(is (= #{admin}
(: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)))))))))
(:admins actual))))))
(testing "a chat with the wrong id"
(let [bad-chat-id (str random-id member-2)
actual (->
@ -162,18 +155,7 @@
(testing "members are updated"
(is (= #{member-1 member-2 member-4} (:contacts actual-chat))))
(testing "the name is updated"
(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)))))))))))))
(is (= "new-name" (:name actual-chat))))))))))
(deftest build-group-test
(testing "only adds"

View File

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

View File

@ -492,14 +492,14 @@
"got-it": "Got it",
"group-chat": "Group chat",
"group-chat-admin": "Admin",
"group-chat-admin-added": "*{{member}}* has been made admin",
"group-chat-created": "*{{member}}* created the group *{{name}}*",
"group-chat-admin-added": "**{{member}}** has been made admin",
"group-chat-created": "**{{member}}** created the group **{{name}}**",
"group-chat-decline-invitation": "Decline invitation",
"group-chat-member-added": "*{{member}}* has been invited",
"group-chat-member-joined": "*{{member}}* has joined the group",
"group-chat-member-removed": "*{{member}}* left the group",
"group-chat-member-added": "**{{member}}** has been invited",
"group-chat-member-joined": "**{{member}}** has joined the group",
"group-chat-member-removed": "**{{member}}** left the group",
"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-info": "Group info",
"gwei": "Gwei",