Upgrade Prettier to 2.4.1
This commit is contained in:
parent
f204784afe
commit
5619eeca83
176 changed files with 1961 additions and 2465 deletions
25
app/main.ts
25
app/main.ts
|
@ -1436,9 +1436,8 @@ app.on('ready', async () => {
|
|||
let getMediaAccessStatus;
|
||||
// This function is not supported on Linux, so we have a fallback.
|
||||
if (systemPreferences.getMediaAccessStatus) {
|
||||
getMediaAccessStatus = systemPreferences.getMediaAccessStatus.bind(
|
||||
systemPreferences
|
||||
);
|
||||
getMediaAccessStatus =
|
||||
systemPreferences.getMediaAccessStatus.bind(systemPreferences);
|
||||
} else {
|
||||
getMediaAccessStatus = noop;
|
||||
}
|
||||
|
@ -1814,18 +1813,18 @@ ipc.on(
|
|||
}
|
||||
);
|
||||
|
||||
ipc.on('update-system-tray-setting', (
|
||||
_event,
|
||||
rawSystemTraySetting /* : Readonly<unknown> */
|
||||
) => {
|
||||
const systemTraySetting = parseSystemTraySetting(rawSystemTraySetting);
|
||||
systemTraySettingCache.set(systemTraySetting);
|
||||
ipc.on(
|
||||
'update-system-tray-setting',
|
||||
(_event, rawSystemTraySetting /* : Readonly<unknown> */) => {
|
||||
const systemTraySetting = parseSystemTraySetting(rawSystemTraySetting);
|
||||
systemTraySettingCache.set(systemTraySetting);
|
||||
|
||||
if (systemTrayService) {
|
||||
const isEnabled = shouldMinimizeToSystemTray(systemTraySetting);
|
||||
systemTrayService.setEnabled(isEnabled);
|
||||
if (systemTrayService) {
|
||||
const isEnabled = shouldMinimizeToSystemTray(systemTraySetting);
|
||||
systemTrayService.setEnabled(isEnabled);
|
||||
}
|
||||
}
|
||||
});
|
||||
);
|
||||
|
||||
ipc.on('close-about', () => {
|
||||
if (aboutWindow) {
|
||||
|
|
|
@ -16,11 +16,10 @@
|
|||
window.SignalContext.log.info(
|
||||
'eraseTapToViewMessages: Loading messages...'
|
||||
);
|
||||
const messages = await window.Signal.Data.getTapToViewMessagesNeedingErase(
|
||||
{
|
||||
const messages =
|
||||
await window.Signal.Data.getTapToViewMessagesNeedingErase({
|
||||
MessageCollection: Whisper.MessageCollection,
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
await Promise.all(
|
||||
messages.map(async fromDB => {
|
||||
|
@ -51,7 +50,8 @@
|
|||
const HOUR = 60 * MINUTE;
|
||||
const THIRTY_DAYS = 30 * 24 * HOUR;
|
||||
|
||||
const receivedAt = await window.Signal.Data.getNextTapToViewMessageTimestampToAgeOut();
|
||||
const receivedAt =
|
||||
await window.Signal.Data.getNextTapToViewMessageTimestampToAgeOut();
|
||||
if (!receivedAt) {
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -196,9 +196,8 @@ function initializeMigrations({
|
|||
const getAbsoluteAttachmentPath = createAbsolutePathGetter(attachmentsPath);
|
||||
const deleteOnDisk = Attachments.createDeleter(attachmentsPath);
|
||||
const writeNewAttachmentData = createWriterForNew(attachmentsPath);
|
||||
const copyIntoAttachmentsDirectory = Attachments.copyIntoAttachmentsDirectory(
|
||||
attachmentsPath
|
||||
);
|
||||
const copyIntoAttachmentsDirectory =
|
||||
Attachments.copyIntoAttachmentsDirectory(attachmentsPath);
|
||||
const doesAttachmentExist = createDoesExist(attachmentsPath);
|
||||
|
||||
const stickersPath = getStickersPath(userDataPath);
|
||||
|
@ -216,9 +215,8 @@ function initializeMigrations({
|
|||
const writeNewTempData = createWriterForNew(tempPath);
|
||||
const deleteTempFile = Attachments.createDeleter(tempPath);
|
||||
const readTempData = createReader(tempPath);
|
||||
const copyIntoTempDirectory = Attachments.copyIntoAttachmentsDirectory(
|
||||
tempPath
|
||||
);
|
||||
const copyIntoTempDirectory =
|
||||
Attachments.copyIntoAttachmentsDirectory(tempPath);
|
||||
|
||||
const draftPath = getDraftPath(userDataPath);
|
||||
const getAbsoluteDraftPath = createAbsolutePathGetter(draftPath);
|
||||
|
|
|
@ -194,64 +194,63 @@ exports._mapContact = upgradeContact => async (message, context) => {
|
|||
// _mapQuotedAttachments :: (QuotedAttachment -> Promise QuotedAttachment) ->
|
||||
// (Message, Context) ->
|
||||
// Promise Message
|
||||
exports._mapQuotedAttachments = upgradeAttachment => async (
|
||||
message,
|
||||
context
|
||||
) => {
|
||||
if (!message.quote) {
|
||||
return message;
|
||||
}
|
||||
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;
|
||||
exports._mapQuotedAttachments =
|
||||
upgradeAttachment => async (message, context) => {
|
||||
if (!message.quote) {
|
||||
return message;
|
||||
}
|
||||
if (!context || !isObject(context.logger)) {
|
||||
throw new Error('_mapQuotedAttachments: context must have logger object');
|
||||
}
|
||||
|
||||
const upgradedThumbnail = await upgradeAttachment(thumbnail, context);
|
||||
return { ...attachment, thumbnail: upgradedThumbnail };
|
||||
const upgradeWithContext = async attachment => {
|
||||
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) ->
|
||||
// (Message, Context) ->
|
||||
// Promise Message
|
||||
exports._mapPreviewAttachments = upgradeAttachment => async (
|
||||
message,
|
||||
context
|
||||
) => {
|
||||
if (!message.preview) {
|
||||
return message;
|
||||
}
|
||||
if (!context || !isObject(context.logger)) {
|
||||
throw new Error('_mapPreviewAttachments: context must have logger object');
|
||||
}
|
||||
|
||||
const upgradeWithContext = async preview => {
|
||||
const { image } = preview;
|
||||
if (!image) {
|
||||
return preview;
|
||||
exports._mapPreviewAttachments =
|
||||
upgradeAttachment => async (message, context) => {
|
||||
if (!message.preview) {
|
||||
return message;
|
||||
}
|
||||
if (!context || !isObject(context.logger)) {
|
||||
throw new Error(
|
||||
'_mapPreviewAttachments: context must have logger object'
|
||||
);
|
||||
}
|
||||
|
||||
const upgradedImage = await upgradeAttachment(image, context);
|
||||
return { ...preview, image: upgradedImage };
|
||||
};
|
||||
const upgradeWithContext = async preview => {
|
||||
const { image } = preview;
|
||||
if (!image) {
|
||||
return preview;
|
||||
}
|
||||
|
||||
const preview = await Promise.all(
|
||||
(message.preview || []).map(upgradeWithContext)
|
||||
);
|
||||
return { ...message, preview };
|
||||
};
|
||||
const upgradedImage = await upgradeAttachment(image, context);
|
||||
return { ...preview, image: upgradedImage };
|
||||
};
|
||||
|
||||
const preview = await Promise.all(
|
||||
(message.preview || []).map(upgradeWithContext)
|
||||
);
|
||||
return { ...message, preview };
|
||||
};
|
||||
|
||||
const toVersion0 = async (message, context) =>
|
||||
exports.initializeSchemaVersion({ message, logger: context.logger });
|
||||
|
|
|
@ -136,5 +136,9 @@
|
|||
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;
|
||||
})();
|
||||
|
|
|
@ -48,9 +48,8 @@ const fakeAPI = {
|
|||
throw new Error('Invalid message');
|
||||
}
|
||||
|
||||
messagesSentMap[
|
||||
`${destination}.${messageArray[i].destinationDeviceId}`
|
||||
] = msg;
|
||||
messagesSentMap[`${destination}.${messageArray[i].destinationDeviceId}`] =
|
||||
msg;
|
||||
}
|
||||
},
|
||||
};
|
||||
|
|
|
@ -5,257 +5,33 @@
|
|||
/* eslint-disable no-console */
|
||||
|
||||
const BUCKET_SIZES = [
|
||||
541,
|
||||
568,
|
||||
596,
|
||||
626,
|
||||
657,
|
||||
690,
|
||||
725,
|
||||
761,
|
||||
799,
|
||||
839,
|
||||
881,
|
||||
925,
|
||||
972,
|
||||
1020,
|
||||
1071,
|
||||
1125,
|
||||
1181,
|
||||
1240,
|
||||
1302,
|
||||
1367,
|
||||
1436,
|
||||
1507,
|
||||
1583,
|
||||
1662,
|
||||
1745,
|
||||
1832,
|
||||
1924,
|
||||
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,
|
||||
541, 568, 596, 626, 657, 690, 725, 761, 799, 839, 881, 925, 972, 1020, 1071,
|
||||
1125, 1181, 1240, 1302, 1367, 1436, 1507, 1583, 1662, 1745, 1832, 1924, 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', () => {
|
||||
|
|
|
@ -275,7 +275,7 @@
|
|||
"npm-run-all": "4.1.5",
|
||||
"nyc": "11.4.1",
|
||||
"patch-package": "6.4.7",
|
||||
"prettier": "^2.2.1",
|
||||
"prettier": "2.4.1",
|
||||
"react-docgen-typescript": "1.2.6",
|
||||
"sass-loader": "10.2.0",
|
||||
"sinon": "11.1.1",
|
||||
|
|
16
preload.js
16
preload.js
|
@ -389,18 +389,18 @@ try {
|
|||
|
||||
window.imageToBlurHash = imageToBlurHash;
|
||||
window.emojiData = require('emoji-datasource');
|
||||
window.libphonenumber = require('google-libphonenumber').PhoneNumberUtil.getInstance();
|
||||
window.libphonenumber.PhoneNumberFormat = require('google-libphonenumber').PhoneNumberFormat;
|
||||
window.libphonenumber =
|
||||
require('google-libphonenumber').PhoneNumberUtil.getInstance();
|
||||
window.libphonenumber.PhoneNumberFormat =
|
||||
require('google-libphonenumber').PhoneNumberFormat;
|
||||
|
||||
const activeWindowService = new ActiveWindowService();
|
||||
activeWindowService.initialize(window.document, ipc);
|
||||
window.isActive = activeWindowService.isActive.bind(activeWindowService);
|
||||
window.registerForActive = activeWindowService.registerForActive.bind(
|
||||
activeWindowService
|
||||
);
|
||||
window.unregisterForActive = activeWindowService.unregisterForActive.bind(
|
||||
activeWindowService
|
||||
);
|
||||
window.registerForActive =
|
||||
activeWindowService.registerForActive.bind(activeWindowService);
|
||||
window.unregisterForActive =
|
||||
activeWindowService.unregisterForActive.bind(activeWindowService);
|
||||
|
||||
window.Accessibility = {
|
||||
reducedMotionSetting: Boolean(config.reducedMotionSetting),
|
||||
|
|
|
@ -35,9 +35,8 @@ export const MetaStage: React.ComponentType = () => {
|
|||
[actions]
|
||||
);
|
||||
|
||||
const { getRootProps, getInputProps, isDragActive } = useStickerDropzone(
|
||||
onDrop
|
||||
);
|
||||
const { getRootProps, getInputProps, isDragActive } =
|
||||
useStickerDropzone(onDrop);
|
||||
|
||||
const onNext = React.useCallback(() => {
|
||||
setConfirming(true);
|
||||
|
|
|
@ -81,15 +81,11 @@ export const StickerFrame = React.memo(
|
|||
}: Props) => {
|
||||
const i18n = useI18n();
|
||||
const [emojiPickerOpen, setEmojiPickerOpen] = React.useState(false);
|
||||
const [
|
||||
emojiPopperRoot,
|
||||
setEmojiPopperRoot,
|
||||
] = React.useState<HTMLElement | null>(null);
|
||||
const [emojiPopperRoot, setEmojiPopperRoot] =
|
||||
React.useState<HTMLElement | null>(null);
|
||||
const [previewActive, setPreviewActive] = React.useState(false);
|
||||
const [
|
||||
previewPopperRoot,
|
||||
setPreviewPopperRoot,
|
||||
] = React.useState<HTMLElement | null>(null);
|
||||
const [previewPopperRoot, setPreviewPopperRoot] =
|
||||
React.useState<HTMLElement | null>(null);
|
||||
const timerRef = React.useRef<number>();
|
||||
|
||||
const handleToggleEmojiPicker = React.useCallback(() => {
|
||||
|
|
|
@ -37,9 +37,8 @@ export const DropZone: React.ComponentType<Props> = props => {
|
|||
[onDrop]
|
||||
);
|
||||
|
||||
const { getRootProps, getInputProps, isDragActive } = useStickerDropzone(
|
||||
handleDrop
|
||||
);
|
||||
const { getRootProps, getInputProps, isDragActive } =
|
||||
useStickerDropzone(handleDrop);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (onDragActive) {
|
||||
|
|
|
@ -169,9 +169,8 @@ window.encryptAndUpload = async (
|
|||
const passwordItem = await window.Signal.Data.getItemById('password');
|
||||
|
||||
if (!oldUsernameItem || !passwordItem) {
|
||||
const { message } = window.localeMessages[
|
||||
'StickerCreator--Authentication--error'
|
||||
];
|
||||
const { message } =
|
||||
window.localeMessages['StickerCreator--Authentication--error'];
|
||||
|
||||
ipc.send('show-message-box', {
|
||||
type: 'warning',
|
||||
|
|
|
@ -32,9 +32,8 @@ export const removeSticker = createAction<string>('stickers/removeSticker');
|
|||
export const moveSticker = createAction<SortEnd>('stickers/moveSticker');
|
||||
export const setCover = createAction<StickerImageData>('stickers/setCover');
|
||||
export const resetCover = createAction<StickerImageData>('stickers/resetCover');
|
||||
export const setEmoji = createAction<{ id: string; emoji: EmojiPickDataType }>(
|
||||
'stickers/setEmoji'
|
||||
);
|
||||
export const setEmoji =
|
||||
createAction<{ id: string; emoji: EmojiPickDataType }>('stickers/setEmoji');
|
||||
export const setTitle = createAction<string>('stickers/setTitle');
|
||||
export const setAuthor = createAction<string>('stickers/setAuthor');
|
||||
export const setPackMeta = createAction<PackMetaData>('stickers/setPackMeta');
|
||||
|
|
|
@ -72,8 +72,7 @@ describe('Stickers', () => {
|
|||
),
|
||||
{
|
||||
id: 'c8c83285b547872ac4c589d64a6edd6a',
|
||||
key:
|
||||
'59bb3a8860f0e6a5a83a5337a015c8d55ecd2193f82d77202f3b8112a845636e',
|
||||
key: '59bb3a8860f0e6a5a83a5337a015c8d55ecd2193f82d77202f3b8112a845636e',
|
||||
}
|
||||
);
|
||||
});
|
||||
|
@ -85,8 +84,7 @@ describe('Stickers', () => {
|
|||
),
|
||||
{
|
||||
id: 'c8c83285b547872ac4c589d64a6edd6a',
|
||||
key:
|
||||
'59bb3a8860f0e6a5a83a5337a015c8d55ecd2193f82d77202f3b8112a845636e',
|
||||
key: '59bb3a8860f0e6a5a83a5337a015c8d55ecd2193f82d77202f3b8112a845636e',
|
||||
}
|
||||
);
|
||||
});
|
||||
|
@ -98,8 +96,7 @@ describe('Stickers', () => {
|
|||
),
|
||||
{
|
||||
id: 'c8c83285b547872ac4c589d64a6edd6a',
|
||||
key:
|
||||
'59bb3a8860f0e6a5a83a5337a015c8d55ecd2193f82d77202f3b8112a845636e',
|
||||
key: '59bb3a8860f0e6a5a83a5337a015c8d55ecd2193f82d77202f3b8112a845636e',
|
||||
}
|
||||
);
|
||||
});
|
||||
|
|
|
@ -409,10 +409,8 @@ export async function encryptCdsDiscoveryRequest(
|
|||
iv,
|
||||
queryDataPlaintext
|
||||
);
|
||||
const {
|
||||
data: queryDataCiphertextData,
|
||||
mac: queryDataCiphertextMac,
|
||||
} = _getMacAndData(queryDataCiphertext);
|
||||
const { data: queryDataCiphertextData, mac: queryDataCiphertextMac } =
|
||||
_getMacAndData(queryDataCiphertext);
|
||||
|
||||
const envelopes = await pProps(
|
||||
attestations,
|
||||
|
|
|
@ -279,10 +279,11 @@ export class SignedPreKeys extends SignedPreKeyStore {
|
|||
}
|
||||
|
||||
async getSignedPreKey(id: number): Promise<SignedPreKeyRecord> {
|
||||
const signedPreKey = await window.textsecure.storage.protocol.loadSignedPreKey(
|
||||
this.ourUuid,
|
||||
id
|
||||
);
|
||||
const signedPreKey =
|
||||
await window.textsecure.storage.protocol.loadSignedPreKey(
|
||||
this.ourUuid,
|
||||
id
|
||||
);
|
||||
|
||||
if (!signedPreKey) {
|
||||
throw new Error(`getSignedPreKey: SignedPreKey ${id} not found`);
|
||||
|
|
|
@ -185,10 +185,10 @@ export function freezeSignedPreKey(
|
|||
}
|
||||
|
||||
// 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);
|
||||
// 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>;
|
||||
|
||||
|
|
147
ts/background.ts
147
ts/background.ts
|
@ -371,8 +371,8 @@ export async function startApp(): Promise<void> {
|
|||
timeout: 1000 * 60 * 2,
|
||||
});
|
||||
window.Whisper.deliveryReceiptQueue.pause();
|
||||
window.Whisper.deliveryReceiptBatcher = window.Signal.Util.createBatcher<DeliveryReceiptBatcherItemType>(
|
||||
{
|
||||
window.Whisper.deliveryReceiptBatcher =
|
||||
window.Signal.Util.createBatcher<DeliveryReceiptBatcherItemType>({
|
||||
name: 'Whisper.deliveryReceiptBatcher',
|
||||
wait: 500,
|
||||
maxSize: 100,
|
||||
|
@ -426,8 +426,7 @@ export async function startApp(): Promise<void> {
|
|||
});
|
||||
}
|
||||
},
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
if (getTitleBarVisibility() === TitleBarVisibility.Hidden) {
|
||||
window.addEventListener('dblclick', (event: Event) => {
|
||||
|
@ -476,10 +475,8 @@ export async function startApp(): Promise<void> {
|
|||
window.setImmediate = window.nodeSetImmediate;
|
||||
|
||||
const { IdleDetector, MessageDataMigrator } = window.Signal.Workflow;
|
||||
const {
|
||||
removeDatabase: removeIndexedDB,
|
||||
doesDatabaseExist,
|
||||
} = window.Signal.IndexedDB;
|
||||
const { removeDatabase: removeIndexedDB, doesDatabaseExist } =
|
||||
window.Signal.IndexedDB;
|
||||
const { Message } = window.Signal.Types;
|
||||
const {
|
||||
upgradeMessageSchema,
|
||||
|
@ -868,12 +865,12 @@ export async function startApp(): Promise<void> {
|
|||
);
|
||||
expired.forEach(item => {
|
||||
const { conversationId, senderUuid, sentAt } = item;
|
||||
const conversation = window.ConversationController.get(
|
||||
conversationId
|
||||
);
|
||||
const conversation =
|
||||
window.ConversationController.get(conversationId);
|
||||
if (conversation) {
|
||||
const receivedAt = Date.now();
|
||||
const receivedAtCounter = window.Signal.Util.incrementMessageCounter();
|
||||
const receivedAtCounter =
|
||||
window.Signal.Util.incrementMessageCounter();
|
||||
conversation.queueJob('addDeliveryIssue', () =>
|
||||
conversation.addDeliveryIssue({
|
||||
receivedAt,
|
||||
|
@ -933,7 +930,8 @@ export async function startApp(): Promise<void> {
|
|||
);
|
||||
const ourNumber = window.textsecure.storage.user.getNumber();
|
||||
const ourUuid = window.textsecure.storage.user.getUuid()?.toString();
|
||||
const ourConversationId = window.ConversationController.getOurConversationId();
|
||||
const ourConversationId =
|
||||
window.ConversationController.getOurConversationId();
|
||||
|
||||
const themeSetting = window.Events.getThemeSetting();
|
||||
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 newNumber = window.textsecure.storage.user.getNumber();
|
||||
const newUuid = window.textsecure.storage.user.getUuid()?.toString();
|
||||
const ourConversation = window.ConversationController.getOurConversation();
|
||||
const ourConversation =
|
||||
window.ConversationController.getOurConversation();
|
||||
|
||||
if (ourConversation?.get('e164') !== newNumber) {
|
||||
ourConversation?.set('e164', newNumber);
|
||||
|
@ -1686,7 +1685,8 @@ export async function startApp(): Promise<void> {
|
|||
window.Signal.challengeHandler = challengeHandler;
|
||||
|
||||
if (!window.storage.user.getNumber()) {
|
||||
const ourConversation = window.ConversationController.getOurConversation();
|
||||
const ourConversation =
|
||||
window.ConversationController.getOurConversation();
|
||||
const ourE164 = ourConversation?.get('e164');
|
||||
if (ourE164) {
|
||||
log.warn('Restoring E164 from our conversation');
|
||||
|
@ -1699,13 +1699,14 @@ export async function startApp(): Promise<void> {
|
|||
badgeImageFileDownloader.checkForFilesToDownload();
|
||||
|
||||
log.info('Expiration start timestamp cleanup: starting...');
|
||||
const messagesUnexpectedlyMissingExpirationStartTimestamp = await window.Signal.Data.getMessagesUnexpectedlyMissingExpirationStartTimestamp();
|
||||
const messagesUnexpectedlyMissingExpirationStartTimestamp =
|
||||
await window.Signal.Data.getMessagesUnexpectedlyMissingExpirationStartTimestamp();
|
||||
log.info(
|
||||
`Expiration start timestamp cleanup: Found ${messagesUnexpectedlyMissingExpirationStartTimestamp.length} messages for cleanup`
|
||||
);
|
||||
if (messagesUnexpectedlyMissingExpirationStartTimestamp.length) {
|
||||
const newMessageAttributes = messagesUnexpectedlyMissingExpirationStartTimestamp.map(
|
||||
message => {
|
||||
const newMessageAttributes =
|
||||
messagesUnexpectedlyMissingExpirationStartTimestamp.map(message => {
|
||||
const expirationStartTimestamp = Math.min(
|
||||
...filter(
|
||||
[
|
||||
|
@ -1727,8 +1728,7 @@ export async function startApp(): Promise<void> {
|
|||
...message,
|
||||
expirationStartTimestamp,
|
||||
};
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
await window.Signal.Data.saveMessages(newMessageAttributes);
|
||||
}
|
||||
|
@ -1804,9 +1804,8 @@ export async function startApp(): Promise<void> {
|
|||
window.Signal.RemoteConfig.onChange(
|
||||
'desktop.clientExpiration',
|
||||
({ value }) => {
|
||||
const remoteBuildExpirationTimestamp = window.Signal.Util.parseRemoteClientExpiration(
|
||||
value as string
|
||||
);
|
||||
const remoteBuildExpirationTimestamp =
|
||||
window.Signal.Util.parseRemoteClientExpiration(value as string);
|
||||
if (remoteBuildExpirationTimestamp) {
|
||||
window.storage.put(
|
||||
'remoteBuildExpiration',
|
||||
|
@ -2013,9 +2012,10 @@ export async function startApp(): Promise<void> {
|
|||
'desktop.clientExpiration'
|
||||
);
|
||||
if (expiration) {
|
||||
const remoteBuildExpirationTimestamp = window.Signal.Util.parseRemoteClientExpiration(
|
||||
expiration as string
|
||||
);
|
||||
const remoteBuildExpirationTimestamp =
|
||||
window.Signal.Util.parseRemoteClientExpiration(
|
||||
expiration as string
|
||||
);
|
||||
if (remoteBuildExpirationTimestamp) {
|
||||
window.storage.put(
|
||||
'remoteBuildExpiration',
|
||||
|
@ -2183,7 +2183,8 @@ export async function startApp(): Promise<void> {
|
|||
runStorageService();
|
||||
});
|
||||
|
||||
const ourConversation = window.ConversationController.getOurConversationOrThrow();
|
||||
const ourConversation =
|
||||
window.ConversationController.getOurConversationOrThrow();
|
||||
const sendOptions = await getSendOptions(ourConversation.attributes, {
|
||||
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
|
||||
// tolerable.
|
||||
const ourConversationId = window.ConversationController.getOurConversationId();
|
||||
const ourConversationId =
|
||||
window.ConversationController.getOurConversationId();
|
||||
if (ourConversationId) {
|
||||
routineProfileRefresh({
|
||||
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,
|
||||
uuid: details.uuid,
|
||||
type: 'private',
|
||||
} as Partial<ConversationAttributesType>) as WhatIsThis);
|
||||
} as Partial<ConversationAttributesType> as WhatIsThis);
|
||||
const validationError = c.validate();
|
||||
if (validationError) {
|
||||
log.error(
|
||||
|
@ -3000,32 +3002,38 @@ export async function startApp(): Promise<void> {
|
|||
|
||||
const { unidentifiedStatus = [] } = data;
|
||||
|
||||
const sendStateByConversationId: SendStateByConversationId = unidentifiedStatus.reduce(
|
||||
(result: SendStateByConversationId, { destinationUuid, destination }) => {
|
||||
const conversationId = window.ConversationController.ensureContactIds({
|
||||
uuid: destinationUuid,
|
||||
e164: destination,
|
||||
highTrust: true,
|
||||
});
|
||||
if (!conversationId || conversationId === ourId) {
|
||||
return result;
|
||||
}
|
||||
const sendStateByConversationId: SendStateByConversationId =
|
||||
unidentifiedStatus.reduce(
|
||||
(
|
||||
result: SendStateByConversationId,
|
||||
{ destinationUuid, destination }
|
||||
) => {
|
||||
const conversationId = window.ConversationController.ensureContactIds(
|
||||
{
|
||||
uuid: destinationUuid,
|
||||
e164: destination,
|
||||
highTrust: true,
|
||||
}
|
||||
);
|
||||
if (!conversationId || conversationId === ourId) {
|
||||
return result;
|
||||
}
|
||||
|
||||
return {
|
||||
...result,
|
||||
[conversationId]: {
|
||||
return {
|
||||
...result,
|
||||
[conversationId]: {
|
||||
status: SendStatus.Sent,
|
||||
updatedAt: timestamp,
|
||||
},
|
||||
};
|
||||
},
|
||||
{
|
||||
[ourId]: {
|
||||
status: SendStatus.Sent,
|
||||
updatedAt: timestamp,
|
||||
},
|
||||
};
|
||||
},
|
||||
{
|
||||
[ourId]: {
|
||||
status: SendStatus.Sent,
|
||||
updatedAt: timestamp,
|
||||
},
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
let unidentifiedDeliveries: Array<string> = [];
|
||||
if (unidentifiedStatus.length) {
|
||||
|
@ -3037,7 +3045,7 @@ export async function startApp(): Promise<void> {
|
|||
.filter(isNotNil);
|
||||
}
|
||||
|
||||
return new window.Whisper.Message(({
|
||||
return new window.Whisper.Message({
|
||||
source: window.textsecure.storage.user.getNumber(),
|
||||
sourceUuid: window.textsecure.storage.user.getUuid()?.toString(),
|
||||
sourceDevice: data.device,
|
||||
|
@ -3054,7 +3062,7 @@ export async function startApp(): Promise<void> {
|
|||
data.expirationStartTimestamp || timestamp,
|
||||
now
|
||||
),
|
||||
} as Partial<MessageAttributesType>) as WhatIsThis);
|
||||
} as Partial<MessageAttributesType> as WhatIsThis);
|
||||
}
|
||||
|
||||
// Works with 'sent' and 'message' data sent from MessageReceiver, with a little massage
|
||||
|
@ -3122,9 +3130,8 @@ export async function startApp(): Promise<void> {
|
|||
);
|
||||
} else {
|
||||
// First we check for an already-migrated GroupV2 group
|
||||
const migratedGroup = window.ConversationController.get(
|
||||
derivedGroupV2Id
|
||||
);
|
||||
const migratedGroup =
|
||||
window.ConversationController.get(derivedGroupV2Id);
|
||||
if (migratedGroup) {
|
||||
return {
|
||||
type: Message.GROUP,
|
||||
|
@ -3275,7 +3282,7 @@ export async function startApp(): Promise<void> {
|
|||
Boolean(data.receivedAtCounter),
|
||||
`Did not receive receivedAtCounter for message: ${data.timestamp}`
|
||||
);
|
||||
return new window.Whisper.Message(({
|
||||
return new window.Whisper.Message({
|
||||
source: data.source,
|
||||
sourceUuid: data.sourceUuid,
|
||||
sourceDevice: data.sourceDevice,
|
||||
|
@ -3289,7 +3296,7 @@ export async function startApp(): Promise<void> {
|
|||
type: 'incoming',
|
||||
readStatus: ReadStatus.Unread,
|
||||
timestamp: data.timestamp,
|
||||
} as Partial<MessageAttributesType>) as WhatIsThis);
|
||||
} as Partial<MessageAttributesType> as WhatIsThis);
|
||||
}
|
||||
|
||||
// Returns `false` if this message isn't a group call message.
|
||||
|
@ -3545,13 +3552,8 @@ export async function startApp(): Promise<void> {
|
|||
logTitle: string;
|
||||
type: MessageReceiptType.Read | MessageReceiptType.View;
|
||||
}>): void {
|
||||
const {
|
||||
envelopeTimestamp,
|
||||
timestamp,
|
||||
source,
|
||||
sourceUuid,
|
||||
sourceDevice,
|
||||
} = event.receipt;
|
||||
const { envelopeTimestamp, timestamp, source, sourceUuid, sourceDevice } =
|
||||
event.receipt;
|
||||
const sourceConversationId = window.ConversationController.ensureContactIds(
|
||||
{
|
||||
e164: source,
|
||||
|
@ -3664,11 +3666,11 @@ export async function startApp(): Promise<void> {
|
|||
ev.confirm();
|
||||
}
|
||||
|
||||
const c = new window.Whisper.Conversation(({
|
||||
const c = new window.Whisper.Conversation({
|
||||
e164,
|
||||
uuid,
|
||||
type: 'private',
|
||||
} as Partial<ConversationAttributesType>) as WhatIsThis);
|
||||
} as Partial<ConversationAttributesType> as WhatIsThis);
|
||||
const error = c.validate();
|
||||
if (error) {
|
||||
log.error(
|
||||
|
@ -3726,13 +3728,8 @@ export async function startApp(): Promise<void> {
|
|||
|
||||
function onDeliveryReceipt(ev: DeliveryEvent) {
|
||||
const { deliveryReceipt } = ev;
|
||||
const {
|
||||
envelopeTimestamp,
|
||||
sourceUuid,
|
||||
source,
|
||||
sourceDevice,
|
||||
timestamp,
|
||||
} = deliveryReceipt;
|
||||
const { envelopeTimestamp, sourceUuid, source, sourceDevice, timestamp } =
|
||||
deliveryReceipt;
|
||||
|
||||
ev.confirm();
|
||||
|
||||
|
|
|
@ -154,13 +154,12 @@ export class ChallengeHandler {
|
|||
|
||||
const retryIds = new Set<string>(stored.map(({ messageId }) => messageId));
|
||||
|
||||
const maybeMessages: ReadonlyArray<
|
||||
MinimalMessage | undefined
|
||||
> = await Promise.all(
|
||||
Array.from(retryIds).map(async messageId =>
|
||||
this.options.getMessageById(messageId)
|
||||
)
|
||||
);
|
||||
const maybeMessages: ReadonlyArray<MinimalMessage | undefined> =
|
||||
await Promise.all(
|
||||
Array.from(retryIds).map(async messageId =>
|
||||
this.options.getMessageById(messageId)
|
||||
)
|
||||
);
|
||||
|
||||
const messages: Array<MinimalMessage> = maybeMessages.filter(isNotNil);
|
||||
|
||||
|
|
|
@ -35,43 +35,47 @@ type PropsType = {
|
|||
onClose: () => void;
|
||||
} & PropsDataType;
|
||||
|
||||
export const AddGroupMemberErrorDialog: FunctionComponent<PropsType> = props => {
|
||||
const { i18n, onClose } = props;
|
||||
export const AddGroupMemberErrorDialog: FunctionComponent<PropsType> =
|
||||
props => {
|
||||
const { i18n, onClose } = props;
|
||||
|
||||
let title: string;
|
||||
let body: ReactNode;
|
||||
switch (props.mode) {
|
||||
case AddGroupMemberErrorDialogMode.CantAddContact: {
|
||||
const { contact } = props;
|
||||
title = i18n('chooseGroupMembers__cant-add-member__title');
|
||||
body = (
|
||||
<Intl
|
||||
i18n={i18n}
|
||||
id="chooseGroupMembers__cant-add-member__body"
|
||||
components={[<ContactName key="name" title={contact.title} />]}
|
||||
/>
|
||||
);
|
||||
break;
|
||||
let title: string;
|
||||
let body: ReactNode;
|
||||
switch (props.mode) {
|
||||
case AddGroupMemberErrorDialogMode.CantAddContact: {
|
||||
const { contact } = props;
|
||||
title = i18n('chooseGroupMembers__cant-add-member__title');
|
||||
body = (
|
||||
<Intl
|
||||
i18n={i18n}
|
||||
id="chooseGroupMembers__cant-add-member__body"
|
||||
components={[<ContactName key="name" title={contact.title} />]}
|
||||
/>
|
||||
);
|
||||
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} />;
|
||||
};
|
||||
|
|
|
@ -23,9 +23,8 @@ export const AvatarIconEditor = ({
|
|||
onClose,
|
||||
}: PropsType): JSX.Element => {
|
||||
const [avatarBuffer, setAvatarBuffer] = useState<Uint8Array | undefined>();
|
||||
const [avatarData, setAvatarData] = useState<AvatarDataType>(
|
||||
initialAvatarData
|
||||
);
|
||||
const [avatarData, setAvatarData] =
|
||||
useState<AvatarDataType>(initialAvatarData);
|
||||
|
||||
const onColorSelected = useCallback(
|
||||
(color: AvatarColorType) => {
|
||||
|
|
|
@ -47,9 +47,10 @@ export const AvatarTextEditor = ({
|
|||
onDone,
|
||||
}: PropsType): JSX.Element => {
|
||||
const initialText = useMemo(() => avatarData?.text || '', [avatarData]);
|
||||
const initialColor = useMemo(() => avatarData?.color || AvatarColors[0], [
|
||||
avatarData,
|
||||
]);
|
||||
const initialColor = useMemo(
|
||||
() => avatarData?.color || AvatarColors[0],
|
||||
[avatarData]
|
||||
);
|
||||
|
||||
const [inputText, setInputText] = useState(initialText);
|
||||
const [fontSize, setFontSize] = useState(getFontSizes(BUBBLE_SIZE).text);
|
||||
|
|
|
@ -514,7 +514,8 @@ export const CallScreen: React.FC<PropsType> = ({
|
|||
</div>
|
||||
<div
|
||||
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}
|
||||
|
|
|
@ -58,7 +58,8 @@ export const CallingHeader = ({
|
|||
className={classNames(
|
||||
'module-calling-button__participants--container',
|
||||
{
|
||||
'module-calling-button__participants--shown': showParticipantsList,
|
||||
'module-calling-button__participants--shown':
|
||||
showParticipantsList,
|
||||
}
|
||||
)}
|
||||
onClick={toggleParticipants}
|
||||
|
|
|
@ -77,10 +77,8 @@ export const CallingLobbyJoinButton: FunctionComponent<{
|
|||
if (!button) {
|
||||
return;
|
||||
}
|
||||
const {
|
||||
width: variantWidth,
|
||||
height: variantHeight,
|
||||
} = button.getBoundingClientRect();
|
||||
const { width: variantWidth, height: variantHeight } =
|
||||
button.getBoundingClientRect();
|
||||
|
||||
// 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
|
||||
|
|
|
@ -90,17 +90,16 @@ export const CallingPipRemoteVideo = ({
|
|||
|
||||
const isPageVisible = usePageVisibility();
|
||||
|
||||
const activeGroupCallSpeaker:
|
||||
| undefined
|
||||
| GroupCallRemoteParticipantType = useMemo(() => {
|
||||
if (activeCall.callMode !== CallMode.Group) {
|
||||
return undefined;
|
||||
}
|
||||
const activeGroupCallSpeaker: undefined | GroupCallRemoteParticipantType =
|
||||
useMemo(() => {
|
||||
if (activeCall.callMode !== CallMode.Group) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return maxBy(activeCall.remoteParticipants, participant =>
|
||||
participant.presenting ? Infinity : participant.speakerTime || -Infinity
|
||||
);
|
||||
}, [activeCall.callMode, activeCall.remoteParticipants]);
|
||||
return maxBy(activeCall.remoteParticipants, participant =>
|
||||
participant.presenting ? Infinity : participant.speakerTime || -Infinity
|
||||
);
|
||||
}, [activeCall.callMode, activeCall.remoteParticipants]);
|
||||
|
||||
useEffect(() => {
|
||||
if (activeCall.callMode !== CallMode.Group) {
|
||||
|
|
|
@ -110,18 +110,12 @@ export function CompositionInput(props: Props): React.ReactElement {
|
|||
sortedGroupMembers,
|
||||
} = props;
|
||||
|
||||
const [
|
||||
emojiCompletionElement,
|
||||
setEmojiCompletionElement,
|
||||
] = React.useState<JSX.Element>();
|
||||
const [
|
||||
lastSelectionRange,
|
||||
setLastSelectionRange,
|
||||
] = React.useState<RangeStatic | null>(null);
|
||||
const [
|
||||
mentionCompletionElement,
|
||||
setMentionCompletionElement,
|
||||
] = React.useState<JSX.Element>();
|
||||
const [emojiCompletionElement, setEmojiCompletionElement] =
|
||||
React.useState<JSX.Element>();
|
||||
const [lastSelectionRange, setLastSelectionRange] =
|
||||
React.useState<RangeStatic | null>(null);
|
||||
const [mentionCompletionElement, setMentionCompletionElement] =
|
||||
React.useState<JSX.Element>();
|
||||
|
||||
const emojiCompletionRef = React.useRef<EmojiCompletion>();
|
||||
const mentionCompletionRef = React.useRef<MentionCompletion>();
|
||||
|
@ -607,9 +601,8 @@ export function CompositionInput(props: Props): React.ReactElement {
|
|||
);
|
||||
quillRef.current = quill;
|
||||
emojiCompletionRef.current = quill.getModule('emojiCompletion');
|
||||
mentionCompletionRef.current = quill.getModule(
|
||||
'mentionCompletion'
|
||||
);
|
||||
mentionCompletionRef.current =
|
||||
quill.getModule('mentionCompletion');
|
||||
}
|
||||
}}
|
||||
/>
|
||||
|
|
|
@ -49,19 +49,20 @@ export const CompositionUpload = forwardRef<HTMLInputElement, PropsType>(
|
|||
AttachmentToastType | undefined
|
||||
>();
|
||||
|
||||
const onFileInputChange: ChangeEventHandler<HTMLInputElement> = async event => {
|
||||
const files = event.target.files || [];
|
||||
const onFileInputChange: ChangeEventHandler<HTMLInputElement> =
|
||||
async event => {
|
||||
const files = event.target.files || [];
|
||||
|
||||
await processAttachments({
|
||||
addAttachment,
|
||||
addPendingAttachment,
|
||||
conversationId,
|
||||
files: Array.from(files),
|
||||
draftAttachments,
|
||||
onShowToast: setToastType,
|
||||
removeAttachment,
|
||||
});
|
||||
};
|
||||
await processAttachments({
|
||||
addAttachment,
|
||||
addPendingAttachment,
|
||||
conversationId,
|
||||
files: Array.from(files),
|
||||
draftAttachments,
|
||||
onShowToast: setToastType,
|
||||
removeAttachment,
|
||||
});
|
||||
};
|
||||
|
||||
function closeToast() {
|
||||
setToastType(undefined);
|
||||
|
|
|
@ -70,16 +70,8 @@ export class ContactListItem extends React.Component<Props> {
|
|||
}
|
||||
|
||||
public render(): JSX.Element {
|
||||
const {
|
||||
about,
|
||||
i18n,
|
||||
isAdmin,
|
||||
isMe,
|
||||
name,
|
||||
onClick,
|
||||
title,
|
||||
type,
|
||||
} = this.props;
|
||||
const { about, i18n, isAdmin, isMe, name, onClick, title, type } =
|
||||
this.props;
|
||||
|
||||
const displayName = isMe ? i18n('you') : title;
|
||||
|
||||
|
|
|
@ -35,8 +35,7 @@ const defaultConversations: Array<ConversationListItemPropsType> = [
|
|||
title: 'Marc Barraca',
|
||||
lastMessage: {
|
||||
deletedForEveryone: false,
|
||||
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.',
|
||||
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.',
|
||||
},
|
||||
}),
|
||||
getDefaultConversation({
|
||||
|
|
|
@ -17,27 +17,26 @@ const story = storiesOf(
|
|||
module
|
||||
);
|
||||
|
||||
const defaultProps: ComponentProps<
|
||||
typeof CustomizingPreferredReactionsModal
|
||||
> = {
|
||||
cancelCustomizePreferredReactionsModal: action(
|
||||
'cancelCustomizePreferredReactionsModal'
|
||||
),
|
||||
deselectDraftEmoji: action('deselectDraftEmoji'),
|
||||
draftPreferredReactions: ['✨', '❇️', '🎇', '🦈', '💖', '🅿️'],
|
||||
hadSaveError: false,
|
||||
i18n,
|
||||
isSaving: false,
|
||||
onSetSkinTone: action('onSetSkinTone'),
|
||||
originalPreferredReactions: ['❤️', '👍', '👎', '😂', '😮', '😢'],
|
||||
recentEmojis: ['cake'],
|
||||
replaceSelectedDraftEmoji: action('replaceSelectedDraftEmoji'),
|
||||
resetDraftEmoji: action('resetDraftEmoji'),
|
||||
savePreferredReactions: action('savePreferredReactions'),
|
||||
selectDraftEmojiToBeReplaced: action('selectDraftEmojiToBeReplaced'),
|
||||
selectedDraftEmojiIndex: undefined,
|
||||
skinTone: 4,
|
||||
};
|
||||
const defaultProps: ComponentProps<typeof CustomizingPreferredReactionsModal> =
|
||||
{
|
||||
cancelCustomizePreferredReactionsModal: action(
|
||||
'cancelCustomizePreferredReactionsModal'
|
||||
),
|
||||
deselectDraftEmoji: action('deselectDraftEmoji'),
|
||||
draftPreferredReactions: ['✨', '❇️', '🎇', '🦈', '💖', '🅿️'],
|
||||
hadSaveError: false,
|
||||
i18n,
|
||||
isSaving: false,
|
||||
onSetSkinTone: action('onSetSkinTone'),
|
||||
originalPreferredReactions: ['❤️', '👍', '👎', '😂', '😮', '😢'],
|
||||
recentEmojis: ['cake'],
|
||||
replaceSelectedDraftEmoji: action('replaceSelectedDraftEmoji'),
|
||||
resetDraftEmoji: action('resetDraftEmoji'),
|
||||
savePreferredReactions: action('savePreferredReactions'),
|
||||
selectDraftEmojiToBeReplaced: action('selectDraftEmojiToBeReplaced'),
|
||||
selectedDraftEmojiIndex: undefined,
|
||||
skinTone: 4,
|
||||
};
|
||||
|
||||
story.add('Default', () => (
|
||||
<CustomizingPreferredReactionsModal {...defaultProps} />
|
||||
|
|
|
@ -54,10 +54,8 @@ export function CustomizingPreferredReactionsModal({
|
|||
selectedDraftEmojiIndex,
|
||||
skinTone,
|
||||
}: Readonly<PropsType>): JSX.Element {
|
||||
const [
|
||||
referenceElement,
|
||||
setReferenceElement,
|
||||
] = useState<null | HTMLDivElement>(null);
|
||||
const [referenceElement, setReferenceElement] =
|
||||
useState<null | HTMLDivElement>(null);
|
||||
const [popperElement, setPopperElement] = useState<null | HTMLDivElement>(
|
||||
null
|
||||
);
|
||||
|
|
|
@ -60,10 +60,12 @@ story.add('Knobs Playground', () => {
|
|||
);
|
||||
});
|
||||
|
||||
([
|
||||
['wide', WidthBreakpoint.Wide],
|
||||
['narrow', WidthBreakpoint.Narrow],
|
||||
] as const).forEach(([name, containerWidthBreakpoint]) => {
|
||||
(
|
||||
[
|
||||
['wide', WidthBreakpoint.Wide],
|
||||
['narrow', WidthBreakpoint.Narrow],
|
||||
] as const
|
||||
).forEach(([name, containerWidthBreakpoint]) => {
|
||||
const defaultPropsForBreakpoint = {
|
||||
...defaultProps,
|
||||
containerWidthBreakpoint,
|
||||
|
|
|
@ -54,10 +54,12 @@ story.add('Knobs Playground', () => {
|
|||
);
|
||||
});
|
||||
|
||||
([
|
||||
['wide', WidthBreakpoint.Wide],
|
||||
['narrow', WidthBreakpoint.Narrow],
|
||||
] as const).forEach(([name, containerWidthBreakpoint]) => {
|
||||
(
|
||||
[
|
||||
['wide', WidthBreakpoint.Wide],
|
||||
['narrow', WidthBreakpoint.Narrow],
|
||||
] as const
|
||||
).forEach(([name, containerWidthBreakpoint]) => {
|
||||
const defaultPropsForBreakpoint = {
|
||||
...defaultProps,
|
||||
containerWidthBreakpoint,
|
||||
|
|
|
@ -38,9 +38,8 @@ export const DisappearingTimerSelect: React.FC<Props> = (props: Props) => {
|
|||
};
|
||||
});
|
||||
|
||||
const isCustomTimeSelected = !expirationTimer.DEFAULT_DURATIONS_SET.has(
|
||||
value
|
||||
);
|
||||
const isCustomTimeSelected =
|
||||
!expirationTimer.DEFAULT_DURATIONS_SET.has(value);
|
||||
|
||||
const onSelectChange = (newValue: string) => {
|
||||
const intValue = parseInt(newValue, 10);
|
||||
|
|
|
@ -69,9 +69,8 @@ export const GroupCallRemoteParticipant: React.FC<PropsType> = React.memo(
|
|||
videoAspectRatio,
|
||||
} = props.remoteParticipant;
|
||||
|
||||
const [hasReceivedVideoRecently, setHasReceivedVideoRecently] = useState(
|
||||
false
|
||||
);
|
||||
const [hasReceivedVideoRecently, setHasReceivedVideoRecently] =
|
||||
useState(false);
|
||||
const [isWide, setIsWide] = useState<boolean>(
|
||||
videoAspectRatio ? videoAspectRatio >= 1 : true
|
||||
);
|
||||
|
@ -90,10 +89,8 @@ export const GroupCallRemoteParticipant: React.FC<PropsType> = React.memo(
|
|||
const remoteVideoRef = useRef<HTMLCanvasElement | null>(null);
|
||||
const canvasContextRef = useRef<CanvasRenderingContext2D | null>(null);
|
||||
|
||||
const [
|
||||
intersectionRef,
|
||||
intersectionObserverEntry,
|
||||
] = useIntersectionObserver();
|
||||
const [intersectionRef, intersectionObserverEntry] =
|
||||
useIntersectionObserver();
|
||||
const isVisible = intersectionObserverEntry
|
||||
? intersectionObserverEntry.isIntersecting
|
||||
: true;
|
||||
|
@ -268,7 +265,8 @@ export const GroupCallRemoteParticipant: React.FC<PropsType> = React.memo(
|
|||
className={classNames(
|
||||
'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,
|
||||
}
|
||||
)}
|
||||
>
|
||||
|
|
|
@ -135,33 +135,34 @@ export const GroupCallRemoteParticipants: React.FC<PropsType> = ({
|
|||
),
|
||||
[remoteParticipants]
|
||||
);
|
||||
const gridParticipants: Array<GroupCallRemoteParticipantType> = useMemo(() => {
|
||||
if (!sortedParticipants.length) {
|
||||
return [];
|
||||
}
|
||||
const gridParticipants: Array<GroupCallRemoteParticipantType> =
|
||||
useMemo(() => {
|
||||
if (!sortedParticipants.length) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const candidateParticipants = isInSpeakerView
|
||||
? [sortedParticipants[0]]
|
||||
: sortedParticipants;
|
||||
const candidateParticipants = isInSpeakerView
|
||||
? [sortedParticipants[0]]
|
||||
: sortedParticipants;
|
||||
|
||||
// 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
|
||||
// possibly fit more than 500px of participants.
|
||||
const maxTotalWidth = maxRowCount * containerDimensions.width;
|
||||
// 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
|
||||
// possibly fit more than 500px of participants.
|
||||
const maxTotalWidth = maxRowCount * containerDimensions.width;
|
||||
|
||||
// We do the same thing for participants, "laying them out end-to-end" until they
|
||||
// exceed the maximum total width.
|
||||
let totalWidth = 0;
|
||||
return takeWhile(candidateParticipants, remoteParticipant => {
|
||||
totalWidth += remoteParticipant.videoAspectRatio * MIN_RENDERED_HEIGHT;
|
||||
return totalWidth < maxTotalWidth;
|
||||
}).sort(stableParticipantComparator);
|
||||
}, [
|
||||
containerDimensions.width,
|
||||
isInSpeakerView,
|
||||
maxRowCount,
|
||||
sortedParticipants,
|
||||
]);
|
||||
// We do the same thing for participants, "laying them out end-to-end" until they
|
||||
// exceed the maximum total width.
|
||||
let totalWidth = 0;
|
||||
return takeWhile(candidateParticipants, remoteParticipant => {
|
||||
totalWidth += remoteParticipant.videoAspectRatio * MIN_RENDERED_HEIGHT;
|
||||
return totalWidth < maxTotalWidth;
|
||||
}).sort(stableParticipantComparator);
|
||||
}, [
|
||||
containerDimensions.width,
|
||||
isInSpeakerView,
|
||||
maxRowCount,
|
||||
sortedParticipants,
|
||||
]);
|
||||
const overflowedParticipants: Array<GroupCallRemoteParticipantType> = useMemo(
|
||||
() =>
|
||||
sortedParticipants
|
||||
|
|
|
@ -24,8 +24,8 @@ export type HousekeepingPropsType = {
|
|||
|
||||
export type PropsType = DataPropsType & HousekeepingPropsType;
|
||||
|
||||
export const GroupV1MigrationDialog: React.FunctionComponent<PropsType> = React.memo(
|
||||
(props: PropsType) => {
|
||||
export const GroupV1MigrationDialog: React.FunctionComponent<PropsType> =
|
||||
React.memo((props: PropsType) => {
|
||||
const {
|
||||
areWeInvited,
|
||||
droppedMembers,
|
||||
|
@ -94,8 +94,7 @@ export const GroupV1MigrationDialog: React.FunctionComponent<PropsType> = React.
|
|||
)}
|
||||
</GroupDialog>
|
||||
);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
function renderMembers(
|
||||
members: Array<ConversationType>,
|
||||
|
|
|
@ -330,9 +330,8 @@ export const LeftPane: React.FC<PropsType> = ({
|
|||
|
||||
const numericIndex = keyboardKeyToNumericIndex(event.key);
|
||||
if (commandOrCtrl && isNumber(numericIndex)) {
|
||||
conversationToOpen = helper.getConversationAndMessageAtIndex(
|
||||
numericIndex
|
||||
);
|
||||
conversationToOpen =
|
||||
helper.getConversationAndMessageAtIndex(numericIndex);
|
||||
} else {
|
||||
let toFind: undefined | ToFindType;
|
||||
if (
|
||||
|
|
|
@ -63,9 +63,8 @@ export function Lightbox({
|
|||
selectedIndex: initialSelectedIndex = 0,
|
||||
}: PropsType): JSX.Element | null {
|
||||
const [root, setRoot] = React.useState<HTMLElement | undefined>();
|
||||
const [selectedIndex, setSelectedIndex] = useState<number>(
|
||||
initialSelectedIndex
|
||||
);
|
||||
const [selectedIndex, setSelectedIndex] =
|
||||
useState<number>(initialSelectedIndex);
|
||||
|
||||
const [videoElement, setVideoElement] = useState<HTMLVideoElement | null>(
|
||||
null
|
||||
|
@ -224,8 +223,13 @@ export function Lightbox({
|
|||
};
|
||||
}, [onKeyDown]);
|
||||
|
||||
const { attachment, contentType, loop = false, objectURL, message } =
|
||||
media[selectedIndex] || {};
|
||||
const {
|
||||
attachment,
|
||||
contentType,
|
||||
loop = false,
|
||||
objectURL,
|
||||
message,
|
||||
} = media[selectedIndex] || {};
|
||||
|
||||
const isAttachmentGIF = isGIF(attachment ? [attachment] : undefined);
|
||||
|
||||
|
@ -251,23 +255,23 @@ export function Lightbox({
|
|||
() => INITIAL_IMAGE_TRANSFORM
|
||||
);
|
||||
|
||||
const maxBoundsLimiter = useCallback((x: number, y: number): [
|
||||
number,
|
||||
number
|
||||
] => {
|
||||
const zoomCache = zoomCacheRef.current;
|
||||
const maxBoundsLimiter = useCallback(
|
||||
(x: number, y: number): [number, number] => {
|
||||
const zoomCache = zoomCacheRef.current;
|
||||
|
||||
if (!zoomCache) {
|
||||
return [0, 0];
|
||||
}
|
||||
if (!zoomCache) {
|
||||
return [0, 0];
|
||||
}
|
||||
|
||||
const { maxX, maxY } = zoomCache;
|
||||
const { maxX, maxY } = zoomCache;
|
||||
|
||||
const posX = Math.min(maxX, Math.max(-maxX, x));
|
||||
const posY = Math.min(maxY, Math.max(-maxY, y));
|
||||
const posX = Math.min(maxX, Math.max(-maxX, x));
|
||||
const posY = Math.min(maxY, Math.max(-maxY, y));
|
||||
|
||||
return [posX, posY];
|
||||
}, []);
|
||||
return [posX, posY];
|
||||
},
|
||||
[]
|
||||
);
|
||||
|
||||
const positionImage = useCallback(
|
||||
(ev: MouseEvent) => {
|
||||
|
@ -352,12 +356,8 @@ export function Lightbox({
|
|||
screenWidth: window.innerWidth,
|
||||
};
|
||||
|
||||
const {
|
||||
height,
|
||||
left,
|
||||
top,
|
||||
width,
|
||||
} = animateNode.getBoundingClientRect();
|
||||
const { height, left, top, width } =
|
||||
animateNode.getBoundingClientRect();
|
||||
|
||||
const offsetX = ev.clientX - left - width / 2;
|
||||
const offsetY = ev.clientY - top - height / 2;
|
||||
|
|
|
@ -133,7 +133,8 @@ export const MediaQualitySelector = ({
|
|||
<div
|
||||
className={classNames({
|
||||
'MediaQualitySelector__option--checkmark': true,
|
||||
'MediaQualitySelector__option--selected': !isHighQuality,
|
||||
'MediaQualitySelector__option--selected':
|
||||
!isHighQuality,
|
||||
})}
|
||||
/>
|
||||
<div>
|
||||
|
|
|
@ -17,67 +17,68 @@ type PropsType = {
|
|||
onClose: () => void;
|
||||
};
|
||||
|
||||
export const NewlyCreatedGroupInvitedContactsDialog: FunctionComponent<PropsType> = ({
|
||||
contacts,
|
||||
i18n,
|
||||
onClose,
|
||||
}) => {
|
||||
let title: string;
|
||||
let body: ReactNode;
|
||||
if (contacts.length === 1) {
|
||||
const contact = contacts[0];
|
||||
export const NewlyCreatedGroupInvitedContactsDialog: FunctionComponent<PropsType> =
|
||||
({ contacts, i18n, onClose }) => {
|
||||
let title: string;
|
||||
let body: ReactNode;
|
||||
if (contacts.length === 1) {
|
||||
const contact = contacts[0];
|
||||
|
||||
title = i18n('NewlyCreatedGroupInvitedContactsDialog--title--one');
|
||||
body = (
|
||||
<>
|
||||
<GroupDialog.Paragraph>
|
||||
<Intl
|
||||
i18n={i18n}
|
||||
id="NewlyCreatedGroupInvitedContactsDialog--body--user-paragraph--one"
|
||||
components={[<ContactName title={contact.title} />]}
|
||||
/>
|
||||
</GroupDialog.Paragraph>
|
||||
<GroupDialog.Paragraph>
|
||||
{i18n('NewlyCreatedGroupInvitedContactsDialog--body--info-paragraph')}
|
||||
</GroupDialog.Paragraph>
|
||||
</>
|
||||
);
|
||||
} else {
|
||||
title = i18n('NewlyCreatedGroupInvitedContactsDialog--title--many', [
|
||||
contacts.length.toString(),
|
||||
]);
|
||||
body = (
|
||||
<>
|
||||
<GroupDialog.Paragraph>
|
||||
{i18n(
|
||||
'NewlyCreatedGroupInvitedContactsDialog--body--user-paragraph--many'
|
||||
)}
|
||||
</GroupDialog.Paragraph>
|
||||
<GroupDialog.Paragraph>
|
||||
{i18n('NewlyCreatedGroupInvitedContactsDialog--body--info-paragraph')}
|
||||
</GroupDialog.Paragraph>
|
||||
<GroupDialog.Contacts contacts={contacts} i18n={i18n} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
title = i18n('NewlyCreatedGroupInvitedContactsDialog--title--one');
|
||||
body = (
|
||||
<>
|
||||
<GroupDialog.Paragraph>
|
||||
<Intl
|
||||
i18n={i18n}
|
||||
id="NewlyCreatedGroupInvitedContactsDialog--body--user-paragraph--one"
|
||||
components={[<ContactName title={contact.title} />]}
|
||||
/>
|
||||
</GroupDialog.Paragraph>
|
||||
<GroupDialog.Paragraph>
|
||||
{i18n(
|
||||
'NewlyCreatedGroupInvitedContactsDialog--body--info-paragraph'
|
||||
)}
|
||||
</GroupDialog.Paragraph>
|
||||
</>
|
||||
);
|
||||
} else {
|
||||
title = i18n('NewlyCreatedGroupInvitedContactsDialog--title--many', [
|
||||
contacts.length.toString(),
|
||||
]);
|
||||
body = (
|
||||
<>
|
||||
<GroupDialog.Paragraph>
|
||||
{i18n(
|
||||
'NewlyCreatedGroupInvitedContactsDialog--body--user-paragraph--many'
|
||||
)}
|
||||
</GroupDialog.Paragraph>
|
||||
<GroupDialog.Paragraph>
|
||||
{i18n(
|
||||
'NewlyCreatedGroupInvitedContactsDialog--body--info-paragraph'
|
||||
)}
|
||||
</GroupDialog.Paragraph>
|
||||
<GroupDialog.Contacts contacts={contacts} i18n={i18n} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<GroupDialog
|
||||
i18n={i18n}
|
||||
onClickPrimaryButton={onClose}
|
||||
primaryButtonText={i18n('Confirmation--confirm')}
|
||||
secondaryButtonText={i18n(
|
||||
'NewlyCreatedGroupInvitedContactsDialog--body--learn-more'
|
||||
)}
|
||||
onClickSecondaryButton={() => {
|
||||
openLinkInWebBrowser(
|
||||
'https://support.signal.org/hc/articles/360007319331-Group-chats'
|
||||
);
|
||||
}}
|
||||
onClose={onClose}
|
||||
title={title}
|
||||
>
|
||||
{body}
|
||||
</GroupDialog>
|
||||
);
|
||||
};
|
||||
return (
|
||||
<GroupDialog
|
||||
i18n={i18n}
|
||||
onClickPrimaryButton={onClose}
|
||||
primaryButtonText={i18n('Confirmation--confirm')}
|
||||
secondaryButtonText={i18n(
|
||||
'NewlyCreatedGroupInvitedContactsDialog--body--learn-more'
|
||||
)}
|
||||
onClickSecondaryButton={() => {
|
||||
openLinkInWebBrowser(
|
||||
'https://support.signal.org/hc/articles/360007319331-Group-chats'
|
||||
);
|
||||
}}
|
||||
onClose={onClose}
|
||||
title={title}
|
||||
>
|
||||
{body}
|
||||
</GroupDialog>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -267,10 +267,8 @@ export const Preferences = ({
|
|||
const [page, setPage] = useState<Page>(Page.General);
|
||||
const [showSyncFailed, setShowSyncFailed] = useState(false);
|
||||
const [nowSyncing, setNowSyncing] = useState(false);
|
||||
const [
|
||||
showDisappearingTimerDialog,
|
||||
setShowDisappearingTimerDialog,
|
||||
] = useState(false);
|
||||
const [showDisappearingTimerDialog, setShowDisappearingTimerDialog] =
|
||||
useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
doneRendering();
|
||||
|
@ -281,7 +279,7 @@ export const Preferences = ({
|
|||
const onZoomSelectChange = useCallback(
|
||||
(value: string) => {
|
||||
const number = parseFloat(value);
|
||||
onZoomFactorChange((number as unknown) as ZoomFactorType);
|
||||
onZoomFactorChange(number as unknown as ZoomFactorType);
|
||||
},
|
||||
[onZoomFactorChange]
|
||||
);
|
||||
|
@ -764,9 +762,8 @@ export const Preferences = ({
|
|||
</>
|
||||
);
|
||||
} else if (page === Page.Privacy) {
|
||||
const isCustomDisappearingMessageValue = !DEFAULT_DURATIONS_SET.has(
|
||||
universalExpireTimer
|
||||
);
|
||||
const isCustomDisappearingMessageValue =
|
||||
!DEFAULT_DURATIONS_SET.has(universalExpireTimer);
|
||||
|
||||
settings = (
|
||||
<>
|
||||
|
|
|
@ -57,281 +57,286 @@ enum ConfirmationStateType {
|
|||
ConfirmingGroupRemoval,
|
||||
}
|
||||
|
||||
export const ContactSpoofingReviewDialog: FunctionComponent<PropsType> = props => {
|
||||
const {
|
||||
i18n,
|
||||
onBlock,
|
||||
onBlockAndReportSpam,
|
||||
onClose,
|
||||
onDelete,
|
||||
onShowContactModal,
|
||||
onUnblock,
|
||||
removeMember,
|
||||
} = props;
|
||||
export const ContactSpoofingReviewDialog: FunctionComponent<PropsType> =
|
||||
props => {
|
||||
const {
|
||||
i18n,
|
||||
onBlock,
|
||||
onBlockAndReportSpam,
|
||||
onClose,
|
||||
onDelete,
|
||||
onShowContactModal,
|
||||
onUnblock,
|
||||
removeMember,
|
||||
} = props;
|
||||
|
||||
const [confirmationState, setConfirmationState] = useState<
|
||||
| undefined
|
||||
| {
|
||||
type: ConfirmationStateType;
|
||||
affectedConversation: ConversationType;
|
||||
}
|
||||
>();
|
||||
const [confirmationState, setConfirmationState] = useState<
|
||||
| undefined
|
||||
| {
|
||||
type: ConfirmationStateType;
|
||||
affectedConversation: ConversationType;
|
||||
}
|
||||
>();
|
||||
|
||||
if (confirmationState) {
|
||||
const { affectedConversation, type } = confirmationState;
|
||||
switch (type) {
|
||||
case ConfirmationStateType.ConfirmingDelete:
|
||||
case ConfirmationStateType.ConfirmingBlock:
|
||||
return (
|
||||
<MessageRequestActionsConfirmation
|
||||
i18n={i18n}
|
||||
onBlock={() => {
|
||||
onBlock(affectedConversation.id);
|
||||
}}
|
||||
onBlockAndReportSpam={() => {
|
||||
onBlockAndReportSpam(affectedConversation.id);
|
||||
}}
|
||||
onUnblock={() => {
|
||||
onUnblock(affectedConversation.id);
|
||||
}}
|
||||
onDelete={() => {
|
||||
onDelete(affectedConversation.id);
|
||||
}}
|
||||
title={affectedConversation.title}
|
||||
conversationType="direct"
|
||||
state={
|
||||
type === ConfirmationStateType.ConfirmingDelete
|
||||
? MessageRequestState.deleting
|
||||
: 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);
|
||||
if (confirmationState) {
|
||||
const { affectedConversation, type } = confirmationState;
|
||||
switch (type) {
|
||||
case ConfirmationStateType.ConfirmingDelete:
|
||||
case ConfirmationStateType.ConfirmingBlock:
|
||||
return (
|
||||
<MessageRequestActionsConfirmation
|
||||
i18n={i18n}
|
||||
onBlock={() => {
|
||||
onBlock(affectedConversation.id);
|
||||
}}
|
||||
onBlockAndReportSpam={() => {
|
||||
onBlockAndReportSpam(affectedConversation.id);
|
||||
}}
|
||||
onUnblock={() => {
|
||||
onUnblock(affectedConversation.id);
|
||||
}}
|
||||
onDelete={() => {
|
||||
onDelete(affectedConversation.id);
|
||||
}}
|
||||
title={affectedConversation.title}
|
||||
conversationType="direct"
|
||||
state={
|
||||
type === ConfirmationStateType.ConfirmingDelete
|
||||
? MessageRequestState.deleting
|
||||
: MessageRequestState.blocking
|
||||
}
|
||||
}}
|
||||
/>
|
||||
);
|
||||
case ConfirmationStateType.ConfirmingGroupRemoval:
|
||||
return (
|
||||
<RemoveGroupMemberConfirmationDialog
|
||||
conversation={affectedConversation}
|
||||
i18n={i18n}
|
||||
onClose={() => {
|
||||
setConfirmationState(undefined);
|
||||
}}
|
||||
onRemove={() => {
|
||||
removeMember(affectedConversation.id);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
default:
|
||||
throw missingCaseError(type);
|
||||
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);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
);
|
||||
case ConfirmationStateType.ConfirmingGroupRemoval:
|
||||
return (
|
||||
<RemoveGroupMemberConfirmationDialog
|
||||
conversation={affectedConversation}
|
||||
i18n={i18n}
|
||||
onClose={() => {
|
||||
setConfirmationState(undefined);
|
||||
}}
|
||||
onRemove={() => {
|
||||
removeMember(affectedConversation.id);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
default:
|
||||
throw missingCaseError(type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let title: string;
|
||||
let contents: ReactChild;
|
||||
let title: string;
|
||||
let contents: ReactChild;
|
||||
|
||||
switch (props.type) {
|
||||
case ContactSpoofingType.DirectConversationWithSameTitle: {
|
||||
const { possiblyUnsafeConversation, safeConversation } = props;
|
||||
assert(
|
||||
possiblyUnsafeConversation.type === 'direct',
|
||||
'<ContactSpoofingReviewDialog> expected a direct conversation for the "possibly unsafe" conversation'
|
||||
);
|
||||
assert(
|
||||
safeConversation.type === 'direct',
|
||||
'<ContactSpoofingReviewDialog> expected a direct conversation for the "safe" conversation'
|
||||
);
|
||||
switch (props.type) {
|
||||
case ContactSpoofingType.DirectConversationWithSameTitle: {
|
||||
const { possiblyUnsafeConversation, safeConversation } = props;
|
||||
assert(
|
||||
possiblyUnsafeConversation.type === 'direct',
|
||||
'<ContactSpoofingReviewDialog> expected a direct conversation for the "possibly unsafe" conversation'
|
||||
);
|
||||
assert(
|
||||
safeConversation.type === 'direct',
|
||||
'<ContactSpoofingReviewDialog> expected a direct conversation for the "safe" conversation'
|
||||
);
|
||||
|
||||
title = i18n('ContactSpoofingReviewDialog__title');
|
||||
contents = (
|
||||
<>
|
||||
<p>{i18n('ContactSpoofingReviewDialog__description')}</p>
|
||||
<h2>{i18n('ContactSpoofingReviewDialog__possibly-unsafe-title')}</h2>
|
||||
<ContactSpoofingReviewDialogPerson
|
||||
conversation={possiblyUnsafeConversation}
|
||||
i18n={i18n}
|
||||
>
|
||||
<div className="module-ContactSpoofingReviewDialog__buttons">
|
||||
<Button
|
||||
variant={ButtonVariant.SecondaryDestructive}
|
||||
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 = (
|
||||
title = i18n('ContactSpoofingReviewDialog__title');
|
||||
contents = (
|
||||
<>
|
||||
<p>{i18n('ContactSpoofingReviewDialog__description')}</p>
|
||||
<h2>
|
||||
{i18n('ContactSpoofingReviewDialog__possibly-unsafe-title')}
|
||||
</h2>
|
||||
<ContactSpoofingReviewDialogPerson
|
||||
conversation={possiblyUnsafeConversation}
|
||||
i18n={i18n}
|
||||
>
|
||||
<div className="module-ContactSpoofingReviewDialog__buttons">
|
||||
<Button
|
||||
variant={ButtonVariant.SecondaryAffirmative}
|
||||
variant={ButtonVariant.SecondaryDestructive}
|
||||
onClick={() => {
|
||||
setConfirmationState({
|
||||
type: ConfirmationStateType.ConfirmingGroupRemoval,
|
||||
affectedConversation: conversationInfo.conversation,
|
||||
type: ConfirmationStateType.ConfirmingDelete,
|
||||
affectedConversation: possiblyUnsafeConversation,
|
||||
});
|
||||
}}
|
||||
>
|
||||
{i18n('RemoveGroupMemberConfirmation__remove-button')}
|
||||
{i18n('MessageRequests--delete')}
|
||||
</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,
|
||||
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
|
||||
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 =
|
||||
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>
|
||||
</>
|
||||
);
|
||||
})}
|
||||
</>
|
||||
);
|
||||
break;
|
||||
})}
|
||||
</>
|
||||
);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
throw missingCaseError(props);
|
||||
}
|
||||
default:
|
||||
throw missingCaseError(props);
|
||||
}
|
||||
|
||||
return (
|
||||
<Modal
|
||||
hasXButton
|
||||
i18n={i18n}
|
||||
moduleClassName="module-ContactSpoofingReviewDialog"
|
||||
onClose={onClose}
|
||||
title={title}
|
||||
>
|
||||
{contents}
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
return (
|
||||
<Modal
|
||||
hasXButton
|
||||
i18n={i18n}
|
||||
moduleClassName="module-ContactSpoofingReviewDialog"
|
||||
onClose={onClose}
|
||||
title={title}
|
||||
>
|
||||
{contents}
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -19,60 +19,56 @@ type PropsType = {
|
|||
onClick?: () => void;
|
||||
};
|
||||
|
||||
export const ContactSpoofingReviewDialogPerson: FunctionComponent<PropsType> = ({
|
||||
children,
|
||||
conversation,
|
||||
i18n,
|
||||
onClick,
|
||||
}) => {
|
||||
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>
|
||||
export const ContactSpoofingReviewDialogPerson: FunctionComponent<PropsType> =
|
||||
({ children, conversation, i18n, onClick }) => {
|
||||
assert(
|
||||
conversation.type === 'direct',
|
||||
'<ContactSpoofingReviewDialogPerson> expected a direct conversation'
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="module-ContactSpoofingReviewDialogPerson">{contents}</div>
|
||||
);
|
||||
};
|
||||
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 (
|
||||
<div className="module-ContactSpoofingReviewDialogPerson">{contents}</div>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -523,12 +523,8 @@ export class ConversationHeader extends React.Component<PropsType, StateType> {
|
|||
}
|
||||
|
||||
private renderHeader(): ReactNode {
|
||||
const {
|
||||
conversationTitle,
|
||||
groupVersion,
|
||||
onShowConversationDetails,
|
||||
type,
|
||||
} = this.props;
|
||||
const { conversationTitle, groupVersion, onShowConversationDetails, type } =
|
||||
this.props;
|
||||
|
||||
if (conversationTitle !== undefined) {
|
||||
return (
|
||||
|
@ -592,13 +588,8 @@ export class ConversationHeader extends React.Component<PropsType, StateType> {
|
|||
}
|
||||
|
||||
public render(): ReactNode {
|
||||
const {
|
||||
id,
|
||||
isSMSOnly,
|
||||
i18n,
|
||||
onSetDisappearingMessages,
|
||||
expireTimer,
|
||||
} = this.props;
|
||||
const { id, isSMSOnly, i18n, onSetDisappearingMessages, expireTimer } =
|
||||
this.props;
|
||||
const { isNarrow, modalState } = this.state;
|
||||
const triggerId = `conversation-${id}`;
|
||||
|
||||
|
|
|
@ -118,10 +118,8 @@ export const ConversationHero = ({
|
|||
}: Props): JSX.Element => {
|
||||
const firstRenderRef = useRef(true);
|
||||
|
||||
const [
|
||||
isShowingMessageRequestWarning,
|
||||
setIsShowingMessageRequestWarning,
|
||||
] = useState(false);
|
||||
const [isShowingMessageRequestWarning, setIsShowingMessageRequestWarning] =
|
||||
useState(false);
|
||||
const closeMessageRequestWarning = () => {
|
||||
setIsShowingMessageRequestWarning(false);
|
||||
};
|
||||
|
|
|
@ -26,8 +26,7 @@ story.add('Default', () => <GroupDescription {...createProps()} />);
|
|||
story.add('Long', () => (
|
||||
<GroupDescription
|
||||
{...createProps({
|
||||
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.',
|
||||
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.',
|
||||
})}
|
||||
/>
|
||||
));
|
||||
|
@ -51,8 +50,7 @@ story.add('With emoji', () => (
|
|||
story.add('With link', () => (
|
||||
<GroupDescription
|
||||
{...createProps({
|
||||
text:
|
||||
'I love https://example.com and http://example.com and example.com, but not https://user:bar@example.com',
|
||||
text: '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', () => (
|
||||
<GroupDescription
|
||||
{...createProps({
|
||||
text:
|
||||
'🍒 https://example.com this is a long thing\nhttps://example.com on another line\nhttps://example.com',
|
||||
text: '🍒 https://example.com this is a long thing\nhttps://example.com on another line\nhttps://example.com',
|
||||
})}
|
||||
/>
|
||||
));
|
||||
|
|
|
@ -26,8 +26,7 @@ story.add('Only Link', () => {
|
|||
|
||||
story.add('Links with Text', () => {
|
||||
const props = createProps({
|
||||
text:
|
||||
'you should see this: https://www.signal.org - it is good. Also: https://placekitten.com!',
|
||||
text: 'you should see this: https://www.signal.org - it is good. Also: https://placekitten.com!',
|
||||
});
|
||||
|
||||
return <Linkify {...props} />;
|
||||
|
@ -43,8 +42,7 @@ story.add('Links with Emoji without space', () => {
|
|||
|
||||
story.add('Links with Emoji and Text', () => {
|
||||
const props = createProps({
|
||||
text:
|
||||
'https://example.com ⚠️ 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ https://example.com',
|
||||
text: 'https://example.com ⚠️ 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ https://example.com',
|
||||
});
|
||||
|
||||
return <Linkify {...props} />;
|
||||
|
@ -60,8 +58,7 @@ story.add('No Link', () => {
|
|||
|
||||
story.add('Blocked Protocols', () => {
|
||||
const props = createProps({
|
||||
text:
|
||||
'smailto:someone@somewhere.com - ftp://something.com - //local/share - \\localshare',
|
||||
text: 'smailto:someone@somewhere.com - ftp://something.com - //local/share - \\localshare',
|
||||
});
|
||||
|
||||
return <Linkify {...props} />;
|
||||
|
@ -69,8 +66,7 @@ story.add('Blocked Protocols', () => {
|
|||
|
||||
story.add('Missing protocols', () => {
|
||||
const props = createProps({
|
||||
text:
|
||||
'I love example.com. I also love кц.рф. I also love مثال.تونس. But I do not love test.example.',
|
||||
text: 'I love example.com. I also love кц.рф. I also love مثال.تونس. But I do not love test.example.',
|
||||
});
|
||||
|
||||
return <Linkify {...props} />;
|
||||
|
@ -78,8 +74,7 @@ story.add('Missing protocols', () => {
|
|||
|
||||
story.add('Custom Text Render', () => {
|
||||
const props = createProps({
|
||||
text:
|
||||
'you should see this: https://www.signal.org - it is good. Also: https://placekitten.com!',
|
||||
text: 'you should see this: https://www.signal.org - it is good. Also: https://placekitten.com!',
|
||||
renderNonLink: ({ text: theText, key }) => (
|
||||
<div key={key} style={{ backgroundColor: 'aquamarine' }}>
|
||||
{theText}
|
||||
|
|
|
@ -188,8 +188,7 @@ const renderBothDirections = (props: Props) => (
|
|||
|
||||
story.add('Plain Message', () => {
|
||||
const props = createProps({
|
||||
text:
|
||||
'Hello there from a pal! I am sending a long message so that it will wrap a bit, since I like that look.',
|
||||
text: '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);
|
||||
|
@ -296,8 +295,7 @@ story.add('Delivered', () => {
|
|||
const props = createProps({
|
||||
direction: 'outgoing',
|
||||
status: 'delivered',
|
||||
text:
|
||||
'Hello there from a pal! I am sending a long message so that it will wrap a bit, since I like that look.',
|
||||
text: '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} />;
|
||||
|
@ -307,8 +305,7 @@ story.add('Read', () => {
|
|||
const props = createProps({
|
||||
direction: 'outgoing',
|
||||
status: 'read',
|
||||
text:
|
||||
'Hello there from a pal! I am sending a long message so that it will wrap a bit, since I like that look.',
|
||||
text: '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} />;
|
||||
|
@ -318,8 +315,7 @@ story.add('Sending', () => {
|
|||
const props = createProps({
|
||||
direction: 'outgoing',
|
||||
status: 'sending',
|
||||
text:
|
||||
'Hello there from a pal! I am sending a long message so that it will wrap a bit, since I like that look.',
|
||||
text: '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} />;
|
||||
|
@ -329,8 +325,7 @@ story.add('Expiring', () => {
|
|||
const props = createProps({
|
||||
expirationLength: 30 * 1000,
|
||||
expirationTimestamp: Date.now() + 30 * 1000,
|
||||
text:
|
||||
'Hello there from a pal! I am sending a long message so that it will wrap a bit, since I like that look.',
|
||||
text: '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);
|
||||
|
@ -338,8 +333,7 @@ story.add('Expiring', () => {
|
|||
|
||||
story.add('Pending', () => {
|
||||
const props = createProps({
|
||||
text:
|
||||
'Hello there from a pal! I am sending a long message so that it will wrap a bit, since I like that look.',
|
||||
text: '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,
|
||||
});
|
||||
|
||||
|
|
|
@ -296,7 +296,8 @@ export class Message extends React.PureComponent<Props, State> {
|
|||
|
||||
public audioButtonRef: React.RefObject<HTMLButtonElement> = React.createRef();
|
||||
|
||||
public reactionsContainerRef: React.RefObject<HTMLDivElement> = React.createRef();
|
||||
public reactionsContainerRef: React.RefObject<HTMLDivElement> =
|
||||
React.createRef();
|
||||
|
||||
public reactionsContainerRefMerger = createRefMerger();
|
||||
|
||||
|
@ -1194,7 +1195,8 @@ export class Message extends React.PureComponent<Props, State> {
|
|||
return (
|
||||
<div
|
||||
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
|
||||
|
@ -2405,14 +2407,8 @@ export class Message extends React.PureComponent<Props, State> {
|
|||
}
|
||||
|
||||
public render(): JSX.Element | null {
|
||||
const {
|
||||
author,
|
||||
attachments,
|
||||
direction,
|
||||
id,
|
||||
isSticker,
|
||||
timestamp,
|
||||
} = this.props;
|
||||
const { author, attachments, direction, id, isSticker, timestamp } =
|
||||
this.props;
|
||||
const { expired, expiring, imageBroken, isSelected } = this.state;
|
||||
|
||||
// This id is what connects our triple-dot click with our associated pop-up menu.
|
||||
|
|
|
@ -107,8 +107,7 @@ story.add('@Mention', () => {
|
|||
replacementText: 'Bender B Rodriguez 🤖',
|
||||
},
|
||||
],
|
||||
text:
|
||||
'Like \uFFFC once said: My story is a lot like yours, only more interesting because it involves robots',
|
||||
text: 'Like \uFFFC once said: My story is a lot like yours, only more interesting because it involves robots',
|
||||
});
|
||||
|
||||
return <MessageBody {...props} />;
|
||||
|
@ -167,8 +166,7 @@ story.add('Complex MessageBody', () => {
|
|||
},
|
||||
],
|
||||
direction: 'outgoing',
|
||||
text:
|
||||
'Hey \uFFFC\nCheck out https://www.signal.org I think you will really like it 😍\n\ncc \uFFFC \uFFFC',
|
||||
text: 'Hey \uFFFC\nCheck out https://www.signal.org I think you will really like it 😍\n\ncc \uFFFC \uFFFC',
|
||||
});
|
||||
|
||||
return <MessageBody {...props} />;
|
||||
|
|
|
@ -85,7 +85,8 @@ export const MessageMetadata: FunctionComponent<PropsType> = props => {
|
|||
'module-message__metadata__date': true,
|
||||
'module-message__metadata__date--with-sticker': 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}
|
||||
|
|
|
@ -127,10 +127,8 @@ export class Quote extends React.Component<Props, State> {
|
|||
}
|
||||
|
||||
componentDidMount(): void {
|
||||
const {
|
||||
doubleCheckMissingQuoteReference,
|
||||
referencedMessageNotFound,
|
||||
} = this.props;
|
||||
const { doubleCheckMissingQuoteReference, referencedMessageNotFound } =
|
||||
this.props;
|
||||
|
||||
if (referencedMessageNotFound) {
|
||||
doubleCheckMissingQuoteReference?.();
|
||||
|
@ -275,14 +273,8 @@ export class Quote extends React.Component<Props, State> {
|
|||
}
|
||||
|
||||
public renderText(): JSX.Element | null {
|
||||
const {
|
||||
bodyRanges,
|
||||
i18n,
|
||||
text,
|
||||
rawAttachment,
|
||||
isIncoming,
|
||||
isViewOnce,
|
||||
} = this.props;
|
||||
const { bodyRanges, i18n, text, rawAttachment, isIncoming, isViewOnce } =
|
||||
this.props;
|
||||
|
||||
if (text) {
|
||||
const quoteText = bodyRanges
|
||||
|
|
|
@ -123,10 +123,8 @@ export const ReactionViewer = React.forwardRef<HTMLDivElement, Props>(
|
|||
[reactionsWithEmojiData, groupedAndSortedReactions]
|
||||
);
|
||||
|
||||
const [
|
||||
selectedReactionCategory,
|
||||
setSelectedReactionCategory,
|
||||
] = React.useState(pickedReaction || 'all');
|
||||
const [selectedReactionCategory, setSelectedReactionCategory] =
|
||||
React.useState(pickedReaction || 'all');
|
||||
|
||||
// Handle escape key
|
||||
useEscapeHandling(onClose);
|
||||
|
|
|
@ -18,30 +18,26 @@ type PropsType = {
|
|||
onRemove: () => void;
|
||||
};
|
||||
|
||||
export const RemoveGroupMemberConfirmationDialog: FunctionComponent<PropsType> = ({
|
||||
conversation,
|
||||
i18n,
|
||||
onClose,
|
||||
onRemove,
|
||||
}) => (
|
||||
<ConfirmationDialog
|
||||
actions={[
|
||||
{
|
||||
action: onRemove,
|
||||
text: i18n('RemoveGroupMemberConfirmation__remove-button'),
|
||||
style: 'negative',
|
||||
},
|
||||
]}
|
||||
i18n={i18n}
|
||||
onClose={onClose}
|
||||
title={
|
||||
<Intl
|
||||
i18n={i18n}
|
||||
id="RemoveGroupMemberConfirmation__description"
|
||||
components={{
|
||||
name: <ContactName title={conversation.title} />,
|
||||
}}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
);
|
||||
export const RemoveGroupMemberConfirmationDialog: FunctionComponent<PropsType> =
|
||||
({ conversation, i18n, onClose, onRemove }) => (
|
||||
<ConfirmationDialog
|
||||
actions={[
|
||||
{
|
||||
action: onRemove,
|
||||
text: i18n('RemoveGroupMemberConfirmation__remove-button'),
|
||||
style: 'negative',
|
||||
},
|
||||
]}
|
||||
i18n={i18n}
|
||||
onClose={onClose}
|
||||
title={
|
||||
<Intl
|
||||
i18n={i18n}
|
||||
id="RemoveGroupMemberConfirmation__description"
|
||||
components={{
|
||||
name: <ContactName title={conversation.title} />,
|
||||
}}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
);
|
||||
|
|
|
@ -263,8 +263,7 @@ const items: Record<string, TimelineItemType> = {
|
|||
previews: [],
|
||||
readStatus: ReadStatus.Read,
|
||||
status: 'sent',
|
||||
text:
|
||||
'Hello there from the new world! And this is multiple lines of text. Lines and lines and lines.',
|
||||
text: 'Hello there from the new world! And this is multiple lines of text. Lines and lines and lines.',
|
||||
timestamp: Date.now(),
|
||||
},
|
||||
},
|
||||
|
@ -285,8 +284,7 @@ const items: Record<string, TimelineItemType> = {
|
|||
previews: [],
|
||||
readStatus: ReadStatus.Read,
|
||||
status: 'read',
|
||||
text:
|
||||
'Hello there from the new world! And this is multiple lines of text. Lines and lines and lines.',
|
||||
text: 'Hello there from the new world! And this is multiple lines of text. Lines and lines and lines.',
|
||||
timestamp: Date.now(),
|
||||
},
|
||||
},
|
||||
|
|
|
@ -710,12 +710,8 @@ export class Timeline extends React.PureComponent<PropsType, StateType> {
|
|||
);
|
||||
|
||||
public loadOlderMessages = (): void => {
|
||||
const {
|
||||
haveOldest,
|
||||
isLoadingMessages,
|
||||
items,
|
||||
loadOlderMessages,
|
||||
} = this.props;
|
||||
const { haveOldest, isLoadingMessages, items, loadOlderMessages } =
|
||||
this.props;
|
||||
|
||||
if (this.loadCountdownTimeout) {
|
||||
clearTimeout(this.loadCountdownTimeout);
|
||||
|
|
|
@ -185,9 +185,10 @@ export const AddGroupMembersModal: FunctionComponent<PropsType> = ({
|
|||
stage: Stage.ChoosingContacts,
|
||||
});
|
||||
|
||||
const contactLookup = useMemo(() => makeLookup(candidateContacts, 'id'), [
|
||||
candidateContacts,
|
||||
]);
|
||||
const contactLookup = useMemo(
|
||||
() => makeLookup(candidateContacts, 'id'),
|
||||
[candidateContacts]
|
||||
);
|
||||
|
||||
const selectedContacts = deconstructLookup(
|
||||
contactLookup,
|
||||
|
|
|
@ -148,17 +148,12 @@ export const ConversationDetails: React.ComponentType<Props> = ({
|
|||
const [modalState, setModalState] = useState<ModalState>(
|
||||
ModalState.NothingOpen
|
||||
);
|
||||
const [
|
||||
editGroupAttributesRequestState,
|
||||
setEditGroupAttributesRequestState,
|
||||
] = useState<RequestState>(RequestState.Inactive);
|
||||
const [
|
||||
addGroupMembersRequestState,
|
||||
setAddGroupMembersRequestState,
|
||||
] = useState<RequestState>(RequestState.Inactive);
|
||||
const [membersMissingCapability, setMembersMissingCapability] = useState(
|
||||
false
|
||||
);
|
||||
const [editGroupAttributesRequestState, setEditGroupAttributesRequestState] =
|
||||
useState<RequestState>(RequestState.Inactive);
|
||||
const [addGroupMembersRequestState, setAddGroupMembersRequestState] =
|
||||
useState<RequestState>(RequestState.Inactive);
|
||||
const [membersMissingCapability, setMembersMissingCapability] =
|
||||
useState(false);
|
||||
|
||||
if (conversation === undefined) {
|
||||
throw new Error('ConversationDetails rendered without a conversation');
|
||||
|
|
|
@ -25,98 +25,103 @@ type PropsType = {
|
|||
setMuteExpiration: (muteExpiresAt: undefined | number) => unknown;
|
||||
};
|
||||
|
||||
export const ConversationNotificationsSettings: FunctionComponent<PropsType> = ({
|
||||
conversationType,
|
||||
dontNotifyForMentionsIfMuted,
|
||||
i18n,
|
||||
muteExpiresAt,
|
||||
setMuteExpiration,
|
||||
setDontNotifyForMentionsIfMuted,
|
||||
}) => {
|
||||
const muteOptions = useMemo(
|
||||
() => [
|
||||
...(isMuted(muteExpiresAt)
|
||||
? []
|
||||
: [
|
||||
{
|
||||
disabled: true,
|
||||
text: i18n('notMuted'),
|
||||
value: -1,
|
||||
},
|
||||
]),
|
||||
...getMuteOptions(muteExpiresAt, i18n).map(
|
||||
({ disabled, name, value }) => ({
|
||||
disabled,
|
||||
text: name,
|
||||
value,
|
||||
})
|
||||
),
|
||||
],
|
||||
[i18n, muteExpiresAt]
|
||||
);
|
||||
|
||||
const onMuteChange = (rawValue: string) => {
|
||||
const ms = parseIntOrThrow(
|
||||
rawValue,
|
||||
'NotificationSettings: mute ms was not an integer'
|
||||
export const ConversationNotificationsSettings: FunctionComponent<PropsType> =
|
||||
({
|
||||
conversationType,
|
||||
dontNotifyForMentionsIfMuted,
|
||||
i18n,
|
||||
muteExpiresAt,
|
||||
setMuteExpiration,
|
||||
setDontNotifyForMentionsIfMuted,
|
||||
}) => {
|
||||
const muteOptions = useMemo(
|
||||
() => [
|
||||
...(isMuted(muteExpiresAt)
|
||||
? []
|
||||
: [
|
||||
{
|
||||
disabled: true,
|
||||
text: i18n('notMuted'),
|
||||
value: -1,
|
||||
},
|
||||
]),
|
||||
...getMuteOptions(muteExpiresAt, i18n).map(
|
||||
({ disabled, name, value }) => ({
|
||||
disabled,
|
||||
text: name,
|
||||
value,
|
||||
})
|
||||
),
|
||||
],
|
||||
[i18n, muteExpiresAt]
|
||||
);
|
||||
setMuteExpiration(ms);
|
||||
};
|
||||
|
||||
const onChangeDontNotifyForMentionsIfMuted = (rawValue: string) => {
|
||||
setDontNotifyForMentionsIfMuted(rawValue === 'yes');
|
||||
};
|
||||
const onMuteChange = (rawValue: string) => {
|
||||
const ms = parseIntOrThrow(
|
||||
rawValue,
|
||||
'NotificationSettings: mute ms was not an integer'
|
||||
);
|
||||
setMuteExpiration(ms);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="conversation-details-panel">
|
||||
<PanelSection>
|
||||
<PanelRow
|
||||
icon={
|
||||
<ConversationDetailsIcon
|
||||
ariaLabel={i18n('muteNotificationsTitle')}
|
||||
icon={IconType.mute}
|
||||
/>
|
||||
}
|
||||
label={i18n('muteNotificationsTitle')}
|
||||
right={
|
||||
<Select options={muteOptions} onChange={onMuteChange} value={-1} />
|
||||
}
|
||||
/>
|
||||
{conversationType === 'group' && (
|
||||
const onChangeDontNotifyForMentionsIfMuted = (rawValue: string) => {
|
||||
setDontNotifyForMentionsIfMuted(rawValue === 'yes');
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="conversation-details-panel">
|
||||
<PanelSection>
|
||||
<PanelRow
|
||||
icon={
|
||||
<ConversationDetailsIcon
|
||||
ariaLabel={i18n(
|
||||
'ConversationNotificationsSettings__mentions__label'
|
||||
)}
|
||||
icon={IconType.mention}
|
||||
ariaLabel={i18n('muteNotificationsTitle')}
|
||||
icon={IconType.mute}
|
||||
/>
|
||||
}
|
||||
label={i18n('ConversationNotificationsSettings__mentions__label')}
|
||||
info={i18n('ConversationNotificationsSettings__mentions__info')}
|
||||
label={i18n('muteNotificationsTitle')}
|
||||
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'}
|
||||
options={muteOptions}
|
||||
onChange={onMuteChange}
|
||||
value={-1}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
)}
|
||||
</PanelSection>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
{conversationType === 'group' && (
|
||||
<PanelRow
|
||||
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>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -67,10 +67,8 @@ export const PendingInvites: React.ComponentType<PropsType> = ({
|
|||
}
|
||||
|
||||
const [selectedTab, setSelectedTab] = React.useState(Tab.Requests);
|
||||
const [
|
||||
stagedMemberships,
|
||||
setStagedMemberships,
|
||||
] = React.useState<Array<StagedMembershipType> | null>(null);
|
||||
const [stagedMemberships, setStagedMemberships] =
|
||||
React.useState<Array<StagedMembershipType> | null>(null);
|
||||
|
||||
return (
|
||||
<div className="conversation-details-panel">
|
||||
|
@ -379,10 +377,8 @@ function MembersPendingProfileKey({
|
|||
membership => membership.metadata.addedByUserId
|
||||
);
|
||||
|
||||
const {
|
||||
[ourUuid]: ourPendingMemberships,
|
||||
...otherPendingMembershipGroups
|
||||
} = groupedPendingMemberships;
|
||||
const { [ourUuid]: ourPendingMemberships, ...otherPendingMembershipGroups } =
|
||||
groupedPendingMemberships;
|
||||
|
||||
const otherPendingMemberships = Object.keys(otherPendingMembershipGroups)
|
||||
.map(id => members.find(member => member.id === id))
|
||||
|
|
|
@ -9,28 +9,27 @@ export enum RequestState {
|
|||
Active,
|
||||
}
|
||||
|
||||
export const bemGenerator = (block: string) => (
|
||||
element: string,
|
||||
modifier?: string | Record<string, boolean>
|
||||
): string => {
|
||||
const base = `${block}__${element}`;
|
||||
const classes = [base];
|
||||
export const bemGenerator =
|
||||
(block: string) =>
|
||||
(element: string, modifier?: string | Record<string, boolean>): string => {
|
||||
const base = `${block}__${element}`;
|
||||
const classes = [base];
|
||||
|
||||
let conditionals: Record<string, boolean> = {};
|
||||
let conditionals: Record<string, boolean> = {};
|
||||
|
||||
if (modifier) {
|
||||
if (typeof modifier === 'string') {
|
||||
classes.push(`${base}--${modifier}`);
|
||||
} else {
|
||||
conditionals = Object.keys(modifier).reduce(
|
||||
(acc, key) => ({
|
||||
...acc,
|
||||
[`${base}--${key}`]: modifier[key],
|
||||
}),
|
||||
{} as Record<string, boolean>
|
||||
);
|
||||
if (modifier) {
|
||||
if (typeof modifier === 'string') {
|
||||
classes.push(`${base}--${modifier}`);
|
||||
} else {
|
||||
conditionals = Object.keys(modifier).reduce(
|
||||
(acc, key) => ({
|
||||
...acc,
|
||||
[`${base}--${key}`]: modifier[key],
|
||||
}),
|
||||
{} as Record<string, boolean>
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return classNames(classes, conditionals);
|
||||
};
|
||||
return classNames(classes, conditionals);
|
||||
};
|
||||
|
|
|
@ -31,7 +31,7 @@ const DAY_MS = 24 * 60 * 60 * 1000;
|
|||
export const days = (n: number) => n * DAY_MS;
|
||||
const tokens = ['foo', 'bar', 'baz', 'qux', 'quux'];
|
||||
|
||||
const contentTypes = ({
|
||||
const contentTypes = {
|
||||
gif: 'image/gif',
|
||||
jpg: 'image/jpeg',
|
||||
png: 'image/png',
|
||||
|
@ -39,7 +39,7 @@ const contentTypes = ({
|
|||
docx: 'application/text',
|
||||
pdf: 'application/pdf',
|
||||
txt: 'application/text',
|
||||
} as unknown) as Record<string, MIMEType>;
|
||||
} as unknown as Record<string, MIMEType>;
|
||||
|
||||
const createRandomFile = (
|
||||
startTime: number,
|
||||
|
|
|
@ -94,62 +94,66 @@ type GenericMediaItemWithSection<T> = {
|
|||
type: T;
|
||||
mediaItem: MediaItemType;
|
||||
};
|
||||
type MediaItemWithStaticSection = GenericMediaItemWithSection<StaticSectionType>;
|
||||
type MediaItemWithYearMonthSection = GenericMediaItemWithSection<YearMonthSectionType> & {
|
||||
year: number;
|
||||
month: number;
|
||||
};
|
||||
type MediaItemWithStaticSection =
|
||||
GenericMediaItemWithSection<StaticSectionType>;
|
||||
type MediaItemWithYearMonthSection =
|
||||
GenericMediaItemWithSection<YearMonthSectionType> & {
|
||||
year: number;
|
||||
month: number;
|
||||
};
|
||||
type MediaItemWithSection =
|
||||
| MediaItemWithStaticSection
|
||||
| MediaItemWithYearMonthSection;
|
||||
|
||||
const withSection = (referenceDateTime: moment.Moment) => (
|
||||
mediaItem: MediaItemType
|
||||
): MediaItemWithSection => {
|
||||
const today = moment(referenceDateTime).startOf('day');
|
||||
const yesterday = moment(referenceDateTime).subtract(1, 'day').startOf('day');
|
||||
const thisWeek = moment(referenceDateTime).startOf('isoWeek');
|
||||
const thisMonth = moment(referenceDateTime).startOf('month');
|
||||
const withSection =
|
||||
(referenceDateTime: moment.Moment) =>
|
||||
(mediaItem: MediaItemType): MediaItemWithSection => {
|
||||
const today = moment(referenceDateTime).startOf('day');
|
||||
const yesterday = moment(referenceDateTime)
|
||||
.subtract(1, 'day')
|
||||
.startOf('day');
|
||||
const thisWeek = moment(referenceDateTime).startOf('isoWeek');
|
||||
const thisMonth = moment(referenceDateTime).startOf('month');
|
||||
|
||||
const { message } = mediaItem;
|
||||
const mediaItemReceivedDate = moment.utc(getMessageTimestamp(message));
|
||||
if (mediaItemReceivedDate.isAfter(today)) {
|
||||
return {
|
||||
order: 0,
|
||||
type: 'today',
|
||||
mediaItem,
|
||||
};
|
||||
}
|
||||
if (mediaItemReceivedDate.isAfter(yesterday)) {
|
||||
return {
|
||||
order: 1,
|
||||
type: 'yesterday',
|
||||
mediaItem,
|
||||
};
|
||||
}
|
||||
if (mediaItemReceivedDate.isAfter(thisWeek)) {
|
||||
return {
|
||||
order: 2,
|
||||
type: 'thisWeek',
|
||||
mediaItem,
|
||||
};
|
||||
}
|
||||
if (mediaItemReceivedDate.isAfter(thisMonth)) {
|
||||
return {
|
||||
order: 3,
|
||||
type: 'thisMonth',
|
||||
mediaItem,
|
||||
};
|
||||
}
|
||||
const { message } = mediaItem;
|
||||
const mediaItemReceivedDate = moment.utc(getMessageTimestamp(message));
|
||||
if (mediaItemReceivedDate.isAfter(today)) {
|
||||
return {
|
||||
order: 0,
|
||||
type: 'today',
|
||||
mediaItem,
|
||||
};
|
||||
}
|
||||
if (mediaItemReceivedDate.isAfter(yesterday)) {
|
||||
return {
|
||||
order: 1,
|
||||
type: 'yesterday',
|
||||
mediaItem,
|
||||
};
|
||||
}
|
||||
if (mediaItemReceivedDate.isAfter(thisWeek)) {
|
||||
return {
|
||||
order: 2,
|
||||
type: 'thisWeek',
|
||||
mediaItem,
|
||||
};
|
||||
}
|
||||
if (mediaItemReceivedDate.isAfter(thisMonth)) {
|
||||
return {
|
||||
order: 3,
|
||||
type: 'thisMonth',
|
||||
mediaItem,
|
||||
};
|
||||
}
|
||||
|
||||
const month: number = mediaItemReceivedDate.month();
|
||||
const year: number = mediaItemReceivedDate.year();
|
||||
const month: number = mediaItemReceivedDate.month();
|
||||
const year: number = mediaItemReceivedDate.year();
|
||||
|
||||
return {
|
||||
order: year * 100 + month,
|
||||
type: 'yearMonth',
|
||||
month,
|
||||
year,
|
||||
mediaItem,
|
||||
return {
|
||||
order: year * 100 + month,
|
||||
type: 'yearMonth',
|
||||
month,
|
||||
year,
|
||||
mediaItem,
|
||||
};
|
||||
};
|
||||
};
|
||||
|
|
|
@ -61,8 +61,8 @@ type PropsType = {
|
|||
| 'unblurredAvatarPath'
|
||||
>;
|
||||
|
||||
export const BaseConversationListItem: FunctionComponent<PropsType> = React.memo(
|
||||
function BaseConversationListItem({
|
||||
export const BaseConversationListItem: FunctionComponent<PropsType> =
|
||||
React.memo(function BaseConversationListItem({
|
||||
acceptedMessageRequest,
|
||||
avatarPath,
|
||||
badge,
|
||||
|
@ -238,8 +238,7 @@ export const BaseConversationListItem: FunctionComponent<PropsType> = React.memo
|
|||
{contents}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
function UnreadIndicator({ count = 0 }: Readonly<{ count?: number }>) {
|
||||
let classModifier: undefined | string;
|
||||
|
|
|
@ -42,8 +42,7 @@ story.add('No Replacement', () => {
|
|||
|
||||
story.add('Two Replacements', () => {
|
||||
const props = createProps({
|
||||
text:
|
||||
'Begin <<left>>Inside #1<<right>> This is between the two <<left>>Inside #2<<right>> End.',
|
||||
text: 'Begin <<left>>Inside #1<<right>> This is between the two <<left>>Inside #2<<right>> End.',
|
||||
});
|
||||
|
||||
return <MessageBodyHighlight {...props} />;
|
||||
|
@ -59,8 +58,7 @@ story.add('Two Replacements with an @mention', () => {
|
|||
start: 33,
|
||||
},
|
||||
],
|
||||
text:
|
||||
'Begin <<left>>Inside #1<<right>> \uFFFC This is between the two <<left>>Inside #2<<right>> End.',
|
||||
text: 'Begin <<left>>Inside #1<<right>> \uFFFC This is between the two <<left>>Inside #2<<right>> End.',
|
||||
});
|
||||
|
||||
return <MessageBodyHighlight {...props} />;
|
||||
|
@ -68,8 +66,7 @@ story.add('Two Replacements with an @mention', () => {
|
|||
|
||||
story.add('Emoji + Newlines + URLs', () => {
|
||||
const props = createProps({
|
||||
text:
|
||||
'\nhttp://somewhere.com\n\n🔥 Before -- <<left>>A 🔥 inside<<right>> -- After 🔥',
|
||||
text: '\nhttp://somewhere.com\n\n🔥 Before -- <<left>>A 🔥 inside<<right>> -- After 🔥',
|
||||
});
|
||||
|
||||
return <MessageBodyHighlight {...props} />;
|
||||
|
|
|
@ -148,8 +148,7 @@ story.add('Empty (should be invalid)', () => {
|
|||
|
||||
story.add('@mention', () => {
|
||||
const props = createProps({
|
||||
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',
|
||||
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',
|
||||
bodyRanges: [
|
||||
{
|
||||
length: 1,
|
||||
|
@ -175,8 +174,7 @@ story.add('@mention', () => {
|
|||
|
||||
story.add('@mention regexp', () => {
|
||||
const props = createProps({
|
||||
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',
|
||||
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',
|
||||
bodyRanges: [
|
||||
{
|
||||
length: 1,
|
||||
|
@ -215,8 +213,7 @@ story.add('@mention no-matches', () => {
|
|||
|
||||
story.add('@mention no-matches', () => {
|
||||
const props = createProps({
|
||||
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',
|
||||
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',
|
||||
bodyRanges: [
|
||||
{
|
||||
length: 1,
|
||||
|
|
|
@ -6,6 +6,5 @@ import React from 'react';
|
|||
|
||||
type PropsType = Record<string, never>;
|
||||
|
||||
export const SearchResultsLoadingFakeHeader: FunctionComponent<PropsType> = () => (
|
||||
<div className="module-SearchResultsLoadingFakeHeader" />
|
||||
);
|
||||
export const SearchResultsLoadingFakeHeader: FunctionComponent<PropsType> =
|
||||
() => <div className="module-SearchResultsLoadingFakeHeader" />;
|
||||
|
|
|
@ -353,9 +353,8 @@ data.forEach(emoji => {
|
|||
|
||||
if (skin_variations) {
|
||||
Object.entries(skin_variations).forEach(([tone, variation]) => {
|
||||
imageByEmoji[
|
||||
convertShortName(short_name, tone as SkinToneKey)
|
||||
] = makeImagePath(variation.image);
|
||||
imageByEmoji[convertShortName(short_name, tone as SkinToneKey)] =
|
||||
makeImagePath(variation.image);
|
||||
dataByEmoji[convertShortName(short_name, tone as SkinToneKey)] = emoji;
|
||||
});
|
||||
}
|
||||
|
|
|
@ -62,7 +62,8 @@ export class LeftPaneChooseGroupMembersHelper extends LeftPaneHelper<LeftPaneCho
|
|||
this.candidateContacts = candidateContacts;
|
||||
this.cantAddContactForModal = cantAddContactForModal;
|
||||
this.isShowingMaximumGroupSizeModal = isShowingMaximumGroupSizeModal;
|
||||
this.isShowingRecommendedGroupSizeModal = isShowingRecommendedGroupSizeModal;
|
||||
this.isShowingRecommendedGroupSizeModal =
|
||||
isShowingRecommendedGroupSizeModal;
|
||||
this.searchTerm = searchTerm;
|
||||
this.selectedContacts = selectedContacts;
|
||||
|
||||
|
|
|
@ -161,9 +161,8 @@ export class LeftPaneSearchHelper extends LeftPaneHelper<LeftPaneSearchPropsType
|
|||
return undefined;
|
||||
}
|
||||
|
||||
const conversationRowCount = getRowCountForLoadedSearchResults(
|
||||
conversationResults
|
||||
);
|
||||
const conversationRowCount =
|
||||
getRowCountForLoadedSearchResults(conversationResults);
|
||||
const contactRowCount = getRowCountForLoadedSearchResults(contactResults);
|
||||
const messageRowCount = getRowCountForLoadedSearchResults(messageResults);
|
||||
|
||||
|
|
|
@ -33,10 +33,8 @@ export const StickerManager = React.memo(
|
|||
i18n,
|
||||
}: Props) => {
|
||||
const focusRef = React.createRef<HTMLDivElement>();
|
||||
const [
|
||||
packToPreview,
|
||||
setPackToPreview,
|
||||
] = React.useState<StickerPackType | null>(null);
|
||||
const [packToPreview, setPackToPreview] =
|
||||
React.useState<StickerPackType | null>(null);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (!knownPacks) {
|
||||
|
|
|
@ -237,7 +237,8 @@ export const StickerPicker = React.memo(
|
|||
'module-sticker-picker__header__button',
|
||||
'module-sticker-picker__header__button--add-pack',
|
||||
{
|
||||
'module-sticker-picker__header__button--hint': showPickerHint,
|
||||
'module-sticker-picker__header__button--hint':
|
||||
showPickerHint,
|
||||
}
|
||||
)}
|
||||
onClick={onClickAddPack}
|
||||
|
@ -300,8 +301,10 @@ export const StickerPicker = React.memo(
|
|||
className={classNames(
|
||||
'module-sticker-picker__body__content',
|
||||
{
|
||||
'module-sticker-picker__body__content--under-text': showText,
|
||||
'module-sticker-picker__body__content--under-long-text': showLongText,
|
||||
'module-sticker-picker__body__content--under-text':
|
||||
showText,
|
||||
'module-sticker-picker__body__content--under-long-text':
|
||||
showLongText,
|
||||
}
|
||||
)}
|
||||
>
|
||||
|
|
|
@ -28,7 +28,7 @@ export class Timers {
|
|||
|
||||
this.timers.set(id, timer);
|
||||
|
||||
return ({ id } as unknown) as Timeout;
|
||||
return { id } as unknown as Timeout;
|
||||
}
|
||||
|
||||
public clearTimeout({ id }: Timeout): ReturnType<typeof clearTimeout> {
|
||||
|
|
98
ts/groups.ts
98
ts/groups.ts
|
@ -311,9 +311,10 @@ export function buildGroupLink(conversation: ConversationModel): string {
|
|||
return `https://signal.group/#${hash}`;
|
||||
}
|
||||
|
||||
export function parseGroupLink(
|
||||
hash: string
|
||||
): { masterKey: string; inviteLinkPassword: string } {
|
||||
export function parseGroupLink(hash: string): {
|
||||
masterKey: string;
|
||||
inviteLinkPassword: string;
|
||||
} {
|
||||
const base64 = fromWebSafeBase64(hash);
|
||||
const buffer = Bytes.fromBase64(base64);
|
||||
|
||||
|
@ -614,7 +615,8 @@ export async function buildAddMembersChange(
|
|||
const now = Date.now();
|
||||
|
||||
const addMembers: Array<Proto.GroupChange.Actions.AddMemberAction> = [];
|
||||
const addPendingMembers: Array<Proto.GroupChange.Actions.AddMemberPendingProfileKeyAction> = [];
|
||||
const addPendingMembers: Array<Proto.GroupChange.Actions.AddMemberPendingProfileKeyAction> =
|
||||
[];
|
||||
|
||||
await Promise.all(
|
||||
conversationIds.map(async conversationId => {
|
||||
|
@ -687,7 +689,8 @@ export async function buildAddMembersChange(
|
|||
memberPendingProfileKey.addedByUserId = ourUuidCipherTextBuffer;
|
||||
memberPendingProfileKey.timestamp = now;
|
||||
|
||||
const addPendingMemberAction = new Proto.GroupChange.Actions.AddMemberPendingProfileKeyAction();
|
||||
const addPendingMemberAction =
|
||||
new Proto.GroupChange.Actions.AddMemberPendingProfileKeyAction();
|
||||
addPendingMemberAction.added = memberPendingProfileKey;
|
||||
|
||||
addPendingMembers.push(addPendingMemberAction);
|
||||
|
@ -782,7 +785,8 @@ export async function buildUpdateAttributesChange(
|
|||
if (typeof description === 'string') {
|
||||
hasChangedSomething = true;
|
||||
|
||||
actions.modifyDescription = new Proto.GroupChange.Actions.ModifyDescriptionAction();
|
||||
actions.modifyDescription =
|
||||
new Proto.GroupChange.Actions.ModifyDescriptionAction();
|
||||
actions.modifyDescription.descriptionBytes = buildGroupDescriptionBuffer(
|
||||
clientZkGroupCipher,
|
||||
description
|
||||
|
@ -822,7 +826,8 @@ export function buildDisappearingMessagesTimerChange({
|
|||
const blobPlaintext = Proto.GroupAttributeBlob.encode(blob).finish();
|
||||
const blobCipherText = encryptGroupBlob(clientZkGroupCipher, blobPlaintext);
|
||||
|
||||
const timerAction = new Proto.GroupChange.Actions.ModifyDisappearingMessagesTimerAction();
|
||||
const timerAction =
|
||||
new Proto.GroupChange.Actions.ModifyDisappearingMessagesTimerAction();
|
||||
timerAction.timer = blobCipherText;
|
||||
|
||||
actions.version = (group.revision || 0) + 1;
|
||||
|
@ -835,10 +840,10 @@ export function buildInviteLinkPasswordChange(
|
|||
group: ConversationAttributesType,
|
||||
inviteLinkPassword: string
|
||||
): Proto.GroupChange.Actions {
|
||||
const inviteLinkPasswordAction = new Proto.GroupChange.Actions.ModifyInviteLinkPasswordAction();
|
||||
inviteLinkPasswordAction.inviteLinkPassword = Bytes.fromBase64(
|
||||
inviteLinkPassword
|
||||
);
|
||||
const inviteLinkPasswordAction =
|
||||
new Proto.GroupChange.Actions.ModifyInviteLinkPasswordAction();
|
||||
inviteLinkPasswordAction.inviteLinkPassword =
|
||||
Bytes.fromBase64(inviteLinkPassword);
|
||||
|
||||
const actions = new Proto.GroupChange.Actions();
|
||||
actions.version = (group.revision || 0) + 1;
|
||||
|
@ -852,13 +857,14 @@ export function buildNewGroupLinkChange(
|
|||
inviteLinkPassword: string,
|
||||
addFromInviteLinkAccess: AccessRequiredEnum
|
||||
): Proto.GroupChange.Actions {
|
||||
const accessControlAction = new Proto.GroupChange.Actions.ModifyAddFromInviteLinkAccessControlAction();
|
||||
const accessControlAction =
|
||||
new Proto.GroupChange.Actions.ModifyAddFromInviteLinkAccessControlAction();
|
||||
accessControlAction.addFromInviteLinkAccess = addFromInviteLinkAccess;
|
||||
|
||||
const inviteLinkPasswordAction = new Proto.GroupChange.Actions.ModifyInviteLinkPasswordAction();
|
||||
inviteLinkPasswordAction.inviteLinkPassword = Bytes.fromBase64(
|
||||
inviteLinkPassword
|
||||
);
|
||||
const inviteLinkPasswordAction =
|
||||
new Proto.GroupChange.Actions.ModifyInviteLinkPasswordAction();
|
||||
inviteLinkPasswordAction.inviteLinkPassword =
|
||||
Bytes.fromBase64(inviteLinkPassword);
|
||||
|
||||
const actions = new Proto.GroupChange.Actions();
|
||||
actions.version = (group.revision || 0) + 1;
|
||||
|
@ -872,7 +878,8 @@ export function buildAccessControlAddFromInviteLinkChange(
|
|||
group: ConversationAttributesType,
|
||||
value: AccessRequiredEnum
|
||||
): Proto.GroupChange.Actions {
|
||||
const accessControlAction = new Proto.GroupChange.Actions.ModifyAddFromInviteLinkAccessControlAction();
|
||||
const accessControlAction =
|
||||
new Proto.GroupChange.Actions.ModifyAddFromInviteLinkAccessControlAction();
|
||||
accessControlAction.addFromInviteLinkAccess = value;
|
||||
|
||||
const actions = new Proto.GroupChange.Actions();
|
||||
|
@ -900,7 +907,8 @@ export function buildAccessControlAttributesChange(
|
|||
group: ConversationAttributesType,
|
||||
value: AccessRequiredEnum
|
||||
): Proto.GroupChange.Actions {
|
||||
const accessControlAction = new Proto.GroupChange.Actions.ModifyAttributesAccessControlAction();
|
||||
const accessControlAction =
|
||||
new Proto.GroupChange.Actions.ModifyAttributesAccessControlAction();
|
||||
accessControlAction.attributesAccess = value;
|
||||
|
||||
const actions = new Proto.GroupChange.Actions();
|
||||
|
@ -914,7 +922,8 @@ export function buildAccessControlMembersChange(
|
|||
group: ConversationAttributesType,
|
||||
value: AccessRequiredEnum
|
||||
): Proto.GroupChange.Actions {
|
||||
const accessControlAction = new Proto.GroupChange.Actions.ModifyMembersAccessControlAction();
|
||||
const accessControlAction =
|
||||
new Proto.GroupChange.Actions.ModifyMembersAccessControlAction();
|
||||
accessControlAction.membersAccess = value;
|
||||
|
||||
const actions = new Proto.GroupChange.Actions();
|
||||
|
@ -942,7 +951,8 @@ export function buildDeletePendingAdminApprovalMemberChange({
|
|||
const clientZkGroupCipher = getClientZkGroupCipher(group.secretParams);
|
||||
const uuidCipherTextBuffer = encryptUuid(clientZkGroupCipher, uuid);
|
||||
|
||||
const deleteMemberPendingAdminApproval = new Proto.GroupChange.Actions.DeleteMemberPendingAdminApprovalAction();
|
||||
const deleteMemberPendingAdminApproval =
|
||||
new Proto.GroupChange.Actions.DeleteMemberPendingAdminApprovalAction();
|
||||
deleteMemberPendingAdminApproval.deletedUserId = uuidCipherTextBuffer;
|
||||
|
||||
actions.version = (group.revision || 0) + 1;
|
||||
|
@ -973,7 +983,8 @@ export function buildAddPendingAdminApprovalMemberChange({
|
|||
serverPublicParamsBase64
|
||||
);
|
||||
|
||||
const addMemberPendingAdminApproval = new Proto.GroupChange.Actions.AddMemberPendingAdminApprovalAction();
|
||||
const addMemberPendingAdminApproval =
|
||||
new Proto.GroupChange.Actions.AddMemberPendingAdminApprovalAction();
|
||||
const presentation = createProfileKeyCredentialPresentation(
|
||||
clientZkProfileCipher,
|
||||
profileKeyCredentialBase64,
|
||||
|
@ -1049,7 +1060,8 @@ export function buildDeletePendingMemberChange({
|
|||
|
||||
const deletePendingMembers = uuids.map(uuid => {
|
||||
const uuidCipherTextBuffer = encryptUuid(clientZkGroupCipher, uuid);
|
||||
const deletePendingMember = new Proto.GroupChange.Actions.DeleteMemberPendingProfileKeyAction();
|
||||
const deletePendingMember =
|
||||
new Proto.GroupChange.Actions.DeleteMemberPendingProfileKeyAction();
|
||||
deletePendingMember.deletedUserId = uuidCipherTextBuffer;
|
||||
return deletePendingMember;
|
||||
});
|
||||
|
@ -1131,7 +1143,8 @@ export function buildPromotePendingAdminApprovalMemberChange({
|
|||
const clientZkGroupCipher = getClientZkGroupCipher(group.secretParams);
|
||||
const uuidCipherTextBuffer = encryptUuid(clientZkGroupCipher, uuid);
|
||||
|
||||
const promotePendingMember = new Proto.GroupChange.Actions.PromoteMemberPendingAdminApprovalAction();
|
||||
const promotePendingMember =
|
||||
new Proto.GroupChange.Actions.PromoteMemberPendingAdminApprovalAction();
|
||||
promotePendingMember.userId = uuidCipherTextBuffer;
|
||||
promotePendingMember.role = MEMBER_ROLE_ENUM.DEFAULT;
|
||||
|
||||
|
@ -1167,7 +1180,8 @@ export function buildPromoteMemberChange({
|
|||
group.secretParams
|
||||
);
|
||||
|
||||
const promotePendingMember = new Proto.GroupChange.Actions.PromoteMemberPendingProfileKeyAction();
|
||||
const promotePendingMember =
|
||||
new Proto.GroupChange.Actions.PromoteMemberPendingProfileKeyAction();
|
||||
promotePendingMember.presentation = presentation;
|
||||
|
||||
actions.version = (group.revision || 0) + 1;
|
||||
|
@ -1270,9 +1284,8 @@ export async function modifyGroupV2({
|
|||
group: conversation.attributes,
|
||||
});
|
||||
|
||||
const groupChangeBuffer = Proto.GroupChange.encode(
|
||||
groupChange
|
||||
).finish();
|
||||
const groupChangeBuffer =
|
||||
Proto.GroupChange.encode(groupChange).finish();
|
||||
const groupChangeBase64 = Bytes.toBase64(groupChangeBuffer);
|
||||
|
||||
// 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
|
||||
// sent to our linked devices.
|
||||
const m = new window.Whisper.Message(({
|
||||
const m = new window.Whisper.Message({
|
||||
conversationId: conversation.id,
|
||||
type: 'not-to-save',
|
||||
sent_at: timestamp,
|
||||
received_at: timestamp,
|
||||
// TODO: DESKTOP-722
|
||||
// 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()
|
||||
// don't save anything to the database.
|
||||
|
@ -1825,7 +1838,8 @@ export async function getGroupMigrationMembers(
|
|||
const logId = conversation.idForLogging();
|
||||
const MEMBER_ROLE_ENUM = Proto.Member.Role;
|
||||
|
||||
const ourConversationId = window.ConversationController.getOurConversationId();
|
||||
const ourConversationId =
|
||||
window.ConversationController.getOurConversationId();
|
||||
if (!ourConversationId) {
|
||||
throw new Error(
|
||||
`getGroupMigrationMembers/${logId}: Couldn't fetch our own conversationId!`
|
||||
|
@ -2034,15 +2048,15 @@ export async function initiateMigrationToGroupV2(
|
|||
const secretParams = Bytes.toBase64(fields.secretParams);
|
||||
const publicParams = Bytes.toBase64(fields.publicParams);
|
||||
|
||||
const ourConversationId = window.ConversationController.getOurConversationId();
|
||||
const ourConversationId =
|
||||
window.ConversationController.getOurConversationId();
|
||||
if (!ourConversationId) {
|
||||
throw new Error(
|
||||
`initiateMigrationToGroupV2/${logId}: Couldn't fetch our own conversationId!`
|
||||
);
|
||||
}
|
||||
const ourConversation = window.ConversationController.get(
|
||||
ourConversationId
|
||||
);
|
||||
const ourConversation =
|
||||
window.ConversationController.get(ourConversationId);
|
||||
if (!ourConversation) {
|
||||
throw new Error(
|
||||
`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) {
|
||||
throw new Error(
|
||||
`wrapWithSyncMessageSend/${logId}: Cannot get our conversationId!`
|
||||
|
@ -2324,7 +2339,8 @@ export function buildMigrationBubble(
|
|||
newAttributes: ConversationAttributesType
|
||||
): MessageAttributesType {
|
||||
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..
|
||||
const combinedConversationIds: Array<string> = [
|
||||
|
@ -4007,12 +4023,10 @@ async function applyGroupChange({
|
|||
const members: Record<UUIDStringType, GroupV2MemberType> = fromPairs(
|
||||
(result.membersV2 || []).map(member => [member.uuid, member])
|
||||
);
|
||||
const pendingMembers: Record<
|
||||
UUIDStringType,
|
||||
GroupV2PendingMemberType
|
||||
> = fromPairs(
|
||||
(result.pendingMembersV2 || []).map(member => [member.uuid, member])
|
||||
);
|
||||
const pendingMembers: Record<UUIDStringType, GroupV2PendingMemberType> =
|
||||
fromPairs(
|
||||
(result.pendingMembersV2 || []).map(member => [member.uuid, member])
|
||||
);
|
||||
const pendingAdminApprovalMembers: Record<
|
||||
UUIDStringType,
|
||||
GroupV2PendingAdminApprovalType
|
||||
|
|
|
@ -56,7 +56,8 @@ export async function joinViaLink(hash: string): Promise<void> {
|
|||
const existingConversation =
|
||||
window.ConversationController.get(id) ||
|
||||
window.ConversationController.getByDerivedGroupV2Id(id);
|
||||
const ourConversationId = window.ConversationController.getOurConversationIdOrThrow();
|
||||
const ourConversationId =
|
||||
window.ConversationController.getOurConversationIdOrThrow();
|
||||
|
||||
if (
|
||||
existingConversation &&
|
||||
|
@ -339,15 +340,14 @@ export async function joinViaLink(hash: string): Promise<void> {
|
|||
|
||||
log.info(`joinViaLink/${logId}: Showing modal`);
|
||||
|
||||
let groupV2InfoDialog:
|
||||
| Backbone.View
|
||||
| undefined = new window.Whisper.ReactWrapperView({
|
||||
className: 'group-v2-join-dialog-wrapper',
|
||||
JSX: window.Signal.State.Roots.createGroupV2JoinModal(window.reduxStore, {
|
||||
join,
|
||||
onClose: closeDialog,
|
||||
}),
|
||||
});
|
||||
let groupV2InfoDialog: Backbone.View | undefined =
|
||||
new window.Whisper.ReactWrapperView({
|
||||
className: 'group-v2-join-dialog-wrapper',
|
||||
JSX: window.Signal.State.Roots.createGroupV2JoinModal(window.reduxStore, {
|
||||
join,
|
||||
onClose: closeDialog,
|
||||
}),
|
||||
});
|
||||
|
||||
// We declare a new function here so we can await but not block
|
||||
const fetchAvatar = async () => {
|
||||
|
|
|
@ -30,10 +30,8 @@ export function toggleSelectedContactForGroupAddition(
|
|||
numberOfContactsAlreadyInGroup,
|
||||
selectedConversationIds: oldSelectedConversationIds,
|
||||
} = currentState;
|
||||
let {
|
||||
maximumGroupSizeModalState,
|
||||
recommendedGroupSizeModalState,
|
||||
} = currentState;
|
||||
let { maximumGroupSizeModalState, recommendedGroupSizeModalState } =
|
||||
currentState;
|
||||
|
||||
const selectedConversationIds = without(
|
||||
oldSelectedConversationIds,
|
||||
|
|
|
@ -26,10 +26,8 @@ export function useIntersectionObserver(): [
|
|||
(el?: Element | null) => void,
|
||||
IntersectionObserverEntry | null
|
||||
] {
|
||||
const [
|
||||
intersectionObserverEntry,
|
||||
setIntersectionObserverEntry,
|
||||
] = useState<IntersectionObserverEntry | null>(null);
|
||||
const [intersectionObserverEntry, setIntersectionObserverEntry] =
|
||||
useState<IntersectionObserverEntry | null>(null);
|
||||
|
||||
const unobserveRef = useRef<(() => unknown) | null>(null);
|
||||
|
||||
|
|
|
@ -102,7 +102,8 @@ export async function runReadOrViewSyncJob({
|
|||
return;
|
||||
}
|
||||
|
||||
const ourConversation = window.ConversationController.getOurConversationOrThrow();
|
||||
const ourConversation =
|
||||
window.ConversationController.getOurConversationOrThrow();
|
||||
const sendOptions = await getSendOptions(ourConversation.attributes, {
|
||||
syncMessage: true,
|
||||
});
|
||||
|
|
|
@ -31,12 +31,8 @@ import { JobQueue } from './JobQueue';
|
|||
import { jobQueueDatabaseStore } from './JobQueueDatabaseStore';
|
||||
import { getHttpErrorCode } from './helpers/getHttpErrorCode';
|
||||
|
||||
const {
|
||||
loadAttachmentData,
|
||||
loadPreviewData,
|
||||
loadQuoteData,
|
||||
loadStickerData,
|
||||
} = window.Signal.Migrations;
|
||||
const { loadAttachmentData, loadPreviewData, loadQuoteData, loadStickerData } =
|
||||
window.Signal.Migrations;
|
||||
const { Message } = window.Signal.Types;
|
||||
|
||||
const MAX_RETRY_TIME = durations.DAY;
|
||||
|
@ -357,7 +353,8 @@ function getMessageRecipients({
|
|||
const recipientIdentifiersWithoutMe: Array<string> = [];
|
||||
const untrustedConversationIds: Array<string> = [];
|
||||
|
||||
const currentConversationRecipients = conversation.getRecipientConversationIds();
|
||||
const currentConversationRecipients =
|
||||
conversation.getRecipientConversationIds();
|
||||
|
||||
Object.entries(message.get('sendStateByConversationId') || {}).forEach(
|
||||
([recipientConversationId, sendState]) => {
|
||||
|
@ -439,25 +436,22 @@ async function getMessageSendData({
|
|||
messageTimestamp = Date.now();
|
||||
}
|
||||
|
||||
const [
|
||||
attachmentsWithData,
|
||||
preview,
|
||||
quote,
|
||||
sticker,
|
||||
profileKey,
|
||||
] = await Promise.all([
|
||||
// We don't update the caches here because (1) we expect the caches to be populated on
|
||||
// initial send, so they should be there in the 99% case (2) if you're retrying a
|
||||
// failed message across restarts, we don't touch the cache for simplicity. If sends
|
||||
// are failing, let's not add the complication of a cache.
|
||||
Promise.all((message.get('attachments') ?? []).map(loadAttachmentData)),
|
||||
message.cachedOutgoingPreviewData ||
|
||||
loadPreviewData(message.get('preview')),
|
||||
message.cachedOutgoingQuoteData || loadQuoteData(message.get('quote')),
|
||||
message.cachedOutgoingStickerData ||
|
||||
loadStickerData(message.get('sticker')),
|
||||
conversation.get('profileSharing') ? ourProfileKeyService.get() : undefined,
|
||||
]);
|
||||
const [attachmentsWithData, preview, quote, sticker, profileKey] =
|
||||
await Promise.all([
|
||||
// We don't update the caches here because (1) we expect the caches to be populated
|
||||
// on initial send, so they should be there in the 99% case (2) if you're retrying
|
||||
// a failed message across restarts, we don't touch the cache for simplicity. If
|
||||
// sends are failing, let's not add the complication of a cache.
|
||||
Promise.all((message.get('attachments') ?? []).map(loadAttachmentData)),
|
||||
message.cachedOutgoingPreviewData ||
|
||||
loadPreviewData(message.get('preview')),
|
||||
message.cachedOutgoingQuoteData || loadQuoteData(message.get('quote')),
|
||||
message.cachedOutgoingStickerData ||
|
||||
loadStickerData(message.get('sticker')),
|
||||
conversation.get('profileSharing')
|
||||
? ourProfileKeyService.get()
|
||||
: undefined,
|
||||
]);
|
||||
|
||||
const { body, attachments } = window.Whisper.Message.getLongMessageAttachment(
|
||||
{
|
||||
|
|
|
@ -72,7 +72,8 @@ export class ReactionJobQueue extends JobQueue<ReactionJobData> {
|
|||
|
||||
await window.ConversationController.load();
|
||||
|
||||
const ourConversationId = window.ConversationController.getOurConversationIdOrThrow();
|
||||
const ourConversationId =
|
||||
window.ConversationController.getOurConversationIdOrThrow();
|
||||
|
||||
const message = await getMessageById(messageId);
|
||||
if (!message) {
|
||||
|
@ -82,13 +83,11 @@ export class ReactionJobQueue extends JobQueue<ReactionJobData> {
|
|||
return;
|
||||
}
|
||||
|
||||
const {
|
||||
pendingReaction,
|
||||
emojiToRemove,
|
||||
} = reactionUtil.getNewestPendingOutgoingReaction(
|
||||
getReactions(message),
|
||||
ourConversationId
|
||||
);
|
||||
const { pendingReaction, emojiToRemove } =
|
||||
reactionUtil.getNewestPendingOutgoingReaction(
|
||||
getReactions(message),
|
||||
ourConversationId
|
||||
);
|
||||
if (!pendingReaction) {
|
||||
log.info(`no pending reaction for ${messageId}. Doing nothing`);
|
||||
return;
|
||||
|
@ -122,10 +121,8 @@ export class ReactionJobQueue extends JobQueue<ReactionJobData> {
|
|||
);
|
||||
}
|
||||
|
||||
const {
|
||||
allRecipientIdentifiers,
|
||||
recipientIdentifiersWithoutMe,
|
||||
} = getRecipients(pendingReaction, conversation);
|
||||
const { allRecipientIdentifiers, recipientIdentifiersWithoutMe } =
|
||||
getRecipients(pendingReaction, conversation);
|
||||
|
||||
const expireTimer = message.get('expireTimer');
|
||||
const profileKey = conversation.get('profileSharing')
|
||||
|
@ -295,7 +292,8 @@ function getRecipients(
|
|||
const allRecipientIdentifiers: Array<string> = [];
|
||||
const recipientIdentifiersWithoutMe: Array<string> = [];
|
||||
|
||||
const currentConversationRecipients = conversation.getRecipientConversationIds();
|
||||
const currentConversationRecipients =
|
||||
conversation.getRecipientConversationIds();
|
||||
|
||||
for (const id of reactionUtil.getUnsentConversationIds(reaction)) {
|
||||
const recipient = window.ConversationController.get(id);
|
||||
|
|
|
@ -18,21 +18,7 @@ import { HTTPError } from '../textsecure/Errors';
|
|||
|
||||
const RETRY_WAIT_TIME = durations.MINUTE;
|
||||
const RETRYABLE_4XX_FAILURE_STATUSES = new Set([
|
||||
404,
|
||||
408,
|
||||
410,
|
||||
412,
|
||||
413,
|
||||
414,
|
||||
417,
|
||||
423,
|
||||
424,
|
||||
425,
|
||||
426,
|
||||
428,
|
||||
429,
|
||||
431,
|
||||
449,
|
||||
404, 408, 410, 412, 413, 414, 417, 423, 424, 425, 426, 428, 429, 431, 449,
|
||||
]);
|
||||
|
||||
const is4xxStatus = (code: number): boolean => code >= 400 && code <= 499;
|
||||
|
|
|
@ -41,10 +41,8 @@ const RETRY_BACKOFF: Record<number, number> = {
|
|||
let enabled = false;
|
||||
let timeout: NodeJS.Timeout | null;
|
||||
let logger: LoggerType;
|
||||
const _activeAttachmentDownloadJobs: Record<
|
||||
string,
|
||||
Promise<void> | undefined
|
||||
> = {};
|
||||
const _activeAttachmentDownloadJobs: Record<string, Promise<void> | undefined> =
|
||||
{};
|
||||
|
||||
type StartOptionsType = {
|
||||
logger: LoggerType;
|
||||
|
@ -231,9 +229,8 @@ async function _runJob(job?: AttachmentDownloadJobType): Promise<void> {
|
|||
return;
|
||||
}
|
||||
|
||||
const upgradedAttachment = await window.Signal.Migrations.processNewAttachment(
|
||||
downloaded
|
||||
);
|
||||
const upgradedAttachment =
|
||||
await window.Signal.Migrations.processNewAttachment(downloaded);
|
||||
|
||||
await _addAttachmentToMessage(message, upgradedAttachment, { type, index });
|
||||
|
||||
|
|
|
@ -47,10 +47,11 @@ export class Deletes extends Collection<DeleteModel> {
|
|||
try {
|
||||
// The conversation the deleted message was in; we have to find it in the database
|
||||
// to to figure that out.
|
||||
const targetConversation = await window.ConversationController.getConversationForTargetMessage(
|
||||
del.get('fromId'),
|
||||
del.get('targetSentTimestamp')
|
||||
);
|
||||
const targetConversation =
|
||||
await window.ConversationController.getConversationForTargetMessage(
|
||||
del.get('fromId'),
|
||||
del.get('targetSentTimestamp')
|
||||
);
|
||||
|
||||
if (!targetConversation) {
|
||||
log.info(
|
||||
|
|
|
@ -222,9 +222,8 @@ export class MessageReceipts extends Collection<MessageReceiptModel> {
|
|||
wasDeliveredWithSealedSender(sourceConversationId, message)) ||
|
||||
type === MessageReceiptType.Read
|
||||
) {
|
||||
const recipient = window.ConversationController.get(
|
||||
sourceConversationId
|
||||
);
|
||||
const recipient =
|
||||
window.ConversationController.get(sourceConversationId);
|
||||
const recipientUuid = recipient?.get('uuid');
|
||||
const deviceId = receipt.get('sourceDevice');
|
||||
|
||||
|
|
|
@ -58,21 +58,21 @@ export class Reactions extends Collection<ReactionModel> {
|
|||
try {
|
||||
// The conversation the target message was in; we have to find it in the database
|
||||
// to to figure that out.
|
||||
const targetConversationId = window.ConversationController.ensureContactIds(
|
||||
{
|
||||
const targetConversationId =
|
||||
window.ConversationController.ensureContactIds({
|
||||
uuid: reaction.get('targetAuthorUuid'),
|
||||
}
|
||||
);
|
||||
});
|
||||
if (!targetConversationId) {
|
||||
throw new Error(
|
||||
'onReaction: No conversationId returned from ensureContactIds!'
|
||||
);
|
||||
}
|
||||
|
||||
const targetConversation = await window.ConversationController.getConversationForTargetMessage(
|
||||
targetConversationId,
|
||||
reaction.get('targetTimestamp')
|
||||
);
|
||||
const targetConversation =
|
||||
await window.ConversationController.getConversationForTargetMessage(
|
||||
targetConversationId,
|
||||
reaction.get('targetTimestamp')
|
||||
);
|
||||
if (!targetConversation) {
|
||||
log.info(
|
||||
'No target conversation for reaction',
|
||||
|
|
|
@ -543,7 +543,8 @@ export class ConversationModel extends window.Backbone
|
|||
const idLog = this.idForLogging();
|
||||
|
||||
// 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);
|
||||
if (!toRequest) {
|
||||
|
@ -1234,9 +1235,8 @@ export class ConversationModel extends window.Backbone
|
|||
`sendTypingMessage(${this.idForLogging()}): sending ${content.isTyping}`
|
||||
);
|
||||
|
||||
const contentMessage = window.textsecure.messaging.getTypingContentMessage(
|
||||
content
|
||||
);
|
||||
const contentMessage =
|
||||
window.textsecure.messaging.getTypingContentMessage(content);
|
||||
|
||||
const { ContentHint } = Proto.UnidentifiedSenderMessage.Message;
|
||||
|
||||
|
@ -1415,7 +1415,8 @@ export class ConversationModel extends window.Backbone
|
|||
const messageRequestsEnabled = window.Signal.RemoteConfig.isEnabled(
|
||||
'desktop.messageRequests'
|
||||
);
|
||||
const ourConversationId = window.ConversationController.getOurConversationId();
|
||||
const ourConversationId =
|
||||
window.ConversationController.getOurConversationId();
|
||||
|
||||
let groupVersion: undefined | 1 | 2;
|
||||
if (isGroupV1(this.attributes)) {
|
||||
|
@ -1499,8 +1500,8 @@ export class ConversationModel extends window.Backbone
|
|||
pendingApprovalMemberships: this.getPendingApprovalMemberships(),
|
||||
profileKey: this.get('profileKey'),
|
||||
messageRequestsEnabled,
|
||||
accessControlAddFromInviteLink: this.get('accessControl')
|
||||
?.addFromInviteLink,
|
||||
accessControlAddFromInviteLink:
|
||||
this.get('accessControl')?.addFromInviteLink,
|
||||
accessControlAttributes: this.get('accessControl')?.attributes,
|
||||
accessControlMembers: this.get('accessControl')?.members,
|
||||
announcementsOnly: Boolean(this.get('announcementsOnly')),
|
||||
|
@ -1708,7 +1709,8 @@ export class ConversationModel extends window.Backbone
|
|||
try {
|
||||
const messageRequestEnum = Proto.SyncMessage.MessageRequestResponse.Type;
|
||||
const isLocalAction = !fromSync && !viaStorageServiceSync;
|
||||
const ourConversationId = window.ConversationController.getOurConversationId();
|
||||
const ourConversationId =
|
||||
window.ConversationController.getOurConversationId();
|
||||
|
||||
const currentMessageRequestState = this.get('messageRequestResponseType');
|
||||
const didResponseChange = response !== currentMessageRequestState;
|
||||
|
@ -1846,7 +1848,8 @@ export class ConversationModel extends window.Backbone
|
|||
inviteLinkPassword: string;
|
||||
approvalRequired: boolean;
|
||||
}): Promise<void> {
|
||||
const ourConversationId = window.ConversationController.getOurConversationIdOrThrow();
|
||||
const ourConversationId =
|
||||
window.ConversationController.getOurConversationIdOrThrow();
|
||||
const ourUuid = window.textsecure.storage.user.getCheckedUuid().toString();
|
||||
try {
|
||||
if (approvalRequired) {
|
||||
|
@ -1899,7 +1902,8 @@ export class ConversationModel extends window.Backbone
|
|||
}
|
||||
|
||||
async cancelJoinRequest(): Promise<void> {
|
||||
const ourConversationId = window.ConversationController.getOurConversationIdOrThrow();
|
||||
const ourConversationId =
|
||||
window.ConversationController.getOurConversationIdOrThrow();
|
||||
|
||||
const inviteLinkPassword = this.get('groupInviteLinkPassword');
|
||||
if (!inviteLinkPassword) {
|
||||
|
@ -1965,7 +1969,8 @@ export class ConversationModel extends window.Backbone
|
|||
}
|
||||
|
||||
async leaveGroupV2(): Promise<void> {
|
||||
const ourConversationId = window.ConversationController.getOurConversationId();
|
||||
const ourConversationId =
|
||||
window.ConversationController.getOurConversationId();
|
||||
|
||||
if (
|
||||
ourConversationId &&
|
||||
|
@ -2095,7 +2100,8 @@ export class ConversationModel extends window.Backbone
|
|||
// server updates were successful.
|
||||
await this.applyMessageRequestResponse(response);
|
||||
|
||||
const ourConversation = window.ConversationController.getOurConversationOrThrow();
|
||||
const ourConversation =
|
||||
window.ConversationController.getOurConversationOrThrow();
|
||||
const sendOptions = await getSendOptions(ourConversation.attributes, {
|
||||
syncMessage: true,
|
||||
});
|
||||
|
@ -2244,11 +2250,12 @@ export class ConversationModel extends window.Backbone
|
|||
|
||||
// handle the incoming key from the sync messages - need different
|
||||
// behavior if that key doesn't match the current key
|
||||
keyChange = await window.textsecure.storage.protocol.processVerifiedMessage(
|
||||
uuid,
|
||||
verified,
|
||||
options.key || undefined
|
||||
);
|
||||
keyChange =
|
||||
await window.textsecure.storage.protocol.processVerifiedMessage(
|
||||
uuid,
|
||||
verified,
|
||||
options.key || undefined
|
||||
);
|
||||
} else if (uuid) {
|
||||
keyChange = await window.textsecure.storage.protocol.setVerified(
|
||||
uuid,
|
||||
|
@ -2315,7 +2322,8 @@ export class ConversationModel extends window.Backbone
|
|||
// 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
|
||||
// contacts. So we merge their sendOptions.
|
||||
const ourConversation = window.ConversationController.getOurConversationOrThrow();
|
||||
const ourConversation =
|
||||
window.ConversationController.getOurConversationOrThrow();
|
||||
const sendOptions = await getSendOptions(ourConversation.attributes, {
|
||||
syncMessage: true,
|
||||
});
|
||||
|
@ -2523,7 +2531,7 @@ export class ConversationModel extends window.Backbone
|
|||
receivedAt,
|
||||
});
|
||||
|
||||
const message = ({
|
||||
const message = {
|
||||
conversationId: this.id,
|
||||
type: 'chat-session-refreshed',
|
||||
sent_at: receivedAt,
|
||||
|
@ -2532,7 +2540,7 @@ export class ConversationModel extends window.Backbone
|
|||
readStatus: ReadStatus.Unread,
|
||||
// TODO: DESKTOP-722
|
||||
// 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 model = window.MessageController.register(
|
||||
|
@ -2562,7 +2570,7 @@ export class ConversationModel extends window.Backbone
|
|||
senderUuid,
|
||||
});
|
||||
|
||||
const message = ({
|
||||
const message = {
|
||||
conversationId: this.id,
|
||||
type: 'delivery-issue',
|
||||
sourceUuid: senderUuid,
|
||||
|
@ -2572,7 +2580,7 @@ export class ConversationModel extends window.Backbone
|
|||
readStatus: ReadStatus.Unread,
|
||||
// TODO: DESKTOP-722
|
||||
// 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 model = window.MessageController.register(
|
||||
|
@ -2597,7 +2605,7 @@ export class ConversationModel extends window.Backbone
|
|||
);
|
||||
|
||||
const timestamp = Date.now();
|
||||
const message = ({
|
||||
const message = {
|
||||
conversationId: this.id,
|
||||
type: 'keychange',
|
||||
sent_at: this.get('timestamp'),
|
||||
|
@ -2608,7 +2616,7 @@ export class ConversationModel extends window.Backbone
|
|||
schemaVersion: Message.VERSION_NEEDED_FOR_DISPLAY,
|
||||
// TODO: DESKTOP-722
|
||||
// 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 model = window.MessageController.register(
|
||||
|
@ -2653,7 +2661,7 @@ export class ConversationModel extends window.Backbone
|
|||
);
|
||||
|
||||
const timestamp = Date.now();
|
||||
const message = ({
|
||||
const message = {
|
||||
conversationId: this.id,
|
||||
type: 'verified-change',
|
||||
sent_at: lastMessage,
|
||||
|
@ -2664,7 +2672,7 @@ export class ConversationModel extends window.Backbone
|
|||
local: options.local,
|
||||
readStatus: ReadStatus.Unread,
|
||||
// TODO: DESKTOP-722
|
||||
} as unknown) as MessageAttributesType;
|
||||
} as unknown as MessageAttributesType;
|
||||
|
||||
const id = await window.Signal.Data.saveMessage(message);
|
||||
const model = window.MessageController.register(
|
||||
|
@ -2715,7 +2723,7 @@ export class ConversationModel extends window.Backbone
|
|||
throw missingCaseError(callHistoryDetails);
|
||||
}
|
||||
|
||||
const message = ({
|
||||
const message = {
|
||||
conversationId: this.id,
|
||||
type: 'call-history',
|
||||
sent_at: timestamp,
|
||||
|
@ -2724,7 +2732,7 @@ export class ConversationModel extends window.Backbone
|
|||
readStatus: unread ? ReadStatus.Unread : ReadStatus.Read,
|
||||
callHistoryDetails: detailsToSave,
|
||||
// TODO: DESKTOP-722
|
||||
} as unknown) as MessageAttributesType;
|
||||
} as unknown as MessageAttributesType;
|
||||
|
||||
const id = await window.Signal.Data.saveMessage(message);
|
||||
const model = window.MessageController.register(
|
||||
|
@ -2774,7 +2782,7 @@ export class ConversationModel extends window.Backbone
|
|||
conversationId?: string
|
||||
): Promise<void> {
|
||||
const now = Date.now();
|
||||
const message = ({
|
||||
const message = {
|
||||
conversationId: this.id,
|
||||
type: 'profile-change',
|
||||
sent_at: now,
|
||||
|
@ -2784,7 +2792,7 @@ export class ConversationModel extends window.Backbone
|
|||
changedId: conversationId || this.id,
|
||||
profileChange,
|
||||
// TODO: DESKTOP-722
|
||||
} as unknown) as MessageAttributesType;
|
||||
} as unknown as MessageAttributesType;
|
||||
|
||||
const id = await window.Signal.Data.saveMessage(message);
|
||||
const model = window.MessageController.register(
|
||||
|
@ -3720,7 +3728,8 @@ export class ConversationModel extends window.Backbone
|
|||
return;
|
||||
}
|
||||
|
||||
const ourConversationId = window.ConversationController.getOurConversationId();
|
||||
const ourConversationId =
|
||||
window.ConversationController.getOurConversationId();
|
||||
if (!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);
|
||||
|
||||
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
|
||||
// indicator above it. We set it to 'unread' to trigger that placement.
|
||||
readStatus: ReadStatus.Unread,
|
||||
|
@ -4068,7 +4077,7 @@ export class ConversationModel extends window.Backbone
|
|||
fromGroupUpdate: options.fromGroupUpdate,
|
||||
},
|
||||
// TODO: DESKTOP-722
|
||||
} as unknown) as MessageAttributesType);
|
||||
} as unknown as MessageAttributesType);
|
||||
|
||||
const id = await window.Signal.Data.saveMessage(model.attributes);
|
||||
|
||||
|
@ -4112,14 +4121,15 @@ export class ConversationModel extends window.Backbone
|
|||
}
|
||||
|
||||
if (isDirectConversation(this.attributes)) {
|
||||
promise = window.textsecure.messaging.sendExpirationTimerUpdateToIdentifier(
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
this.getSendTarget()!,
|
||||
expireTimer,
|
||||
message.get('sent_at'),
|
||||
profileKey,
|
||||
sendOptions
|
||||
);
|
||||
promise =
|
||||
window.textsecure.messaging.sendExpirationTimerUpdateToIdentifier(
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
this.getSendTarget()!,
|
||||
expireTimer,
|
||||
message.get('sent_at'),
|
||||
profileKey,
|
||||
sendOptions
|
||||
);
|
||||
} else {
|
||||
promise = window.textsecure.messaging.sendExpirationTimerUpdateToGroup(
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
|
@ -4153,7 +4163,7 @@ export class ConversationModel extends window.Backbone
|
|||
}
|
||||
this.hasAddedHistoryDisclaimer = true;
|
||||
|
||||
const model = new window.Whisper.Message(({
|
||||
const model = new window.Whisper.Message({
|
||||
type: 'message-history-unsynced',
|
||||
// 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.
|
||||
|
@ -4163,7 +4173,7 @@ export class ConversationModel extends window.Backbone
|
|||
received_at: window.Signal.Util.incrementMessageCounter(),
|
||||
received_at_ms: timestamp,
|
||||
// TODO: DESKTOP-722
|
||||
} as unknown) as MessageAttributesType);
|
||||
} as unknown as MessageAttributesType);
|
||||
|
||||
const id = await window.Signal.Data.saveMessage(model.attributes);
|
||||
|
||||
|
@ -4190,7 +4200,7 @@ export class ConversationModel extends window.Backbone
|
|||
this.set({ left: true });
|
||||
window.Signal.Data.updateConversation(this.attributes);
|
||||
|
||||
const model = new window.Whisper.Message(({
|
||||
const model = new window.Whisper.Message({
|
||||
group_update: { left: 'You' },
|
||||
conversationId: this.id,
|
||||
type: 'outgoing',
|
||||
|
@ -4198,7 +4208,7 @@ export class ConversationModel extends window.Backbone
|
|||
received_at: window.Signal.Util.incrementMessageCounter(),
|
||||
received_at_ms: now,
|
||||
// TODO: DESKTOP-722
|
||||
} as unknown) as MessageAttributesType);
|
||||
} as unknown as MessageAttributesType);
|
||||
|
||||
const id = await window.Signal.Data.saveMessage(model.attributes);
|
||||
model.set({ id });
|
||||
|
@ -4250,16 +4260,14 @@ export class ConversationModel extends window.Backbone
|
|||
}
|
||||
|
||||
const ourUuid = window.textsecure.storage.user.getCheckedUuid();
|
||||
const ourGroups = await window.ConversationController.getAllGroupsInvolvingUuid(
|
||||
ourUuid
|
||||
);
|
||||
const ourGroups =
|
||||
await window.ConversationController.getAllGroupsInvolvingUuid(ourUuid);
|
||||
const theirUuid = this.getUuid();
|
||||
if (!theirUuid) {
|
||||
return;
|
||||
}
|
||||
const theirGroups = await window.ConversationController.getAllGroupsInvolvingUuid(
|
||||
theirUuid
|
||||
);
|
||||
const theirGroups =
|
||||
await window.ConversationController.getAllGroupsInvolvingUuid(theirUuid);
|
||||
|
||||
const sharedGroups = window._.intersection(ourGroups, theirGroups);
|
||||
const sharedGroupNames = sharedGroups.map(conversation =>
|
||||
|
@ -4277,7 +4285,8 @@ export class ConversationModel extends window.Backbone
|
|||
|
||||
async getProfiles(): Promise<void> {
|
||||
// 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(
|
||||
window._.map(conversations, conversation =>
|
||||
getProfile(conversation.get('uuid'), conversation.get('e164'))
|
||||
|
@ -4290,7 +4299,7 @@ export class ConversationModel extends window.Backbone
|
|||
return;
|
||||
}
|
||||
// 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) {
|
||||
return;
|
||||
}
|
||||
|
@ -4344,7 +4353,7 @@ export class ConversationModel extends window.Backbone
|
|||
|
||||
const avatar = await window.textsecure.messaging.getAvatar(avatarPath);
|
||||
// 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) {
|
||||
return;
|
||||
}
|
||||
|
@ -4405,7 +4414,7 @@ export class ConversationModel extends window.Backbone
|
|||
|
||||
async deriveAccessKeyIfNeeded(): Promise<void> {
|
||||
// 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) {
|
||||
return;
|
||||
}
|
||||
|
@ -4499,9 +4508,8 @@ export class ConversationModel extends window.Backbone
|
|||
const number = this.get('e164')!;
|
||||
try {
|
||||
const parsedNumber = window.libphonenumber.parse(number);
|
||||
const regionCode = window.libphonenumber.getRegionCodeForNumber(
|
||||
parsedNumber
|
||||
);
|
||||
const regionCode =
|
||||
window.libphonenumber.getRegionCodeForNumber(parsedNumber);
|
||||
if (regionCode === window.storage.get('regionCode')) {
|
||||
return window.libphonenumber.format(
|
||||
parsedNumber,
|
||||
|
|
|
@ -148,10 +148,8 @@ declare const _: typeof window._;
|
|||
window.Whisper = window.Whisper || {};
|
||||
|
||||
const { Message: TypedMessage } = window.Signal.Types;
|
||||
const {
|
||||
deleteExternalMessageFiles,
|
||||
upgradeMessageSchema,
|
||||
} = window.Signal.Migrations;
|
||||
const { deleteExternalMessageFiles, upgradeMessageSchema } =
|
||||
window.Signal.Migrations;
|
||||
const { getTextWithMentions, GoogleChrome } = window.Signal.Util;
|
||||
|
||||
const { addStickerPackReference, getMessageBySender } = window.Signal.Data;
|
||||
|
@ -351,8 +349,8 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
|
|||
return window.ConversationController.getConversationId(identifier);
|
||||
});
|
||||
|
||||
const contacts: ReadonlyArray<SmartMessageDetailContact> = conversationIds.map(
|
||||
id => {
|
||||
const contacts: ReadonlyArray<SmartMessageDetailContact> =
|
||||
conversationIds.map(id => {
|
||||
const errorsForContact = getOwn(errorsGroupedById, id);
|
||||
const isOutgoingKeyError = Boolean(
|
||||
errorsForContact?.some(error => error.name === OUTGOING_KEY_ERROR)
|
||||
|
@ -384,8 +382,7 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
|
|||
isOutgoingKeyError,
|
||||
isUnidentifiedDelivery,
|
||||
};
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
return {
|
||||
sentAt: this.get('sent_at'),
|
||||
|
@ -474,9 +471,8 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
|
|||
i18n: window.i18n,
|
||||
ourConversationId: window.ConversationController.getOurConversationId(),
|
||||
renderContact: (conversationId: string) => {
|
||||
const conversation = window.ConversationController.get(
|
||||
conversationId
|
||||
);
|
||||
const conversation =
|
||||
window.ConversationController.get(conversationId);
|
||||
return conversation
|
||||
? conversation.getTitle()
|
||||
: window.i18n('unknownUser');
|
||||
|
@ -887,7 +883,8 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
|
|||
}
|
||||
|
||||
const timestamp = this.get('sent_at');
|
||||
const ourConversation = window.ConversationController.getOurConversationOrThrow();
|
||||
const ourConversation =
|
||||
window.ConversationController.getOurConversationOrThrow();
|
||||
const sendOptions = await getSendOptions(ourConversation.attributes, {
|
||||
syncMessage: true,
|
||||
});
|
||||
|
@ -1036,9 +1033,8 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
|
|||
const isKeyChangeValue = isKeyChange(attributes);
|
||||
const isMessageHistoryUnsyncedValue = isMessageHistoryUnsynced(attributes);
|
||||
const isProfileChangeValue = isProfileChange(attributes);
|
||||
const isUniversalTimerNotificationValue = isUniversalTimerNotification(
|
||||
attributes
|
||||
);
|
||||
const isUniversalTimerNotificationValue =
|
||||
isUniversalTimerNotification(attributes);
|
||||
|
||||
// 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
|
||||
const conversation = this.getConversation()!;
|
||||
|
||||
const currentConversationRecipients = conversation.getRecipientConversationIds();
|
||||
const currentConversationRecipients =
|
||||
conversation.getRecipientConversationIds();
|
||||
|
||||
// Determine retry recipients and get their most up-to-date addressing information
|
||||
const oldSendStateByConversationId =
|
||||
|
@ -1299,9 +1296,8 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
|
|||
}
|
||||
|
||||
removeOutgoingErrors(incomingIdentifier: string): CustomError {
|
||||
const incomingConversationId = window.ConversationController.getConversationId(
|
||||
incomingIdentifier
|
||||
);
|
||||
const incomingConversationId =
|
||||
window.ConversationController.getConversationId(incomingIdentifier);
|
||||
const errors = _.partition(
|
||||
this.get('errors'),
|
||||
e =>
|
||||
|
@ -1563,7 +1559,8 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
|
|||
}
|
||||
|
||||
async sendSyncMessage(): Promise<CallbackResultType | void> {
|
||||
const ourConversation = window.ConversationController.getOurConversationOrThrow();
|
||||
const ourConversation =
|
||||
window.ConversationController.getOurConversationOrThrow();
|
||||
const sendOptions = await getSendOptions(ourConversation.attributes, {
|
||||
syncMessage: true,
|
||||
});
|
||||
|
@ -1796,11 +1793,9 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
|
|||
} attachment downloads for message ${this.idForLogging()}`
|
||||
);
|
||||
|
||||
const [
|
||||
longMessageAttachments,
|
||||
normalAttachments,
|
||||
] = _.partition(attachmentsToQueue, attachment =>
|
||||
MIME.isLongMessage(attachment.contentType)
|
||||
const [longMessageAttachments, normalAttachments] = _.partition(
|
||||
attachmentsToQueue,
|
||||
attachment => MIME.isLongMessage(attachment.contentType)
|
||||
);
|
||||
|
||||
if (longMessageAttachments.length > 1) {
|
||||
|
@ -2313,11 +2308,10 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
|
|||
...(toUpdate.get('sendStateByConversationId') || {}),
|
||||
};
|
||||
|
||||
const unidentifiedStatus: Array<ProcessedUnidentifiedDeliveryStatus> = Array.isArray(
|
||||
data.unidentifiedStatus
|
||||
)
|
||||
? data.unidentifiedStatus
|
||||
: [];
|
||||
const unidentifiedStatus: Array<ProcessedUnidentifiedDeliveryStatus> =
|
||||
Array.isArray(data.unidentifiedStatus)
|
||||
? data.unidentifiedStatus
|
||||
: [];
|
||||
|
||||
unidentifiedStatus.forEach(
|
||||
({ destinationUuid, destination, unidentified }) => {
|
||||
|
@ -2326,13 +2320,12 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
|
|||
return;
|
||||
}
|
||||
|
||||
const destinationConversationId = window.ConversationController.ensureContactIds(
|
||||
{
|
||||
const destinationConversationId =
|
||||
window.ConversationController.ensureContactIds({
|
||||
uuid: destinationUuid,
|
||||
e164: destination,
|
||||
highTrust: true,
|
||||
}
|
||||
);
|
||||
});
|
||||
if (!destinationConversationId) {
|
||||
return;
|
||||
}
|
||||
|
@ -2345,17 +2338,16 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
|
|||
sendStateByConversationId,
|
||||
destinationConversationId
|
||||
);
|
||||
sendStateByConversationId[
|
||||
destinationConversationId
|
||||
] = previousSendState
|
||||
? sendStateReducer(previousSendState, {
|
||||
type: SendActionType.Sent,
|
||||
updatedAt,
|
||||
})
|
||||
: {
|
||||
status: SendStatus.Sent,
|
||||
updatedAt,
|
||||
};
|
||||
sendStateByConversationId[destinationConversationId] =
|
||||
previousSendState
|
||||
? sendStateReducer(previousSendState, {
|
||||
type: SendActionType.Sent,
|
||||
updatedAt,
|
||||
})
|
||||
: {
|
||||
status: SendStatus.Sent,
|
||||
updatedAt,
|
||||
};
|
||||
|
||||
if (unidentified) {
|
||||
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 = window.ConversationController.getOurConversationId()!;
|
||||
const ourConversationId =
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
window.ConversationController.getOurConversationId()!;
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
const senderId = window.ConversationController.ensureContactIds({
|
||||
e164: source,
|
||||
|
@ -2602,14 +2595,15 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
|
|||
if (!hasGroupV2Prop && dataMessage.group) {
|
||||
const pendingGroupUpdate: GroupV1Update = {};
|
||||
|
||||
const memberConversations: Array<ConversationModel> = await Promise.all(
|
||||
dataMessage.group.membersE164.map((e164: string) =>
|
||||
window.ConversationController.getOrCreateAndWait(
|
||||
e164,
|
||||
'private'
|
||||
const memberConversations: Array<ConversationModel> =
|
||||
await Promise.all(
|
||||
dataMessage.group.membersE164.map((e164: string) =>
|
||||
window.ConversationController.getOrCreateAndWait(
|
||||
e164,
|
||||
'private'
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
);
|
||||
const members = memberConversations.map(c => c.get('id'));
|
||||
attributes = {
|
||||
...attributes,
|
||||
|
@ -2633,14 +2627,16 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
|
|||
let hash;
|
||||
if (avatarAttachment) {
|
||||
try {
|
||||
downloadedAvatar = await window.Signal.Util.downloadAttachment(
|
||||
avatarAttachment
|
||||
);
|
||||
downloadedAvatar =
|
||||
await window.Signal.Util.downloadAttachment(
|
||||
avatarAttachment
|
||||
);
|
||||
|
||||
if (downloadedAvatar) {
|
||||
const loadedAttachment = await window.Signal.Migrations.loadAttachmentData(
|
||||
downloadedAvatar
|
||||
);
|
||||
const loadedAttachment =
|
||||
await window.Signal.Migrations.loadAttachmentData(
|
||||
downloadedAvatar
|
||||
);
|
||||
|
||||
hash = computeHash(loadedAttachment.data);
|
||||
}
|
||||
|
@ -2668,13 +2664,11 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
|
|||
|
||||
let avatar = null;
|
||||
if (downloadedAvatar && avatarAttachment !== null) {
|
||||
const onDiskAttachment = await Attachment.migrateDataToFileSystem(
|
||||
downloadedAvatar,
|
||||
{
|
||||
const onDiskAttachment =
|
||||
await Attachment.migrateDataToFileSystem(downloadedAvatar, {
|
||||
writeNewAttachmentData:
|
||||
window.Signal.Migrations.writeNewAttachmentData,
|
||||
}
|
||||
);
|
||||
});
|
||||
avatar = {
|
||||
...onDiskAttachment,
|
||||
hash,
|
||||
|
@ -3085,9 +3079,8 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
|
|||
|
||||
// Check for out-of-order view once open syncs
|
||||
if (isTapToView(message.attributes)) {
|
||||
const viewOnceOpenSync = ViewOnceOpenSyncs.getSingleton().forMessage(
|
||||
message
|
||||
);
|
||||
const viewOnceOpenSync =
|
||||
ViewOnceOpenSyncs.getSingleton().forMessage(message);
|
||||
if (viewOnceOpenSync) {
|
||||
await message.markViewOnceMessageViewed({ fromSync: true });
|
||||
changed = true;
|
||||
|
|
|
@ -5,46 +5,46 @@ import Delta from 'quill-delta';
|
|||
import type { RefObject } from 'react';
|
||||
import type { MemberRepository } from '../memberRepository';
|
||||
|
||||
export const matchMention = (
|
||||
memberRepositoryRef: RefObject<MemberRepository>
|
||||
) => (node: HTMLElement, delta: Delta): Delta => {
|
||||
const memberRepository = memberRepositoryRef.current;
|
||||
export const matchMention =
|
||||
(memberRepositoryRef: RefObject<MemberRepository>) =>
|
||||
(node: HTMLElement, delta: Delta): Delta => {
|
||||
const memberRepository = memberRepositoryRef.current;
|
||||
|
||||
if (memberRepository) {
|
||||
const { title } = node.dataset;
|
||||
if (memberRepository) {
|
||||
const { title } = node.dataset;
|
||||
|
||||
if (node.classList.contains('MessageBody__at-mention')) {
|
||||
const { id } = node.dataset;
|
||||
const conversation = memberRepository.getMemberById(id);
|
||||
if (node.classList.contains('MessageBody__at-mention')) {
|
||||
const { id } = node.dataset;
|
||||
const conversation = memberRepository.getMemberById(id);
|
||||
|
||||
if (conversation && conversation.uuid) {
|
||||
return new Delta().insert({
|
||||
mention: {
|
||||
title,
|
||||
uuid: conversation.uuid,
|
||||
},
|
||||
});
|
||||
if (conversation && conversation.uuid) {
|
||||
return new Delta().insert({
|
||||
mention: {
|
||||
title,
|
||||
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')) {
|
||||
const { uuid } = node.dataset;
|
||||
const conversation = memberRepository.getMemberByUuid(uuid);
|
||||
if (conversation && conversation.uuid) {
|
||||
return new Delta().insert({
|
||||
mention: {
|
||||
title: title || conversation.title,
|
||||
uuid: conversation.uuid,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
if (conversation && conversation.uuid) {
|
||||
return new Delta().insert({
|
||||
mention: {
|
||||
title: title || conversation.title,
|
||||
uuid: conversation.uuid,
|
||||
},
|
||||
});
|
||||
return new Delta().insert(`@${title}`);
|
||||
}
|
||||
|
||||
return new Delta().insert(`@${title}`);
|
||||
}
|
||||
}
|
||||
|
||||
return delta;
|
||||
};
|
||||
return delta;
|
||||
};
|
||||
|
|
|
@ -269,18 +269,15 @@ export class CallingClass {
|
|||
|
||||
RingRTC.handleOutgoingSignaling = this.handleOutgoingSignaling.bind(this);
|
||||
RingRTC.handleIncomingCall = this.handleIncomingCall.bind(this);
|
||||
RingRTC.handleAutoEndedIncomingCallRequest = this.handleAutoEndedIncomingCallRequest.bind(
|
||||
this
|
||||
);
|
||||
RingRTC.handleAutoEndedIncomingCallRequest =
|
||||
this.handleAutoEndedIncomingCallRequest.bind(this);
|
||||
RingRTC.handleLogMessage = this.handleLogMessage.bind(this);
|
||||
RingRTC.handleSendHttpRequest = this.handleSendHttpRequest.bind(this);
|
||||
RingRTC.handleSendCallMessage = this.handleSendCallMessage.bind(this);
|
||||
RingRTC.handleSendCallMessageToGroup = this.handleSendCallMessageToGroup.bind(
|
||||
this
|
||||
);
|
||||
RingRTC.handleGroupCallRingUpdate = this.handleGroupCallRingUpdate.bind(
|
||||
this
|
||||
);
|
||||
RingRTC.handleSendCallMessageToGroup =
|
||||
this.handleSendCallMessageToGroup.bind(this);
|
||||
RingRTC.handleGroupCallRingUpdate =
|
||||
this.handleGroupCallRingUpdate.bind(this);
|
||||
|
||||
this.attemptToGiveOurUuidToRingRtc();
|
||||
window.Whisper.events.on('userChanged', () => {
|
||||
|
@ -402,9 +399,8 @@ export class CallingClass {
|
|||
this.uxActions.showCallLobby({
|
||||
callMode: CallMode.Group,
|
||||
conversationId: conversationProps.id,
|
||||
isConversationTooBigToRing: isConversationTooBigToRing(
|
||||
conversationProps
|
||||
),
|
||||
isConversationTooBigToRing:
|
||||
isConversationTooBigToRing(conversationProps),
|
||||
...this.formatGroupCallForRedux(groupCall),
|
||||
});
|
||||
break;
|
||||
|
@ -708,9 +704,8 @@ export class CallingClass {
|
|||
hasLocalVideo: boolean,
|
||||
shouldRing: boolean
|
||||
): Promise<void> {
|
||||
const conversation = window.ConversationController.get(
|
||||
conversationId
|
||||
)?.format();
|
||||
const conversation =
|
||||
window.ConversationController.get(conversationId)?.format();
|
||||
if (!conversation) {
|
||||
log.error('Missing conversation; not joining group call');
|
||||
return;
|
||||
|
@ -999,9 +994,8 @@ export class CallingClass {
|
|||
declineGroupCall(conversationId: string, ringId: bigint): void {
|
||||
log.info('CallingClass.declineGroupCall()');
|
||||
|
||||
const groupId = window.ConversationController.get(conversationId)?.get(
|
||||
'groupId'
|
||||
);
|
||||
const groupId =
|
||||
window.ConversationController.get(conversationId)?.get('groupId');
|
||||
if (!groupId) {
|
||||
log.error(
|
||||
'declineGroupCall: could not find the group ID for that conversation'
|
||||
|
@ -1300,11 +1294,8 @@ export class CallingClass {
|
|||
);
|
||||
}
|
||||
|
||||
const {
|
||||
availableCameras,
|
||||
availableMicrophones,
|
||||
availableSpeakers,
|
||||
} = await this.getAvailableIODevices();
|
||||
const { availableCameras, availableMicrophones, availableSpeakers } =
|
||||
await this.getAvailableIODevices();
|
||||
|
||||
const preferredMicrophone = window.Events.getPreferredAudioInputDevice();
|
||||
const selectedMicIndex = findBestMatchingAudioDeviceIndex({
|
||||
|
@ -1394,9 +1385,8 @@ export class CallingClass {
|
|||
|
||||
const { storage } = window.textsecure;
|
||||
|
||||
const senderIdentityRecord = await storage.protocol.getOrMigrateIdentityRecord(
|
||||
new UUID(remoteUserId)
|
||||
);
|
||||
const senderIdentityRecord =
|
||||
await storage.protocol.getOrMigrateIdentityRecord(new UUID(remoteUserId));
|
||||
if (!senderIdentityRecord) {
|
||||
log.error('Missing sender identity record; ignoring call message.');
|
||||
return;
|
||||
|
|
|
@ -10,7 +10,7 @@ let layoutMap: LayoutMapType | undefined;
|
|||
export async function initialize(): Promise<void> {
|
||||
strictAssert(layoutMap === undefined, 'keyboardLayout already initialized');
|
||||
|
||||
const experimentalNavigator = (window.navigator as unknown) as {
|
||||
const experimentalNavigator = window.navigator as unknown as {
|
||||
keyboard: { getLayoutMap(): Promise<LayoutMapType> };
|
||||
};
|
||||
|
||||
|
|
|
@ -42,10 +42,8 @@ import * as log from '../logging/log';
|
|||
|
||||
type IManifestRecordIdentifier = Proto.ManifestRecord.IIdentifier;
|
||||
|
||||
const {
|
||||
eraseStorageServiceStateFromConversations,
|
||||
updateConversation,
|
||||
} = dataInterface;
|
||||
const { eraseStorageServiceStateFromConversations, updateConversation } =
|
||||
dataInterface;
|
||||
|
||||
const uploadBucket: Array<number> = [];
|
||||
|
||||
|
@ -604,11 +602,8 @@ async function createNewManifest() {
|
|||
|
||||
const version = window.storage.get('manifestVersion', 0);
|
||||
|
||||
const {
|
||||
conversationsToUpdate,
|
||||
newItems,
|
||||
storageManifest,
|
||||
} = await generateManifest(version, undefined, true);
|
||||
const { conversationsToUpdate, newItems, storageManifest } =
|
||||
await generateManifest(version, undefined, true);
|
||||
|
||||
await uploadManifest(version, {
|
||||
conversationsToUpdate,
|
||||
|
@ -650,7 +645,8 @@ async function fetchManifest(
|
|||
}
|
||||
|
||||
try {
|
||||
const credentials = await window.textsecure.messaging.getStorageCredentials();
|
||||
const credentials =
|
||||
await window.textsecure.messaging.getStorageCredentials();
|
||||
window.storage.put('storageCredentials', credentials);
|
||||
|
||||
const manifestBinary = await window.textsecure.messaging.getStorageManifest(
|
||||
|
@ -869,12 +865,13 @@ async function processRemoteRecords(
|
|||
);
|
||||
|
||||
const credentials = window.storage.get('storageCredentials');
|
||||
const storageItemsBuffer = await window.textsecure.messaging.getStorageRecords(
|
||||
Proto.ReadOperation.encode(readOperation).finish(),
|
||||
{
|
||||
credentials,
|
||||
}
|
||||
);
|
||||
const storageItemsBuffer =
|
||||
await window.textsecure.messaging.getStorageRecords(
|
||||
Proto.ReadOperation.encode(readOperation).finish(),
|
||||
{
|
||||
credentials,
|
||||
}
|
||||
);
|
||||
|
||||
const storageItems = Proto.StorageItems.decode(storageItemsBuffer);
|
||||
|
||||
|
@ -959,10 +956,11 @@ async function processRemoteRecords(
|
|||
// Collect full map of previously and currently unknown records
|
||||
const unknownRecords: Map<string, UnknownRecord> = new Map();
|
||||
|
||||
const unknownRecordsArray: ReadonlyArray<UnknownRecord> = window.storage.get(
|
||||
'storage-service-unknown-records',
|
||||
new Array<UnknownRecord>()
|
||||
);
|
||||
const unknownRecordsArray: ReadonlyArray<UnknownRecord> =
|
||||
window.storage.get(
|
||||
'storage-service-unknown-records',
|
||||
new Array<UnknownRecord>()
|
||||
);
|
||||
unknownRecordsArray.forEach((record: UnknownRecord) => {
|
||||
unknownRecords.set(record.storageID, record);
|
||||
});
|
||||
|
|
|
@ -246,7 +246,8 @@ export async function toAccountRecord(
|
|||
const pinnedConversation = window.ConversationController.get(id);
|
||||
|
||||
if (pinnedConversation) {
|
||||
const pinnedConversationRecord = new Proto.AccountRecord.PinnedConversation();
|
||||
const pinnedConversationRecord =
|
||||
new Proto.AccountRecord.PinnedConversation();
|
||||
|
||||
if (pinnedConversation.get('type') === 'private') {
|
||||
pinnedConversationRecord.identifier = 'contact';
|
||||
|
@ -953,9 +954,8 @@ export async function mergeAccountRecord(
|
|||
let conversationId: string | undefined;
|
||||
|
||||
if (contact) {
|
||||
conversationId = window.ConversationController.ensureContactIds(
|
||||
contact
|
||||
);
|
||||
conversationId =
|
||||
window.ConversationController.ensureContactIds(contact);
|
||||
} else if (legacyGroupId && legacyGroupId.length) {
|
||||
conversationId = Bytes.toBinary(legacyGroupId);
|
||||
} else if (groupMasterKey && groupMasterKey.length) {
|
||||
|
|
|
@ -6,20 +6,21 @@ import type { Middleware } from 'redux';
|
|||
|
||||
import { COLORS_CHANGED, COLOR_SELECTED } from '../state/ducks/conversations';
|
||||
|
||||
export const dispatchItemsMiddleware: Middleware = ({
|
||||
getState,
|
||||
}) => next => action => {
|
||||
const result = next(action);
|
||||
if (
|
||||
action.type === 'items/PUT' ||
|
||||
action.type === 'items/PUT_EXTERNAL' ||
|
||||
action.type === 'items/REMOVE' ||
|
||||
action.type === 'items/REMOVE_EXTERNAL' ||
|
||||
action.type === 'items/RESET' ||
|
||||
action.type === COLOR_SELECTED ||
|
||||
action.type === COLORS_CHANGED
|
||||
) {
|
||||
ipcRenderer.send('preferences-changed', getState().items);
|
||||
}
|
||||
return result;
|
||||
};
|
||||
export const dispatchItemsMiddleware: Middleware =
|
||||
({ getState }) =>
|
||||
next =>
|
||||
action => {
|
||||
const result = next(action);
|
||||
if (
|
||||
action.type === 'items/PUT' ||
|
||||
action.type === 'items/PUT_EXTERNAL' ||
|
||||
action.type === 'items/REMOVE' ||
|
||||
action.type === 'items/REMOVE_EXTERNAL' ||
|
||||
action.type === 'items/RESET' ||
|
||||
action.type === COLOR_SELECTED ||
|
||||
action.type === COLORS_CHANGED
|
||||
) {
|
||||
ipcRenderer.send('preferences-changed', getState().items);
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
|
|
@ -970,13 +970,11 @@ const updateConversationBatcher = createBatcher<ConversationType>({
|
|||
// We only care about the most recent update for each conversation
|
||||
const byId = groupBy(items, item => item.id);
|
||||
const ids = Object.keys(byId);
|
||||
const mostRecent = ids.map(
|
||||
(id: string): ConversationType => {
|
||||
const maybeLast = last(byId[id]);
|
||||
assert(maybeLast !== undefined, 'Empty array in `groupBy` result');
|
||||
return maybeLast;
|
||||
}
|
||||
);
|
||||
const mostRecent = ids.map((id: string): ConversationType => {
|
||||
const maybeLast = last(byId[id]);
|
||||
assert(maybeLast !== undefined, 'Empty array in `groupBy` result');
|
||||
return maybeLast;
|
||||
});
|
||||
|
||||
await updateConversations(mostRecent);
|
||||
},
|
||||
|
@ -1338,14 +1336,11 @@ async function getLastConversationMessages({
|
|||
ourUuid: UUIDStringType;
|
||||
Message: typeof MessageModel;
|
||||
}): Promise<LastConversationMessagesType> {
|
||||
const {
|
||||
preview,
|
||||
activity,
|
||||
hasUserInitiatedMessages,
|
||||
} = await channels.getLastConversationMessages({
|
||||
conversationId,
|
||||
ourUuid,
|
||||
});
|
||||
const { preview, activity, hasUserInitiatedMessages } =
|
||||
await channels.getLastConversationMessages({
|
||||
conversationId,
|
||||
ourUuid,
|
||||
});
|
||||
|
||||
return {
|
||||
preview: preview ? new Message(preview) : undefined,
|
||||
|
|
|
@ -724,7 +724,7 @@ async function getAllItems(): Promise<AllItemsType> {
|
|||
result[id] = value;
|
||||
}
|
||||
|
||||
return (result as unknown) as AllItemsType;
|
||||
return result as unknown as AllItemsType;
|
||||
}
|
||||
async function removeItemById(id: ItemKeyType): Promise<void> {
|
||||
return removeById(getInstance(), ITEMS_TABLE, id);
|
||||
|
@ -3215,16 +3215,8 @@ async function clearAllErrorStickerPackAttempts(): Promise<void> {
|
|||
}
|
||||
async function createOrUpdateSticker(sticker: StickerType): Promise<void> {
|
||||
const db = getInstance();
|
||||
const {
|
||||
emoji,
|
||||
height,
|
||||
id,
|
||||
isCoverOnly,
|
||||
lastUsed,
|
||||
packId,
|
||||
path,
|
||||
width,
|
||||
} = sticker;
|
||||
const { emoji, height, id, isCoverOnly, lastUsed, packId, path, width } =
|
||||
sticker;
|
||||
|
||||
if (!isNumber(id)) {
|
||||
throw new Error(
|
||||
|
|
|
@ -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
|
||||
*/
|
||||
export function cleanDataForIpc(
|
||||
data: unknown
|
||||
): {
|
||||
export function cleanDataForIpc(data: unknown): {
|
||||
// `any`s are dangerous but it's difficult (impossible?) to type this with generics.
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
cleaned: any;
|
||||
|
|
|
@ -98,10 +98,8 @@ export class MainSQL {
|
|||
join(scriptDir, isBundled ? 'mainWorker.bundle.js' : 'mainWorker.js')
|
||||
);
|
||||
|
||||
const {
|
||||
promise: onCorruption,
|
||||
resolve: resolveCorruption,
|
||||
} = explodePromise<Error>();
|
||||
const { promise: onCorruption, resolve: resolveCorruption } =
|
||||
explodePromise<Error>();
|
||||
this.onCorruption = onCorruption;
|
||||
|
||||
this.worker.on('message', (wrappedResponse: WrappedWorkerResponse) => {
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue