Fix "copy image" context menu
This commit is contained in:
parent
b40dd2dd9c
commit
8f06df9f2a
2 changed files with 161 additions and 122 deletions
|
@ -30,6 +30,7 @@ import { isPathInside } from '../ts/util/isPathInside';
|
|||
import { missingCaseError } from '../ts/util/missingCaseError';
|
||||
import { safeParseInteger } from '../ts/util/numbers';
|
||||
import { drop } from '../ts/util/drop';
|
||||
import { strictAssert } from '../ts/util/assert';
|
||||
import { decryptAttachmentV2ToSink } from '../ts/AttachmentCrypto';
|
||||
|
||||
let initialized = false;
|
||||
|
@ -200,6 +201,12 @@ function deleteOrphanedAttachments({
|
|||
void runSafe();
|
||||
}
|
||||
|
||||
let attachmentsDir: string | undefined;
|
||||
let stickersDir: string | undefined;
|
||||
let tempDir: string | undefined;
|
||||
let draftDir: string | undefined;
|
||||
let avatarDataDir: string | undefined;
|
||||
|
||||
export function initialize({
|
||||
configDir,
|
||||
sql,
|
||||
|
@ -212,16 +219,28 @@ export function initialize({
|
|||
}
|
||||
initialized = true;
|
||||
|
||||
const attachmentsDir = getPath(configDir);
|
||||
const stickersDir = getStickersPath(configDir);
|
||||
const tempDir = getTempPath(configDir);
|
||||
const draftDir = getDraftPath(configDir);
|
||||
const avatarDataDir = getAvatarsPath(configDir);
|
||||
attachmentsDir = getPath(configDir);
|
||||
stickersDir = getStickersPath(configDir);
|
||||
tempDir = getTempPath(configDir);
|
||||
draftDir = getDraftPath(configDir);
|
||||
avatarDataDir = getAvatarsPath(configDir);
|
||||
|
||||
ipcMain.handle(ERASE_TEMP_KEY, () => rimraf.sync(tempDir));
|
||||
ipcMain.handle(ERASE_ATTACHMENTS_KEY, () => rimraf.sync(attachmentsDir));
|
||||
ipcMain.handle(ERASE_STICKERS_KEY, () => rimraf.sync(stickersDir));
|
||||
ipcMain.handle(ERASE_DRAFTS_KEY, () => rimraf.sync(draftDir));
|
||||
ipcMain.handle(ERASE_TEMP_KEY, () => {
|
||||
strictAssert(tempDir != null, 'not initialized');
|
||||
rimraf.sync(tempDir);
|
||||
});
|
||||
ipcMain.handle(ERASE_ATTACHMENTS_KEY, () => {
|
||||
strictAssert(attachmentsDir != null, 'not initialized');
|
||||
rimraf.sync(attachmentsDir);
|
||||
});
|
||||
ipcMain.handle(ERASE_STICKERS_KEY, () => {
|
||||
strictAssert(stickersDir != null, 'not initialized');
|
||||
rimraf.sync(stickersDir);
|
||||
});
|
||||
ipcMain.handle(ERASE_DRAFTS_KEY, () => {
|
||||
strictAssert(draftDir != null, 'not initialized');
|
||||
rimraf.sync(draftDir);
|
||||
});
|
||||
|
||||
ipcMain.handle(CLEANUP_ORPHANED_ATTACHMENTS_KEY, async () => {
|
||||
const start = Date.now();
|
||||
|
@ -230,7 +249,10 @@ export function initialize({
|
|||
console.log(`cleanupOrphanedAttachments: took ${duration}ms`);
|
||||
});
|
||||
|
||||
protocol.handle('attachment', async req => {
|
||||
protocol.handle('attachment', handleAttachmentRequest);
|
||||
}
|
||||
|
||||
export async function handleAttachmentRequest(req: Request): Promise<Response> {
|
||||
const url = new URL(req.url);
|
||||
if (url.host !== 'v1' && url.host !== 'v2') {
|
||||
return new Response('Unknown host', { status: 404 });
|
||||
|
@ -243,6 +265,12 @@ export function initialize({
|
|||
disposition = dispositionSchema.parse(dispositionParam);
|
||||
}
|
||||
|
||||
strictAssert(attachmentsDir != null, 'not initialized');
|
||||
strictAssert(tempDir != null, 'not initialized');
|
||||
strictAssert(draftDir != null, 'not initialized');
|
||||
strictAssert(stickersDir != null, 'not initialized');
|
||||
strictAssert(avatarDataDir != null, 'not initialized');
|
||||
|
||||
let parentDir: string;
|
||||
switch (disposition) {
|
||||
case 'attachment':
|
||||
|
@ -335,7 +363,6 @@ export function initialize({
|
|||
size: maybeSize,
|
||||
plaintext,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
type HandleRangeRequestOptionsType = Readonly<{
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
|
||||
import type { BrowserWindow } from 'electron';
|
||||
import { Menu, clipboard, nativeImage } from 'electron';
|
||||
import { fileURLToPath } from 'url';
|
||||
import * as LocaleMatcher from '@formatjs/intl-localematcher';
|
||||
|
||||
import { maybeParseUrl } from '../ts/util/url';
|
||||
|
@ -12,6 +11,7 @@ import type { MenuListType } from '../ts/types/menu';
|
|||
import type { LocalizerType } from '../ts/types/Util';
|
||||
import { strictAssert } from '../ts/util/assert';
|
||||
import type { LoggerType } from '../ts/types/Logging';
|
||||
import { handleAttachmentRequest } from './attachment_channel';
|
||||
|
||||
export const FAKE_DEFAULT_LOCALE = 'en-x-ignore'; // -x- is an extension space for attaching other metadata to the locale
|
||||
|
||||
|
@ -151,23 +151,35 @@ export const setup = (
|
|||
};
|
||||
label = i18n('icu:contextMenuCopyLink');
|
||||
} else if (isImage) {
|
||||
click = async () => {
|
||||
const parsedSrcUrl = maybeParseUrl(params.srcURL);
|
||||
if (!parsedSrcUrl || parsedSrcUrl.protocol !== 'attachment:') {
|
||||
return;
|
||||
}
|
||||
|
||||
const urlIsViewOnce =
|
||||
params.srcURL?.includes('/temp/') ||
|
||||
params.srcURL?.includes('\\temp\\');
|
||||
parsedSrcUrl.searchParams.get('disposition') === 'temporary';
|
||||
if (urlIsViewOnce) {
|
||||
return;
|
||||
}
|
||||
|
||||
click = () => {
|
||||
const parsedSrcUrl = maybeParseUrl(params.srcURL);
|
||||
if (!parsedSrcUrl || parsedSrcUrl.protocol !== 'file:') {
|
||||
const req = new Request(parsedSrcUrl, {
|
||||
method: 'GET',
|
||||
});
|
||||
|
||||
try {
|
||||
const res = await handleAttachmentRequest(req);
|
||||
if (!res.ok) {
|
||||
return;
|
||||
}
|
||||
|
||||
const image = nativeImage.createFromPath(
|
||||
fileURLToPath(params.srcURL)
|
||||
const image = nativeImage.createFromBuffer(
|
||||
Buffer.from(await res.arrayBuffer())
|
||||
);
|
||||
clipboard.writeImage(image);
|
||||
} catch (error) {
|
||||
logger.error('Failed to load image', error);
|
||||
}
|
||||
};
|
||||
label = i18n('icu:contextMenuCopyImage');
|
||||
} else {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue