mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-08-23 13:02:21 +08:00
feat(electron): more desktop app related shortcuts (#9724)
fix AF-2126, AF-2124 - Add CMD+M for minimize the app. - Enhance how CMD+W works. Close the following in order, stop if any one is closed: - peek view - split view - tab - otherwise, hide the app
This commit is contained in:
@@ -2,10 +2,10 @@ import { app, Menu } from 'electron';
|
|||||||
|
|
||||||
import { isMacOS } from '../../shared/utils';
|
import { isMacOS } from '../../shared/utils';
|
||||||
import { logger, revealLogFile } from '../logger';
|
import { logger, revealLogFile } from '../logger';
|
||||||
|
import { uiSubjects } from '../ui/subject';
|
||||||
import { checkForUpdates } from '../updater';
|
import { checkForUpdates } from '../updater';
|
||||||
import {
|
import {
|
||||||
addTab,
|
addTab,
|
||||||
closeTab,
|
|
||||||
initAndShowMainWindow,
|
initAndShowMainWindow,
|
||||||
reloadView,
|
reloadView,
|
||||||
showDevTools,
|
showDevTools,
|
||||||
@@ -103,6 +103,9 @@ export function createApplicationMenu() {
|
|||||||
reloadView().catch(console.error);
|
reloadView().catch(console.error);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
role: 'windowMenu',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
label: 'Open devtools',
|
label: 'Open devtools',
|
||||||
accelerator: isMac ? 'Cmd+Option+I' : 'Ctrl+Shift+I',
|
accelerator: isMac ? 'Cmd+Option+I' : 'Ctrl+Shift+I',
|
||||||
@@ -129,11 +132,12 @@ export function createApplicationMenu() {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Close tab',
|
label: 'Close view',
|
||||||
accelerator: 'CommandOrControl+W',
|
accelerator: 'CommandOrControl+W',
|
||||||
click() {
|
click() {
|
||||||
logger.info('Close tab with shortcut');
|
logger.info('Close view with shortcut');
|
||||||
closeTab().catch(console.error);
|
// tell the active workbench to close the current view
|
||||||
|
uiSubjects.onCloseView$.next();
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -195,7 +199,7 @@ export function createApplicationMenu() {
|
|||||||
{
|
{
|
||||||
label: 'Learn More',
|
label: 'Learn More',
|
||||||
click: async () => {
|
click: async () => {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
// oxlint-disable-next-line
|
||||||
const { shell } = require('electron');
|
const { shell } = require('electron');
|
||||||
await shell.openExternal('https://affine.pro/');
|
await shell.openExternal('https://affine.pro/');
|
||||||
},
|
},
|
||||||
@@ -216,7 +220,7 @@ export function createApplicationMenu() {
|
|||||||
{
|
{
|
||||||
label: 'Documentation',
|
label: 'Documentation',
|
||||||
click: async () => {
|
click: async () => {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
// oxlint-disable-next-line
|
||||||
const { shell } = require('electron');
|
const { shell } = require('electron');
|
||||||
await shell.openExternal(
|
await shell.openExternal(
|
||||||
'https://docs.affine.pro/docs/hello-bonjour-aloha-你好'
|
'https://docs.affine.pro/docs/hello-bonjour-aloha-你好'
|
||||||
|
|||||||
@@ -42,4 +42,10 @@ export const uiEvents = {
|
|||||||
sub.unsubscribe();
|
sub.unsubscribe();
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
onCloseView: (fn: () => void) => {
|
||||||
|
const sub = uiSubjects.onCloseView$.subscribe(fn);
|
||||||
|
return () => {
|
||||||
|
sub.unsubscribe();
|
||||||
|
};
|
||||||
|
},
|
||||||
} satisfies Record<string, MainEventRegister>;
|
} satisfies Record<string, MainEventRegister>;
|
||||||
|
|||||||
@@ -71,6 +71,10 @@ export const uiHandlers = {
|
|||||||
handleCloseApp: async () => {
|
handleCloseApp: async () => {
|
||||||
app.quit();
|
app.quit();
|
||||||
},
|
},
|
||||||
|
handleHideApp: async () => {
|
||||||
|
const window = await getMainWindow();
|
||||||
|
window?.hide();
|
||||||
|
},
|
||||||
handleNetworkChange: async (_, _isOnline: boolean) => {
|
handleNetworkChange: async (_, _isOnline: boolean) => {
|
||||||
isOnline = _isOnline;
|
isOnline = _isOnline;
|
||||||
},
|
},
|
||||||
@@ -191,6 +195,7 @@ export const uiHandlers = {
|
|||||||
closeTab: async (_, ...args: Parameters<typeof closeTab>) => {
|
closeTab: async (_, ...args: Parameters<typeof closeTab>) => {
|
||||||
await closeTab(...args);
|
await closeTab(...args);
|
||||||
},
|
},
|
||||||
|
|
||||||
activateView: async (_, ...args: Parameters<typeof activateView>) => {
|
activateView: async (_, ...args: Parameters<typeof activateView>) => {
|
||||||
await activateView(...args);
|
await activateView(...args);
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -7,4 +7,6 @@ export const uiSubjects = {
|
|||||||
onFullScreen$: new Subject<boolean>(),
|
onFullScreen$: new Subject<boolean>(),
|
||||||
onToggleRightSidebar$: new Subject<string>(),
|
onToggleRightSidebar$: new Subject<string>(),
|
||||||
authenticationRequest$: new Subject<AuthenticationRequest>(),
|
authenticationRequest$: new Subject<AuthenticationRequest>(),
|
||||||
|
// via menu -> close view (CMD+W)
|
||||||
|
onCloseView$: new Subject<void>(),
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ export { WorkbenchRoot } from './view/workbench-root';
|
|||||||
import { type Framework } from '@toeverything/infra';
|
import { type Framework } from '@toeverything/infra';
|
||||||
|
|
||||||
import { DesktopApiService } from '../desktop-api';
|
import { DesktopApiService } from '../desktop-api';
|
||||||
|
import { PeekViewService } from '../peek-view';
|
||||||
import { GlobalStateService } from '../storage';
|
import { GlobalStateService } from '../storage';
|
||||||
import { WorkspaceScope } from '../workspace';
|
import { WorkspaceScope } from '../workspace';
|
||||||
import { SidebarTab } from './entities/sidebar-tab';
|
import { SidebarTab } from './entities/sidebar-tab';
|
||||||
@@ -64,5 +65,9 @@ export function configureDesktopWorkbenchModule(services: Framework) {
|
|||||||
.impl(WorkbenchNewTabHandler, DesktopWorkbenchNewTabHandler, [
|
.impl(WorkbenchNewTabHandler, DesktopWorkbenchNewTabHandler, [
|
||||||
DesktopApiService,
|
DesktopApiService,
|
||||||
])
|
])
|
||||||
.service(DesktopStateSynchronizer, [WorkbenchService, DesktopApiService]);
|
.service(DesktopStateSynchronizer, [
|
||||||
|
WorkbenchService,
|
||||||
|
DesktopApiService,
|
||||||
|
PeekViewService,
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
|
|||||||
+27
-1
@@ -1,6 +1,7 @@
|
|||||||
import { LiveData, Service } from '@toeverything/infra';
|
import { LiveData, Service } from '@toeverything/infra';
|
||||||
|
|
||||||
import type { DesktopApiService } from '../../desktop-api';
|
import type { DesktopApiService } from '../../desktop-api';
|
||||||
|
import type { PeekViewService } from '../../peek-view';
|
||||||
import type { WorkbenchService } from '../../workbench';
|
import type { WorkbenchService } from '../../workbench';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -9,7 +10,8 @@ import type { WorkbenchService } from '../../workbench';
|
|||||||
export class DesktopStateSynchronizer extends Service {
|
export class DesktopStateSynchronizer extends Service {
|
||||||
constructor(
|
constructor(
|
||||||
private readonly workbenchService: WorkbenchService,
|
private readonly workbenchService: WorkbenchService,
|
||||||
private readonly electronApi: DesktopApiService
|
private readonly electronApi: DesktopApiService,
|
||||||
|
private readonly peekViewService: PeekViewService
|
||||||
) {
|
) {
|
||||||
super();
|
super();
|
||||||
this.startSync();
|
this.startSync();
|
||||||
@@ -52,6 +54,30 @@ export class DesktopStateSynchronizer extends Service {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.electronApi.events.ui.onCloseView(() => {
|
||||||
|
(async () => {
|
||||||
|
if (await this.electronApi.handler.ui.isActiveTab()) {
|
||||||
|
// close current view. stop if any one is successful
|
||||||
|
// 1. peek view
|
||||||
|
// 2. split view
|
||||||
|
// 3. tab
|
||||||
|
// 4. otherwise, hide the window
|
||||||
|
if (this.peekViewService.peekView.show$.value?.value) {
|
||||||
|
this.peekViewService.peekView.close();
|
||||||
|
} else if (workbench.views$.value.length > 1) {
|
||||||
|
workbench.close(workbench.activeView$.value);
|
||||||
|
} else {
|
||||||
|
const tabs = await this.electronApi.handler.ui.getTabsStatus();
|
||||||
|
if (tabs.length > 1) {
|
||||||
|
await this.electronApi.handler.ui.closeTab();
|
||||||
|
} else {
|
||||||
|
await this.electronApi.handler.ui.handleHideApp();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})().catch(console.error);
|
||||||
|
});
|
||||||
|
|
||||||
this.electronApi.events.ui.onToggleRightSidebar(tabId => {
|
this.electronApi.events.ui.onToggleRightSidebar(tabId => {
|
||||||
if (tabId === appInfo?.viewId) {
|
if (tabId === appInfo?.viewId) {
|
||||||
workbench.sidebarOpen$.next(!workbench.sidebarOpen$.value);
|
workbench.sidebarOpen$.next(!workbench.sidebarOpen$.value);
|
||||||
|
|||||||
Reference in New Issue
Block a user