mirror of
https://github.com/frappe/books.git
synced 2025-01-03 07:12:21 +00:00
ux: allow to view attached files directly
This commit is contained in:
parent
a2b80a3414
commit
8de91f74fe
28
main.ts
28
main.ts
@ -6,6 +6,7 @@ require('source-map-support').install({
|
||||
|
||||
import { emitMainProcessError } from 'backend/helpers';
|
||||
import {
|
||||
shell,
|
||||
app,
|
||||
BrowserWindow,
|
||||
BrowserWindowConstructorOptions,
|
||||
@ -22,6 +23,19 @@ import registerIpcMainActionListeners from './main/registerIpcMainActionListener
|
||||
import registerIpcMainMessageListeners from './main/registerIpcMainMessageListeners';
|
||||
import registerProcessListeners from './main/registerProcessListeners';
|
||||
|
||||
const EXTENSIONS = {
|
||||
'.js': 'text/javascript',
|
||||
'.css': 'text/css',
|
||||
'.html': 'text/html',
|
||||
'.svg': 'image/svg+xml',
|
||||
'.json': 'application/json',
|
||||
'.pdf': 'application/pdf',
|
||||
};
|
||||
|
||||
const MIME_TYPES = Object.fromEntries(
|
||||
Object.entries(EXTENSIONS).map(([ext, mime]) => [mime, ext])
|
||||
);
|
||||
|
||||
export class Main {
|
||||
title = 'Frappe Books';
|
||||
icon: string;
|
||||
@ -119,6 +133,11 @@ export class Main {
|
||||
const options = this.getOptions();
|
||||
this.mainWindow = new BrowserWindow(options);
|
||||
|
||||
this.mainWindow.webContents.setWindowOpenHandler(({ url }) => {
|
||||
shell.openExternal(url);
|
||||
return { action: 'deny' };
|
||||
});
|
||||
|
||||
if (this.isDevelopment) {
|
||||
this.setViteServerURL();
|
||||
} else {
|
||||
@ -189,14 +208,7 @@ function bufferProtocolCallback(
|
||||
|
||||
fs.readFile(filePath, (_, data) => {
|
||||
const extension = path.extname(filePath).toLowerCase();
|
||||
const mimeType =
|
||||
{
|
||||
'.js': 'text/javascript',
|
||||
'.css': 'text/css',
|
||||
'.html': 'text/html',
|
||||
'.svg': 'image/svg+xml',
|
||||
'.json': 'application/json',
|
||||
}[extension] ?? '';
|
||||
const mimeType = EXTENSIONS[extension] ?? '';
|
||||
|
||||
callback({ mimeType, data });
|
||||
});
|
||||
|
@ -117,6 +117,10 @@ const ipc = {
|
||||
ipcRenderer.send(IPC_MESSAGES.OPEN_EXTERNAL, link);
|
||||
},
|
||||
|
||||
openDataURL(link: string, filename: string) {
|
||||
ipcRenderer.send(IPC_MESSAGES.OPEN_DATA_URL, { link, filename });
|
||||
},
|
||||
|
||||
async deleteFile(filePath: string) {
|
||||
return (await ipcRenderer.invoke(
|
||||
IPC_ACTIONS.DELETE_FILE,
|
||||
|
@ -1,8 +1,42 @@
|
||||
import { ipcMain, Menu, shell } from 'electron';
|
||||
import { ipcMain, Menu, shell, app } from 'electron';
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import { Main } from '../main';
|
||||
import { IPC_MESSAGES } from '../utils/messages';
|
||||
import { emitMainProcessError } from 'backend/helpers';
|
||||
|
||||
function parseDataURL(url) {
|
||||
const regex =
|
||||
/^data:([a-z]+\/[a-z0-9-+.]+(;[a-z0-9-.!#$%*+.{}|~`]+=[a-z0-9-.!#$%*+.{}()_|~`]+)*)?(;base64)?,([a-z0-9!$&',()*+;=\-._~:@\/?%\s<>]*?)$/i;
|
||||
|
||||
const parts = url.trim().match(regex);
|
||||
if (!parts) return null;
|
||||
|
||||
const parsed = {};
|
||||
|
||||
parsed.mediaType = (parts[1] || 'text/plain;charset=us-ascii').toLowerCase();
|
||||
|
||||
const mediaTypeParts = parsed.mediaType
|
||||
.split(';')
|
||||
.map((x) => x.toLowerCase());
|
||||
parsed.contentType = mediaTypeParts[0];
|
||||
|
||||
mediaTypeParts.slice(1).forEach((attribute) => {
|
||||
const p = attribute.split('=');
|
||||
parsed[p[0]] = p[1];
|
||||
});
|
||||
|
||||
parsed.base64 = !!parts[parts.length - 2];
|
||||
parsed.data = parts[parts.length - 1] || '';
|
||||
parsed.encoding = parsed.base64 ? 'base64' : 'utf8';
|
||||
parsed.buffer = Buffer.from(
|
||||
parsed.base64 ? parsed.data : decodeURIComponent(parsed.data),
|
||||
parsed.encoding
|
||||
);
|
||||
|
||||
return parsed;
|
||||
}
|
||||
|
||||
export default function registerIpcMainMessageListeners(main: Main) {
|
||||
ipcMain.on(IPC_MESSAGES.OPEN_MENU, (event) => {
|
||||
if (event.sender === null) {
|
||||
@ -49,6 +83,25 @@ export default function registerIpcMainMessageListeners(main: Main) {
|
||||
shell.openExternal(link).catch((err) => emitMainProcessError(err));
|
||||
});
|
||||
|
||||
ipcMain.on(
|
||||
IPC_MESSAGES.OPEN_DATA_URL,
|
||||
(_, { link, filename }: { link: string; filename: string }) => {
|
||||
const data = parseDataURL(link);
|
||||
if (data) {
|
||||
const s =
|
||||
'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
|
||||
const temp = Array.apply(null, Array(16))
|
||||
.map(() => {
|
||||
return s.charAt(Math.floor(Math.random() * s.length));
|
||||
})
|
||||
.join('');
|
||||
const filepath = path.join(app.getPath('temp'), temp + ' ' + filename);
|
||||
fs.writeFileSync(filepath, data.buffer);
|
||||
shell.openPath(filepath);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
ipcMain.on(IPC_MESSAGES.SHOW_ITEM_IN_FOLDER, (_, filePath: string) => {
|
||||
return shell.showItemInFolder(filePath);
|
||||
});
|
||||
|
@ -33,6 +33,14 @@
|
||||
/>
|
||||
</button>
|
||||
|
||||
<!-- Open Button -->
|
||||
<button v-if="value" class="p-0.5 rounded" @click="open">
|
||||
<FeatherIcon
|
||||
name="eye"
|
||||
class="h-4 w-4 text-gray-600 dark:text-gray-400"
|
||||
/>
|
||||
</button>
|
||||
|
||||
<!-- Download Button -->
|
||||
<button v-if="value" class="p-0.5 rounded" @click="download">
|
||||
<FeatherIcon
|
||||
@ -125,6 +133,18 @@ export default defineComponent({
|
||||
a.click();
|
||||
document.body.removeChild(a);
|
||||
},
|
||||
open() {
|
||||
if (!this.value) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { name, data } = this.value;
|
||||
if (!name || !data) {
|
||||
return;
|
||||
}
|
||||
|
||||
ipc.openDataURL(data, name);
|
||||
},
|
||||
async selectFile(e: Event) {
|
||||
const target = e.target as HTMLInputElement;
|
||||
const file = target.files?.[0];
|
||||
|
@ -3,6 +3,7 @@ export enum IPC_MESSAGES {
|
||||
OPEN_MENU = 'open-menu',
|
||||
OPEN_SETTINGS = 'open-settings',
|
||||
OPEN_EXTERNAL = 'open-external',
|
||||
OPEN_DATA_URL = 'open-data-url',
|
||||
SHOW_ITEM_IN_FOLDER = 'show-item-in-folder',
|
||||
RELOAD_MAIN_WINDOW = 'reload-main-window',
|
||||
MINIMIZE_MAIN_WINDOW = 'minimize-main-window',
|
||||
|
Loading…
Reference in New Issue
Block a user