fix: Bug - Local files should be remembered by client

This commit is contained in:
2026-06-11 01:54:00 +02:00
parent 5bf4f698df
commit 494a05e606
19 changed files with 1611 additions and 143 deletions
@@ -257,39 +257,7 @@ export class AttachmentTransferService {
} catch { /* non-critical */ }
}
if (attachment.size <= MAX_AUTO_SAVE_SIZE_BYTES) {
void this.persistence.saveFileToDisk(attachment, file);
} else if (shouldCopyUploaderMediaToAppData(
attachment,
attachment.filePath,
this.attachmentStorage.canCopyFiles()
) && attachment.filePath) {
const savedPath = await this.persistence.persistUploadCopyFromSourcePath(attachment, attachment.filePath);
if (savedPath) {
const fileUrl = await this.attachmentStorage.getFileUrl(savedPath);
if (fileUrl) {
attachment.objectUrl = fileUrl;
attachment.available = true;
}
}
} else if (
this.isPlayableMedia(attachment) &&
attachment.size > MAX_AUTO_SAVE_SIZE_BYTES &&
this.attachmentStorage.canWriteFiles()
) {
const savedPath = await this.persistence.saveFileToDisk(attachment, file);
if (savedPath) {
const fileUrl = await this.attachmentStorage.getFileUrl(savedPath);
if (fileUrl) {
attachment.objectUrl = fileUrl;
attachment.available = true;
}
}
}
await this.persistPublishedAttachment(attachment, file);
const fileAnnounceEvent: FileAnnounceEvent = {
type: 'file-announce',
@@ -760,12 +728,63 @@ export class AttachmentTransferService {
void this.persistence.persistAttachmentMeta(attachment);
}
/**
* Persist an outgoing attachment so it survives restart/logout-login: small
* files are auto-saved, oversized uploader media is copied or streamed to the
* active store, and the inline object URL is upgraded to the saved file when
* the store provides one. Oversized media on capped stores (browser) stays in
* memory / peer-served and degrades gracefully.
*/
private async persistPublishedAttachment(attachment: Attachment, file: File): Promise<void> {
if (attachment.size <= MAX_AUTO_SAVE_SIZE_BYTES) {
void this.persistence.saveFileToDisk(attachment, file);
return;
}
if (!this.attachmentStorage.canPersistSize(attachment.size)) {
return;
}
if (shouldCopyUploaderMediaToAppData(
attachment,
attachment.filePath,
this.attachmentStorage.canCopyFiles()
) && attachment.filePath) {
await this.applySavedPathObjectUrl(
attachment,
await this.persistence.persistUploadCopyFromSourcePath(attachment, attachment.filePath)
);
return;
}
if (this.isPlayableMedia(attachment) && this.attachmentStorage.canWriteFiles()) {
await this.applySavedPathObjectUrl(attachment, await this.persistence.saveFileToDisk(attachment, file));
}
}
private async applySavedPathObjectUrl(attachment: Attachment, savedPath: string | null): Promise<void> {
if (!savedPath) {
return;
}
const fileUrl = await this.attachmentStorage.getFileUrl(savedPath);
if (fileUrl) {
attachment.objectUrl = fileUrl;
attachment.available = true;
}
}
private isPlayableMedia(attachment: Pick<Attachment, 'mime'>): boolean {
return attachment.mime.startsWith('video/') || attachment.mime.startsWith('audio/');
}
private shouldReceiveToDisk(attachment: Attachment): boolean {
return this.isPlayableMedia(attachment) && !attachment.filePath && this.attachmentStorage.canWriteFiles();
return this.isPlayableMedia(attachment) &&
!attachment.filePath &&
this.attachmentStorage.canStreamToDisk() &&
this.attachmentStorage.canPersistSize(attachment.size);
}
private enqueueDiskFileChunk(