mirror of
https://github.com/frappe/books.git
synced 2025-01-05 08:02:15 +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 { emitMainProcessError } from 'backend/helpers';
|
||||||
import {
|
import {
|
||||||
|
shell,
|
||||||
app,
|
app,
|
||||||
BrowserWindow,
|
BrowserWindow,
|
||||||
BrowserWindowConstructorOptions,
|
BrowserWindowConstructorOptions,
|
||||||
@ -22,6 +23,19 @@ import registerIpcMainActionListeners from './main/registerIpcMainActionListener
|
|||||||
import registerIpcMainMessageListeners from './main/registerIpcMainMessageListeners';
|
import registerIpcMainMessageListeners from './main/registerIpcMainMessageListeners';
|
||||||
import registerProcessListeners from './main/registerProcessListeners';
|
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 {
|
export class Main {
|
||||||
title = 'Frappe Books';
|
title = 'Frappe Books';
|
||||||
icon: string;
|
icon: string;
|
||||||
@ -119,6 +133,11 @@ export class Main {
|
|||||||
const options = this.getOptions();
|
const options = this.getOptions();
|
||||||
this.mainWindow = new BrowserWindow(options);
|
this.mainWindow = new BrowserWindow(options);
|
||||||
|
|
||||||
|
this.mainWindow.webContents.setWindowOpenHandler(({ url }) => {
|
||||||
|
shell.openExternal(url);
|
||||||
|
return { action: 'deny' };
|
||||||
|
});
|
||||||
|
|
||||||
if (this.isDevelopment) {
|
if (this.isDevelopment) {
|
||||||
this.setViteServerURL();
|
this.setViteServerURL();
|
||||||
} else {
|
} else {
|
||||||
@ -189,14 +208,7 @@ function bufferProtocolCallback(
|
|||||||
|
|
||||||
fs.readFile(filePath, (_, data) => {
|
fs.readFile(filePath, (_, data) => {
|
||||||
const extension = path.extname(filePath).toLowerCase();
|
const extension = path.extname(filePath).toLowerCase();
|
||||||
const mimeType =
|
const mimeType = EXTENSIONS[extension] ?? '';
|
||||||
{
|
|
||||||
'.js': 'text/javascript',
|
|
||||||
'.css': 'text/css',
|
|
||||||
'.html': 'text/html',
|
|
||||||
'.svg': 'image/svg+xml',
|
|
||||||
'.json': 'application/json',
|
|
||||||
}[extension] ?? '';
|
|
||||||
|
|
||||||
callback({ mimeType, data });
|
callback({ mimeType, data });
|
||||||
});
|
});
|
||||||
|
@ -117,6 +117,10 @@ const ipc = {
|
|||||||
ipcRenderer.send(IPC_MESSAGES.OPEN_EXTERNAL, link);
|
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) {
|
async deleteFile(filePath: string) {
|
||||||
return (await ipcRenderer.invoke(
|
return (await ipcRenderer.invoke(
|
||||||
IPC_ACTIONS.DELETE_FILE,
|
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 { Main } from '../main';
|
||||||
import { IPC_MESSAGES } from '../utils/messages';
|
import { IPC_MESSAGES } from '../utils/messages';
|
||||||
import { emitMainProcessError } from 'backend/helpers';
|
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) {
|
export default function registerIpcMainMessageListeners(main: Main) {
|
||||||
ipcMain.on(IPC_MESSAGES.OPEN_MENU, (event) => {
|
ipcMain.on(IPC_MESSAGES.OPEN_MENU, (event) => {
|
||||||
if (event.sender === null) {
|
if (event.sender === null) {
|
||||||
@ -49,6 +83,25 @@ export default function registerIpcMainMessageListeners(main: Main) {
|
|||||||
shell.openExternal(link).catch((err) => emitMainProcessError(err));
|
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) => {
|
ipcMain.on(IPC_MESSAGES.SHOW_ITEM_IN_FOLDER, (_, filePath: string) => {
|
||||||
return shell.showItemInFolder(filePath);
|
return shell.showItemInFolder(filePath);
|
||||||
});
|
});
|
||||||
|
@ -33,6 +33,14 @@
|
|||||||
/>
|
/>
|
||||||
</button>
|
</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 -->
|
<!-- Download Button -->
|
||||||
<button v-if="value" class="p-0.5 rounded" @click="download">
|
<button v-if="value" class="p-0.5 rounded" @click="download">
|
||||||
<FeatherIcon
|
<FeatherIcon
|
||||||
@ -125,6 +133,18 @@ export default defineComponent({
|
|||||||
a.click();
|
a.click();
|
||||||
document.body.removeChild(a);
|
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) {
|
async selectFile(e: Event) {
|
||||||
const target = e.target as HTMLInputElement;
|
const target = e.target as HTMLInputElement;
|
||||||
const file = target.files?.[0];
|
const file = target.files?.[0];
|
||||||
|
@ -3,6 +3,7 @@ export enum IPC_MESSAGES {
|
|||||||
OPEN_MENU = 'open-menu',
|
OPEN_MENU = 'open-menu',
|
||||||
OPEN_SETTINGS = 'open-settings',
|
OPEN_SETTINGS = 'open-settings',
|
||||||
OPEN_EXTERNAL = 'open-external',
|
OPEN_EXTERNAL = 'open-external',
|
||||||
|
OPEN_DATA_URL = 'open-data-url',
|
||||||
SHOW_ITEM_IN_FOLDER = 'show-item-in-folder',
|
SHOW_ITEM_IN_FOLDER = 'show-item-in-folder',
|
||||||
RELOAD_MAIN_WINDOW = 'reload-main-window',
|
RELOAD_MAIN_WINDOW = 'reload-main-window',
|
||||||
MINIMIZE_MAIN_WINDOW = 'minimize-main-window',
|
MINIMIZE_MAIN_WINDOW = 'minimize-main-window',
|
||||||
|
Loading…
Reference in New Issue
Block a user