mirror of
https://github.com/toeverything/AFFiNE.git
synced 2024-11-24 11:03:48 +03:00
feat: add download tips banner (#2151)
This commit is contained in:
parent
b937c1b5f6
commit
b978bb171a
@ -10,12 +10,15 @@ export type Guide = {
|
||||
changeLog: boolean;
|
||||
// should show recording tips
|
||||
onBoarding: boolean;
|
||||
// should show download client tips
|
||||
downloadClientTip: boolean;
|
||||
};
|
||||
|
||||
const guidePrimitiveAtom = atomWithStorage<Guide>('helper-guide', {
|
||||
quickSearchTips: true,
|
||||
changeLog: true,
|
||||
onBoarding: true,
|
||||
downloadClientTip: true,
|
||||
});
|
||||
|
||||
export const guideQuickSearchTipsAtom = atom<
|
||||
@ -67,3 +70,18 @@ export const guideOnboardingAtom = atom<
|
||||
}));
|
||||
}
|
||||
);
|
||||
export const guideDownloadClientTipAtom = atom<
|
||||
Guide['downloadClientTip'],
|
||||
[open: boolean],
|
||||
void
|
||||
>(
|
||||
get => {
|
||||
return get(guidePrimitiveAtom).downloadClientTip;
|
||||
},
|
||||
(_, set, open) => {
|
||||
set(guidePrimitiveAtom, tips => ({
|
||||
...tips,
|
||||
downloadClientTip: open,
|
||||
}));
|
||||
}
|
||||
);
|
||||
|
@ -0,0 +1,22 @@
|
||||
import { DownloadTips } from '@affine/component/affine-banner';
|
||||
import { getEnvironment } from '@affine/env';
|
||||
import { useAtom } from 'jotai';
|
||||
import { useCallback } from 'react';
|
||||
|
||||
import { guideDownloadClientTipAtom } from '../../../atoms/guide';
|
||||
|
||||
export const DownloadClientTip = () => {
|
||||
const env = getEnvironment();
|
||||
const [showDownloadClientTips, setShowDownloadClientTips] = useAtom(
|
||||
guideDownloadClientTipAtom
|
||||
);
|
||||
const onCloseDownloadClient = useCallback(() => {
|
||||
setShowDownloadClientTips(false);
|
||||
}, [setShowDownloadClientTips]);
|
||||
|
||||
if (!showDownloadClientTips || env.isDesktop) {
|
||||
return <></>;
|
||||
}
|
||||
return <DownloadTips onClose={onCloseDownloadClient} />;
|
||||
};
|
||||
export default DownloadClientTip;
|
@ -1,7 +1,7 @@
|
||||
import { BrowserWarning } from '@affine/component/affine-banner';
|
||||
import { appSidebarOpenAtom } from '@affine/component/app-sidebar';
|
||||
import { useAFFiNEI18N } from '@affine/i18n/hooks';
|
||||
import { WorkspaceFlavour } from '@affine/workspace/type';
|
||||
import { CloseIcon } from '@blocksuite/icons';
|
||||
import type { Page } from '@blocksuite/store';
|
||||
import { useAtom } from 'jotai';
|
||||
import type { FC, HTMLAttributes, PropsWithChildren } from 'react';
|
||||
@ -14,8 +14,10 @@ import {
|
||||
useState,
|
||||
} from 'react';
|
||||
|
||||
import { guideDownloadClientTipAtom } from '../../../atoms/guide';
|
||||
import { useCurrentMode } from '../../../hooks/current/use-current-mode';
|
||||
import type { AffineOfficialWorkspace } from '../../../shared';
|
||||
import { DownloadClientTip } from './download-tips';
|
||||
import EditPage from './header-right-items/edit-page';
|
||||
import { EditorOptionMenu } from './header-right-items/editor-option-menu';
|
||||
import { HeaderShareMenu } from './header-right-items/share-menu';
|
||||
@ -23,8 +25,6 @@ import SyncUser from './header-right-items/sync-user';
|
||||
import TrashButtonGroup from './header-right-items/trash-button-group';
|
||||
import UserAvatar from './header-right-items/user-avatar';
|
||||
import {
|
||||
StyledBrowserWarning,
|
||||
StyledCloseButton,
|
||||
StyledHeader,
|
||||
StyledHeaderContainer,
|
||||
StyledHeaderRightSide,
|
||||
@ -37,23 +37,6 @@ const SidebarSwitch = lazy(() =>
|
||||
}))
|
||||
);
|
||||
|
||||
const BrowserWarning = ({
|
||||
show,
|
||||
onClose,
|
||||
}: {
|
||||
show: boolean;
|
||||
onClose: () => void;
|
||||
}) => {
|
||||
return (
|
||||
<StyledBrowserWarning show={show}>
|
||||
<OSWarningMessage />
|
||||
<StyledCloseButton onClick={onClose}>
|
||||
<CloseIcon />
|
||||
</StyledCloseButton>
|
||||
</StyledBrowserWarning>
|
||||
);
|
||||
};
|
||||
|
||||
export type BaseHeaderProps<
|
||||
Workspace extends AffineOfficialWorkspace = AffineOfficialWorkspace
|
||||
> = {
|
||||
@ -130,9 +113,15 @@ export const Header = forwardRef<
|
||||
PropsWithChildren<HeaderProps> & HTMLAttributes<HTMLDivElement>
|
||||
>((props, ref) => {
|
||||
const [showWarning, setShowWarning] = useState(false);
|
||||
const [showGuideDownloadClientTip, setShowGuideDownloadClientTip] =
|
||||
useState(false);
|
||||
const [shouldShowGuideDownloadClientTip] = useAtom(
|
||||
guideDownloadClientTipAtom
|
||||
);
|
||||
useEffect(() => {
|
||||
setShowWarning(shouldShowWarning());
|
||||
}, []);
|
||||
setShowGuideDownloadClientTip(shouldShowGuideDownloadClientTip);
|
||||
}, [shouldShowGuideDownloadClientTip]);
|
||||
const [open] = useAtom(appSidebarOpenAtom);
|
||||
const t = useAFFiNEI18N();
|
||||
|
||||
@ -144,12 +133,18 @@ export const Header = forwardRef<
|
||||
data-open={open}
|
||||
{...props}
|
||||
>
|
||||
<BrowserWarning
|
||||
show={showWarning}
|
||||
onClose={() => {
|
||||
setShowWarning(false);
|
||||
}}
|
||||
/>
|
||||
{showGuideDownloadClientTip ? (
|
||||
<DownloadClientTip />
|
||||
) : (
|
||||
<BrowserWarning
|
||||
show={showWarning}
|
||||
message={<OSWarningMessage />}
|
||||
onClose={() => {
|
||||
setShowWarning(false);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
|
||||
<StyledHeader
|
||||
hasWarning={showWarning}
|
||||
data-testid="editor-header-items"
|
||||
|
@ -0,0 +1,32 @@
|
||||
import { CloseIcon } from '@blocksuite/icons';
|
||||
import type React from 'react';
|
||||
|
||||
import {
|
||||
browserWarningStyle,
|
||||
closeButtonStyle,
|
||||
closeIconStyle,
|
||||
} from './index.css';
|
||||
|
||||
export const BrowserWarning = ({
|
||||
show,
|
||||
onClose,
|
||||
message,
|
||||
}: {
|
||||
show: boolean;
|
||||
onClose: () => void;
|
||||
message: React.ReactNode;
|
||||
}) => {
|
||||
if (!show) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<div className={browserWarningStyle}>
|
||||
{message}
|
||||
<div className={closeButtonStyle} onClick={onClose}>
|
||||
<CloseIcon className={closeIconStyle} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default BrowserWarning;
|
@ -0,0 +1,44 @@
|
||||
import { AffineLogoSimSBlue1_1Icon, CloseIcon } from '@blocksuite/icons';
|
||||
|
||||
import {
|
||||
downloadCloseButtonStyle,
|
||||
downloadMessageStyle,
|
||||
downloadTipContainerStyle,
|
||||
downloadTipIconStyle,
|
||||
downloadTipStyle,
|
||||
linkStyle,
|
||||
} from './index.css';
|
||||
|
||||
export const DownloadTips = ({ onClose }: { onClose: () => void }) => {
|
||||
return (
|
||||
<div
|
||||
className={downloadTipContainerStyle}
|
||||
data-testid="download-client-tip"
|
||||
>
|
||||
<div className={downloadTipStyle}>
|
||||
<AffineLogoSimSBlue1_1Icon className={downloadTipIconStyle} />
|
||||
<div className={downloadMessageStyle}>
|
||||
Enjoying the demo?
|
||||
<a
|
||||
className={linkStyle}
|
||||
href="https://github.com/toeverything/AFFiNE/releases"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
Download the AFFiNE Client
|
||||
</a>
|
||||
for the full experience.
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className={downloadCloseButtonStyle}
|
||||
onClick={onClose}
|
||||
data-testid="download-client-tip-close-button"
|
||||
>
|
||||
<CloseIcon className={downloadTipIconStyle} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default DownloadTips;
|
87
packages/component/src/components/affine-banner/index.css.ts
Normal file
87
packages/component/src/components/affine-banner/index.css.ts
Normal file
@ -0,0 +1,87 @@
|
||||
import { keyframes, style } from '@vanilla-extract/css';
|
||||
|
||||
const slideDown = keyframes({
|
||||
'0%': {
|
||||
height: '0px',
|
||||
},
|
||||
'100%': {
|
||||
height: '44px',
|
||||
},
|
||||
});
|
||||
|
||||
export const browserWarningStyle = style({
|
||||
backgroundColor: 'var(--affine-background-warning-color)',
|
||||
color: 'var(--affine-warning-color)',
|
||||
height: '36px',
|
||||
fontSize: 'var(--affine-font-sm)',
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
position: 'relative',
|
||||
});
|
||||
export const closeButtonStyle = style({
|
||||
width: '36px',
|
||||
height: '36px',
|
||||
color: 'var(--affine-icon-color)',
|
||||
cursor: 'pointer',
|
||||
display: 'flex',
|
||||
justifyContent: 'flex-end',
|
||||
alignItems: 'center',
|
||||
position: 'absolute',
|
||||
right: '16px',
|
||||
});
|
||||
export const closeIconStyle = style({
|
||||
width: '15px',
|
||||
height: '15px',
|
||||
position: 'relative',
|
||||
zIndex: 1,
|
||||
});
|
||||
export const downloadTipContainerStyle = style({
|
||||
backgroundColor: 'var(--affine-primary-color)',
|
||||
color: 'var(--affine-white)',
|
||||
width: '100%',
|
||||
height: '44px',
|
||||
fontSize: 'var(--affine-font-base)',
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
position: 'relative',
|
||||
animation: `${slideDown} .3s ease-in-out forwards`,
|
||||
});
|
||||
export const downloadTipStyle = style({
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
});
|
||||
export const downloadTipIconStyle = style({
|
||||
color: 'var(--affine-white)',
|
||||
width: '24px',
|
||||
height: '24px',
|
||||
fontSize: '24px',
|
||||
position: 'relative',
|
||||
zIndex: 1,
|
||||
});
|
||||
export const downloadCloseButtonStyle = style({
|
||||
color: 'var(--affine-white)',
|
||||
cursor: 'pointer',
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
position: 'absolute',
|
||||
right: '24px',
|
||||
});
|
||||
export const downloadMessageStyle = style({
|
||||
color: 'var(--affine-white)',
|
||||
marginLeft: '8px',
|
||||
});
|
||||
export const linkStyle = style({
|
||||
color: 'var(--affine-white)',
|
||||
textDecoration: 'underline',
|
||||
':hover': {
|
||||
textDecoration: 'underline',
|
||||
},
|
||||
':visited': {
|
||||
color: 'var(--affine-white)',
|
||||
textDecoration: 'underline',
|
||||
},
|
||||
});
|
@ -0,0 +1,2 @@
|
||||
export * from './browser-warning';
|
||||
export * from './download-client';
|
37
packages/component/src/stories/affine-banner.stories.tsx
Normal file
37
packages/component/src/stories/affine-banner.stories.tsx
Normal file
@ -0,0 +1,37 @@
|
||||
import type { StoryFn } from '@storybook/react';
|
||||
import { useState } from 'react';
|
||||
|
||||
import { BrowserWarning, DownloadTips } from '../components/affine-banner';
|
||||
|
||||
export default {
|
||||
title: 'AFFiNE/Banner',
|
||||
component: BrowserWarning,
|
||||
};
|
||||
|
||||
export const Default: StoryFn = () => {
|
||||
const [closed, setIsClosed] = useState(true);
|
||||
return (
|
||||
<div>
|
||||
<BrowserWarning
|
||||
message={<span>test</span>}
|
||||
show={closed}
|
||||
onClose={() => {
|
||||
setIsClosed(false);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export const Download: StoryFn = () => {
|
||||
const [, setIsClosed] = useState(true);
|
||||
return (
|
||||
<div>
|
||||
<DownloadTips
|
||||
onClose={() => {
|
||||
setIsClosed(false);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
@ -54,3 +54,21 @@ test('Click right-bottom corner change log icon', async ({ page }) => {
|
||||
);
|
||||
expect(await normalRightBottomChangeLog.isVisible()).toEqual(true);
|
||||
});
|
||||
|
||||
test('Download client tip', async ({ page }) => {
|
||||
await openHomePage(page);
|
||||
const downloadClientTipItem = page.locator(
|
||||
'[data-testid=download-client-tip]'
|
||||
);
|
||||
await expect(downloadClientTipItem).toBeVisible();
|
||||
const closeButton = page.locator(
|
||||
'[data-testid=download-client-tip-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(currentDownloadClientTipItem).not.toBeVisible();
|
||||
});
|
||||
|
Loading…
Reference in New Issue
Block a user