You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
205 lines
7.2 KiB
TypeScript
205 lines
7.2 KiB
TypeScript
import _ from 'lodash';
|
|
import { getMessageById } from '../../data/data';
|
|
import { SignalService } from '../../protobuf';
|
|
import { MessageController } from '../messages';
|
|
import { OpenGroupMessage } from '../messages/outgoing';
|
|
import { OpenGroupVisibleMessage } from '../messages/outgoing/visibleMessage/OpenGroupVisibleMessage';
|
|
import { EncryptionType, RawMessage } from '../types';
|
|
import { UserUtils } from '../utils';
|
|
|
|
// tslint:disable-next-line no-unnecessary-class
|
|
export class MessageSentHandler {
|
|
public static async handlePublicMessageSentSuccess(
|
|
sentMessage: OpenGroupMessage | OpenGroupVisibleMessage,
|
|
result: { serverId: number; serverTimestamp: number }
|
|
) {
|
|
const { serverId, serverTimestamp } = result;
|
|
try {
|
|
const foundMessage = await MessageSentHandler.fetchHandleMessageSentData(sentMessage);
|
|
|
|
if (!foundMessage) {
|
|
throw new Error(
|
|
'handlePublicMessageSentSuccess(): The message should be in memory for an openGroup message'
|
|
);
|
|
}
|
|
|
|
foundMessage.set({
|
|
serverTimestamp,
|
|
serverId,
|
|
isPublic: true,
|
|
sent: true,
|
|
sent_at: serverTimestamp, // we quote by sent_at, so we MUST sent_at: serverTimestamp
|
|
sync: true,
|
|
synced: true,
|
|
sentSync: true,
|
|
});
|
|
await foundMessage.commit();
|
|
foundMessage.getConversation()?.updateLastMessage();
|
|
} catch (e) {
|
|
window.log.error('Error setting public on message');
|
|
}
|
|
}
|
|
|
|
public static async handleMessageSentSuccess(
|
|
sentMessage: RawMessage,
|
|
wrappedEnvelope?: Uint8Array
|
|
) {
|
|
// The wrappedEnvelope will be set only if the message is not one of OpenGroupMessage type.
|
|
const fetchedMessage = await MessageSentHandler.fetchHandleMessageSentData(sentMessage);
|
|
if (!fetchedMessage) {
|
|
return;
|
|
}
|
|
|
|
let sentTo = fetchedMessage.get('sent_to') || [];
|
|
|
|
let isOurDevice = false;
|
|
if (sentMessage.device) {
|
|
isOurDevice = UserUtils.isUsFromCache(sentMessage.device);
|
|
}
|
|
// FIXME this is not correct and will cause issues with syncing
|
|
// At this point the only way to check for medium
|
|
// group is by comparing the encryption type
|
|
const isClosedGroupMessage = sentMessage.encryption === EncryptionType.ClosedGroup;
|
|
|
|
// We trigger a sync message only when the message is not to one of our devices, AND
|
|
// the message is not for an open group (there is no sync for opengroups, each device pulls all messages), AND
|
|
// if we did not sync or trigger a sync message for this specific message already
|
|
const shouldTriggerSyncMessage =
|
|
!isOurDevice &&
|
|
!isClosedGroupMessage &&
|
|
!fetchedMessage.get('synced') &&
|
|
!fetchedMessage.get('sentSync');
|
|
|
|
// A message is synced if we triggered a sync message (sentSync)
|
|
// and the current message was sent to our device (so a sync message)
|
|
const shouldMarkMessageAsSynced = isOurDevice && fetchedMessage.get('sentSync');
|
|
|
|
const contentDecoded = SignalService.Content.decode(sentMessage.plainTextBuffer);
|
|
const { dataMessage } = contentDecoded;
|
|
|
|
/**
|
|
* We should hit the notify endpoint for push notification only if:
|
|
* • It's a one-to-one chat or a closed group
|
|
* • The message has either text or attachments
|
|
*/
|
|
const hasBodyOrAttachments = Boolean(
|
|
dataMessage &&
|
|
(dataMessage.body || (dataMessage.attachments && dataMessage.attachments.length))
|
|
);
|
|
const shouldNotifyPushServer = hasBodyOrAttachments && !isOurDevice;
|
|
|
|
if (shouldNotifyPushServer) {
|
|
// notify the push notification server if needed
|
|
if (!wrappedEnvelope) {
|
|
window.log.warn('Should send PN notify but no wrapped envelope set.');
|
|
} else {
|
|
if (!window.LokiPushNotificationServer) {
|
|
window.LokiPushNotificationServer = new window.LokiPushNotificationServerApi();
|
|
}
|
|
|
|
window.LokiPushNotificationServer.notify(wrappedEnvelope, sentMessage.device);
|
|
}
|
|
}
|
|
|
|
// Handle the sync logic here
|
|
if (shouldTriggerSyncMessage) {
|
|
if (dataMessage) {
|
|
try {
|
|
await fetchedMessage.sendSyncMessage(
|
|
dataMessage as SignalService.DataMessage,
|
|
sentMessage.timestamp
|
|
);
|
|
} catch (e) {
|
|
window.log.warn('Got an error while trying to sendSyncMessage():', e);
|
|
}
|
|
}
|
|
} else if (shouldMarkMessageAsSynced) {
|
|
fetchedMessage.set({ synced: true });
|
|
}
|
|
|
|
sentTo = _.union(sentTo, [sentMessage.device]);
|
|
|
|
fetchedMessage.set({
|
|
sent_to: sentTo,
|
|
sent: true,
|
|
expirationStartTimestamp: Date.now(),
|
|
sent_at: sentMessage.timestamp,
|
|
});
|
|
|
|
await fetchedMessage.commit();
|
|
fetchedMessage.getConversation()?.updateLastMessage();
|
|
}
|
|
|
|
public static async handleMessageSentFailure(
|
|
sentMessage: RawMessage | OpenGroupMessage | OpenGroupVisibleMessage,
|
|
error: any
|
|
) {
|
|
const fetchedMessage = await MessageSentHandler.fetchHandleMessageSentData(sentMessage);
|
|
if (!fetchedMessage) {
|
|
return;
|
|
}
|
|
|
|
if (error instanceof Error) {
|
|
await fetchedMessage.saveErrors(error);
|
|
}
|
|
|
|
if (
|
|
!(sentMessage instanceof OpenGroupMessage) &&
|
|
!(sentMessage instanceof OpenGroupVisibleMessage)
|
|
) {
|
|
const isOurDevice = UserUtils.isUsFromCache(sentMessage.device);
|
|
// if this message was for ourself, and it was not already synced,
|
|
// it means that we failed to sync it.
|
|
// so just remove the flag saying that we are currently sending the sync message
|
|
if (isOurDevice && !fetchedMessage.get('sync')) {
|
|
fetchedMessage.set({ sentSync: false });
|
|
}
|
|
|
|
fetchedMessage.set({
|
|
expirationStartTimestamp: Date.now(),
|
|
});
|
|
}
|
|
|
|
// always mark the message as sent.
|
|
// the fact that we have errors on the sent is based on the saveErrors()
|
|
fetchedMessage.set({
|
|
sent: true,
|
|
});
|
|
|
|
await fetchedMessage.commit();
|
|
await fetchedMessage.getConversation()?.updateLastMessage();
|
|
}
|
|
|
|
/**
|
|
* This function tries to find a message by messageId by first looking on the MessageController.
|
|
* The MessageController holds all messages being in memory.
|
|
* Those are the messages sent recently, recieved recently, or the one shown to the user.
|
|
*
|
|
* If the app restarted, it's very likely those messages won't be on the memory anymore.
|
|
* In this case, this function will look for it in the database and return it.
|
|
* If the message is found on the db, it will also register it to the MessageController so our subsequent calls are quicker.
|
|
*/
|
|
private static async fetchHandleMessageSentData(
|
|
m: RawMessage | OpenGroupMessage | OpenGroupVisibleMessage
|
|
) {
|
|
// if a message was sent and this message was sent after the last app restart,
|
|
// this message is still in memory in the MessageController
|
|
const msg = MessageController.getInstance().get(m.identifier);
|
|
|
|
if (!msg || !msg.message) {
|
|
// otherwise, look for it in the database
|
|
// nobody is listening to this freshly fetched message .trigger calls
|
|
// so we can just update the fields on the database
|
|
const dbMessage = await getMessageById(m.identifier);
|
|
|
|
if (!dbMessage) {
|
|
return null;
|
|
}
|
|
MessageController.getInstance().register(m.identifier, dbMessage);
|
|
return dbMessage;
|
|
}
|
|
|
|
return msg.message;
|
|
}
|
|
}
|