diff --git a/background.html b/background.html
index ada17196fbfd..cd25b7d83802 100644
--- a/background.html
+++ b/background.html
@@ -348,7 +348,6 @@
-
diff --git a/js/background.js b/js/background.js
index 2ab80c420b71..9b6aa63d4d6b 100644
--- a/js/background.js
+++ b/js/background.js
@@ -25,14 +25,24 @@
wait: 500,
maxSize: 500,
processBatch: async items => {
- const bySource = _.groupBy(items, item => item.source || item.sourceUuid);
- const sources = Object.keys(bySource);
+ const byConversationId = _.groupBy(items, item =>
+ ConversationController.ensureContactIds({
+ e164: item.source,
+ uuid: item.sourceUuid,
+ })
+ );
+ const ids = Object.keys(byConversationId);
- for (let i = 0, max = sources.length; i < max; i += 1) {
- const source = sources[i];
- const timestamps = bySource[source].map(item => item.timestamp);
+ for (let i = 0, max = ids.length; i < max; i += 1) {
+ const conversationId = ids[i];
+ const timestamps = byConversationId[conversationId].map(
+ item => item.timestamp
+ );
+
+ const c = ConversationController.get(conversationId);
+ const uuid = c.get('uuid');
+ const e164 = c.get('e164');
- const c = ConversationController.get(source);
c.queueJob(async () => {
try {
const { wrap, sendOptions } = ConversationController.prepareForSend(
@@ -41,15 +51,15 @@
// eslint-disable-next-line no-await-in-loop
await wrap(
textsecure.messaging.sendDeliveryReceipt(
- c.get('e164'),
- c.get('uuid'),
+ e164,
+ uuid,
timestamps,
sendOptions
)
);
} catch (error) {
window.log.error(
- `Failed to send delivery receipt to ${source} for timestamps ${timestamps}:`,
+ `Failed to send delivery receipt to ${e164}/${uuid} for timestamps ${timestamps}:`,
error && error.stack ? error.stack : error
);
}
@@ -577,6 +587,7 @@
}
});
+ window.Signal.conversationControllerStart();
try {
await Promise.all([
ConversationController.load(),
@@ -584,6 +595,7 @@
Signal.Emojis.load(),
textsecure.storage.protocol.hydrateCaches(),
]);
+ await ConversationController.checkForConflicts();
} catch (error) {
window.log.error(
'background.js: ConversationController failed to load:',
@@ -1759,6 +1771,7 @@
const deviceId = textsecure.storage.user.getDeviceId();
+ // If we didn't capture a UUID on registration, go get it from the server
if (!textsecure.storage.user.getUuid()) {
const server = WebAPI.connect({
username: OLD_USERNAME,
@@ -1954,6 +1967,7 @@
const senderId = ConversationController.ensureContactIds({
e164: sender,
uuid: senderUuid,
+ highTrust: true,
});
const conversation = ConversationController.get(groupId || senderId);
const ourId = ConversationController.getOurConversationId();
@@ -2047,6 +2061,7 @@
const detailsId = ConversationController.ensureContactIds({
e164: details.number,
uuid: details.uuid,
+ highTrust: true,
});
const conversation = ConversationController.get(detailsId);
let activeAt = conversation.get('active_at');
@@ -2236,12 +2251,10 @@
return;
}
- const sourceE164 = textsecure.storage.user.getNumber();
- const sourceUuid = textsecure.storage.user.getUuid();
const receivedAt = Date.now();
await conversation.updateExpirationTimer(
expireTimer,
- sourceE164 || sourceUuid,
+ ConversationController.getOurConversationId(),
receivedAt,
{
fromSync: true,
@@ -2256,10 +2269,16 @@
});
// Matches event data from `libtextsecure` `MessageReceiver::handleSentMessage`:
- const getDescriptorForSent = ({ message, destination }) =>
+ const getDescriptorForSent = ({ message, destination, destinationUuid }) =>
message.group
? getGroupDescriptor(message.group)
- : { type: Message.PRIVATE, id: destination };
+ : {
+ type: Message.PRIVATE,
+ id: ConversationController.ensureContactIds({
+ e164: destination,
+ uuid: destinationUuid,
+ }),
+ };
// Matches event data from `libtextsecure` `MessageReceiver::handleDataMessage`:
const getDescriptorForReceived = ({ message, source, sourceUuid }) =>
@@ -2270,6 +2289,7 @@
id: ConversationController.ensureContactIds({
e164: source,
uuid: sourceUuid,
+ highTrust: true,
}),
};
@@ -2280,13 +2300,12 @@
messageDescriptor,
}) {
const profileKey = data.message.profileKey.toString('base64');
- const sender = await ConversationController.getOrCreateAndWait(
- messageDescriptor.id,
- 'private'
- );
+ const sender = await ConversationController.get(messageDescriptor.id);
- // Will do the save for us
- await sender.setProfileKey(profileKey);
+ if (sender) {
+ // Will do the save for us
+ await sender.setProfileKey(profileKey);
+ }
return confirm();
}
@@ -2357,9 +2376,12 @@
}
async function onProfileKeyUpdate({ data, confirm }) {
- const conversation = ConversationController.get(
- data.source || data.sourceUuid
- );
+ const conversationId = ConversationController.ensureContactIds({
+ e164: data.source,
+ uuid: data.sourceUuid,
+ highTrust: true,
+ });
+ const conversation = ConversationController.get(conversationId);
if (!conversation) {
window.log.error(
@@ -2397,11 +2419,8 @@
messageDescriptor,
}) {
// First set profileSharing = true for the conversation we sent to
- const { id, type } = messageDescriptor;
- const conversation = await ConversationController.getOrCreateAndWait(
- id,
- type
- );
+ const { id } = messageDescriptor;
+ const conversation = await ConversationController.get(id);
conversation.enableProfileSharing();
window.Signal.Data.updateConversation(conversation.attributes);
@@ -2417,7 +2436,7 @@
return confirm();
}
- function createSentMessage(data) {
+ function createSentMessage(data, descriptor) {
const now = Date.now();
let sentTo = [];
@@ -2430,6 +2449,11 @@
data.unidentifiedDeliveries = unidentified.map(item => item.destination);
}
+ const isGroup = descriptor.type === Message.GROUP;
+ const conversationId = isGroup
+ ? ConversationController.ensureGroup(descriptor.id)
+ : descriptor.id;
+
return new Whisper.Message({
source: textsecure.storage.user.getNumber(),
sourceUuid: textsecure.storage.user.getUuid(),
@@ -2438,7 +2462,7 @@
serverTimestamp: data.serverTimestamp,
sent_to: sentTo,
received_at: now,
- conversationId: data.destination,
+ conversationId,
type: 'outgoing',
sent: true,
unidentifiedDeliveries: data.unidentifiedDeliveries || [],
@@ -2468,7 +2492,7 @@
});
}
- const message = createSentMessage(data);
+ const message = createSentMessage(data, messageDescriptor);
if (data.message.reaction) {
const { reaction } = data.message;
@@ -2502,12 +2526,7 @@
return Promise.resolve();
}
- ConversationController.getOrCreate(
- messageDescriptor.id,
- messageDescriptor.type
- );
// Don't wait for handleDataMessage, as it has its own per-conversation queueing
-
message.handleDataMessage(data.message, event.confirm, {
data,
});
@@ -2520,12 +2539,10 @@
const fromContactId = ConversationController.ensureContactIds({
e164: data.source,
uuid: data.sourceUuid,
+ highTrust: true,
});
- // Determine if this message is in a group
const isGroup = descriptor.type === Message.GROUP;
-
- // Determine the conversationId this message belongs to
const conversationId = isGroup
? ConversationController.ensureGroup(descriptor.id, {
addedBy: fromContactId,
@@ -2651,10 +2668,7 @@
});
const conversationId = message.get('conversationId');
- const conversation = ConversationController.getOrCreate(
- conversationId,
- 'private'
- );
+ const conversation = ConversationController.get(conversationId);
// This matches the queueing behavior used in Message.handleDataMessage
conversation.queueJob(async () => {
@@ -2791,9 +2805,13 @@
function onReadReceipt(ev) {
const readAt = ev.timestamp;
- const { timestamp } = ev.read;
- const reader = ConversationController.getConversationId(ev.read.reader);
- window.log.info('read receipt', reader, timestamp);
+ const { timestamp, source, sourceUuid } = ev.read;
+ const reader = ConversationController.ensureContactIds({
+ e164: source,
+ uuid: sourceUuid,
+ highTrust: true,
+ });
+ window.log.info('read receipt', timestamp, source, sourceUuid, reader);
ev.confirm();
@@ -2879,7 +2897,11 @@
ev.viaContactSync ? 'via contact sync' : ''
);
- const verifiedId = ConversationController.ensureContactIds({ e164, uuid });
+ const verifiedId = ConversationController.ensureContactIds({
+ e164,
+ uuid,
+ highTrust: true,
+ });
const contact = await ConversationController.get(verifiedId, 'private');
const options = {
viaSyncMessage: true,
@@ -2899,20 +2921,23 @@
function onDeliveryReceipt(ev) {
const { deliveryReceipt } = ev;
const { sourceUuid, source } = deliveryReceipt;
- const identifier = source || sourceUuid;
window.log.info(
'delivery receipt from',
- `${identifier}.${deliveryReceipt.sourceDevice}`,
+ `${source} ${sourceUuid} ${deliveryReceipt.sourceDevice}`,
deliveryReceipt.timestamp
);
ev.confirm();
- const deliveredTo = ConversationController.getConversationId(identifier);
+ const deliveredTo = ConversationController.ensureContactIds({
+ e164: source,
+ uuid: sourceUuid,
+ highTrust: true,
+ });
if (!deliveredTo) {
- window.log.info('no conversation for identifier', identifier);
+ window.log.info('no conversation for', source, sourceUuid);
return;
}
diff --git a/js/conversation_controller.js b/js/conversation_controller.js
deleted file mode 100644
index ad447ffb98ee..000000000000
--- a/js/conversation_controller.js
+++ /dev/null
@@ -1,353 +0,0 @@
-/* global _, Whisper, Backbone, storage, textsecure */
-
-/* eslint-disable more/no-then */
-
-// eslint-disable-next-line func-names
-(function() {
- 'use strict';
-
- window.Whisper = window.Whisper || {};
-
- const MAX_MESSAGE_BODY_LENGTH = 64 * 1024;
-
- const conversations = new Whisper.ConversationCollection();
- const inboxCollection = new (Backbone.Collection.extend({
- initialize() {
- this.listenTo(conversations, 'add change:active_at', this.addActive);
- this.listenTo(conversations, 'reset', () => this.reset([]));
-
- this.on(
- 'add remove change:unreadCount',
- _.debounce(this.updateUnreadCount.bind(this), 1000)
- );
- },
- addActive(model) {
- if (model.get('active_at')) {
- this.add(model);
- } else {
- this.remove(model);
- }
- },
- updateUnreadCount() {
- const newUnreadCount = _.reduce(
- this.map(m => m.get('unreadCount')),
- (item, memo) => item + memo,
- 0
- );
- storage.put('unreadCount', newUnreadCount);
-
- if (newUnreadCount > 0) {
- window.setBadgeCount(newUnreadCount);
- window.document.title = `${window.getTitle()} (${newUnreadCount})`;
- } else {
- window.setBadgeCount(0);
- window.document.title = window.getTitle();
- }
- window.updateTrayIcon(newUnreadCount);
- },
- }))();
-
- window.getInboxCollection = () => inboxCollection;
- window.getConversations = () => conversations;
-
- window.ConversationController = {
- get(id) {
- if (!this._initialFetchComplete) {
- throw new Error(
- 'ConversationController.get() needs complete initial fetch'
- );
- }
-
- return conversations.get(id);
- },
- // Needed for some model setup which happens during the initial fetch() call below
- getUnsafe(id) {
- return conversations.get(id);
- },
- dangerouslyCreateAndAdd(attributes) {
- return conversations.add(attributes);
- },
- getOrCreate(identifier, type, additionalInitialProps = {}) {
- if (typeof identifier !== 'string') {
- throw new TypeError("'id' must be a string");
- }
-
- if (type !== 'private' && type !== 'group') {
- throw new TypeError(
- `'type' must be 'private' or 'group'; got: '${type}'`
- );
- }
-
- if (!this._initialFetchComplete) {
- throw new Error(
- 'ConversationController.get() needs complete initial fetch'
- );
- }
-
- let conversation = conversations.get(identifier);
- if (conversation) {
- return conversation;
- }
-
- const id = window.getGuid();
-
- if (type === 'group') {
- conversation = conversations.add({
- id,
- uuid: null,
- e164: null,
- groupId: identifier,
- type,
- version: 2,
- ...additionalInitialProps,
- });
- } else if (window.isValidGuid(identifier)) {
- conversation = conversations.add({
- id,
- uuid: identifier,
- e164: null,
- groupId: null,
- type,
- version: 2,
- ...additionalInitialProps,
- });
- } else {
- conversation = conversations.add({
- id,
- uuid: null,
- e164: identifier,
- groupId: null,
- type,
- version: 2,
- ...additionalInitialProps,
- });
- }
-
- const create = async () => {
- if (!conversation.isValid()) {
- const validationError = conversation.validationError || {};
- window.log.error(
- 'Contact is not valid. Not saving, but adding to collection:',
- conversation.idForLogging(),
- validationError.stack
- );
-
- return conversation;
- }
-
- try {
- await window.Signal.Data.saveConversation(conversation.attributes, {
- Conversation: Whisper.Conversation,
- });
- } catch (error) {
- window.log.error(
- 'Conversation save failed! ',
- identifier,
- type,
- 'Error:',
- error && error.stack ? error.stack : error
- );
- throw error;
- }
-
- return conversation;
- };
-
- conversation.initialPromise = create();
-
- return conversation;
- },
- getOrCreateAndWait(id, type, additionalInitialProps = {}) {
- return this._initialPromise.then(() => {
- const conversation = this.getOrCreate(id, type, additionalInitialProps);
-
- if (conversation) {
- return conversation.initialPromise.then(() => conversation);
- }
-
- return Promise.reject(
- new Error('getOrCreateAndWait: did not get conversation')
- );
- });
- },
- getConversationId(address) {
- if (!address) {
- return null;
- }
-
- const [id] = textsecure.utils.unencodeNumber(address);
- const conv = this.get(id);
-
- if (conv) {
- return conv.get('id');
- }
-
- return null;
- },
- getOurConversationId() {
- const e164 = textsecure.storage.user.getNumber();
- const uuid = textsecure.storage.user.getUuid();
- return this.ensureContactIds({ e164, uuid });
- },
- /**
- * Given a UUID and/or an E164, resolves to a string representing the local
- * database of the given contact. If a conversation is found it is updated
- * to have the given UUID and E164. If a conversation is not found, this
- * function creates a conversation with the given UUID and E164. If the
- * conversation * is found in the local database it is updated.
- *
- * This function also additionally checks for mismatched e164/uuid pairs out
- * of abundance of caution.
- */
- ensureContactIds({ e164, uuid }) {
- // Check for at least one parameter being provided. This is necessary
- // because this path can be called on startup to resolve our own ID before
- // our phone number or UUID are known. The existing behavior in these
- // cases can handle a returned `undefined` id, so we do that.
- if (!e164 && !uuid) {
- return undefined;
- }
-
- const lowerUuid = uuid ? uuid.toLowerCase() : undefined;
-
- const convoE164 = this.get(e164);
- const convoUuid = this.get(lowerUuid);
-
- // Check for mismatched UUID and E164
- if (
- convoE164 &&
- convoUuid &&
- convoE164.get('id') !== convoUuid.get('id')
- ) {
- window.log.warn('Received a message with a mismatched UUID and E164.');
- }
-
- const convo = convoUuid || convoE164;
-
- const idOrIdentifier = convo ? convo.get('id') : e164 || lowerUuid;
-
- const finalConversation = this.getOrCreate(idOrIdentifier, 'private');
- finalConversation.updateE164(e164);
- finalConversation.updateUuid(lowerUuid);
-
- return finalConversation.get('id');
- },
- /**
- * Given a groupId and optional additional initialization properties,
- * ensures the existence of a group conversation and returns a string
- * representing the local database ID of the group conversation.
- */
- ensureGroup(groupId, additionalInitProps = {}) {
- return this.getOrCreate(groupId, 'group', additionalInitProps).get('id');
- },
- /**
- * Given certain metadata about a message (an identifier of who wrote the
- * message and the sent_at timestamp of the message) returns the
- * conversation the message belongs to OR null if a conversation isn't
- * found.
- * @param {string} targetFrom The E164, UUID, or Conversation ID of the message author
- * @param {number} targetTimestamp The sent_at timestamp of the target message
- */
- async getConversationForTargetMessage(targetFrom, targetTimestamp) {
- const targetFromId = this.getConversationId(targetFrom);
-
- const messages = await window.Signal.Data.getMessagesBySentAt(
- targetTimestamp,
- {
- MessageCollection: Whisper.MessageCollection,
- }
- );
- const targetMessage = messages.find(m => {
- const contact = m.getContact();
-
- if (!contact) {
- return false;
- }
-
- const mcid = contact.get('id');
- return mcid === targetFromId;
- });
-
- if (targetMessage) {
- return targetMessage.getConversation();
- }
-
- return null;
- },
- prepareForSend(id, options) {
- // id is any valid conversation identifier
- const conversation = this.get(id);
- const sendOptions = conversation
- ? conversation.getSendOptions(options)
- : null;
- const wrap = conversation
- ? conversation.wrapSend.bind(conversation)
- : promise => promise;
-
- return { wrap, sendOptions };
- },
- async getAllGroupsInvolvingId(conversationId) {
- const groups = await window.Signal.Data.getAllGroupsInvolvingId(
- conversationId,
- {
- ConversationCollection: Whisper.ConversationCollection,
- }
- );
- return groups.map(group => conversations.add(group));
- },
- loadPromise() {
- return this._initialPromise;
- },
- reset() {
- this._initialPromise = Promise.resolve();
- this._initialFetchComplete = false;
- conversations.reset([]);
- },
- async load() {
- window.log.info('ConversationController: starting initial fetch');
-
- if (conversations.length) {
- throw new Error('ConversationController: Already loaded!');
- }
-
- const load = async () => {
- try {
- const collection = await window.Signal.Data.getAllConversations({
- ConversationCollection: Whisper.ConversationCollection,
- });
-
- conversations.add(collection.models);
-
- this._initialFetchComplete = true;
- await Promise.all(
- conversations.map(async conversation => {
- if (!conversation.get('lastMessage')) {
- await conversation.updateLastMessage();
- }
-
- // In case a too-large draft was saved to the database
- const draft = conversation.get('draft');
- if (draft && draft.length > MAX_MESSAGE_BODY_LENGTH) {
- this.model.set({
- draft: draft.slice(0, MAX_MESSAGE_BODY_LENGTH),
- });
- window.Signal.Data.updateConversation(conversation.attributes);
- }
- })
- );
- window.log.info('ConversationController: done with initial fetch');
- } catch (error) {
- window.log.error(
- 'ConversationController: initial fetch failed',
- error && error.stack ? error.stack : error
- );
- throw error;
- }
- };
-
- this._initialPromise = load();
-
- return this._initialPromise;
- },
- };
-})();
diff --git a/js/delivery_receipts.js b/js/delivery_receipts.js
index 144db2363f12..8b19e41c8885 100644
--- a/js/delivery_receipts.js
+++ b/js/delivery_receipts.js
@@ -30,11 +30,10 @@
this.remove(receipts);
return receipts;
},
- async getTargetMessage(source, messages) {
+ async getTargetMessage(sourceId, messages) {
if (messages.length === 0) {
return null;
}
- const sourceId = ConversationController.getConversationId(source);
const message = messages.find(
item => !item.isIncoming() && sourceId === item.get('conversationId')
);
diff --git a/js/message_requests.js b/js/message_requests.js
index d2c4cb97e75a..7816c83a1182 100644
--- a/js/message_requests.js
+++ b/js/message_requests.js
@@ -15,7 +15,7 @@
forConversation(conversation) {
if (conversation.get('e164')) {
const syncByE164 = this.findWhere({
- e164: conversation.get('e164'),
+ threadE164: conversation.get('e164'),
});
if (syncByE164) {
window.log.info(
@@ -30,7 +30,7 @@
if (conversation.get('uuid')) {
const syncByUuid = this.findWhere({
- uuid: conversation.get('uuid'),
+ threadUuid: conversation.get('uuid'),
});
if (syncByUuid) {
window.log.info(
@@ -45,7 +45,7 @@
if (conversation.get('groupId')) {
const syncByGroupId = this.findWhere({
- uuid: conversation.get('groupId'),
+ groupId: conversation.get('groupId'),
});
if (syncByGroupId) {
window.log.info(
@@ -65,12 +65,19 @@
const threadE164 = sync.get('threadE164');
const threadUuid = sync.get('threadUuid');
const groupId = sync.get('groupId');
- const identifier = threadE164 || threadUuid || groupId;
- const conversation = ConversationController.get(identifier);
+
+ const conversation = groupId
+ ? ConversationController.get(groupId)
+ : ConversationController.get(
+ ConversationController.ensureContactIds({
+ e164: threadE164,
+ uuid: threadUuid,
+ })
+ );
if (!conversation) {
window.log(
- `Received message request response for unknown conversation: ${identifier}`
+ `Received message request response for unknown conversation: ${groupId} ${threadUuid} ${threadE164}`
);
return;
}
diff --git a/js/models/conversations.js b/js/models/conversations.js
index aaafd7f0bc60..0c016d9c07ba 100644
--- a/js/models/conversations.js
+++ b/js/models/conversations.js
@@ -85,6 +85,12 @@
return `group(${groupId})`;
},
+ // This is one of the few times that we want to collapse our uuid/e164 pair down into
+ // just one bit of data. If we have a UUID, we'll send using it.
+ getSendTarget() {
+ return this.get('uuid') || this.get('e164');
+ },
+
handleMessageError(message, errors) {
this.trigger('messageError', message, errors);
},
@@ -318,9 +324,8 @@
}
const groupId = !this.isPrivate() ? this.get('groupId') : null;
- const maybeRecipientId = this.get('uuid') || this.get('e164');
- const recipientId = this.isPrivate() ? maybeRecipientId : null;
const groupNumbers = this.getRecipients();
+ const recipientId = this.isPrivate() ? this.getSendTarget() : null;
const sendOptions = this.getSendOptions();
@@ -395,10 +400,10 @@
async onNewMessage(message) {
// Clear typing indicator for a given contact if we receive a message from them
- const identifier = message.get
- ? `${message.get('source')}.${message.get('sourceDevice')}`
- : `${message.source}.${message.sourceDevice}`;
- this.clearContactTypingTimer(identifier);
+ const deviceId = message.get
+ ? `${message.get('conversationId')}.${message.get('sourceDevice')}`
+ : `${message.conversationId}.${message.sourceDevice}`;
+ this.clearContactTypingTimer(deviceId);
this.debouncedUpdateLastMessage();
},
@@ -582,7 +587,13 @@
m => !m.hasErrors() && m.isIncoming()
);
const receiptSpecs = readMessages.map(m => ({
- sender: m.get('source') || m.get('sourceUuid'),
+ senderE164: m.get('source'),
+ senderUuid: m.get('sourceUuid'),
+ senderId: ConversationController.get({
+ e164: m.get('source'),
+ uuid: m.get('sourceUuid'),
+ lowTrust: true,
+ }),
timestamp: m.get('sent_at'),
hasErrors: m.hasErrors(),
}));
@@ -1167,14 +1178,12 @@
getRecipients() {
if (this.isPrivate()) {
- return [this.get('uuid') || this.get('e164')];
+ return [this.getSendTarget()];
}
- const me = ConversationController.getConversationId(
- textsecure.storage.user.getUuid() || textsecure.storage.user.getNumber()
- );
+ const me = ConversationController.getOurConversationId();
return _.without(this.get('members'), me).map(memberId => {
const c = ConversationController.get(memberId);
- return c.get('uuid') || c.get('e164');
+ return c.getSendTarget();
});
},
@@ -1331,11 +1340,7 @@
const reactionModel = Whisper.Reactions.add({
...outgoingReaction,
- fromId:
- this.ourNumber ||
- this.ourUuid ||
- textsecure.storage.user.getNumber() ||
- textsecure.storage.user.getUuid(),
+ fromId: ConversationController.getOurConversationId(),
timestamp,
fromSync: true,
});
@@ -1446,9 +1451,7 @@
async sendProfileKeyUpdate() {
const id = this.get('id');
- const recipients = this.isPrivate()
- ? [this.get('uuid') || this.get('e164')]
- : this.getRecipients();
+ const recipients = this.getRecipients();
if (!this.get('profileSharing')) {
window.log.error(
'Attempted to send profileKeyUpdate to conversation without profileSharing enabled',
@@ -1477,7 +1480,7 @@
const { clearUnreadMetrics } = window.reduxActions.conversations;
clearUnreadMetrics(this.id);
- const destination = this.get('uuid') || this.get('e164');
+ const destination = this.getSendTarget();
const expireTimer = this.get('expireTimer');
const recipients = this.getRecipients();
@@ -1549,7 +1552,7 @@
).map(contact => {
const error = new Error('Network is not available');
error.name = 'SendMessageNetworkError';
- error.identifier = contact.get('uuid') || contact.get('e164');
+ error.identifier = contact.get('id');
return error;
});
await message.saveErrors(errors);
@@ -1658,8 +1661,8 @@
async handleMessageSendResult(failoverIdentifiers, unidentifiedDeliveries) {
await Promise.all(
- (failoverIdentifiers || []).map(async number => {
- const conversation = ConversationController.get(number);
+ (failoverIdentifiers || []).map(async identifier => {
+ const conversation = ConversationController.get(identifier);
if (
conversation &&
@@ -1677,8 +1680,8 @@
);
await Promise.all(
- (unidentifiedDeliveries || []).map(async number => {
- const conversation = ConversationController.get(number);
+ (unidentifiedDeliveries || []).map(async identifier => {
+ const conversation = ConversationController.get(identifier);
if (
conversation &&
@@ -1722,17 +1725,10 @@
getSendMetadata(options = {}) {
const { syncMessage, disableMeCheck } = options;
- if (!this.ourNumber && !this.ourUuid) {
- return null;
- }
-
// START: this code has an Expiration date of ~2018/11/21
// We don't want to enable unidentified delivery for send unless it is
// also enabled for our own account.
- const myId = ConversationController.ensureContactIds({
- e164: this.ourNumber,
- uuid: this.ourUuid,
- });
+ const myId = ConversationController.getOurConversationId();
const me = ConversationController.get(myId);
if (
!disableMeCheck &&
@@ -1903,10 +1899,7 @@
source,
});
- source =
- source ||
- textsecure.storage.user.getNumber() ||
- textsecure.storage.user.getUuid();
+ source = source || ConversationController.getOurConversationId();
// When we add a disappearing messages notification to the conversation, we want it
// to be above the message that initiated that change, hence the subtraction.
@@ -1933,7 +1926,7 @@
});
if (this.isPrivate()) {
- model.set({ destination: this.get('uuid') || this.get('e164') });
+ model.set({ destination: this.getSendTarget() });
}
if (model.isOutgoing()) {
model.set({ recipients: this.getRecipients() });
@@ -1963,7 +1956,7 @@
const flags =
textsecure.protobuf.DataMessage.Flags.EXPIRATION_TIMER_UPDATE;
const dataMessage = await textsecure.messaging.getMessageProto(
- this.get('uuid') || this.get('e164'),
+ this.getSendTarget(),
null,
[],
null,
@@ -1980,7 +1973,7 @@
if (this.get('type') === 'private') {
promise = textsecure.messaging.sendExpirationTimerUpdateToIdentifier(
- this.get('uuid') || this.get('e164'),
+ this.getSendTarget(),
expireTimer,
message.get('sent_at'),
profileKey,
@@ -2180,7 +2173,12 @@
await m.markRead(options.readAt);
return {
- sender: m.get('source') || m.get('sourceUuid'),
+ senderE164: m.get('source'),
+ senderUuid: m.get('sourceUuid'),
+ senderId: ConversationController.ensureContactIds({
+ e164: m.get('source'),
+ uuid: m.get('sourceUuid'),
+ }),
timestamp: m.get('sent_at'),
hasErrors: m.hasErrors(),
};
@@ -2188,7 +2186,7 @@
);
// Some messages we're marking read are local notifications with no sender
- read = _.filter(read, m => Boolean(m.sender));
+ read = _.filter(read, m => Boolean(m.senderId));
unreadMessages = unreadMessages.filter(m => Boolean(m.isIncoming()));
const unreadCount = unreadMessages.length - read.length;
@@ -2206,8 +2204,10 @@
window.log.info(`Sending ${read.length} read syncs`);
// Because syncReadMessages sends to our other devices, and sendReadReceipts goes
// to a contact, we need accessKeys for both.
- const { sendOptions } = ConversationController.prepareForSend(
- this.ourUuid || this.ourNumber,
+ const {
+ sendOptions,
+ } = ConversationController.prepareForSend(
+ ConversationController.getOurConversationId(),
{ syncMessage: true }
);
await this.wrapSend(
@@ -2222,12 +2222,12 @@
if (storage.get('read-receipt-setting') && this.getAccepted()) {
window.log.info(`Sending ${items.length} read receipts`);
const convoSendOptions = this.getSendOptions();
- const receiptsBySender = _.groupBy(items, 'sender');
+ const receiptsBySender = _.groupBy(items, 'senderId');
await Promise.all(
- _.map(receiptsBySender, async (receipts, identifier) => {
+ _.map(receiptsBySender, async (receipts, senderId) => {
const timestamps = _.map(receipts, 'timestamp');
- const c = ConversationController.get(identifier);
+ const c = ConversationController.get(senderId);
await this.wrapSend(
textsecure.messaging.sendReadReceipts(
c.get('e164'),
@@ -2249,28 +2249,33 @@
getProfiles() {
// request all conversation members' keys
- let ids = [];
+ let conversations = [];
if (this.isPrivate()) {
- ids = [this.get('uuid') || this.get('e164')];
+ conversations = [this];
} else {
- ids = this.get('members')
- .map(id => {
- const c = ConversationController.get(id);
- return c ? c.get('uuid') || c.get('e164') : null;
- })
+ conversations = this.get('members')
+ .map(id => ConversationController.get(id))
.filter(Boolean);
}
- return Promise.all(_.map(ids, this.getProfile));
+ return Promise.all(
+ _.map(conversations, conversation => {
+ this.getProfile(conversation.get('uuid'), conversation.get('e164'));
+ })
+ );
},
- async getProfile(id) {
+ async getProfile(providedUuid, providedE164) {
if (!textsecure.messaging) {
throw new Error(
'Conversation.getProfile: textsecure.messaging not available'
);
}
- const c = ConversationController.getOrCreate(id, 'private');
+ const id = ConversationController.ensureContactIds({
+ uuid: providedUuid,
+ e164: providedE164,
+ });
+ const c = ConversationController.get(id);
const {
generateProfileKeyCredentialRequest,
getClientZkProfileOperations,
@@ -2294,6 +2299,7 @@
const profileKey = c.get('profileKey');
const uuid = c.get('uuid');
+ const identifier = c.getSendTarget();
const profileKeyVersionHex = c.get('profileKeyVersion');
const existingProfileKeyCredential = c.get('profileKeyCredential');
@@ -2317,11 +2323,11 @@
const sendMetadata = c.getSendMetadata({ disableMeCheck: true }) || {};
const getInfo =
- sendMetadata[c.get('e164')] || sendMetadata[c.get('uuid')] || {};
+ sendMetadata[c.get('uuid')] || sendMetadata[c.get('e164')] || {};
if (getInfo.accessKey) {
try {
- profile = await textsecure.messaging.getProfile(id, {
+ profile = await textsecure.messaging.getProfile(identifier, {
accessKey: getInfo.accessKey,
profileKeyVersion: profileKeyVersionHex,
profileKeyCredentialRequest: profileKeyCredentialRequestHex,
@@ -2332,7 +2338,7 @@
`Setting sealedSender to DISABLED for conversation ${c.idForLogging()}`
);
c.set({ sealedSender: SEALED_SENDER.DISABLED });
- profile = await textsecure.messaging.getProfile(id, {
+ profile = await textsecure.messaging.getProfile(identifier, {
profileKeyVersion: profileKeyVersionHex,
profileKeyCredentialRequest: profileKeyCredentialRequestHex,
});
@@ -2341,7 +2347,7 @@
}
}
} else {
- profile = await textsecure.messaging.getProfile(id, {
+ profile = await textsecure.messaging.getProfile(identifier, {
profileKeyVersion: profileKeyVersionHex,
profileKeyCredentialRequest: profileKeyCredentialRequestHex,
});
@@ -2349,14 +2355,14 @@
const identityKey = base64ToArrayBuffer(profile.identityKey);
const changed = await textsecure.storage.protocol.saveIdentity(
- `${id}.1`,
+ `${identifier}.1`,
identityKey,
false
);
if (changed) {
// save identity will close all sessions except for .1, so we
// must close that one manually.
- const address = new libsignal.SignalProtocolAddress(id, 1);
+ const address = new libsignal.SignalProtocolAddress(identifier, 1);
window.log.info('closing session for', address.toString());
const sessionCipher = new libsignal.SessionCipher(
textsecure.storage.protocol,
@@ -2421,7 +2427,7 @@
if (error.code !== 403 && error.code !== 404) {
window.log.warn(
'getProfile failure:',
- id,
+ c.idForLogging(),
error && error.stack ? error.stack : error
);
} else {
@@ -2435,7 +2441,7 @@
} catch (error) {
window.log.warn(
'getProfile decryption failure:',
- id,
+ c.idForLogging(),
error && error.stack ? error.stack : error
);
await c.dropProfileKey();
@@ -2780,10 +2786,9 @@
const conversationId = this.id;
- const sender = await ConversationController.getOrCreateAndWait(
- reaction ? reaction.get('fromId') : message.get('source'),
- 'private'
- );
+ const sender = reaction
+ ? ConversationController.get(reaction.get('fromId'))
+ : message.getContact();
const iconUrl = await sender.getNotificationIcon();
@@ -2805,42 +2810,33 @@
},
notifyTyping(options = {}) {
- const {
- isTyping,
- sender,
- senderUuid,
- senderId,
- isMe,
- senderDevice,
- } = options;
+ const { isTyping, senderId, isMe, senderDevice } = options;
// We don't do anything with typing messages from our other devices
if (isMe) {
return;
}
- const identifier = `${sender}.${senderDevice}`;
+ const deviceId = `${senderId}.${senderDevice}`;
this.contactTypingTimers = this.contactTypingTimers || {};
- const record = this.contactTypingTimers[identifier];
+ const record = this.contactTypingTimers[deviceId];
if (record) {
clearTimeout(record.timer);
}
if (isTyping) {
- this.contactTypingTimers[identifier] = this.contactTypingTimers[
- identifier
+ this.contactTypingTimers[deviceId] = this.contactTypingTimers[
+ deviceId
] || {
timestamp: Date.now(),
- sender,
senderId,
- senderUuid,
senderDevice,
};
- this.contactTypingTimers[identifier].timer = setTimeout(
- this.clearContactTypingTimer.bind(this, identifier),
+ this.contactTypingTimers[deviceId].timer = setTimeout(
+ this.clearContactTypingTimer.bind(this, deviceId),
15 * 1000
);
if (!record) {
@@ -2848,7 +2844,7 @@
this.trigger('change', this);
}
} else {
- delete this.contactTypingTimers[identifier];
+ delete this.contactTypingTimers[deviceId];
if (record) {
// User was previously typing, and is no longer. State change!
this.trigger('change', this);
@@ -2856,13 +2852,13 @@
}
},
- clearContactTypingTimer(identifier) {
+ clearContactTypingTimer(deviceId) {
this.contactTypingTimers = this.contactTypingTimers || {};
- const record = this.contactTypingTimers[identifier];
+ const record = this.contactTypingTimers[deviceId];
if (record) {
clearTimeout(record.timer);
- delete this.contactTypingTimers[identifier];
+ delete this.contactTypingTimers[deviceId];
// User was previously typing, but timed out or we received message. State change!
this.trigger('change', this);
@@ -2879,9 +2875,9 @@
* than just their id.
*/
initialize() {
- this._byE164 = {};
- this._byUuid = {};
- this._byGroupId = {};
+ this._byE164 = Object.create(null);
+ this._byUuid = Object.create(null);
+ this._byGroupId = Object.create(null);
this.on('idUpdated', (model, idProp, oldValue) => {
if (oldValue) {
if (idProp === 'e164') {
@@ -2908,9 +2904,9 @@
reset(...args) {
Backbone.Collection.prototype.reset.apply(this, args);
- this._byE164 = {};
- this._byUuid = {};
- this._byGroupId = {};
+ this._byE164 = Object.create(null);
+ this._byUuid = Object.create(null);
+ this._byGroupId = Object.create(null);
},
add(...models) {
@@ -2918,12 +2914,22 @@
[].concat(res).forEach(model => {
const e164 = model.get('e164');
if (e164) {
- this._byE164[e164] = model;
+ const existing = this._byE164[e164];
+
+ // Prefer the contact with both e164 and uuid
+ if (!existing || (existing && !existing.get('uuid'))) {
+ this._byE164[e164] = model;
+ }
}
const uuid = model.get('uuid');
if (uuid) {
- this._byUuid[uuid] = model;
+ const existing = this._byUuid[uuid];
+
+ // Prefer the contact with both e164 and uuid
+ if (!existing || (existing && !existing.get('e164'))) {
+ this._byUuid[uuid] = model;
+ }
}
const groupId = model.get('groupId');
diff --git a/js/models/messages.js b/js/models/messages.js
index 9fe04af502e8..abc776f92c0a 100644
--- a/js/models/messages.js
+++ b/js/models/messages.js
@@ -193,30 +193,20 @@
// Other top-level prop-generation
getPropsForSearchResult() {
- const sourceE164 = this.getSource();
- const sourceUuid = this.getSourceUuid();
- const fromContact = this.findAndFormatContact(sourceE164 || sourceUuid);
+ const ourId = ConversationController.getOurConversationId();
+ const sourceId = this.getContactId();
+ const fromContact = this.findAndFormatContact(sourceId);
- if (
- (sourceE164 && sourceE164 === this.OUR_NUMBER) ||
- (sourceUuid && sourceUuid === this.OUR_UUID)
- ) {
+ if (ourId === sourceId) {
fromContact.isMe = true;
}
const convo = this.getConversation();
- let to = convo ? this.findAndFormatContact(convo.get('id')) : {};
+ const to = convo ? this.findAndFormatContact(convo.get('id')) : {};
- if (convo && convo.isMe()) {
+ if (to && convo && convo.isMe()) {
to.isMe = true;
- } else if (
- (sourceE164 && convo && sourceE164 === convo.get('e164')) ||
- (sourceUuid && convo && sourceUuid === convo.get('uuid'))
- ) {
- to = {
- isMe: true,
- };
}
return {
@@ -237,10 +227,10 @@
const unidentifiedLookup = (
this.get('unidentifiedDeliveries') || []
- ).reduce((accumulator, uuidOrE164) => {
+ ).reduce((accumulator, identifier) => {
// eslint-disable-next-line no-param-reassign
accumulator[
- ConversationController.getConversationId(uuidOrE164)
+ ConversationController.getConversationId(identifier)
] = true;
return accumulator;
}, Object.create(null));
@@ -249,7 +239,7 @@
// Older messages don't have the recipients included on the message, so we fall
// back to the conversation's current recipients
const conversationIds = this.isIncoming()
- ? [this.getContact().get('id')]
+ ? [this.getContactId()]
: _.union(
(this.get('sent_to') || []).map(id =>
ConversationController.getConversationId(id)
@@ -379,11 +369,11 @@
getPropsForUnsupportedMessage() {
const requiredVersion = this.get('requiredProtocolVersion');
const canProcessNow = this.CURRENT_PROTOCOL_VERSION >= requiredVersion;
- const phoneNumber = this.getSource();
+ const sourceId = this.getContactId();
return {
canProcessNow,
- contact: this.findAndFormatContact(phoneNumber),
+ contact: this.findAndFormatContact(sourceId),
};
},
getPropsForTimerNotification() {
@@ -396,8 +386,14 @@
const timespan = Whisper.ExpirationTimerOptions.getName(expireTimer || 0);
const disabled = !expireTimer;
+ const sourceId = ConversationController.ensureContactIds({
+ e164: source,
+ uuid: sourceUuid,
+ });
+ const ourId = ConversationController.getOurConversationId();
+
const basicProps = {
- ...this.findAndFormatContact(source),
+ ...this.findAndFormatContact(sourceId),
type: 'fromOther',
timespan,
disabled,
@@ -408,7 +404,7 @@
...basicProps,
type: 'fromSync',
};
- } else if (source === this.OUR_NUMBER || sourceUuid === this.OUR_UUID) {
+ } else if (sourceId && sourceId === ourId) {
return {
...basicProps,
type: 'fromMe',
@@ -430,12 +426,12 @@
getPropsForVerificationNotification() {
const type = this.get('verified') ? 'markVerified' : 'markNotVerified';
const isLocal = this.get('local');
- const phoneNumber = this.get('verifiedChanged');
+ const identifier = this.get('verifiedChanged');
return {
type,
isLocal,
- contact: this.findAndFormatContact(phoneNumber),
+ contact: this.findAndFormatContact(identifier),
};
},
getPropsForGroupNotification() {
@@ -460,7 +456,7 @@
Array.isArray(groupUpdate.joined)
? groupUpdate.joined
: [groupUpdate.joined],
- phoneNumber => this.findAndFormatContact(phoneNumber)
+ identifier => this.findAndFormatContact(identifier)
),
});
}
@@ -477,7 +473,7 @@
Array.isArray(groupUpdate.left)
? groupUpdate.left
: [groupUpdate.left],
- phoneNumber => this.findAndFormatContact(phoneNumber)
+ identifier => this.findAndFormatContact(identifier)
),
});
}
@@ -495,9 +491,8 @@
});
}
- const sourceE164 = this.getSource();
- const sourceUuid = this.getSourceUuid();
- const from = this.findAndFormatContact(sourceE164 || sourceUuid);
+ const sourceId = this.getContactId();
+ const from = this.findAndFormatContact(sourceId);
return {
from,
@@ -537,10 +532,9 @@
.map(attachment => this.getPropsForAttachment(attachment));
},
getPropsForMessage() {
- const sourceE164 = this.getSource();
- const sourceUuid = this.getSourceUuid();
- const contact = this.findAndFormatContact(sourceE164 || sourceUuid);
- const contactModel = this.findContact(sourceE164 || sourceUuid);
+ const sourceId = this.getContactId();
+ const contact = this.findAndFormatContact(sourceId);
+ const contactModel = this.findContact(sourceId);
const authorColor = contactModel ? contactModel.getColor() : null;
const authorAvatarPath = contactModel
@@ -774,7 +768,13 @@
referencedMessageNotFound,
} = quote;
const contact =
- author && ConversationController.get(author || authorUuid);
+ (author || authorUuid) &&
+ ConversationController.get(
+ ConversationController.ensureContactIds({
+ e164: author,
+ uuid: authorUuid,
+ })
+ );
const authorColor = contact ? contact.getColor() : 'grey';
const authorPhoneNumber = format(author, {
@@ -810,17 +810,18 @@
const e164 = conversation.get('e164');
const uuid = conversation.get('uuid');
+ const conversationId = conversation.get('id');
const readBy = this.get('read_by') || [];
- if (includesAny(readBy, identifier, e164, uuid)) {
+ if (includesAny(readBy, conversationId, e164, uuid)) {
return 'read';
}
const deliveredTo = this.get('delivered_to') || [];
- if (includesAny(deliveredTo, identifier, e164, uuid)) {
+ if (includesAny(deliveredTo, conversationId, e164, uuid)) {
return 'delivered';
}
const sentTo = this.get('sent_to') || [];
- if (includesAny(sentTo, identifier, e164, uuid)) {
+ if (includesAny(sentTo, conversationId, e164, uuid)) {
return 'sent';
}
@@ -1220,19 +1221,22 @@
return this.OUR_UUID;
},
- getContact() {
+ getContactId() {
const source = this.getSource();
const sourceUuid = this.getSourceUuid();
if (!source && !sourceUuid) {
- return null;
+ return ConversationController.getOurConversationId();
}
- const contactId = ConversationController.ensureContactIds({
+ return ConversationController.ensureContactIds({
e164: source,
uuid: sourceUuid,
});
- return ConversationController.get(contactId, 'private');
+ },
+ getContact() {
+ const id = this.getContactId();
+ return ConversationController.get(id);
},
isOutgoing() {
return this.get('type') === 'outgoing';
@@ -1395,7 +1399,7 @@
let recipients = _.intersection(intendedRecipients, currentRecipients);
recipients = _.without(recipients, successfulRecipients).map(id => {
const c = ConversationController.get(id);
- return c.get('uuid') || c.get('e164');
+ return c.getSendTarget();
});
if (!recipients.length) {
@@ -1699,7 +1703,7 @@
try {
this.set({
// These are the same as a normal send()
- sent_to: [conv.get('uuid') || conv.get('e164')],
+ sent_to: [conv.getSendTarget()],
sent: true,
expirationStartTimestamp: Date.now(),
});
@@ -1709,8 +1713,8 @@
unidentifiedDeliveries: result ? result.unidentifiedDeliveries : null,
// These are unique to a Note to Self message - immediately read/delivered
- delivered_to: [this.OUR_UUID || this.OUR_NUMBER],
- read_by: [this.OUR_UUID || this.OUR_NUMBER],
+ delivered_to: [ConversationController.getOurConversationId()],
+ read_by: [ConversationController.getOurConversationId()],
});
} catch (result) {
const errors = (result && result.errors) || [
@@ -2004,20 +2008,20 @@
return message;
}
- const { attachments, id, author } = quote;
+ const { attachments, id, author, authorUuid } = quote;
const firstAttachment = attachments[0];
+ const authorConversationId = ConversationController.ensureContactIds({
+ e164: author,
+ uuid: authorUuid,
+ });
const collection = await window.Signal.Data.getMessagesBySentAt(id, {
MessageCollection: Whisper.MessageCollection,
});
const found = collection.find(item => {
- const messageAuthor = item.getContact();
+ const messageAuthorId = item.getContactId();
- return (
- messageAuthor &&
- ConversationController.getConversationId(author) ===
- messageAuthor.get('id')
- );
+ return authorConversationId === messageAuthorId;
});
if (!found) {
@@ -2119,10 +2123,7 @@
const source = message.get('source');
const sourceUuid = message.get('sourceUuid');
const type = message.get('type');
- let conversationId = message.get('conversationId');
- if (initialMessage.group) {
- conversationId = initialMessage.group.id;
- }
+ const conversationId = message.get('conversationId');
const GROUP_TYPES = textsecure.protobuf.GroupContext.Type;
const conversation = ConversationController.get(conversationId);
@@ -2392,10 +2393,13 @@
if (conversation.get('left')) {
window.log.warn('re-added to a left group');
attributes.left = false;
- conversation.set({ addedBy: message.getContact().get('id') });
+ conversation.set({ addedBy: message.getContactId() });
}
} else if (dataMessage.group.type === GROUP_TYPES.QUIT) {
- const sender = ConversationController.get(source || sourceUuid);
+ const sender = ConversationController.ensureContactIds({
+ e164: source,
+ uuid: sourceUuid,
+ });
const inGroup = Boolean(
sender &&
(conversation.get('members') || []).includes(sender.id)
@@ -2453,6 +2457,7 @@
message.set({
expirationTimerUpdate: {
source,
+ sourceUuid,
expireTimer: dataMessage.expireTimer,
},
});
@@ -2567,9 +2572,7 @@
e164: source,
uuid: sourceUuid,
});
- ConversationController.get(localId, 'private').setProfileKey(
- profileKey
- );
+ ConversationController.get(localId).setProfileKey(profileKey);
}
}
diff --git a/js/modules/refresh_sender_certificate.js b/js/modules/refresh_sender_certificate.js
index 8f1433278bb0..316af56eb9e6 100644
--- a/js/modules/refresh_sender_certificate.js
+++ b/js/modules/refresh_sender_certificate.js
@@ -15,15 +15,8 @@ let scheduleNext = null;
// do not support unidentified delivery.
function refreshOurProfile() {
window.log.info('refreshOurProfile');
- const ourNumber = textsecure.storage.user.getNumber();
- const ourUuid = textsecure.storage.user.getUuid();
- const ourId = ConversationController.ensureContactIds({
- e164: ourNumber,
- uuid: ourUuid,
- });
- const conversation = ConversationController.get(ourId, 'private');
- conversation.updateUuid(ourUuid);
- conversation.updateE164(ourNumber);
+ const ourId = ConversationController.getOurConversationId();
+ const conversation = ConversationController.get(ourId);
conversation.getProfiles();
}
diff --git a/js/modules/signal.js b/js/modules/signal.js
index 94aac80df597..fcb804f269c0 100644
--- a/js/modules/signal.js
+++ b/js/modules/signal.js
@@ -3,6 +3,9 @@
const { bindActionCreators } = require('redux');
const Backbone = require('../../ts/backbone');
const Crypto = require('../../ts/Crypto');
+const {
+ start: conversationControllerStart,
+} = require('../../ts/ConversationController');
const Data = require('../../ts/sql/Client').default;
const Emojis = require('./emojis');
const EmojiLib = require('../../ts/components/emoji/lib');
@@ -357,6 +360,7 @@ exports.setup = (options = {}) => {
Backbone,
Components,
Crypto,
+ conversationControllerStart,
Data,
Emojis,
EmojiLib,
diff --git a/js/reactions.js b/js/reactions.js
index 254a744bbde5..36ebc282e3ab 100644
--- a/js/reactions.js
+++ b/js/reactions.js
@@ -28,9 +28,10 @@
const reactionsBySource = this.filter(re => {
const mcid = message.get('conversationId');
- const recid = ConversationController.getConversationId(
- re.get('targetAuthorE164') || re.get('targetAuthorUuid')
- );
+ const recid = ConversationController.ensureContactIds({
+ e164: re.get('targetAuthorE164'),
+ uuid: re.get('targetAuthorUuid'),
+ });
const mTime = message.get('sent_at');
const rTime = re.get('targetTimestamp');
return mcid === recid && mTime === rTime;
@@ -47,9 +48,10 @@
async onReaction(reaction) {
try {
const targetConversation = await ConversationController.getConversationForTargetMessage(
- // Do not use ensureContactIds here since maliciously malformed
- // reactions from clients could cause issues
- reaction.get('targetAuthorE164') || reaction.get('targetAuthorUuid'),
+ ConversationController.ensureContactIds({
+ e164: reaction.get('targetAuthorE164'),
+ uuid: reaction.get('targetAuthorUuid'),
+ }),
reaction.get('targetTimestamp')
);
if (!targetConversation) {
@@ -85,10 +87,10 @@
}
const mcid = contact.get('id');
- const recid = ConversationController.getConversationId(
- reaction.get('targetAuthorE164') ||
- reaction.get('targetAuthorUuid')
- );
+ const recid = ConversationController.ensureContactIds({
+ e164: reaction.get('targetAuthorE164'),
+ uuid: reaction.get('targetAuthorUuid'),
+ });
return mcid === recid;
});
diff --git a/js/views/conversation_view.js b/js/views/conversation_view.js
index 7436cadb3c0b..288965a64816 100644
--- a/js/views/conversation_view.js
+++ b/js/views/conversation_view.js
@@ -2058,7 +2058,7 @@
await contact.setApproved();
}
- message.resend(contact.get('uuid') || contact.get('e164'));
+ message.resend(contact.getSendTarget());
},
});
diff --git a/js/views/inbox_view.js b/js/views/inbox_view.js
index b66c554b186d..f28a4a50fb19 100644
--- a/js/views/inbox_view.js
+++ b/js/views/inbox_view.js
@@ -84,6 +84,13 @@
model: { window: options.window },
});
+ Whisper.events.on('refreshConversation', ({ oldId, newId }) => {
+ const convo = this.conversation_stack.lastConversation;
+ if (convo && convo.get('id') === oldId) {
+ this.conversation_stack.open(newId);
+ }
+ });
+
if (!options.initialLoadComplete) {
this.appLoadingScreen = new Whisper.AppLoadingScreen();
this.appLoadingScreen.render();
diff --git a/libtextsecure/test/index.html b/libtextsecure/test/index.html
index 137d001a95b1..0836f1df6f8b 100644
--- a/libtextsecure/test/index.html
+++ b/libtextsecure/test/index.html
@@ -30,7 +30,6 @@
-
@@ -51,6 +50,7 @@