mirror of
https://github.com/toeverything/AFFiNE.git
synced 2024-11-26 02:51:57 +03:00
feat(core): add download app button to web (#5023)
This commit is contained in:
parent
3499dbbb7f
commit
ad2d3b9167
@ -1,47 +0,0 @@
|
||||
import { Trans } from '@affine/i18n';
|
||||
import { CloseIcon, Logo1Icon } 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}>
|
||||
<Logo1Icon className={downloadTipIconStyle} />
|
||||
<div className={downloadMessageStyle}>
|
||||
<Trans i18nKey="com.affine.banner.content">
|
||||
This demo is limited.
|
||||
<a
|
||||
className={linkStyle}
|
||||
href="https://affine.pro/download"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
Download the AFFiNE Client
|
||||
</a>
|
||||
for the latest features and Performance.
|
||||
</Trans>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className={downloadCloseButtonStyle}
|
||||
onClick={onClose}
|
||||
data-testid="download-client-tip-close-button"
|
||||
>
|
||||
<CloseIcon className={downloadTipIconStyle} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default DownloadTips;
|
@ -1,13 +1,4 @@
|
||||
import { keyframes, style } from '@vanilla-extract/css';
|
||||
|
||||
const slideDown = keyframes({
|
||||
'0%': {
|
||||
height: '0px',
|
||||
},
|
||||
'100%': {
|
||||
height: '44px',
|
||||
},
|
||||
});
|
||||
import { style } from '@vanilla-extract/css';
|
||||
|
||||
export const browserWarningStyle = style({
|
||||
backgroundColor: 'var(--affine-background-warning-color)',
|
||||
@ -36,52 +27,31 @@ export const closeIconStyle = style({
|
||||
position: 'relative',
|
||||
zIndex: 1,
|
||||
});
|
||||
export const downloadTipContainerStyle = style({
|
||||
backgroundColor: 'var(--affine-primary-color)',
|
||||
color: 'var(--affine-white)',
|
||||
export const tipsContainer = style({
|
||||
backgroundColor: 'var(--affine-background-error-color)',
|
||||
color: 'var(--affine-error-color)',
|
||||
width: '100%',
|
||||
height: '44px',
|
||||
fontSize: 'var(--affine-font-base)',
|
||||
fontSize: 'var(--affine-font-sm)',
|
||||
fontWeight: '700',
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'center',
|
||||
position: 'relative',
|
||||
animation: `${slideDown} .3s ease-in-out forwards`,
|
||||
padding: '12px 16px',
|
||||
position: 'sticky',
|
||||
gap: '16px',
|
||||
containerType: 'inline-size',
|
||||
});
|
||||
export const downloadTipStyle = style({
|
||||
|
||||
export const tipsMessage = style({
|
||||
color: 'var(--affine-error-color)',
|
||||
flexGrow: 1,
|
||||
flexShrink: 1,
|
||||
});
|
||||
|
||||
export const tipsRightItem = style({
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
flexShrink: 0,
|
||||
justifyContent: 'space-between',
|
||||
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',
|
||||
},
|
||||
gap: '16px',
|
||||
});
|
||||
|
@ -1,2 +1,2 @@
|
||||
export * from './browser-warning';
|
||||
export * from './download-client';
|
||||
export * from './local-demo-tips';
|
||||
|
@ -0,0 +1,54 @@
|
||||
import { CloseIcon } from '@blocksuite/icons';
|
||||
import { Button, IconButton } from '@toeverything/components/button';
|
||||
import { useCallback } from 'react';
|
||||
|
||||
import * as styles from './index.css';
|
||||
|
||||
type LocalDemoTipsProps = {
|
||||
isLoggedIn: boolean;
|
||||
onLogin: () => void;
|
||||
onEnableCloud: () => void;
|
||||
onClose: () => void;
|
||||
};
|
||||
|
||||
export const LocalDemoTips = ({
|
||||
onClose,
|
||||
isLoggedIn,
|
||||
onLogin,
|
||||
onEnableCloud,
|
||||
}: LocalDemoTipsProps) => {
|
||||
const content = isLoggedIn
|
||||
? 'This is a local demo workspace, and the data is stored locally. We recommend enabling AFFiNE Cloud.'
|
||||
: 'This is a local demo workspace, and the data is stored locally in the browser. We recommend Enabling AFFiNE Cloud or downloading the client for a better experience.';
|
||||
|
||||
const buttonLabel = isLoggedIn
|
||||
? 'Enable AFFiNE Cloud'
|
||||
: 'Sign in with AFFiNE Cloud';
|
||||
|
||||
const handleClick = useCallback(() => {
|
||||
if (isLoggedIn) {
|
||||
return onEnableCloud();
|
||||
}
|
||||
return onLogin();
|
||||
}, [isLoggedIn, onEnableCloud, onLogin]);
|
||||
|
||||
return (
|
||||
<div className={styles.tipsContainer} data-testid="local-demo-tips">
|
||||
<div className={styles.tipsMessage}>{content}</div>
|
||||
|
||||
<div className={styles.tipsRightItem}>
|
||||
<div>
|
||||
<Button onClick={handleClick}>{buttonLabel}</Button>
|
||||
</div>
|
||||
<IconButton
|
||||
onClick={onClose}
|
||||
data-testid="local-demo-tips-close-button"
|
||||
>
|
||||
<CloseIcon />
|
||||
</IconButton>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default LocalDemoTips;
|
@ -0,0 +1,22 @@
|
||||
import { style } from '@vanilla-extract/css';
|
||||
export {
|
||||
closeIcon,
|
||||
ellipsisTextOverflow,
|
||||
halo,
|
||||
icon,
|
||||
particles,
|
||||
root,
|
||||
} from '../app-updater-button/index.css';
|
||||
|
||||
export const rootPadding = style({
|
||||
padding: '0 24px',
|
||||
});
|
||||
|
||||
export const label = style({
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
fontSize: 'var(--affine-font-sm)',
|
||||
whiteSpace: 'nowrap',
|
||||
});
|
@ -0,0 +1,53 @@
|
||||
import { CloseIcon, DownloadIcon } from '@blocksuite/icons';
|
||||
import clsx from 'clsx';
|
||||
import { useCallback, useState } from 'react';
|
||||
|
||||
import * as styles from './index.css';
|
||||
|
||||
// Although it is called an input, it is actually a button.
|
||||
export function AppDownloadButton({
|
||||
className,
|
||||
style,
|
||||
}: {
|
||||
className?: string;
|
||||
style?: React.CSSProperties;
|
||||
}) {
|
||||
const [show, setShow] = useState(true);
|
||||
|
||||
const handleClose = useCallback(() => {
|
||||
setShow(false);
|
||||
}, []);
|
||||
|
||||
// TODO: unify this type of literal value.
|
||||
const handleClick = useCallback(() => {
|
||||
const url = `https://affine.pro/download?channel=stable`;
|
||||
open(url, '_blank');
|
||||
}, []);
|
||||
|
||||
if (!show) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<button
|
||||
style={style}
|
||||
className={clsx([styles.root, styles.rootPadding, className])}
|
||||
onClick={handleClick}
|
||||
>
|
||||
<div className={clsx([styles.label])}>
|
||||
<DownloadIcon className={styles.icon} />
|
||||
<span className={styles.ellipsisTextOverflow}>Download App</span>
|
||||
</div>
|
||||
<div
|
||||
className={styles.closeIcon}
|
||||
onClick={e => {
|
||||
e.stopPropagation();
|
||||
handleClose();
|
||||
}}
|
||||
>
|
||||
<CloseIcon />
|
||||
</div>
|
||||
<div className={styles.particles} aria-hidden="true"></div>
|
||||
<span className={styles.halo} aria-hidden="true"></span>
|
||||
</button>
|
||||
);
|
||||
}
|
@ -159,6 +159,7 @@ export const AppSidebarFallback = (): ReactElement | null => {
|
||||
};
|
||||
|
||||
export * from './add-page-button';
|
||||
export * from './app-download-button';
|
||||
export * from './app-updater-button';
|
||||
export * from './category-divider';
|
||||
export * from './index.css';
|
||||
|
@ -10,7 +10,6 @@ import type { ReactNode } from 'react';
|
||||
import { forwardRef, useRef } from 'react';
|
||||
|
||||
import * as style from './style.css';
|
||||
import { TopTip } from './top-tip';
|
||||
import { WindowsAppControls } from './windows-app-controls';
|
||||
|
||||
interface HeaderPros {
|
||||
@ -49,61 +48,58 @@ export const Header = forwardRef<HTMLDivElement, HeaderPros>(function Header(
|
||||
const open = useAtomValue(appSidebarOpenAtom);
|
||||
const appSidebarFloating = useAtomValue(appSidebarFloatingAtom);
|
||||
return (
|
||||
<>
|
||||
<TopTip />
|
||||
<div
|
||||
className={clsx(style.header, bottomBorder && style.bottomBorder)}
|
||||
// data-has-warning={showWarning}
|
||||
data-open={open}
|
||||
data-sidebar-floating={appSidebarFloating}
|
||||
data-testid="header"
|
||||
ref={ref}
|
||||
>
|
||||
<div
|
||||
className={clsx(style.header, bottomBorder && style.bottomBorder)}
|
||||
// data-has-warning={showWarning}
|
||||
data-open={open}
|
||||
data-sidebar-floating={appSidebarFloating}
|
||||
data-testid="header"
|
||||
ref={ref}
|
||||
className={clsx(style.headerSideContainer, {
|
||||
block: isTinyScreen,
|
||||
})}
|
||||
>
|
||||
<div
|
||||
className={clsx(style.headerSideContainer, {
|
||||
block: isTinyScreen,
|
||||
})}
|
||||
className={clsx(
|
||||
style.headerItem,
|
||||
'top-item',
|
||||
!open ? 'top-item-visible' : ''
|
||||
)}
|
||||
>
|
||||
<div
|
||||
className={clsx(
|
||||
style.headerItem,
|
||||
'top-item',
|
||||
!open ? 'top-item-visible' : ''
|
||||
)}
|
||||
>
|
||||
<div ref={sidebarSwitchRef}>
|
||||
<SidebarSwitch show={!open} />
|
||||
</div>
|
||||
</div>
|
||||
<div className={clsx(style.headerItem, 'left')}>
|
||||
<div ref={leftSlotRef}>{left}</div>
|
||||
<div ref={sidebarSwitchRef}>
|
||||
<SidebarSwitch show={!open} />
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className={clsx({
|
||||
[style.headerCenter]: center,
|
||||
'is-window': isWindowsDesktop,
|
||||
})}
|
||||
ref={centerSlotRef}
|
||||
>
|
||||
{center}
|
||||
</div>
|
||||
<div
|
||||
className={clsx(style.headerSideContainer, 'right', {
|
||||
block: isTinyScreen,
|
||||
})}
|
||||
>
|
||||
<div className={clsx(style.headerItem, 'top-item')}>
|
||||
<div ref={windowControlsRef}>
|
||||
{isWindowsDesktop ? <WindowsAppControls /> : null}
|
||||
</div>
|
||||
</div>
|
||||
<div className={clsx(style.headerItem, 'right')}>
|
||||
<div ref={rightSlotRef}>{right}</div>
|
||||
</div>
|
||||
<div className={clsx(style.headerItem, 'left')}>
|
||||
<div ref={leftSlotRef}>{left}</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
<div
|
||||
className={clsx({
|
||||
[style.headerCenter]: center,
|
||||
'is-window': isWindowsDesktop,
|
||||
})}
|
||||
ref={centerSlotRef}
|
||||
>
|
||||
{center}
|
||||
</div>
|
||||
<div
|
||||
className={clsx(style.headerSideContainer, 'right', {
|
||||
block: isTinyScreen,
|
||||
})}
|
||||
>
|
||||
<div className={clsx(style.headerItem, 'top-item')}>
|
||||
<div ref={windowControlsRef}>
|
||||
{isWindowsDesktop ? <WindowsAppControls /> : null}
|
||||
</div>
|
||||
</div>
|
||||
<div className={clsx(style.headerItem, 'right')}>
|
||||
<div ref={rightSlotRef}>{right}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
|
@ -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 (
|
||||
<span>
|
||||
<Trans i18nKey="recommendBrowser">
|
||||
We recommend the <strong>Chrome</strong> browser for an optimal
|
||||
experience.
|
||||
</Trans>
|
||||
</span>
|
||||
);
|
||||
} else if (notGoodVersion) {
|
||||
return <span>{t['upgradeBrowser']()}</span>;
|
||||
}
|
||||
return null;
|
||||
};
|
||||
export const TopTip = () => {
|
||||
const [showWarning, setShowWarning] = useState(false);
|
||||
const [showDownloadTip, setShowDownloadTip] = useAtom(
|
||||
guideDownloadClientTipAtom
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
setShowWarning(shouldShowWarning());
|
||||
}, []);
|
||||
|
||||
if (showDownloadTip && environment.isDesktop) {
|
||||
return (
|
||||
<DownloadTips
|
||||
onClose={() => {
|
||||
setShowDownloadTip(false);
|
||||
localStorage.setItem('affine-is-dt-hide', '1');
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<BrowserWarning
|
||||
show={showWarning}
|
||||
message={<OSWarningMessage />}
|
||||
onClose={() => {
|
||||
setShowWarning(false);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
@ -5,6 +5,7 @@ import {
|
||||
} from '@affine/workspace/providers';
|
||||
import {
|
||||
CloudWorkspaceIcon,
|
||||
InformationFillDuotoneIcon,
|
||||
LocalWorkspaceIcon,
|
||||
NoNetworkIcon,
|
||||
UnsyncIcon,
|
||||
@ -67,7 +68,11 @@ const UnSyncWorkspaceStatus = () => {
|
||||
const LocalWorkspaceStatus = () => {
|
||||
return (
|
||||
<>
|
||||
<LocalWorkspaceIcon />
|
||||
{!environment.isDesktop ? (
|
||||
<InformationFillDuotoneIcon data-warning-color="true" />
|
||||
) : (
|
||||
<LocalWorkspaceIcon />
|
||||
)}
|
||||
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) {
|
||||
|
@ -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)',
|
||||
},
|
||||
},
|
||||
};
|
||||
});
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { AnimatedDeleteIcon } from '@affine/component';
|
||||
import {
|
||||
AddPageButton,
|
||||
AppDownloadButton,
|
||||
AppSidebar,
|
||||
appSidebarOpenAtom,
|
||||
AppUpdaterButton,
|
||||
@ -266,7 +267,7 @@ export const RootAppSidebar = ({
|
||||
)}
|
||||
</SidebarScrollableContainer>
|
||||
<SidebarContainer>
|
||||
{environment.isDesktop && <AppUpdaterButton />}
|
||||
{environment.isDesktop ? <AppUpdaterButton /> : <AppDownloadButton />}
|
||||
<div style={{ height: '4px' }} />
|
||||
<AddPageButton onClick={onClickNewPage} />
|
||||
</SidebarContainer>
|
||||
|
122
packages/frontend/core/src/components/top-tip.tsx
Normal file
122
packages/frontend/core/src/components/top-tip.tsx
Normal file
@ -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 (
|
||||
<span>
|
||||
<Trans i18nKey="recommendBrowser">
|
||||
We recommend the <strong>Chrome</strong> browser for an optimal
|
||||
experience.
|
||||
</Trans>
|
||||
</span>
|
||||
);
|
||||
} else if (notGoodVersion) {
|
||||
return <span>{t['upgradeBrowser']()}</span>;
|
||||
}
|
||||
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 (
|
||||
<>
|
||||
<LocalDemoTips
|
||||
isLoggedIn={isLoggedIn}
|
||||
onLogin={onLogin}
|
||||
onEnableCloud={() => setOpen(true)}
|
||||
onClose={() => {
|
||||
setShowLocalDemoTips(false);
|
||||
}}
|
||||
/>
|
||||
<EnableAffineCloudModal
|
||||
open={open}
|
||||
onOpenChange={setOpen}
|
||||
onConfirm={handleConfirm}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<BrowserWarning
|
||||
show={showWarning}
|
||||
message={<OSWarningMessage />}
|
||||
onClose={() => {
|
||||
setShowWarning(false);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
@ -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({
|
||||
<SharePageModal workspace={currentWorkspace} page={currentPage} />
|
||||
) : null;
|
||||
return (
|
||||
<Header
|
||||
mainContainerAtom={mainContainerAtom}
|
||||
ref={setAppHeader}
|
||||
center={
|
||||
<BlockSuiteHeaderTitle
|
||||
workspace={currentWorkspace}
|
||||
pageId={currentEntry.pageId}
|
||||
/>
|
||||
}
|
||||
right={
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: '8px' }}>
|
||||
{sharePageModal}
|
||||
<PluginHeader />
|
||||
</div>
|
||||
}
|
||||
bottomBorder
|
||||
/>
|
||||
<>
|
||||
<Header
|
||||
mainContainerAtom={mainContainerAtom}
|
||||
ref={setAppHeader}
|
||||
center={
|
||||
<BlockSuiteHeaderTitle
|
||||
workspace={currentWorkspace}
|
||||
pageId={currentEntry.pageId}
|
||||
/>
|
||||
}
|
||||
right={
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: '8px' }}>
|
||||
{sharePageModal}
|
||||
<PluginHeader />
|
||||
</div>
|
||||
}
|
||||
bottomBorder
|
||||
/>
|
||||
<TopTip workspace={currentWorkspace} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -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 }) => {
|
||||
|
@ -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 (
|
||||
<div>
|
||||
<DownloadTips
|
||||
<LocalDemoTips
|
||||
isLoggedIn={isLoggedIn}
|
||||
onLogin={() => setIsLoggedIn(true)}
|
||||
onEnableCloud={() => {}}
|
||||
onClose={() => {
|
||||
setIsClosed(false);
|
||||
}}
|
||||
|
Loading…
Reference in New Issue
Block a user