Skip to content

Commit 9085284

Browse files
committed
Fix: File can be exported now
1 parent 3f63e36 commit 9085284

File tree

4 files changed

+115
-16
lines changed

4 files changed

+115
-16
lines changed

app/main.ts

Lines changed: 57 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/* eslint-disable @typescript-eslint/no-unused-vars */
2-
import {app, BrowserWindow, ipcMain, shell} from 'electron';
2+
import {app, BrowserWindow, ipcMain, shell, dialog} from 'electron';
33
import * as path from 'path';
44
import * as fs from 'fs';
55
import * as os from 'os';
@@ -71,6 +71,62 @@ function createWindow(): BrowserWindow {
7171
}
7272
});
7373
});
74+
75+
// Save file handler
76+
ipcMain.handle('save-file', async (event, args) => {
77+
try {
78+
if (!win) return { success: false, error: 'Window not available' };
79+
80+
const { fileBuffer, fileName, fileType } = args;
81+
82+
// Show save dialog
83+
const result = await dialog.showSaveDialog(win, {
84+
title: 'Save File',
85+
defaultPath: fileName,
86+
filters: [
87+
{ name: 'All Files', extensions: ['*'] }
88+
]
89+
});
90+
91+
if (result.canceled || !result.filePath) {
92+
return { success: false, canceled: true };
93+
}
94+
95+
// Convert base64 to buffer if needed
96+
let buffer;
97+
if (typeof fileBuffer === 'string') {
98+
buffer = Buffer.from(fileBuffer, 'base64');
99+
} else {
100+
buffer = Buffer.from(fileBuffer);
101+
}
102+
103+
// Write file to disk
104+
fs.writeFileSync(result.filePath, buffer);
105+
106+
return { success: true, filePath: result.filePath };
107+
} catch (error) {
108+
console.error('Error saving file:', error);
109+
return { success: false, error: String(error) };
110+
}
111+
});
112+
113+
// Open file handler
114+
ipcMain.handle('open-file', async (event, filePath) => {
115+
try {
116+
if (!filePath) return { success: false, error: 'No file path provided' };
117+
118+
const result = await shell.openPath(filePath);
119+
120+
if (result) {
121+
return { success: false, error: result };
122+
}
123+
124+
return { success: true };
125+
} catch (error) {
126+
console.error('Error opening file:', error);
127+
return { success: false, error: String(error) };
128+
}
129+
});
74130

75131
// Window control handlers
76132

app/preload.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,4 +26,7 @@ contextBridge.exposeInMainWorld('electron', {
2626
// File and URL operations
2727
openUrl: (url: string) => ipcRenderer.send('open-url', url),
2828
openFolderByFile: (path: string) => ipcRenderer.send('open-folder-by-file', path),
29+
saveFile: (fileBuffer: ArrayBuffer | string, fileName: string, fileType: string) =>
30+
ipcRenderer.invoke('save-file', { fileBuffer, fileName, fileType }),
31+
openFile: (filePath: string) => ipcRenderer.invoke('open-file', filePath),
2932
});

src/components/pages/FileManagementPage.tsx

Lines changed: 45 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -257,16 +257,24 @@ export const FileManagementPage = () => {
257257
// Export file
258258
const handleExportFile = async (file: FileData) => {
259259
try {
260-
const blob = new Blob([file.data], { type: file.type || 'application/octet-stream' });
261-
const url = URL.createObjectURL(blob);
260+
if (!window.electron || !window.electron.saveFile) {
261+
console.error("Electron saveFile API not available");
262+
return;
263+
}
264+
265+
const result = await window.electron.saveFile(
266+
file.data,
267+
file.name,
268+
file.type || 'application/octet-stream'
269+
);
262270

263-
const a = document.createElement('a');
264-
a.href = url;
265-
a.download = file.name;
266-
document.body.appendChild(a);
267-
a.click();
268-
document.body.removeChild(a);
269-
URL.revokeObjectURL(url);
271+
if (!result.success) {
272+
if (result.canceled) {
273+
// User canceled the save dialog, no need to show error
274+
return;
275+
}
276+
console.error("Error saving file:", result.error);
277+
}
270278
} catch (error) {
271279
console.error("Error exporting file:", error);
272280
}
@@ -275,10 +283,32 @@ export const FileManagementPage = () => {
275283
// Open file
276284
const handleOpenFile = async (file: FileData) => {
277285
try {
278-
// Use electron API to open the file
279-
const blob = new Blob([file.data], { type: file.type || 'application/octet-stream' });
280-
const url = URL.createObjectURL(blob);
281-
window.electron.openUrl(url);
286+
// First save the file to a temporary location
287+
if (!window.electron || !window.electron.saveFile || !window.electron.openFile) {
288+
console.error("Electron API not available");
289+
return;
290+
}
291+
292+
// Save file to temp location and then open it
293+
const saveResult = await window.electron.saveFile(
294+
file.data,
295+
file.name,
296+
file.type || 'application/octet-stream'
297+
);
298+
299+
if (!saveResult.success || !saveResult.filePath) {
300+
if (!saveResult.canceled) {
301+
console.error("Error saving file:", saveResult.error);
302+
}
303+
return;
304+
}
305+
306+
// Now open the file with the default application
307+
const openResult = await window.electron.openFile(saveResult.filePath);
308+
309+
if (!openResult.success) {
310+
console.error("Error opening file:", openResult.error);
311+
}
282312
} catch (error) {
283313
console.error("Error opening file:", error);
284314
}
@@ -642,13 +672,13 @@ export const FileManagementPage = () => {
642672
</td>
643673
<td className="px-4 py-3">
644674
<div className="flex items-center justify-center space-x-2">
645-
<button
675+
{/* <button
646676
onClick={() => handleOpenFile(file)}
647677
className="p-2 rounded-md message-icon-btn"
648678
title={t("fileManagement.open")}
649679
>
650680
<FolderOpen size={16} />
651-
</button>
681+
</button> */}
652682
<button
653683
onClick={() => {
654684
setSelectedFile(file);

src/types/window.d.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,5 +14,15 @@ interface Window {
1414
}>;
1515
openUrl: (url: string) => Promise<void>;
1616
onWindowMaximizedChange: (callback: (event: IpcRendererEvent, maximized: boolean) => void) => void;
17+
saveFile: (fileBuffer: ArrayBuffer | string, fileName: string, fileType: string) => Promise<{
18+
success: boolean;
19+
filePath?: string;
20+
canceled?: boolean;
21+
error?: string;
22+
}>;
23+
openFile: (filePath: string) => Promise<{
24+
success: boolean;
25+
error?: string;
26+
}>;
1727
};
1828
}

0 commit comments

Comments
 (0)