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

View File

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

View File

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