diff --git a/packages/frontend/apps/electron/package.json b/packages/frontend/apps/electron/package.json index e0c0487cfd..7f37eedaa2 100644 --- a/packages/frontend/apps/electron/package.json +++ b/packages/frontend/apps/electron/package.json @@ -73,6 +73,7 @@ "async-call-rpc": "^6.4.2", "electron-updater": "^6.2.1", "link-preview-js": "^3.0.5", + "next-themes": "^0.3.0", "yjs": "patch:yjs@npm%3A13.6.18#~/.yarn/patches/yjs-npm-13.6.18-ad0d5f7c43.patch" }, "build": { diff --git a/packages/frontend/apps/electron/renderer/app.tsx b/packages/frontend/apps/electron/renderer/app.tsx index 58618a8220..24b878561d 100644 --- a/packages/frontend/apps/electron/renderer/app.tsx +++ b/packages/frontend/apps/electron/renderer/app.tsx @@ -7,6 +7,11 @@ import { router } from '@affine/core/desktop/router'; import { configureCommonModules } from '@affine/core/modules'; import { configureAppTabsHeaderModule } from '@affine/core/modules/app-tabs-header'; import { ValidatorProvider } from '@affine/core/modules/cloud'; +import { + configureDesktopApiModule, + DesktopApiService, +} from '@affine/core/modules/desktop-api'; +import { configureFindInPageModule } from '@affine/core/modules/find-in-page'; import { I18nProvider } from '@affine/core/modules/i18n'; import { configureElectronStateStorageImpls } from '@affine/core/modules/storage'; import { CustomThemeModifier } from '@affine/core/modules/theme-editor'; @@ -21,7 +26,6 @@ import { configureSqliteWorkspaceEngineStorageProvider, } from '@affine/core/modules/workspace-engine'; import createEmotionCache from '@affine/core/utils/create-emotion-cache'; -import { apis, appInfo } from '@affine/electron-api'; import { CacheProvider } from '@emotion/react'; import { Framework, @@ -32,6 +36,8 @@ import { import { Suspense } from 'react'; import { RouterProvider } from 'react-router-dom'; +import { DesktopThemeSync } from './theme-sync'; + const desktopWhiteList = [ '/open-app/signin-redirect', '/open-app/url', @@ -64,26 +70,38 @@ configureSqliteWorkspaceEngineStorageProvider(framework); configureSqliteUserspaceStorageProvider(framework); configureDesktopWorkbenchModule(framework); configureAppTabsHeaderModule(framework); -framework.impl(PopupWindowProvider, { - open: (url: string) => { - apis?.ui.openExternal(url).catch(e => { - console.error('Failed to open external URL', e); - }); - }, +configureFindInPageModule(framework); +configureDesktopApiModule(framework); + +framework.impl(PopupWindowProvider, p => { + const apis = p.get(DesktopApiService).api; + return { + open: (url: string) => { + apis.handler.ui.openExternal(url).catch(e => { + console.error('Failed to open external URL', e); + }); + }, + }; }); -framework.impl(ClientSchemeProvider, { - getClientScheme() { - return appInfo?.scheme; - }, +framework.impl(ClientSchemeProvider, p => { + const appInfo = p.get(DesktopApiService).appInfo; + return { + getClientScheme() { + return appInfo?.scheme; + }, + }; }); -framework.impl(ValidatorProvider, { - async validate(_challenge, resource) { - const token = await apis?.ui?.getChallengeResponse(resource); - if (!token) { - throw new Error('Challenge failed'); - } - return token; - }, +framework.impl(ValidatorProvider, p => { + const apis = p.get(DesktopApiService).api; + return { + async validate(_challenge, resource) { + const token = await apis.handler.ui.getChallengeResponse(resource); + if (!token) { + throw new Error('Challenge failed'); + } + return token; + }, + }; }); const frameworkProvider = framework.provider(); @@ -100,6 +118,7 @@ export function App() { + diff --git a/packages/frontend/apps/electron/renderer/index.tsx b/packages/frontend/apps/electron/renderer/index.tsx index cfbb027e4e..97303a93bc 100644 --- a/packages/frontend/apps/electron/renderer/index.tsx +++ b/packages/frontend/apps/electron/renderer/index.tsx @@ -1,21 +1,8 @@ import './setup'; import { appConfigProxy } from '@affine/core/components/hooks/use-app-config-storage'; -import { apis, appInfo, events } from '@affine/electron-api'; -import { - init, - reactRouterV6BrowserTracingIntegration, - setTags, -} from '@sentry/react'; -import { debounce } from 'lodash-es'; -import { StrictMode, useEffect } from 'react'; +import { StrictMode } from 'react'; import { createRoot } from 'react-dom/client'; -import { - createRoutesFromChildren, - matchRoutes, - useLocation, - useNavigationType, -} from 'react-router-dom'; import { App } from './app'; @@ -26,82 +13,6 @@ function main() { .getSync() .catch(() => console.error('failed to load app config')); - // skip bootstrap setup for desktop onboarding - if ( - !( - appInfo?.windowName === 'onboarding' || - appInfo?.windowName === 'theme-editor' - ) - ) { - if (BUILD_CONFIG.debug || window.SENTRY_RELEASE) { - // https://docs.sentry.io/platforms/javascript/guides/electron/ - init({ - dsn: process.env.SENTRY_DSN, - environment: process.env.BUILD_TYPE ?? 'development', - integrations: [ - reactRouterV6BrowserTracingIntegration({ - useEffect, - useLocation, - useNavigationType, - createRoutesFromChildren, - matchRoutes, - }), - ], - }); - setTags({ - appVersion: BUILD_CONFIG.appVersion, - editorVersion: BUILD_CONFIG.editorVersion, - }); - - apis?.ui.handleNetworkChange(navigator.onLine); - window.addEventListener('offline', () => { - apis?.ui.handleNetworkChange(false); - }); - window.addEventListener('online', () => { - apis?.ui.handleNetworkChange(true); - }); - } - - const handleMaximized = (maximized: boolean | undefined) => { - document.documentElement.dataset.maximized = String(maximized); - }; - const handleFullscreen = (fullscreen: boolean | undefined) => { - document.documentElement.dataset.fullscreen = String(fullscreen); - }; - apis?.ui.isMaximized().then(handleMaximized).catch(console.error); - apis?.ui.isFullScreen().then(handleFullscreen).catch(console.error); - events?.ui.onMaximized(handleMaximized); - events?.ui.onFullScreen(handleFullscreen); - - const tabId = appInfo?.viewId; - const handleActiveTabChange = (active: boolean) => { - document.documentElement.dataset.active = String(active); - }; - - if (tabId) { - apis?.ui - .isActiveTab() - .then(active => { - handleActiveTabChange(active); - events?.ui.onActiveTabChanged(id => { - handleActiveTabChange(id === tabId); - }); - }) - .catch(console.error); - } - - const handleResize = debounce(() => { - apis?.ui.handleWindowResize().catch(console.error); - }, 50); - window.addEventListener('resize', handleResize); - window.addEventListener('dragstart', () => { - document.documentElement.dataset.dragging = 'true'; - }); - window.addEventListener('dragend', () => { - document.documentElement.dataset.dragging = 'false'; - }); - } - mountApp(); } diff --git a/packages/frontend/apps/electron/renderer/shell/app.tsx b/packages/frontend/apps/electron/renderer/shell/app.tsx index 42179afa0d..11d589a6bb 100644 --- a/packages/frontend/apps/electron/renderer/shell/app.tsx +++ b/packages/frontend/apps/electron/renderer/shell/app.tsx @@ -6,6 +6,7 @@ import { AppTabsHeader, configureAppTabsHeaderModule, } from '@affine/core/modules/app-tabs-header'; +import { configureDesktopApiModule } from '@affine/core/modules/desktop-api'; import { configureI18nModule, I18nProvider } from '@affine/core/modules/i18n'; import { configureElectronStateStorageImpls } from '@affine/core/modules/storage'; import { SplitViewFallback } from '@affine/core/modules/workbench/view/split-view/split-view'; @@ -23,6 +24,7 @@ configureElectronStateStorageImpls(framework); configureAppTabsHeaderModule(framework); configureAppSidebarModule(framework); configureI18nModule(framework); +configureDesktopApiModule(framework); const frameworkProvider = framework.provider(); export function App() { diff --git a/packages/frontend/apps/electron/renderer/shell/index.tsx b/packages/frontend/apps/electron/renderer/shell/index.tsx index 00ae860c9c..99b366bddf 100644 --- a/packages/frontend/apps/electron/renderer/shell/index.tsx +++ b/packages/frontend/apps/electron/renderer/shell/index.tsx @@ -1,28 +1,16 @@ import './setup'; -import { apis, events } from '@affine/electron-api'; +import { events } from '@affine/electron-api'; import { StrictMode } from 'react'; import { createRoot } from 'react-dom/client'; import { App } from './app'; async function main() { - const handleMaximized = (maximized: boolean | undefined) => { - document.documentElement.dataset.maximized = String(maximized); - }; - const handleFullscreen = (fullscreen: boolean | undefined) => { - document.documentElement.dataset.fullscreen = String(fullscreen); - }; const handleActive = (active: boolean | undefined) => { document.documentElement.dataset.active = String(active); }; - - apis?.ui.isMaximized().then(handleMaximized).catch(console.error); - apis?.ui.isFullScreen().then(handleFullscreen).catch(console.error); - events?.ui.onMaximized(handleMaximized); - events?.ui.onFullScreen(handleFullscreen); events?.ui.onTabShellViewActiveChange(handleActive); - mountApp(); } diff --git a/packages/frontend/apps/electron/renderer/theme-sync.ts b/packages/frontend/apps/electron/renderer/theme-sync.ts new file mode 100644 index 0000000000..30a43c500d --- /dev/null +++ b/packages/frontend/apps/electron/renderer/theme-sync.ts @@ -0,0 +1,25 @@ +import { DesktopApiService } from '@affine/core/modules/desktop-api/service'; +import { useService } from '@toeverything/infra'; +import { useTheme } from 'next-themes'; +import { useRef } from 'react'; + +export const DesktopThemeSync = () => { + const { theme } = useTheme(); + const lastThemeRef = useRef(theme); + const onceRef = useRef(false); + + const handler = useService(DesktopApiService).api.handler; + + if (lastThemeRef.current !== theme || !onceRef.current) { + if (BUILD_CONFIG.isElectron && theme) { + handler.ui + .handleThemeChange(theme as 'dark' | 'light' | 'system') + .catch(err => { + console.error(err); + }); + } + lastThemeRef.current = theme; + onceRef.current = true; + } + return null; +}; diff --git a/packages/frontend/apps/electron/src/preload/electron-api.ts b/packages/frontend/apps/electron/src/preload/electron-api.ts index f02f741f6e..6c63db6c2f 100644 --- a/packages/frontend/apps/electron/src/preload/electron-api.ts +++ b/packages/frontend/apps/electron/src/preload/electron-api.ts @@ -56,6 +56,8 @@ export const appInfo = { scheme, }; +export type AppInfo = typeof appInfo; + function getMainAPIs() { const meta: ExposedMeta = (() => { const val = process.argv diff --git a/packages/frontend/apps/electron/src/preload/shared-storage.ts b/packages/frontend/apps/electron/src/preload/shared-storage.ts index f0f72c6b84..0657812612 100644 --- a/packages/frontend/apps/electron/src/preload/shared-storage.ts +++ b/packages/frontend/apps/electron/src/preload/shared-storage.ts @@ -94,3 +94,5 @@ export const sharedStorage = { globalState, globalCache, }; + +export type SharedStorage = typeof sharedStorage; diff --git a/packages/frontend/component/src/components/theme-provider/index.tsx b/packages/frontend/component/src/components/theme-provider/index.tsx index d5c12434c9..ad69e27aae 100644 --- a/packages/frontend/component/src/components/theme-provider/index.tsx +++ b/packages/frontend/component/src/components/theme-provider/index.tsx @@ -1,33 +1,12 @@ -import { apis } from '@affine/electron-api'; -import { ThemeProvider as NextThemeProvider, useTheme } from 'next-themes'; +import { ThemeProvider as NextThemeProvider } from 'next-themes'; import type { PropsWithChildren } from 'react'; -import { memo, useRef } from 'react'; const themes = ['dark', 'light']; -const DesktopThemeSync = memo(function DesktopThemeSync() { - const { theme } = useTheme(); - const lastThemeRef = useRef(theme); - const onceRef = useRef(false); - if (lastThemeRef.current !== theme || !onceRef.current) { - if (BUILD_CONFIG.isElectron && theme) { - apis?.ui - .handleThemeChange(theme as 'dark' | 'light' | 'system') - .catch(err => { - console.error(err); - }); - } - lastThemeRef.current = theme; - onceRef.current = true; - } - return null; -}); - export const ThemeProvider = ({ children }: PropsWithChildren) => { return ( {children} - ); }; diff --git a/packages/frontend/core/src/commands/affine-settings.tsx b/packages/frontend/core/src/commands/affine-settings.tsx index 1fd05bc785..4bc1827305 100644 --- a/packages/frontend/core/src/commands/affine-settings.tsx +++ b/packages/frontend/core/src/commands/affine-settings.tsx @@ -5,7 +5,7 @@ import { appSettingAtom } from '@toeverything/infra'; import type { createStore } from 'jotai'; import type { useTheme } from 'next-themes'; -import type { EditorSettingService } from '../modules/editor-settting'; +import type { EditorSettingService } from '../modules/editor-setting'; import { registerAffineCommand } from './registry'; export function registerAffineSettingsCommands({ diff --git a/packages/frontend/core/src/commands/affine-updates.tsx b/packages/frontend/core/src/commands/affine-updates.tsx index 082aeefc7b..c1b7cdf16c 100644 --- a/packages/frontend/core/src/commands/affine-updates.tsx +++ b/packages/frontend/core/src/commands/affine-updates.tsx @@ -1,6 +1,5 @@ import { notify } from '@affine/component'; import { updateReadyAtom } from '@affine/core/components/hooks/use-app-updater'; -import { apis } from '@affine/electron-api'; import type { useI18n } from '@affine/i18n'; import { track } from '@affine/track'; import { ResetIcon } from '@blocksuite/icons/rc'; @@ -11,9 +10,11 @@ import { registerAffineCommand } from './registry'; export function registerAffineUpdatesCommands({ t, store, + quitAndInstall, }: { t: ReturnType; store: ReturnType; + quitAndInstall: () => Promise; }) { const unsubs: Array<() => void> = []; @@ -27,7 +28,7 @@ export function registerAffineUpdatesCommands({ run() { track.$.cmdk.updates.quitAndInstall(); - apis?.updater.quitAndInstall().catch(err => { + quitAndInstall().catch(err => { notify.error({ title: 'Failed to restart to upgrade', message: 'Please restart the app manually to upgrade.', diff --git a/packages/frontend/core/src/components/affine/setting-modal/general-setting/appearance/theme-editor-setting.tsx b/packages/frontend/core/src/components/affine/setting-modal/general-setting/appearance/theme-editor-setting.tsx index b61f5e9c3f..34d13f26db 100644 --- a/packages/frontend/core/src/components/affine/setting-modal/general-setting/appearance/theme-editor-setting.tsx +++ b/packages/frontend/core/src/components/affine/setting-modal/general-setting/appearance/theme-editor-setting.tsx @@ -1,10 +1,14 @@ import { Button } from '@affine/component'; import { SettingRow } from '@affine/component/setting-components'; +import { DesktopApiService } from '@affine/core/modules/desktop-api/service'; import { ThemeEditorService } from '@affine/core/modules/theme-editor'; import { UrlService } from '@affine/core/modules/url'; -import { apis } from '@affine/electron-api'; import { DeleteIcon } from '@blocksuite/icons/rc'; -import { useLiveData, useService } from '@toeverything/infra'; +import { + useLiveData, + useService, + useServiceOptional, +} from '@toeverything/infra'; import { cssVar } from '@toeverything/theme'; import { useCallback } from 'react'; @@ -12,14 +16,15 @@ export const ThemeEditorSetting = () => { const themeEditor = useService(ThemeEditorService); const modified = useLiveData(themeEditor.modified$); const urlService = useService(UrlService); + const desktopApi = useServiceOptional(DesktopApiService); const open = useCallback(() => { - if (BUILD_CONFIG.isElectron) { - apis?.ui.openThemeEditor().catch(console.error); + if (desktopApi) { + desktopApi.handler.ui.openThemeEditor().catch(console.error); } else if (BUILD_CONFIG.isMobileWeb || BUILD_CONFIG.isWeb) { urlService.openPopupWindow(location.origin + '/theme-editor'); } - }, [urlService]); + }, [desktopApi, urlService]); return ( { @@ -21,6 +25,7 @@ export const ExportPanel = ({ const t = useI18n(); const [saving, setSaving] = useState(false); const isOnline = useSystemOnline(); + const desktopApi = useService(DesktopApiService); const onExport = useAsyncCallback(async () => { if (saving || !workspace) { @@ -33,7 +38,7 @@ export const ExportPanel = ({ await workspace.engine.blob.sync(); } - const result = await apis?.dialog.saveDBFileAs(workspaceId); + const result = await desktopApi.handler.dialog.saveDBFileAs(workspaceId); if (result?.error) { throw new Error(result.error); } else if (!result?.canceled) { @@ -44,7 +49,7 @@ export const ExportPanel = ({ } finally { setSaving(false); } - }, [isOnline, saving, t, workspace, workspaceId]); + }, [isOnline, saving, t, workspace, workspaceId, desktopApi]); return ( diff --git a/packages/frontend/core/src/components/affine/setting-modal/workspace-setting/new-workspace-setting-detail/index.tsx b/packages/frontend/core/src/components/affine/setting-modal/workspace-setting/new-workspace-setting-detail/index.tsx index 880ddce3b1..9a423101a6 100644 --- a/packages/frontend/core/src/components/affine/setting-modal/workspace-setting/new-workspace-setting-detail/index.tsx +++ b/packages/frontend/core/src/components/affine/setting-modal/workspace-setting/new-workspace-setting-detail/index.tsx @@ -13,7 +13,7 @@ import { useCallback } from 'react'; import { DeleteLeaveWorkspace } from './delete-leave-workspace'; import { EnableCloudPanel } from './enable-cloud'; -import { ExportPanel } from './export'; +import { DesktopExportPanel } from './export'; import { LabelsPanel } from './labels'; import { MembersPanel } from './members'; import { ProfilePanel } from './profile'; @@ -71,7 +71,7 @@ export const WorkspaceSettingDetail = ({ {BUILD_CONFIG.isElectron && ( - diff --git a/packages/frontend/core/src/components/blocksuite/block-suite-editor/lit-adaper.tsx b/packages/frontend/core/src/components/blocksuite/block-suite-editor/lit-adaper.tsx index 49cb5043f6..13458e33ea 100644 --- a/packages/frontend/core/src/components/blocksuite/block-suite-editor/lit-adaper.tsx +++ b/packages/frontend/core/src/components/blocksuite/block-suite-editor/lit-adaper.tsx @@ -5,7 +5,7 @@ import { } from '@affine/component'; import { ServerConfigService } from '@affine/core/modules/cloud'; import { EditorService } from '@affine/core/modules/editor'; -import { EditorSettingService } from '@affine/core/modules/editor-settting'; +import { EditorSettingService } from '@affine/core/modules/editor-setting'; import { JournalService } from '@affine/core/modules/journal'; import { toURLSearchParams } from '@affine/core/modules/navigation'; import { PeekViewService } from '@affine/core/modules/peek-view/services/peek-view'; diff --git a/packages/frontend/core/src/components/blocksuite/block-suite-editor/specs/custom/root-block.ts b/packages/frontend/core/src/components/blocksuite/block-suite-editor/specs/custom/root-block.ts index f6ee2e3df1..ee47d9dd95 100644 --- a/packages/frontend/core/src/components/blocksuite/block-suite-editor/specs/custom/root-block.ts +++ b/packages/frontend/core/src/components/blocksuite/block-suite-editor/specs/custom/root-block.ts @@ -2,7 +2,7 @@ import { AIEdgelessRootBlockSpec, AIPageRootBlockSpec, } from '@affine/core/blocksuite/presets/ai'; -import { EditorSettingService } from '@affine/core/modules/editor-settting'; +import { EditorSettingService } from '@affine/core/modules/editor-setting'; import { mixpanel } from '@affine/track'; import { ConfigExtension, diff --git a/packages/frontend/core/src/components/blocksuite/block-suite-editor/specs/custom/spec-patchers.tsx b/packages/frontend/core/src/components/blocksuite/block-suite-editor/specs/custom/spec-patchers.tsx index 9fbb56d606..199dd1414b 100644 --- a/packages/frontend/core/src/components/blocksuite/block-suite-editor/specs/custom/spec-patchers.tsx +++ b/packages/frontend/core/src/components/blocksuite/block-suite-editor/specs/custom/spec-patchers.tsx @@ -8,7 +8,7 @@ import { type useConfirmModal, } from '@affine/component'; import type { EditorService } from '@affine/core/modules/editor'; -import { EditorSettingService } from '@affine/core/modules/editor-settting'; +import { EditorSettingService } from '@affine/core/modules/editor-setting'; import { resolveLinkToDoc } from '@affine/core/modules/navigation'; import type { PeekViewService } from '@affine/core/modules/peek-view'; import { diff --git a/packages/frontend/core/src/components/blocksuite/block-suite-page-list/utils.tsx b/packages/frontend/core/src/components/blocksuite/block-suite-page-list/utils.tsx index 54a254b799..caf548d053 100644 --- a/packages/frontend/core/src/components/blocksuite/block-suite-page-list/utils.tsx +++ b/packages/frontend/core/src/components/blocksuite/block-suite-page-list/utils.tsx @@ -1,6 +1,6 @@ import { toast } from '@affine/component'; import { AppSidebarService } from '@affine/core/modules/app-sidebar'; -import { EditorSettingService } from '@affine/core/modules/editor-settting'; +import { EditorSettingService } from '@affine/core/modules/editor-setting'; import { WorkbenchService } from '@affine/core/modules/workbench'; import type { DocMode } from '@blocksuite/affine/blocks'; import type { DocCollection } from '@blocksuite/affine/store'; diff --git a/packages/frontend/core/src/components/hooks/affine/use-register-find-in-page-commands.ts b/packages/frontend/core/src/components/hooks/affine/use-register-find-in-page-commands.ts index bdea9af672..f02a7ff206 100644 --- a/packages/frontend/core/src/components/hooks/affine/use-register-find-in-page-commands.ts +++ b/packages/frontend/core/src/components/hooks/affine/use-register-find-in-page-commands.ts @@ -4,17 +4,17 @@ import { } from '@affine/core/commands'; import { FindInPageService } from '@affine/core/modules/find-in-page/services/find-in-page'; import { track } from '@affine/track'; -import { useService } from '@toeverything/infra'; +import { useServiceOptional } from '@toeverything/infra'; import { useCallback, useEffect } from 'react'; export function useRegisterFindInPageCommands() { - const findInPage = useService(FindInPageService).findInPage; + const findInPage = useServiceOptional(FindInPageService)?.findInPage; const toggleVisible = useCallback(() => { // get the selected text in page const selection = window.getSelection(); const selectedText = selection?.toString(); - findInPage.toggleVisible(selectedText); + findInPage?.toggleVisible(selectedText); }, [findInPage]); useEffect(() => { diff --git a/packages/frontend/core/src/components/hooks/use-app-updater.ts b/packages/frontend/core/src/components/hooks/use-app-updater.ts index 8e44dfbf69..4277f4f9ec 100644 --- a/packages/frontend/core/src/components/hooks/use-app-updater.ts +++ b/packages/frontend/core/src/components/hooks/use-app-updater.ts @@ -1,3 +1,4 @@ +// todo(@pengx17): remove jotai import { UrlService } from '@affine/core/modules/url'; import type { UpdateMeta } from '@affine/electron-api'; import { apis, events } from '@affine/electron-api'; diff --git a/packages/frontend/core/src/components/hooks/use-journal.ts b/packages/frontend/core/src/components/hooks/use-journal.ts index 69d984fcda..dc27533de4 100644 --- a/packages/frontend/core/src/components/hooks/use-journal.ts +++ b/packages/frontend/core/src/components/hooks/use-journal.ts @@ -1,4 +1,4 @@ -import { EditorSettingService } from '@affine/core/modules/editor-settting'; +import { EditorSettingService } from '@affine/core/modules/editor-setting'; import { JournalService } from '@affine/core/modules/journal'; import { i18nTime } from '@affine/i18n'; import { track } from '@affine/track'; diff --git a/packages/frontend/core/src/components/hooks/use-register-workspace-commands.ts b/packages/frontend/core/src/components/hooks/use-register-workspace-commands.ts index 4f625f9ba7..e94d278d18 100644 --- a/packages/frontend/core/src/components/hooks/use-register-workspace-commands.ts +++ b/packages/frontend/core/src/components/hooks/use-register-workspace-commands.ts @@ -1,9 +1,14 @@ import { AppSidebarService } from '@affine/core/modules/app-sidebar'; +import { DesktopApiService } from '@affine/core/modules/desktop-api/service'; import { I18nService } from '@affine/core/modules/i18n'; import { UrlService } from '@affine/core/modules/url'; import { useI18n } from '@affine/i18n'; import type { AffineEditorContainer } from '@blocksuite/affine/presets'; -import { useService, WorkspaceService } from '@toeverything/infra'; +import { + useService, + useServiceOptional, + WorkspaceService, +} from '@toeverything/infra'; import { useStore } from 'jotai'; import { useTheme } from 'next-themes'; import { useEffect } from 'react'; @@ -21,7 +26,7 @@ import { } from '../../commands'; import { usePageHelper } from '../../components/blocksuite/block-suite-page-list/utils'; import { CreateWorkspaceDialogService } from '../../modules/create-workspace'; -import { EditorSettingService } from '../../modules/editor-settting'; +import { EditorSettingService } from '../../modules/editor-setting'; import { CMDKQuickSearchService } from '../../modules/quicksearch/services/cmdk'; import { useActiveBlocksuiteEditor } from './use-block-suite-editor'; import { useNavigateHelper } from './use-navigate-helper'; @@ -77,6 +82,9 @@ export function useRegisterWorkspaceCommands() { const appSidebarService = useService(AppSidebarService); const i18n = useService(I18nService).i18n; + const quitAndInstall = + useServiceOptional(DesktopApiService)?.handler.updater.quitAndInstall; + useEffect(() => { const unsub = registerCMDKCommand(cmdkQuickSearchService, editor); @@ -87,15 +95,20 @@ export function useRegisterWorkspaceCommands() { // register AffineUpdatesCommands useEffect(() => { + if (!quitAndInstall) { + return; + } + const unsub = registerAffineUpdatesCommands({ store, t, + quitAndInstall, }); return () => { unsub(); }; - }, [store, t]); + }, [quitAndInstall, store, t]); // register AffineNavigationCommands useEffect(() => { diff --git a/packages/frontend/core/src/components/layouts/workspace-layout.tsx b/packages/frontend/core/src/components/layouts/workspace-layout.tsx index f85d06cd99..965d24af0a 100644 --- a/packages/frontend/core/src/components/layouts/workspace-layout.tsx +++ b/packages/frontend/core/src/components/layouts/workspace-layout.tsx @@ -4,6 +4,7 @@ import { resolveGlobalLoadingEventAtom, } from '@affine/component/global-loading'; import { SidebarSwitch } from '@affine/core/modules/app-sidebar/views'; +import { WorkspaceDesktopApiService } from '@affine/core/modules/desktop-api/service'; import { useI18n } from '@affine/i18n'; import { type DocMode, ZipTransformer } from '@blocksuite/affine/blocks'; import { @@ -33,7 +34,7 @@ import { Map as YMap } from 'yjs'; import { AIProvider } from '../../blocksuite/presets/ai'; import { AppTabsHeader } from '../../modules/app-tabs-header'; -import { EditorSettingService } from '../../modules/editor-settting'; +import { EditorSettingService } from '../../modules/editor-setting'; import { NavigationButtons } from '../../modules/navigation'; import { useRegisterNavigationCommands } from '../../modules/navigation/view/use-register-navigation-commands'; import { QuickSearchContainer } from '../../modules/quicksearch'; @@ -179,6 +180,8 @@ export const WorkspaceLayoutProviders = ({ children }: PropsWithChildren) => { }; const DesktopLayout = ({ children }: PropsWithChildren) => { + // is there a better way to make sure service is always available even if it's not explicitly used? + useService(WorkspaceDesktopApiService); return (
diff --git a/packages/frontend/core/src/components/page-detail-editor.tsx b/packages/frontend/core/src/components/page-detail-editor.tsx index f81b982a6a..b6a8b4487e 100644 --- a/packages/frontend/core/src/components/page-detail-editor.tsx +++ b/packages/frontend/core/src/components/page-detail-editor.tsx @@ -11,7 +11,7 @@ import { EditorService } from '../modules/editor'; import { EditorSettingService, fontStyleOptions, -} from '../modules/editor-settting'; +} from '../modules/editor-setting'; import { BlockSuiteEditor as Editor } from './blocksuite/block-suite-editor'; import * as styles from './page-detail-editor.css'; diff --git a/packages/frontend/core/src/components/providers/modal-provider.tsx b/packages/frontend/core/src/components/providers/modal-provider.tsx index 50fcf94ddb..3ce0e5eb76 100644 --- a/packages/frontend/core/src/components/providers/modal-provider.tsx +++ b/packages/frontend/core/src/components/providers/modal-provider.tsx @@ -1,10 +1,11 @@ import { NotificationCenter, notify } from '@affine/component'; -import { events } from '@affine/electron-api'; +import { DesktopApiService } from '@affine/core/modules/desktop-api/service'; import { WorkspaceFlavour } from '@affine/env/workspace'; import { GlobalContextService, useLiveData, useService, + useServiceOptional, WorkspaceService, WorkspacesService, } from '@toeverything/infra'; @@ -61,17 +62,16 @@ export const Setting = () => { [setOpenSettingModalAtom] ); + const desktopApi = useServiceOptional(DesktopApiService); + useEffect(() => { - if (BUILD_CONFIG.isElectron) { - return events?.applicationMenu.openAboutPageInSettingModal(() => - setOpenSettingModalAtom({ - activeTab: 'about', - open: true, - }) - ); - } - return; - }, [setOpenSettingModalAtom]); + return desktopApi?.events?.applicationMenu.openAboutPageInSettingModal(() => + setOpenSettingModalAtom({ + activeTab: 'about', + open: true, + }) + ); + }, [desktopApi?.events?.applicationMenu, setOpenSettingModalAtom]); if (!open) { return null; diff --git a/packages/frontend/core/src/components/pure/header/windows-app-controls.tsx b/packages/frontend/core/src/components/pure/header/windows-app-controls.tsx index addd072828..91ca8ea24a 100644 --- a/packages/frontend/core/src/components/pure/header/windows-app-controls.tsx +++ b/packages/frontend/core/src/components/pure/header/windows-app-controls.tsx @@ -1,37 +1,9 @@ -import { apis, events } from '@affine/electron-api'; -import { useAtomValue } from 'jotai'; -import { atomWithObservable } from 'jotai/utils'; -import { useCallback } from 'react'; -import { combineLatest, map, Observable } from 'rxjs'; +import { DesktopApiService } from '@affine/core/modules/desktop-api/service'; +import { useService } from '@toeverything/infra'; +import { useCallback, useEffect, useState } from 'react'; import * as style from './style.css'; -const maximized$ = new Observable(subscriber => { - subscriber.next(false); - if (events) { - return events.ui.onMaximized(res => { - subscriber.next(res); - }); - } - return () => {}; -}); - -const fullscreen$ = new Observable(subscriber => { - subscriber.next(false); - if (events) { - return events.ui.onFullScreen(res => { - subscriber.next(res); - }); - } - return () => {}; -}); - -const maximizedAtom = atomWithObservable(() => { - return combineLatest([maximized$, fullscreen$]).pipe( - map(([maximized, fullscreen]) => maximized || fullscreen) - ); -}); - const minimizeSVG = ( { - const handleMinimizeApp = useCallback(() => { - apis?.ui.handleMinimizeApp().catch(err => { - console.error(err); - }); - }, []); - const handleMaximizeApp = useCallback(() => { - apis?.ui.handleMaximizeApp().catch(err => { - console.error(err); - }); - }, []); - const handleCloseApp = useCallback(() => { - apis?.ui.handleCloseApp().catch(err => { - console.error(err); - }); - }, []); + const desktopApi = useService(DesktopApiService); - const maximized = useAtomValue(maximizedAtom); + const handleMinimizeApp = useCallback(() => { + desktopApi.handler.ui.handleMinimizeApp().catch(err => { + console.error(err); + }); + }, [desktopApi.handler.ui]); + const handleMaximizeApp = useCallback(() => { + desktopApi.handler.ui.handleMaximizeApp().catch(err => { + console.error(err); + }); + }, [desktopApi.handler.ui]); + const handleCloseApp = useCallback(() => { + desktopApi.handler.ui.handleCloseApp().catch(err => { + console.error(err); + }); + }, [desktopApi.handler.ui]); + + const [maximized, setMaximized] = useState(false); + const [fullscreen, setFullscreen] = useState(false); + + useEffect(() => { + return desktopApi.events.ui.onMaximized(setMaximized); + }, [desktopApi.events.ui]); + + useEffect(() => { + return desktopApi.events.ui.onFullScreen(setFullscreen); + }, [desktopApi.events.ui]); return (
{ className={style.windowAppControl} onClick={handleMaximizeApp} > - {maximized ? unmaximizedSVG : maximizeSVG} + {maximized || fullscreen ? unmaximizedSVG : maximizeSVG}