Replace MessageController with MessageCache
This commit is contained in:
parent
ba1a8aad09
commit
7d35216fda
73 changed files with 2237 additions and 1229 deletions
452
ts/util/getNotificationDataForMessage.ts
Normal file
452
ts/util/getNotificationDataForMessage.ts
Normal file
|
@ -0,0 +1,452 @@
|
|||
// Copyright 2022 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import type { RawBodyRange } from '../types/BodyRange';
|
||||
import type { MessageAttributesType } from '../model-types.d';
|
||||
import type { ReplacementValuesType } from '../types/I18N';
|
||||
import * as Attachment from '../types/Attachment';
|
||||
import * as EmbeddedContact from '../types/EmbeddedContact';
|
||||
import * as GroupChange from '../groupChange';
|
||||
import * as MIME from '../types/MIME';
|
||||
import * as Stickers from '../types/Stickers';
|
||||
import * as expirationTimer from './expirationTimer';
|
||||
import * as log from '../logging/log';
|
||||
import { GiftBadgeStates } from '../components/conversation/Message';
|
||||
import { dropNull } from './dropNull';
|
||||
import { getCallHistorySelector } from '../state/selectors/callHistory';
|
||||
import { getCallSelector, getActiveCall } from '../state/selectors/calling';
|
||||
import { getCallingNotificationText } from './callingNotification';
|
||||
import { getConversationSelector } from '../state/selectors/conversations';
|
||||
import { getStringForConversationMerge } from './getStringForConversationMerge';
|
||||
import { getStringForProfileChange } from './getStringForProfileChange';
|
||||
import { getTitleNoDefault, getNumber } from './getTitle';
|
||||
import { findAndFormatContact } from './findAndFormatContact';
|
||||
import { isMe } from './whatTypeOfConversation';
|
||||
import { strictAssert } from './assert';
|
||||
import {
|
||||
getPropsForCallHistory,
|
||||
hasErrors,
|
||||
isCallHistory,
|
||||
isChatSessionRefreshed,
|
||||
isDeliveryIssue,
|
||||
isEndSession,
|
||||
isExpirationTimerUpdate,
|
||||
isGroupUpdate,
|
||||
isGroupV1Migration,
|
||||
isGroupV2Change,
|
||||
isIncoming,
|
||||
isKeyChange,
|
||||
isOutgoing,
|
||||
isProfileChange,
|
||||
isTapToView,
|
||||
isUnsupportedMessage,
|
||||
isConversationMerge,
|
||||
} from '../state/selectors/message';
|
||||
import {
|
||||
getContact,
|
||||
messageHasPaymentEvent,
|
||||
getPaymentEventNotificationText,
|
||||
} from '../messages/helpers';
|
||||
|
||||
function getNameForNumber(e164: string): string {
|
||||
const conversation = window.ConversationController.get(e164);
|
||||
if (!conversation) {
|
||||
return e164;
|
||||
}
|
||||
return conversation.getTitle();
|
||||
}
|
||||
|
||||
export function getNotificationDataForMessage(
|
||||
attributes: MessageAttributesType
|
||||
): {
|
||||
bodyRanges?: ReadonlyArray<RawBodyRange>;
|
||||
emoji?: string;
|
||||
text: string;
|
||||
} {
|
||||
if (isDeliveryIssue(attributes)) {
|
||||
return {
|
||||
emoji: '⚠️',
|
||||
text: window.i18n('icu:DeliveryIssue--preview'),
|
||||
};
|
||||
}
|
||||
|
||||
if (isConversationMerge(attributes)) {
|
||||
const conversation = window.ConversationController.get(
|
||||
attributes.conversationId
|
||||
);
|
||||
strictAssert(
|
||||
conversation,
|
||||
'getNotificationData/isConversationMerge/conversation'
|
||||
);
|
||||
strictAssert(
|
||||
attributes.conversationMerge,
|
||||
'getNotificationData/isConversationMerge/conversationMerge'
|
||||
);
|
||||
|
||||
return {
|
||||
text: getStringForConversationMerge({
|
||||
obsoleteConversationTitle: getTitleNoDefault(
|
||||
attributes.conversationMerge.renderInfo
|
||||
),
|
||||
obsoleteConversationNumber: getNumber(
|
||||
attributes.conversationMerge.renderInfo
|
||||
),
|
||||
conversationTitle: conversation.getTitle(),
|
||||
i18n: window.i18n,
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
if (isChatSessionRefreshed(attributes)) {
|
||||
return {
|
||||
emoji: '🔁',
|
||||
text: window.i18n('icu:ChatRefresh--notification'),
|
||||
};
|
||||
}
|
||||
|
||||
if (isUnsupportedMessage(attributes)) {
|
||||
return {
|
||||
text: window.i18n('icu:message--getDescription--unsupported-message'),
|
||||
};
|
||||
}
|
||||
|
||||
if (isGroupV1Migration(attributes)) {
|
||||
return {
|
||||
text: window.i18n('icu:GroupV1--Migration--was-upgraded'),
|
||||
};
|
||||
}
|
||||
|
||||
if (isProfileChange(attributes)) {
|
||||
const { profileChange: change, changedId } = attributes;
|
||||
const changedContact = findAndFormatContact(changedId);
|
||||
if (!change) {
|
||||
throw new Error('getNotificationData: profileChange was missing!');
|
||||
}
|
||||
|
||||
return {
|
||||
text: getStringForProfileChange(change, changedContact, window.i18n),
|
||||
};
|
||||
}
|
||||
|
||||
if (isGroupV2Change(attributes)) {
|
||||
const { groupV2Change: change } = attributes;
|
||||
strictAssert(
|
||||
change,
|
||||
'getNotificationData: isGroupV2Change true, but no groupV2Change!'
|
||||
);
|
||||
|
||||
const changes = GroupChange.renderChange<string>(change, {
|
||||
i18n: window.i18n,
|
||||
ourAci: window.textsecure.storage.user.getCheckedAci(),
|
||||
ourPni: window.textsecure.storage.user.getCheckedPni(),
|
||||
renderContact: (conversationId: string) => {
|
||||
const conversation = window.ConversationController.get(conversationId);
|
||||
return conversation
|
||||
? conversation.getTitle()
|
||||
: window.i18n('icu:unknownContact');
|
||||
},
|
||||
renderString: (
|
||||
key: string,
|
||||
_i18n: unknown,
|
||||
components: ReplacementValuesType<string | number> | undefined
|
||||
) => {
|
||||
// eslint-disable-next-line local-rules/valid-i18n-keys
|
||||
return window.i18n(key, components);
|
||||
},
|
||||
});
|
||||
|
||||
return { text: changes.map(({ text }) => text).join(' ') };
|
||||
}
|
||||
|
||||
if (messageHasPaymentEvent(attributes)) {
|
||||
const sender = findAndFormatContact(attributes.sourceServiceId);
|
||||
const conversation = findAndFormatContact(attributes.conversationId);
|
||||
return {
|
||||
text: getPaymentEventNotificationText(
|
||||
attributes.payment,
|
||||
sender.title,
|
||||
conversation.title,
|
||||
sender.isMe,
|
||||
window.i18n
|
||||
),
|
||||
emoji: '💳',
|
||||
};
|
||||
}
|
||||
|
||||
const { attachments = [] } = attributes;
|
||||
|
||||
if (isTapToView(attributes)) {
|
||||
if (attributes.isErased) {
|
||||
return {
|
||||
text: window.i18n('icu:message--getDescription--disappearing-media'),
|
||||
};
|
||||
}
|
||||
|
||||
if (Attachment.isImage(attachments)) {
|
||||
return {
|
||||
text: window.i18n('icu:message--getDescription--disappearing-photo'),
|
||||
emoji: '📷',
|
||||
};
|
||||
}
|
||||
if (Attachment.isVideo(attachments)) {
|
||||
return {
|
||||
text: window.i18n('icu:message--getDescription--disappearing-video'),
|
||||
emoji: '🎥',
|
||||
};
|
||||
}
|
||||
// There should be an image or video attachment, but we have a fallback just in
|
||||
// case.
|
||||
return { text: window.i18n('icu:mediaMessage'), emoji: '📎' };
|
||||
}
|
||||
|
||||
if (isGroupUpdate(attributes)) {
|
||||
const { group_update: groupUpdate } = attributes;
|
||||
const fromContact = getContact(attributes);
|
||||
const messages = [];
|
||||
if (!groupUpdate) {
|
||||
throw new Error('getNotificationData: Missing group_update');
|
||||
}
|
||||
|
||||
if (groupUpdate.left === 'You') {
|
||||
return { text: window.i18n('icu:youLeftTheGroup') };
|
||||
}
|
||||
if (groupUpdate.left) {
|
||||
return {
|
||||
text: window.i18n('icu:leftTheGroup', {
|
||||
name: getNameForNumber(groupUpdate.left),
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
if (!fromContact) {
|
||||
return { text: '' };
|
||||
}
|
||||
|
||||
if (isMe(fromContact.attributes)) {
|
||||
messages.push(window.i18n('icu:youUpdatedTheGroup'));
|
||||
} else {
|
||||
messages.push(
|
||||
window.i18n('icu:updatedTheGroup', {
|
||||
name: fromContact.getTitle(),
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
if (groupUpdate.joined && groupUpdate.joined.length) {
|
||||
const joinedContacts = groupUpdate.joined.map(item =>
|
||||
window.ConversationController.getOrCreate(item, 'private')
|
||||
);
|
||||
const joinedWithoutMe = joinedContacts.filter(
|
||||
contact => !isMe(contact.attributes)
|
||||
);
|
||||
|
||||
if (joinedContacts.length > 1) {
|
||||
messages.push(
|
||||
window.i18n('icu:multipleJoinedTheGroup', {
|
||||
names: joinedWithoutMe
|
||||
.map(contact => contact.getTitle())
|
||||
.join(', '),
|
||||
})
|
||||
);
|
||||
|
||||
if (joinedWithoutMe.length < joinedContacts.length) {
|
||||
messages.push(window.i18n('icu:youJoinedTheGroup'));
|
||||
}
|
||||
} else {
|
||||
const joinedContact = window.ConversationController.getOrCreate(
|
||||
groupUpdate.joined[0],
|
||||
'private'
|
||||
);
|
||||
if (isMe(joinedContact.attributes)) {
|
||||
messages.push(window.i18n('icu:youJoinedTheGroup'));
|
||||
} else {
|
||||
messages.push(
|
||||
window.i18n('icu:joinedTheGroup', {
|
||||
name: joinedContacts[0].getTitle(),
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (groupUpdate.name) {
|
||||
messages.push(
|
||||
window.i18n('icu:titleIsNow', {
|
||||
name: groupUpdate.name,
|
||||
})
|
||||
);
|
||||
}
|
||||
if (groupUpdate.avatarUpdated) {
|
||||
messages.push(window.i18n('icu:updatedGroupAvatar'));
|
||||
}
|
||||
|
||||
return { text: messages.join(' ') };
|
||||
}
|
||||
if (isEndSession(attributes)) {
|
||||
return { text: window.i18n('icu:sessionEnded') };
|
||||
}
|
||||
if (isIncoming(attributes) && hasErrors(attributes)) {
|
||||
return { text: window.i18n('icu:incomingError') };
|
||||
}
|
||||
|
||||
const { body: untrimmedBody = '', bodyRanges = [] } = attributes;
|
||||
const body = untrimmedBody.trim();
|
||||
|
||||
if (attachments.length) {
|
||||
// This should never happen but we want to be extra-careful.
|
||||
const attachment = attachments[0] || {};
|
||||
const { contentType } = attachment;
|
||||
|
||||
if (contentType === MIME.IMAGE_GIF || Attachment.isGIF(attachments)) {
|
||||
return {
|
||||
bodyRanges,
|
||||
emoji: '🎡',
|
||||
text: body || window.i18n('icu:message--getNotificationText--gif'),
|
||||
};
|
||||
}
|
||||
if (Attachment.isImage(attachments)) {
|
||||
return {
|
||||
bodyRanges,
|
||||
emoji: '📷',
|
||||
text: body || window.i18n('icu:message--getNotificationText--photo'),
|
||||
};
|
||||
}
|
||||
if (Attachment.isVideo(attachments)) {
|
||||
return {
|
||||
bodyRanges,
|
||||
emoji: '🎥',
|
||||
text: body || window.i18n('icu:message--getNotificationText--video'),
|
||||
};
|
||||
}
|
||||
if (Attachment.isVoiceMessage(attachment)) {
|
||||
return {
|
||||
bodyRanges,
|
||||
emoji: '🎤',
|
||||
text:
|
||||
body ||
|
||||
window.i18n('icu:message--getNotificationText--voice-message'),
|
||||
};
|
||||
}
|
||||
if (Attachment.isAudio(attachments)) {
|
||||
return {
|
||||
bodyRanges,
|
||||
emoji: '🔈',
|
||||
text:
|
||||
body ||
|
||||
window.i18n('icu:message--getNotificationText--audio-message'),
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
bodyRanges,
|
||||
text: body || window.i18n('icu:message--getNotificationText--file'),
|
||||
emoji: '📎',
|
||||
};
|
||||
}
|
||||
|
||||
const { sticker: stickerData } = attributes;
|
||||
if (stickerData) {
|
||||
const emoji =
|
||||
Stickers.getSticker(stickerData.packId, stickerData.stickerId)?.emoji ||
|
||||
stickerData?.emoji;
|
||||
|
||||
if (!emoji) {
|
||||
log.warn('Unable to get emoji for sticker');
|
||||
}
|
||||
return {
|
||||
text: window.i18n('icu:message--getNotificationText--stickers'),
|
||||
emoji: dropNull(emoji),
|
||||
};
|
||||
}
|
||||
|
||||
if (isCallHistory(attributes)) {
|
||||
const state = window.reduxStore.getState();
|
||||
const callingNotification = getPropsForCallHistory(attributes, {
|
||||
callSelector: getCallSelector(state),
|
||||
activeCall: getActiveCall(state),
|
||||
callHistorySelector: getCallHistorySelector(state),
|
||||
conversationSelector: getConversationSelector(state),
|
||||
});
|
||||
if (callingNotification) {
|
||||
const text = getCallingNotificationText(callingNotification, window.i18n);
|
||||
if (text != null) {
|
||||
return {
|
||||
text,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
log.error("This call history message doesn't have valid call history");
|
||||
}
|
||||
if (isExpirationTimerUpdate(attributes)) {
|
||||
const { expireTimer } = attributes.expirationTimerUpdate ?? {};
|
||||
if (!expireTimer) {
|
||||
return { text: window.i18n('icu:disappearingMessagesDisabled') };
|
||||
}
|
||||
|
||||
return {
|
||||
text: window.i18n('icu:timerSetTo', {
|
||||
time: expirationTimer.format(window.i18n, expireTimer),
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
if (isKeyChange(attributes)) {
|
||||
const { key_changed: identifier } = attributes;
|
||||
const conversation = window.ConversationController.get(identifier);
|
||||
return {
|
||||
text: window.i18n('icu:safetyNumberChangedGroup', {
|
||||
name: conversation ? conversation.getTitle() : '',
|
||||
}),
|
||||
};
|
||||
}
|
||||
const { contact: contacts } = attributes;
|
||||
if (contacts && contacts.length) {
|
||||
return {
|
||||
text:
|
||||
EmbeddedContact.getName(contacts[0]) ||
|
||||
window.i18n('icu:unknownContact'),
|
||||
emoji: '👤',
|
||||
};
|
||||
}
|
||||
|
||||
const { giftBadge } = attributes;
|
||||
if (giftBadge) {
|
||||
const emoji = '✨';
|
||||
|
||||
if (isOutgoing(attributes)) {
|
||||
const toContact = window.ConversationController.get(
|
||||
attributes.conversationId
|
||||
);
|
||||
const recipient =
|
||||
toContact?.getTitle() ?? window.i18n('icu:unknownContact');
|
||||
return {
|
||||
emoji,
|
||||
text: window.i18n('icu:message--donation--preview--sent', {
|
||||
recipient,
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
const fromContact = getContact(attributes);
|
||||
const sender = fromContact?.getTitle() ?? window.i18n('icu:unknownContact');
|
||||
return {
|
||||
emoji,
|
||||
text:
|
||||
giftBadge.state === GiftBadgeStates.Unopened
|
||||
? window.i18n('icu:message--donation--preview--unopened', {
|
||||
sender,
|
||||
})
|
||||
: window.i18n('icu:message--donation--preview--redeemed'),
|
||||
};
|
||||
}
|
||||
|
||||
if (body) {
|
||||
return {
|
||||
text: body,
|
||||
bodyRanges,
|
||||
};
|
||||
}
|
||||
|
||||
return { text: '' };
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue