feat: support force sync by click (#4089)

Co-authored-by: JimmFly <yangjinfei001@gmail.com>
This commit is contained in:
Alex Yang
2023-09-01 01:15:07 -05:00
committed by GitHub
parent 83e7e9db8d
commit 92f0b31196
13 changed files with 341 additions and 117 deletions

View File

@@ -1,4 +1,20 @@
import { style } from '@vanilla-extract/css';
import { createVar, keyframes, style } from '@vanilla-extract/css';
export const workspaceAvatarStyle = style({
flexShrink: 0,
});
export const speedVar = createVar('speedVar');
const rotate = keyframes({
'0%': { transform: 'rotate(0deg)' },
'50%': { transform: 'rotate(180deg)' },
'100%': { transform: 'rotate(360deg)' },
});
export const loading = style({
vars: {
[speedVar]: '1.5s',
},
textRendering: 'optimizeLegibility',
WebkitFontSmoothing: 'antialiased',
animation: `${rotate} ${speedVar} infinite linear`,
});

View File

@@ -0,0 +1,30 @@
import { assignInlineVars } from '@vanilla-extract/dynamic';
import { loading, speedVar } from './index.css';
export type LoadingProps = {
size?: number;
speed?: number;
};
export const Loading = ({ size, speed = 1.2 }: LoadingProps) => {
return (
<svg
className={loading}
viewBox="0 0 1024 1024"
focusable="false"
data-icon="loading"
width={size ? `${size}px` : '.8em'}
height={size ? `${size}px` : '.8em'}
fill="currentColor"
aria-hidden="true"
style={{
...assignInlineVars({
[speedVar]: `${speed}s`,
}),
}}
>
<path d="M988 548c-19.9 0-36-16.1-36-36 0-59.4-11.6-117-34.6-171.3a440.45 440.45 0 00-94.3-139.9 437.71 437.71 0 00-139.9-94.3C629 83.6 571.4 72 512 72c-19.9 0-36-16.1-36-36s16.1-36 36-36c69.1 0 136.2 13.5 199.3 40.3C772.3 66 827 103 874 150c47 47 83.9 101.8 109.7 162.7 26.7 63.1 40.2 130.2 40.2 199.3.1 19.9-16 36-35.9 36z"></path>
</svg>
);
};

View File

@@ -1,18 +1,27 @@
import { WorkspaceAvatar } from '@affine/component/workspace-avatar';
import { WorkspaceFlavour } from '@affine/env/workspace';
import {
CloudWorkspaceIcon,
LocalWorkspaceIcon,
NoNetworkIcon,
UnsyncIcon,
} from '@blocksuite/icons';
import { Tooltip } from '@toeverything/components/tooltip';
import { useBlockSuiteWorkspaceName } from '@toeverything/hooks/use-block-suite-workspace-name';
import type React from 'react';
import { useCallback, useMemo, useState } from 'react';
import { atom, useAtomValue, useSetAtom } from 'jotai';
import {
type KeyboardEvent,
type MouseEvent,
useCallback,
useMemo,
useState,
} from 'react';
import { useCurrentLoginStatus } from '../../../../hooks/affine/use-current-login-status';
import { useDatasourceSync } from '../../../../hooks/use-datasource-sync';
import { useSystemOnline } from '../../../../hooks/use-system-online';
import type { AllWorkspace } from '../../../../shared';
import { workspaceAvatarStyle } from './index.css';
import { Loading } from './loading-icon';
import {
StyledSelectorContainer,
StyledSelectorWrapper,
@@ -25,6 +34,125 @@ export interface WorkspaceSelectorProps {
onClick: () => void;
}
const hoverAtom = atom(false);
const CloudWorkspaceStatus = () => {
return (
<>
<CloudWorkspaceIcon />
AFFiNE Cloud
</>
);
};
const SyncingWorkspaceStatus = () => {
return (
<>
<Loading />
Syncing...
</>
);
};
const UnSyncWorkspaceStatus = () => {
return (
<>
<UnsyncIcon />
Wait for upload
</>
);
};
const LocalWorkspaceStatus = () => {
return (
<>
<LocalWorkspaceIcon />
Local
</>
);
};
const OfflineStatus = () => {
return (
<>
<NoNetworkIcon />
Offline
</>
);
};
const WorkspaceStatus = ({
currentWorkspace,
}: {
currentWorkspace: AllWorkspace;
}) => {
const isOnline = useSystemOnline();
// todo: finish display sync status
const [forceSyncStatus, startForceSync] = useDatasourceSync(
currentWorkspace.blockSuiteWorkspace
);
const setIsHovered = useSetAtom(hoverAtom);
const [container, setContainer] = useState<HTMLDivElement | null>(null);
const content = useMemo(() => {
if (currentWorkspace.flavour === WorkspaceFlavour.LOCAL) {
return 'Saved locally';
}
if (!isOnline) {
return 'Disconnected, please check your network connection';
}
switch (forceSyncStatus.type) {
case 'syncing':
return 'Syncing with AFFiNE Cloud';
case 'error':
return 'Sync failed due to server issues, please try again later.';
default:
return 'Sync with AFFiNE Cloud';
}
}, [currentWorkspace.flavour, forceSyncStatus.type, isOnline]);
const CloudWorkspaceSyncStatus = useCallback(() => {
if (forceSyncStatus.type === 'syncing') {
return SyncingWorkspaceStatus();
} else if (forceSyncStatus.type === 'error') {
return UnSyncWorkspaceStatus();
} else {
return CloudWorkspaceStatus();
}
}, [forceSyncStatus.type]);
return (
<div style={{ display: 'flex' }}>
<Tooltip
content={content}
portalOptions={{
container,
}}
>
<StyledWorkspaceStatus
onMouseEnter={() => {
setIsHovered(true);
}}
ref={setContainer}
onMouseLeave={() => setIsHovered(false)}
onClick={useCallback(
(e: MouseEvent<HTMLDivElement>) => {
e.stopPropagation();
startForceSync();
},
[startForceSync]
)}
>
{currentWorkspace.flavour === WorkspaceFlavour.AFFINE_CLOUD ? (
!isOnline ? (
<OfflineStatus />
) : (
<CloudWorkspaceSyncStatus />
)
) : (
<LocalWorkspaceStatus />
)}
</StyledWorkspaceStatus>
</Tooltip>
</div>
);
};
/**
* @todo-Doma Co-locate WorkspaceListModal with {@link WorkspaceSelector},
* because it's never used elsewhere.
@@ -36,13 +164,11 @@ export const WorkspaceSelector = ({
const [name] = useBlockSuiteWorkspaceName(
currentWorkspace.blockSuiteWorkspace
);
const [isHovered, setIsHovered] = useState(false);
const [container, setContainer] = useState<HTMLDivElement | null>(null);
// Open dialog when `Enter` or `Space` pressed
// TODO-Doma Refactor with `@radix-ui/react-dialog` or other libraries that handle these out of the box and be accessible by default
// TODO: Delete this?
const handleKeyDown = useCallback(
(e: React.KeyboardEvent) => {
(e: KeyboardEvent) => {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
// TODO-Doma Rename this callback to `onOpenDialog` or something to reduce ambiguity.
@@ -51,41 +177,8 @@ export const WorkspaceSelector = ({
},
[onClick]
);
const loginStatus = useCurrentLoginStatus();
const isOnline = useSystemOnline();
const content = useMemo(() => {
if (!isOnline) {
return 'Disconnected, please check your network connection';
}
if (
loginStatus === 'authenticated' &&
currentWorkspace.flavour !== 'local'
) {
return 'Sync with AFFiNE Cloud';
}
return 'Saved locally';
}, [currentWorkspace.flavour, isOnline, loginStatus]);
const isHovered = useAtomValue(hoverAtom);
const WorkspaceStatus = () => {
if (!isOnline) {
return (
<>
<NoNetworkIcon />
Offline
</>
);
}
return (
<>
{currentWorkspace.flavour === 'local' ? (
<LocalWorkspaceIcon />
) : (
<CloudWorkspaceIcon />
)}
{currentWorkspace.flavour === 'local' ? 'Local' : 'AFFiNE Cloud'}
</>
);
};
return (
<StyledSelectorContainer
role="button"
@@ -106,25 +199,7 @@ export const WorkspaceSelector = ({
<StyledWorkspaceName data-testid="workspace-name">
{name}
</StyledWorkspaceName>
<div style={{ display: 'flex' }}>
<Tooltip
content={content}
portalOptions={{
container,
}}
>
<StyledWorkspaceStatus
onMouseEnter={() => {
setIsHovered(true);
}}
ref={setContainer}
onMouseLeave={() => setIsHovered(false)}
onClick={e => e.stopPropagation()}
>
<WorkspaceStatus />
</StyledWorkspaceStatus>
</Tooltip>
</div>
<WorkspaceStatus currentWorkspace={currentWorkspace} />
</StyledSelectorWrapper>
</StyledSelectorContainer>
);