Skip to content

Commit

Permalink
ux: allow to view attached files directly
Browse files Browse the repository at this point in the history
  • Loading branch information
mildred committed Nov 21, 2024
1 parent a2b80a3 commit cb38867
Show file tree
Hide file tree
Showing 5 changed files with 99 additions and 9 deletions.
28 changes: 20 additions & 8 deletions main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ require('source-map-support').install({

import { emitMainProcessError } from 'backend/helpers';
import {
shell,
app,
BrowserWindow,
BrowserWindowConstructorOptions,
Expand All @@ -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(

Check warning on line 35 in main.ts

View workflow job for this annotation

GitHub Actions / setup_and_lint

'MIME_TYPES' is assigned a value but never used
Object.entries(EXTENSIONS).map(([ext, mime]) => [mime, ext])
);

export class Main {
title = 'Frappe Books';
icon: string;
Expand Down Expand Up @@ -119,6 +133,11 @@ export class Main {
const options = this.getOptions();
this.mainWindow = new BrowserWindow(options);

this.mainWindow.webContents.setWindowOpenHandler(({ url }) => {
shell.openExternal(url);

Check warning on line 137 in main.ts

View workflow job for this annotation

GitHub Actions / setup_and_lint

Promises must be awaited, end with a call to .catch, end with a call to .then with a rejection handler or be explicitly marked as ignored with the `void` operator
return { action: 'deny' };
});

if (this.isDevelopment) {
this.setViteServerURL();
} else {
Expand Down Expand Up @@ -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] ?? '';

Check failure on line 211 in main.ts

View workflow job for this annotation

GitHub Actions / setup_and_lint

Unsafe assignment of an `any` value

callback({ mimeType, data });

Check failure on line 213 in main.ts

View workflow job for this annotation

GitHub Actions / setup_and_lint

Unsafe assignment of an `any` value
});
Expand Down
4 changes: 4 additions & 0 deletions main/preload.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
55 changes: 54 additions & 1 deletion main/registerIpcMainMessageListeners.ts
Original file line number Diff line number Diff line change
@@ -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);

Check failure on line 12 in main/registerIpcMainMessageListeners.ts

View workflow job for this annotation

GitHub Actions / setup_and_lint

Unsafe assignment of an `any` value

Check failure on line 12 in main/registerIpcMainMessageListeners.ts

View workflow job for this annotation

GitHub Actions / setup_and_lint

Unsafe member access .match on an `any` value

Check failure on line 12 in main/registerIpcMainMessageListeners.ts

View workflow job for this annotation

GitHub Actions / setup_and_lint

Unsafe call of an `any` typed value

Check failure on line 12 in main/registerIpcMainMessageListeners.ts

View workflow job for this annotation

GitHub Actions / setup_and_lint

Unsafe member access .trim on an `any` value

Check failure on line 12 in main/registerIpcMainMessageListeners.ts

View workflow job for this annotation

GitHub Actions / setup_and_lint

Unsafe call of an `any` typed value
if (!parts) return null;

const parsed = {};

parsed.mediaType = (parts[1] || 'text/plain;charset=us-ascii').toLowerCase();

Check failure on line 17 in main/registerIpcMainMessageListeners.ts

View workflow job for this annotation

GitHub Actions / setup_and_lint

Unsafe assignment of an `any` value

Check failure on line 17 in main/registerIpcMainMessageListeners.ts

View workflow job for this annotation

GitHub Actions / setup_and_lint

Unsafe member access .toLowerCase on an `any` value

Check failure on line 17 in main/registerIpcMainMessageListeners.ts

View workflow job for this annotation

GitHub Actions / setup_and_lint

Unsafe call of an `any` typed value

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) {
Expand Down Expand Up @@ -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);

Check warning on line 100 in main/registerIpcMainMessageListeners.ts

View workflow job for this annotation

GitHub Actions / setup_and_lint

Promises must be awaited, end with a call to .catch, end with a call to .then with a rejection handler or be explicitly marked as ignored with the `void` operator
}
}
);

ipcMain.on(IPC_MESSAGES.SHOW_ITEM_IN_FOLDER, (_, filePath: string) => {
return shell.showItemInFolder(filePath);
});
Expand Down
20 changes: 20 additions & 0 deletions src/components/Controls/Attachment.vue
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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];
Expand Down
1 change: 1 addition & 0 deletions utils/messages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down

0 comments on commit cb38867

Please sign in to comment.