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
9 changed files with 282 additions and 27 deletions

View File

@@ -10,12 +10,15 @@ export type Guide = {
changeLog: boolean; changeLog: boolean;
// should show recording tips // should show recording tips
onBoarding: boolean; onBoarding: boolean;
// should show download client tips
downloadClientTip: boolean;
}; };
const guidePrimitiveAtom = atomWithStorage<Guide>('helper-guide', { const guidePrimitiveAtom = atomWithStorage<Guide>('helper-guide', {
quickSearchTips: true, quickSearchTips: true,
changeLog: true, changeLog: true,
onBoarding: true, onBoarding: true,
downloadClientTip: true,
}); });
export const guideQuickSearchTipsAtom = atom< 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 { appSidebarOpenAtom } from '@affine/component/app-sidebar';
import { useAFFiNEI18N } from '@affine/i18n/hooks'; import { useAFFiNEI18N } from '@affine/i18n/hooks';
import { WorkspaceFlavour } from '@affine/workspace/type'; import { WorkspaceFlavour } from '@affine/workspace/type';
import { CloseIcon } from '@blocksuite/icons';
import type { Page } from '@blocksuite/store'; import type { Page } from '@blocksuite/store';
import { useAtom } from 'jotai'; import { useAtom } from 'jotai';
import type { FC, HTMLAttributes, PropsWithChildren } from 'react'; import type { FC, HTMLAttributes, PropsWithChildren } from 'react';
@@ -14,8 +14,10 @@ import {
useState, useState,
} from 'react'; } from 'react';
import { guideDownloadClientTipAtom } from '../../../atoms/guide';
import { useCurrentMode } from '../../../hooks/current/use-current-mode'; import { useCurrentMode } from '../../../hooks/current/use-current-mode';
import type { AffineOfficialWorkspace } from '../../../shared'; import type { AffineOfficialWorkspace } from '../../../shared';
import { DownloadClientTip } from './download-tips';
import EditPage from './header-right-items/edit-page'; import EditPage from './header-right-items/edit-page';
import { EditorOptionMenu } from './header-right-items/editor-option-menu'; import { EditorOptionMenu } from './header-right-items/editor-option-menu';
import { HeaderShareMenu } from './header-right-items/share-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 TrashButtonGroup from './header-right-items/trash-button-group';
import UserAvatar from './header-right-items/user-avatar'; import UserAvatar from './header-right-items/user-avatar';
import { import {
StyledBrowserWarning,
StyledCloseButton,
StyledHeader, StyledHeader,
StyledHeaderContainer, StyledHeaderContainer,
StyledHeaderRightSide, 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< export type BaseHeaderProps<
Workspace extends AffineOfficialWorkspace = AffineOfficialWorkspace Workspace extends AffineOfficialWorkspace = AffineOfficialWorkspace
> = { > = {
@@ -130,9 +113,15 @@ export const Header = forwardRef<
PropsWithChildren<HeaderProps> & HTMLAttributes<HTMLDivElement> PropsWithChildren<HeaderProps> & HTMLAttributes<HTMLDivElement>
>((props, ref) => { >((props, ref) => {
const [showWarning, setShowWarning] = useState(false); const [showWarning, setShowWarning] = useState(false);
const [showGuideDownloadClientTip, setShowGuideDownloadClientTip] =
useState(false);
const [shouldShowGuideDownloadClientTip] = useAtom(
guideDownloadClientTipAtom
);
useEffect(() => { useEffect(() => {
setShowWarning(shouldShowWarning()); setShowWarning(shouldShowWarning());
}, []); setShowGuideDownloadClientTip(shouldShowGuideDownloadClientTip);
}, [shouldShowGuideDownloadClientTip]);
const [open] = useAtom(appSidebarOpenAtom); const [open] = useAtom(appSidebarOpenAtom);
const t = useAFFiNEI18N(); const t = useAFFiNEI18N();
@@ -144,12 +133,18 @@ export const Header = forwardRef<
data-open={open} data-open={open}
{...props} {...props}
> >
<BrowserWarning {showGuideDownloadClientTip ? (
show={showWarning} <DownloadClientTip />
onClose={() => { ) : (
setShowWarning(false); <BrowserWarning
}} show={showWarning}
/> message={<OSWarningMessage />}
onClose={() => {
setShowWarning(false);
}}
/>
)}
<StyledHeader <StyledHeader
hasWarning={showWarning} hasWarning={showWarning}
data-testid="editor-header-items" 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); 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();
});