Create group link previews; don't open Signal links in browser first; allow ephemeral download of previously-error'd pack

This commit is contained in:
Scott Nonnenberg 2021-02-10 14:39:26 -08:00 committed by GitHub
parent f832b018fc
commit e10ae03bb7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 338 additions and 33 deletions

View file

@ -12,16 +12,18 @@ type GroupV2PendingMemberType = import('../model-types.d').GroupV2PendingMemberT
type MediaItemType = import('../components/LightboxGallery').MediaItemType;
type MessageType = import('../state/ducks/conversations').MessageType;
type GetLinkPreviewImageResult = {
data: ArrayBuffer;
size: number;
contentType: string;
width?: number;
height?: number;
};
type GetLinkPreviewResult = {
title: string;
url: string;
image: {
data: ArrayBuffer;
size: number;
contentType: string;
width: number;
height: number;
};
image?: GetLinkPreviewImageResult;
description: string | null;
date: number | null;
};
@ -3580,7 +3582,10 @@ Whisper.ConversationView = Whisper.View.extend({
this.renderLinkPreview();
},
async getStickerPackPreview(url: any) {
async getStickerPackPreview(
url: string,
abortSignal: any
): Promise<null | GetLinkPreviewResult> {
const isPackDownloaded = (pack: any) =>
pack && (pack.status === 'downloaded' || pack.status === 'installed');
const isPackValid = (pack: any) =>
@ -3604,7 +3609,12 @@ Whisper.ConversationView = Whisper.View.extend({
await window.Signal.Stickers.downloadEphemeralPack(id, keyBase64);
}
if (abortSignal.aborted) {
return null;
}
const pack = window.Signal.Stickers.getStickerPack(id);
if (!isPackValid(pack)) {
return null;
}
@ -3619,6 +3629,10 @@ Whisper.ConversationView = Whisper.View.extend({
? await window.Signal.Migrations.readTempData(sticker.path)
: await window.Signal.Migrations.readStickerData(sticker.path);
if (abortSignal.aborted) {
return null;
}
return {
title,
url,
@ -3628,6 +3642,8 @@ Whisper.ConversationView = Whisper.View.extend({
size: data.byteLength,
contentType: 'image/webp',
},
description: null,
date: null,
};
} catch (error) {
window.log.error(
@ -3642,12 +3658,99 @@ Whisper.ConversationView = Whisper.View.extend({
}
},
async getGroupPreview(
url: string,
abortSignal: any
): Promise<null | GetLinkPreviewResult> {
let urlObject;
try {
urlObject = new URL(url);
} catch (err) {
return null;
}
const { hash } = urlObject;
if (!hash) {
return null;
}
const groupData = hash.slice(1);
const {
inviteLinkPassword,
masterKey,
} = window.Signal.Groups.parseGroupLink(groupData);
const fields = window.Signal.Groups.deriveGroupFields(
window.Signal.Crypto.base64ToArrayBuffer(masterKey)
);
const id = window.Signal.Crypto.arrayBufferToBase64(fields.id);
const logId = `groupv2(${id})`;
const secretParams = window.Signal.Crypto.arrayBufferToBase64(
fields.secretParams
);
window.log.info(`getGroupPreview/${logId}: Fetching pre-join state`);
const result = await window.Signal.Groups.getPreJoinGroupInfo(
inviteLinkPassword,
masterKey
);
if (abortSignal.aborted) {
return null;
}
const title =
window.Signal.Groups.decryptGroupTitle(result.title, secretParams) ||
window.i18n('unknownGroup');
const description =
result.memberCount === 1 || result.memberCount === undefined
? window.i18n('GroupV2--join--member-count--single')
: window.i18n('GroupV2--join--member-count--multiple', {
count: result.memberCount.toString(),
});
let image: undefined | GetLinkPreviewImageResult;
if (result.avatar) {
try {
const data = await window.Signal.Groups.decryptGroupAvatar(
result.avatar,
secretParams
);
image = {
data,
size: data.byteLength,
contentType: 'image/jpeg',
};
} catch (error) {
const errorString = error && error.stack ? error.stack : error;
window.log.error(
`getGroupPreview/${logId}: Failed to fetch avatar ${errorString}`
);
}
}
if (abortSignal.aborted) {
return null;
}
return {
title,
description,
url,
image,
date: null,
};
},
async getPreview(
url: string,
abortSignal: any
): Promise<null | GetLinkPreviewResult> {
if (window.Signal.LinkPreviews.isStickerPack(url)) {
return this.getStickerPackPreview(url);
return this.getStickerPackPreview(url, abortSignal);
}
if (window.Signal.LinkPreviews.isGroupLink(url)) {
return this.getGroupPreview(url, abortSignal);
}
// This is already checked elsewhere, but we want to be extra-careful.