From 7fc207d1287356e5df3fb5c7d7ff864497a2a5b7 Mon Sep 17 00:00:00 2001 From: EYHN Date: Fri, 20 Sep 2024 17:04:32 +0800 Subject: [PATCH] refactor(core): desktop project struct --- .../frontend/apps/electron/renderer/app.tsx | 6 - packages/frontend/apps/mobile/src/app.tsx | 2 - packages/frontend/apps/web/src/app.tsx | 6 - .../src/components/global-loading/index.tsx | 30 -- .../setting-modal/account-setting/index.tsx | 15 +- .../affine/subscription-landing/notify.tsx | 41 --- .../core/src/components/atoms/index.ts | 1 - .../hooks/affine/use-subscription-notify.tsx | 83 ------ .../components/layouts/workspace-layout.tsx | 2 - .../components/providers/modal-provider.tsx | 81 +----- .../components/root-app-sidebar/user-info.tsx | 15 +- .../core/src/desktop/dialogs/README.md | 7 + .../dialogs/create-workspace/dialog.css.ts | 77 ++++++ .../dialogs/create-workspace/index.tsx | 259 ++++++++++++++++++ .../dialogs/custom-theme/index.tsx} | 17 +- .../dialogs}/global-loading/index.css.ts | 0 .../desktop/dialogs/global-loading/index.tsx | 28 ++ .../dialogs/import-template}/dialog.css.ts | 0 .../dialogs/import-template/index.tsx} | 14 +- .../core/src/desktop/dialogs/index.tsx | 22 ++ .../dialogs/telemetry/index.tsx} | 0 .../frontend/core/src/desktop/pages/404.tsx | 75 ----- .../core/src/desktop/pages/404/index.tsx | 58 ++++ .../src/desktop/pages/ai-upgrade-success.tsx | 5 - .../pages/ai-upgrade-success}/index.tsx | 47 +--- .../pages/ai-upgrade-success/styles.css.ts | 15 + .../pages/{expired.tsx => expired/index.tsx} | 7 +- .../index.tsx} | 9 +- .../src/desktop/pages/{ => index}/index.tsx | 32 ++- .../pages/{invite.tsx => invite/index.tsx} | 13 +- .../core/src/desktop/pages/onboarding.tsx | 42 --- .../src/desktop/pages/onboarding/index.tsx | 30 ++ .../{open-app.tsx => open-app/index.tsx} | 5 + .../pages/{ => open-app}/open-app.css.ts | 0 .../{redirect.tsx => redirect/index.tsx} | 5 + .../frontend/core/src/desktop/pages/root.tsx | 4 +- .../{subscribe.tsx => subscribe/index.tsx} | 9 +- .../pages/{ => subscribe}/subscribe.css.ts | 0 .../core/src/desktop/pages/theme-editor.tsx | 5 - .../theme-editor}/components/color-cell.tsx | 0 .../pages/theme-editor}/components/empty.tsx | 0 .../components/simple-color-picker.css.ts | 0 .../components/simple-color-picker.tsx | 0 .../components/string-cell.css.ts | 0 .../theme-editor}/components/string-cell.tsx | 0 .../theme-editor}/components/tree-node.tsx | 0 .../components/variable-list.tsx | 2 +- .../src/desktop/pages/theme-editor/index.tsx | 8 + .../pages/theme-editor}/resource.ts | 0 .../pages/theme-editor}/theme-editor.css.ts | 0 .../pages/theme-editor}/theme-editor.tsx | 2 +- .../pages/theme-editor}/utils.ts | 0 .../src/desktop/pages/upgrade-success.tsx | 5 - .../desktop/pages/upgrade-success/index.tsx | 57 ++++ .../pages/upgrade-success/styles.css.ts | 15 + .../src/mobile/provider/model-provider.tsx | 2 - .../core/src/modules/import-template/index.ts | 3 +- .../core/src/modules/theme-editor/index.ts | 2 - 58 files changed, 672 insertions(+), 491 deletions(-) create mode 100644 packages/frontend/core/src/desktop/dialogs/README.md create mode 100644 packages/frontend/core/src/desktop/dialogs/create-workspace/dialog.css.ts create mode 100644 packages/frontend/core/src/desktop/dialogs/create-workspace/index.tsx rename packages/frontend/core/src/{modules/theme-editor/views/custom-theme.tsx => desktop/dialogs/custom-theme/index.tsx} (75%) rename packages/frontend/{component/src/components => core/src/desktop/dialogs}/global-loading/index.css.ts (100%) create mode 100644 packages/frontend/core/src/desktop/dialogs/global-loading/index.tsx rename packages/frontend/core/src/{modules/import-template/views => desktop/dialogs/import-template}/dialog.css.ts (100%) rename packages/frontend/core/src/{modules/import-template/views/dialog.tsx => desktop/dialogs/import-template/index.tsx} (95%) create mode 100644 packages/frontend/core/src/desktop/dialogs/index.tsx rename packages/frontend/core/src/{components/telemetry.tsx => desktop/dialogs/telemetry/index.tsx} (100%) delete mode 100644 packages/frontend/core/src/desktop/pages/404.tsx create mode 100644 packages/frontend/core/src/desktop/pages/404/index.tsx delete mode 100644 packages/frontend/core/src/desktop/pages/ai-upgrade-success.tsx rename packages/frontend/core/src/{components/affine/subscription-landing => desktop/pages/ai-upgrade-success}/index.tsx (58%) create mode 100644 packages/frontend/core/src/desktop/pages/ai-upgrade-success/styles.css.ts rename packages/frontend/core/src/desktop/pages/{expired.tsx => expired/index.tsx} (88%) rename packages/frontend/core/src/desktop/pages/{import-template.tsx => import-template/index.tsx} (75%) rename packages/frontend/core/src/desktop/pages/{ => index}/index.tsx (88%) rename packages/frontend/core/src/desktop/pages/{invite.tsx => invite/index.tsx} (90%) delete mode 100644 packages/frontend/core/src/desktop/pages/onboarding.tsx create mode 100644 packages/frontend/core/src/desktop/pages/onboarding/index.tsx rename packages/frontend/core/src/desktop/pages/{open-app.tsx => open-app/index.tsx} (99%) rename packages/frontend/core/src/desktop/pages/{ => open-app}/open-app.css.ts (100%) rename packages/frontend/core/src/desktop/pages/{redirect.tsx => redirect/index.tsx} (95%) rename packages/frontend/core/src/desktop/pages/{subscribe.tsx => subscribe/index.tsx} (95%) rename packages/frontend/core/src/desktop/pages/{ => subscribe}/subscribe.css.ts (100%) delete mode 100644 packages/frontend/core/src/desktop/pages/theme-editor.tsx rename packages/frontend/core/src/{modules/theme-editor/views => desktop/pages/theme-editor}/components/color-cell.tsx (100%) rename packages/frontend/core/src/{modules/theme-editor/views => desktop/pages/theme-editor}/components/empty.tsx (100%) rename packages/frontend/core/src/{modules/theme-editor/views => desktop/pages/theme-editor}/components/simple-color-picker.css.ts (100%) rename packages/frontend/core/src/{modules/theme-editor/views => desktop/pages/theme-editor}/components/simple-color-picker.tsx (100%) rename packages/frontend/core/src/{modules/theme-editor/views => desktop/pages/theme-editor}/components/string-cell.css.ts (100%) rename packages/frontend/core/src/{modules/theme-editor/views => desktop/pages/theme-editor}/components/string-cell.tsx (100%) rename packages/frontend/core/src/{modules/theme-editor/views => desktop/pages/theme-editor}/components/tree-node.tsx (100%) rename packages/frontend/core/src/{modules/theme-editor/views => desktop/pages/theme-editor}/components/variable-list.tsx (96%) create mode 100644 packages/frontend/core/src/desktop/pages/theme-editor/index.tsx rename packages/frontend/core/src/{modules/theme-editor/views => desktop/pages/theme-editor}/resource.ts (100%) rename packages/frontend/core/src/{modules/theme-editor/views => desktop/pages/theme-editor}/theme-editor.css.ts (100%) rename packages/frontend/core/src/{modules/theme-editor/views => desktop/pages/theme-editor}/theme-editor.tsx (97%) rename packages/frontend/core/src/{modules/theme-editor/views => desktop/pages/theme-editor}/utils.ts (100%) delete mode 100644 packages/frontend/core/src/desktop/pages/upgrade-success.tsx create mode 100644 packages/frontend/core/src/desktop/pages/upgrade-success/index.tsx create mode 100644 packages/frontend/core/src/desktop/pages/upgrade-success/styles.css.ts diff --git a/packages/frontend/apps/electron/renderer/app.tsx b/packages/frontend/apps/electron/renderer/app.tsx index a78f8f67dff17..6a9a9a7a1c71a 100644 --- a/packages/frontend/apps/electron/renderer/app.tsx +++ b/packages/frontend/apps/electron/renderer/app.tsx @@ -1,13 +1,10 @@ import { AffineContext } from '@affine/component/context'; -import { GlobalLoading } from '@affine/component/global-loading'; import { AppFallback } from '@affine/core/components/affine/app-container'; import { WindowsAppControls } from '@affine/core/components/pure/header/windows-app-controls'; -import { Telemetry } from '@affine/core/components/telemetry'; import { router } from '@affine/core/desktop/router'; import { configureCommonModules } from '@affine/core/modules'; import { configureAppTabsHeaderModule } from '@affine/core/modules/app-tabs-header'; import { configureElectronStateStorageImpls } from '@affine/core/modules/storage'; -import { CustomThemeModifier } from '@affine/core/modules/theme-editor'; import { configureSqliteUserspaceStorageProvider } from '@affine/core/modules/userspace'; import { configureDesktopWorkbenchModule } from '@affine/core/modules/workbench'; import { @@ -85,9 +82,6 @@ export function App() { - - - } router={router} diff --git a/packages/frontend/apps/mobile/src/app.tsx b/packages/frontend/apps/mobile/src/app.tsx index 6c5b3a707d5cb..793d7ea064e59 100644 --- a/packages/frontend/apps/mobile/src/app.tsx +++ b/packages/frontend/apps/mobile/src/app.tsx @@ -1,6 +1,5 @@ import { AffineContext } from '@affine/component/context'; import { AppFallback } from '@affine/core/components/affine/app-container'; -import { Telemetry } from '@affine/core/components/telemetry'; import { configureMobileModules } from '@affine/core/mobile/modules'; import { router } from '@affine/core/mobile/router'; import { configureCommonModules } from '@affine/core/modules'; @@ -59,7 +58,6 @@ export function App() { - } router={router} diff --git a/packages/frontend/apps/web/src/app.tsx b/packages/frontend/apps/web/src/app.tsx index 53d4c4fa75c1b..2312f187cdb96 100644 --- a/packages/frontend/apps/web/src/app.tsx +++ b/packages/frontend/apps/web/src/app.tsx @@ -1,11 +1,8 @@ import { AffineContext } from '@affine/component/context'; -import { GlobalLoading } from '@affine/component/global-loading'; import { AppFallback } from '@affine/core/components/affine/app-container'; -import { Telemetry } from '@affine/core/components/telemetry'; import { router } from '@affine/core/desktop/router'; import { configureCommonModules } from '@affine/core/modules'; import { configureLocalStorageStateStorageImpls } from '@affine/core/modules/storage'; -import { CustomThemeModifier } from '@affine/core/modules/theme-editor'; import { configureIndexedDBUserspaceStorageProvider } from '@affine/core/modules/userspace'; import { configureBrowserWorkbenchModule } from '@affine/core/modules/workbench'; import { @@ -64,9 +61,6 @@ export function App() { - - - } router={router} diff --git a/packages/frontend/component/src/components/global-loading/index.tsx b/packages/frontend/component/src/components/global-loading/index.tsx index a2053651290fb..7d4f53d12be5e 100644 --- a/packages/frontend/component/src/components/global-loading/index.tsx +++ b/packages/frontend/component/src/components/global-loading/index.tsx @@ -1,35 +1,5 @@ -import { useAtomValue } from 'jotai'; -import type { ReactNode } from 'react'; -import { useEffect, useState } from 'react'; - -import { Loading } from '../../ui/loading'; -import * as styles from './index.css'; -import { globalLoadingEventsAtom } from './index.jotai'; - export { type GlobalLoadingEvent, pushGlobalLoadingEventAtom, resolveGlobalLoadingEventAtom, } from './index.jotai'; - -export function GlobalLoading(): ReactNode { - const globalLoadingEvents = useAtomValue(globalLoadingEventsAtom); - const [loading, setLoading] = useState(false); - - useEffect(() => { - if (globalLoadingEvents.length) { - setLoading(true); - } else { - setLoading(false); - } - }, [globalLoadingEvents]); - - if (!globalLoadingEvents.length) { - return null; - } - return ( -
- -
- ); -} diff --git a/packages/frontend/core/src/components/affine/setting-modal/account-setting/index.tsx b/packages/frontend/core/src/components/affine/setting-modal/account-setting/index.tsx index 31d59be2eb7e2..1196669cac75a 100644 --- a/packages/frontend/core/src/components/affine/setting-modal/account-setting/index.tsx +++ b/packages/frontend/core/src/components/affine/setting-modal/account-setting/index.tsx @@ -5,6 +5,7 @@ import { } from '@affine/component/setting-components'; import { Avatar } from '@affine/component/ui/avatar'; import { Button } from '@affine/component/ui/button'; +import { useSignOut } from '@affine/core/components/hooks/affine/use-sign-out'; import { useAsyncCallback } from '@affine/core/components/hooks/affine-async-hooks'; import { useCatchEventCallback } from '@affine/core/components/hooks/use-catch-event-hook'; import { SubscriptionPlan } from '@affine/graphql'; @@ -22,11 +23,7 @@ import type { FC } from 'react'; import { useCallback, useEffect, useState } from 'react'; import { AuthService, ServerConfigService } from '../../../../modules/cloud'; -import { - authAtom, - openSettingModalAtom, - openSignOutModalAtom, -} from '../../../atoms'; +import { authAtom, openSettingModalAtom } from '../../../atoms'; import { Upload } from '../../../pure/file-upload'; import { AIUsagePanel } from './ai-usage-panel'; import { StorageProgress } from './storage-progress'; @@ -189,7 +186,7 @@ export const AccountSetting: FC = () => { }, [session]); const account = useEnsureLiveData(session.account$); const setAuthModal = useSetAtom(authAtom); - const setSignOutModal = useSetAtom(openSignOutModalAtom); + const openSignOutModal = useSignOut(); const onChangeEmail = useCallback(() => { setAuthModal({ @@ -211,10 +208,6 @@ export const AccountSetting: FC = () => { }); }, [account.email, account.info?.hasPassword, setAuthModal]); - const onOpenSignOutModal = useCallback(() => { - setSignOutModal(true); - }, [setSignOutModal]); - return ( <> { desc={t['com.affine.setting.sign.out.message']()} style={{ cursor: 'pointer' }} data-testid="sign-out-button" - onClick={onOpenSignOutModal} + onClick={openSignOutModal} > diff --git a/packages/frontend/core/src/components/affine/subscription-landing/notify.tsx b/packages/frontend/core/src/components/affine/subscription-landing/notify.tsx index 4f3d990400ac0..973349aff20e2 100644 --- a/packages/frontend/core/src/components/affine/subscription-landing/notify.tsx +++ b/packages/frontend/core/src/components/affine/subscription-landing/notify.tsx @@ -49,47 +49,6 @@ const SubscriptionChangedNotifyFooter = ({ ); }; -export const useUpgradeNotify = () => { - const t = useI18n(); - const prevNotifyIdRef = useRef(null); - - return useCallback( - (link: string) => { - prevNotifyIdRef.current && notify.dismiss(prevNotifyIdRef.current); - const id = notify( - { - title: ( - - {t['com.affine.payment.upgrade-success-notify.title']()} - - ), - message: t['com.affine.payment.upgrade-success-notify.content'](), - alignMessage: 'title', - icon: null, - footer: ( - notify.dismiss(id)} - onConfirm={() => notify.dismiss(id)} - /> - ), - }, - { duration: 24 * 60 * 60 * 1000 } - ); - prevNotifyIdRef.current = id; - }, - [t] - ); -}; - export const useDowngradeNotify = () => { const t = useI18n(); const prevNotifyIdRef = useRef(null); diff --git a/packages/frontend/core/src/components/atoms/index.ts b/packages/frontend/core/src/components/atoms/index.ts index c519e959ffca2..43e8627aa45ca 100644 --- a/packages/frontend/core/src/components/atoms/index.ts +++ b/packages/frontend/core/src/components/atoms/index.ts @@ -7,7 +7,6 @@ export const openWorkspacesModalAtom = atom(false); /** * @deprecated use `useSignOut` hook instated */ -export const openSignOutModalAtom = atom(false); export const openQuotaModalAtom = atom(false); export const openStarAFFiNEModalAtom = atom(false); export const openIssueFeedbackModalAtom = atom(false); diff --git a/packages/frontend/core/src/components/hooks/affine/use-subscription-notify.tsx b/packages/frontend/core/src/components/hooks/affine/use-subscription-notify.tsx index e4e272db42fc3..5c01cc724c526 100644 --- a/packages/frontend/core/src/components/hooks/affine/use-subscription-notify.tsx +++ b/packages/frontend/core/src/components/hooks/affine/use-subscription-notify.tsx @@ -1,16 +1,10 @@ -import { useUpgradeNotify } from '@affine/core/components/affine/subscription-landing/notify'; import { SubscriptionPlan, SubscriptionRecurring } from '@affine/graphql'; -import { track } from '@affine/track'; import { nanoid } from 'nanoid'; -import { useCallback, useEffect } from 'react'; -import { useSearchParams } from 'react-router-dom'; import { type AuthAccountInfo } from '../../../modules/cloud'; const separator = '::'; const recoverSeparator = nanoid(); -const localStorageKey = 'subscription-succeed-info'; - const typeFormUrl = 'https://6dxre9ihosp.typeform.com/to'; const typeFormUpgradeId = 'mUMGGQS8'; const typeFormDowngradeId = 'RvD9AoRg'; @@ -69,80 +63,3 @@ export const generateSubscriptionCallbackLink = ( return `${baseUrl}?info=${encodeURIComponent(query)}`; }; - -/** - * Parse subscription callback query.info - * @returns - */ -export const parseSubscriptionCallbackLink = (query: string) => { - const [plan, recurring, id, email, rawName] = - decodeURIComponent(query).split(separator); - const name = rawName.replaceAll(recoverSeparator, separator); - - return { - plan: plan as SubscriptionPlan, - recurring: recurring as SubscriptionRecurring, - account: { - id, - email, - info: { - name, - }, - }, - }; -}; - -/** - * Hook to parse subscription callback link, and save to local storage and delete the query - */ -export const useSubscriptionNotifyWriter = () => { - const [searchParams] = useSearchParams(); - - useEffect(() => { - const query = searchParams.get('info'); - if (query) { - localStorage.setItem(localStorageKey, query); - searchParams.delete('info'); - } - }, [searchParams]); -}; - -/** - * Hook to read and parse subscription info from localStorage - */ -export const useSubscriptionNotifyReader = () => { - const upgradeNotify = useUpgradeNotify(); - - const readAndNotify = useCallback(() => { - const query = localStorage.getItem(localStorageKey); - if (!query) return; - - try { - const { plan, recurring, account } = parseSubscriptionCallbackLink(query); - const link = getUpgradeQuestionnaireLink({ - id: account.id, - email: account.email, - name: account.info?.name ?? '', - plan, - recurring, - }); - upgradeNotify(link); - localStorage.removeItem(localStorageKey); - - track.$.settingsPanel.plans.subscribe({ - plan, - recurring, - }); - } catch (err) { - console.error('Failed to parse subscription callback link', err); - } - }, [upgradeNotify]); - - useEffect(() => { - readAndNotify(); - window.addEventListener('focus', readAndNotify); - return () => { - window.removeEventListener('focus', readAndNotify); - }; - }, [readAndNotify]); -}; diff --git a/packages/frontend/core/src/components/layouts/workspace-layout.tsx b/packages/frontend/core/src/components/layouts/workspace-layout.tsx index 751700810c923..30a3e4c4281f5 100644 --- a/packages/frontend/core/src/components/layouts/workspace-layout.tsx +++ b/packages/frontend/core/src/components/layouts/workspace-layout.tsx @@ -42,7 +42,6 @@ import { AppContainer } from '../affine/app-container'; import { SyncAwareness } from '../affine/awareness'; import { appSidebarResizingAtom, SidebarSwitch } from '../app-sidebar'; import { useRegisterFindInPageCommands } from '../hooks/affine/use-register-find-in-page-commands'; -import { useSubscriptionNotifyReader } from '../hooks/affine/use-subscription-notify'; import { useRegisterWorkspaceCommands } from '../hooks/use-register-workspace-commands'; import { OverCapacityNotification } from '../over-capacity'; import { CurrentWorkspaceModals } from '../providers/modal-provider'; @@ -141,7 +140,6 @@ export const WorkspaceLayoutProviders = ({ children }: PropsWithChildren) => { workbench, ]); - useSubscriptionNotifyReader(); useRegisterWorkspaceCommands(); useRegisterNavigationCommands(); useRegisterFindInPageCommands(); diff --git a/packages/frontend/core/src/components/providers/modal-provider.tsx b/packages/frontend/core/src/components/providers/modal-provider.tsx index 83a26951527c8..158a3130cc5f4 100644 --- a/packages/frontend/core/src/components/providers/modal-provider.tsx +++ b/packages/frontend/core/src/components/providers/modal-provider.tsx @@ -1,23 +1,11 @@ -import { NotificationCenter, notify } from '@affine/component'; import { events } from '@affine/electron-api'; import { WorkspaceFlavour } from '@affine/env/workspace'; -import { - GlobalContextService, - useLiveData, - useService, - WorkspaceService, - WorkspacesService, -} from '@toeverything/infra'; +import { useService, WorkspaceService } from '@toeverything/infra'; import { useAtom } from 'jotai'; -import type { ReactElement } from 'react'; import { useCallback, useEffect } from 'react'; -import { AuthService } from '../../modules/cloud/services/auth'; -import { CreateWorkspaceDialogProvider } from '../../modules/create-workspace'; import { FindInPageModal } from '../../modules/find-in-page/view/find-in-page-modal'; -import { ImportTemplateDialogProvider } from '../../modules/import-template'; import { PeekViewManagerModal } from '../../modules/peek-view'; -import { AuthModal } from '../affine/auth'; import { AiLoginRequiredModal } from '../affine/auth/ai-login-required'; import { HistoryTipsModal } from '../affine/history-tips-modal'; import { IssueFeedbackModal } from '../affine/issue-feedback-modal'; @@ -26,13 +14,10 @@ import { LocalQuotaModal, } from '../affine/quota-reached-modal'; import { SettingModal } from '../affine/setting-modal'; -import { SignOutModal } from '../affine/sign-out-modal'; import { StarAFFiNEModal } from '../affine/star-affine-modal'; import type { SettingAtom } from '../atoms'; -import { openSettingModalAtom, openSignOutModalAtom } from '../atoms'; +import { openSettingModalAtom } from '../atoms'; import { useTrashModalHelper } from '../hooks/affine/use-trash-modal-helper'; -import { useAsyncCallback } from '../hooks/affine-async-hooks'; -import { useNavigateHelper } from '../hooks/use-navigate-helper'; import { MoveToTrash } from '../page-list'; export const Setting = () => { @@ -129,65 +114,3 @@ export function CurrentWorkspaceModals() { ); } - -export const SignOutConfirmModal = () => { - const { openPage } = useNavigateHelper(); - const authService = useService(AuthService); - const [open, setOpen] = useAtom(openSignOutModalAtom); - const globalContextService = useService(GlobalContextService); - const currentWorkspaceId = useLiveData( - globalContextService.globalContext.workspaceId.$ - ); - const workspacesService = useService(WorkspacesService); - const workspaces = useLiveData(workspacesService.list.workspaces$); - const currentWorkspaceMetadata = useLiveData( - currentWorkspaceId - ? workspacesService.list.workspace$(currentWorkspaceId) - : undefined - ); - - const onConfirm = useAsyncCallback(async () => { - setOpen(false); - try { - await authService.signOut(); - } catch (err) { - console.error(err); - // TODO(@eyhn): i18n - notify.error({ - title: 'Failed to sign out', - }); - } - - // if current workspace is affine cloud, switch to local workspace - if (currentWorkspaceMetadata?.flavour === WorkspaceFlavour.AFFINE_CLOUD) { - const localWorkspace = workspaces.find( - w => w.flavour === WorkspaceFlavour.LOCAL - ); - if (localWorkspace) { - openPage(localWorkspace.id, 'all'); - } - } - }, [ - authService, - currentWorkspaceMetadata?.flavour, - openPage, - setOpen, - workspaces, - ]); - - return ( - - ); -}; - -export const AllWorkspaceModals = (): ReactElement => { - return ( - <> - - - - - - - ); -}; diff --git a/packages/frontend/core/src/components/root-app-sidebar/user-info.tsx b/packages/frontend/core/src/components/root-app-sidebar/user-info.tsx index 7e41ed591170a..e0e3cfa911668 100644 --- a/packages/frontend/core/src/components/root-app-sidebar/user-info.tsx +++ b/packages/frontend/core/src/components/root-app-sidebar/user-info.tsx @@ -8,11 +8,7 @@ import { type MenuProps, Skeleton, } from '@affine/component'; -import { - authAtom, - openSettingModalAtom, - openSignOutModalAtom, -} from '@affine/core/components/atoms'; +import { authAtom, openSettingModalAtom } from '@affine/core/components/atoms'; import { useI18n } from '@affine/i18n'; import { track } from '@affine/track'; import { AccountIcon, SignOutIcon } from '@blocksuite/icons/rc'; @@ -32,6 +28,7 @@ import { UserQuotaService, } from '../../modules/cloud'; import { UserPlanButton } from '../affine/auth/user-plan-button'; +import { useSignOut } from '../hooks/affine/use-sign-out'; import * as styles from './index.css'; import { UnknownUserIcon } from './unknow-user'; @@ -79,7 +76,7 @@ const UnauthorizedUserInfo = () => { const AccountMenu = () => { const setSettingModalAtom = useSetAtom(openSettingModalAtom); - const setOpenSignOutModalAtom = useSetAtom(openSignOutModalAtom); + const openSignOutModal = useSignOut(); const onOpenAccountSetting = useCallback(() => { track.$.navigationPanel.profileAndBadge.openSettings({ to: 'account' }); @@ -90,10 +87,6 @@ const AccountMenu = () => { })); }, [setSettingModalAtom]); - const onOpenSignOutModal = useCallback(() => { - setOpenSignOutModalAtom(true); - }, [setOpenSignOutModalAtom]); - const t = useI18n(); return ( @@ -108,7 +101,7 @@ const AccountMenu = () => { } data-testid="workspace-modal-sign-out-option" - onClick={onOpenSignOutModal} + onClick={openSignOutModal} > {t['com.affine.workspace.cloud.account.logout']()} diff --git a/packages/frontend/core/src/desktop/dialogs/README.md b/packages/frontend/core/src/desktop/dialogs/README.md new file mode 100644 index 0000000000000..e8cc663434996 --- /dev/null +++ b/packages/frontend/core/src/desktop/dialogs/README.md @@ -0,0 +1,7 @@ +# dialogs + +This folder contains all the dialogs in the app, such as "sign in/out dialog", "create workspace dialog", etc. + + in `index.tsx` will mount all dialogs. Whether each dialog is displayed or not is controlled by the global service. + +Some components that need to be mounted all the time will also be placed in this folder, but this is not a best practice and should be properly handled in the future. diff --git a/packages/frontend/core/src/desktop/dialogs/create-workspace/dialog.css.ts b/packages/frontend/core/src/desktop/dialogs/create-workspace/dialog.css.ts new file mode 100644 index 0000000000000..5b0ede3fe8cc2 --- /dev/null +++ b/packages/frontend/core/src/desktop/dialogs/create-workspace/dialog.css.ts @@ -0,0 +1,77 @@ +import { cssVar } from '@toeverything/theme'; +import { style } from '@vanilla-extract/css'; + +export const header = style({ + position: 'relative', + marginTop: '44px', +}); + +export const subTitle = style({ + fontSize: cssVar('fontSm'), + color: cssVar('textPrimaryColor'), + fontWeight: 600, +}); + +export const avatarWrapper = style({ + display: 'flex', + margin: '10px 0', +}); + +export const workspaceNameWrapper = style({ + display: 'flex', + flexDirection: 'column', + gap: '8px', + padding: '12px 0', +}); +export const affineCloudWrapper = style({ + display: 'flex', + flexDirection: 'column', + gap: '6px', + paddingTop: '10px', +}); + +export const card = style({ + padding: '12px', + display: 'flex', + alignItems: 'center', + borderRadius: '8px', + backgroundColor: cssVar('backgroundSecondaryColor'), + minHeight: '114px', + position: 'relative', +}); + +export const cardText = style({ + display: 'flex', + flexDirection: 'column', + justifyContent: 'center', + width: '100%', + gap: '12px', +}); + +export const cardTitle = style({ + fontSize: cssVar('fontBase'), + color: cssVar('textPrimaryColor'), + display: 'flex', + justifyContent: 'space-between', +}); +export const cardDescription = style({ + fontSize: cssVar('fontXs'), + color: cssVar('textSecondaryColor'), + maxWidth: '288px', +}); + +export const cloudTips = style({ + fontSize: cssVar('fontXs'), + color: cssVar('textSecondaryColor'), +}); + +export const cloudSvgContainer = style({ + width: '146px', + display: 'flex', + justifyContent: 'flex-end', + alignItems: 'center', + position: 'absolute', + bottom: '0', + right: '0', + pointerEvents: 'none', +}); diff --git a/packages/frontend/core/src/desktop/dialogs/create-workspace/index.tsx b/packages/frontend/core/src/desktop/dialogs/create-workspace/index.tsx new file mode 100644 index 0000000000000..1bd354ebe3cc9 --- /dev/null +++ b/packages/frontend/core/src/desktop/dialogs/create-workspace/index.tsx @@ -0,0 +1,259 @@ +import { Avatar, ConfirmModal, Input, Switch, toast } from '@affine/component'; +import type { ConfirmModalProps } from '@affine/component/ui/modal'; +import { CloudSvg } from '@affine/core/components/affine/share-page-modal/cloud-svg'; +import { authAtom } from '@affine/core/components/atoms'; +import { useAsyncCallback } from '@affine/core/components/hooks/affine-async-hooks'; +import { AuthService } from '@affine/core/modules/cloud'; +import { CreateWorkspaceDialogService } from '@affine/core/modules/create-workspace'; +import { _addLocalWorkspace } from '@affine/core/modules/workspace-engine'; +import { DebugLogger } from '@affine/debug'; +import { apis } from '@affine/electron-api'; +import { WorkspaceFlavour } from '@affine/env/workspace'; +import { useI18n } from '@affine/i18n'; +import { track } from '@affine/track'; +import { + FeatureFlagService, + useLiveData, + useService, + WorkspacesService, +} from '@toeverything/infra'; +import { useSetAtom } from 'jotai'; +import type { KeyboardEvent } from 'react'; +import { useCallback, useLayoutEffect, useState } from 'react'; + +import { buildShowcaseWorkspace } from '../../../utils/first-app-data'; +import * as styles from './dialog.css'; + +const logger = new DebugLogger('CreateWorkspaceModal'); + +interface NameWorkspaceContentProps extends ConfirmModalProps { + loading: boolean; + onConfirmName: ( + name: string, + workspaceFlavour: WorkspaceFlavour, + avatar?: File + ) => void; +} + +const NameWorkspaceContent = ({ + loading, + onConfirmName, + ...props +}: NameWorkspaceContentProps) => { + const t = useI18n(); + const [workspaceName, setWorkspaceName] = useState(''); + const featureFlagService = useService(FeatureFlagService); + const enableLocalWorkspace = useLiveData( + featureFlagService.flags.enable_local_workspace.$ + ); + const [enable, setEnable] = useState(!enableLocalWorkspace); + const session = useService(AuthService).session; + const loginStatus = useLiveData(session.status$); + + const setOpenSignIn = useSetAtom(authAtom); + + const openSignInModal = useCallback(() => { + setOpenSignIn(state => ({ + ...state, + openModal: true, + })); + }, [setOpenSignIn]); + + const onSwitchChange = useCallback( + (checked: boolean) => { + if (loginStatus !== 'authenticated') { + return openSignInModal(); + } + return setEnable(checked); + }, + [loginStatus, openSignInModal] + ); + + const handleCreateWorkspace = useCallback(() => { + onConfirmName( + workspaceName, + enable ? WorkspaceFlavour.AFFINE_CLOUD : WorkspaceFlavour.LOCAL + ); + }, [enable, onConfirmName, workspaceName]); + + const handleKeyDown = useCallback( + (event: KeyboardEvent) => { + if (event.key === 'Enter' && workspaceName) { + handleCreateWorkspace(); + } + }, + [handleCreateWorkspace, workspaceName] + ); + + // Currently, when we create a new workspace and upload an avatar at the same time, + // an error occurs after the creation is successful: get blob 404 not found + return ( + +
+ +
+ +
+
+ {t['com.affine.nameWorkspace.subtitle.workspace-name']()} +
+ +
+
+
{t['AFFiNE Cloud']()}
+
+
+
+ {t['com.affine.nameWorkspace.affine-cloud.title']()} + +
+
+ {t['com.affine.nameWorkspace.affine-cloud.description']()} +
+
+
+ +
+
+ {!enableLocalWorkspace ? ( + + {t['com.affine.nameWorkspace.affine-cloud.web-tips']()} + + ) : null} +
+
+ ); +}; + +const CreateWorkspaceDialogInner = () => { + const createWorkspaceDialogService = useService(CreateWorkspaceDialogService); + const mode = useLiveData(createWorkspaceDialogService.dialog.mode$); + const t = useI18n(); + const workspacesService = useService(WorkspacesService); + const [loading, setLoading] = useState(false); + + // TODO(@Peng): maybe refactor using xstate? + useLayoutEffect(() => { + let canceled = false; + // if mode changed, reset step + if (mode === 'add') { + // a hack for now + // when adding a workspace, we will immediately let user select a db file + // after it is done, it will effectively add a new workspace to app-data folder + // so after that, we will be able to load it via importLocalWorkspace + (async () => { + if (!apis) { + return; + } + logger.info('load db file'); + const result = await apis.dialog.loadDBFile(); + if (result.workspaceId && !canceled) { + _addLocalWorkspace(result.workspaceId); + workspacesService.list.revalidate(); + createWorkspaceDialogService.dialog.callback({ + meta: { + flavour: WorkspaceFlavour.LOCAL, + id: result.workspaceId, + }, + }); + } else if (result.error || result.canceled) { + if (result.error) { + toast(t[result.error]()); + } + createWorkspaceDialogService.dialog.callback(undefined); + createWorkspaceDialogService.dialog.close(); + } + })().catch(err => { + console.error(err); + }); + } + return () => { + canceled = true; + }; + }, [createWorkspaceDialogService, mode, t, workspacesService]); + + const onConfirmName = useAsyncCallback( + async (name: string, workspaceFlavour: WorkspaceFlavour) => { + track.$.$.$.createWorkspace({ flavour: workspaceFlavour }); + if (loading) return; + setLoading(true); + + // this will be the last step for web for now + // fix me later + const { meta, defaultDocId } = await buildShowcaseWorkspace( + workspacesService, + workspaceFlavour, + name + ); + createWorkspaceDialogService.dialog.callback({ meta, defaultDocId }); + + createWorkspaceDialogService.dialog.close(); + setLoading(false); + }, + [createWorkspaceDialogService.dialog, loading, workspacesService] + ); + + const onOpenChange = useCallback( + (open: boolean) => { + if (!open) { + createWorkspaceDialogService.dialog.close(); + } + }, + [createWorkspaceDialogService] + ); + + if (mode === 'new') { + return ( + + ); + } else { + return null; + } +}; + +export const CreateWorkspaceDialog = () => { + const createWorkspaceDialogService = useService(CreateWorkspaceDialogService); + const isOpen = useLiveData(createWorkspaceDialogService.dialog.isOpen$); + + return isOpen ? : null; +}; diff --git a/packages/frontend/core/src/modules/theme-editor/views/custom-theme.tsx b/packages/frontend/core/src/desktop/dialogs/custom-theme/index.tsx similarity index 75% rename from packages/frontend/core/src/modules/theme-editor/views/custom-theme.tsx rename to packages/frontend/core/src/desktop/dialogs/custom-theme/index.tsx index c00ade73e2bec..9451ae7ff9a62 100644 --- a/packages/frontend/core/src/modules/theme-editor/views/custom-theme.tsx +++ b/packages/frontend/core/src/desktop/dialogs/custom-theme/index.tsx @@ -1,3 +1,4 @@ +import { ThemeEditorService } from '@affine/core/modules/theme-editor'; import { FeatureFlagService, useLiveData, @@ -6,11 +7,9 @@ import { import { useTheme } from 'next-themes'; import { useEffect } from 'react'; -import { ThemeEditorService } from '../services/theme-editor'; - let _provided = false; -export const useCustomTheme = (target: HTMLElement) => { +export const CustomThemeModifier = () => { const { themeEditorService, featureFlagService } = useServices({ ThemeEditorService, FeatureFlagService, @@ -34,12 +33,12 @@ export const useCustomTheme = (target: HTMLElement) => { // remove previous style // TOOD(@CatsJuice): find better way to remove previous style - target.style.cssText = ''; + document.documentElement.style.cssText = ''; // recover color scheme set by next-themes - target.style.colorScheme = mode; + document.documentElement.style.colorScheme = mode; Object.entries(valueMap).forEach(([key, value]) => { - value && target.style.setProperty(key, value); + value && document.documentElement.style.setProperty(key, value); }); }); @@ -47,11 +46,7 @@ export const useCustomTheme = (target: HTMLElement) => { _provided = false; sub.unsubscribe(); }; - }, [resolvedTheme, target.style, enableThemeEditor, themeEditorService]); -}; - -export const CustomThemeModifier = () => { - useCustomTheme(document.documentElement); + }, [resolvedTheme, enableThemeEditor, themeEditorService]); return null; }; diff --git a/packages/frontend/component/src/components/global-loading/index.css.ts b/packages/frontend/core/src/desktop/dialogs/global-loading/index.css.ts similarity index 100% rename from packages/frontend/component/src/components/global-loading/index.css.ts rename to packages/frontend/core/src/desktop/dialogs/global-loading/index.css.ts diff --git a/packages/frontend/core/src/desktop/dialogs/global-loading/index.tsx b/packages/frontend/core/src/desktop/dialogs/global-loading/index.tsx new file mode 100644 index 0000000000000..8e11e1d31c6f5 --- /dev/null +++ b/packages/frontend/core/src/desktop/dialogs/global-loading/index.tsx @@ -0,0 +1,28 @@ +import { Loading } from '@affine/component'; +import { globalLoadingEventsAtom } from '@affine/component/global-loading/index.jotai'; +import { useAtomValue } from 'jotai'; +import { type ReactNode, useEffect, useState } from 'react'; + +import * as styles from './index.css'; + +export function GlobalLoading(): ReactNode { + const globalLoadingEvents = useAtomValue(globalLoadingEventsAtom); + const [loading, setLoading] = useState(false); + + useEffect(() => { + if (globalLoadingEvents.length) { + setLoading(true); + } else { + setLoading(false); + } + }, [globalLoadingEvents]); + + if (!globalLoadingEvents.length) { + return null; + } + return ( +
+ +
+ ); +} diff --git a/packages/frontend/core/src/modules/import-template/views/dialog.css.ts b/packages/frontend/core/src/desktop/dialogs/import-template/dialog.css.ts similarity index 100% rename from packages/frontend/core/src/modules/import-template/views/dialog.css.ts rename to packages/frontend/core/src/desktop/dialogs/import-template/dialog.css.ts diff --git a/packages/frontend/core/src/modules/import-template/views/dialog.tsx b/packages/frontend/core/src/desktop/dialogs/import-template/index.tsx similarity index 95% rename from packages/frontend/core/src/modules/import-template/views/dialog.tsx rename to packages/frontend/core/src/desktop/dialogs/import-template/index.tsx index 12f863d79b6da..c616909adf22b 100644 --- a/packages/frontend/core/src/modules/import-template/views/dialog.tsx +++ b/packages/frontend/core/src/desktop/dialogs/import-template/index.tsx @@ -3,6 +3,13 @@ import { useAsyncCallback } from '@affine/core/components/hooks/affine-async-hoo import { useNavigateHelper } from '@affine/core/components/hooks/use-navigate-helper'; import { useWorkspaceName } from '@affine/core/components/hooks/use-workspace-info'; import { WorkspaceSelector } from '@affine/core/components/workspace-selector'; +import { AuthService } from '@affine/core/modules/cloud'; +import type { CreateWorkspaceCallbackPayload } from '@affine/core/modules/create-workspace'; +import { + ImportTemplateDialogService, + ImportTemplateService, + TemplateDownloaderService, +} from '@affine/core/modules/import-template'; import { WorkspaceFlavour } from '@affine/env/workspace'; import { useI18n } from '@affine/i18n'; import type { DocMode } from '@blocksuite/blocks'; @@ -16,11 +23,6 @@ import { import { cssVar } from '@toeverything/theme'; import { useCallback, useEffect, useState } from 'react'; -import { AuthService } from '../../cloud'; -import type { CreateWorkspaceCallbackPayload } from '../../create-workspace'; -import { ImportTemplateDialogService } from '../services/dialog'; -import { TemplateDownloaderService } from '../services/downloader'; -import { ImportTemplateService } from '../services/import'; import * as styles from './dialog.css'; const Dialog = ({ @@ -224,7 +226,7 @@ const Dialog = ({ ); }; -export const ImportTemplateDialogProvider = () => { +export const ImportTemplateDialog = () => { const importTemplateDialogService = useService(ImportTemplateDialogService); const isOpen = useLiveData(importTemplateDialogService.dialog.isOpen$); const template = useLiveData(importTemplateDialogService.dialog.template$); diff --git a/packages/frontend/core/src/desktop/dialogs/index.tsx b/packages/frontend/core/src/desktop/dialogs/index.tsx new file mode 100644 index 0000000000000..3885caca1b5a4 --- /dev/null +++ b/packages/frontend/core/src/desktop/dialogs/index.tsx @@ -0,0 +1,22 @@ +import { NotificationCenter } from '@affine/component'; +import { AuthModal } from '@affine/core/components/affine/auth'; + +import { CreateWorkspaceDialog } from './create-workspace'; +import { CustomThemeModifier } from './custom-theme'; +import { GlobalLoading } from './global-loading'; +import { ImportTemplateDialog } from './import-template'; +import { Telemetry } from './telemetry'; + +export const AllDialogs = () => { + return ( + <> + + + + + + + + + ); +}; diff --git a/packages/frontend/core/src/components/telemetry.tsx b/packages/frontend/core/src/desktop/dialogs/telemetry/index.tsx similarity index 100% rename from packages/frontend/core/src/components/telemetry.tsx rename to packages/frontend/core/src/desktop/dialogs/telemetry/index.tsx diff --git a/packages/frontend/core/src/desktop/pages/404.tsx b/packages/frontend/core/src/desktop/pages/404.tsx deleted file mode 100644 index 01a61399a5d40..0000000000000 --- a/packages/frontend/core/src/desktop/pages/404.tsx +++ /dev/null @@ -1,75 +0,0 @@ -import { - NoPermissionOrNotFound, - NotFoundPage, -} from '@affine/component/not-found-page'; -import { useAsyncCallback } from '@affine/core/components/hooks/affine-async-hooks'; -import { apis } from '@affine/electron-api'; -import { useLiveData, useService } from '@toeverything/infra'; -import type { ReactElement } from 'react'; -import { useCallback, useEffect, useState } from 'react'; - -import { SignOutModal } from '../../components/affine/sign-out-modal'; -import { - RouteLogic, - useNavigateHelper, -} from '../../components/hooks/use-navigate-helper'; -import { AuthService } from '../../modules/cloud'; -import { SignIn } from './auth/sign-in'; - -export const PageNotFound = ({ - noPermission, -}: { - noPermission?: boolean; -}): ReactElement => { - const authService = useService(AuthService); - const account = useLiveData(authService.session.account$); - const { jumpToIndex } = useNavigateHelper(); - const [open, setOpen] = useState(false); - - const handleBackButtonClick = useCallback( - () => jumpToIndex(RouteLogic.REPLACE), - [jumpToIndex] - ); - - const handleOpenSignOutModal = useCallback(() => { - setOpen(true); - }, [setOpen]); - - const onConfirmSignOut = useAsyncCallback(async () => { - setOpen(false); - await authService.signOut(); - }, [authService]); - - useEffect(() => { - apis?.ui.pingAppLayoutReady().catch(console.error); - }, []); - - return ( - <> - {noPermission ? ( - } - /> - ) : ( - - )} - - - - ); -}; - -export const Component = () => { - return ; -}; diff --git a/packages/frontend/core/src/desktop/pages/404/index.tsx b/packages/frontend/core/src/desktop/pages/404/index.tsx new file mode 100644 index 0000000000000..f003535d1af75 --- /dev/null +++ b/packages/frontend/core/src/desktop/pages/404/index.tsx @@ -0,0 +1,58 @@ +import { + NoPermissionOrNotFound, + NotFoundPage, +} from '@affine/component/not-found-page'; +import { useSignOut } from '@affine/core/components/hooks/affine/use-sign-out'; +import { apis } from '@affine/electron-api'; +import { useLiveData, useService } from '@toeverything/infra'; +import type { ReactElement } from 'react'; +import { useCallback, useEffect } from 'react'; + +import { + RouteLogic, + useNavigateHelper, +} from '../../../components/hooks/use-navigate-helper'; +import { AuthService } from '../../../modules/cloud'; +import { SignIn } from '../auth/sign-in'; + +/** + * only for web, should not be used in electron + */ +export const PageNotFound = ({ + noPermission, +}: { + noPermission?: boolean; +}): ReactElement => { + const authService = useService(AuthService); + const account = useLiveData(authService.session.account$); + const { jumpToIndex } = useNavigateHelper(); + const openSignOutModal = useSignOut(); + + const handleBackButtonClick = useCallback( + () => jumpToIndex(RouteLogic.REPLACE), + [jumpToIndex] + ); + + useEffect(() => { + apis?.ui.pingAppLayoutReady().catch(console.error); + }, []); + + return noPermission ? ( + } + /> + ) : ( + + ); +}; + +export const Component = () => { + return ; +}; diff --git a/packages/frontend/core/src/desktop/pages/ai-upgrade-success.tsx b/packages/frontend/core/src/desktop/pages/ai-upgrade-success.tsx deleted file mode 100644 index 5f35deca0065a..0000000000000 --- a/packages/frontend/core/src/desktop/pages/ai-upgrade-success.tsx +++ /dev/null @@ -1,5 +0,0 @@ -import { AIUpgradeSuccess } from '../../components/affine/subscription-landing'; - -export const Component = () => { - return ; -}; diff --git a/packages/frontend/core/src/components/affine/subscription-landing/index.tsx b/packages/frontend/core/src/desktop/pages/ai-upgrade-success/index.tsx similarity index 58% rename from packages/frontend/core/src/components/affine/subscription-landing/index.tsx rename to packages/frontend/core/src/desktop/pages/ai-upgrade-success/index.tsx index b90fe9bcaf4a8..d56b237c3759f 100644 --- a/packages/frontend/core/src/components/affine/subscription-landing/index.tsx +++ b/packages/frontend/core/src/desktop/pages/ai-upgrade-success/index.tsx @@ -1,20 +1,18 @@ +import { Button } from '@affine/component'; import { AuthPageContainer } from '@affine/component/auth-components'; -import { Button } from '@affine/component/ui/button'; -import { useSubscriptionNotifyWriter } from '@affine/core/components/hooks/affine/use-subscription-notify'; import { useNavigateHelper } from '@affine/core/components/hooks/use-navigate-helper'; import { Trans, useI18n } from '@affine/i18n'; -import { type ReactNode, useCallback } from 'react'; +import { useCallback } from 'react'; import { useSearchParams } from 'react-router-dom'; import * as styles from './styles.css'; -const UpgradeSuccessLayout = ({ - title, - description, -}: { - title?: ReactNode; - description?: ReactNode; -}) => { +/** + * /ai-upgrade-success page + * + * only on web + */ +export const Component = () => { const t = useI18n(); const [params] = useSearchParams(); @@ -29,7 +27,7 @@ const UpgradeSuccessLayout = ({ const subtitle = (
- {description} + {t['com.affine.payment.ai-upgrade-success-page.text']()}
+ ); }; - -export const CloudUpgradeSuccess = () => { - const t = useI18n(); - useSubscriptionNotifyWriter(); - return ( - - ); -}; - -export const AIUpgradeSuccess = () => { - const t = useI18n(); - useSubscriptionNotifyWriter(); - return ( - - ); -}; diff --git a/packages/frontend/core/src/desktop/pages/ai-upgrade-success/styles.css.ts b/packages/frontend/core/src/desktop/pages/ai-upgrade-success/styles.css.ts new file mode 100644 index 0000000000000..578ec70beff37 --- /dev/null +++ b/packages/frontend/core/src/desktop/pages/ai-upgrade-success/styles.css.ts @@ -0,0 +1,15 @@ +import { cssVar } from '@toeverything/theme'; +import { style } from '@vanilla-extract/css'; +export const leftContentText = style({ + fontSize: cssVar('fontBase'), + fontWeight: 400, + lineHeight: '1.6', + maxWidth: '548px', +}); +export const mail = style({ + color: cssVar('linkColor'), + textDecoration: 'none', + ':visited': { + color: cssVar('linkColor'), + }, +}); diff --git a/packages/frontend/core/src/desktop/pages/expired.tsx b/packages/frontend/core/src/desktop/pages/expired/index.tsx similarity index 88% rename from packages/frontend/core/src/desktop/pages/expired.tsx rename to packages/frontend/core/src/desktop/pages/expired/index.tsx index 87e0a94c024f8..027cf24804fa4 100644 --- a/packages/frontend/core/src/desktop/pages/expired.tsx +++ b/packages/frontend/core/src/desktop/pages/expired/index.tsx @@ -6,8 +6,13 @@ import { useCallback } from 'react'; import { RouteLogic, useNavigateHelper, -} from '../../components/hooks/use-navigate-helper'; +} from '../../../components/hooks/use-navigate-helper'; +/** + * /expired page + * + * only on web + */ export const Component = () => { const t = useI18n(); const { jumpToIndex } = useNavigateHelper(); diff --git a/packages/frontend/core/src/desktop/pages/import-template.tsx b/packages/frontend/core/src/desktop/pages/import-template/index.tsx similarity index 75% rename from packages/frontend/core/src/desktop/pages/import-template.tsx rename to packages/frontend/core/src/desktop/pages/import-template/index.tsx index caf479c0f8b2b..a80016b541c6e 100644 --- a/packages/frontend/core/src/desktop/pages/import-template.tsx +++ b/packages/frontend/core/src/desktop/pages/import-template/index.tsx @@ -3,9 +3,14 @@ import { useService } from '@toeverything/infra'; import { useEffect } from 'react'; import { useSearchParams } from 'react-router-dom'; -import { useNavigateHelper } from '../../components/hooks/use-navigate-helper'; -import { ImportTemplateDialogService } from '../../modules/import-template'; +import { useNavigateHelper } from '../../../components/hooks/use-navigate-helper'; +import { ImportTemplateDialogService } from '../../../modules/import-template'; +/** + * /template/import page, only for web + * + * no ui for this route, just open the dialog + */ export const Component = () => { const importTemplateDialogService = useService(ImportTemplateDialogService); const [searchParams] = useSearchParams(); diff --git a/packages/frontend/core/src/desktop/pages/index.tsx b/packages/frontend/core/src/desktop/pages/index/index.tsx similarity index 88% rename from packages/frontend/core/src/desktop/pages/index.tsx rename to packages/frontend/core/src/desktop/pages/index/index.tsx index 73723583c0e1b..3a72594784ad2 100644 --- a/packages/frontend/core/src/desktop/pages/index.tsx +++ b/packages/frontend/core/src/desktop/pages/index/index.tsx @@ -1,3 +1,7 @@ +import { + buildShowcaseWorkspace, + createFirstAppData, +} from '@affine/core/utils/first-app-data'; import { apis } from '@affine/electron-api'; import { WorkspaceFlavour } from '@affine/env/workspace'; import { @@ -12,21 +16,19 @@ import { useRef, useState, } from 'react'; -import { type LoaderFunction, useSearchParams } from 'react-router-dom'; - -import { AppFallback } from '../../components/affine/app-container'; -import { useNavigateHelper } from '../../components/hooks/use-navigate-helper'; -import { WorkspaceNavigator } from '../../components/workspace-selector'; -import { AuthService } from '../../modules/cloud'; -import { - buildShowcaseWorkspace, - createFirstAppData, -} from '../../utils/first-app-data'; - -export const loader: LoaderFunction = async () => { - return null; -}; - +import { useSearchParams } from 'react-router-dom'; + +import { AppFallback } from '../../../components/affine/app-container'; +import { useNavigateHelper } from '../../../components/hooks/use-navigate-helper'; +import { WorkspaceNavigator } from '../../../components/workspace-selector'; +import { AuthService } from '../../../modules/cloud'; + +/** + * index page + * + * query string: + * - initCloud: boolean, if true, when user is logged in, create a cloud workspace + */ export const Component = ({ defaultIndexRoute = 'all', }: { diff --git a/packages/frontend/core/src/desktop/pages/invite.tsx b/packages/frontend/core/src/desktop/pages/invite/index.tsx similarity index 90% rename from packages/frontend/core/src/desktop/pages/invite.tsx rename to packages/frontend/core/src/desktop/pages/invite/index.tsx index 9f2a2106aad4a..1866c13960a19 100644 --- a/packages/frontend/core/src/desktop/pages/invite.tsx +++ b/packages/frontend/core/src/desktop/pages/invite/index.tsx @@ -6,18 +6,21 @@ import { getInviteInfoQuery, } from '@affine/graphql'; import { useLiveData, useService } from '@toeverything/infra'; -import { useSetAtom } from 'jotai'; import { useCallback, useEffect } from 'react'; import type { LoaderFunction } from 'react-router-dom'; import { redirect, useLoaderData } from 'react-router-dom'; -import { authAtom } from '../../components/atoms'; import { RouteLogic, useNavigateHelper, -} from '../../components/hooks/use-navigate-helper'; -import { AuthService } from '../../modules/cloud'; +} from '../../../components/hooks/use-navigate-helper'; +import { AuthService } from '../../../modules/cloud'; +/** + * /invite/:inviteId page + * + * only for web + */ export const loader: LoaderFunction = async args => { const inviteId = args.params.inviteId || ''; const res = await fetcher({ @@ -60,7 +63,6 @@ export const Component = () => { const { jumpToSignIn } = useNavigateHelper(); const { jumpToPage } = useNavigateHelper(); - const setAuthAtom = useSetAtom(authAtom); const { inviteInfo } = useLoaderData() as { inviteId: string; inviteInfo: GetInviteInfoQuery['getInviteInfo']; @@ -84,7 +86,6 @@ export const Component = () => { jumpToSignIn, loginStatus, openWorkspace, - setAuthAtom, ]); if (loginStatus === 'authenticated') { diff --git a/packages/frontend/core/src/desktop/pages/onboarding.tsx b/packages/frontend/core/src/desktop/pages/onboarding.tsx deleted file mode 100644 index 2ef01cafcb5d8..0000000000000 --- a/packages/frontend/core/src/desktop/pages/onboarding.tsx +++ /dev/null @@ -1,42 +0,0 @@ -import { apis } from '@affine/electron-api'; -import { assertExists } from '@blocksuite/global/utils'; -import { useCallback } from 'react'; -import { redirect } from 'react-router-dom'; - -import { Onboarding } from '../../components/affine/onboarding/onboarding'; -import { - appConfigStorage, - useAppConfigStorage, -} from '../../components/hooks/use-app-config-storage'; -import { - RouteLogic, - useNavigateHelper, -} from '../../components/hooks/use-navigate-helper'; - -export const loader = () => { - if (!BUILD_CONFIG.isElectron && !appConfigStorage.get('onBoarding')) { - // onboarding is off, redirect to index - return redirect('/'); - } - - return null; -}; - -export const Component = () => { - const { jumpToIndex } = useNavigateHelper(); - const [, setOnboarding] = useAppConfigStorage('onBoarding'); - - const openApp = useCallback(() => { - if (BUILD_CONFIG.isElectron) { - assertExists(apis); - apis.ui.handleOpenMainApp().catch(err => { - console.log('failed to open main app', err); - }); - } else { - jumpToIndex(RouteLogic.REPLACE); - setOnboarding(false); - } - }, [jumpToIndex, setOnboarding]); - - return ; -}; diff --git a/packages/frontend/core/src/desktop/pages/onboarding/index.tsx b/packages/frontend/core/src/desktop/pages/onboarding/index.tsx new file mode 100644 index 0000000000000..10be6f6409307 --- /dev/null +++ b/packages/frontend/core/src/desktop/pages/onboarding/index.tsx @@ -0,0 +1,30 @@ +import { apis } from '@affine/electron-api'; +import { useCallback } from 'react'; +import { redirect } from 'react-router-dom'; + +import { Onboarding } from '../../../components/affine/onboarding/onboarding'; +import { appConfigStorage } from '../../../components/hooks/use-app-config-storage'; + +/** + * /onboarding page + * + * only for electron + */ +export const loader = () => { + if (!BUILD_CONFIG.isElectron && !appConfigStorage.get('onBoarding')) { + // onboarding is off, redirect to index + return redirect('/'); + } + + return null; +}; + +export const Component = () => { + const openApp = useCallback(() => { + apis?.ui.handleOpenMainApp().catch(err => { + console.log('failed to open main app', err); + }); + }, []); + + return ; +}; diff --git a/packages/frontend/core/src/desktop/pages/open-app.tsx b/packages/frontend/core/src/desktop/pages/open-app/index.tsx similarity index 99% rename from packages/frontend/core/src/desktop/pages/open-app.tsx rename to packages/frontend/core/src/desktop/pages/open-app/index.tsx index 3276a4f035551..45c89c5b10882 100644 --- a/packages/frontend/core/src/desktop/pages/open-app.tsx +++ b/packages/frontend/core/src/desktop/pages/open-app/index.tsx @@ -57,6 +57,11 @@ interface LoaderData { currentUser?: GetCurrentUserQuery['currentUser']; } +/** + * /open-app/:action page + * + * only for web + */ const OpenAppImpl = ({ urlToOpen, channel }: OpenAppProps) => { const t = useI18n(); const openDownloadLink = useCallback(() => { diff --git a/packages/frontend/core/src/desktop/pages/open-app.css.ts b/packages/frontend/core/src/desktop/pages/open-app/open-app.css.ts similarity index 100% rename from packages/frontend/core/src/desktop/pages/open-app.css.ts rename to packages/frontend/core/src/desktop/pages/open-app/open-app.css.ts diff --git a/packages/frontend/core/src/desktop/pages/redirect.tsx b/packages/frontend/core/src/desktop/pages/redirect/index.tsx similarity index 95% rename from packages/frontend/core/src/desktop/pages/redirect.tsx rename to packages/frontend/core/src/desktop/pages/redirect/index.tsx index fad78a7944394..3405d237d851c 100644 --- a/packages/frontend/core/src/desktop/pages/redirect.tsx +++ b/packages/frontend/core/src/desktop/pages/redirect/index.tsx @@ -15,6 +15,11 @@ const trustedDomain = [ const logger = new DebugLogger('redirect_proxy'); +/** + * /redirect-proxy page + * + * only for web + */ export const loader: LoaderFunction = async ({ request }) => { const url = new URL(request.url); const searchParams = url.searchParams; diff --git a/packages/frontend/core/src/desktop/pages/root.tsx b/packages/frontend/core/src/desktop/pages/root.tsx index 2b48f64932b58..c6ddcd821186e 100644 --- a/packages/frontend/core/src/desktop/pages/root.tsx +++ b/packages/frontend/core/src/desktop/pages/root.tsx @@ -1,11 +1,11 @@ import { Outlet } from 'react-router-dom'; -import { AllWorkspaceModals } from '../../components/providers/modal-provider'; +import { AllDialogs } from '../dialogs'; export const RootWrapper = () => { return ( <> - + ); diff --git a/packages/frontend/core/src/desktop/pages/subscribe.tsx b/packages/frontend/core/src/desktop/pages/subscribe/index.tsx similarity index 95% rename from packages/frontend/core/src/desktop/pages/subscribe.tsx rename to packages/frontend/core/src/desktop/pages/subscribe/index.tsx index cf0b0f9a33a1c..9e7505e66f9da 100644 --- a/packages/frontend/core/src/desktop/pages/subscribe.tsx +++ b/packages/frontend/core/src/desktop/pages/subscribe/index.tsx @@ -7,14 +7,17 @@ import { useEffect, useMemo, useState } from 'react'; import { useSearchParams } from 'react-router-dom'; import { EMPTY, mergeMap, switchMap } from 'rxjs'; -import { generateSubscriptionCallbackLink } from '../../components/hooks/affine/use-subscription-notify'; +import { generateSubscriptionCallbackLink } from '../../../components/hooks/affine/use-subscription-notify'; import { RouteLogic, useNavigateHelper, -} from '../../components/hooks/use-navigate-helper'; -import { AuthService, SubscriptionService } from '../../modules/cloud'; +} from '../../../components/hooks/use-navigate-helper'; +import { AuthService, SubscriptionService } from '../../../modules/cloud'; import { container } from './subscribe.css'; +/** + * /subscribe page + */ export const Component = () => { const { authService, subscriptionService } = useServices({ AuthService, diff --git a/packages/frontend/core/src/desktop/pages/subscribe.css.ts b/packages/frontend/core/src/desktop/pages/subscribe/subscribe.css.ts similarity index 100% rename from packages/frontend/core/src/desktop/pages/subscribe.css.ts rename to packages/frontend/core/src/desktop/pages/subscribe/subscribe.css.ts diff --git a/packages/frontend/core/src/desktop/pages/theme-editor.tsx b/packages/frontend/core/src/desktop/pages/theme-editor.tsx deleted file mode 100644 index 1d413c41282b0..0000000000000 --- a/packages/frontend/core/src/desktop/pages/theme-editor.tsx +++ /dev/null @@ -1,5 +0,0 @@ -import { ThemeEditor } from '../../modules/theme-editor'; - -export const Component = () => { - return ; -}; diff --git a/packages/frontend/core/src/modules/theme-editor/views/components/color-cell.tsx b/packages/frontend/core/src/desktop/pages/theme-editor/components/color-cell.tsx similarity index 100% rename from packages/frontend/core/src/modules/theme-editor/views/components/color-cell.tsx rename to packages/frontend/core/src/desktop/pages/theme-editor/components/color-cell.tsx diff --git a/packages/frontend/core/src/modules/theme-editor/views/components/empty.tsx b/packages/frontend/core/src/desktop/pages/theme-editor/components/empty.tsx similarity index 100% rename from packages/frontend/core/src/modules/theme-editor/views/components/empty.tsx rename to packages/frontend/core/src/desktop/pages/theme-editor/components/empty.tsx diff --git a/packages/frontend/core/src/modules/theme-editor/views/components/simple-color-picker.css.ts b/packages/frontend/core/src/desktop/pages/theme-editor/components/simple-color-picker.css.ts similarity index 100% rename from packages/frontend/core/src/modules/theme-editor/views/components/simple-color-picker.css.ts rename to packages/frontend/core/src/desktop/pages/theme-editor/components/simple-color-picker.css.ts diff --git a/packages/frontend/core/src/modules/theme-editor/views/components/simple-color-picker.tsx b/packages/frontend/core/src/desktop/pages/theme-editor/components/simple-color-picker.tsx similarity index 100% rename from packages/frontend/core/src/modules/theme-editor/views/components/simple-color-picker.tsx rename to packages/frontend/core/src/desktop/pages/theme-editor/components/simple-color-picker.tsx diff --git a/packages/frontend/core/src/modules/theme-editor/views/components/string-cell.css.ts b/packages/frontend/core/src/desktop/pages/theme-editor/components/string-cell.css.ts similarity index 100% rename from packages/frontend/core/src/modules/theme-editor/views/components/string-cell.css.ts rename to packages/frontend/core/src/desktop/pages/theme-editor/components/string-cell.css.ts diff --git a/packages/frontend/core/src/modules/theme-editor/views/components/string-cell.tsx b/packages/frontend/core/src/desktop/pages/theme-editor/components/string-cell.tsx similarity index 100% rename from packages/frontend/core/src/modules/theme-editor/views/components/string-cell.tsx rename to packages/frontend/core/src/desktop/pages/theme-editor/components/string-cell.tsx diff --git a/packages/frontend/core/src/modules/theme-editor/views/components/tree-node.tsx b/packages/frontend/core/src/desktop/pages/theme-editor/components/tree-node.tsx similarity index 100% rename from packages/frontend/core/src/modules/theme-editor/views/components/tree-node.tsx rename to packages/frontend/core/src/desktop/pages/theme-editor/components/tree-node.tsx diff --git a/packages/frontend/core/src/modules/theme-editor/views/components/variable-list.tsx b/packages/frontend/core/src/desktop/pages/theme-editor/components/variable-list.tsx similarity index 96% rename from packages/frontend/core/src/modules/theme-editor/views/components/variable-list.tsx rename to packages/frontend/core/src/desktop/pages/theme-editor/components/variable-list.tsx index 5d70686d81b7a..658bd0dbd5e8b 100644 --- a/packages/frontend/core/src/modules/theme-editor/views/components/variable-list.tsx +++ b/packages/frontend/core/src/desktop/pages/theme-editor/components/variable-list.tsx @@ -1,7 +1,7 @@ import { Scrollable } from '@affine/component'; +import { ThemeEditorService } from '@affine/core/modules/theme-editor'; import { useLiveData, useService } from '@toeverything/infra'; -import { ThemeEditorService } from '../../services/theme-editor'; import type { TreeNode } from '../resource'; import * as styles from '../theme-editor.css'; import { isColor } from '../utils'; diff --git a/packages/frontend/core/src/desktop/pages/theme-editor/index.tsx b/packages/frontend/core/src/desktop/pages/theme-editor/index.tsx new file mode 100644 index 0000000000000..0b19480cf7531 --- /dev/null +++ b/packages/frontend/core/src/desktop/pages/theme-editor/index.tsx @@ -0,0 +1,8 @@ +import { ThemeEditor } from './theme-editor'; + +/** + * /theme-editor page + */ +export const Component = () => { + return ; +}; diff --git a/packages/frontend/core/src/modules/theme-editor/views/resource.ts b/packages/frontend/core/src/desktop/pages/theme-editor/resource.ts similarity index 100% rename from packages/frontend/core/src/modules/theme-editor/views/resource.ts rename to packages/frontend/core/src/desktop/pages/theme-editor/resource.ts diff --git a/packages/frontend/core/src/modules/theme-editor/views/theme-editor.css.ts b/packages/frontend/core/src/desktop/pages/theme-editor/theme-editor.css.ts similarity index 100% rename from packages/frontend/core/src/modules/theme-editor/views/theme-editor.css.ts rename to packages/frontend/core/src/desktop/pages/theme-editor/theme-editor.css.ts diff --git a/packages/frontend/core/src/modules/theme-editor/views/theme-editor.tsx b/packages/frontend/core/src/desktop/pages/theme-editor/theme-editor.tsx similarity index 97% rename from packages/frontend/core/src/modules/theme-editor/views/theme-editor.tsx rename to packages/frontend/core/src/desktop/pages/theme-editor/theme-editor.tsx index f805fcf779de5..d99b2a6dbf020 100644 --- a/packages/frontend/core/src/modules/theme-editor/views/theme-editor.tsx +++ b/packages/frontend/core/src/desktop/pages/theme-editor/theme-editor.tsx @@ -1,8 +1,8 @@ import { RadioGroup, Scrollable } from '@affine/component'; +import { ThemeEditorService } from '@affine/core/modules/theme-editor'; import { useService } from '@toeverything/infra'; import { useCallback, useEffect, useState } from 'react'; -import { ThemeEditorService } from '../services/theme-editor'; import { ThemeEmpty } from './components/empty'; import { ThemeTreeNode } from './components/tree-node'; import { VariableList } from './components/variable-list'; diff --git a/packages/frontend/core/src/modules/theme-editor/views/utils.ts b/packages/frontend/core/src/desktop/pages/theme-editor/utils.ts similarity index 100% rename from packages/frontend/core/src/modules/theme-editor/views/utils.ts rename to packages/frontend/core/src/desktop/pages/theme-editor/utils.ts diff --git a/packages/frontend/core/src/desktop/pages/upgrade-success.tsx b/packages/frontend/core/src/desktop/pages/upgrade-success.tsx deleted file mode 100644 index 6353f1c9a68eb..0000000000000 --- a/packages/frontend/core/src/desktop/pages/upgrade-success.tsx +++ /dev/null @@ -1,5 +0,0 @@ -import { CloudUpgradeSuccess } from '../../components/affine/subscription-landing'; - -export const Component = () => { - return ; -}; diff --git a/packages/frontend/core/src/desktop/pages/upgrade-success/index.tsx b/packages/frontend/core/src/desktop/pages/upgrade-success/index.tsx new file mode 100644 index 0000000000000..a3de3a1f3d471 --- /dev/null +++ b/packages/frontend/core/src/desktop/pages/upgrade-success/index.tsx @@ -0,0 +1,57 @@ +import { Button } from '@affine/component'; +import { AuthPageContainer } from '@affine/component/auth-components'; +import { useNavigateHelper } from '@affine/core/components/hooks/use-navigate-helper'; +import { Trans, useI18n } from '@affine/i18n'; +import { useCallback } from 'react'; +import { useSearchParams } from 'react-router-dom'; + +import * as styles from './styles.css'; + +/** + * /upgrade-success page + * + * only on web + */ +export const Component = () => { + const t = useI18n(); + const [params] = useSearchParams(); + + const { jumpToIndex, openInApp } = useNavigateHelper(); + const openAffine = useCallback(() => { + if (params.get('schema')) { + openInApp(params.get('schema') ?? 'affine', 'bring-to-front'); + } else { + jumpToIndex(); + } + }, [jumpToIndex, openInApp, params]); + + const subtitle = ( +
+ {t['com.affine.payment.upgrade-success-page.text']()} +
+ + ), + }} + /> +
+
+ ); + + return ( + + + + ); +}; diff --git a/packages/frontend/core/src/desktop/pages/upgrade-success/styles.css.ts b/packages/frontend/core/src/desktop/pages/upgrade-success/styles.css.ts new file mode 100644 index 0000000000000..578ec70beff37 --- /dev/null +++ b/packages/frontend/core/src/desktop/pages/upgrade-success/styles.css.ts @@ -0,0 +1,15 @@ +import { cssVar } from '@toeverything/theme'; +import { style } from '@vanilla-extract/css'; +export const leftContentText = style({ + fontSize: cssVar('fontBase'), + fontWeight: 400, + lineHeight: '1.6', + maxWidth: '548px', +}); +export const mail = style({ + color: cssVar('linkColor'), + textDecoration: 'none', + ':visited': { + color: cssVar('linkColor'), + }, +}); diff --git a/packages/frontend/core/src/mobile/provider/model-provider.tsx b/packages/frontend/core/src/mobile/provider/model-provider.tsx index 606ddc00f5879..26e46079f0623 100644 --- a/packages/frontend/core/src/mobile/provider/model-provider.tsx +++ b/packages/frontend/core/src/mobile/provider/model-provider.tsx @@ -9,7 +9,6 @@ import { import { StarAFFiNEModal } from '@affine/core/components/affine/star-affine-modal'; import { useTrashModalHelper } from '@affine/core/components/hooks/affine/use-trash-modal-helper'; import { MoveToTrash } from '@affine/core/components/page-list'; -import { SignOutConfirmModal } from '@affine/core/components/providers/modal-provider'; import { CreateWorkspaceDialogProvider } from '@affine/core/modules/create-workspace'; import { PeekViewManagerModal } from '@affine/core/modules/peek-view'; import { WorkspaceFlavour } from '@affine/env/workspace'; @@ -68,7 +67,6 @@ export const AllWorkspaceModals = () => { - ); }; diff --git a/packages/frontend/core/src/modules/import-template/index.ts b/packages/frontend/core/src/modules/import-template/index.ts index be7ab4708e18a..b01a81cde2aef 100644 --- a/packages/frontend/core/src/modules/import-template/index.ts +++ b/packages/frontend/core/src/modules/import-template/index.ts @@ -9,7 +9,8 @@ import { ImportTemplateService } from './services/import'; import { TemplateDownloaderStore } from './store/downloader'; export { ImportTemplateDialogService } from './services/dialog'; -export { ImportTemplateDialogProvider } from './views/dialog'; +export { TemplateDownloaderService } from './services/downloader'; +export { ImportTemplateService } from './services/import'; export function configureImportTemplateModule(framework: Framework) { framework diff --git a/packages/frontend/core/src/modules/theme-editor/index.ts b/packages/frontend/core/src/modules/theme-editor/index.ts index 07fd082ca024e..20ad54662ec5b 100644 --- a/packages/frontend/core/src/modules/theme-editor/index.ts +++ b/packages/frontend/core/src/modules/theme-editor/index.ts @@ -2,8 +2,6 @@ import { type Framework, GlobalState } from '@toeverything/infra'; import { ThemeEditorService } from './services/theme-editor'; -export { CustomThemeModifier, useCustomTheme } from './views/custom-theme'; -export { ThemeEditor } from './views/theme-editor'; export { ThemeEditorService }; export function configureThemeEditorModule(framework: Framework) {