-
-
-
- {center}
-
-
-
-
- {isWindowsDesktop ? : null}
-
-
-
+
- >
+
+ {center}
+
+
+
+
+ {isWindowsDesktop ? : null}
+
+
+
+
+
);
});
diff --git a/packages/frontend/core/src/components/pure/header/top-tip.tsx b/packages/frontend/core/src/components/pure/header/top-tip.tsx
deleted file mode 100644
index 7490198670..0000000000
--- a/packages/frontend/core/src/components/pure/header/top-tip.tsx
+++ /dev/null
@@ -1,86 +0,0 @@
-import { BrowserWarning } from '@affine/component/affine-banner';
-import { DownloadTips } from '@affine/component/affine-banner';
-import { Trans } from '@affine/i18n';
-import { useAFFiNEI18N } from '@affine/i18n/hooks';
-import { useAtom } from 'jotai';
-import { useEffect, useState } from 'react';
-
-import { guideDownloadClientTipAtom } from '../../../atoms/guide';
-
-const minimumChromeVersion = 102;
-
-const shouldShowWarning = () => {
- if (environment.isDesktop) {
- // even though desktop has compatibility issues,
- // we don't want to show the warning
- return false;
- }
- if (!environment.isBrowser) {
- // disable in SSR
- return false;
- }
- if (environment.isChrome) {
- return environment.chromeVersion < minimumChromeVersion;
- } else {
- return !environment.isMobile;
- }
-};
-
-const OSWarningMessage = () => {
- const t = useAFFiNEI18N();
- const [notChrome, setNotChrome] = useState(false);
- const [notGoodVersion, setNotGoodVersion] = useState(false);
- useEffect(() => {
- setNotChrome(environment.isBrowser && !environment.isChrome);
- setNotGoodVersion(
- environment.isBrowser &&
- environment.isChrome &&
- environment.chromeVersion < minimumChromeVersion
- );
- }, []);
-
- if (notChrome) {
- return (
-
-
- We recommend the Chrome browser for an optimal
- experience.
-
-
- );
- } else if (notGoodVersion) {
- return
{t['upgradeBrowser']()};
- }
- return null;
-};
-export const TopTip = () => {
- const [showWarning, setShowWarning] = useState(false);
- const [showDownloadTip, setShowDownloadTip] = useAtom(
- guideDownloadClientTipAtom
- );
-
- useEffect(() => {
- setShowWarning(shouldShowWarning());
- }, []);
-
- if (showDownloadTip && environment.isDesktop) {
- return (
-
{
- setShowDownloadTip(false);
- localStorage.setItem('affine-is-dt-hide', '1');
- }}
- />
- );
- }
-
- return (
- }
- onClose={() => {
- setShowWarning(false);
- }}
- />
- );
-};
diff --git a/packages/frontend/core/src/components/pure/workspace-slider-bar/workspace-card/index.tsx b/packages/frontend/core/src/components/pure/workspace-slider-bar/workspace-card/index.tsx
index 59981a169a..223a30b02c 100644
--- a/packages/frontend/core/src/components/pure/workspace-slider-bar/workspace-card/index.tsx
+++ b/packages/frontend/core/src/components/pure/workspace-slider-bar/workspace-card/index.tsx
@@ -5,6 +5,7 @@ import {
} from '@affine/workspace/providers';
import {
CloudWorkspaceIcon,
+ InformationFillDuotoneIcon,
LocalWorkspaceIcon,
NoNetworkIcon,
UnsyncIcon,
@@ -67,7 +68,11 @@ const UnSyncWorkspaceStatus = () => {
const LocalWorkspaceStatus = () => {
return (
<>
-
+ {!environment.isDesktop ? (
+
+ ) : (
+
+ )}
Local
>
);
@@ -109,6 +114,9 @@ const WorkspaceStatus = ({
const content = useMemo(() => {
// TODO: add i18n
if (currentWorkspace.flavour === WorkspaceFlavour.LOCAL) {
+ if (!environment.isDesktop) {
+ return 'This is a local demo workspace.';
+ }
return 'Saved locally';
}
if (!isOnline) {
diff --git a/packages/frontend/core/src/components/pure/workspace-slider-bar/workspace-card/styles.ts b/packages/frontend/core/src/components/pure/workspace-slider-bar/workspace-card/styles.ts
index 00423908a0..45f2f0b924 100644
--- a/packages/frontend/core/src/components/pure/workspace-slider-bar/workspace-card/styles.ts
+++ b/packages/frontend/core/src/components/pure/workspace-slider-bar/workspace-card/styles.ts
@@ -44,6 +44,9 @@ export const StyledWorkspaceStatus = styled('div')(() => {
svg: {
color: 'var(--affine-icon-color)',
fontSize: 'var(--affine-font-base)',
+ '&[data-warning-color="true"]': {
+ color: 'var(--affine-error-color)',
+ },
},
};
});
diff --git a/packages/frontend/core/src/components/root-app-sidebar/index.tsx b/packages/frontend/core/src/components/root-app-sidebar/index.tsx
index a6620301c5..c5367a1530 100644
--- a/packages/frontend/core/src/components/root-app-sidebar/index.tsx
+++ b/packages/frontend/core/src/components/root-app-sidebar/index.tsx
@@ -1,6 +1,7 @@
import { AnimatedDeleteIcon } from '@affine/component';
import {
AddPageButton,
+ AppDownloadButton,
AppSidebar,
appSidebarOpenAtom,
AppUpdaterButton,
@@ -266,7 +267,7 @@ export const RootAppSidebar = ({
)}
- {environment.isDesktop && }
+ {environment.isDesktop ? : }
diff --git a/packages/frontend/core/src/components/top-tip.tsx b/packages/frontend/core/src/components/top-tip.tsx
new file mode 100644
index 0000000000..9a609380b7
--- /dev/null
+++ b/packages/frontend/core/src/components/top-tip.tsx
@@ -0,0 +1,122 @@
+import { BrowserWarning } from '@affine/component/affine-banner';
+import { LocalDemoTips } from '@affine/component/affine-banner';
+import {
+ type AffineOfficialWorkspace,
+ WorkspaceFlavour,
+} from '@affine/env/workspace';
+import { Trans } from '@affine/i18n';
+import { useAFFiNEI18N } from '@affine/i18n/hooks';
+import { useSetAtom } from 'jotai';
+import { useCallback, useState } from 'react';
+
+import { authAtom } from '../atoms';
+import { useCurrentLoginStatus } from '../hooks/affine/use-current-login-status';
+import { useOnTransformWorkspace } from '../hooks/root/use-on-transform-workspace';
+import { EnableAffineCloudModal } from './affine/enable-affine-cloud-modal';
+
+const minimumChromeVersion = 106;
+
+const shouldShowWarning = (() => {
+ if (environment.isDesktop) {
+ // even though desktop has compatibility issues,
+ // we don't want to show the warning
+ return false;
+ }
+ if (!environment.isBrowser) {
+ // disable in SSR
+ return false;
+ }
+ if (environment.isChrome) {
+ return environment.chromeVersion < minimumChromeVersion;
+ } else {
+ return !environment.isMobile;
+ }
+})();
+
+const OSWarningMessage = () => {
+ const t = useAFFiNEI18N();
+ const notChrome = environment.isBrowser && !environment.isChrome;
+ const notGoodVersion =
+ environment.isBrowser &&
+ environment.isChrome &&
+ environment.chromeVersion < minimumChromeVersion;
+
+ if (notChrome) {
+ return (
+
+
+ We recommend the Chrome browser for an optimal
+ experience.
+
+
+ );
+ } else if (notGoodVersion) {
+ return {t['upgradeBrowser']()};
+ }
+ return null;
+};
+
+export const TopTip = ({
+ workspace,
+}: {
+ workspace: AffineOfficialWorkspace;
+}) => {
+ const loginStatus = useCurrentLoginStatus();
+ const isLoggedIn = loginStatus === 'authenticated';
+
+ const [showWarning, setShowWarning] = useState(shouldShowWarning);
+ const [showLocalDemoTips, setShowLocalDemoTips] = useState(true);
+ const [open, setOpen] = useState(false);
+
+ const setAuthModal = useSetAtom(authAtom);
+ const onLogin = useCallback(() => {
+ setAuthModal({ openModal: true, state: 'signIn' });
+ }, [setAuthModal]);
+
+ const onTransformWorkspace = useOnTransformWorkspace();
+ const handleConfirm = useCallback(() => {
+ if (workspace.flavour !== WorkspaceFlavour.LOCAL) {
+ return;
+ }
+ onTransformWorkspace(
+ WorkspaceFlavour.LOCAL,
+ WorkspaceFlavour.AFFINE_CLOUD,
+ workspace
+ );
+ setOpen(false);
+ }, [onTransformWorkspace, workspace]);
+
+ if (
+ showLocalDemoTips &&
+ !environment.isDesktop &&
+ workspace.flavour === WorkspaceFlavour.LOCAL
+ ) {
+ return (
+ <>
+ setOpen(true)}
+ onClose={() => {
+ setShowLocalDemoTips(false);
+ }}
+ />
+
+ >
+ );
+ }
+
+ return (
+ }
+ onClose={() => {
+ setShowWarning(false);
+ }}
+ />
+ );
+};
diff --git a/packages/frontend/core/src/components/workspace-header.tsx b/packages/frontend/core/src/components/workspace-header.tsx
index 719a35b409..18d15026e1 100644
--- a/packages/frontend/core/src/components/workspace-header.tsx
+++ b/packages/frontend/core/src/components/workspace-header.tsx
@@ -29,6 +29,7 @@ import { filterContainerStyle } from './filter-container.css';
import { Header } from './pure/header';
import { PluginHeader } from './pure/plugin-header';
import { WorkspaceModeFilterTab } from './pure/workspace-mode-filter-tab';
+import { TopTip } from './top-tip';
import * as styles from './workspace-header.css';
const FilterContainer = ({ workspaceId }: { workspaceId: string }) => {
@@ -161,23 +162,26 @@ export function WorkspaceHeader({
) : null;
return (
-
- }
- right={
-
- }
- bottomBorder
- />
+ <>
+
+ }
+ right={
+
+ }
+ bottomBorder
+ />
+
+ >
);
}
diff --git a/tests/affine-local/e2e/open-affine.spec.ts b/tests/affine-local/e2e/open-affine.spec.ts
index 9c80920342..2a9b639808 100644
--- a/tests/affine-local/e2e/open-affine.spec.ts
+++ b/tests/affine-local/e2e/open-affine.spec.ts
@@ -25,22 +25,20 @@ test('Open last workspace when back to affine', async ({ page }) => {
expect(currentWorkspaceName).toEqual('New Workspace 2');
});
-test.skip('Download client tip', async ({ page }) => {
+test('Download client tip', async ({ page }) => {
await openHomePage(page);
- const downloadClientTipItem = page.locator(
- '[data-testid=download-client-tip]'
- );
- await expect(downloadClientTipItem).toBeVisible();
+ const localDemoTipsItem = page.locator('[data-testid=local-demo-tips]');
+ await expect(localDemoTipsItem).toBeVisible();
const closeButton = page.locator(
- '[data-testid=download-client-tip-close-button]'
+ '[data-testid=local-demo-tips-close-button]'
);
await closeButton.click();
- await expect(downloadClientTipItem).not.toBeVisible();
- await page.goto('http://localhost:8080');
- const currentDownloadClientTipItem = page.locator(
- '[data-testid=download-client-tip]'
+ await expect(localDemoTipsItem).not.toBeVisible();
+ await page.reload();
+ const currentLocalDemoTipsItemItem = page.locator(
+ '[data-testid=local-demo-tips]'
);
- await expect(currentDownloadClientTipItem).toBeVisible();
+ await expect(currentLocalDemoTipsItemItem).toBeVisible();
});
test('Check the class name for the scrollbar', async ({ page }) => {
diff --git a/tests/storybook/src/stories/affine-banner.stories.tsx b/tests/storybook/src/stories/affine-banner.stories.tsx
index 618a7752cc..27f95e66a2 100644
--- a/tests/storybook/src/stories/affine-banner.stories.tsx
+++ b/tests/storybook/src/stories/affine-banner.stories.tsx
@@ -1,4 +1,4 @@
-import { BrowserWarning, DownloadTips } from '@affine/component/affine-banner';
+import { BrowserWarning, LocalDemoTips } from '@affine/component/affine-banner';
import type { StoryFn } from '@storybook/react';
import { useState } from 'react';
@@ -24,9 +24,13 @@ export const Default: StoryFn = () => {
export const Download: StoryFn = () => {
const [, setIsClosed] = useState(true);
+ const [isLoggedIn, setIsLoggedIn] = useState(false);
return (
- setIsLoggedIn(true)}
+ onEnableCloud={() => {}}
onClose={() => {
setIsClosed(false);
}}