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:
parent
f832b018fc
commit
e10ae03bb7
8 changed files with 338 additions and 33 deletions
|
@ -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.
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue