diff --git a/packages/frontend/core/src/components/workspace-upgrade/index.tsx b/packages/frontend/core/src/components/workspace-upgrade/index.tsx new file mode 100644 index 0000000000..7eecc97e97 --- /dev/null +++ b/packages/frontend/core/src/components/workspace-upgrade/index.tsx @@ -0,0 +1 @@ +export { WorkspaceUpgrade } from './upgrade'; diff --git a/packages/frontend/core/src/components/migration-fallback.tsx b/packages/frontend/core/src/components/workspace-upgrade/upgrade-hooks.ts similarity index 56% rename from packages/frontend/core/src/components/migration-fallback.tsx rename to packages/frontend/core/src/components/workspace-upgrade/upgrade-hooks.ts index f6c0f12b4e..a0e6e1201c 100644 --- a/packages/frontend/core/src/components/migration-fallback.tsx +++ b/packages/frontend/core/src/components/workspace-upgrade/upgrade-hooks.ts @@ -4,15 +4,18 @@ import type { SQLiteProvider, } from '@affine/env/workspace'; import { assertExists } from '@blocksuite/global/utils'; -import { Button } from '@toeverything/components/button'; import { forceUpgradePages } from '@toeverything/infra/blocksuite'; import { useCallback, useMemo, useState } from 'react'; import { syncDataSourceFromDoc, syncDocFromDataSource } from 'y-provider'; -import { useCurrentWorkspace } from '../hooks/current/use-current-workspace'; +import { useCurrentWorkspace } from '../../hooks/current/use-current-workspace'; + +export type UpgradeState = 'pending' | 'upgrading' | 'done' | 'error'; + +export function useUpgradeWorkspace() { + const [state, setState] = useState('pending'); + const [error, setError] = useState(null); -export const MigrationFallback = function MigrationFallback() { - const [done, setDone] = useState(false); const [workspace] = useCurrentWorkspace(); const providers = workspace.blockSuiteWorkspace.providers; const remoteProvider: AffineSocketIOProvider | undefined = useMemo(() => { @@ -33,46 +36,50 @@ export const MigrationFallback = function MigrationFallback() { assertExists(provider, 'no local provider'); return provider; }, [providers]); - const handleClick = useCallback(async () => { - setDone(false); - await syncDocFromDataSource( - workspace.blockSuiteWorkspace.doc, - localProvider.datasource - ); - if (remoteProvider) { + + const upgradeWorkspace = useCallback(() => { + setState('upgrading'); + setError(null); + + (async () => { await syncDocFromDataSource( workspace.blockSuiteWorkspace.doc, - remoteProvider.datasource + localProvider.datasource ); - } + if (remoteProvider) { + await syncDocFromDataSource( + workspace.blockSuiteWorkspace.doc, + remoteProvider.datasource + ); + } - await forceUpgradePages({ - getCurrentRootDoc: async () => workspace.blockSuiteWorkspace.doc, - getSchema: () => workspace.blockSuiteWorkspace.schema, - }); - await syncDataSourceFromDoc( - workspace.blockSuiteWorkspace.doc, - localProvider.datasource - ); - if (remoteProvider) { + await forceUpgradePages({ + getCurrentRootDoc: async () => workspace.blockSuiteWorkspace.doc, + getSchema: () => workspace.blockSuiteWorkspace.schema, + }); await syncDataSourceFromDoc( workspace.blockSuiteWorkspace.doc, - remoteProvider.datasource + localProvider.datasource ); - } - setDone(true); + if (remoteProvider) { + await syncDataSourceFromDoc( + workspace.blockSuiteWorkspace.doc, + remoteProvider.datasource + ); + } + + setState('done'); + })().catch((e: any) => { + console.error(e); + setError(e); + setState('error'); + }); }, [ localProvider.datasource, remoteProvider, workspace.blockSuiteWorkspace.doc, workspace.blockSuiteWorkspace.schema, ]); - if (done) { - return
Done, please refresh the page.
; - } - return ( - - ); -}; + + return [state, error, upgradeWorkspace] as const; +} diff --git a/packages/frontend/core/src/components/workspace-upgrade/upgrade-icon.tsx b/packages/frontend/core/src/components/workspace-upgrade/upgrade-icon.tsx new file mode 100644 index 0000000000..5470b12c6c --- /dev/null +++ b/packages/frontend/core/src/components/workspace-upgrade/upgrade-icon.tsx @@ -0,0 +1,44 @@ +import type React from 'react'; + +/** + * TODO: Define a place to manage all icons and svg images. + */ +export const ArrowCircleIcon = (props: React.SVGProps) => { + return ( + + + + ); +}; + +export const HeartBreakIcon = (props: React.SVGProps) => { + return ( + + + + ); +}; diff --git a/packages/frontend/core/src/components/workspace-upgrade/upgrade.css.ts b/packages/frontend/core/src/components/workspace-upgrade/upgrade.css.ts new file mode 100644 index 0000000000..8cca9f637f --- /dev/null +++ b/packages/frontend/core/src/components/workspace-upgrade/upgrade.css.ts @@ -0,0 +1,32 @@ +import { keyframes, style } from '@vanilla-extract/css'; + +export const layout = style({ + margin: '80px auto', + maxWidth: '536px', +}); + +export const upgradeBox = style({ + padding: '48px 52px', + display: 'flex', + flexDirection: 'column', + alignItems: 'center', +}); + +export const upgradeTips = style({ + margin: '20px 0', + color: 'var(--affine-text-secondary-color)', + fontSize: '12px', + fontStyle: 'normal', + fontWeight: '400', + lineHeight: '20px', +}); + +const rotate = keyframes({ + '0%': { transform: 'rotate(0deg)' }, + '50%': { transform: 'rotate(180deg)' }, + '100%': { transform: 'rotate(360deg)' }, +}); + +export const loadingIcon = style({ + animation: `${rotate} 1.2s infinite linear`, +}); diff --git a/packages/frontend/core/src/components/workspace-upgrade/upgrade.tsx b/packages/frontend/core/src/components/workspace-upgrade/upgrade.tsx new file mode 100644 index 0000000000..9022985156 --- /dev/null +++ b/packages/frontend/core/src/components/workspace-upgrade/upgrade.tsx @@ -0,0 +1,77 @@ +import { AffineShapeIcon } from '@affine/component/page-list'; // TODO: import from page-list temporarily, need to defined common svg icon/images management. +import { useAFFiNEI18N } from '@affine/i18n/hooks'; +import { Button } from '@toeverything/components/button'; +import { useCallback, useMemo } from 'react'; + +import * as styles from './upgrade.css'; +import { type UpgradeState, useUpgradeWorkspace } from './upgrade-hooks'; +import { ArrowCircleIcon, HeartBreakIcon } from './upgrade-icon'; + +const UPGRADE_TIPS_KEYS = { + pending: 'com.affine.upgrade.tips.normal', + upgrading: 'com.affine.upgrade.tips.normal', + done: 'com.affine.upgrade.tips.done', + error: 'com.affine.upgrade.tips.error', +} as const; + +const BUTTON_TEXT_KEYS = { + pending: 'com.affine.upgrade.button-text.pending', + upgrading: 'com.affine.upgrade.button-text.upgrading', + done: 'com.affine.upgrade.button-text.done', + error: 'com.affine.upgrade.button-text.error', +} as const; + +function UpgradeIcon({ upgradeState }: { upgradeState: UpgradeState }) { + if (upgradeState === 'error') { + return ; + } + return ( + + ); +} + +/** + * TODO: Help info is not implemented yet. + */ +export const WorkspaceUpgrade = function MigrationFallback() { + const [upgradeState, , upgradeWorkspace] = useUpgradeWorkspace(); + const t = useAFFiNEI18N(); + + const refreshPage = useCallback(() => { + window.location.reload(); + }, []); + + const onButtonClick = useMemo(() => { + if (upgradeState === 'done') { + return refreshPage; + } + + if (upgradeState === 'pending') { + return upgradeWorkspace; + } + + return undefined; + }, [upgradeState, upgradeWorkspace, refreshPage]); + + return ( +
+
+ +

+ {t[UPGRADE_TIPS_KEYS[upgradeState]]()} +

+ +
+
+ ); +}; diff --git a/packages/frontend/core/src/layouts/workspace-layout.tsx b/packages/frontend/core/src/layouts/workspace-layout.tsx index 2e87d161ac..a1686f89f0 100644 --- a/packages/frontend/core/src/layouts/workspace-layout.tsx +++ b/packages/frontend/core/src/layouts/workspace-layout.tsx @@ -44,7 +44,6 @@ import { mainContainerAtom } from '../atoms/element'; import { AdapterProviderWrapper } from '../components/adapter-worksapce-wrapper'; import { AppContainer } from '../components/affine/app-container'; import { usePageHelper } from '../components/blocksuite/block-suite-page-list/utils'; -import { MigrationFallback } from '../components/migration-fallback'; import type { IslandItemNames } from '../components/pure/help-island'; import { HelpIsland } from '../components/pure/help-island'; import { processCollectionsDrag } from '../components/pure/workspace-slider-bar/collections'; @@ -52,6 +51,7 @@ import { DROPPABLE_SIDEBAR_TRASH, RootAppSidebar, } from '../components/root-app-sidebar'; +import { WorkspaceUpgrade } from '../components/workspace-upgrade'; import { useAppSettingHelper } from '../hooks/affine/use-app-setting-helper'; import { useBlockSuiteMetaHelper } from '../hooks/affine/use-block-suite-meta-helper'; import { useCurrentWorkspace } from '../hooks/current/use-current-workspace'; @@ -297,7 +297,7 @@ export const WorkspaceLayoutInner = ({ padding={appSettings.clientBorder} inTrashPage={inTrashPage} > - {incompatible ? : children} + {incompatible ? : children} diff --git a/packages/frontend/i18n/src/resources/en.json b/packages/frontend/i18n/src/resources/en.json index b438b679f5..03cb2153a9 100644 --- a/packages/frontend/i18n/src/resources/en.json +++ b/packages/frontend/i18n/src/resources/en.json @@ -775,5 +775,12 @@ "com.affine.payment.member.description": "Manage members here. {{planName}} Users can invite up to {{memberLimit}}", "com.affine.cmdk.affine.switch-state.on": "ON", "com.affine.cmdk.affine.switch-state.off": "OFF", + "com.affine.upgrade.button-text.pending": "Upgrade Workspace Data", + "com.affine.upgrade.button-text.upgrading": "Upgrading", + "com.affine.upgrade.button-text.done": "Refresh Current Page", + "com.affine.upgrade.button-text.error": "Data Upgrade Error", + "com.affine.upgrade.tips.normal": "To ensure compatibility with the updated AFFiNE client, please upgrade your data by clicking the \"Upgrade Workspace Data\" button below.", + "com.affine.upgrade.tips.done": "After upgrading the workspace data, please refresh the page to see the changes.", + "com.affine.upgrade.tips.error": "We encountered some errors while upgrading the workspace data.", "com.affine.workspaceSubPath.trash.empty-description": "Deleted pages will appear here." } diff --git a/tests/affine-cloud/e2e/basic.spec.ts b/tests/affine-cloud/e2e/basic.spec.ts index 9d6051d2ef..291fa07de5 100644 --- a/tests/affine-cloud/e2e/basic.spec.ts +++ b/tests/affine-cloud/e2e/basic.spec.ts @@ -95,8 +95,8 @@ test.describe('basic', () => { await page.reload(); await page.waitForTimeout(1000); await page.goto(`${coreUrl}/workspace/${workspaceId}/all`); - await page.getByTestId('upgrade-workspace').click(); - await expect(page.getByText('Done, please refresh the page.')).toBeVisible({ + await page.getByTestId('upgrade-workspace-button').click(); + await expect(page.getByText('Refresh Current Page')).toBeVisible({ timeout: 60000, }); await page.goto(