Revert the automated member-ordering pass that broke Angular field init (TS2729) and disable that rule until a safe reorder strategy exists. Fix modal/confirm dialog i18n defaults via template fallbacks, search all active endpoints (including offline), register foreign rooms with actor owner IDs, sync profile display names from avatar summaries, and guard dm-chat when a private call converts to a group conversation. Co-authored-by: Cursor <cursoragent@cursor.com>
173 lines
4.1 KiB
TypeScript
173 lines
4.1 KiB
TypeScript
import { Injectable, inject } from '@angular/core';
|
|
import { ElectronBridgeService } from '../../../../core/platform/electron/electron-bridge.service';
|
|
import type { AttachmentFileStore } from './attachment-file-store';
|
|
|
|
/** Attachment file store backed by the Electron main-process filesystem (IPC). */
|
|
@Injectable({ providedIn: 'root' })
|
|
export class ElectronAttachmentFileStore implements AttachmentFileStore {
|
|
readonly maxPersistableBytes = Number.POSITIVE_INFINITY;
|
|
readonly supportsStreamingToDisk = true;
|
|
readonly supportsChunkedReads = true;
|
|
readonly providesInlineObjectUrl = false;
|
|
|
|
private readonly electronBridge = inject(ElectronBridgeService);
|
|
|
|
get isAvailable(): boolean {
|
|
const electronApi = this.electronBridge.getApi();
|
|
|
|
return !!electronApi?.appendFile && !!electronApi.writeFile && !!electronApi.getAppDataPath;
|
|
}
|
|
|
|
async getAppDataPath(): Promise<string | null> {
|
|
const electronApi = this.electronBridge.getApi();
|
|
|
|
if (!electronApi) {
|
|
return null;
|
|
}
|
|
|
|
try {
|
|
return await electronApi.getAppDataPath();
|
|
} catch {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
async ensureDir(dirPath: string): Promise<boolean> {
|
|
const electronApi = this.electronBridge.getApi();
|
|
|
|
if (!electronApi?.ensureDir || !dirPath) {
|
|
return false;
|
|
}
|
|
|
|
try {
|
|
return await electronApi.ensureDir(dirPath);
|
|
} catch {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
async writeFile(filePath: string, base64Data: string): Promise<boolean> {
|
|
const electronApi = this.electronBridge.getApi();
|
|
|
|
if (!electronApi?.writeFile || !filePath) {
|
|
return false;
|
|
}
|
|
|
|
try {
|
|
return await electronApi.writeFile(filePath, base64Data);
|
|
} catch {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
async appendFile(filePath: string, base64Data: string): Promise<boolean> {
|
|
const electronApi = this.electronBridge.getApi();
|
|
|
|
if (!electronApi?.appendFile || !filePath) {
|
|
return false;
|
|
}
|
|
|
|
try {
|
|
return await electronApi.appendFile(filePath, base64Data);
|
|
} catch {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
async readFile(filePath: string): Promise<string | null> {
|
|
const electronApi = this.electronBridge.getApi();
|
|
|
|
if (!electronApi || !filePath) {
|
|
return null;
|
|
}
|
|
|
|
try {
|
|
return await electronApi.readFile(filePath);
|
|
} catch {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
async readFileChunk(filePath: string, start: number, end: number): Promise<string | null> {
|
|
const electronApi = this.electronBridge.getApi();
|
|
|
|
if (!electronApi?.readFileChunk || !filePath) {
|
|
return null;
|
|
}
|
|
|
|
try {
|
|
return await electronApi.readFileChunk(filePath, start, end);
|
|
} catch {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
async getFileSize(filePath: string): Promise<number | null> {
|
|
const electronApi = this.electronBridge.getApi();
|
|
|
|
if (!electronApi?.getFileSize || !filePath) {
|
|
return null;
|
|
}
|
|
|
|
try {
|
|
return await electronApi.getFileSize(filePath);
|
|
} catch {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
async fileExists(filePath: string): Promise<boolean> {
|
|
const electronApi = this.electronBridge.getApi();
|
|
|
|
if (!electronApi?.fileExists || !filePath) {
|
|
return false;
|
|
}
|
|
|
|
try {
|
|
return await electronApi.fileExists(filePath);
|
|
} catch {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
async copyFile(sourceFilePath: string, destinationFilePath: string): Promise<boolean> {
|
|
const electronApi = this.electronBridge.getApi();
|
|
|
|
if (!electronApi?.copyFile || !sourceFilePath || !destinationFilePath) {
|
|
return false;
|
|
}
|
|
|
|
try {
|
|
return await electronApi.copyFile(sourceFilePath, destinationFilePath);
|
|
} catch {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
async deleteFile(filePath: string): Promise<void> {
|
|
const electronApi = this.electronBridge.getApi();
|
|
|
|
if (!electronApi || !filePath) {
|
|
return;
|
|
}
|
|
|
|
try {
|
|
await electronApi.deleteFile(filePath);
|
|
} catch { /* best-effort cleanup */ }
|
|
}
|
|
|
|
async getFileUrl(filePath: string): Promise<string | null> {
|
|
const electronApi = this.electronBridge.getApi();
|
|
|
|
if (!electronApi?.getFileUrl || !filePath) {
|
|
return null;
|
|
}
|
|
|
|
try {
|
|
return await electronApi.getFileUrl(filePath);
|
|
} catch {
|
|
return null;
|
|
}
|
|
}
|
|
}
|