mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-08-18 02:21:51 +08:00
0d889bc643
#### PR Dependency Tree * **PR #15334** 👈 This tree was auto-generated by [Charcoal](https://github.com/danerwilliams/charcoal) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Enhancements** * Improved main-window restoration for deep links, second-instance launches, tray/menu actions, and when recordings finish. * Refined macOS Dock show/hide behavior with throttling for smoother window visibility. * Updated close-to-tray/close-to-background handling to better manage the app’s window lifecycle. * Ensured popup/dock visibility is consistent when opening new windows. * Updated window behavior settings display so tray-related options render correctly across platforms. * **Localization** * Updated Simplified Chinese wording for menubar window behavior title. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
156 lines
4.2 KiB
TypeScript
156 lines
4.2 KiB
TypeScript
import path from 'node:path';
|
|
|
|
import type { App } from 'electron';
|
|
|
|
import { buildType, isDev } from './config';
|
|
import { logger } from './logger';
|
|
import { uiSubjects } from './ui';
|
|
import {
|
|
addTabWithUrl,
|
|
loadUrlInActiveTab,
|
|
openUrlInHiddenWindow,
|
|
showMainWindow,
|
|
} from './windows-manager';
|
|
|
|
let protocol = buildType === 'stable' ? 'affine' : `affine-${buildType}`;
|
|
if (isDev) {
|
|
protocol = 'affine-dev';
|
|
}
|
|
|
|
const authMethods = new Set(['magic-link', 'oauth', 'open-app-signin']);
|
|
|
|
function summarizeDeepLink(rawUrl: string) {
|
|
try {
|
|
const url = new URL(rawUrl);
|
|
const method = url.searchParams.get('method');
|
|
const server = url.searchParams.get('server');
|
|
let serverOrigin: string | undefined;
|
|
try {
|
|
serverOrigin = server ? new URL(server).origin : undefined;
|
|
} catch {
|
|
serverOrigin = undefined;
|
|
}
|
|
return {
|
|
protocol: url.protocol,
|
|
action: url.hostname,
|
|
method: method && authMethods.has(method) ? method : undefined,
|
|
serverOrigin,
|
|
};
|
|
} catch {
|
|
return { valid: false };
|
|
}
|
|
}
|
|
|
|
function logDeepLinkFailure(rawUrl: string, error: unknown) {
|
|
logger.error('failed to handle affine url', summarizeDeepLink(rawUrl), {
|
|
error: error instanceof Error ? error.name : typeof error,
|
|
});
|
|
}
|
|
|
|
export function setupDeepLink(app: App) {
|
|
if (process.defaultApp) {
|
|
if (process.argv.length >= 2) {
|
|
app.setAsDefaultProtocolClient(protocol, process.execPath, [
|
|
path.resolve(process.argv[1]),
|
|
]);
|
|
}
|
|
} else {
|
|
app.setAsDefaultProtocolClient(protocol);
|
|
}
|
|
|
|
app.on('open-url', (event, url) => {
|
|
logger.log('open-url', summarizeDeepLink(url));
|
|
if (url.startsWith(`${protocol}://`)) {
|
|
event.preventDefault();
|
|
app
|
|
.whenReady()
|
|
.then(() => handleAffineUrl(url))
|
|
.catch(e => {
|
|
logDeepLinkFailure(url, e);
|
|
});
|
|
}
|
|
});
|
|
|
|
// on windows & linux, we need to listen for the second-instance event
|
|
app.on('second-instance', (event, commandLine) => {
|
|
showMainWindow()
|
|
.then(() => {
|
|
const url = commandLine.pop();
|
|
if (url?.startsWith(`${protocol}://`)) {
|
|
event.preventDefault();
|
|
handleAffineUrl(url).catch(e => {
|
|
logDeepLinkFailure(url, e);
|
|
});
|
|
}
|
|
})
|
|
.catch(e => console.error('Failed to restore or create window:', e));
|
|
});
|
|
|
|
app.on('ready', () => {
|
|
// app may be brought up without having a running instance
|
|
// need to read the url from the command line
|
|
const url = process.argv.at(-1);
|
|
logger.log(
|
|
'url from argv',
|
|
url?.startsWith(`${protocol}://`)
|
|
? summarizeDeepLink(url)
|
|
: { deepLink: false, argumentCount: process.argv.length }
|
|
);
|
|
if (url?.startsWith(`${protocol}://`)) {
|
|
handleAffineUrl(url).catch(e => {
|
|
logDeepLinkFailure(url, e);
|
|
});
|
|
}
|
|
});
|
|
}
|
|
|
|
async function handleAffineUrl(url: string) {
|
|
await showMainWindow();
|
|
|
|
logger.info('open affine url', summarizeDeepLink(url));
|
|
const urlObj = new URL(url);
|
|
|
|
if (urlObj.hostname === 'authentication') {
|
|
const method = urlObj.searchParams.get('method');
|
|
const payload = JSON.parse(urlObj.searchParams.get('payload') ?? 'false');
|
|
const server = urlObj.searchParams.get('server') || undefined;
|
|
|
|
if (
|
|
!method ||
|
|
(method !== 'magic-link' &&
|
|
method !== 'oauth' &&
|
|
method !== 'open-app-signin') ||
|
|
!payload
|
|
) {
|
|
logger.error('Invalid authentication url', summarizeDeepLink(url));
|
|
return;
|
|
}
|
|
|
|
uiSubjects.authenticationRequest$.next({
|
|
method,
|
|
payload,
|
|
server,
|
|
});
|
|
} else if (
|
|
urlObj.searchParams.get('new-tab') &&
|
|
urlObj.pathname.startsWith('/workspace')
|
|
) {
|
|
// @todo(@forehalo): refactor router utilities
|
|
// basename of /workspace/xxx/yyy is /workspace/xxx
|
|
await addTabWithUrl(url);
|
|
} else {
|
|
const hiddenWindow = urlObj.searchParams.get('hidden')
|
|
? await openUrlInHiddenWindow(urlObj)
|
|
: await loadUrlInActiveTab(url);
|
|
|
|
if (hiddenWindow) {
|
|
// when hidden window closed, the main window will be hidden somehow
|
|
hiddenWindow.on('close', () => {
|
|
void showMainWindow().catch(e => {
|
|
logger.error('Failed to restore main window:', e);
|
|
});
|
|
});
|
|
}
|
|
}
|
|
}
|