Skip to content

Commit

Permalink
feat(core): open app in electron app entry (#8637)
Browse files Browse the repository at this point in the history
fix PD-208
fix PD-210
fix PD-209
fix AF-1495
  • Loading branch information
pengx17 committed Oct 31, 2024
1 parent 5d92c90 commit 0f8b273
Show file tree
Hide file tree
Showing 36 changed files with 886 additions and 225 deletions.
19 changes: 4 additions & 15 deletions packages/frontend/apps/electron/src/main/deep-link.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@ import { buildType, isDev } from './config';
import { logger } from './logger';
import { uiSubjects } from './ui';
import {
addTab,
addTabWithUrl,
getMainWindow,
loadUrlInActiveTab,
openUrlInHiddenWindow,
openUrlInMainWindow,
showMainWindow,
} from './windows-manager';

Expand Down Expand Up @@ -88,22 +88,11 @@ async function handleAffineUrl(url: string) {
) {
// @todo(@forehalo): refactor router utilities
// basename of /workspace/xxx/yyy is /workspace/xxx
const basename = urlObj.pathname.split('/').slice(0, 3).join('/');
const pathname = '/' + urlObj.pathname.split('/').slice(3).join('/');

await addTab({
basename,
show: true,
view: {
path: {
pathname: pathname,
},
},
});
await addTabWithUrl(url);
} else {
const hiddenWindow = urlObj.searchParams.get('hidden')
? await openUrlInHiddenWindow(urlObj)
: await openUrlInMainWindow(urlObj);
: await loadUrlInActiveTab(url);

const main = await getMainWindow();
if (main && hiddenWindow) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -273,15 +273,3 @@ export async function openUrlInHiddenWindow(urlObj: URL) {
});
return win;
}

// TODO(@pengx17): somehow the page won't load the url passed, help needed
export async function openUrlInMainWindow(urlObj: URL) {
const url = transformToAppUrl(urlObj);
logger.info('loading page at', url);
const mainWindow = await getMainWindow();
if (mainWindow) {
await mainWindow.loadURL(url);
}

return null;
}
Original file line number Diff line number Diff line change
Expand Up @@ -932,7 +932,35 @@ export const isActiveTab = (wc: WebContents) => {
WebContentViewsManager.instance.activeWorkbenchView?.webContents.id
);
};

// parse the full pathname to basename and pathname
// eg: /workspace/xxx/yyy => { basename: '/workspace/xxx', pathname: '/yyy' }
export const parseFullPathname = (url: string) => {
const urlObj = new URL(url);
const basename = urlObj.pathname.match(/\/workspace\/[^/]+/g)?.[0] ?? '/';
return {
basename,
pathname: urlObj.pathname.slice(basename.length),
search: urlObj.search,
hash: urlObj.hash,
};
};

export const addTab = WebContentViewsManager.instance.addTab;
export const addTabWithUrl = (url: string) => {
const { basename, pathname, search, hash } = parseFullPathname(url);
return addTab({
basename,
view: {
path: { pathname, search, hash },
},
});
};

export const loadUrlInActiveTab = async (_url: string) => {
// todo: implement
throw new Error('loadUrlInActiveTab not implemented');
};
export const showTab = WebContentViewsManager.instance.showTab;
export const closeTab = WebContentViewsManager.instance.closeTab;
export const undoCloseTab = WebContentViewsManager.instance.undoCloseTab;
Expand Down
15 changes: 10 additions & 5 deletions packages/frontend/apps/web/src/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import { Telemetry } from '@affine/core/components/telemetry';
import { router } from '@affine/core/desktop/router';
import { configureCommonModules } from '@affine/core/modules';
import { I18nProvider } from '@affine/core/modules/i18n';
import { configureOpenInApp } from '@affine/core/modules/open-in-app';
import { WebOpenInAppGuard } from '@affine/core/modules/open-in-app/views/open-in-app-guard';
import { configureLocalStorageStateStorageImpls } from '@affine/core/modules/storage';
import { CustomThemeModifier } from '@affine/core/modules/theme-editor';
import { PopupWindowProvider } from '@affine/core/modules/url';
Expand Down Expand Up @@ -38,6 +40,7 @@ configureLocalStorageStateStorageImpls(framework);
configureBrowserWorkspaceFlavours(framework);
configureIndexedDBWorkspaceEngineStorageProvider(framework);
configureIndexedDBUserspaceStorageProvider(framework);
configureOpenInApp(framework);
framework.impl(PopupWindowProvider, {
open: (target: string) => {
const targetUrl = new URL(target);
Expand Down Expand Up @@ -75,11 +78,13 @@ export function App() {
<Telemetry />
<CustomThemeModifier />
<GlobalLoading />
<RouterProvider
fallbackElement={<AppFallback key="RouterFallback" />}
router={router}
future={future}
/>
<WebOpenInAppGuard>
<RouterProvider
fallbackElement={<AppFallback key="RouterFallback" />}
router={router}
future={future}
/>
</WebOpenInAppGuard>
</AffineContext>
</I18nProvider>
</CacheProvider>
Expand Down
4 changes: 2 additions & 2 deletions packages/frontend/component/src/ui/checkbox/checkbox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export type CheckboxProps = Omit<
'onChange'
> & {
checked: boolean;
onChange: (
onChange?: (
event: React.ChangeEvent<HTMLInputElement>,
checked: boolean
) => void;
Expand Down Expand Up @@ -41,7 +41,7 @@ export const Checkbox = ({
const handleChange = useCallback(
(event: React.ChangeEvent<HTMLInputElement>) => {
const newChecked = event.target.checked;
onChange(event, newChecked);
onChange?.(event, newChecked);
const inputElement = inputRef.current;
if (newChecked && inputElement && animation) {
playCheckAnimation(inputElement.parentElement as Element).catch(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,8 @@ import {
SettingWrapper,
} from '@affine/component/setting-components';
import { useAppUpdater } from '@affine/core/components/hooks/use-app-updater';
import {
appIconMap,
appNames,
} from '@affine/core/modules/open-in-app/constant';
import { UrlService } from '@affine/core/modules/url';
import { appIconMap, appNames } from '@affine/core/utils';
import { useI18n } from '@affine/i18n';
import { mixpanel } from '@affine/track';
import { ArrowRightSmallIcon, OpenInNewIcon } from '@blocksuite/icons/rc';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { useCallback, useMemo } from 'react';

import { useAppSettingHelper } from '../../../../../components/hooks/affine/use-app-setting-helper';
import { LanguageMenu } from '../../../language-menu';
import { OpenInAppLinksMenu } from './links';
import { settingWrapper } from './style.css';
import { ThemeEditorSetting } from './theme-editor-setting';

Expand Down Expand Up @@ -106,6 +107,17 @@ export const AppearanceSettings = () => {
{enableThemeEditor ? <ThemeEditorSetting /> : null}
</SettingWrapper>

{BUILD_CONFIG.isWeb ? (
<SettingWrapper title={t['com.affine.setting.appearance.links']()}>
<SettingRow
name={t['com.affine.setting.appearance.open-in-app']()}
desc={t['com.affine.setting.appearance.open-in-app.hint']()}
data-testid="open-in-app-links-trigger"
>
<OpenInAppLinksMenu />
</SettingRow>
</SettingWrapper>
) : null}
{BUILD_CONFIG.isElectron ? (
<SettingWrapper
title={t['com.affine.appearanceSettings.sidebar.title']()}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { cssVar } from '@toeverything/theme';
import { style } from '@vanilla-extract/css';

export const menu = style({
background: cssVar('white'),
width: '250px',
maxHeight: '30vh',
overflowY: 'auto',
});

export const menuItem = style({
color: cssVar('textPrimaryColor'),
selectors: {
'&[data-selected=true]': {
color: cssVar('primaryColor'),
},
},
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { Menu, MenuItem, MenuTrigger } from '@affine/component';
import {
OpenInAppService,
OpenLinkMode,
} from '@affine/core/modules/open-in-app';
import { useI18n } from '@affine/i18n';
import { useLiveData, useService } from '@toeverything/infra';
import { useMemo } from 'react';

import * as styles from './links.css';

export const OpenInAppLinksMenu = () => {
const t = useI18n();
const openInAppService = useService(OpenInAppService);
const currentOpenInAppMode = useLiveData(openInAppService.openLinkMode$);

const options = useMemo(
() =>
Object.values(OpenLinkMode).map(mode => ({
label:
t.t(`com.affine.setting.appearance.open-in-app.${mode}`) ||
`com.affine.setting.appearance.open-in-app.${mode}`,
value: mode,
})),
[t]
);

return (
<Menu
items={options.map(option => {
return (
<MenuItem
key={option.value}
title={option.label}
onSelect={() => openInAppService.setOpenLinkMode(option.value)}
data-selected={currentOpenInAppMode === option.value}
>
{option.label}
</MenuItem>
);
})}
contentOptions={{
className: styles.menu,
align: 'end',
}}
>
<MenuTrigger
style={{ textTransform: 'capitalize', fontWeight: 600, width: '250px' }}
block={true}
>
{options.find(option => option.value === currentOpenInAppMode)?.label}
</MenuTrigger>
</Menu>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,14 @@ const UpgradeSuccessLayout = ({
const t = useI18n();
const [params] = useSearchParams();

const { jumpToIndex, openInApp } = useNavigateHelper();
const { jumpToIndex, jumpToOpenInApp } = useNavigateHelper();
const openAffine = useCallback(() => {
if (params.get('scheme')) {
openInApp(params.get('scheme') ?? 'affine', 'bring-to-front');
jumpToOpenInApp('bring-to-front');
} else {
jumpToIndex();
}
}, [jumpToIndex, openInApp, params]);
}, [jumpToIndex, jumpToOpenInApp, params]);

const subtitle = (
<div className={styles.leftContentText}>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import { IsFavoriteIcon } from '@affine/core/components/pure/icons';
import { useDetailPageHeaderResponsive } from '@affine/core/desktop/pages/workspace/detail-page/use-header-responsive';
import { DocInfoService } from '@affine/core/modules/doc-info';
import { EditorService } from '@affine/core/modules/editor';
import { getOpenUrlInDesktopAppLink } from '@affine/core/modules/open-in-app/utils';
import { OpenInAppService } from '@affine/core/modules/open-in-app/services';
import { WorkbenchService } from '@affine/core/modules/workbench';
import { ViewService } from '@affine/core/modules/workbench/services/view';
import { WorkspaceFlavour } from '@affine/env/workspace';
Expand All @@ -52,6 +52,7 @@ import {
FeatureFlagService,
useLiveData,
useService,
useServiceOptional,
WorkspaceService,
} from '@toeverything/infra';
import { useSetAtom } from 'jotai';
Expand Down Expand Up @@ -92,6 +93,7 @@ export const PageHeaderMenuButton = ({
const enableSnapshotImportExport = useLiveData(
featureFlagService.flags.enable_snapshot_import_export.$
);
const openInAppService = useServiceOptional(OpenInAppService);

const { favorite, toggleFavorite } = useFavorite(pageId);

Expand Down Expand Up @@ -265,11 +267,8 @@ export const PageHeaderMenuButton = ({
);

const onOpenInDesktop = useCallback(() => {
const url = getOpenUrlInDesktopAppLink(window.location.href, true);
if (url) {
window.open(url, '_blank');
}
}, []);
openInAppService?.showOpenInAppPage();
}, [openInAppService]);

const EditMenu = (
<>
Expand Down Expand Up @@ -376,7 +375,8 @@ export const PageHeaderMenuButton = ({
data-testid="editor-option-menu-delete"
onSelect={handleOpenTrashModal}
/>
{BUILD_CONFIG.isWeb ? (
{BUILD_CONFIG.isWeb &&
workspace.flavour === WorkspaceFlavour.AFFINE_CLOUD ? (
<MenuItem
prefixIcon={<LocalWorkspaceIcon />}
data-testid="editor-option-menu-link"
Expand Down
19 changes: 13 additions & 6 deletions packages/frontend/core/src/components/hooks/use-navigate-helper.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { toURLSearchParams } from '@affine/core/modules/navigation';
import { getOpenUrlInDesktopAppLink } from '@affine/core/modules/open-in-app';
import type { DocMode } from '@blocksuite/affine/blocks';
import { createContext, useCallback, useContext, useMemo } from 'react';
import type { NavigateFunction, NavigateOptions } from 'react-router-dom';
Expand Down Expand Up @@ -159,10 +160,16 @@ export function useNavigateHelper() {
[navigate]
);

const openInApp = useCallback(
(scheme: string, path: string) => {
const encodedUrl = encodeURIComponent(`${scheme}://${path}`);
return navigate(`/open-app/url?scheme=${scheme}&url=${encodedUrl}`);
const jumpToOpenInApp = useCallback(
(url: string, newTab = true) => {
const deeplink = getOpenUrlInDesktopAppLink(url, newTab);

if (!deeplink) {
return;
}

const encodedUrl = encodeURIComponent(deeplink);
return navigate(`/open-app/url?url=${encodedUrl}`);
},
[navigate]
);
Expand All @@ -189,7 +196,7 @@ export function useNavigateHelper() {
jumpToCollections,
jumpToTags,
jumpToTag,
openInApp,
jumpToOpenInApp,
jumpToImportTemplate,
}),
[
Expand All @@ -204,7 +211,7 @@ export function useNavigateHelper() {
jumpToCollections,
jumpToTags,
jumpToTag,
openInApp,
jumpToOpenInApp,
jumpToImportTemplate,
]
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@ import {
import { useAsyncCallback } from '@affine/core/components/hooks/affine-async-hooks';
import {
AddPageButton,
AppDownloadButton,
AppSidebar,
CategoryDivider,
MenuItem,
MenuLinkItem,
OpenInAppCard,
QuickSearchInput,
SidebarContainer,
SidebarScrollableContainer,
Expand Down Expand Up @@ -190,7 +190,7 @@ export const RootAppSidebar = (): ReactElement => {
</div>
</SidebarScrollableContainer>
<SidebarContainer>
{BUILD_CONFIG.isElectron ? <UpdaterButton /> : <AppDownloadButton />}
{BUILD_CONFIG.isElectron ? <UpdaterButton /> : <OpenInAppCard />}
</SidebarContainer>
</AppSidebar>
);
Expand Down
Loading

0 comments on commit 0f8b273

Please sign in to comment.