feat(core): add loading to quick search modal (#5785)

close AFF-285

add `useSyncEngineStatus` hooks
add loading style

<img width="977" alt="test1" src="https://github.com/toeverything/AFFiNE/assets/102217452/e8bf6714-e42b-4adf-a279-341ef5f5cfc0">
This commit is contained in:
JimmFly 2024-02-20 13:05:22 +00:00
parent 3792506b09
commit 876b85304e
No known key found for this signature in database
GPG Key ID: 14A6F56854E1BED7
5 changed files with 90 additions and 43 deletions

View File

@ -0,0 +1,4 @@
import type { SyncEngineStatus } from '@toeverything/infra';
import { atom } from 'jotai';
export const syncEngineStatusAtom = atom<SyncEngineStatus | null>(null);

View File

@ -5,18 +5,16 @@ export const commandsContainer = style({
height: 'calc(100% - 65px)',
padding: '8px 6px 18px 6px',
});
export const searchInput = style({
export const searchInputContainer = style({
height: 66,
color: cssVar('textPrimaryColor'),
fontSize: cssVar('fontH5'),
padding: '21px 24px',
padding: '18px 16px',
marginBottom: '8px',
width: '100%',
display: 'flex',
alignItems: 'center',
gap: 12,
borderBottom: `1px solid ${cssVar('borderColor')}`,
flexShrink: 0,
'::placeholder': {
color: cssVar('textSecondaryColor'),
},
selectors: {
'&.inEditor': {
paddingTop: '12px',
@ -24,6 +22,14 @@ export const searchInput = style({
},
},
});
export const searchInput = style({
color: cssVar('textPrimaryColor'),
fontSize: cssVar('fontH5'),
width: '100%',
'::placeholder': {
color: cssVar('textSecondaryColor'),
},
});
export const pageTitleWrapper = style({
display: 'flex',
alignItems: 'center',

View File

@ -1,7 +1,10 @@
import { Loading } from '@affine/component/ui/loading';
import { formatDate } from '@affine/core/components/page-list';
import { useSyncEngineStatus } from '@affine/core/hooks/affine/use-sync-engine-status';
import { useAsyncCallback } from '@affine/core/hooks/affine-async-hooks';
import { useAFFiNEI18N } from '@affine/i18n/hooks';
import type { PageMeta } from '@blocksuite/store';
import { SyncEngineStep } from '@toeverything/infra';
import type { CommandCategory } from '@toeverything/infra/command';
import clsx from 'clsx';
import { Command } from 'cmdk';
@ -160,7 +163,7 @@ export const CMDKContainer = ({
const [value, setValue] = useAtom(cmdkValueAtom);
const isInEditor = pageMeta !== undefined;
const [opening, setOpening] = useState(open);
const { syncEngineStatus, progress } = useSyncEngineStatus();
const inputRef = useRef<HTMLInputElement>(null);
// fix list height animation on opening
@ -197,16 +200,29 @@ export const CMDKContainer = ({
</span>
</div>
) : null}
<Command.Input
placeholder={t['com.affine.cmdk.placeholder']()}
ref={inputRef}
{...rest}
value={query}
onValueChange={onQueryChange}
className={clsx(className, styles.searchInput, {
<div
className={clsx(className, styles.searchInputContainer, {
inEditor: isInEditor,
})}
/>
>
{!syncEngineStatus ||
syncEngineStatus.step === SyncEngineStep.Syncing ? (
<Loading
size={24}
progress={progress ? Math.max(progress, 0.2) : undefined}
speed={progress ? 0 : undefined}
/>
) : null}
<Command.Input
placeholder={t['com.affine.cmdk.placeholder']()}
ref={inputRef}
{...rest}
value={query}
onValueChange={onQueryChange}
className={clsx(className, styles.searchInput)}
/>
</div>
<Command.List data-opening={opening ? true : undefined}>
{children}
</Command.List>

View File

@ -4,6 +4,7 @@ import { Loading } from '@affine/component/ui/loading';
import { Tooltip } from '@affine/component/ui/tooltip';
import { openSettingModalAtom } from '@affine/core/atoms';
import { useIsWorkspaceOwner } from '@affine/core/hooks/affine/use-is-workspace-owner';
import { useSyncEngineStatus } from '@affine/core/hooks/affine/use-sync-engine-status';
import { useWorkspaceBlobObjectUrl } from '@affine/core/hooks/use-workspace-blob';
import { useWorkspaceInfo } from '@affine/core/hooks/use-workspace-info';
import { UNTITLED_WORKSPACE_NAME } from '@affine/env/constant';
@ -16,14 +17,10 @@ import {
NoNetworkIcon,
UnsyncIcon,
} from '@blocksuite/icons';
import {
type SyncEngineStatus,
SyncEngineStep,
Workspace,
} from '@toeverything/infra';
import { SyncEngineStep, Workspace } from '@toeverything/infra';
import { useService } from '@toeverything/infra/di';
import { useSetAtom } from 'jotai';
import { debounce, mean } from 'lodash-es';
import { debounce } from 'lodash-es';
import {
forwardRef,
type HTMLAttributes,
@ -97,8 +94,8 @@ const useSyncEngineSyncProgress = () => {
const t = useAFFiNEI18N();
const isOnline = useSystemOnline();
const pushNotification = useSetAtom(pushNotificationAtom);
const [syncEngineStatus, setSyncEngineStatus] =
useState<SyncEngineStatus | null>(null);
const { syncEngineStatus, setSyncEngineStatus, progress } =
useSyncEngineStatus();
const [isOverCapacity, setIsOverCapacity] = useState(false);
const currentWorkspace = useService(Workspace);
@ -159,25 +156,14 @@ const useSyncEngineSyncProgress = () => {
disposable?.dispose();
disposableOverCapacity?.dispose();
};
}, [currentWorkspace, isOwner, jumpToPricePlan, pushNotification, t]);
const progress = useMemo(() => {
if (!syncEngineStatus?.remotes || syncEngineStatus?.remotes.length === 0) {
return null;
}
return mean(
syncEngineStatus.remotes.map(peer => {
if (!peer) {
return 0;
}
const totalTask =
peer.totalDocs + peer.pendingPullUpdates + peer.pendingPushUpdates;
const doneTask = peer.loadedDocs;
return doneTask / totalTask;
})
);
}, [syncEngineStatus?.remotes]);
}, [
currentWorkspace,
isOwner,
jumpToPricePlan,
pushNotification,
setSyncEngineStatus,
t,
]);
const content = useMemo(() => {
// TODO: add i18n

View File

@ -0,0 +1,35 @@
import { syncEngineStatusAtom } from '@affine/core/atoms/sync-engine-status';
import { useAtom } from 'jotai';
import { mean } from 'lodash-es';
import { useMemo } from 'react';
export function useSyncEngineStatus() {
const [syncEngineStatus, setSyncEngineStatus] = useAtom(syncEngineStatusAtom);
const progress = useMemo(() => {
if (!syncEngineStatus?.remotes || syncEngineStatus?.remotes.length === 0) {
return null;
}
return mean(
syncEngineStatus.remotes.map(peer => {
if (!peer) {
return 0;
}
const totalTask =
peer.totalDocs + peer.pendingPullUpdates + peer.pendingPushUpdates;
const doneTask = peer.loadedDocs;
return doneTask / totalTask;
})
);
}, [syncEngineStatus?.remotes]);
return useMemo(
() => ({
syncEngineStatus,
setSyncEngineStatus,
progress,
}),
[progress, setSyncEngineStatus, syncEngineStatus]
);
}