feat(core): add download app button to web (#5023)

This commit is contained in:
JimmFly
2023-11-23 14:33:25 +08:00
committed by GitHub
parent 3499dbbb7f
commit ad2d3b9167
16 changed files with 369 additions and 266 deletions

View File

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

View File

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

View File

@@ -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) {

View File

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

View File

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

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

View File

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