feat: add download tips banner (#2151)

This commit is contained in:
JimmFly 2023-05-10 00:07:34 +08:00 committed by GitHub
parent b937c1b5f6
commit b978bb171a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 282 additions and 27 deletions

View File

@ -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,
}));
}
);

View File

@ -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;

View File

@ -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"

View File

@ -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;

View File

@ -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? &nbsp;
<a
className={linkStyle}
href="https://github.com/toeverything/AFFiNE/releases"
target="_blank"
rel="noreferrer"
>
Download the AFFiNE Client
</a>
&nbsp;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;

View 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',
},
});

View File

@ -0,0 +1,2 @@
export * from './browser-warning';
export * from './download-client';

View 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>
);
};

View File

@ -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();
});