mirror of
https://github.com/tiddly-gittly/TidGi-Desktop.git
synced 2025-12-06 02:30:47 -08:00
128 lines
No EOL
5.3 KiB
Markdown
128 lines
No EOL
5.3 KiB
Markdown
# FileProtocol
|
|
|
|
## Click the link
|
|
|
|
Normally, link like
|
|
|
|
```wikitext
|
|
[ext[外部文件|file:///Users/linonetwo/Downloads/(OCRed)奖励的惩罚 ((美)科恩著) (Z-Library).pdf]]
|
|
|
|
[ext[外部文件夹|file:///Users/linonetwo/Downloads/]]
|
|
```
|
|
|
|
Will become external link that will open new window, so this feature is handled in `handleOpenFileExternalLink` in `src/services/view/setupViewFileProtocol.ts`.
|
|
|
|
## Load the file content
|
|
|
|
Image syntax like
|
|
|
|
```wikitext
|
|
[img[file://./files/1644384970572.jpeg]]
|
|
```
|
|
|
|
will ask `view.webContent` to send a request, which will be handled in `handleViewFileContentLoading` in `src/services/view/setupViewFileProtocol.ts`, we use `onBeforeRequest` to catch this request.
|
|
|
|
If the url in the request is absolute, we just let `webContent` load it as normal. We use `callback({ cancel: false });` to hand back control to `webContent`.
|
|
|
|
If it is relative to workspace path, we use `nativeService.formatFileUrlToAbsolutePath(details.url)` to get the absolute path, and pass it to `redirectURL` to ask ``webContent` to load this new absolute path.
|
|
|
|
### Deprecated ways
|
|
|
|
#### `protocol.handle('filefix')`
|
|
|
|
This implementation is buggy, that will crash app when loading pdf.
|
|
|
|
```ts
|
|
const fileTypeThatWillCrash = new Set(['.pdf']);
|
|
async function loadFileContentHandler(request: Request) {
|
|
let { pathname } = new URL(request.url);
|
|
pathname = decodeURIComponent(pathname);
|
|
logger.info(`Loading file content from ${pathname}`, { function: 'handleViewFileContentLoading view.webContents.session.protocol.handle' });
|
|
try {
|
|
// mimeType will be `text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7` so is useless, `contentType` will also be `null`
|
|
// const mimeType = request.headers.get('accept');
|
|
// const contentType = request.headers.get('Content-Type');
|
|
const extname = path.extname(pathname);
|
|
if (fileTypeThatWillCrash.has(extname)) {
|
|
return new Response(undefined, { status: 500, statusText: `${extname} will crash electron, prevented loading.` });
|
|
}
|
|
const response = await net.fetch(pathToFileURL(pathname).toString(), {
|
|
method: request.method,
|
|
headers: request.headers,
|
|
body: request.body,
|
|
bypassCustomProtocolHandlers: true,
|
|
});
|
|
logger.info(`${pathname} loaded`, { function: 'handleViewFileContentLoading view.webContents.session.protocol.handle' });
|
|
return response;
|
|
} catch (error) {
|
|
return new Response(undefined, { status: 404, statusText: (error as Error).message });
|
|
}
|
|
}
|
|
|
|
try {
|
|
/**
|
|
* This function is called for every view, but seems register on two different view will throw error, so we check if it's already registered.
|
|
*/
|
|
if (!view.webContents.session.protocol.isProtocolHandled('filefix')) {
|
|
/**
|
|
* Electron's bug, file protocol is not handle-able, won't get any callback. But things like `filea://` `filefix` works.
|
|
*/
|
|
view.webContents.session.protocol.handle('filefix', loadFileContentHandler);
|
|
}
|
|
/**
|
|
* Alternative `open://` protocol for a backup if `file://` doesn't work for some reason.
|
|
*/
|
|
if (!view.webContents.session.protocol.isProtocolHandled('open')) {
|
|
view.webContents.session.protocol.handle('open', loadFileContentHandler);
|
|
}
|
|
} catch (error) {
|
|
logger.error(`Failed to register protocol: ${(error as Error).message}`, { function: 'handleViewFileContentLoading' });
|
|
}
|
|
```
|
|
|
|
#### `protocol.handle('file')`
|
|
|
|
`protocol.handle('file'`'s handler won't receive anything.
|
|
|
|
```ts
|
|
public async handleFileProtocol(request: GlobalRequest): Promise<GlobalResponse> {
|
|
logger.info('handleFileProtocol() getting url', { url: request.url });
|
|
const { pathname } = new URL(request.url);
|
|
logger.info('handleFileProtocol() handle file:// or open:// This url will open file in-wiki', { pathname });
|
|
let fileExists = fs.existsSync(pathname);
|
|
logger.info(`This file (decodeURI) ${fileExists ? '' : 'not '}exists`, { pathname });
|
|
if (fileExists) {
|
|
return await net.fetch(pathname);
|
|
}
|
|
logger.info(`try find file relative to workspace folder`);
|
|
const workspace = await this.workspaceService.getActiveWorkspace();
|
|
if (workspace === undefined) {
|
|
logger.error(`No active workspace, abort. Try loading pathname as-is.`, { pathname });
|
|
return await net.fetch(pathname);
|
|
}
|
|
const filePathInWorkspaceFolder = path.resolve(workspace.wikiFolderLocation, pathname);
|
|
fileExists = fs.existsSync(filePathInWorkspaceFolder);
|
|
logger.info(`This file ${fileExists ? '' : 'not '}exists in workspace folder.`, { filePathInWorkspaceFolder });
|
|
if (fileExists) {
|
|
return await net.fetch(filePathInWorkspaceFolder);
|
|
}
|
|
logger.info(`try find file relative to TidGi App folder`);
|
|
// on production, __dirname will be in .webpack/main
|
|
const inTidGiAppAbsoluteFilePath = path.join(app.getAppPath(), '.webpack', 'renderer', pathname);
|
|
fileExists = fs.existsSync(inTidGiAppAbsoluteFilePath);
|
|
if (fileExists) {
|
|
return await net.fetch(inTidGiAppAbsoluteFilePath);
|
|
}
|
|
logger.warn(`This url can't be loaded in-wiki. Try loading url as-is.`, { url: request.url });
|
|
return await net.fetch(request.url);
|
|
}
|
|
```
|
|
|
|
if
|
|
|
|
```ts
|
|
await app.whenReady();
|
|
protocol.handle('file', nativeService.handleFileProtocol.bind(nativeService));
|
|
```
|
|
|
|
works. But currently it is not. |