Upgrade Prettier to 2.4.1

This commit is contained in:
Evan Hahn 2021-11-11 16:43:05 -06:00 committed by GitHub
parent f204784afe
commit 5619eeca83
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
176 changed files with 1961 additions and 2465 deletions

View File

@ -1436,9 +1436,8 @@ app.on('ready', async () => {
let getMediaAccessStatus; let getMediaAccessStatus;
// This function is not supported on Linux, so we have a fallback. // This function is not supported on Linux, so we have a fallback.
if (systemPreferences.getMediaAccessStatus) { if (systemPreferences.getMediaAccessStatus) {
getMediaAccessStatus = systemPreferences.getMediaAccessStatus.bind( getMediaAccessStatus =
systemPreferences systemPreferences.getMediaAccessStatus.bind(systemPreferences);
);
} else { } else {
getMediaAccessStatus = noop; getMediaAccessStatus = noop;
} }
@ -1814,18 +1813,18 @@ ipc.on(
} }
); );
ipc.on('update-system-tray-setting', ( ipc.on(
_event, 'update-system-tray-setting',
rawSystemTraySetting /* : Readonly<unknown> */ (_event, rawSystemTraySetting /* : Readonly<unknown> */) => {
) => { const systemTraySetting = parseSystemTraySetting(rawSystemTraySetting);
const systemTraySetting = parseSystemTraySetting(rawSystemTraySetting); systemTraySettingCache.set(systemTraySetting);
systemTraySettingCache.set(systemTraySetting);
if (systemTrayService) { if (systemTrayService) {
const isEnabled = shouldMinimizeToSystemTray(systemTraySetting); const isEnabled = shouldMinimizeToSystemTray(systemTraySetting);
systemTrayService.setEnabled(isEnabled); systemTrayService.setEnabled(isEnabled);
}
} }
}); );
ipc.on('close-about', () => { ipc.on('close-about', () => {
if (aboutWindow) { if (aboutWindow) {

View File

@ -16,11 +16,10 @@
window.SignalContext.log.info( window.SignalContext.log.info(
'eraseTapToViewMessages: Loading messages...' 'eraseTapToViewMessages: Loading messages...'
); );
const messages = await window.Signal.Data.getTapToViewMessagesNeedingErase( const messages =
{ await window.Signal.Data.getTapToViewMessagesNeedingErase({
MessageCollection: Whisper.MessageCollection, MessageCollection: Whisper.MessageCollection,
} });
);
await Promise.all( await Promise.all(
messages.map(async fromDB => { messages.map(async fromDB => {
@ -51,7 +50,8 @@
const HOUR = 60 * MINUTE; const HOUR = 60 * MINUTE;
const THIRTY_DAYS = 30 * 24 * HOUR; const THIRTY_DAYS = 30 * 24 * HOUR;
const receivedAt = await window.Signal.Data.getNextTapToViewMessageTimestampToAgeOut(); const receivedAt =
await window.Signal.Data.getNextTapToViewMessageTimestampToAgeOut();
if (!receivedAt) { if (!receivedAt) {
return; return;
} }

View File

@ -196,9 +196,8 @@ function initializeMigrations({
const getAbsoluteAttachmentPath = createAbsolutePathGetter(attachmentsPath); const getAbsoluteAttachmentPath = createAbsolutePathGetter(attachmentsPath);
const deleteOnDisk = Attachments.createDeleter(attachmentsPath); const deleteOnDisk = Attachments.createDeleter(attachmentsPath);
const writeNewAttachmentData = createWriterForNew(attachmentsPath); const writeNewAttachmentData = createWriterForNew(attachmentsPath);
const copyIntoAttachmentsDirectory = Attachments.copyIntoAttachmentsDirectory( const copyIntoAttachmentsDirectory =
attachmentsPath Attachments.copyIntoAttachmentsDirectory(attachmentsPath);
);
const doesAttachmentExist = createDoesExist(attachmentsPath); const doesAttachmentExist = createDoesExist(attachmentsPath);
const stickersPath = getStickersPath(userDataPath); const stickersPath = getStickersPath(userDataPath);
@ -216,9 +215,8 @@ function initializeMigrations({
const writeNewTempData = createWriterForNew(tempPath); const writeNewTempData = createWriterForNew(tempPath);
const deleteTempFile = Attachments.createDeleter(tempPath); const deleteTempFile = Attachments.createDeleter(tempPath);
const readTempData = createReader(tempPath); const readTempData = createReader(tempPath);
const copyIntoTempDirectory = Attachments.copyIntoAttachmentsDirectory( const copyIntoTempDirectory =
tempPath Attachments.copyIntoAttachmentsDirectory(tempPath);
);
const draftPath = getDraftPath(userDataPath); const draftPath = getDraftPath(userDataPath);
const getAbsoluteDraftPath = createAbsolutePathGetter(draftPath); const getAbsoluteDraftPath = createAbsolutePathGetter(draftPath);

View File

@ -194,64 +194,63 @@ exports._mapContact = upgradeContact => async (message, context) => {
// _mapQuotedAttachments :: (QuotedAttachment -> Promise QuotedAttachment) -> // _mapQuotedAttachments :: (QuotedAttachment -> Promise QuotedAttachment) ->
// (Message, Context) -> // (Message, Context) ->
// Promise Message // Promise Message
exports._mapQuotedAttachments = upgradeAttachment => async ( exports._mapQuotedAttachments =
message, upgradeAttachment => async (message, context) => {
context if (!message.quote) {
) => { return message;
if (!message.quote) { }
return message; if (!context || !isObject(context.logger)) {
} throw new Error('_mapQuotedAttachments: context must have logger object');
if (!context || !isObject(context.logger)) {
throw new Error('_mapQuotedAttachments: context must have logger object');
}
const upgradeWithContext = async attachment => {
const { thumbnail } = attachment;
if (!thumbnail) {
return attachment;
} }
const upgradedThumbnail = await upgradeAttachment(thumbnail, context); const upgradeWithContext = async attachment => {
return { ...attachment, thumbnail: upgradedThumbnail }; const { thumbnail } = attachment;
if (!thumbnail) {
return attachment;
}
const upgradedThumbnail = await upgradeAttachment(thumbnail, context);
return { ...attachment, thumbnail: upgradedThumbnail };
};
const quotedAttachments =
(message.quote && message.quote.attachments) || [];
const attachments = await Promise.all(
quotedAttachments.map(upgradeWithContext)
);
return { ...message, quote: { ...message.quote, attachments } };
}; };
const quotedAttachments = (message.quote && message.quote.attachments) || [];
const attachments = await Promise.all(
quotedAttachments.map(upgradeWithContext)
);
return { ...message, quote: { ...message.quote, attachments } };
};
// _mapPreviewAttachments :: (PreviewAttachment -> Promise PreviewAttachment) -> // _mapPreviewAttachments :: (PreviewAttachment -> Promise PreviewAttachment) ->
// (Message, Context) -> // (Message, Context) ->
// Promise Message // Promise Message
exports._mapPreviewAttachments = upgradeAttachment => async ( exports._mapPreviewAttachments =
message, upgradeAttachment => async (message, context) => {
context if (!message.preview) {
) => { return message;
if (!message.preview) { }
return message; if (!context || !isObject(context.logger)) {
} throw new Error(
if (!context || !isObject(context.logger)) { '_mapPreviewAttachments: context must have logger object'
throw new Error('_mapPreviewAttachments: context must have logger object'); );
}
const upgradeWithContext = async preview => {
const { image } = preview;
if (!image) {
return preview;
} }
const upgradedImage = await upgradeAttachment(image, context); const upgradeWithContext = async preview => {
return { ...preview, image: upgradedImage }; const { image } = preview;
}; if (!image) {
return preview;
}
const preview = await Promise.all( const upgradedImage = await upgradeAttachment(image, context);
(message.preview || []).map(upgradeWithContext) return { ...preview, image: upgradedImage };
); };
return { ...message, preview };
}; const preview = await Promise.all(
(message.preview || []).map(upgradeWithContext)
);
return { ...message, preview };
};
const toVersion0 = async (message, context) => const toVersion0 = async (message, context) =>
exports.initializeSchemaVersion({ message, logger: context.logger }); exports.initializeSchemaVersion({ message, logger: context.logger });

View File

@ -136,5 +136,9 @@
return this; return this;
} }
Backbone.Model.prototype.trigger = Backbone.View.prototype.trigger = Backbone.Collection.prototype.trigger = Backbone.Events.trigger = trigger; Backbone.Model.prototype.trigger =
Backbone.View.prototype.trigger =
Backbone.Collection.prototype.trigger =
Backbone.Events.trigger =
trigger;
})(); })();

View File

@ -48,9 +48,8 @@ const fakeAPI = {
throw new Error('Invalid message'); throw new Error('Invalid message');
} }
messagesSentMap[ messagesSentMap[`${destination}.${messageArray[i].destinationDeviceId}`] =
`${destination}.${messageArray[i].destinationDeviceId}` msg;
] = msg;
} }
}, },
}; };

View File

@ -5,257 +5,33 @@
/* eslint-disable no-console */ /* eslint-disable no-console */
const BUCKET_SIZES = [ const BUCKET_SIZES = [
541, 541, 568, 596, 626, 657, 690, 725, 761, 799, 839, 881, 925, 972, 1020, 1071,
568, 1125, 1181, 1240, 1302, 1367, 1436, 1507, 1583, 1662, 1745, 1832, 1924, 2020,
596, 2121, 2227, 2339, 2456, 2579, 2708, 2843, 2985, 3134, 3291, 3456, 3629, 3810,
626, 4001, 4201, 4411, 4631, 4863, 5106, 5361, 5629, 5911, 6207, 6517, 6843, 7185,
657, 7544, 7921, 8318, 8733, 9170, 9629, 10110, 10616, 11146, 11704, 12289, 12903,
690, 13549, 14226, 14937, 15684, 16469, 17292, 18157, 19065, 20018, 21019, 22070,
725, 23173, 24332, 25549, 26826, 28167, 29576, 31054, 32607, 34238, 35950, 37747,
761, 39634, 41616, 43697, 45882, 48176, 50585, 53114, 55770, 58558, 61486, 64561,
799, 67789, 71178, 74737, 78474, 82398, 86518, 90843, 95386, 100155, 105163,
839, 110421, 115942, 121739, 127826, 134217, 140928, 147975, 155373, 163142,
881, 171299, 179864, 188858, 198300, 208215, 218626, 229558, 241036, 253087,
925, 265742, 279029, 292980, 307629, 323011, 339161, 356119, 373925, 392622,
972, 412253, 432866, 454509, 477234, 501096, 526151, 552458, 580081, 609086,
1020, 639540, 671517, 705093, 740347, 777365, 816233, 857045, 899897, 944892,
1071, 992136, 1041743, 1093831, 1148522, 1205948, 1266246, 1329558, 1396036,
1125, 1465838, 1539130, 1616086, 1696890, 1781735, 1870822, 1964363, 2062581,
1181, 2165710, 2273996, 2387695, 2507080, 2632434, 2764056, 2902259, 3047372,
1240, 3199740, 3359727, 3527714, 3704100, 3889305, 4083770, 4287958, 4502356,
1302, 4727474, 4963848, 5212040, 5472642, 5746274, 6033588, 6335268, 6652031,
1367, 6984633, 7333864, 7700558, 8085585, 8489865, 8914358, 9360076, 9828080,
1436, 10319484, 10835458, 11377231, 11946092, 12543397, 13170567, 13829095,
1507, 14520550, 15246578, 16008907, 16809352, 17649820, 18532311, 19458926,
1583, 20431872, 21453466, 22526139, 23652446, 24835069, 26076822, 27380663,
1662, 28749697, 30187181, 31696540, 33281368, 34945436, 36692708, 38527343,
1745, 40453710, 42476396, 44600216, 46830227, 49171738, 51630325, 54211841,
1832, 56922433, 59768555, 62756983, 65894832, 69189573, 72649052, 76281505,
1924, 80095580, 84100359, 88305377, 92720646, 97356678, 102224512, 107335738,
2020,
2121,
2227,
2339,
2456,
2579,
2708,
2843,
2985,
3134,
3291,
3456,
3629,
3810,
4001,
4201,
4411,
4631,
4863,
5106,
5361,
5629,
5911,
6207,
6517,
6843,
7185,
7544,
7921,
8318,
8733,
9170,
9629,
10110,
10616,
11146,
11704,
12289,
12903,
13549,
14226,
14937,
15684,
16469,
17292,
18157,
19065,
20018,
21019,
22070,
23173,
24332,
25549,
26826,
28167,
29576,
31054,
32607,
34238,
35950,
37747,
39634,
41616,
43697,
45882,
48176,
50585,
53114,
55770,
58558,
61486,
64561,
67789,
71178,
74737,
78474,
82398,
86518,
90843,
95386,
100155,
105163,
110421,
115942,
121739,
127826,
134217,
140928,
147975,
155373,
163142,
171299,
179864,
188858,
198300,
208215,
218626,
229558,
241036,
253087,
265742,
279029,
292980,
307629,
323011,
339161,
356119,
373925,
392622,
412253,
432866,
454509,
477234,
501096,
526151,
552458,
580081,
609086,
639540,
671517,
705093,
740347,
777365,
816233,
857045,
899897,
944892,
992136,
1041743,
1093831,
1148522,
1205948,
1266246,
1329558,
1396036,
1465838,
1539130,
1616086,
1696890,
1781735,
1870822,
1964363,
2062581,
2165710,
2273996,
2387695,
2507080,
2632434,
2764056,
2902259,
3047372,
3199740,
3359727,
3527714,
3704100,
3889305,
4083770,
4287958,
4502356,
4727474,
4963848,
5212040,
5472642,
5746274,
6033588,
6335268,
6652031,
6984633,
7333864,
7700558,
8085585,
8489865,
8914358,
9360076,
9828080,
10319484,
10835458,
11377231,
11946092,
12543397,
13170567,
13829095,
14520550,
15246578,
16008907,
16809352,
17649820,
18532311,
19458926,
20431872,
21453466,
22526139,
23652446,
24835069,
26076822,
27380663,
28749697,
30187181,
31696540,
33281368,
34945436,
36692708,
38527343,
40453710,
42476396,
44600216,
46830227,
49171738,
51630325,
54211841,
56922433,
59768555,
62756983,
65894832,
69189573,
72649052,
76281505,
80095580,
84100359,
88305377,
92720646,
97356678,
102224512,
107335738,
]; ];
describe('sendmessage', () => { describe('sendmessage', () => {

View File

@ -275,7 +275,7 @@
"npm-run-all": "4.1.5", "npm-run-all": "4.1.5",
"nyc": "11.4.1", "nyc": "11.4.1",
"patch-package": "6.4.7", "patch-package": "6.4.7",
"prettier": "^2.2.1", "prettier": "2.4.1",
"react-docgen-typescript": "1.2.6", "react-docgen-typescript": "1.2.6",
"sass-loader": "10.2.0", "sass-loader": "10.2.0",
"sinon": "11.1.1", "sinon": "11.1.1",

View File

@ -389,18 +389,18 @@ try {
window.imageToBlurHash = imageToBlurHash; window.imageToBlurHash = imageToBlurHash;
window.emojiData = require('emoji-datasource'); window.emojiData = require('emoji-datasource');
window.libphonenumber = require('google-libphonenumber').PhoneNumberUtil.getInstance(); window.libphonenumber =
window.libphonenumber.PhoneNumberFormat = require('google-libphonenumber').PhoneNumberFormat; require('google-libphonenumber').PhoneNumberUtil.getInstance();
window.libphonenumber.PhoneNumberFormat =
require('google-libphonenumber').PhoneNumberFormat;
const activeWindowService = new ActiveWindowService(); const activeWindowService = new ActiveWindowService();
activeWindowService.initialize(window.document, ipc); activeWindowService.initialize(window.document, ipc);
window.isActive = activeWindowService.isActive.bind(activeWindowService); window.isActive = activeWindowService.isActive.bind(activeWindowService);
window.registerForActive = activeWindowService.registerForActive.bind( window.registerForActive =
activeWindowService activeWindowService.registerForActive.bind(activeWindowService);
); window.unregisterForActive =
window.unregisterForActive = activeWindowService.unregisterForActive.bind( activeWindowService.unregisterForActive.bind(activeWindowService);
activeWindowService
);
window.Accessibility = { window.Accessibility = {
reducedMotionSetting: Boolean(config.reducedMotionSetting), reducedMotionSetting: Boolean(config.reducedMotionSetting),

View File

@ -35,9 +35,8 @@ export const MetaStage: React.ComponentType = () => {
[actions] [actions]
); );
const { getRootProps, getInputProps, isDragActive } = useStickerDropzone( const { getRootProps, getInputProps, isDragActive } =
onDrop useStickerDropzone(onDrop);
);
const onNext = React.useCallback(() => { const onNext = React.useCallback(() => {
setConfirming(true); setConfirming(true);

View File

@ -81,15 +81,11 @@ export const StickerFrame = React.memo(
}: Props) => { }: Props) => {
const i18n = useI18n(); const i18n = useI18n();
const [emojiPickerOpen, setEmojiPickerOpen] = React.useState(false); const [emojiPickerOpen, setEmojiPickerOpen] = React.useState(false);
const [ const [emojiPopperRoot, setEmojiPopperRoot] =
emojiPopperRoot, React.useState<HTMLElement | null>(null);
setEmojiPopperRoot,
] = React.useState<HTMLElement | null>(null);
const [previewActive, setPreviewActive] = React.useState(false); const [previewActive, setPreviewActive] = React.useState(false);
const [ const [previewPopperRoot, setPreviewPopperRoot] =
previewPopperRoot, React.useState<HTMLElement | null>(null);
setPreviewPopperRoot,
] = React.useState<HTMLElement | null>(null);
const timerRef = React.useRef<number>(); const timerRef = React.useRef<number>();
const handleToggleEmojiPicker = React.useCallback(() => { const handleToggleEmojiPicker = React.useCallback(() => {

View File

@ -37,9 +37,8 @@ export const DropZone: React.ComponentType<Props> = props => {
[onDrop] [onDrop]
); );
const { getRootProps, getInputProps, isDragActive } = useStickerDropzone( const { getRootProps, getInputProps, isDragActive } =
handleDrop useStickerDropzone(handleDrop);
);
React.useEffect(() => { React.useEffect(() => {
if (onDragActive) { if (onDragActive) {

View File

@ -169,9 +169,8 @@ window.encryptAndUpload = async (
const passwordItem = await window.Signal.Data.getItemById('password'); const passwordItem = await window.Signal.Data.getItemById('password');
if (!oldUsernameItem || !passwordItem) { if (!oldUsernameItem || !passwordItem) {
const { message } = window.localeMessages[ const { message } =
'StickerCreator--Authentication--error' window.localeMessages['StickerCreator--Authentication--error'];
];
ipc.send('show-message-box', { ipc.send('show-message-box', {
type: 'warning', type: 'warning',

View File

@ -32,9 +32,8 @@ export const removeSticker = createAction<string>('stickers/removeSticker');
export const moveSticker = createAction<SortEnd>('stickers/moveSticker'); export const moveSticker = createAction<SortEnd>('stickers/moveSticker');
export const setCover = createAction<StickerImageData>('stickers/setCover'); export const setCover = createAction<StickerImageData>('stickers/setCover');
export const resetCover = createAction<StickerImageData>('stickers/resetCover'); export const resetCover = createAction<StickerImageData>('stickers/resetCover');
export const setEmoji = createAction<{ id: string; emoji: EmojiPickDataType }>( export const setEmoji =
'stickers/setEmoji' createAction<{ id: string; emoji: EmojiPickDataType }>('stickers/setEmoji');
);
export const setTitle = createAction<string>('stickers/setTitle'); export const setTitle = createAction<string>('stickers/setTitle');
export const setAuthor = createAction<string>('stickers/setAuthor'); export const setAuthor = createAction<string>('stickers/setAuthor');
export const setPackMeta = createAction<PackMetaData>('stickers/setPackMeta'); export const setPackMeta = createAction<PackMetaData>('stickers/setPackMeta');

View File

@ -72,8 +72,7 @@ describe('Stickers', () => {
), ),
{ {
id: 'c8c83285b547872ac4c589d64a6edd6a', id: 'c8c83285b547872ac4c589d64a6edd6a',
key: key: '59bb3a8860f0e6a5a83a5337a015c8d55ecd2193f82d77202f3b8112a845636e',
'59bb3a8860f0e6a5a83a5337a015c8d55ecd2193f82d77202f3b8112a845636e',
} }
); );
}); });
@ -85,8 +84,7 @@ describe('Stickers', () => {
), ),
{ {
id: 'c8c83285b547872ac4c589d64a6edd6a', id: 'c8c83285b547872ac4c589d64a6edd6a',
key: key: '59bb3a8860f0e6a5a83a5337a015c8d55ecd2193f82d77202f3b8112a845636e',
'59bb3a8860f0e6a5a83a5337a015c8d55ecd2193f82d77202f3b8112a845636e',
} }
); );
}); });
@ -98,8 +96,7 @@ describe('Stickers', () => {
), ),
{ {
id: 'c8c83285b547872ac4c589d64a6edd6a', id: 'c8c83285b547872ac4c589d64a6edd6a',
key: key: '59bb3a8860f0e6a5a83a5337a015c8d55ecd2193f82d77202f3b8112a845636e',
'59bb3a8860f0e6a5a83a5337a015c8d55ecd2193f82d77202f3b8112a845636e',
} }
); );
}); });

View File

@ -409,10 +409,8 @@ export async function encryptCdsDiscoveryRequest(
iv, iv,
queryDataPlaintext queryDataPlaintext
); );
const { const { data: queryDataCiphertextData, mac: queryDataCiphertextMac } =
data: queryDataCiphertextData, _getMacAndData(queryDataCiphertext);
mac: queryDataCiphertextMac,
} = _getMacAndData(queryDataCiphertext);
const envelopes = await pProps( const envelopes = await pProps(
attestations, attestations,

View File

@ -279,10 +279,11 @@ export class SignedPreKeys extends SignedPreKeyStore {
} }
async getSignedPreKey(id: number): Promise<SignedPreKeyRecord> { async getSignedPreKey(id: number): Promise<SignedPreKeyRecord> {
const signedPreKey = await window.textsecure.storage.protocol.loadSignedPreKey( const signedPreKey =
this.ourUuid, await window.textsecure.storage.protocol.loadSignedPreKey(
id this.ourUuid,
); id
);
if (!signedPreKey) { if (!signedPreKey) {
throw new Error(`getSignedPreKey: SignedPreKey ${id} not found`); throw new Error(`getSignedPreKey: SignedPreKey ${id} not found`);

View File

@ -185,10 +185,10 @@ export function freezeSignedPreKey(
} }
// We add a this parameter to avoid an 'implicit any' error on the next line // We add a this parameter to avoid an 'implicit any' error on the next line
const EventsMixin = (function EventsMixin(this: unknown) { const EventsMixin = function EventsMixin(this: unknown) {
window._.assign(this, window.Backbone.Events); window._.assign(this, window.Backbone.Events);
// eslint-disable-next-line @typescript-eslint/no-explicit-any // eslint-disable-next-line @typescript-eslint/no-explicit-any
} as any) as typeof window.Backbone.EventsMixin; } as any as typeof window.Backbone.EventsMixin;
type SessionCacheEntry = CacheEntryType<SessionType, SessionRecord>; type SessionCacheEntry = CacheEntryType<SessionType, SessionRecord>;

View File

@ -371,8 +371,8 @@ export async function startApp(): Promise<void> {
timeout: 1000 * 60 * 2, timeout: 1000 * 60 * 2,
}); });
window.Whisper.deliveryReceiptQueue.pause(); window.Whisper.deliveryReceiptQueue.pause();
window.Whisper.deliveryReceiptBatcher = window.Signal.Util.createBatcher<DeliveryReceiptBatcherItemType>( window.Whisper.deliveryReceiptBatcher =
{ window.Signal.Util.createBatcher<DeliveryReceiptBatcherItemType>({
name: 'Whisper.deliveryReceiptBatcher', name: 'Whisper.deliveryReceiptBatcher',
wait: 500, wait: 500,
maxSize: 100, maxSize: 100,
@ -426,8 +426,7 @@ export async function startApp(): Promise<void> {
}); });
} }
}, },
} });
);
if (getTitleBarVisibility() === TitleBarVisibility.Hidden) { if (getTitleBarVisibility() === TitleBarVisibility.Hidden) {
window.addEventListener('dblclick', (event: Event) => { window.addEventListener('dblclick', (event: Event) => {
@ -476,10 +475,8 @@ export async function startApp(): Promise<void> {
window.setImmediate = window.nodeSetImmediate; window.setImmediate = window.nodeSetImmediate;
const { IdleDetector, MessageDataMigrator } = window.Signal.Workflow; const { IdleDetector, MessageDataMigrator } = window.Signal.Workflow;
const { const { removeDatabase: removeIndexedDB, doesDatabaseExist } =
removeDatabase: removeIndexedDB, window.Signal.IndexedDB;
doesDatabaseExist,
} = window.Signal.IndexedDB;
const { Message } = window.Signal.Types; const { Message } = window.Signal.Types;
const { const {
upgradeMessageSchema, upgradeMessageSchema,
@ -868,12 +865,12 @@ export async function startApp(): Promise<void> {
); );
expired.forEach(item => { expired.forEach(item => {
const { conversationId, senderUuid, sentAt } = item; const { conversationId, senderUuid, sentAt } = item;
const conversation = window.ConversationController.get( const conversation =
conversationId window.ConversationController.get(conversationId);
);
if (conversation) { if (conversation) {
const receivedAt = Date.now(); const receivedAt = Date.now();
const receivedAtCounter = window.Signal.Util.incrementMessageCounter(); const receivedAtCounter =
window.Signal.Util.incrementMessageCounter();
conversation.queueJob('addDeliveryIssue', () => conversation.queueJob('addDeliveryIssue', () =>
conversation.addDeliveryIssue({ conversation.addDeliveryIssue({
receivedAt, receivedAt,
@ -933,7 +930,8 @@ export async function startApp(): Promise<void> {
); );
const ourNumber = window.textsecure.storage.user.getNumber(); const ourNumber = window.textsecure.storage.user.getNumber();
const ourUuid = window.textsecure.storage.user.getUuid()?.toString(); const ourUuid = window.textsecure.storage.user.getUuid()?.toString();
const ourConversationId = window.ConversationController.getOurConversationId(); const ourConversationId =
window.ConversationController.getOurConversationId();
const themeSetting = window.Events.getThemeSetting(); const themeSetting = window.Events.getThemeSetting();
const theme = themeSetting === 'system' ? window.systemTheme : themeSetting; const theme = themeSetting === 'system' ? window.systemTheme : themeSetting;
@ -1094,7 +1092,8 @@ export async function startApp(): Promise<void> {
const newDeviceId = window.textsecure.storage.user.getDeviceId(); const newDeviceId = window.textsecure.storage.user.getDeviceId();
const newNumber = window.textsecure.storage.user.getNumber(); const newNumber = window.textsecure.storage.user.getNumber();
const newUuid = window.textsecure.storage.user.getUuid()?.toString(); const newUuid = window.textsecure.storage.user.getUuid()?.toString();
const ourConversation = window.ConversationController.getOurConversation(); const ourConversation =
window.ConversationController.getOurConversation();
if (ourConversation?.get('e164') !== newNumber) { if (ourConversation?.get('e164') !== newNumber) {
ourConversation?.set('e164', newNumber); ourConversation?.set('e164', newNumber);
@ -1686,7 +1685,8 @@ export async function startApp(): Promise<void> {
window.Signal.challengeHandler = challengeHandler; window.Signal.challengeHandler = challengeHandler;
if (!window.storage.user.getNumber()) { if (!window.storage.user.getNumber()) {
const ourConversation = window.ConversationController.getOurConversation(); const ourConversation =
window.ConversationController.getOurConversation();
const ourE164 = ourConversation?.get('e164'); const ourE164 = ourConversation?.get('e164');
if (ourE164) { if (ourE164) {
log.warn('Restoring E164 from our conversation'); log.warn('Restoring E164 from our conversation');
@ -1699,13 +1699,14 @@ export async function startApp(): Promise<void> {
badgeImageFileDownloader.checkForFilesToDownload(); badgeImageFileDownloader.checkForFilesToDownload();
log.info('Expiration start timestamp cleanup: starting...'); log.info('Expiration start timestamp cleanup: starting...');
const messagesUnexpectedlyMissingExpirationStartTimestamp = await window.Signal.Data.getMessagesUnexpectedlyMissingExpirationStartTimestamp(); const messagesUnexpectedlyMissingExpirationStartTimestamp =
await window.Signal.Data.getMessagesUnexpectedlyMissingExpirationStartTimestamp();
log.info( log.info(
`Expiration start timestamp cleanup: Found ${messagesUnexpectedlyMissingExpirationStartTimestamp.length} messages for cleanup` `Expiration start timestamp cleanup: Found ${messagesUnexpectedlyMissingExpirationStartTimestamp.length} messages for cleanup`
); );
if (messagesUnexpectedlyMissingExpirationStartTimestamp.length) { if (messagesUnexpectedlyMissingExpirationStartTimestamp.length) {
const newMessageAttributes = messagesUnexpectedlyMissingExpirationStartTimestamp.map( const newMessageAttributes =
message => { messagesUnexpectedlyMissingExpirationStartTimestamp.map(message => {
const expirationStartTimestamp = Math.min( const expirationStartTimestamp = Math.min(
...filter( ...filter(
[ [
@ -1727,8 +1728,7 @@ export async function startApp(): Promise<void> {
...message, ...message,
expirationStartTimestamp, expirationStartTimestamp,
}; };
} });
);
await window.Signal.Data.saveMessages(newMessageAttributes); await window.Signal.Data.saveMessages(newMessageAttributes);
} }
@ -1804,9 +1804,8 @@ export async function startApp(): Promise<void> {
window.Signal.RemoteConfig.onChange( window.Signal.RemoteConfig.onChange(
'desktop.clientExpiration', 'desktop.clientExpiration',
({ value }) => { ({ value }) => {
const remoteBuildExpirationTimestamp = window.Signal.Util.parseRemoteClientExpiration( const remoteBuildExpirationTimestamp =
value as string window.Signal.Util.parseRemoteClientExpiration(value as string);
);
if (remoteBuildExpirationTimestamp) { if (remoteBuildExpirationTimestamp) {
window.storage.put( window.storage.put(
'remoteBuildExpiration', 'remoteBuildExpiration',
@ -2013,9 +2012,10 @@ export async function startApp(): Promise<void> {
'desktop.clientExpiration' 'desktop.clientExpiration'
); );
if (expiration) { if (expiration) {
const remoteBuildExpirationTimestamp = window.Signal.Util.parseRemoteClientExpiration( const remoteBuildExpirationTimestamp =
expiration as string window.Signal.Util.parseRemoteClientExpiration(
); expiration as string
);
if (remoteBuildExpirationTimestamp) { if (remoteBuildExpirationTimestamp) {
window.storage.put( window.storage.put(
'remoteBuildExpiration', 'remoteBuildExpiration',
@ -2183,7 +2183,8 @@ export async function startApp(): Promise<void> {
runStorageService(); runStorageService();
}); });
const ourConversation = window.ConversationController.getOurConversationOrThrow(); const ourConversation =
window.ConversationController.getOurConversationOrThrow();
const sendOptions = await getSendOptions(ourConversation.attributes, { const sendOptions = await getSendOptions(ourConversation.attributes, {
syncMessage: true, syncMessage: true,
}); });
@ -2223,7 +2224,8 @@ export async function startApp(): Promise<void> {
// Kick off a profile refresh if necessary, but don't wait for it, as failure is // Kick off a profile refresh if necessary, but don't wait for it, as failure is
// tolerable. // tolerable.
const ourConversationId = window.ConversationController.getOurConversationId(); const ourConversationId =
window.ConversationController.getOurConversationId();
if (ourConversationId) { if (ourConversationId) {
routineProfileRefresh({ routineProfileRefresh({
allConversations: window.ConversationController.getAll(), allConversations: window.ConversationController.getAll(),
@ -2565,11 +2567,11 @@ export async function startApp(): Promise<void> {
} }
} }
const c = new window.Whisper.Conversation(({ const c = new window.Whisper.Conversation({
e164: details.number, e164: details.number,
uuid: details.uuid, uuid: details.uuid,
type: 'private', type: 'private',
} as Partial<ConversationAttributesType>) as WhatIsThis); } as Partial<ConversationAttributesType> as WhatIsThis);
const validationError = c.validate(); const validationError = c.validate();
if (validationError) { if (validationError) {
log.error( log.error(
@ -3000,32 +3002,38 @@ export async function startApp(): Promise<void> {
const { unidentifiedStatus = [] } = data; const { unidentifiedStatus = [] } = data;
const sendStateByConversationId: SendStateByConversationId = unidentifiedStatus.reduce( const sendStateByConversationId: SendStateByConversationId =
(result: SendStateByConversationId, { destinationUuid, destination }) => { unidentifiedStatus.reduce(
const conversationId = window.ConversationController.ensureContactIds({ (
uuid: destinationUuid, result: SendStateByConversationId,
e164: destination, { destinationUuid, destination }
highTrust: true, ) => {
}); const conversationId = window.ConversationController.ensureContactIds(
if (!conversationId || conversationId === ourId) { {
return result; uuid: destinationUuid,
} e164: destination,
highTrust: true,
}
);
if (!conversationId || conversationId === ourId) {
return result;
}
return { return {
...result, ...result,
[conversationId]: { [conversationId]: {
status: SendStatus.Sent,
updatedAt: timestamp,
},
};
},
{
[ourId]: {
status: SendStatus.Sent, status: SendStatus.Sent,
updatedAt: timestamp, updatedAt: timestamp,
}, },
}; }
}, );
{
[ourId]: {
status: SendStatus.Sent,
updatedAt: timestamp,
},
}
);
let unidentifiedDeliveries: Array<string> = []; let unidentifiedDeliveries: Array<string> = [];
if (unidentifiedStatus.length) { if (unidentifiedStatus.length) {
@ -3037,7 +3045,7 @@ export async function startApp(): Promise<void> {
.filter(isNotNil); .filter(isNotNil);
} }
return new window.Whisper.Message(({ return new window.Whisper.Message({
source: window.textsecure.storage.user.getNumber(), source: window.textsecure.storage.user.getNumber(),
sourceUuid: window.textsecure.storage.user.getUuid()?.toString(), sourceUuid: window.textsecure.storage.user.getUuid()?.toString(),
sourceDevice: data.device, sourceDevice: data.device,
@ -3054,7 +3062,7 @@ export async function startApp(): Promise<void> {
data.expirationStartTimestamp || timestamp, data.expirationStartTimestamp || timestamp,
now now
), ),
} as Partial<MessageAttributesType>) as WhatIsThis); } as Partial<MessageAttributesType> as WhatIsThis);
} }
// Works with 'sent' and 'message' data sent from MessageReceiver, with a little massage // Works with 'sent' and 'message' data sent from MessageReceiver, with a little massage
@ -3122,9 +3130,8 @@ export async function startApp(): Promise<void> {
); );
} else { } else {
// First we check for an already-migrated GroupV2 group // First we check for an already-migrated GroupV2 group
const migratedGroup = window.ConversationController.get( const migratedGroup =
derivedGroupV2Id window.ConversationController.get(derivedGroupV2Id);
);
if (migratedGroup) { if (migratedGroup) {
return { return {
type: Message.GROUP, type: Message.GROUP,
@ -3275,7 +3282,7 @@ export async function startApp(): Promise<void> {
Boolean(data.receivedAtCounter), Boolean(data.receivedAtCounter),
`Did not receive receivedAtCounter for message: ${data.timestamp}` `Did not receive receivedAtCounter for message: ${data.timestamp}`
); );
return new window.Whisper.Message(({ return new window.Whisper.Message({
source: data.source, source: data.source,
sourceUuid: data.sourceUuid, sourceUuid: data.sourceUuid,
sourceDevice: data.sourceDevice, sourceDevice: data.sourceDevice,
@ -3289,7 +3296,7 @@ export async function startApp(): Promise<void> {
type: 'incoming', type: 'incoming',
readStatus: ReadStatus.Unread, readStatus: ReadStatus.Unread,
timestamp: data.timestamp, timestamp: data.timestamp,
} as Partial<MessageAttributesType>) as WhatIsThis); } as Partial<MessageAttributesType> as WhatIsThis);
} }
// Returns `false` if this message isn't a group call message. // Returns `false` if this message isn't a group call message.
@ -3545,13 +3552,8 @@ export async function startApp(): Promise<void> {
logTitle: string; logTitle: string;
type: MessageReceiptType.Read | MessageReceiptType.View; type: MessageReceiptType.Read | MessageReceiptType.View;
}>): void { }>): void {
const { const { envelopeTimestamp, timestamp, source, sourceUuid, sourceDevice } =
envelopeTimestamp, event.receipt;
timestamp,
source,
sourceUuid,
sourceDevice,
} = event.receipt;
const sourceConversationId = window.ConversationController.ensureContactIds( const sourceConversationId = window.ConversationController.ensureContactIds(
{ {
e164: source, e164: source,
@ -3664,11 +3666,11 @@ export async function startApp(): Promise<void> {
ev.confirm(); ev.confirm();
} }
const c = new window.Whisper.Conversation(({ const c = new window.Whisper.Conversation({
e164, e164,
uuid, uuid,
type: 'private', type: 'private',
} as Partial<ConversationAttributesType>) as WhatIsThis); } as Partial<ConversationAttributesType> as WhatIsThis);
const error = c.validate(); const error = c.validate();
if (error) { if (error) {
log.error( log.error(
@ -3726,13 +3728,8 @@ export async function startApp(): Promise<void> {
function onDeliveryReceipt(ev: DeliveryEvent) { function onDeliveryReceipt(ev: DeliveryEvent) {
const { deliveryReceipt } = ev; const { deliveryReceipt } = ev;
const { const { envelopeTimestamp, sourceUuid, source, sourceDevice, timestamp } =
envelopeTimestamp, deliveryReceipt;
sourceUuid,
source,
sourceDevice,
timestamp,
} = deliveryReceipt;
ev.confirm(); ev.confirm();

View File

@ -154,13 +154,12 @@ export class ChallengeHandler {
const retryIds = new Set<string>(stored.map(({ messageId }) => messageId)); const retryIds = new Set<string>(stored.map(({ messageId }) => messageId));
const maybeMessages: ReadonlyArray< const maybeMessages: ReadonlyArray<MinimalMessage | undefined> =
MinimalMessage | undefined await Promise.all(
> = await Promise.all( Array.from(retryIds).map(async messageId =>
Array.from(retryIds).map(async messageId => this.options.getMessageById(messageId)
this.options.getMessageById(messageId) )
) );
);
const messages: Array<MinimalMessage> = maybeMessages.filter(isNotNil); const messages: Array<MinimalMessage> = maybeMessages.filter(isNotNil);

View File

@ -35,43 +35,47 @@ type PropsType = {
onClose: () => void; onClose: () => void;
} & PropsDataType; } & PropsDataType;
export const AddGroupMemberErrorDialog: FunctionComponent<PropsType> = props => { export const AddGroupMemberErrorDialog: FunctionComponent<PropsType> =
const { i18n, onClose } = props; props => {
const { i18n, onClose } = props;
let title: string; let title: string;
let body: ReactNode; let body: ReactNode;
switch (props.mode) { switch (props.mode) {
case AddGroupMemberErrorDialogMode.CantAddContact: { case AddGroupMemberErrorDialogMode.CantAddContact: {
const { contact } = props; const { contact } = props;
title = i18n('chooseGroupMembers__cant-add-member__title'); title = i18n('chooseGroupMembers__cant-add-member__title');
body = ( body = (
<Intl <Intl
i18n={i18n} i18n={i18n}
id="chooseGroupMembers__cant-add-member__body" id="chooseGroupMembers__cant-add-member__body"
components={[<ContactName key="name" title={contact.title} />]} components={[<ContactName key="name" title={contact.title} />]}
/> />
); );
break; break;
}
case AddGroupMemberErrorDialogMode.MaximumGroupSize: {
const { maximumNumberOfContacts } = props;
title = i18n('chooseGroupMembers__maximum-group-size__title');
body = i18n('chooseGroupMembers__maximum-group-size__body', [
maximumNumberOfContacts.toString(),
]);
break;
}
case AddGroupMemberErrorDialogMode.RecommendedMaximumGroupSize: {
const { recommendedMaximumNumberOfContacts } = props;
title = i18n(
'chooseGroupMembers__maximum-recommended-group-size__title'
);
body = i18n(
'chooseGroupMembers__maximum-recommended-group-size__body',
[recommendedMaximumNumberOfContacts.toString()]
);
break;
}
default:
throw missingCaseError(props);
} }
case AddGroupMemberErrorDialogMode.MaximumGroupSize: {
const { maximumNumberOfContacts } = props;
title = i18n('chooseGroupMembers__maximum-group-size__title');
body = i18n('chooseGroupMembers__maximum-group-size__body', [
maximumNumberOfContacts.toString(),
]);
break;
}
case AddGroupMemberErrorDialogMode.RecommendedMaximumGroupSize: {
const { recommendedMaximumNumberOfContacts } = props;
title = i18n('chooseGroupMembers__maximum-recommended-group-size__title');
body = i18n('chooseGroupMembers__maximum-recommended-group-size__body', [
recommendedMaximumNumberOfContacts.toString(),
]);
break;
}
default:
throw missingCaseError(props);
}
return <Alert body={body} i18n={i18n} onClose={onClose} title={title} />; return <Alert body={body} i18n={i18n} onClose={onClose} title={title} />;
}; };

View File

@ -23,9 +23,8 @@ export const AvatarIconEditor = ({
onClose, onClose,
}: PropsType): JSX.Element => { }: PropsType): JSX.Element => {
const [avatarBuffer, setAvatarBuffer] = useState<Uint8Array | undefined>(); const [avatarBuffer, setAvatarBuffer] = useState<Uint8Array | undefined>();
const [avatarData, setAvatarData] = useState<AvatarDataType>( const [avatarData, setAvatarData] =
initialAvatarData useState<AvatarDataType>(initialAvatarData);
);
const onColorSelected = useCallback( const onColorSelected = useCallback(
(color: AvatarColorType) => { (color: AvatarColorType) => {

View File

@ -47,9 +47,10 @@ export const AvatarTextEditor = ({
onDone, onDone,
}: PropsType): JSX.Element => { }: PropsType): JSX.Element => {
const initialText = useMemo(() => avatarData?.text || '', [avatarData]); const initialText = useMemo(() => avatarData?.text || '', [avatarData]);
const initialColor = useMemo(() => avatarData?.color || AvatarColors[0], [ const initialColor = useMemo(
avatarData, () => avatarData?.color || AvatarColors[0],
]); [avatarData]
);
const [inputText, setInputText] = useState(initialText); const [inputText, setInputText] = useState(initialText);
const [fontSize, setFontSize] = useState(getFontSizes(BUBBLE_SIZE).text); const [fontSize, setFontSize] = useState(getFontSizes(BUBBLE_SIZE).text);

View File

@ -514,7 +514,8 @@ export const CallScreen: React.FC<PropsType> = ({
</div> </div>
<div <div
className={classNames('module-ongoing-call__footer__local-preview', { className={classNames('module-ongoing-call__footer__local-preview', {
'module-ongoing-call__footer__local-preview--audio-muted': !hasLocalAudio, 'module-ongoing-call__footer__local-preview--audio-muted':
!hasLocalAudio,
})} })}
> >
{localPreviewNode} {localPreviewNode}

View File

@ -58,7 +58,8 @@ export const CallingHeader = ({
className={classNames( className={classNames(
'module-calling-button__participants--container', 'module-calling-button__participants--container',
{ {
'module-calling-button__participants--shown': showParticipantsList, 'module-calling-button__participants--shown':
showParticipantsList,
} }
)} )}
onClick={toggleParticipants} onClick={toggleParticipants}

View File

@ -77,10 +77,8 @@ export const CallingLobbyJoinButton: FunctionComponent<{
if (!button) { if (!button) {
return; return;
} }
const { const { width: variantWidth, height: variantHeight } =
width: variantWidth, button.getBoundingClientRect();
height: variantHeight,
} = button.getBoundingClientRect();
// We could set the padding in CSS, but we don't do that in case some other // We could set the padding in CSS, but we don't do that in case some other
// styling causes a re-render of the button but not of the component. This // styling causes a re-render of the button but not of the component. This

View File

@ -90,17 +90,16 @@ export const CallingPipRemoteVideo = ({
const isPageVisible = usePageVisibility(); const isPageVisible = usePageVisibility();
const activeGroupCallSpeaker: const activeGroupCallSpeaker: undefined | GroupCallRemoteParticipantType =
| undefined useMemo(() => {
| GroupCallRemoteParticipantType = useMemo(() => { if (activeCall.callMode !== CallMode.Group) {
if (activeCall.callMode !== CallMode.Group) { return undefined;
return undefined; }
}
return maxBy(activeCall.remoteParticipants, participant => return maxBy(activeCall.remoteParticipants, participant =>
participant.presenting ? Infinity : participant.speakerTime || -Infinity participant.presenting ? Infinity : participant.speakerTime || -Infinity
); );
}, [activeCall.callMode, activeCall.remoteParticipants]); }, [activeCall.callMode, activeCall.remoteParticipants]);
useEffect(() => { useEffect(() => {
if (activeCall.callMode !== CallMode.Group) { if (activeCall.callMode !== CallMode.Group) {

View File

@ -110,18 +110,12 @@ export function CompositionInput(props: Props): React.ReactElement {
sortedGroupMembers, sortedGroupMembers,
} = props; } = props;
const [ const [emojiCompletionElement, setEmojiCompletionElement] =
emojiCompletionElement, React.useState<JSX.Element>();
setEmojiCompletionElement, const [lastSelectionRange, setLastSelectionRange] =
] = React.useState<JSX.Element>(); React.useState<RangeStatic | null>(null);
const [ const [mentionCompletionElement, setMentionCompletionElement] =
lastSelectionRange, React.useState<JSX.Element>();
setLastSelectionRange,
] = React.useState<RangeStatic | null>(null);
const [
mentionCompletionElement,
setMentionCompletionElement,
] = React.useState<JSX.Element>();
const emojiCompletionRef = React.useRef<EmojiCompletion>(); const emojiCompletionRef = React.useRef<EmojiCompletion>();
const mentionCompletionRef = React.useRef<MentionCompletion>(); const mentionCompletionRef = React.useRef<MentionCompletion>();
@ -607,9 +601,8 @@ export function CompositionInput(props: Props): React.ReactElement {
); );
quillRef.current = quill; quillRef.current = quill;
emojiCompletionRef.current = quill.getModule('emojiCompletion'); emojiCompletionRef.current = quill.getModule('emojiCompletion');
mentionCompletionRef.current = quill.getModule( mentionCompletionRef.current =
'mentionCompletion' quill.getModule('mentionCompletion');
);
} }
}} }}
/> />

View File

@ -49,19 +49,20 @@ export const CompositionUpload = forwardRef<HTMLInputElement, PropsType>(
AttachmentToastType | undefined AttachmentToastType | undefined
>(); >();
const onFileInputChange: ChangeEventHandler<HTMLInputElement> = async event => { const onFileInputChange: ChangeEventHandler<HTMLInputElement> =
const files = event.target.files || []; async event => {
const files = event.target.files || [];
await processAttachments({ await processAttachments({
addAttachment, addAttachment,
addPendingAttachment, addPendingAttachment,
conversationId, conversationId,
files: Array.from(files), files: Array.from(files),
draftAttachments, draftAttachments,
onShowToast: setToastType, onShowToast: setToastType,
removeAttachment, removeAttachment,
}); });
}; };
function closeToast() { function closeToast() {
setToastType(undefined); setToastType(undefined);

View File

@ -70,16 +70,8 @@ export class ContactListItem extends React.Component<Props> {
} }
public render(): JSX.Element { public render(): JSX.Element {
const { const { about, i18n, isAdmin, isMe, name, onClick, title, type } =
about, this.props;
i18n,
isAdmin,
isMe,
name,
onClick,
title,
type,
} = this.props;
const displayName = isMe ? i18n('you') : title; const displayName = isMe ? i18n('you') : title;

View File

@ -35,8 +35,7 @@ const defaultConversations: Array<ConversationListItemPropsType> = [
title: 'Marc Barraca', title: 'Marc Barraca',
lastMessage: { lastMessage: {
deletedForEveryone: false, deletedForEveryone: false,
text: text: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec a diam lectus. Sed sit amet ipsum mauris. Maecenas congue ligula ac quam viverra nec consectetur ante hendrerit. Donec et mollis dolor. Praesent et diam eget libero egestas mattis sit amet vitae augue. Nam tincidunt congue enim, ut porta lorem lacinia consectetur. Donec ut libero sed arcu vehicula ultricies a non tortor. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean ut gravida lorem. Ut turpis felis, pulvinar a semper sed, adipiscing id dolor. Pellentesque auctor nisi id magna consequat sagittis. Curabitur dapibus enim sit amet elit pharetra tincidunt feugiat nisl imperdiet. Ut convallis libero in urna ultrices accumsan. Donec sed odio eros. Donec viverra mi quis quam pulvinar at malesuada arcu rhoncus. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. In rutrum accumsan ultricies. Mauris vitae nisi at sem facilisis semper ac in est.',
'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec a diam lectus. Sed sit amet ipsum mauris. Maecenas congue ligula ac quam viverra nec consectetur ante hendrerit. Donec et mollis dolor. Praesent et diam eget libero egestas mattis sit amet vitae augue. Nam tincidunt congue enim, ut porta lorem lacinia consectetur. Donec ut libero sed arcu vehicula ultricies a non tortor. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean ut gravida lorem. Ut turpis felis, pulvinar a semper sed, adipiscing id dolor. Pellentesque auctor nisi id magna consequat sagittis. Curabitur dapibus enim sit amet elit pharetra tincidunt feugiat nisl imperdiet. Ut convallis libero in urna ultrices accumsan. Donec sed odio eros. Donec viverra mi quis quam pulvinar at malesuada arcu rhoncus. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. In rutrum accumsan ultricies. Mauris vitae nisi at sem facilisis semper ac in est.',
}, },
}), }),
getDefaultConversation({ getDefaultConversation({

View File

@ -17,27 +17,26 @@ const story = storiesOf(
module module
); );
const defaultProps: ComponentProps< const defaultProps: ComponentProps<typeof CustomizingPreferredReactionsModal> =
typeof CustomizingPreferredReactionsModal {
> = { cancelCustomizePreferredReactionsModal: action(
cancelCustomizePreferredReactionsModal: action( 'cancelCustomizePreferredReactionsModal'
'cancelCustomizePreferredReactionsModal' ),
), deselectDraftEmoji: action('deselectDraftEmoji'),
deselectDraftEmoji: action('deselectDraftEmoji'), draftPreferredReactions: ['✨', '❇️', '🎇', '🦈', '💖', '🅿️'],
draftPreferredReactions: ['✨', '❇️', '🎇', '🦈', '💖', '🅿️'], hadSaveError: false,
hadSaveError: false, i18n,
i18n, isSaving: false,
isSaving: false, onSetSkinTone: action('onSetSkinTone'),
onSetSkinTone: action('onSetSkinTone'), originalPreferredReactions: ['❤️', '👍', '👎', '😂', '😮', '😢'],
originalPreferredReactions: ['❤️', '👍', '👎', '😂', '😮', '😢'], recentEmojis: ['cake'],
recentEmojis: ['cake'], replaceSelectedDraftEmoji: action('replaceSelectedDraftEmoji'),
replaceSelectedDraftEmoji: action('replaceSelectedDraftEmoji'), resetDraftEmoji: action('resetDraftEmoji'),
resetDraftEmoji: action('resetDraftEmoji'), savePreferredReactions: action('savePreferredReactions'),
savePreferredReactions: action('savePreferredReactions'), selectDraftEmojiToBeReplaced: action('selectDraftEmojiToBeReplaced'),
selectDraftEmojiToBeReplaced: action('selectDraftEmojiToBeReplaced'), selectedDraftEmojiIndex: undefined,
selectedDraftEmojiIndex: undefined, skinTone: 4,
skinTone: 4, };
};
story.add('Default', () => ( story.add('Default', () => (
<CustomizingPreferredReactionsModal {...defaultProps} /> <CustomizingPreferredReactionsModal {...defaultProps} />

View File

@ -54,10 +54,8 @@ export function CustomizingPreferredReactionsModal({
selectedDraftEmojiIndex, selectedDraftEmojiIndex,
skinTone, skinTone,
}: Readonly<PropsType>): JSX.Element { }: Readonly<PropsType>): JSX.Element {
const [ const [referenceElement, setReferenceElement] =
referenceElement, useState<null | HTMLDivElement>(null);
setReferenceElement,
] = useState<null | HTMLDivElement>(null);
const [popperElement, setPopperElement] = useState<null | HTMLDivElement>( const [popperElement, setPopperElement] = useState<null | HTMLDivElement>(
null null
); );

View File

@ -60,10 +60,12 @@ story.add('Knobs Playground', () => {
); );
}); });
([ (
['wide', WidthBreakpoint.Wide], [
['narrow', WidthBreakpoint.Narrow], ['wide', WidthBreakpoint.Wide],
] as const).forEach(([name, containerWidthBreakpoint]) => { ['narrow', WidthBreakpoint.Narrow],
] as const
).forEach(([name, containerWidthBreakpoint]) => {
const defaultPropsForBreakpoint = { const defaultPropsForBreakpoint = {
...defaultProps, ...defaultProps,
containerWidthBreakpoint, containerWidthBreakpoint,

View File

@ -54,10 +54,12 @@ story.add('Knobs Playground', () => {
); );
}); });
([ (
['wide', WidthBreakpoint.Wide], [
['narrow', WidthBreakpoint.Narrow], ['wide', WidthBreakpoint.Wide],
] as const).forEach(([name, containerWidthBreakpoint]) => { ['narrow', WidthBreakpoint.Narrow],
] as const
).forEach(([name, containerWidthBreakpoint]) => {
const defaultPropsForBreakpoint = { const defaultPropsForBreakpoint = {
...defaultProps, ...defaultProps,
containerWidthBreakpoint, containerWidthBreakpoint,

View File

@ -38,9 +38,8 @@ export const DisappearingTimerSelect: React.FC<Props> = (props: Props) => {
}; };
}); });
const isCustomTimeSelected = !expirationTimer.DEFAULT_DURATIONS_SET.has( const isCustomTimeSelected =
value !expirationTimer.DEFAULT_DURATIONS_SET.has(value);
);
const onSelectChange = (newValue: string) => { const onSelectChange = (newValue: string) => {
const intValue = parseInt(newValue, 10); const intValue = parseInt(newValue, 10);

View File

@ -69,9 +69,8 @@ export const GroupCallRemoteParticipant: React.FC<PropsType> = React.memo(
videoAspectRatio, videoAspectRatio,
} = props.remoteParticipant; } = props.remoteParticipant;
const [hasReceivedVideoRecently, setHasReceivedVideoRecently] = useState( const [hasReceivedVideoRecently, setHasReceivedVideoRecently] =
false useState(false);
);
const [isWide, setIsWide] = useState<boolean>( const [isWide, setIsWide] = useState<boolean>(
videoAspectRatio ? videoAspectRatio >= 1 : true videoAspectRatio ? videoAspectRatio >= 1 : true
); );
@ -90,10 +89,8 @@ export const GroupCallRemoteParticipant: React.FC<PropsType> = React.memo(
const remoteVideoRef = useRef<HTMLCanvasElement | null>(null); const remoteVideoRef = useRef<HTMLCanvasElement | null>(null);
const canvasContextRef = useRef<CanvasRenderingContext2D | null>(null); const canvasContextRef = useRef<CanvasRenderingContext2D | null>(null);
const [ const [intersectionRef, intersectionObserverEntry] =
intersectionRef, useIntersectionObserver();
intersectionObserverEntry,
] = useIntersectionObserver();
const isVisible = intersectionObserverEntry const isVisible = intersectionObserverEntry
? intersectionObserverEntry.isIntersecting ? intersectionObserverEntry.isIntersecting
: true; : true;
@ -268,7 +265,8 @@ export const GroupCallRemoteParticipant: React.FC<PropsType> = React.memo(
className={classNames( className={classNames(
'module-ongoing-call__group-call-remote-participant--title', 'module-ongoing-call__group-call-remote-participant--title',
{ {
'module-ongoing-call__group-call-remote-participant--audio-muted': !hasRemoteAudio, 'module-ongoing-call__group-call-remote-participant--audio-muted':
!hasRemoteAudio,
} }
)} )}
> >

View File

@ -135,33 +135,34 @@ export const GroupCallRemoteParticipants: React.FC<PropsType> = ({
), ),
[remoteParticipants] [remoteParticipants]
); );
const gridParticipants: Array<GroupCallRemoteParticipantType> = useMemo(() => { const gridParticipants: Array<GroupCallRemoteParticipantType> =
if (!sortedParticipants.length) { useMemo(() => {
return []; if (!sortedParticipants.length) {
} return [];
}
const candidateParticipants = isInSpeakerView const candidateParticipants = isInSpeakerView
? [sortedParticipants[0]] ? [sortedParticipants[0]]
: sortedParticipants; : sortedParticipants;
// Imagine that we laid out all of the rows end-to-end. That's the maximum total // Imagine that we laid out all of the rows end-to-end. That's the maximum total
// width. So if there were 5 rows and the container was 100px wide, then we can't // width. So if there were 5 rows and the container was 100px wide, then we can't
// possibly fit more than 500px of participants. // possibly fit more than 500px of participants.
const maxTotalWidth = maxRowCount * containerDimensions.width; const maxTotalWidth = maxRowCount * containerDimensions.width;
// We do the same thing for participants, "laying them out end-to-end" until they // We do the same thing for participants, "laying them out end-to-end" until they
// exceed the maximum total width. // exceed the maximum total width.
let totalWidth = 0; let totalWidth = 0;
return takeWhile(candidateParticipants, remoteParticipant => { return takeWhile(candidateParticipants, remoteParticipant => {
totalWidth += remoteParticipant.videoAspectRatio * MIN_RENDERED_HEIGHT; totalWidth += remoteParticipant.videoAspectRatio * MIN_RENDERED_HEIGHT;
return totalWidth < maxTotalWidth; return totalWidth < maxTotalWidth;
}).sort(stableParticipantComparator); }).sort(stableParticipantComparator);
}, [ }, [
containerDimensions.width, containerDimensions.width,
isInSpeakerView, isInSpeakerView,
maxRowCount, maxRowCount,
sortedParticipants, sortedParticipants,
]); ]);
const overflowedParticipants: Array<GroupCallRemoteParticipantType> = useMemo( const overflowedParticipants: Array<GroupCallRemoteParticipantType> = useMemo(
() => () =>
sortedParticipants sortedParticipants

View File

@ -24,8 +24,8 @@ export type HousekeepingPropsType = {
export type PropsType = DataPropsType & HousekeepingPropsType; export type PropsType = DataPropsType & HousekeepingPropsType;
export const GroupV1MigrationDialog: React.FunctionComponent<PropsType> = React.memo( export const GroupV1MigrationDialog: React.FunctionComponent<PropsType> =
(props: PropsType) => { React.memo((props: PropsType) => {
const { const {
areWeInvited, areWeInvited,
droppedMembers, droppedMembers,
@ -94,8 +94,7 @@ export const GroupV1MigrationDialog: React.FunctionComponent<PropsType> = React.
)} )}
</GroupDialog> </GroupDialog>
); );
} });
);
function renderMembers( function renderMembers(
members: Array<ConversationType>, members: Array<ConversationType>,

View File

@ -330,9 +330,8 @@ export const LeftPane: React.FC<PropsType> = ({
const numericIndex = keyboardKeyToNumericIndex(event.key); const numericIndex = keyboardKeyToNumericIndex(event.key);
if (commandOrCtrl && isNumber(numericIndex)) { if (commandOrCtrl && isNumber(numericIndex)) {
conversationToOpen = helper.getConversationAndMessageAtIndex( conversationToOpen =
numericIndex helper.getConversationAndMessageAtIndex(numericIndex);
);
} else { } else {
let toFind: undefined | ToFindType; let toFind: undefined | ToFindType;
if ( if (

View File

@ -63,9 +63,8 @@ export function Lightbox({
selectedIndex: initialSelectedIndex = 0, selectedIndex: initialSelectedIndex = 0,
}: PropsType): JSX.Element | null { }: PropsType): JSX.Element | null {
const [root, setRoot] = React.useState<HTMLElement | undefined>(); const [root, setRoot] = React.useState<HTMLElement | undefined>();
const [selectedIndex, setSelectedIndex] = useState<number>( const [selectedIndex, setSelectedIndex] =
initialSelectedIndex useState<number>(initialSelectedIndex);
);
const [videoElement, setVideoElement] = useState<HTMLVideoElement | null>( const [videoElement, setVideoElement] = useState<HTMLVideoElement | null>(
null null
@ -224,8 +223,13 @@ export function Lightbox({
}; };
}, [onKeyDown]); }, [onKeyDown]);
const { attachment, contentType, loop = false, objectURL, message } = const {
media[selectedIndex] || {}; attachment,
contentType,
loop = false,
objectURL,
message,
} = media[selectedIndex] || {};
const isAttachmentGIF = isGIF(attachment ? [attachment] : undefined); const isAttachmentGIF = isGIF(attachment ? [attachment] : undefined);
@ -251,23 +255,23 @@ export function Lightbox({
() => INITIAL_IMAGE_TRANSFORM () => INITIAL_IMAGE_TRANSFORM
); );
const maxBoundsLimiter = useCallback((x: number, y: number): [ const maxBoundsLimiter = useCallback(
number, (x: number, y: number): [number, number] => {
number const zoomCache = zoomCacheRef.current;
] => {
const zoomCache = zoomCacheRef.current;
if (!zoomCache) { if (!zoomCache) {
return [0, 0]; return [0, 0];
} }
const { maxX, maxY } = zoomCache; const { maxX, maxY } = zoomCache;
const posX = Math.min(maxX, Math.max(-maxX, x)); const posX = Math.min(maxX, Math.max(-maxX, x));
const posY = Math.min(maxY, Math.max(-maxY, y)); const posY = Math.min(maxY, Math.max(-maxY, y));
return [posX, posY]; return [posX, posY];
}, []); },
[]
);
const positionImage = useCallback( const positionImage = useCallback(
(ev: MouseEvent) => { (ev: MouseEvent) => {
@ -352,12 +356,8 @@ export function Lightbox({
screenWidth: window.innerWidth, screenWidth: window.innerWidth,
}; };
const { const { height, left, top, width } =
height, animateNode.getBoundingClientRect();
left,
top,
width,
} = animateNode.getBoundingClientRect();
const offsetX = ev.clientX - left - width / 2; const offsetX = ev.clientX - left - width / 2;
const offsetY = ev.clientY - top - height / 2; const offsetY = ev.clientY - top - height / 2;

View File

@ -133,7 +133,8 @@ export const MediaQualitySelector = ({
<div <div
className={classNames({ className={classNames({
'MediaQualitySelector__option--checkmark': true, 'MediaQualitySelector__option--checkmark': true,
'MediaQualitySelector__option--selected': !isHighQuality, 'MediaQualitySelector__option--selected':
!isHighQuality,
})} })}
/> />
<div> <div>

View File

@ -17,67 +17,68 @@ type PropsType = {
onClose: () => void; onClose: () => void;
}; };
export const NewlyCreatedGroupInvitedContactsDialog: FunctionComponent<PropsType> = ({ export const NewlyCreatedGroupInvitedContactsDialog: FunctionComponent<PropsType> =
contacts, ({ contacts, i18n, onClose }) => {
i18n, let title: string;
onClose, let body: ReactNode;
}) => { if (contacts.length === 1) {
let title: string; const contact = contacts[0];
let body: ReactNode;
if (contacts.length === 1) {
const contact = contacts[0];
title = i18n('NewlyCreatedGroupInvitedContactsDialog--title--one'); title = i18n('NewlyCreatedGroupInvitedContactsDialog--title--one');
body = ( body = (
<> <>
<GroupDialog.Paragraph> <GroupDialog.Paragraph>
<Intl <Intl
i18n={i18n} i18n={i18n}
id="NewlyCreatedGroupInvitedContactsDialog--body--user-paragraph--one" id="NewlyCreatedGroupInvitedContactsDialog--body--user-paragraph--one"
components={[<ContactName title={contact.title} />]} components={[<ContactName title={contact.title} />]}
/> />
</GroupDialog.Paragraph> </GroupDialog.Paragraph>
<GroupDialog.Paragraph> <GroupDialog.Paragraph>
{i18n('NewlyCreatedGroupInvitedContactsDialog--body--info-paragraph')} {i18n(
</GroupDialog.Paragraph> 'NewlyCreatedGroupInvitedContactsDialog--body--info-paragraph'
</> )}
); </GroupDialog.Paragraph>
} else { </>
title = i18n('NewlyCreatedGroupInvitedContactsDialog--title--many', [ );
contacts.length.toString(), } else {
]); title = i18n('NewlyCreatedGroupInvitedContactsDialog--title--many', [
body = ( contacts.length.toString(),
<> ]);
<GroupDialog.Paragraph> body = (
{i18n( <>
'NewlyCreatedGroupInvitedContactsDialog--body--user-paragraph--many' <GroupDialog.Paragraph>
)} {i18n(
</GroupDialog.Paragraph> 'NewlyCreatedGroupInvitedContactsDialog--body--user-paragraph--many'
<GroupDialog.Paragraph> )}
{i18n('NewlyCreatedGroupInvitedContactsDialog--body--info-paragraph')} </GroupDialog.Paragraph>
</GroupDialog.Paragraph> <GroupDialog.Paragraph>
<GroupDialog.Contacts contacts={contacts} i18n={i18n} /> {i18n(
</> 'NewlyCreatedGroupInvitedContactsDialog--body--info-paragraph'
); )}
} </GroupDialog.Paragraph>
<GroupDialog.Contacts contacts={contacts} i18n={i18n} />
</>
);
}
return ( return (
<GroupDialog <GroupDialog
i18n={i18n} i18n={i18n}
onClickPrimaryButton={onClose} onClickPrimaryButton={onClose}
primaryButtonText={i18n('Confirmation--confirm')} primaryButtonText={i18n('Confirmation--confirm')}
secondaryButtonText={i18n( secondaryButtonText={i18n(
'NewlyCreatedGroupInvitedContactsDialog--body--learn-more' 'NewlyCreatedGroupInvitedContactsDialog--body--learn-more'
)} )}
onClickSecondaryButton={() => { onClickSecondaryButton={() => {
openLinkInWebBrowser( openLinkInWebBrowser(
'https://support.signal.org/hc/articles/360007319331-Group-chats' 'https://support.signal.org/hc/articles/360007319331-Group-chats'
); );
}} }}
onClose={onClose} onClose={onClose}
title={title} title={title}
> >
{body} {body}
</GroupDialog> </GroupDialog>
); );
}; };

View File

@ -267,10 +267,8 @@ export const Preferences = ({
const [page, setPage] = useState<Page>(Page.General); const [page, setPage] = useState<Page>(Page.General);
const [showSyncFailed, setShowSyncFailed] = useState(false); const [showSyncFailed, setShowSyncFailed] = useState(false);
const [nowSyncing, setNowSyncing] = useState(false); const [nowSyncing, setNowSyncing] = useState(false);
const [ const [showDisappearingTimerDialog, setShowDisappearingTimerDialog] =
showDisappearingTimerDialog, useState(false);
setShowDisappearingTimerDialog,
] = useState(false);
useEffect(() => { useEffect(() => {
doneRendering(); doneRendering();
@ -281,7 +279,7 @@ export const Preferences = ({
const onZoomSelectChange = useCallback( const onZoomSelectChange = useCallback(
(value: string) => { (value: string) => {
const number = parseFloat(value); const number = parseFloat(value);
onZoomFactorChange((number as unknown) as ZoomFactorType); onZoomFactorChange(number as unknown as ZoomFactorType);
}, },
[onZoomFactorChange] [onZoomFactorChange]
); );
@ -764,9 +762,8 @@ export const Preferences = ({
</> </>
); );
} else if (page === Page.Privacy) { } else if (page === Page.Privacy) {
const isCustomDisappearingMessageValue = !DEFAULT_DURATIONS_SET.has( const isCustomDisappearingMessageValue =
universalExpireTimer !DEFAULT_DURATIONS_SET.has(universalExpireTimer);
);
settings = ( settings = (
<> <>

View File

@ -57,281 +57,286 @@ enum ConfirmationStateType {
ConfirmingGroupRemoval, ConfirmingGroupRemoval,
} }
export const ContactSpoofingReviewDialog: FunctionComponent<PropsType> = props => { export const ContactSpoofingReviewDialog: FunctionComponent<PropsType> =
const { props => {
i18n, const {
onBlock, i18n,
onBlockAndReportSpam, onBlock,
onClose, onBlockAndReportSpam,
onDelete, onClose,
onShowContactModal, onDelete,
onUnblock, onShowContactModal,
removeMember, onUnblock,
} = props; removeMember,
} = props;
const [confirmationState, setConfirmationState] = useState< const [confirmationState, setConfirmationState] = useState<
| undefined | undefined
| { | {
type: ConfirmationStateType; type: ConfirmationStateType;
affectedConversation: ConversationType; affectedConversation: ConversationType;
} }
>(); >();
if (confirmationState) { if (confirmationState) {
const { affectedConversation, type } = confirmationState; const { affectedConversation, type } = confirmationState;
switch (type) { switch (type) {
case ConfirmationStateType.ConfirmingDelete: case ConfirmationStateType.ConfirmingDelete:
case ConfirmationStateType.ConfirmingBlock: case ConfirmationStateType.ConfirmingBlock:
return ( return (
<MessageRequestActionsConfirmation <MessageRequestActionsConfirmation
i18n={i18n} i18n={i18n}
onBlock={() => { onBlock={() => {
onBlock(affectedConversation.id); onBlock(affectedConversation.id);
}} }}
onBlockAndReportSpam={() => { onBlockAndReportSpam={() => {
onBlockAndReportSpam(affectedConversation.id); onBlockAndReportSpam(affectedConversation.id);
}} }}
onUnblock={() => { onUnblock={() => {
onUnblock(affectedConversation.id); onUnblock(affectedConversation.id);
}} }}
onDelete={() => { onDelete={() => {
onDelete(affectedConversation.id); onDelete(affectedConversation.id);
}} }}
title={affectedConversation.title} title={affectedConversation.title}
conversationType="direct" conversationType="direct"
state={ state={
type === ConfirmationStateType.ConfirmingDelete type === ConfirmationStateType.ConfirmingDelete
? MessageRequestState.deleting ? MessageRequestState.deleting
: MessageRequestState.blocking : MessageRequestState.blocking
}
onChangeState={messageRequestState => {
switch (messageRequestState) {
case MessageRequestState.blocking:
setConfirmationState({
type: ConfirmationStateType.ConfirmingBlock,
affectedConversation,
});
break;
case MessageRequestState.deleting:
setConfirmationState({
type: ConfirmationStateType.ConfirmingDelete,
affectedConversation,
});
break;
case MessageRequestState.unblocking:
assert(
false,
'Got unexpected MessageRequestState.unblocking state. Clearing confiration state'
);
setConfirmationState(undefined);
break;
case MessageRequestState.default:
setConfirmationState(undefined);
break;
default:
throw missingCaseError(messageRequestState);
} }
}} onChangeState={messageRequestState => {
/> switch (messageRequestState) {
); case MessageRequestState.blocking:
case ConfirmationStateType.ConfirmingGroupRemoval: setConfirmationState({
return ( type: ConfirmationStateType.ConfirmingBlock,
<RemoveGroupMemberConfirmationDialog affectedConversation,
conversation={affectedConversation} });
i18n={i18n} break;
onClose={() => { case MessageRequestState.deleting:
setConfirmationState(undefined); setConfirmationState({
}} type: ConfirmationStateType.ConfirmingDelete,
onRemove={() => { affectedConversation,
removeMember(affectedConversation.id); });
}} break;
/> case MessageRequestState.unblocking:
); assert(
default: false,
throw missingCaseError(type); 'Got unexpected MessageRequestState.unblocking state. Clearing confiration state'
);
setConfirmationState(undefined);
break;
case MessageRequestState.default:
setConfirmationState(undefined);
break;
default:
throw missingCaseError(messageRequestState);
}
}}
/>
);
case ConfirmationStateType.ConfirmingGroupRemoval:
return (
<RemoveGroupMemberConfirmationDialog
conversation={affectedConversation}
i18n={i18n}
onClose={() => {
setConfirmationState(undefined);
}}
onRemove={() => {
removeMember(affectedConversation.id);
}}
/>
);
default:
throw missingCaseError(type);
}
} }
}
let title: string; let title: string;
let contents: ReactChild; let contents: ReactChild;
switch (props.type) { switch (props.type) {
case ContactSpoofingType.DirectConversationWithSameTitle: { case ContactSpoofingType.DirectConversationWithSameTitle: {
const { possiblyUnsafeConversation, safeConversation } = props; const { possiblyUnsafeConversation, safeConversation } = props;
assert( assert(
possiblyUnsafeConversation.type === 'direct', possiblyUnsafeConversation.type === 'direct',
'<ContactSpoofingReviewDialog> expected a direct conversation for the "possibly unsafe" conversation' '<ContactSpoofingReviewDialog> expected a direct conversation for the "possibly unsafe" conversation'
); );
assert( assert(
safeConversation.type === 'direct', safeConversation.type === 'direct',
'<ContactSpoofingReviewDialog> expected a direct conversation for the "safe" conversation' '<ContactSpoofingReviewDialog> expected a direct conversation for the "safe" conversation'
); );
title = i18n('ContactSpoofingReviewDialog__title'); title = i18n('ContactSpoofingReviewDialog__title');
contents = ( contents = (
<> <>
<p>{i18n('ContactSpoofingReviewDialog__description')}</p> <p>{i18n('ContactSpoofingReviewDialog__description')}</p>
<h2>{i18n('ContactSpoofingReviewDialog__possibly-unsafe-title')}</h2> <h2>
<ContactSpoofingReviewDialogPerson {i18n('ContactSpoofingReviewDialog__possibly-unsafe-title')}
conversation={possiblyUnsafeConversation} </h2>
i18n={i18n} <ContactSpoofingReviewDialogPerson
> conversation={possiblyUnsafeConversation}
<div className="module-ContactSpoofingReviewDialog__buttons"> i18n={i18n}
<Button >
variant={ButtonVariant.SecondaryDestructive} <div className="module-ContactSpoofingReviewDialog__buttons">
onClick={() => {
setConfirmationState({
type: ConfirmationStateType.ConfirmingDelete,
affectedConversation: possiblyUnsafeConversation,
});
}}
>
{i18n('MessageRequests--delete')}
</Button>
<Button
variant={ButtonVariant.SecondaryDestructive}
onClick={() => {
setConfirmationState({
type: ConfirmationStateType.ConfirmingBlock,
affectedConversation: possiblyUnsafeConversation,
});
}}
>
{i18n('MessageRequests--block')}
</Button>
</div>
</ContactSpoofingReviewDialogPerson>
<hr />
<h2>{i18n('ContactSpoofingReviewDialog__safe-title')}</h2>
<ContactSpoofingReviewDialogPerson
conversation={safeConversation}
i18n={i18n}
onClick={() => {
onShowContactModal(safeConversation.id);
}}
/>
</>
);
break;
}
case ContactSpoofingType.MultipleGroupMembersWithSameTitle: {
const { areWeAdmin, collisionInfoByTitle } = props;
const unsortedConversationInfos = concat(
// This empty array exists to appease Lodash's type definitions.
[],
...Object.values(collisionInfoByTitle)
);
const conversationInfos = orderBy(unsortedConversationInfos, [
// We normally use an `Intl.Collator` to sort by title. We do this instead, as we
// only really care about stability (not perfect ordering).
'title',
'id',
]);
title = i18n('ContactSpoofingReviewDialog__group__title');
contents = (
<>
<p>
{i18n('ContactSpoofingReviewDialog__group__description', [
conversationInfos.length.toString(),
])}
</p>
<h2>{i18n('ContactSpoofingReviewDialog__group__members-header')}</h2>
{conversationInfos.map((conversationInfo, index) => {
let button: ReactNode;
if (areWeAdmin) {
button = (
<Button <Button
variant={ButtonVariant.SecondaryAffirmative} variant={ButtonVariant.SecondaryDestructive}
onClick={() => { onClick={() => {
setConfirmationState({ setConfirmationState({
type: ConfirmationStateType.ConfirmingGroupRemoval, type: ConfirmationStateType.ConfirmingDelete,
affectedConversation: conversationInfo.conversation, affectedConversation: possiblyUnsafeConversation,
}); });
}} }}
> >
{i18n('RemoveGroupMemberConfirmation__remove-button')} {i18n('MessageRequests--delete')}
</Button> </Button>
);
} else if (conversationInfo.conversation.isBlocked) {
button = (
<Button
variant={ButtonVariant.SecondaryAffirmative}
onClick={() => {
onUnblock(conversationInfo.conversation.id);
}}
>
{i18n('MessageRequests--unblock')}
</Button>
);
} else if (!isInSystemContacts(conversationInfo.conversation)) {
button = (
<Button <Button
variant={ButtonVariant.SecondaryDestructive} variant={ButtonVariant.SecondaryDestructive}
onClick={() => { onClick={() => {
setConfirmationState({ setConfirmationState({
type: ConfirmationStateType.ConfirmingBlock, type: ConfirmationStateType.ConfirmingBlock,
affectedConversation: conversationInfo.conversation, affectedConversation: possiblyUnsafeConversation,
}); });
}} }}
> >
{i18n('MessageRequests--block')} {i18n('MessageRequests--block')}
</Button> </Button>
</div>
</ContactSpoofingReviewDialogPerson>
<hr />
<h2>{i18n('ContactSpoofingReviewDialog__safe-title')}</h2>
<ContactSpoofingReviewDialogPerson
conversation={safeConversation}
i18n={i18n}
onClick={() => {
onShowContactModal(safeConversation.id);
}}
/>
</>
);
break;
}
case ContactSpoofingType.MultipleGroupMembersWithSameTitle: {
const { areWeAdmin, collisionInfoByTitle } = props;
const unsortedConversationInfos = concat(
// This empty array exists to appease Lodash's type definitions.
[],
...Object.values(collisionInfoByTitle)
);
const conversationInfos = orderBy(unsortedConversationInfos, [
// We normally use an `Intl.Collator` to sort by title. We do this instead, as
// we only really care about stability (not perfect ordering).
'title',
'id',
]);
title = i18n('ContactSpoofingReviewDialog__group__title');
contents = (
<>
<p>
{i18n('ContactSpoofingReviewDialog__group__description', [
conversationInfos.length.toString(),
])}
</p>
<h2>
{i18n('ContactSpoofingReviewDialog__group__members-header')}
</h2>
{conversationInfos.map((conversationInfo, index) => {
let button: ReactNode;
if (areWeAdmin) {
button = (
<Button
variant={ButtonVariant.SecondaryAffirmative}
onClick={() => {
setConfirmationState({
type: ConfirmationStateType.ConfirmingGroupRemoval,
affectedConversation: conversationInfo.conversation,
});
}}
>
{i18n('RemoveGroupMemberConfirmation__remove-button')}
</Button>
);
} else if (conversationInfo.conversation.isBlocked) {
button = (
<Button
variant={ButtonVariant.SecondaryAffirmative}
onClick={() => {
onUnblock(conversationInfo.conversation.id);
}}
>
{i18n('MessageRequests--unblock')}
</Button>
);
} else if (!isInSystemContacts(conversationInfo.conversation)) {
button = (
<Button
variant={ButtonVariant.SecondaryDestructive}
onClick={() => {
setConfirmationState({
type: ConfirmationStateType.ConfirmingBlock,
affectedConversation: conversationInfo.conversation,
});
}}
>
{i18n('MessageRequests--block')}
</Button>
);
}
const { oldName } = conversationInfo;
const newName =
conversationInfo.conversation.profileName ||
conversationInfo.conversation.title;
return (
<>
{index !== 0 && <hr />}
<ContactSpoofingReviewDialogPerson
key={conversationInfo.conversation.id}
conversation={conversationInfo.conversation}
i18n={i18n}
>
{Boolean(oldName) && oldName !== newName && (
<div className="module-ContactSpoofingReviewDialogPerson__info__property module-ContactSpoofingReviewDialogPerson__info__property--callout">
<Intl
i18n={i18n}
id="ContactSpoofingReviewDialog__group__name-change-info"
components={{
oldName: <Emojify text={oldName} />,
newName: <Emojify text={newName} />,
}}
/>
</div>
)}
{button && (
<div className="module-ContactSpoofingReviewDialog__buttons">
{button}
</div>
)}
</ContactSpoofingReviewDialogPerson>
</>
); );
} })}
</>
const { oldName } = conversationInfo; );
const newName = break;
conversationInfo.conversation.profileName || }
conversationInfo.conversation.title; default:
throw missingCaseError(props);
return (
<>
{index !== 0 && <hr />}
<ContactSpoofingReviewDialogPerson
key={conversationInfo.conversation.id}
conversation={conversationInfo.conversation}
i18n={i18n}
>
{Boolean(oldName) && oldName !== newName && (
<div className="module-ContactSpoofingReviewDialogPerson__info__property module-ContactSpoofingReviewDialogPerson__info__property--callout">
<Intl
i18n={i18n}
id="ContactSpoofingReviewDialog__group__name-change-info"
components={{
oldName: <Emojify text={oldName} />,
newName: <Emojify text={newName} />,
}}
/>
</div>
)}
{button && (
<div className="module-ContactSpoofingReviewDialog__buttons">
{button}
</div>
)}
</ContactSpoofingReviewDialogPerson>
</>
);
})}
</>
);
break;
} }
default:
throw missingCaseError(props);
}
return ( return (
<Modal <Modal
hasXButton hasXButton
i18n={i18n} i18n={i18n}
moduleClassName="module-ContactSpoofingReviewDialog" moduleClassName="module-ContactSpoofingReviewDialog"
onClose={onClose} onClose={onClose}
title={title} title={title}
> >
{contents} {contents}
</Modal> </Modal>
); );
}; };

View File

@ -19,60 +19,56 @@ type PropsType = {
onClick?: () => void; onClick?: () => void;
}; };
export const ContactSpoofingReviewDialogPerson: FunctionComponent<PropsType> = ({ export const ContactSpoofingReviewDialogPerson: FunctionComponent<PropsType> =
children, ({ children, conversation, i18n, onClick }) => {
conversation, assert(
i18n, conversation.type === 'direct',
onClick, '<ContactSpoofingReviewDialogPerson> expected a direct conversation'
}) => {
assert(
conversation.type === 'direct',
'<ContactSpoofingReviewDialogPerson> expected a direct conversation'
);
const contents = (
<>
<Avatar
{...conversation}
conversationType={conversation.type}
size={AvatarSize.FIFTY_TWO}
className="module-ContactSpoofingReviewDialogPerson__avatar"
i18n={i18n}
/>
<div className="module-ContactSpoofingReviewDialogPerson__info">
<ContactName
module="module-ContactSpoofingReviewDialogPerson__info__contact-name"
title={conversation.title}
/>
{conversation.phoneNumber ? (
<div className="module-ContactSpoofingReviewDialogPerson__info__property">
{conversation.phoneNumber}
</div>
) : null}
<div className="module-ContactSpoofingReviewDialogPerson__info__property">
<SharedGroupNames
i18n={i18n}
sharedGroupNames={conversation.sharedGroupNames || []}
/>
</div>
{children}
</div>
</>
);
if (onClick) {
return (
<button
type="button"
className="module-ContactSpoofingReviewDialogPerson"
onClick={onClick}
>
{contents}
</button>
); );
}
return ( const contents = (
<div className="module-ContactSpoofingReviewDialogPerson">{contents}</div> <>
); <Avatar
}; {...conversation}
conversationType={conversation.type}
size={AvatarSize.FIFTY_TWO}
className="module-ContactSpoofingReviewDialogPerson__avatar"
i18n={i18n}
/>
<div className="module-ContactSpoofingReviewDialogPerson__info">
<ContactName
module="module-ContactSpoofingReviewDialogPerson__info__contact-name"
title={conversation.title}
/>
{conversation.phoneNumber ? (
<div className="module-ContactSpoofingReviewDialogPerson__info__property">
{conversation.phoneNumber}
</div>
) : null}
<div className="module-ContactSpoofingReviewDialogPerson__info__property">
<SharedGroupNames
i18n={i18n}
sharedGroupNames={conversation.sharedGroupNames || []}
/>
</div>
{children}
</div>
</>
);
if (onClick) {
return (
<button
type="button"
className="module-ContactSpoofingReviewDialogPerson"
onClick={onClick}
>
{contents}
</button>
);
}
return (
<div className="module-ContactSpoofingReviewDialogPerson">{contents}</div>
);
};

View File

@ -523,12 +523,8 @@ export class ConversationHeader extends React.Component<PropsType, StateType> {
} }
private renderHeader(): ReactNode { private renderHeader(): ReactNode {
const { const { conversationTitle, groupVersion, onShowConversationDetails, type } =
conversationTitle, this.props;
groupVersion,
onShowConversationDetails,
type,
} = this.props;
if (conversationTitle !== undefined) { if (conversationTitle !== undefined) {
return ( return (
@ -592,13 +588,8 @@ export class ConversationHeader extends React.Component<PropsType, StateType> {
} }
public render(): ReactNode { public render(): ReactNode {
const { const { id, isSMSOnly, i18n, onSetDisappearingMessages, expireTimer } =
id, this.props;
isSMSOnly,
i18n,
onSetDisappearingMessages,
expireTimer,
} = this.props;
const { isNarrow, modalState } = this.state; const { isNarrow, modalState } = this.state;
const triggerId = `conversation-${id}`; const triggerId = `conversation-${id}`;

View File

@ -118,10 +118,8 @@ export const ConversationHero = ({
}: Props): JSX.Element => { }: Props): JSX.Element => {
const firstRenderRef = useRef(true); const firstRenderRef = useRef(true);
const [ const [isShowingMessageRequestWarning, setIsShowingMessageRequestWarning] =
isShowingMessageRequestWarning, useState(false);
setIsShowingMessageRequestWarning,
] = useState(false);
const closeMessageRequestWarning = () => { const closeMessageRequestWarning = () => {
setIsShowingMessageRequestWarning(false); setIsShowingMessageRequestWarning(false);
}; };

View File

@ -26,8 +26,7 @@ story.add('Default', () => <GroupDescription {...createProps()} />);
story.add('Long', () => ( story.add('Long', () => (
<GroupDescription <GroupDescription
{...createProps({ {...createProps({
text: text: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas sed vehicula urna. Ut rhoncus, justo a vestibulum elementum, libero ligula molestie massa, et volutpat nibh ipsum sit amet enim. Vestibulum ac mi enim. Nulla fringilla justo justo, volutpat semper ex convallis quis. Proin posuere, mi at auctor tincidunt, magna turpis mattis nibh, ullamcorper vehicula lectus mauris in mauris. Nullam blandit sapien tortor, quis vehicula quam molestie nec. Nam sagittis dolor in eros dapibus scelerisque. Proin vitae ex sed magna lobortis tincidunt. Aenean dictum laoreet dolor, at suscipit ligula fermentum ac. Nam condimentum turpis quis sollicitudin rhoncus.',
'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas sed vehicula urna. Ut rhoncus, justo a vestibulum elementum, libero ligula molestie massa, et volutpat nibh ipsum sit amet enim. Vestibulum ac mi enim. Nulla fringilla justo justo, volutpat semper ex convallis quis. Proin posuere, mi at auctor tincidunt, magna turpis mattis nibh, ullamcorper vehicula lectus mauris in mauris. Nullam blandit sapien tortor, quis vehicula quam molestie nec. Nam sagittis dolor in eros dapibus scelerisque. Proin vitae ex sed magna lobortis tincidunt. Aenean dictum laoreet dolor, at suscipit ligula fermentum ac. Nam condimentum turpis quis sollicitudin rhoncus.',
})} })}
/> />
)); ));
@ -51,8 +50,7 @@ story.add('With emoji', () => (
story.add('With link', () => ( story.add('With link', () => (
<GroupDescription <GroupDescription
{...createProps({ {...createProps({
text: text: 'I love https://example.com and http://example.com and example.com, but not https://user:bar@example.com',
'I love https://example.com and http://example.com and example.com, but not https://user:bar@example.com',
})} })}
/> />
)); ));
@ -60,8 +58,7 @@ story.add('With link', () => (
story.add('Kitchen sink', () => ( story.add('Kitchen sink', () => (
<GroupDescription <GroupDescription
{...createProps({ {...createProps({
text: text: '🍒 https://example.com this is a long thing\nhttps://example.com on another line\nhttps://example.com',
'🍒 https://example.com this is a long thing\nhttps://example.com on another line\nhttps://example.com',
})} })}
/> />
)); ));

View File

@ -26,8 +26,7 @@ story.add('Only Link', () => {
story.add('Links with Text', () => { story.add('Links with Text', () => {
const props = createProps({ const props = createProps({
text: text: 'you should see this: https://www.signal.org - it is good. Also: https://placekitten.com!',
'you should see this: https://www.signal.org - it is good. Also: https://placekitten.com!',
}); });
return <Linkify {...props} />; return <Linkify {...props} />;
@ -43,8 +42,7 @@ story.add('Links with Emoji without space', () => {
story.add('Links with Emoji and Text', () => { story.add('Links with Emoji and Text', () => {
const props = createProps({ const props = createProps({
text: text: 'https://example.com ⚠️ 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ https://example.com',
'https://example.com ⚠️ 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ https://example.com',
}); });
return <Linkify {...props} />; return <Linkify {...props} />;
@ -60,8 +58,7 @@ story.add('No Link', () => {
story.add('Blocked Protocols', () => { story.add('Blocked Protocols', () => {
const props = createProps({ const props = createProps({
text: text: 'smailto:someone@somewhere.com - ftp://something.com - //local/share - \\localshare',
'smailto:someone@somewhere.com - ftp://something.com - //local/share - \\localshare',
}); });
return <Linkify {...props} />; return <Linkify {...props} />;
@ -69,8 +66,7 @@ story.add('Blocked Protocols', () => {
story.add('Missing protocols', () => { story.add('Missing protocols', () => {
const props = createProps({ const props = createProps({
text: text: 'I love example.com. I also love кц.рф. I also love مثال.تونس. But I do not love test.example.',
'I love example.com. I also love кц.рф. I also love مثال.تونس. But I do not love test.example.',
}); });
return <Linkify {...props} />; return <Linkify {...props} />;
@ -78,8 +74,7 @@ story.add('Missing protocols', () => {
story.add('Custom Text Render', () => { story.add('Custom Text Render', () => {
const props = createProps({ const props = createProps({
text: text: 'you should see this: https://www.signal.org - it is good. Also: https://placekitten.com!',
'you should see this: https://www.signal.org - it is good. Also: https://placekitten.com!',
renderNonLink: ({ text: theText, key }) => ( renderNonLink: ({ text: theText, key }) => (
<div key={key} style={{ backgroundColor: 'aquamarine' }}> <div key={key} style={{ backgroundColor: 'aquamarine' }}>
{theText} {theText}

View File

@ -188,8 +188,7 @@ const renderBothDirections = (props: Props) => (
story.add('Plain Message', () => { story.add('Plain Message', () => {
const props = createProps({ const props = createProps({
text: text: 'Hello there from a pal! I am sending a long message so that it will wrap a bit, since I like that look.',
'Hello there from a pal! I am sending a long message so that it will wrap a bit, since I like that look.',
}); });
return renderBothDirections(props); return renderBothDirections(props);
@ -296,8 +295,7 @@ story.add('Delivered', () => {
const props = createProps({ const props = createProps({
direction: 'outgoing', direction: 'outgoing',
status: 'delivered', status: 'delivered',
text: text: 'Hello there from a pal! I am sending a long message so that it will wrap a bit, since I like that look.',
'Hello there from a pal! I am sending a long message so that it will wrap a bit, since I like that look.',
}); });
return <Message {...props} />; return <Message {...props} />;
@ -307,8 +305,7 @@ story.add('Read', () => {
const props = createProps({ const props = createProps({
direction: 'outgoing', direction: 'outgoing',
status: 'read', status: 'read',
text: text: 'Hello there from a pal! I am sending a long message so that it will wrap a bit, since I like that look.',
'Hello there from a pal! I am sending a long message so that it will wrap a bit, since I like that look.',
}); });
return <Message {...props} />; return <Message {...props} />;
@ -318,8 +315,7 @@ story.add('Sending', () => {
const props = createProps({ const props = createProps({
direction: 'outgoing', direction: 'outgoing',
status: 'sending', status: 'sending',
text: text: 'Hello there from a pal! I am sending a long message so that it will wrap a bit, since I like that look.',
'Hello there from a pal! I am sending a long message so that it will wrap a bit, since I like that look.',
}); });
return <Message {...props} />; return <Message {...props} />;
@ -329,8 +325,7 @@ story.add('Expiring', () => {
const props = createProps({ const props = createProps({
expirationLength: 30 * 1000, expirationLength: 30 * 1000,
expirationTimestamp: Date.now() + 30 * 1000, expirationTimestamp: Date.now() + 30 * 1000,
text: text: 'Hello there from a pal! I am sending a long message so that it will wrap a bit, since I like that look.',
'Hello there from a pal! I am sending a long message so that it will wrap a bit, since I like that look.',
}); });
return renderBothDirections(props); return renderBothDirections(props);
@ -338,8 +333,7 @@ story.add('Expiring', () => {
story.add('Pending', () => { story.add('Pending', () => {
const props = createProps({ const props = createProps({
text: text: 'Hello there from a pal! I am sending a long message so that it will wrap a bit, since I like that look.',
'Hello there from a pal! I am sending a long message so that it will wrap a bit, since I like that look.',
textPending: true, textPending: true,
}); });

View File

@ -296,7 +296,8 @@ export class Message extends React.PureComponent<Props, State> {
public audioButtonRef: React.RefObject<HTMLButtonElement> = React.createRef(); public audioButtonRef: React.RefObject<HTMLButtonElement> = React.createRef();
public reactionsContainerRef: React.RefObject<HTMLDivElement> = React.createRef(); public reactionsContainerRef: React.RefObject<HTMLDivElement> =
React.createRef();
public reactionsContainerRefMerger = createRefMerger(); public reactionsContainerRefMerger = createRefMerger();
@ -1194,7 +1195,8 @@ export class Message extends React.PureComponent<Props, State> {
return ( return (
<div <div
className={classNames('module-message__author-avatar-container', { className={classNames('module-message__author-avatar-container', {
'module-message__author-avatar-container--with-reactions': this.hasReactions(), 'module-message__author-avatar-container--with-reactions':
this.hasReactions(),
})} })}
> >
<Avatar <Avatar
@ -2405,14 +2407,8 @@ export class Message extends React.PureComponent<Props, State> {
} }
public render(): JSX.Element | null { public render(): JSX.Element | null {
const { const { author, attachments, direction, id, isSticker, timestamp } =
author, this.props;
attachments,
direction,
id,
isSticker,
timestamp,
} = this.props;
const { expired, expiring, imageBroken, isSelected } = this.state; const { expired, expiring, imageBroken, isSelected } = this.state;
// This id is what connects our triple-dot click with our associated pop-up menu. // This id is what connects our triple-dot click with our associated pop-up menu.

View File

@ -107,8 +107,7 @@ story.add('@Mention', () => {
replacementText: 'Bender B Rodriguez 🤖', replacementText: 'Bender B Rodriguez 🤖',
}, },
], ],
text: text: 'Like \uFFFC once said: My story is a lot like yours, only more interesting because it involves robots',
'Like \uFFFC once said: My story is a lot like yours, only more interesting because it involves robots',
}); });
return <MessageBody {...props} />; return <MessageBody {...props} />;
@ -167,8 +166,7 @@ story.add('Complex MessageBody', () => {
}, },
], ],
direction: 'outgoing', direction: 'outgoing',
text: text: 'Hey \uFFFC\nCheck out https://www.signal.org I think you will really like it 😍\n\ncc \uFFFC \uFFFC',
'Hey \uFFFC\nCheck out https://www.signal.org I think you will really like it 😍\n\ncc \uFFFC \uFFFC',
}); });
return <MessageBody {...props} />; return <MessageBody {...props} />;

View File

@ -85,7 +85,8 @@ export const MessageMetadata: FunctionComponent<PropsType> = props => {
'module-message__metadata__date': true, 'module-message__metadata__date': true,
'module-message__metadata__date--with-sticker': isSticker, 'module-message__metadata__date--with-sticker': isSticker,
[`module-message__metadata__date--${direction}`]: !isSticker, [`module-message__metadata__date--${direction}`]: !isSticker,
'module-message__metadata__date--with-image-no-caption': withImageNoCaption, 'module-message__metadata__date--with-image-no-caption':
withImageNoCaption,
})} })}
> >
{statusInfo} {statusInfo}

View File

@ -127,10 +127,8 @@ export class Quote extends React.Component<Props, State> {
} }
componentDidMount(): void { componentDidMount(): void {
const { const { doubleCheckMissingQuoteReference, referencedMessageNotFound } =
doubleCheckMissingQuoteReference, this.props;
referencedMessageNotFound,
} = this.props;
if (referencedMessageNotFound) { if (referencedMessageNotFound) {
doubleCheckMissingQuoteReference?.(); doubleCheckMissingQuoteReference?.();
@ -275,14 +273,8 @@ export class Quote extends React.Component<Props, State> {
} }
public renderText(): JSX.Element | null { public renderText(): JSX.Element | null {
const { const { bodyRanges, i18n, text, rawAttachment, isIncoming, isViewOnce } =
bodyRanges, this.props;
i18n,
text,
rawAttachment,
isIncoming,
isViewOnce,
} = this.props;
if (text) { if (text) {
const quoteText = bodyRanges const quoteText = bodyRanges

View File

@ -123,10 +123,8 @@ export const ReactionViewer = React.forwardRef<HTMLDivElement, Props>(
[reactionsWithEmojiData, groupedAndSortedReactions] [reactionsWithEmojiData, groupedAndSortedReactions]
); );
const [ const [selectedReactionCategory, setSelectedReactionCategory] =
selectedReactionCategory, React.useState(pickedReaction || 'all');
setSelectedReactionCategory,
] = React.useState(pickedReaction || 'all');
// Handle escape key // Handle escape key
useEscapeHandling(onClose); useEscapeHandling(onClose);

View File

@ -18,30 +18,26 @@ type PropsType = {
onRemove: () => void; onRemove: () => void;
}; };
export const RemoveGroupMemberConfirmationDialog: FunctionComponent<PropsType> = ({ export const RemoveGroupMemberConfirmationDialog: FunctionComponent<PropsType> =
conversation, ({ conversation, i18n, onClose, onRemove }) => (
i18n, <ConfirmationDialog
onClose, actions={[
onRemove, {
}) => ( action: onRemove,
<ConfirmationDialog text: i18n('RemoveGroupMemberConfirmation__remove-button'),
actions={[ style: 'negative',
{ },
action: onRemove, ]}
text: i18n('RemoveGroupMemberConfirmation__remove-button'), i18n={i18n}
style: 'negative', onClose={onClose}
}, title={
]} <Intl
i18n={i18n} i18n={i18n}
onClose={onClose} id="RemoveGroupMemberConfirmation__description"
title={ components={{
<Intl name: <ContactName title={conversation.title} />,
i18n={i18n} }}
id="RemoveGroupMemberConfirmation__description" />
components={{ }
name: <ContactName title={conversation.title} />, />
}} );
/>
}
/>
);

View File

@ -263,8 +263,7 @@ const items: Record<string, TimelineItemType> = {
previews: [], previews: [],
readStatus: ReadStatus.Read, readStatus: ReadStatus.Read,
status: 'sent', status: 'sent',
text: text: 'Hello there from the new world! And this is multiple lines of text. Lines and lines and lines.',
'Hello there from the new world! And this is multiple lines of text. Lines and lines and lines.',
timestamp: Date.now(), timestamp: Date.now(),
}, },
}, },
@ -285,8 +284,7 @@ const items: Record<string, TimelineItemType> = {
previews: [], previews: [],
readStatus: ReadStatus.Read, readStatus: ReadStatus.Read,
status: 'read', status: 'read',
text: text: 'Hello there from the new world! And this is multiple lines of text. Lines and lines and lines.',
'Hello there from the new world! And this is multiple lines of text. Lines and lines and lines.',
timestamp: Date.now(), timestamp: Date.now(),
}, },
}, },

View File

@ -710,12 +710,8 @@ export class Timeline extends React.PureComponent<PropsType, StateType> {
); );
public loadOlderMessages = (): void => { public loadOlderMessages = (): void => {
const { const { haveOldest, isLoadingMessages, items, loadOlderMessages } =
haveOldest, this.props;
isLoadingMessages,
items,
loadOlderMessages,
} = this.props;
if (this.loadCountdownTimeout) { if (this.loadCountdownTimeout) {
clearTimeout(this.loadCountdownTimeout); clearTimeout(this.loadCountdownTimeout);

View File

@ -185,9 +185,10 @@ export const AddGroupMembersModal: FunctionComponent<PropsType> = ({
stage: Stage.ChoosingContacts, stage: Stage.ChoosingContacts,
}); });
const contactLookup = useMemo(() => makeLookup(candidateContacts, 'id'), [ const contactLookup = useMemo(
candidateContacts, () => makeLookup(candidateContacts, 'id'),
]); [candidateContacts]
);
const selectedContacts = deconstructLookup( const selectedContacts = deconstructLookup(
contactLookup, contactLookup,

View File

@ -148,17 +148,12 @@ export const ConversationDetails: React.ComponentType<Props> = ({
const [modalState, setModalState] = useState<ModalState>( const [modalState, setModalState] = useState<ModalState>(
ModalState.NothingOpen ModalState.NothingOpen
); );
const [ const [editGroupAttributesRequestState, setEditGroupAttributesRequestState] =
editGroupAttributesRequestState, useState<RequestState>(RequestState.Inactive);
setEditGroupAttributesRequestState, const [addGroupMembersRequestState, setAddGroupMembersRequestState] =
] = useState<RequestState>(RequestState.Inactive); useState<RequestState>(RequestState.Inactive);
const [ const [membersMissingCapability, setMembersMissingCapability] =
addGroupMembersRequestState, useState(false);
setAddGroupMembersRequestState,
] = useState<RequestState>(RequestState.Inactive);
const [membersMissingCapability, setMembersMissingCapability] = useState(
false
);
if (conversation === undefined) { if (conversation === undefined) {
throw new Error('ConversationDetails rendered without a conversation'); throw new Error('ConversationDetails rendered without a conversation');

View File

@ -25,98 +25,103 @@ type PropsType = {
setMuteExpiration: (muteExpiresAt: undefined | number) => unknown; setMuteExpiration: (muteExpiresAt: undefined | number) => unknown;
}; };
export const ConversationNotificationsSettings: FunctionComponent<PropsType> = ({ export const ConversationNotificationsSettings: FunctionComponent<PropsType> =
conversationType, ({
dontNotifyForMentionsIfMuted, conversationType,
i18n, dontNotifyForMentionsIfMuted,
muteExpiresAt, i18n,
setMuteExpiration, muteExpiresAt,
setDontNotifyForMentionsIfMuted, setMuteExpiration,
}) => { setDontNotifyForMentionsIfMuted,
const muteOptions = useMemo( }) => {
() => [ const muteOptions = useMemo(
...(isMuted(muteExpiresAt) () => [
? [] ...(isMuted(muteExpiresAt)
: [ ? []
{ : [
disabled: true, {
text: i18n('notMuted'), disabled: true,
value: -1, text: i18n('notMuted'),
}, value: -1,
]), },
...getMuteOptions(muteExpiresAt, i18n).map( ]),
({ disabled, name, value }) => ({ ...getMuteOptions(muteExpiresAt, i18n).map(
disabled, ({ disabled, name, value }) => ({
text: name, disabled,
value, text: name,
}) value,
), })
], ),
[i18n, muteExpiresAt] ],
); [i18n, muteExpiresAt]
const onMuteChange = (rawValue: string) => {
const ms = parseIntOrThrow(
rawValue,
'NotificationSettings: mute ms was not an integer'
); );
setMuteExpiration(ms);
};
const onChangeDontNotifyForMentionsIfMuted = (rawValue: string) => { const onMuteChange = (rawValue: string) => {
setDontNotifyForMentionsIfMuted(rawValue === 'yes'); const ms = parseIntOrThrow(
}; rawValue,
'NotificationSettings: mute ms was not an integer'
);
setMuteExpiration(ms);
};
return ( const onChangeDontNotifyForMentionsIfMuted = (rawValue: string) => {
<div className="conversation-details-panel"> setDontNotifyForMentionsIfMuted(rawValue === 'yes');
<PanelSection> };
<PanelRow
icon={ return (
<ConversationDetailsIcon <div className="conversation-details-panel">
ariaLabel={i18n('muteNotificationsTitle')} <PanelSection>
icon={IconType.mute}
/>
}
label={i18n('muteNotificationsTitle')}
right={
<Select options={muteOptions} onChange={onMuteChange} value={-1} />
}
/>
{conversationType === 'group' && (
<PanelRow <PanelRow
icon={ icon={
<ConversationDetailsIcon <ConversationDetailsIcon
ariaLabel={i18n( ariaLabel={i18n('muteNotificationsTitle')}
'ConversationNotificationsSettings__mentions__label' icon={IconType.mute}
)}
icon={IconType.mention}
/> />
} }
label={i18n('ConversationNotificationsSettings__mentions__label')} label={i18n('muteNotificationsTitle')}
info={i18n('ConversationNotificationsSettings__mentions__info')}
right={ right={
<Select <Select
options={[ options={muteOptions}
{ onChange={onMuteChange}
text: i18n( value={-1}
'ConversationNotificationsSettings__mentions__select__always-notify'
),
value: 'no',
},
{
text: i18n(
'ConversationNotificationsSettings__mentions__select__dont-notify-for-mentions-if-muted'
),
value: 'yes',
},
]}
onChange={onChangeDontNotifyForMentionsIfMuted}
value={dontNotifyForMentionsIfMuted ? 'yes' : 'no'}
/> />
} }
/> />
)} {conversationType === 'group' && (
</PanelSection> <PanelRow
</div> icon={
); <ConversationDetailsIcon
}; ariaLabel={i18n(
'ConversationNotificationsSettings__mentions__label'
)}
icon={IconType.mention}
/>
}
label={i18n('ConversationNotificationsSettings__mentions__label')}
info={i18n('ConversationNotificationsSettings__mentions__info')}
right={
<Select
options={[
{
text: i18n(
'ConversationNotificationsSettings__mentions__select__always-notify'
),
value: 'no',
},
{
text: i18n(
'ConversationNotificationsSettings__mentions__select__dont-notify-for-mentions-if-muted'
),
value: 'yes',
},
]}
onChange={onChangeDontNotifyForMentionsIfMuted}
value={dontNotifyForMentionsIfMuted ? 'yes' : 'no'}
/>
}
/>
)}
</PanelSection>
</div>
);
};

View File

@ -67,10 +67,8 @@ export const PendingInvites: React.ComponentType<PropsType> = ({
} }
const [selectedTab, setSelectedTab] = React.useState(Tab.Requests); const [selectedTab, setSelectedTab] = React.useState(Tab.Requests);
const [ const [stagedMemberships, setStagedMemberships] =
stagedMemberships, React.useState<Array<StagedMembershipType> | null>(null);
setStagedMemberships,
] = React.useState<Array<StagedMembershipType> | null>(null);
return ( return (
<div className="conversation-details-panel"> <div className="conversation-details-panel">
@ -379,10 +377,8 @@ function MembersPendingProfileKey({
membership => membership.metadata.addedByUserId membership => membership.metadata.addedByUserId
); );
const { const { [ourUuid]: ourPendingMemberships, ...otherPendingMembershipGroups } =
[ourUuid]: ourPendingMemberships, groupedPendingMemberships;
...otherPendingMembershipGroups
} = groupedPendingMemberships;
const otherPendingMemberships = Object.keys(otherPendingMembershipGroups) const otherPendingMemberships = Object.keys(otherPendingMembershipGroups)
.map(id => members.find(member => member.id === id)) .map(id => members.find(member => member.id === id))

View File

@ -9,28 +9,27 @@ export enum RequestState {
Active, Active,
} }
export const bemGenerator = (block: string) => ( export const bemGenerator =
element: string, (block: string) =>
modifier?: string | Record<string, boolean> (element: string, modifier?: string | Record<string, boolean>): string => {
): string => { const base = `${block}__${element}`;
const base = `${block}__${element}`; const classes = [base];
const classes = [base];
let conditionals: Record<string, boolean> = {}; let conditionals: Record<string, boolean> = {};
if (modifier) { if (modifier) {
if (typeof modifier === 'string') { if (typeof modifier === 'string') {
classes.push(`${base}--${modifier}`); classes.push(`${base}--${modifier}`);
} else { } else {
conditionals = Object.keys(modifier).reduce( conditionals = Object.keys(modifier).reduce(
(acc, key) => ({ (acc, key) => ({
...acc, ...acc,
[`${base}--${key}`]: modifier[key], [`${base}--${key}`]: modifier[key],
}), }),
{} as Record<string, boolean> {} as Record<string, boolean>
); );
}
} }
}
return classNames(classes, conditionals); return classNames(classes, conditionals);
}; };

View File

@ -31,7 +31,7 @@ const DAY_MS = 24 * 60 * 60 * 1000;
export const days = (n: number) => n * DAY_MS; export const days = (n: number) => n * DAY_MS;
const tokens = ['foo', 'bar', 'baz', 'qux', 'quux']; const tokens = ['foo', 'bar', 'baz', 'qux', 'quux'];
const contentTypes = ({ const contentTypes = {
gif: 'image/gif', gif: 'image/gif',
jpg: 'image/jpeg', jpg: 'image/jpeg',
png: 'image/png', png: 'image/png',
@ -39,7 +39,7 @@ const contentTypes = ({
docx: 'application/text', docx: 'application/text',
pdf: 'application/pdf', pdf: 'application/pdf',
txt: 'application/text', txt: 'application/text',
} as unknown) as Record<string, MIMEType>; } as unknown as Record<string, MIMEType>;
const createRandomFile = ( const createRandomFile = (
startTime: number, startTime: number,

View File

@ -94,62 +94,66 @@ type GenericMediaItemWithSection<T> = {
type: T; type: T;
mediaItem: MediaItemType; mediaItem: MediaItemType;
}; };
type MediaItemWithStaticSection = GenericMediaItemWithSection<StaticSectionType>; type MediaItemWithStaticSection =
type MediaItemWithYearMonthSection = GenericMediaItemWithSection<YearMonthSectionType> & { GenericMediaItemWithSection<StaticSectionType>;
year: number; type MediaItemWithYearMonthSection =
month: number; GenericMediaItemWithSection<YearMonthSectionType> & {
}; year: number;
month: number;
};
type MediaItemWithSection = type MediaItemWithSection =
| MediaItemWithStaticSection | MediaItemWithStaticSection
| MediaItemWithYearMonthSection; | MediaItemWithYearMonthSection;
const withSection = (referenceDateTime: moment.Moment) => ( const withSection =
mediaItem: MediaItemType (referenceDateTime: moment.Moment) =>
): MediaItemWithSection => { (mediaItem: MediaItemType): MediaItemWithSection => {
const today = moment(referenceDateTime).startOf('day'); const today = moment(referenceDateTime).startOf('day');
const yesterday = moment(referenceDateTime).subtract(1, 'day').startOf('day'); const yesterday = moment(referenceDateTime)
const thisWeek = moment(referenceDateTime).startOf('isoWeek'); .subtract(1, 'day')
const thisMonth = moment(referenceDateTime).startOf('month'); .startOf('day');
const thisWeek = moment(referenceDateTime).startOf('isoWeek');
const thisMonth = moment(referenceDateTime).startOf('month');
const { message } = mediaItem; const { message } = mediaItem;
const mediaItemReceivedDate = moment.utc(getMessageTimestamp(message)); const mediaItemReceivedDate = moment.utc(getMessageTimestamp(message));
if (mediaItemReceivedDate.isAfter(today)) { if (mediaItemReceivedDate.isAfter(today)) {
return { return {
order: 0, order: 0,
type: 'today', type: 'today',
mediaItem, mediaItem,
}; };
} }
if (mediaItemReceivedDate.isAfter(yesterday)) { if (mediaItemReceivedDate.isAfter(yesterday)) {
return { return {
order: 1, order: 1,
type: 'yesterday', type: 'yesterday',
mediaItem, mediaItem,
}; };
} }
if (mediaItemReceivedDate.isAfter(thisWeek)) { if (mediaItemReceivedDate.isAfter(thisWeek)) {
return { return {
order: 2, order: 2,
type: 'thisWeek', type: 'thisWeek',
mediaItem, mediaItem,
}; };
} }
if (mediaItemReceivedDate.isAfter(thisMonth)) { if (mediaItemReceivedDate.isAfter(thisMonth)) {
return { return {
order: 3, order: 3,
type: 'thisMonth', type: 'thisMonth',
mediaItem, mediaItem,
}; };
} }
const month: number = mediaItemReceivedDate.month(); const month: number = mediaItemReceivedDate.month();
const year: number = mediaItemReceivedDate.year(); const year: number = mediaItemReceivedDate.year();
return { return {
order: year * 100 + month, order: year * 100 + month,
type: 'yearMonth', type: 'yearMonth',
month, month,
year, year,
mediaItem, mediaItem,
};
}; };
};

View File

@ -61,8 +61,8 @@ type PropsType = {
| 'unblurredAvatarPath' | 'unblurredAvatarPath'
>; >;
export const BaseConversationListItem: FunctionComponent<PropsType> = React.memo( export const BaseConversationListItem: FunctionComponent<PropsType> =
function BaseConversationListItem({ React.memo(function BaseConversationListItem({
acceptedMessageRequest, acceptedMessageRequest,
avatarPath, avatarPath,
badge, badge,
@ -238,8 +238,7 @@ export const BaseConversationListItem: FunctionComponent<PropsType> = React.memo
{contents} {contents}
</div> </div>
); );
} });
);
function UnreadIndicator({ count = 0 }: Readonly<{ count?: number }>) { function UnreadIndicator({ count = 0 }: Readonly<{ count?: number }>) {
let classModifier: undefined | string; let classModifier: undefined | string;

View File

@ -42,8 +42,7 @@ story.add('No Replacement', () => {
story.add('Two Replacements', () => { story.add('Two Replacements', () => {
const props = createProps({ const props = createProps({
text: text: 'Begin <<left>>Inside #1<<right>> This is between the two <<left>>Inside #2<<right>> End.',
'Begin <<left>>Inside #1<<right>> This is between the two <<left>>Inside #2<<right>> End.',
}); });
return <MessageBodyHighlight {...props} />; return <MessageBodyHighlight {...props} />;
@ -59,8 +58,7 @@ story.add('Two Replacements with an @mention', () => {
start: 33, start: 33,
}, },
], ],
text: text: 'Begin <<left>>Inside #1<<right>> \uFFFC This is between the two <<left>>Inside #2<<right>> End.',
'Begin <<left>>Inside #1<<right>> \uFFFC This is between the two <<left>>Inside #2<<right>> End.',
}); });
return <MessageBodyHighlight {...props} />; return <MessageBodyHighlight {...props} />;
@ -68,8 +66,7 @@ story.add('Two Replacements with an @mention', () => {
story.add('Emoji + Newlines + URLs', () => { story.add('Emoji + Newlines + URLs', () => {
const props = createProps({ const props = createProps({
text: text: '\nhttp://somewhere.com\n\n🔥 Before -- <<left>>A 🔥 inside<<right>> -- After 🔥',
'\nhttp://somewhere.com\n\n🔥 Before -- <<left>>A 🔥 inside<<right>> -- After 🔥',
}); });
return <MessageBodyHighlight {...props} />; return <MessageBodyHighlight {...props} />;

View File

@ -148,8 +148,7 @@ story.add('Empty (should be invalid)', () => {
story.add('@mention', () => { story.add('@mention', () => {
const props = createProps({ const props = createProps({
body: body: 'moss banana twine sound lake zoo brain count vacuum work stairs try power forget hair dry diary years no results \uFFFC elephant sorry umbrella potato igloo kangaroo home Georgia bayonet vector orange forge diary zebra turtle rise front \uFFFC',
'moss banana twine sound lake zoo brain count vacuum work stairs try power forget hair dry diary years no results \uFFFC elephant sorry umbrella potato igloo kangaroo home Georgia bayonet vector orange forge diary zebra turtle rise front \uFFFC',
bodyRanges: [ bodyRanges: [
{ {
length: 1, length: 1,
@ -175,8 +174,7 @@ story.add('@mention', () => {
story.add('@mention regexp', () => { story.add('@mention regexp', () => {
const props = createProps({ const props = createProps({
body: body: '\uFFFC This is a (long) /text/ ^$ that is ... specially **crafted** to (test) our regexp escaping mechanism! Making sure that the code we write works in all sorts of scenarios',
'\uFFFC This is a (long) /text/ ^$ that is ... specially **crafted** to (test) our regexp escaping mechanism! Making sure that the code we write works in all sorts of scenarios',
bodyRanges: [ bodyRanges: [
{ {
length: 1, length: 1,
@ -215,8 +213,7 @@ story.add('@mention no-matches', () => {
story.add('@mention no-matches', () => { story.add('@mention no-matches', () => {
const props = createProps({ const props = createProps({
body: body: 'moss banana twine sound lake zoo brain count vacuum work stairs try power forget hair dry diary years no results \uFFFC elephant sorry umbrella potato igloo kangaroo home Georgia bayonet vector orange forge diary zebra turtle rise front \uFFFC',
'moss banana twine sound lake zoo brain count vacuum work stairs try power forget hair dry diary years no results \uFFFC elephant sorry umbrella potato igloo kangaroo home Georgia bayonet vector orange forge diary zebra turtle rise front \uFFFC',
bodyRanges: [ bodyRanges: [
{ {
length: 1, length: 1,

View File

@ -6,6 +6,5 @@ import React from 'react';
type PropsType = Record<string, never>; type PropsType = Record<string, never>;
export const SearchResultsLoadingFakeHeader: FunctionComponent<PropsType> = () => ( export const SearchResultsLoadingFakeHeader: FunctionComponent<PropsType> =
<div className="module-SearchResultsLoadingFakeHeader" /> () => <div className="module-SearchResultsLoadingFakeHeader" />;
);

View File

@ -353,9 +353,8 @@ data.forEach(emoji => {
if (skin_variations) { if (skin_variations) {
Object.entries(skin_variations).forEach(([tone, variation]) => { Object.entries(skin_variations).forEach(([tone, variation]) => {
imageByEmoji[ imageByEmoji[convertShortName(short_name, tone as SkinToneKey)] =
convertShortName(short_name, tone as SkinToneKey) makeImagePath(variation.image);
] = makeImagePath(variation.image);
dataByEmoji[convertShortName(short_name, tone as SkinToneKey)] = emoji; dataByEmoji[convertShortName(short_name, tone as SkinToneKey)] = emoji;
}); });
} }

View File

@ -62,7 +62,8 @@ export class LeftPaneChooseGroupMembersHelper extends LeftPaneHelper<LeftPaneCho
this.candidateContacts = candidateContacts; this.candidateContacts = candidateContacts;
this.cantAddContactForModal = cantAddContactForModal; this.cantAddContactForModal = cantAddContactForModal;
this.isShowingMaximumGroupSizeModal = isShowingMaximumGroupSizeModal; this.isShowingMaximumGroupSizeModal = isShowingMaximumGroupSizeModal;
this.isShowingRecommendedGroupSizeModal = isShowingRecommendedGroupSizeModal; this.isShowingRecommendedGroupSizeModal =
isShowingRecommendedGroupSizeModal;
this.searchTerm = searchTerm; this.searchTerm = searchTerm;
this.selectedContacts = selectedContacts; this.selectedContacts = selectedContacts;

View File

@ -161,9 +161,8 @@ export class LeftPaneSearchHelper extends LeftPaneHelper<LeftPaneSearchPropsType
return undefined; return undefined;
} }
const conversationRowCount = getRowCountForLoadedSearchResults( const conversationRowCount =
conversationResults getRowCountForLoadedSearchResults(conversationResults);
);
const contactRowCount = getRowCountForLoadedSearchResults(contactResults); const contactRowCount = getRowCountForLoadedSearchResults(contactResults);
const messageRowCount = getRowCountForLoadedSearchResults(messageResults); const messageRowCount = getRowCountForLoadedSearchResults(messageResults);

View File

@ -33,10 +33,8 @@ export const StickerManager = React.memo(
i18n, i18n,
}: Props) => { }: Props) => {
const focusRef = React.createRef<HTMLDivElement>(); const focusRef = React.createRef<HTMLDivElement>();
const [ const [packToPreview, setPackToPreview] =
packToPreview, React.useState<StickerPackType | null>(null);
setPackToPreview,
] = React.useState<StickerPackType | null>(null);
React.useEffect(() => { React.useEffect(() => {
if (!knownPacks) { if (!knownPacks) {

View File

@ -237,7 +237,8 @@ export const StickerPicker = React.memo(
'module-sticker-picker__header__button', 'module-sticker-picker__header__button',
'module-sticker-picker__header__button--add-pack', 'module-sticker-picker__header__button--add-pack',
{ {
'module-sticker-picker__header__button--hint': showPickerHint, 'module-sticker-picker__header__button--hint':
showPickerHint,
} }
)} )}
onClick={onClickAddPack} onClick={onClickAddPack}
@ -300,8 +301,10 @@ export const StickerPicker = React.memo(
className={classNames( className={classNames(
'module-sticker-picker__body__content', 'module-sticker-picker__body__content',
{ {
'module-sticker-picker__body__content--under-text': showText, 'module-sticker-picker__body__content--under-text':
'module-sticker-picker__body__content--under-long-text': showLongText, showText,
'module-sticker-picker__body__content--under-long-text':
showLongText,
} }
)} )}
> >

View File

@ -28,7 +28,7 @@ export class Timers {
this.timers.set(id, timer); this.timers.set(id, timer);
return ({ id } as unknown) as Timeout; return { id } as unknown as Timeout;
} }
public clearTimeout({ id }: Timeout): ReturnType<typeof clearTimeout> { public clearTimeout({ id }: Timeout): ReturnType<typeof clearTimeout> {

View File

@ -311,9 +311,10 @@ export function buildGroupLink(conversation: ConversationModel): string {
return `https://signal.group/#${hash}`; return `https://signal.group/#${hash}`;
} }
export function parseGroupLink( export function parseGroupLink(hash: string): {
hash: string masterKey: string;
): { masterKey: string; inviteLinkPassword: string } { inviteLinkPassword: string;
} {
const base64 = fromWebSafeBase64(hash); const base64 = fromWebSafeBase64(hash);
const buffer = Bytes.fromBase64(base64); const buffer = Bytes.fromBase64(base64);
@ -614,7 +615,8 @@ export async function buildAddMembersChange(
const now = Date.now(); const now = Date.now();
const addMembers: Array<Proto.GroupChange.Actions.AddMemberAction> = []; const addMembers: Array<Proto.GroupChange.Actions.AddMemberAction> = [];
const addPendingMembers: Array<Proto.GroupChange.Actions.AddMemberPendingProfileKeyAction> = []; const addPendingMembers: Array<Proto.GroupChange.Actions.AddMemberPendingProfileKeyAction> =
[];
await Promise.all( await Promise.all(
conversationIds.map(async conversationId => { conversationIds.map(async conversationId => {
@ -687,7 +689,8 @@ export async function buildAddMembersChange(
memberPendingProfileKey.addedByUserId = ourUuidCipherTextBuffer; memberPendingProfileKey.addedByUserId = ourUuidCipherTextBuffer;
memberPendingProfileKey.timestamp = now; memberPendingProfileKey.timestamp = now;
const addPendingMemberAction = new Proto.GroupChange.Actions.AddMemberPendingProfileKeyAction(); const addPendingMemberAction =
new Proto.GroupChange.Actions.AddMemberPendingProfileKeyAction();
addPendingMemberAction.added = memberPendingProfileKey; addPendingMemberAction.added = memberPendingProfileKey;
addPendingMembers.push(addPendingMemberAction); addPendingMembers.push(addPendingMemberAction);
@ -782,7 +785,8 @@ export async function buildUpdateAttributesChange(
if (typeof description === 'string') { if (typeof description === 'string') {
hasChangedSomething = true; hasChangedSomething = true;
actions.modifyDescription = new Proto.GroupChange.Actions.ModifyDescriptionAction(); actions.modifyDescription =
new Proto.GroupChange.Actions.ModifyDescriptionAction();
actions.modifyDescription.descriptionBytes = buildGroupDescriptionBuffer( actions.modifyDescription.descriptionBytes = buildGroupDescriptionBuffer(
clientZkGroupCipher, clientZkGroupCipher,
description description
@ -822,7 +826,8 @@ export function buildDisappearingMessagesTimerChange({
const blobPlaintext = Proto.GroupAttributeBlob.encode(blob).finish(); const blobPlaintext = Proto.GroupAttributeBlob.encode(blob).finish();
const blobCipherText = encryptGroupBlob(clientZkGroupCipher, blobPlaintext); const blobCipherText = encryptGroupBlob(clientZkGroupCipher, blobPlaintext);
const timerAction = new Proto.GroupChange.Actions.ModifyDisappearingMessagesTimerAction(); const timerAction =
new Proto.GroupChange.Actions.ModifyDisappearingMessagesTimerAction();
timerAction.timer = blobCipherText; timerAction.timer = blobCipherText;
actions.version = (group.revision || 0) + 1; actions.version = (group.revision || 0) + 1;
@ -835,10 +840,10 @@ export function buildInviteLinkPasswordChange(
group: ConversationAttributesType, group: ConversationAttributesType,
inviteLinkPassword: string inviteLinkPassword: string
): Proto.GroupChange.Actions { ): Proto.GroupChange.Actions {
const inviteLinkPasswordAction = new Proto.GroupChange.Actions.ModifyInviteLinkPasswordAction(); const inviteLinkPasswordAction =
inviteLinkPasswordAction.inviteLinkPassword = Bytes.fromBase64( new Proto.GroupChange.Actions.ModifyInviteLinkPasswordAction();
inviteLinkPassword inviteLinkPasswordAction.inviteLinkPassword =
); Bytes.fromBase64(inviteLinkPassword);
const actions = new Proto.GroupChange.Actions(); const actions = new Proto.GroupChange.Actions();
actions.version = (group.revision || 0) + 1; actions.version = (group.revision || 0) + 1;
@ -852,13 +857,14 @@ export function buildNewGroupLinkChange(
inviteLinkPassword: string, inviteLinkPassword: string,
addFromInviteLinkAccess: AccessRequiredEnum addFromInviteLinkAccess: AccessRequiredEnum
): Proto.GroupChange.Actions { ): Proto.GroupChange.Actions {
const accessControlAction = new Proto.GroupChange.Actions.ModifyAddFromInviteLinkAccessControlAction(); const accessControlAction =
new Proto.GroupChange.Actions.ModifyAddFromInviteLinkAccessControlAction();
accessControlAction.addFromInviteLinkAccess = addFromInviteLinkAccess; accessControlAction.addFromInviteLinkAccess = addFromInviteLinkAccess;
const inviteLinkPasswordAction = new Proto.GroupChange.Actions.ModifyInviteLinkPasswordAction(); const inviteLinkPasswordAction =
inviteLinkPasswordAction.inviteLinkPassword = Bytes.fromBase64( new Proto.GroupChange.Actions.ModifyInviteLinkPasswordAction();
inviteLinkPassword inviteLinkPasswordAction.inviteLinkPassword =
); Bytes.fromBase64(inviteLinkPassword);
const actions = new Proto.GroupChange.Actions(); const actions = new Proto.GroupChange.Actions();
actions.version = (group.revision || 0) + 1; actions.version = (group.revision || 0) + 1;
@ -872,7 +878,8 @@ export function buildAccessControlAddFromInviteLinkChange(
group: ConversationAttributesType, group: ConversationAttributesType,
value: AccessRequiredEnum value: AccessRequiredEnum
): Proto.GroupChange.Actions { ): Proto.GroupChange.Actions {
const accessControlAction = new Proto.GroupChange.Actions.ModifyAddFromInviteLinkAccessControlAction(); const accessControlAction =
new Proto.GroupChange.Actions.ModifyAddFromInviteLinkAccessControlAction();
accessControlAction.addFromInviteLinkAccess = value; accessControlAction.addFromInviteLinkAccess = value;
const actions = new Proto.GroupChange.Actions(); const actions = new Proto.GroupChange.Actions();
@ -900,7 +907,8 @@ export function buildAccessControlAttributesChange(
group: ConversationAttributesType, group: ConversationAttributesType,
value: AccessRequiredEnum value: AccessRequiredEnum
): Proto.GroupChange.Actions { ): Proto.GroupChange.Actions {
const accessControlAction = new Proto.GroupChange.Actions.ModifyAttributesAccessControlAction(); const accessControlAction =
new Proto.GroupChange.Actions.ModifyAttributesAccessControlAction();
accessControlAction.attributesAccess = value; accessControlAction.attributesAccess = value;
const actions = new Proto.GroupChange.Actions(); const actions = new Proto.GroupChange.Actions();
@ -914,7 +922,8 @@ export function buildAccessControlMembersChange(
group: ConversationAttributesType, group: ConversationAttributesType,
value: AccessRequiredEnum value: AccessRequiredEnum
): Proto.GroupChange.Actions { ): Proto.GroupChange.Actions {
const accessControlAction = new Proto.GroupChange.Actions.ModifyMembersAccessControlAction(); const accessControlAction =
new Proto.GroupChange.Actions.ModifyMembersAccessControlAction();
accessControlAction.membersAccess = value; accessControlAction.membersAccess = value;
const actions = new Proto.GroupChange.Actions(); const actions = new Proto.GroupChange.Actions();
@ -942,7 +951,8 @@ export function buildDeletePendingAdminApprovalMemberChange({
const clientZkGroupCipher = getClientZkGroupCipher(group.secretParams); const clientZkGroupCipher = getClientZkGroupCipher(group.secretParams);
const uuidCipherTextBuffer = encryptUuid(clientZkGroupCipher, uuid); const uuidCipherTextBuffer = encryptUuid(clientZkGroupCipher, uuid);
const deleteMemberPendingAdminApproval = new Proto.GroupChange.Actions.DeleteMemberPendingAdminApprovalAction(); const deleteMemberPendingAdminApproval =
new Proto.GroupChange.Actions.DeleteMemberPendingAdminApprovalAction();
deleteMemberPendingAdminApproval.deletedUserId = uuidCipherTextBuffer; deleteMemberPendingAdminApproval.deletedUserId = uuidCipherTextBuffer;
actions.version = (group.revision || 0) + 1; actions.version = (group.revision || 0) + 1;
@ -973,7 +983,8 @@ export function buildAddPendingAdminApprovalMemberChange({
serverPublicParamsBase64 serverPublicParamsBase64
); );
const addMemberPendingAdminApproval = new Proto.GroupChange.Actions.AddMemberPendingAdminApprovalAction(); const addMemberPendingAdminApproval =
new Proto.GroupChange.Actions.AddMemberPendingAdminApprovalAction();
const presentation = createProfileKeyCredentialPresentation( const presentation = createProfileKeyCredentialPresentation(
clientZkProfileCipher, clientZkProfileCipher,
profileKeyCredentialBase64, profileKeyCredentialBase64,
@ -1049,7 +1060,8 @@ export function buildDeletePendingMemberChange({
const deletePendingMembers = uuids.map(uuid => { const deletePendingMembers = uuids.map(uuid => {
const uuidCipherTextBuffer = encryptUuid(clientZkGroupCipher, uuid); const uuidCipherTextBuffer = encryptUuid(clientZkGroupCipher, uuid);
const deletePendingMember = new Proto.GroupChange.Actions.DeleteMemberPendingProfileKeyAction(); const deletePendingMember =
new Proto.GroupChange.Actions.DeleteMemberPendingProfileKeyAction();
deletePendingMember.deletedUserId = uuidCipherTextBuffer; deletePendingMember.deletedUserId = uuidCipherTextBuffer;
return deletePendingMember; return deletePendingMember;
}); });
@ -1131,7 +1143,8 @@ export function buildPromotePendingAdminApprovalMemberChange({
const clientZkGroupCipher = getClientZkGroupCipher(group.secretParams); const clientZkGroupCipher = getClientZkGroupCipher(group.secretParams);
const uuidCipherTextBuffer = encryptUuid(clientZkGroupCipher, uuid); const uuidCipherTextBuffer = encryptUuid(clientZkGroupCipher, uuid);
const promotePendingMember = new Proto.GroupChange.Actions.PromoteMemberPendingAdminApprovalAction(); const promotePendingMember =
new Proto.GroupChange.Actions.PromoteMemberPendingAdminApprovalAction();
promotePendingMember.userId = uuidCipherTextBuffer; promotePendingMember.userId = uuidCipherTextBuffer;
promotePendingMember.role = MEMBER_ROLE_ENUM.DEFAULT; promotePendingMember.role = MEMBER_ROLE_ENUM.DEFAULT;
@ -1167,7 +1180,8 @@ export function buildPromoteMemberChange({
group.secretParams group.secretParams
); );
const promotePendingMember = new Proto.GroupChange.Actions.PromoteMemberPendingProfileKeyAction(); const promotePendingMember =
new Proto.GroupChange.Actions.PromoteMemberPendingProfileKeyAction();
promotePendingMember.presentation = presentation; promotePendingMember.presentation = presentation;
actions.version = (group.revision || 0) + 1; actions.version = (group.revision || 0) + 1;
@ -1270,9 +1284,8 @@ export async function modifyGroupV2({
group: conversation.attributes, group: conversation.attributes,
}); });
const groupChangeBuffer = Proto.GroupChange.encode( const groupChangeBuffer =
groupChange Proto.GroupChange.encode(groupChange).finish();
).finish();
const groupChangeBase64 = Bytes.toBase64(groupChangeBuffer); const groupChangeBase64 = Bytes.toBase64(groupChangeBuffer);
// Apply change locally, just like we would with an incoming change. This will // Apply change locally, just like we would with an incoming change. This will
@ -1314,14 +1327,14 @@ export async function modifyGroupV2({
// We don't save this message; we just use it to ensure that a sync message is // We don't save this message; we just use it to ensure that a sync message is
// sent to our linked devices. // sent to our linked devices.
const m = new window.Whisper.Message(({ const m = new window.Whisper.Message({
conversationId: conversation.id, conversationId: conversation.id,
type: 'not-to-save', type: 'not-to-save',
sent_at: timestamp, sent_at: timestamp,
received_at: timestamp, received_at: timestamp,
// TODO: DESKTOP-722 // TODO: DESKTOP-722
// this type does not fully implement the interface it is expected to // this type does not fully implement the interface it is expected to
} as unknown) as MessageAttributesType); } as unknown as MessageAttributesType);
// This is to ensure that the functions in send() and sendSyncMessage() // This is to ensure that the functions in send() and sendSyncMessage()
// don't save anything to the database. // don't save anything to the database.
@ -1825,7 +1838,8 @@ export async function getGroupMigrationMembers(
const logId = conversation.idForLogging(); const logId = conversation.idForLogging();
const MEMBER_ROLE_ENUM = Proto.Member.Role; const MEMBER_ROLE_ENUM = Proto.Member.Role;
const ourConversationId = window.ConversationController.getOurConversationId(); const ourConversationId =
window.ConversationController.getOurConversationId();
if (!ourConversationId) { if (!ourConversationId) {
throw new Error( throw new Error(
`getGroupMigrationMembers/${logId}: Couldn't fetch our own conversationId!` `getGroupMigrationMembers/${logId}: Couldn't fetch our own conversationId!`
@ -2034,15 +2048,15 @@ export async function initiateMigrationToGroupV2(
const secretParams = Bytes.toBase64(fields.secretParams); const secretParams = Bytes.toBase64(fields.secretParams);
const publicParams = Bytes.toBase64(fields.publicParams); const publicParams = Bytes.toBase64(fields.publicParams);
const ourConversationId = window.ConversationController.getOurConversationId(); const ourConversationId =
window.ConversationController.getOurConversationId();
if (!ourConversationId) { if (!ourConversationId) {
throw new Error( throw new Error(
`initiateMigrationToGroupV2/${logId}: Couldn't fetch our own conversationId!` `initiateMigrationToGroupV2/${logId}: Couldn't fetch our own conversationId!`
); );
} }
const ourConversation = window.ConversationController.get( const ourConversation =
ourConversationId window.ConversationController.get(ourConversationId);
);
if (!ourConversation) { if (!ourConversation) {
throw new Error( throw new Error(
`initiateMigrationToGroupV2/${logId}: cannot get our own conversation. Cannot migrate` `initiateMigrationToGroupV2/${logId}: cannot get our own conversation. Cannot migrate`
@ -2262,7 +2276,8 @@ export async function wrapWithSyncMessageSend({
); );
} }
const ourConversationId = window.ConversationController.getOurConversationId(); const ourConversationId =
window.ConversationController.getOurConversationId();
if (!ourConversationId) { if (!ourConversationId) {
throw new Error( throw new Error(
`wrapWithSyncMessageSend/${logId}: Cannot get our conversationId!` `wrapWithSyncMessageSend/${logId}: Cannot get our conversationId!`
@ -2324,7 +2339,8 @@ export function buildMigrationBubble(
newAttributes: ConversationAttributesType newAttributes: ConversationAttributesType
): MessageAttributesType { ): MessageAttributesType {
const ourUuid = window.storage.user.getCheckedUuid().toString(); const ourUuid = window.storage.user.getCheckedUuid().toString();
const ourConversationId = window.ConversationController.getOurConversationId(); const ourConversationId =
window.ConversationController.getOurConversationId();
// Assemble items to commemorate this event for the timeline.. // Assemble items to commemorate this event for the timeline..
const combinedConversationIds: Array<string> = [ const combinedConversationIds: Array<string> = [
@ -4007,12 +4023,10 @@ async function applyGroupChange({
const members: Record<UUIDStringType, GroupV2MemberType> = fromPairs( const members: Record<UUIDStringType, GroupV2MemberType> = fromPairs(
(result.membersV2 || []).map(member => [member.uuid, member]) (result.membersV2 || []).map(member => [member.uuid, member])
); );
const pendingMembers: Record< const pendingMembers: Record<UUIDStringType, GroupV2PendingMemberType> =
UUIDStringType, fromPairs(
GroupV2PendingMemberType (result.pendingMembersV2 || []).map(member => [member.uuid, member])
> = fromPairs( );
(result.pendingMembersV2 || []).map(member => [member.uuid, member])
);
const pendingAdminApprovalMembers: Record< const pendingAdminApprovalMembers: Record<
UUIDStringType, UUIDStringType,
GroupV2PendingAdminApprovalType GroupV2PendingAdminApprovalType

View File

@ -56,7 +56,8 @@ export async function joinViaLink(hash: string): Promise<void> {
const existingConversation = const existingConversation =
window.ConversationController.get(id) || window.ConversationController.get(id) ||
window.ConversationController.getByDerivedGroupV2Id(id); window.ConversationController.getByDerivedGroupV2Id(id);
const ourConversationId = window.ConversationController.getOurConversationIdOrThrow(); const ourConversationId =
window.ConversationController.getOurConversationIdOrThrow();
if ( if (
existingConversation && existingConversation &&
@ -339,15 +340,14 @@ export async function joinViaLink(hash: string): Promise<void> {
log.info(`joinViaLink/${logId}: Showing modal`); log.info(`joinViaLink/${logId}: Showing modal`);
let groupV2InfoDialog: let groupV2InfoDialog: Backbone.View | undefined =
| Backbone.View new window.Whisper.ReactWrapperView({
| undefined = new window.Whisper.ReactWrapperView({ className: 'group-v2-join-dialog-wrapper',
className: 'group-v2-join-dialog-wrapper', JSX: window.Signal.State.Roots.createGroupV2JoinModal(window.reduxStore, {
JSX: window.Signal.State.Roots.createGroupV2JoinModal(window.reduxStore, { join,
join, onClose: closeDialog,
onClose: closeDialog, }),
}), });
});
// We declare a new function here so we can await but not block // We declare a new function here so we can await but not block
const fetchAvatar = async () => { const fetchAvatar = async () => {

View File

@ -30,10 +30,8 @@ export function toggleSelectedContactForGroupAddition(
numberOfContactsAlreadyInGroup, numberOfContactsAlreadyInGroup,
selectedConversationIds: oldSelectedConversationIds, selectedConversationIds: oldSelectedConversationIds,
} = currentState; } = currentState;
let { let { maximumGroupSizeModalState, recommendedGroupSizeModalState } =
maximumGroupSizeModalState, currentState;
recommendedGroupSizeModalState,
} = currentState;
const selectedConversationIds = without( const selectedConversationIds = without(
oldSelectedConversationIds, oldSelectedConversationIds,

View File

@ -26,10 +26,8 @@ export function useIntersectionObserver(): [
(el?: Element | null) => void, (el?: Element | null) => void,
IntersectionObserverEntry | null IntersectionObserverEntry | null
] { ] {
const [ const [intersectionObserverEntry, setIntersectionObserverEntry] =
intersectionObserverEntry, useState<IntersectionObserverEntry | null>(null);
setIntersectionObserverEntry,
] = useState<IntersectionObserverEntry | null>(null);
const unobserveRef = useRef<(() => unknown) | null>(null); const unobserveRef = useRef<(() => unknown) | null>(null);

View File

@ -102,7 +102,8 @@ export async function runReadOrViewSyncJob({
return; return;
} }
const ourConversation = window.ConversationController.getOurConversationOrThrow(); const ourConversation =
window.ConversationController.getOurConversationOrThrow();
const sendOptions = await getSendOptions(ourConversation.attributes, { const sendOptions = await getSendOptions(ourConversation.attributes, {
syncMessage: true, syncMessage: true,
}); });

View File

@ -31,12 +31,8 @@ import { JobQueue } from './JobQueue';
import { jobQueueDatabaseStore } from './JobQueueDatabaseStore'; import { jobQueueDatabaseStore } from './JobQueueDatabaseStore';
import { getHttpErrorCode } from './helpers/getHttpErrorCode'; import { getHttpErrorCode } from './helpers/getHttpErrorCode';
const { const { loadAttachmentData, loadPreviewData, loadQuoteData, loadStickerData } =
loadAttachmentData, window.Signal.Migrations;
loadPreviewData,
loadQuoteData,
loadStickerData,
} = window.Signal.Migrations;
const { Message } = window.Signal.Types; const { Message } = window.Signal.Types;
const MAX_RETRY_TIME = durations.DAY; const MAX_RETRY_TIME = durations.DAY;
@ -357,7 +353,8 @@ function getMessageRecipients({
const recipientIdentifiersWithoutMe: Array<string> = []; const recipientIdentifiersWithoutMe: Array<string> = [];
const untrustedConversationIds: Array<string> = []; const untrustedConversationIds: Array<string> = [];
const currentConversationRecipients = conversation.getRecipientConversationIds(); const currentConversationRecipients =
conversation.getRecipientConversationIds();
Object.entries(message.get('sendStateByConversationId') || {}).forEach( Object.entries(message.get('sendStateByConversationId') || {}).forEach(
([recipientConversationId, sendState]) => { ([recipientConversationId, sendState]) => {
@ -439,25 +436,22 @@ async function getMessageSendData({
messageTimestamp = Date.now(); messageTimestamp = Date.now();
} }
const [ const [attachmentsWithData, preview, quote, sticker, profileKey] =
attachmentsWithData, await Promise.all([
preview, // We don't update the caches here because (1) we expect the caches to be populated
quote, // on initial send, so they should be there in the 99% case (2) if you're retrying
sticker, // a failed message across restarts, we don't touch the cache for simplicity. If
profileKey, // sends are failing, let's not add the complication of a cache.
] = await Promise.all([ Promise.all((message.get('attachments') ?? []).map(loadAttachmentData)),
// We don't update the caches here because (1) we expect the caches to be populated on message.cachedOutgoingPreviewData ||
// initial send, so they should be there in the 99% case (2) if you're retrying a loadPreviewData(message.get('preview')),
// failed message across restarts, we don't touch the cache for simplicity. If sends message.cachedOutgoingQuoteData || loadQuoteData(message.get('quote')),
// are failing, let's not add the complication of a cache. message.cachedOutgoingStickerData ||
Promise.all((message.get('attachments') ?? []).map(loadAttachmentData)), loadStickerData(message.get('sticker')),
message.cachedOutgoingPreviewData || conversation.get('profileSharing')
loadPreviewData(message.get('preview')), ? ourProfileKeyService.get()
message.cachedOutgoingQuoteData || loadQuoteData(message.get('quote')), : undefined,
message.cachedOutgoingStickerData || ]);
loadStickerData(message.get('sticker')),
conversation.get('profileSharing') ? ourProfileKeyService.get() : undefined,
]);
const { body, attachments } = window.Whisper.Message.getLongMessageAttachment( const { body, attachments } = window.Whisper.Message.getLongMessageAttachment(
{ {

View File

@ -72,7 +72,8 @@ export class ReactionJobQueue extends JobQueue<ReactionJobData> {
await window.ConversationController.load(); await window.ConversationController.load();
const ourConversationId = window.ConversationController.getOurConversationIdOrThrow(); const ourConversationId =
window.ConversationController.getOurConversationIdOrThrow();
const message = await getMessageById(messageId); const message = await getMessageById(messageId);
if (!message) { if (!message) {
@ -82,13 +83,11 @@ export class ReactionJobQueue extends JobQueue<ReactionJobData> {
return; return;
} }
const { const { pendingReaction, emojiToRemove } =
pendingReaction, reactionUtil.getNewestPendingOutgoingReaction(
emojiToRemove, getReactions(message),
} = reactionUtil.getNewestPendingOutgoingReaction( ourConversationId
getReactions(message), );
ourConversationId
);
if (!pendingReaction) { if (!pendingReaction) {
log.info(`no pending reaction for ${messageId}. Doing nothing`); log.info(`no pending reaction for ${messageId}. Doing nothing`);
return; return;
@ -122,10 +121,8 @@ export class ReactionJobQueue extends JobQueue<ReactionJobData> {
); );
} }
const { const { allRecipientIdentifiers, recipientIdentifiersWithoutMe } =
allRecipientIdentifiers, getRecipients(pendingReaction, conversation);
recipientIdentifiersWithoutMe,
} = getRecipients(pendingReaction, conversation);
const expireTimer = message.get('expireTimer'); const expireTimer = message.get('expireTimer');
const profileKey = conversation.get('profileSharing') const profileKey = conversation.get('profileSharing')
@ -295,7 +292,8 @@ function getRecipients(
const allRecipientIdentifiers: Array<string> = []; const allRecipientIdentifiers: Array<string> = [];
const recipientIdentifiersWithoutMe: Array<string> = []; const recipientIdentifiersWithoutMe: Array<string> = [];
const currentConversationRecipients = conversation.getRecipientConversationIds(); const currentConversationRecipients =
conversation.getRecipientConversationIds();
for (const id of reactionUtil.getUnsentConversationIds(reaction)) { for (const id of reactionUtil.getUnsentConversationIds(reaction)) {
const recipient = window.ConversationController.get(id); const recipient = window.ConversationController.get(id);

View File

@ -18,21 +18,7 @@ import { HTTPError } from '../textsecure/Errors';
const RETRY_WAIT_TIME = durations.MINUTE; const RETRY_WAIT_TIME = durations.MINUTE;
const RETRYABLE_4XX_FAILURE_STATUSES = new Set([ const RETRYABLE_4XX_FAILURE_STATUSES = new Set([
404, 404, 408, 410, 412, 413, 414, 417, 423, 424, 425, 426, 428, 429, 431, 449,
408,
410,
412,
413,
414,
417,
423,
424,
425,
426,
428,
429,
431,
449,
]); ]);
const is4xxStatus = (code: number): boolean => code >= 400 && code <= 499; const is4xxStatus = (code: number): boolean => code >= 400 && code <= 499;

View File

@ -41,10 +41,8 @@ const RETRY_BACKOFF: Record<number, number> = {
let enabled = false; let enabled = false;
let timeout: NodeJS.Timeout | null; let timeout: NodeJS.Timeout | null;
let logger: LoggerType; let logger: LoggerType;
const _activeAttachmentDownloadJobs: Record< const _activeAttachmentDownloadJobs: Record<string, Promise<void> | undefined> =
string, {};
Promise<void> | undefined
> = {};
type StartOptionsType = { type StartOptionsType = {
logger: LoggerType; logger: LoggerType;
@ -231,9 +229,8 @@ async function _runJob(job?: AttachmentDownloadJobType): Promise<void> {
return; return;
} }
const upgradedAttachment = await window.Signal.Migrations.processNewAttachment( const upgradedAttachment =
downloaded await window.Signal.Migrations.processNewAttachment(downloaded);
);
await _addAttachmentToMessage(message, upgradedAttachment, { type, index }); await _addAttachmentToMessage(message, upgradedAttachment, { type, index });

View File

@ -47,10 +47,11 @@ export class Deletes extends Collection<DeleteModel> {
try { try {
// The conversation the deleted message was in; we have to find it in the database // The conversation the deleted message was in; we have to find it in the database
// to to figure that out. // to to figure that out.
const targetConversation = await window.ConversationController.getConversationForTargetMessage( const targetConversation =
del.get('fromId'), await window.ConversationController.getConversationForTargetMessage(
del.get('targetSentTimestamp') del.get('fromId'),
); del.get('targetSentTimestamp')
);
if (!targetConversation) { if (!targetConversation) {
log.info( log.info(

View File

@ -222,9 +222,8 @@ export class MessageReceipts extends Collection<MessageReceiptModel> {
wasDeliveredWithSealedSender(sourceConversationId, message)) || wasDeliveredWithSealedSender(sourceConversationId, message)) ||
type === MessageReceiptType.Read type === MessageReceiptType.Read
) { ) {
const recipient = window.ConversationController.get( const recipient =
sourceConversationId window.ConversationController.get(sourceConversationId);
);
const recipientUuid = recipient?.get('uuid'); const recipientUuid = recipient?.get('uuid');
const deviceId = receipt.get('sourceDevice'); const deviceId = receipt.get('sourceDevice');

View File

@ -58,21 +58,21 @@ export class Reactions extends Collection<ReactionModel> {
try { try {
// The conversation the target message was in; we have to find it in the database // The conversation the target message was in; we have to find it in the database
// to to figure that out. // to to figure that out.
const targetConversationId = window.ConversationController.ensureContactIds( const targetConversationId =
{ window.ConversationController.ensureContactIds({
uuid: reaction.get('targetAuthorUuid'), uuid: reaction.get('targetAuthorUuid'),
} });
);
if (!targetConversationId) { if (!targetConversationId) {
throw new Error( throw new Error(
'onReaction: No conversationId returned from ensureContactIds!' 'onReaction: No conversationId returned from ensureContactIds!'
); );
} }
const targetConversation = await window.ConversationController.getConversationForTargetMessage( const targetConversation =
targetConversationId, await window.ConversationController.getConversationForTargetMessage(
reaction.get('targetTimestamp') targetConversationId,
); reaction.get('targetTimestamp')
);
if (!targetConversation) { if (!targetConversation) {
log.info( log.info(
'No target conversation for reaction', 'No target conversation for reaction',

View File

@ -543,7 +543,8 @@ export class ConversationModel extends window.Backbone
const idLog = this.idForLogging(); const idLog = this.idForLogging();
// Hard-coded to our own ID, because you don't add other users for admin approval // Hard-coded to our own ID, because you don't add other users for admin approval
const conversationId = window.ConversationController.getOurConversationIdOrThrow(); const conversationId =
window.ConversationController.getOurConversationIdOrThrow();
const toRequest = window.ConversationController.get(conversationId); const toRequest = window.ConversationController.get(conversationId);
if (!toRequest) { if (!toRequest) {
@ -1234,9 +1235,8 @@ export class ConversationModel extends window.Backbone
`sendTypingMessage(${this.idForLogging()}): sending ${content.isTyping}` `sendTypingMessage(${this.idForLogging()}): sending ${content.isTyping}`
); );
const contentMessage = window.textsecure.messaging.getTypingContentMessage( const contentMessage =
content window.textsecure.messaging.getTypingContentMessage(content);
);
const { ContentHint } = Proto.UnidentifiedSenderMessage.Message; const { ContentHint } = Proto.UnidentifiedSenderMessage.Message;
@ -1415,7 +1415,8 @@ export class ConversationModel extends window.Backbone
const messageRequestsEnabled = window.Signal.RemoteConfig.isEnabled( const messageRequestsEnabled = window.Signal.RemoteConfig.isEnabled(
'desktop.messageRequests' 'desktop.messageRequests'
); );
const ourConversationId = window.ConversationController.getOurConversationId(); const ourConversationId =
window.ConversationController.getOurConversationId();
let groupVersion: undefined | 1 | 2; let groupVersion: undefined | 1 | 2;
if (isGroupV1(this.attributes)) { if (isGroupV1(this.attributes)) {
@ -1499,8 +1500,8 @@ export class ConversationModel extends window.Backbone
pendingApprovalMemberships: this.getPendingApprovalMemberships(), pendingApprovalMemberships: this.getPendingApprovalMemberships(),
profileKey: this.get('profileKey'), profileKey: this.get('profileKey'),
messageRequestsEnabled, messageRequestsEnabled,
accessControlAddFromInviteLink: this.get('accessControl') accessControlAddFromInviteLink:
?.addFromInviteLink, this.get('accessControl')?.addFromInviteLink,
accessControlAttributes: this.get('accessControl')?.attributes, accessControlAttributes: this.get('accessControl')?.attributes,
accessControlMembers: this.get('accessControl')?.members, accessControlMembers: this.get('accessControl')?.members,
announcementsOnly: Boolean(this.get('announcementsOnly')), announcementsOnly: Boolean(this.get('announcementsOnly')),
@ -1708,7 +1709,8 @@ export class ConversationModel extends window.Backbone
try { try {
const messageRequestEnum = Proto.SyncMessage.MessageRequestResponse.Type; const messageRequestEnum = Proto.SyncMessage.MessageRequestResponse.Type;
const isLocalAction = !fromSync && !viaStorageServiceSync; const isLocalAction = !fromSync && !viaStorageServiceSync;
const ourConversationId = window.ConversationController.getOurConversationId(); const ourConversationId =
window.ConversationController.getOurConversationId();
const currentMessageRequestState = this.get('messageRequestResponseType'); const currentMessageRequestState = this.get('messageRequestResponseType');
const didResponseChange = response !== currentMessageRequestState; const didResponseChange = response !== currentMessageRequestState;
@ -1846,7 +1848,8 @@ export class ConversationModel extends window.Backbone
inviteLinkPassword: string; inviteLinkPassword: string;
approvalRequired: boolean; approvalRequired: boolean;
}): Promise<void> { }): Promise<void> {
const ourConversationId = window.ConversationController.getOurConversationIdOrThrow(); const ourConversationId =
window.ConversationController.getOurConversationIdOrThrow();
const ourUuid = window.textsecure.storage.user.getCheckedUuid().toString(); const ourUuid = window.textsecure.storage.user.getCheckedUuid().toString();
try { try {
if (approvalRequired) { if (approvalRequired) {
@ -1899,7 +1902,8 @@ export class ConversationModel extends window.Backbone
} }
async cancelJoinRequest(): Promise<void> { async cancelJoinRequest(): Promise<void> {
const ourConversationId = window.ConversationController.getOurConversationIdOrThrow(); const ourConversationId =
window.ConversationController.getOurConversationIdOrThrow();
const inviteLinkPassword = this.get('groupInviteLinkPassword'); const inviteLinkPassword = this.get('groupInviteLinkPassword');
if (!inviteLinkPassword) { if (!inviteLinkPassword) {
@ -1965,7 +1969,8 @@ export class ConversationModel extends window.Backbone
} }
async leaveGroupV2(): Promise<void> { async leaveGroupV2(): Promise<void> {
const ourConversationId = window.ConversationController.getOurConversationId(); const ourConversationId =
window.ConversationController.getOurConversationId();
if ( if (
ourConversationId && ourConversationId &&
@ -2095,7 +2100,8 @@ export class ConversationModel extends window.Backbone
// server updates were successful. // server updates were successful.
await this.applyMessageRequestResponse(response); await this.applyMessageRequestResponse(response);
const ourConversation = window.ConversationController.getOurConversationOrThrow(); const ourConversation =
window.ConversationController.getOurConversationOrThrow();
const sendOptions = await getSendOptions(ourConversation.attributes, { const sendOptions = await getSendOptions(ourConversation.attributes, {
syncMessage: true, syncMessage: true,
}); });
@ -2244,11 +2250,12 @@ export class ConversationModel extends window.Backbone
// handle the incoming key from the sync messages - need different // handle the incoming key from the sync messages - need different
// behavior if that key doesn't match the current key // behavior if that key doesn't match the current key
keyChange = await window.textsecure.storage.protocol.processVerifiedMessage( keyChange =
uuid, await window.textsecure.storage.protocol.processVerifiedMessage(
verified, uuid,
options.key || undefined verified,
); options.key || undefined
);
} else if (uuid) { } else if (uuid) {
keyChange = await window.textsecure.storage.protocol.setVerified( keyChange = await window.textsecure.storage.protocol.setVerified(
uuid, uuid,
@ -2315,7 +2322,8 @@ export class ConversationModel extends window.Backbone
// Because syncVerification sends a (null) message to the target of the verify and // Because syncVerification sends a (null) message to the target of the verify and
// a sync message to our own devices, we need to send the accessKeys down for both // a sync message to our own devices, we need to send the accessKeys down for both
// contacts. So we merge their sendOptions. // contacts. So we merge their sendOptions.
const ourConversation = window.ConversationController.getOurConversationOrThrow(); const ourConversation =
window.ConversationController.getOurConversationOrThrow();
const sendOptions = await getSendOptions(ourConversation.attributes, { const sendOptions = await getSendOptions(ourConversation.attributes, {
syncMessage: true, syncMessage: true,
}); });
@ -2523,7 +2531,7 @@ export class ConversationModel extends window.Backbone
receivedAt, receivedAt,
}); });
const message = ({ const message = {
conversationId: this.id, conversationId: this.id,
type: 'chat-session-refreshed', type: 'chat-session-refreshed',
sent_at: receivedAt, sent_at: receivedAt,
@ -2532,7 +2540,7 @@ export class ConversationModel extends window.Backbone
readStatus: ReadStatus.Unread, readStatus: ReadStatus.Unread,
// TODO: DESKTOP-722 // TODO: DESKTOP-722
// this type does not fully implement the interface it is expected to // this type does not fully implement the interface it is expected to
} as unknown) as MessageAttributesType; } as unknown as MessageAttributesType;
const id = await window.Signal.Data.saveMessage(message); const id = await window.Signal.Data.saveMessage(message);
const model = window.MessageController.register( const model = window.MessageController.register(
@ -2562,7 +2570,7 @@ export class ConversationModel extends window.Backbone
senderUuid, senderUuid,
}); });
const message = ({ const message = {
conversationId: this.id, conversationId: this.id,
type: 'delivery-issue', type: 'delivery-issue',
sourceUuid: senderUuid, sourceUuid: senderUuid,
@ -2572,7 +2580,7 @@ export class ConversationModel extends window.Backbone
readStatus: ReadStatus.Unread, readStatus: ReadStatus.Unread,
// TODO: DESKTOP-722 // TODO: DESKTOP-722
// this type does not fully implement the interface it is expected to // this type does not fully implement the interface it is expected to
} as unknown) as MessageAttributesType; } as unknown as MessageAttributesType;
const id = await window.Signal.Data.saveMessage(message); const id = await window.Signal.Data.saveMessage(message);
const model = window.MessageController.register( const model = window.MessageController.register(
@ -2597,7 +2605,7 @@ export class ConversationModel extends window.Backbone
); );
const timestamp = Date.now(); const timestamp = Date.now();
const message = ({ const message = {
conversationId: this.id, conversationId: this.id,
type: 'keychange', type: 'keychange',
sent_at: this.get('timestamp'), sent_at: this.get('timestamp'),
@ -2608,7 +2616,7 @@ export class ConversationModel extends window.Backbone
schemaVersion: Message.VERSION_NEEDED_FOR_DISPLAY, schemaVersion: Message.VERSION_NEEDED_FOR_DISPLAY,
// TODO: DESKTOP-722 // TODO: DESKTOP-722
// this type does not fully implement the interface it is expected to // this type does not fully implement the interface it is expected to
} as unknown) as MessageAttributesType; } as unknown as MessageAttributesType;
const id = await window.Signal.Data.saveMessage(message); const id = await window.Signal.Data.saveMessage(message);
const model = window.MessageController.register( const model = window.MessageController.register(
@ -2653,7 +2661,7 @@ export class ConversationModel extends window.Backbone
); );
const timestamp = Date.now(); const timestamp = Date.now();
const message = ({ const message = {
conversationId: this.id, conversationId: this.id,
type: 'verified-change', type: 'verified-change',
sent_at: lastMessage, sent_at: lastMessage,
@ -2664,7 +2672,7 @@ export class ConversationModel extends window.Backbone
local: options.local, local: options.local,
readStatus: ReadStatus.Unread, readStatus: ReadStatus.Unread,
// TODO: DESKTOP-722 // TODO: DESKTOP-722
} as unknown) as MessageAttributesType; } as unknown as MessageAttributesType;
const id = await window.Signal.Data.saveMessage(message); const id = await window.Signal.Data.saveMessage(message);
const model = window.MessageController.register( const model = window.MessageController.register(
@ -2715,7 +2723,7 @@ export class ConversationModel extends window.Backbone
throw missingCaseError(callHistoryDetails); throw missingCaseError(callHistoryDetails);
} }
const message = ({ const message = {
conversationId: this.id, conversationId: this.id,
type: 'call-history', type: 'call-history',
sent_at: timestamp, sent_at: timestamp,
@ -2724,7 +2732,7 @@ export class ConversationModel extends window.Backbone
readStatus: unread ? ReadStatus.Unread : ReadStatus.Read, readStatus: unread ? ReadStatus.Unread : ReadStatus.Read,
callHistoryDetails: detailsToSave, callHistoryDetails: detailsToSave,
// TODO: DESKTOP-722 // TODO: DESKTOP-722
} as unknown) as MessageAttributesType; } as unknown as MessageAttributesType;
const id = await window.Signal.Data.saveMessage(message); const id = await window.Signal.Data.saveMessage(message);
const model = window.MessageController.register( const model = window.MessageController.register(
@ -2774,7 +2782,7 @@ export class ConversationModel extends window.Backbone
conversationId?: string conversationId?: string
): Promise<void> { ): Promise<void> {
const now = Date.now(); const now = Date.now();
const message = ({ const message = {
conversationId: this.id, conversationId: this.id,
type: 'profile-change', type: 'profile-change',
sent_at: now, sent_at: now,
@ -2784,7 +2792,7 @@ export class ConversationModel extends window.Backbone
changedId: conversationId || this.id, changedId: conversationId || this.id,
profileChange, profileChange,
// TODO: DESKTOP-722 // TODO: DESKTOP-722
} as unknown) as MessageAttributesType; } as unknown as MessageAttributesType;
const id = await window.Signal.Data.saveMessage(message); const id = await window.Signal.Data.saveMessage(message);
const model = window.MessageController.register( const model = window.MessageController.register(
@ -3720,7 +3728,8 @@ export class ConversationModel extends window.Backbone
return; return;
} }
const ourConversationId = window.ConversationController.getOurConversationId(); const ourConversationId =
window.ConversationController.getOurConversationId();
if (!ourConversationId) { if (!ourConversationId) {
throw new Error('updateLastMessage: Failed to fetch ourConversationId'); throw new Error('updateLastMessage: Failed to fetch ourConversationId');
} }
@ -4051,7 +4060,7 @@ export class ConversationModel extends window.Backbone
window.Signal.Data.updateConversation(this.attributes); window.Signal.Data.updateConversation(this.attributes);
const model = new window.Whisper.Message(({ const model = new window.Whisper.Message({
// Even though this isn't reflected to the user, we want to place the last seen // Even though this isn't reflected to the user, we want to place the last seen
// indicator above it. We set it to 'unread' to trigger that placement. // indicator above it. We set it to 'unread' to trigger that placement.
readStatus: ReadStatus.Unread, readStatus: ReadStatus.Unread,
@ -4068,7 +4077,7 @@ export class ConversationModel extends window.Backbone
fromGroupUpdate: options.fromGroupUpdate, fromGroupUpdate: options.fromGroupUpdate,
}, },
// TODO: DESKTOP-722 // TODO: DESKTOP-722
} as unknown) as MessageAttributesType); } as unknown as MessageAttributesType);
const id = await window.Signal.Data.saveMessage(model.attributes); const id = await window.Signal.Data.saveMessage(model.attributes);
@ -4112,14 +4121,15 @@ export class ConversationModel extends window.Backbone
} }
if (isDirectConversation(this.attributes)) { if (isDirectConversation(this.attributes)) {
promise = window.textsecure.messaging.sendExpirationTimerUpdateToIdentifier( promise =
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion window.textsecure.messaging.sendExpirationTimerUpdateToIdentifier(
this.getSendTarget()!, // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
expireTimer, this.getSendTarget()!,
message.get('sent_at'), expireTimer,
profileKey, message.get('sent_at'),
sendOptions profileKey,
); sendOptions
);
} else { } else {
promise = window.textsecure.messaging.sendExpirationTimerUpdateToGroup( promise = window.textsecure.messaging.sendExpirationTimerUpdateToGroup(
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
@ -4153,7 +4163,7 @@ export class ConversationModel extends window.Backbone
} }
this.hasAddedHistoryDisclaimer = true; this.hasAddedHistoryDisclaimer = true;
const model = new window.Whisper.Message(({ const model = new window.Whisper.Message({
type: 'message-history-unsynced', type: 'message-history-unsynced',
// Even though this isn't reflected to the user, we want to place the last seen // Even though this isn't reflected to the user, we want to place the last seen
// indicator above it. We set it to 'unread' to trigger that placement. // indicator above it. We set it to 'unread' to trigger that placement.
@ -4163,7 +4173,7 @@ export class ConversationModel extends window.Backbone
received_at: window.Signal.Util.incrementMessageCounter(), received_at: window.Signal.Util.incrementMessageCounter(),
received_at_ms: timestamp, received_at_ms: timestamp,
// TODO: DESKTOP-722 // TODO: DESKTOP-722
} as unknown) as MessageAttributesType); } as unknown as MessageAttributesType);
const id = await window.Signal.Data.saveMessage(model.attributes); const id = await window.Signal.Data.saveMessage(model.attributes);
@ -4190,7 +4200,7 @@ export class ConversationModel extends window.Backbone
this.set({ left: true }); this.set({ left: true });
window.Signal.Data.updateConversation(this.attributes); window.Signal.Data.updateConversation(this.attributes);
const model = new window.Whisper.Message(({ const model = new window.Whisper.Message({
group_update: { left: 'You' }, group_update: { left: 'You' },
conversationId: this.id, conversationId: this.id,
type: 'outgoing', type: 'outgoing',
@ -4198,7 +4208,7 @@ export class ConversationModel extends window.Backbone
received_at: window.Signal.Util.incrementMessageCounter(), received_at: window.Signal.Util.incrementMessageCounter(),
received_at_ms: now, received_at_ms: now,
// TODO: DESKTOP-722 // TODO: DESKTOP-722
} as unknown) as MessageAttributesType); } as unknown as MessageAttributesType);
const id = await window.Signal.Data.saveMessage(model.attributes); const id = await window.Signal.Data.saveMessage(model.attributes);
model.set({ id }); model.set({ id });
@ -4250,16 +4260,14 @@ export class ConversationModel extends window.Backbone
} }
const ourUuid = window.textsecure.storage.user.getCheckedUuid(); const ourUuid = window.textsecure.storage.user.getCheckedUuid();
const ourGroups = await window.ConversationController.getAllGroupsInvolvingUuid( const ourGroups =
ourUuid await window.ConversationController.getAllGroupsInvolvingUuid(ourUuid);
);
const theirUuid = this.getUuid(); const theirUuid = this.getUuid();
if (!theirUuid) { if (!theirUuid) {
return; return;
} }
const theirGroups = await window.ConversationController.getAllGroupsInvolvingUuid( const theirGroups =
theirUuid await window.ConversationController.getAllGroupsInvolvingUuid(theirUuid);
);
const sharedGroups = window._.intersection(ourGroups, theirGroups); const sharedGroups = window._.intersection(ourGroups, theirGroups);
const sharedGroupNames = sharedGroups.map(conversation => const sharedGroupNames = sharedGroups.map(conversation =>
@ -4277,7 +4285,8 @@ export class ConversationModel extends window.Backbone
async getProfiles(): Promise<void> { async getProfiles(): Promise<void> {
// request all conversation members' keys // request all conversation members' keys
const conversations = (this.getMembers() as unknown) as Array<ConversationModel>; const conversations =
this.getMembers() as unknown as Array<ConversationModel>;
await Promise.all( await Promise.all(
window._.map(conversations, conversation => window._.map(conversations, conversation =>
getProfile(conversation.get('uuid'), conversation.get('e164')) getProfile(conversation.get('uuid'), conversation.get('e164'))
@ -4290,7 +4299,7 @@ export class ConversationModel extends window.Backbone
return; return;
} }
// isn't this already an Uint8Array? // isn't this already an Uint8Array?
const key = (this.get('profileKey') as unknown) as string; const key = this.get('profileKey') as unknown as string;
if (!key) { if (!key) {
return; return;
} }
@ -4344,7 +4353,7 @@ export class ConversationModel extends window.Backbone
const avatar = await window.textsecure.messaging.getAvatar(avatarPath); const avatar = await window.textsecure.messaging.getAvatar(avatarPath);
// isn't this already an Uint8Array? // isn't this already an Uint8Array?
const key = (this.get('profileKey') as unknown) as string; const key = this.get('profileKey') as unknown as string;
if (!key) { if (!key) {
return; return;
} }
@ -4405,7 +4414,7 @@ export class ConversationModel extends window.Backbone
async deriveAccessKeyIfNeeded(): Promise<void> { async deriveAccessKeyIfNeeded(): Promise<void> {
// isn't this already an array buffer? // isn't this already an array buffer?
const profileKey = (this.get('profileKey') as unknown) as string; const profileKey = this.get('profileKey') as unknown as string;
if (!profileKey) { if (!profileKey) {
return; return;
} }
@ -4499,9 +4508,8 @@ export class ConversationModel extends window.Backbone
const number = this.get('e164')!; const number = this.get('e164')!;
try { try {
const parsedNumber = window.libphonenumber.parse(number); const parsedNumber = window.libphonenumber.parse(number);
const regionCode = window.libphonenumber.getRegionCodeForNumber( const regionCode =
parsedNumber window.libphonenumber.getRegionCodeForNumber(parsedNumber);
);
if (regionCode === window.storage.get('regionCode')) { if (regionCode === window.storage.get('regionCode')) {
return window.libphonenumber.format( return window.libphonenumber.format(
parsedNumber, parsedNumber,

View File

@ -148,10 +148,8 @@ declare const _: typeof window._;
window.Whisper = window.Whisper || {}; window.Whisper = window.Whisper || {};
const { Message: TypedMessage } = window.Signal.Types; const { Message: TypedMessage } = window.Signal.Types;
const { const { deleteExternalMessageFiles, upgradeMessageSchema } =
deleteExternalMessageFiles, window.Signal.Migrations;
upgradeMessageSchema,
} = window.Signal.Migrations;
const { getTextWithMentions, GoogleChrome } = window.Signal.Util; const { getTextWithMentions, GoogleChrome } = window.Signal.Util;
const { addStickerPackReference, getMessageBySender } = window.Signal.Data; const { addStickerPackReference, getMessageBySender } = window.Signal.Data;
@ -351,8 +349,8 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
return window.ConversationController.getConversationId(identifier); return window.ConversationController.getConversationId(identifier);
}); });
const contacts: ReadonlyArray<SmartMessageDetailContact> = conversationIds.map( const contacts: ReadonlyArray<SmartMessageDetailContact> =
id => { conversationIds.map(id => {
const errorsForContact = getOwn(errorsGroupedById, id); const errorsForContact = getOwn(errorsGroupedById, id);
const isOutgoingKeyError = Boolean( const isOutgoingKeyError = Boolean(
errorsForContact?.some(error => error.name === OUTGOING_KEY_ERROR) errorsForContact?.some(error => error.name === OUTGOING_KEY_ERROR)
@ -384,8 +382,7 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
isOutgoingKeyError, isOutgoingKeyError,
isUnidentifiedDelivery, isUnidentifiedDelivery,
}; };
} });
);
return { return {
sentAt: this.get('sent_at'), sentAt: this.get('sent_at'),
@ -474,9 +471,8 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
i18n: window.i18n, i18n: window.i18n,
ourConversationId: window.ConversationController.getOurConversationId(), ourConversationId: window.ConversationController.getOurConversationId(),
renderContact: (conversationId: string) => { renderContact: (conversationId: string) => {
const conversation = window.ConversationController.get( const conversation =
conversationId window.ConversationController.get(conversationId);
);
return conversation return conversation
? conversation.getTitle() ? conversation.getTitle()
: window.i18n('unknownUser'); : window.i18n('unknownUser');
@ -887,7 +883,8 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
} }
const timestamp = this.get('sent_at'); const timestamp = this.get('sent_at');
const ourConversation = window.ConversationController.getOurConversationOrThrow(); const ourConversation =
window.ConversationController.getOurConversationOrThrow();
const sendOptions = await getSendOptions(ourConversation.attributes, { const sendOptions = await getSendOptions(ourConversation.attributes, {
syncMessage: true, syncMessage: true,
}); });
@ -1036,9 +1033,8 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
const isKeyChangeValue = isKeyChange(attributes); const isKeyChangeValue = isKeyChange(attributes);
const isMessageHistoryUnsyncedValue = isMessageHistoryUnsynced(attributes); const isMessageHistoryUnsyncedValue = isMessageHistoryUnsynced(attributes);
const isProfileChangeValue = isProfileChange(attributes); const isProfileChangeValue = isProfileChange(attributes);
const isUniversalTimerNotificationValue = isUniversalTimerNotification( const isUniversalTimerNotificationValue =
attributes isUniversalTimerNotification(attributes);
);
// Note: not all of these message types go through message.handleDataMessage // Note: not all of these message types go through message.handleDataMessage
@ -1219,7 +1215,8 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const conversation = this.getConversation()!; const conversation = this.getConversation()!;
const currentConversationRecipients = conversation.getRecipientConversationIds(); const currentConversationRecipients =
conversation.getRecipientConversationIds();
// Determine retry recipients and get their most up-to-date addressing information // Determine retry recipients and get their most up-to-date addressing information
const oldSendStateByConversationId = const oldSendStateByConversationId =
@ -1299,9 +1296,8 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
} }
removeOutgoingErrors(incomingIdentifier: string): CustomError { removeOutgoingErrors(incomingIdentifier: string): CustomError {
const incomingConversationId = window.ConversationController.getConversationId( const incomingConversationId =
incomingIdentifier window.ConversationController.getConversationId(incomingIdentifier);
);
const errors = _.partition( const errors = _.partition(
this.get('errors'), this.get('errors'),
e => e =>
@ -1563,7 +1559,8 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
} }
async sendSyncMessage(): Promise<CallbackResultType | void> { async sendSyncMessage(): Promise<CallbackResultType | void> {
const ourConversation = window.ConversationController.getOurConversationOrThrow(); const ourConversation =
window.ConversationController.getOurConversationOrThrow();
const sendOptions = await getSendOptions(ourConversation.attributes, { const sendOptions = await getSendOptions(ourConversation.attributes, {
syncMessage: true, syncMessage: true,
}); });
@ -1796,11 +1793,9 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
} attachment downloads for message ${this.idForLogging()}` } attachment downloads for message ${this.idForLogging()}`
); );
const [ const [longMessageAttachments, normalAttachments] = _.partition(
longMessageAttachments, attachmentsToQueue,
normalAttachments, attachment => MIME.isLongMessage(attachment.contentType)
] = _.partition(attachmentsToQueue, attachment =>
MIME.isLongMessage(attachment.contentType)
); );
if (longMessageAttachments.length > 1) { if (longMessageAttachments.length > 1) {
@ -2313,11 +2308,10 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
...(toUpdate.get('sendStateByConversationId') || {}), ...(toUpdate.get('sendStateByConversationId') || {}),
}; };
const unidentifiedStatus: Array<ProcessedUnidentifiedDeliveryStatus> = Array.isArray( const unidentifiedStatus: Array<ProcessedUnidentifiedDeliveryStatus> =
data.unidentifiedStatus Array.isArray(data.unidentifiedStatus)
) ? data.unidentifiedStatus
? data.unidentifiedStatus : [];
: [];
unidentifiedStatus.forEach( unidentifiedStatus.forEach(
({ destinationUuid, destination, unidentified }) => { ({ destinationUuid, destination, unidentified }) => {
@ -2326,13 +2320,12 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
return; return;
} }
const destinationConversationId = window.ConversationController.ensureContactIds( const destinationConversationId =
{ window.ConversationController.ensureContactIds({
uuid: destinationUuid, uuid: destinationUuid,
e164: destination, e164: destination,
highTrust: true, highTrust: true,
} });
);
if (!destinationConversationId) { if (!destinationConversationId) {
return; return;
} }
@ -2345,17 +2338,16 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
sendStateByConversationId, sendStateByConversationId,
destinationConversationId destinationConversationId
); );
sendStateByConversationId[ sendStateByConversationId[destinationConversationId] =
destinationConversationId previousSendState
] = previousSendState ? sendStateReducer(previousSendState, {
? sendStateReducer(previousSendState, { type: SendActionType.Sent,
type: SendActionType.Sent, updatedAt,
updatedAt, })
}) : {
: { status: SendStatus.Sent,
status: SendStatus.Sent, updatedAt,
updatedAt, };
};
if (unidentified) { if (unidentified) {
unidentifiedDeliveriesSet.add(identifier); unidentifiedDeliveriesSet.add(identifier);
@ -2445,8 +2437,9 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
} }
} }
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion const ourConversationId =
const ourConversationId = window.ConversationController.getOurConversationId()!; // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
window.ConversationController.getOurConversationId()!;
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const senderId = window.ConversationController.ensureContactIds({ const senderId = window.ConversationController.ensureContactIds({
e164: source, e164: source,
@ -2602,14 +2595,15 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
if (!hasGroupV2Prop && dataMessage.group) { if (!hasGroupV2Prop && dataMessage.group) {
const pendingGroupUpdate: GroupV1Update = {}; const pendingGroupUpdate: GroupV1Update = {};
const memberConversations: Array<ConversationModel> = await Promise.all( const memberConversations: Array<ConversationModel> =
dataMessage.group.membersE164.map((e164: string) => await Promise.all(
window.ConversationController.getOrCreateAndWait( dataMessage.group.membersE164.map((e164: string) =>
e164, window.ConversationController.getOrCreateAndWait(
'private' e164,
'private'
)
) )
) );
);
const members = memberConversations.map(c => c.get('id')); const members = memberConversations.map(c => c.get('id'));
attributes = { attributes = {
...attributes, ...attributes,
@ -2633,14 +2627,16 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
let hash; let hash;
if (avatarAttachment) { if (avatarAttachment) {
try { try {
downloadedAvatar = await window.Signal.Util.downloadAttachment( downloadedAvatar =
avatarAttachment await window.Signal.Util.downloadAttachment(
); avatarAttachment
);
if (downloadedAvatar) { if (downloadedAvatar) {
const loadedAttachment = await window.Signal.Migrations.loadAttachmentData( const loadedAttachment =
downloadedAvatar await window.Signal.Migrations.loadAttachmentData(
); downloadedAvatar
);
hash = computeHash(loadedAttachment.data); hash = computeHash(loadedAttachment.data);
} }
@ -2668,13 +2664,11 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
let avatar = null; let avatar = null;
if (downloadedAvatar && avatarAttachment !== null) { if (downloadedAvatar && avatarAttachment !== null) {
const onDiskAttachment = await Attachment.migrateDataToFileSystem( const onDiskAttachment =
downloadedAvatar, await Attachment.migrateDataToFileSystem(downloadedAvatar, {
{
writeNewAttachmentData: writeNewAttachmentData:
window.Signal.Migrations.writeNewAttachmentData, window.Signal.Migrations.writeNewAttachmentData,
} });
);
avatar = { avatar = {
...onDiskAttachment, ...onDiskAttachment,
hash, hash,
@ -3085,9 +3079,8 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
// Check for out-of-order view once open syncs // Check for out-of-order view once open syncs
if (isTapToView(message.attributes)) { if (isTapToView(message.attributes)) {
const viewOnceOpenSync = ViewOnceOpenSyncs.getSingleton().forMessage( const viewOnceOpenSync =
message ViewOnceOpenSyncs.getSingleton().forMessage(message);
);
if (viewOnceOpenSync) { if (viewOnceOpenSync) {
await message.markViewOnceMessageViewed({ fromSync: true }); await message.markViewOnceMessageViewed({ fromSync: true });
changed = true; changed = true;

View File

@ -5,46 +5,46 @@ import Delta from 'quill-delta';
import type { RefObject } from 'react'; import type { RefObject } from 'react';
import type { MemberRepository } from '../memberRepository'; import type { MemberRepository } from '../memberRepository';
export const matchMention = ( export const matchMention =
memberRepositoryRef: RefObject<MemberRepository> (memberRepositoryRef: RefObject<MemberRepository>) =>
) => (node: HTMLElement, delta: Delta): Delta => { (node: HTMLElement, delta: Delta): Delta => {
const memberRepository = memberRepositoryRef.current; const memberRepository = memberRepositoryRef.current;
if (memberRepository) { if (memberRepository) {
const { title } = node.dataset; const { title } = node.dataset;
if (node.classList.contains('MessageBody__at-mention')) { if (node.classList.contains('MessageBody__at-mention')) {
const { id } = node.dataset; const { id } = node.dataset;
const conversation = memberRepository.getMemberById(id); const conversation = memberRepository.getMemberById(id);
if (conversation && conversation.uuid) { if (conversation && conversation.uuid) {
return new Delta().insert({ return new Delta().insert({
mention: { mention: {
title, title,
uuid: conversation.uuid, uuid: conversation.uuid,
}, },
}); });
}
return new Delta().insert(`@${title}`);
} }
return new Delta().insert(`@${title}`); if (node.classList.contains('mention-blot')) {
} const { uuid } = node.dataset;
const conversation = memberRepository.getMemberByUuid(uuid);
if (node.classList.contains('mention-blot')) { if (conversation && conversation.uuid) {
const { uuid } = node.dataset; return new Delta().insert({
const conversation = memberRepository.getMemberByUuid(uuid); mention: {
title: title || conversation.title,
uuid: conversation.uuid,
},
});
}
if (conversation && conversation.uuid) { return new Delta().insert(`@${title}`);
return new Delta().insert({
mention: {
title: title || conversation.title,
uuid: conversation.uuid,
},
});
} }
return new Delta().insert(`@${title}`);
} }
}
return delta; return delta;
}; };

View File

@ -269,18 +269,15 @@ export class CallingClass {
RingRTC.handleOutgoingSignaling = this.handleOutgoingSignaling.bind(this); RingRTC.handleOutgoingSignaling = this.handleOutgoingSignaling.bind(this);
RingRTC.handleIncomingCall = this.handleIncomingCall.bind(this); RingRTC.handleIncomingCall = this.handleIncomingCall.bind(this);
RingRTC.handleAutoEndedIncomingCallRequest = this.handleAutoEndedIncomingCallRequest.bind( RingRTC.handleAutoEndedIncomingCallRequest =
this this.handleAutoEndedIncomingCallRequest.bind(this);
);
RingRTC.handleLogMessage = this.handleLogMessage.bind(this); RingRTC.handleLogMessage = this.handleLogMessage.bind(this);
RingRTC.handleSendHttpRequest = this.handleSendHttpRequest.bind(this); RingRTC.handleSendHttpRequest = this.handleSendHttpRequest.bind(this);
RingRTC.handleSendCallMessage = this.handleSendCallMessage.bind(this); RingRTC.handleSendCallMessage = this.handleSendCallMessage.bind(this);
RingRTC.handleSendCallMessageToGroup = this.handleSendCallMessageToGroup.bind( RingRTC.handleSendCallMessageToGroup =
this this.handleSendCallMessageToGroup.bind(this);
); RingRTC.handleGroupCallRingUpdate =
RingRTC.handleGroupCallRingUpdate = this.handleGroupCallRingUpdate.bind( this.handleGroupCallRingUpdate.bind(this);
this
);
this.attemptToGiveOurUuidToRingRtc(); this.attemptToGiveOurUuidToRingRtc();
window.Whisper.events.on('userChanged', () => { window.Whisper.events.on('userChanged', () => {
@ -402,9 +399,8 @@ export class CallingClass {
this.uxActions.showCallLobby({ this.uxActions.showCallLobby({
callMode: CallMode.Group, callMode: CallMode.Group,
conversationId: conversationProps.id, conversationId: conversationProps.id,
isConversationTooBigToRing: isConversationTooBigToRing( isConversationTooBigToRing:
conversationProps isConversationTooBigToRing(conversationProps),
),
...this.formatGroupCallForRedux(groupCall), ...this.formatGroupCallForRedux(groupCall),
}); });
break; break;
@ -708,9 +704,8 @@ export class CallingClass {
hasLocalVideo: boolean, hasLocalVideo: boolean,
shouldRing: boolean shouldRing: boolean
): Promise<void> { ): Promise<void> {
const conversation = window.ConversationController.get( const conversation =
conversationId window.ConversationController.get(conversationId)?.format();
)?.format();
if (!conversation) { if (!conversation) {
log.error('Missing conversation; not joining group call'); log.error('Missing conversation; not joining group call');
return; return;
@ -999,9 +994,8 @@ export class CallingClass {
declineGroupCall(conversationId: string, ringId: bigint): void { declineGroupCall(conversationId: string, ringId: bigint): void {
log.info('CallingClass.declineGroupCall()'); log.info('CallingClass.declineGroupCall()');
const groupId = window.ConversationController.get(conversationId)?.get( const groupId =
'groupId' window.ConversationController.get(conversationId)?.get('groupId');
);
if (!groupId) { if (!groupId) {
log.error( log.error(
'declineGroupCall: could not find the group ID for that conversation' 'declineGroupCall: could not find the group ID for that conversation'
@ -1300,11 +1294,8 @@ export class CallingClass {
); );
} }
const { const { availableCameras, availableMicrophones, availableSpeakers } =
availableCameras, await this.getAvailableIODevices();
availableMicrophones,
availableSpeakers,
} = await this.getAvailableIODevices();
const preferredMicrophone = window.Events.getPreferredAudioInputDevice(); const preferredMicrophone = window.Events.getPreferredAudioInputDevice();
const selectedMicIndex = findBestMatchingAudioDeviceIndex({ const selectedMicIndex = findBestMatchingAudioDeviceIndex({
@ -1394,9 +1385,8 @@ export class CallingClass {
const { storage } = window.textsecure; const { storage } = window.textsecure;
const senderIdentityRecord = await storage.protocol.getOrMigrateIdentityRecord( const senderIdentityRecord =
new UUID(remoteUserId) await storage.protocol.getOrMigrateIdentityRecord(new UUID(remoteUserId));
);
if (!senderIdentityRecord) { if (!senderIdentityRecord) {
log.error('Missing sender identity record; ignoring call message.'); log.error('Missing sender identity record; ignoring call message.');
return; return;

View File

@ -10,7 +10,7 @@ let layoutMap: LayoutMapType | undefined;
export async function initialize(): Promise<void> { export async function initialize(): Promise<void> {
strictAssert(layoutMap === undefined, 'keyboardLayout already initialized'); strictAssert(layoutMap === undefined, 'keyboardLayout already initialized');
const experimentalNavigator = (window.navigator as unknown) as { const experimentalNavigator = window.navigator as unknown as {
keyboard: { getLayoutMap(): Promise<LayoutMapType> }; keyboard: { getLayoutMap(): Promise<LayoutMapType> };
}; };

View File

@ -42,10 +42,8 @@ import * as log from '../logging/log';
type IManifestRecordIdentifier = Proto.ManifestRecord.IIdentifier; type IManifestRecordIdentifier = Proto.ManifestRecord.IIdentifier;
const { const { eraseStorageServiceStateFromConversations, updateConversation } =
eraseStorageServiceStateFromConversations, dataInterface;
updateConversation,
} = dataInterface;
const uploadBucket: Array<number> = []; const uploadBucket: Array<number> = [];
@ -604,11 +602,8 @@ async function createNewManifest() {
const version = window.storage.get('manifestVersion', 0); const version = window.storage.get('manifestVersion', 0);
const { const { conversationsToUpdate, newItems, storageManifest } =
conversationsToUpdate, await generateManifest(version, undefined, true);
newItems,
storageManifest,
} = await generateManifest(version, undefined, true);
await uploadManifest(version, { await uploadManifest(version, {
conversationsToUpdate, conversationsToUpdate,
@ -650,7 +645,8 @@ async function fetchManifest(
} }
try { try {
const credentials = await window.textsecure.messaging.getStorageCredentials(); const credentials =
await window.textsecure.messaging.getStorageCredentials();
window.storage.put('storageCredentials', credentials); window.storage.put('storageCredentials', credentials);
const manifestBinary = await window.textsecure.messaging.getStorageManifest( const manifestBinary = await window.textsecure.messaging.getStorageManifest(
@ -869,12 +865,13 @@ async function processRemoteRecords(
); );
const credentials = window.storage.get('storageCredentials'); const credentials = window.storage.get('storageCredentials');
const storageItemsBuffer = await window.textsecure.messaging.getStorageRecords( const storageItemsBuffer =
Proto.ReadOperation.encode(readOperation).finish(), await window.textsecure.messaging.getStorageRecords(
{ Proto.ReadOperation.encode(readOperation).finish(),
credentials, {
} credentials,
); }
);
const storageItems = Proto.StorageItems.decode(storageItemsBuffer); const storageItems = Proto.StorageItems.decode(storageItemsBuffer);
@ -959,10 +956,11 @@ async function processRemoteRecords(
// Collect full map of previously and currently unknown records // Collect full map of previously and currently unknown records
const unknownRecords: Map<string, UnknownRecord> = new Map(); const unknownRecords: Map<string, UnknownRecord> = new Map();
const unknownRecordsArray: ReadonlyArray<UnknownRecord> = window.storage.get( const unknownRecordsArray: ReadonlyArray<UnknownRecord> =
'storage-service-unknown-records', window.storage.get(
new Array<UnknownRecord>() 'storage-service-unknown-records',
); new Array<UnknownRecord>()
);
unknownRecordsArray.forEach((record: UnknownRecord) => { unknownRecordsArray.forEach((record: UnknownRecord) => {
unknownRecords.set(record.storageID, record); unknownRecords.set(record.storageID, record);
}); });

View File

@ -246,7 +246,8 @@ export async function toAccountRecord(
const pinnedConversation = window.ConversationController.get(id); const pinnedConversation = window.ConversationController.get(id);
if (pinnedConversation) { if (pinnedConversation) {
const pinnedConversationRecord = new Proto.AccountRecord.PinnedConversation(); const pinnedConversationRecord =
new Proto.AccountRecord.PinnedConversation();
if (pinnedConversation.get('type') === 'private') { if (pinnedConversation.get('type') === 'private') {
pinnedConversationRecord.identifier = 'contact'; pinnedConversationRecord.identifier = 'contact';
@ -953,9 +954,8 @@ export async function mergeAccountRecord(
let conversationId: string | undefined; let conversationId: string | undefined;
if (contact) { if (contact) {
conversationId = window.ConversationController.ensureContactIds( conversationId =
contact window.ConversationController.ensureContactIds(contact);
);
} else if (legacyGroupId && legacyGroupId.length) { } else if (legacyGroupId && legacyGroupId.length) {
conversationId = Bytes.toBinary(legacyGroupId); conversationId = Bytes.toBinary(legacyGroupId);
} else if (groupMasterKey && groupMasterKey.length) { } else if (groupMasterKey && groupMasterKey.length) {

View File

@ -6,20 +6,21 @@ import type { Middleware } from 'redux';
import { COLORS_CHANGED, COLOR_SELECTED } from '../state/ducks/conversations'; import { COLORS_CHANGED, COLOR_SELECTED } from '../state/ducks/conversations';
export const dispatchItemsMiddleware: Middleware = ({ export const dispatchItemsMiddleware: Middleware =
getState, ({ getState }) =>
}) => next => action => { next =>
const result = next(action); action => {
if ( const result = next(action);
action.type === 'items/PUT' || if (
action.type === 'items/PUT_EXTERNAL' || action.type === 'items/PUT' ||
action.type === 'items/REMOVE' || action.type === 'items/PUT_EXTERNAL' ||
action.type === 'items/REMOVE_EXTERNAL' || action.type === 'items/REMOVE' ||
action.type === 'items/RESET' || action.type === 'items/REMOVE_EXTERNAL' ||
action.type === COLOR_SELECTED || action.type === 'items/RESET' ||
action.type === COLORS_CHANGED action.type === COLOR_SELECTED ||
) { action.type === COLORS_CHANGED
ipcRenderer.send('preferences-changed', getState().items); ) {
} ipcRenderer.send('preferences-changed', getState().items);
return result; }
}; return result;
};

View File

@ -970,13 +970,11 @@ const updateConversationBatcher = createBatcher<ConversationType>({
// We only care about the most recent update for each conversation // We only care about the most recent update for each conversation
const byId = groupBy(items, item => item.id); const byId = groupBy(items, item => item.id);
const ids = Object.keys(byId); const ids = Object.keys(byId);
const mostRecent = ids.map( const mostRecent = ids.map((id: string): ConversationType => {
(id: string): ConversationType => { const maybeLast = last(byId[id]);
const maybeLast = last(byId[id]); assert(maybeLast !== undefined, 'Empty array in `groupBy` result');
assert(maybeLast !== undefined, 'Empty array in `groupBy` result'); return maybeLast;
return maybeLast; });
}
);
await updateConversations(mostRecent); await updateConversations(mostRecent);
}, },
@ -1338,14 +1336,11 @@ async function getLastConversationMessages({
ourUuid: UUIDStringType; ourUuid: UUIDStringType;
Message: typeof MessageModel; Message: typeof MessageModel;
}): Promise<LastConversationMessagesType> { }): Promise<LastConversationMessagesType> {
const { const { preview, activity, hasUserInitiatedMessages } =
preview, await channels.getLastConversationMessages({
activity, conversationId,
hasUserInitiatedMessages, ourUuid,
} = await channels.getLastConversationMessages({ });
conversationId,
ourUuid,
});
return { return {
preview: preview ? new Message(preview) : undefined, preview: preview ? new Message(preview) : undefined,

View File

@ -724,7 +724,7 @@ async function getAllItems(): Promise<AllItemsType> {
result[id] = value; result[id] = value;
} }
return (result as unknown) as AllItemsType; return result as unknown as AllItemsType;
} }
async function removeItemById(id: ItemKeyType): Promise<void> { async function removeItemById(id: ItemKeyType): Promise<void> {
return removeById(getInstance(), ITEMS_TABLE, id); return removeById(getInstance(), ITEMS_TABLE, id);
@ -3215,16 +3215,8 @@ async function clearAllErrorStickerPackAttempts(): Promise<void> {
} }
async function createOrUpdateSticker(sticker: StickerType): Promise<void> { async function createOrUpdateSticker(sticker: StickerType): Promise<void> {
const db = getInstance(); const db = getInstance();
const { const { emoji, height, id, isCoverOnly, lastUsed, packId, path, width } =
emoji, sticker;
height,
id,
isCoverOnly,
lastUsed,
packId,
path,
width,
} = sticker;
if (!isNumber(id)) { if (!isNumber(id)) {
throw new Error( throw new Error(

View File

@ -15,9 +15,7 @@ import { isIterable } from '../util/iterables';
* *
* [0]: https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm * [0]: https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm
*/ */
export function cleanDataForIpc( export function cleanDataForIpc(data: unknown): {
data: unknown
): {
// `any`s are dangerous but it's difficult (impossible?) to type this with generics. // `any`s are dangerous but it's difficult (impossible?) to type this with generics.
// eslint-disable-next-line @typescript-eslint/no-explicit-any // eslint-disable-next-line @typescript-eslint/no-explicit-any
cleaned: any; cleaned: any;

View File

@ -98,10 +98,8 @@ export class MainSQL {
join(scriptDir, isBundled ? 'mainWorker.bundle.js' : 'mainWorker.js') join(scriptDir, isBundled ? 'mainWorker.bundle.js' : 'mainWorker.js')
); );
const { const { promise: onCorruption, resolve: resolveCorruption } =
promise: onCorruption, explodePromise<Error>();
resolve: resolveCorruption,
} = explodePromise<Error>();
this.onCorruption = onCorruption; this.onCorruption = onCorruption;
this.worker.on('message', (wrappedResponse: WrappedWorkerResponse) => { this.worker.on('message', (wrappedResponse: WrappedWorkerResponse) => {

Some files were not shown because too many files have changed in this diff Show More