mirror of
https://github.com/signalapp/Signal-Desktop.git
synced 2025-12-24 12:19:41 +00:00
Use streams to download attachments directly to disk
Co-authored-by: trevor-signal <131492920+trevor-signal@users.noreply.github.com>
This commit is contained in:
@@ -2,33 +2,87 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import type { ConversationAttributesType } from '../model-types.d';
|
||||
import type { ContactAvatarType } from './Avatar';
|
||||
import { computeHash } from '../Crypto';
|
||||
|
||||
export type BuildAvatarUpdaterOptions = Readonly<{
|
||||
data?: Uint8Array;
|
||||
newAvatar?: ContactAvatarType;
|
||||
deleteAttachmentData: (path: string) => Promise<void>;
|
||||
doesAttachmentExist: (path: string) => Promise<boolean>;
|
||||
writeNewAttachmentData: (data: Uint8Array) => Promise<string>;
|
||||
}>;
|
||||
|
||||
// This function is ready to handle raw avatar data as well as an avatar which has
|
||||
// already been downloaded to disk.
|
||||
// Scenarios that go to disk today:
|
||||
// - During a contact sync (see ContactsParser.ts)
|
||||
// Scenarios that stay in memory today:
|
||||
// - models/Conversations/setProfileAvatar
|
||||
function buildAvatarUpdater({ field }: { field: 'avatar' | 'profileAvatar' }) {
|
||||
return async (
|
||||
conversation: Readonly<ConversationAttributesType>,
|
||||
data: Uint8Array,
|
||||
{
|
||||
data,
|
||||
newAvatar,
|
||||
deleteAttachmentData,
|
||||
doesAttachmentExist,
|
||||
writeNewAttachmentData,
|
||||
}: BuildAvatarUpdaterOptions
|
||||
): Promise<ConversationAttributesType> => {
|
||||
if (!conversation) {
|
||||
if (!conversation || (!data && !newAvatar)) {
|
||||
return conversation;
|
||||
}
|
||||
|
||||
const avatar = conversation[field];
|
||||
const oldAvatar = conversation[field];
|
||||
const newHash = data ? computeHash(data) : undefined;
|
||||
|
||||
const newHash = computeHash(data);
|
||||
if (!oldAvatar || !oldAvatar.hash) {
|
||||
if (newAvatar) {
|
||||
return {
|
||||
...conversation,
|
||||
[field]: newAvatar,
|
||||
};
|
||||
}
|
||||
if (data) {
|
||||
return {
|
||||
...conversation,
|
||||
[field]: {
|
||||
hash: newHash,
|
||||
path: await writeNewAttachmentData(data),
|
||||
},
|
||||
};
|
||||
}
|
||||
throw new Error('buildAvatarUpdater: neither newAvatar or newData');
|
||||
}
|
||||
|
||||
if (!avatar || !avatar.hash) {
|
||||
const { hash, path } = oldAvatar;
|
||||
const exists = await doesAttachmentExist(path);
|
||||
if (!exists) {
|
||||
window.SignalContext.log.warn(
|
||||
`Conversation.buildAvatarUpdater: attachment ${path} did not exist`
|
||||
);
|
||||
}
|
||||
|
||||
if (exists) {
|
||||
if (newAvatar && hash && hash === newAvatar.hash) {
|
||||
await deleteAttachmentData(newAvatar.path);
|
||||
return conversation;
|
||||
}
|
||||
if (data && hash && hash === newHash) {
|
||||
return conversation;
|
||||
}
|
||||
}
|
||||
|
||||
await deleteAttachmentData(path);
|
||||
|
||||
if (newAvatar) {
|
||||
return {
|
||||
...conversation,
|
||||
[field]: newAvatar,
|
||||
};
|
||||
}
|
||||
if (data) {
|
||||
return {
|
||||
...conversation,
|
||||
[field]: {
|
||||
@@ -38,27 +92,7 @@ function buildAvatarUpdater({ field }: { field: 'avatar' | 'profileAvatar' }) {
|
||||
};
|
||||
}
|
||||
|
||||
const { hash, path } = avatar;
|
||||
const exists = await doesAttachmentExist(path);
|
||||
if (!exists) {
|
||||
window.SignalContext.log.warn(
|
||||
`Conversation.buildAvatarUpdater: attachment ${path} did not exist`
|
||||
);
|
||||
}
|
||||
|
||||
if (exists && hash === newHash) {
|
||||
return conversation;
|
||||
}
|
||||
|
||||
await deleteAttachmentData(path);
|
||||
|
||||
return {
|
||||
...conversation,
|
||||
[field]: {
|
||||
hash: newHash,
|
||||
path: await writeNewAttachmentData(data),
|
||||
},
|
||||
};
|
||||
throw new Error('buildAvatarUpdater: neither newAvatar or newData');
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user