perf(core): optimize rendering of all docs (#12188)

close AF-2605

[CleanShot 2025-05-08 at 13.56.38.mp4 <span class="graphite__hidden">(uploaded via Graphite)</span> <img class="graphite__hidden" src="https://app.graphite.dev/api/v1/graphite/video/thumbnail/LakojjjzZNf6ogjOVwKE/4e36e838-7c7f-4f0a-89a8-fd582c2ef573.mp4" />](https://app.graphite.dev/media/video/LakojjjzZNf6ogjOVwKE/4e36e838-7c7f-4f0a-89a8-fd582c2ef573.mp4)

<!-- This is an auto-generated comment: release notes by coderabbit.ai -->

## Summary by CodeRabbit

- **Refactor**
  - Centralized state management in the document explorer using a reactive context, replacing prop drilling and local state with observable streams for preferences, selection, grouping, and view options.
  - Updated multiple components (display menus, quick actions, doc list items, group headers) to consume and update state directly via context observables.
  - Simplified component signatures by removing now-unnecessary props related to preferences and state handlers.

- **Style**
  - Adjusted user avatar display: avatars now only have right margin when user names are shown, and vertical alignment was improved.

- **New Features**
  - User avatar elements now include a data attribute to indicate if the user name is displayed.

- **Bug Fixes**
  - Improved handling of document tags to prevent errors when tags are not in the expected format.

- **Documentation**
  - Added missing group header property for updated date fields in workspace property types.

- **Chores**
  - Clarified and renamed internal types for quick actions to improve code clarity.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
CatsJuice
2025-05-10 07:01:49 +00:00
parent 76b4da54b7
commit 66500669c8
16 changed files with 274 additions and 351 deletions
@@ -1,31 +1,50 @@
import { createContext, type Dispatch, type SetStateAction } from 'react';
import { LiveData } from '@toeverything/infra';
import { createContext } from 'react';
import type { DocListItemView } from './docs-view/doc-list-item';
import type { ExplorerPreference } from './types';
export type DocExplorerContextType = ExplorerPreference & {
view: DocListItemView;
setView: Dispatch<SetStateAction<DocListItemView>>;
groups: Array<{ key: string; items: string[] }>;
collapsed: string[];
selectMode?: boolean;
selectedDocIds: string[];
prevCheckAnchorId?: string | null;
onToggleCollapse: (groupId: string) => void;
onToggleSelect: (docId: string) => void;
onSelect: Dispatch<SetStateAction<string[]>>;
setPrevCheckAnchorId: Dispatch<SetStateAction<string | null>>;
export type DocExplorerContextType = {
view$: LiveData<DocListItemView>;
groups$: LiveData<Array<{ key: string; items: string[] }>>;
collapsedGroups$: LiveData<string[]>;
selectMode$?: LiveData<boolean>;
selectedDocIds$: LiveData<string[]>;
prevCheckAnchorId$?: LiveData<string | null>;
} & {
[K in keyof ExplorerPreference as `${K}$`]: LiveData<ExplorerPreference[K]>;
};
export const DocExplorerContext = createContext<DocExplorerContextType>({
view: 'list',
setView: () => {},
groups: [],
collapsed: [],
selectedDocIds: [],
prevCheckAnchorId: null,
onToggleSelect: () => {},
onToggleCollapse: () => {},
onSelect: () => {},
setPrevCheckAnchorId: () => {},
});
export const DocExplorerContext = createContext<DocExplorerContextType>(
{} as any
);
export const createDocExplorerContext = () =>
({
view$: new LiveData<DocListItemView>('list'),
groups$: new LiveData<Array<{ key: string; items: string[] }>>([]),
collapsedGroups$: new LiveData<string[]>([]),
selectMode$: new LiveData<boolean>(false),
selectedDocIds$: new LiveData<string[]>([]),
prevCheckAnchorId$: new LiveData<string | null>(null),
filters$: new LiveData<ExplorerPreference['filters']>([
{
type: 'system',
key: 'trash',
value: 'false',
method: 'is',
},
]),
groupBy$: new LiveData<ExplorerPreference['groupBy']>(undefined),
orderBy$: new LiveData<ExplorerPreference['orderBy']>(undefined),
displayProperties$: new LiveData<ExplorerPreference['displayProperties']>(
[]
),
showDocIcon$: new LiveData<ExplorerPreference['showDocIcon']>(true),
showDocPreview$: new LiveData<ExplorerPreference['showDocPreview']>(true),
quickFavorite$: new LiveData<ExplorerPreference['quickFavorite']>(false),
quickSelect$: new LiveData<ExplorerPreference['quickSelect']>(false),
quickSplit$: new LiveData<ExplorerPreference['quickSplit']>(false),
quickTrash$: new LiveData<ExplorerPreference['quickTrash']>(false),
quickTab$: new LiveData<ExplorerPreference['quickTab']>(false),
}) satisfies DocExplorerContextType;
@@ -11,85 +11,63 @@ import type {
} from '@affine/core/modules/collection-rules/types';
import { useI18n } from '@affine/i18n';
import { ArrowDownSmallIcon } from '@blocksuite/icons/rc';
import { useLiveData } from '@toeverything/infra';
import type React from 'react';
import { useCallback } from 'react';
import { useCallback, useContext } from 'react';
import type { ExplorerPreference } from '../types';
import { DocExplorerContext } from '../context';
import { GroupByList, GroupByName } from './group';
import { OrderByList, OrderByName } from './order';
import { DisplayProperties } from './properties';
import { QuickActionsConfig } from './quick-actions';
import * as styles from './styles.css';
const ExplorerDisplayMenu = ({
preference,
onChange,
}: {
preference: ExplorerPreference;
onChange?: (preference: ExplorerPreference) => void;
}) => {
const ExplorerDisplayMenu = () => {
const t = useI18n();
const explorerContextValue = useContext(DocExplorerContext);
const groupBy = useLiveData(explorerContextValue.groupBy$);
const orderBy = useLiveData(explorerContextValue.orderBy$);
const handleGroupByChange = useCallback(
(groupBy: GroupByParams) => {
onChange?.({
...preference,
groupBy,
});
explorerContextValue.groupBy$?.next(groupBy);
},
[onChange, preference]
[explorerContextValue.groupBy$]
);
const handleOrderByChange = useCallback(
(orderBy: OrderByParams) => {
onChange?.({
...preference,
orderBy,
});
explorerContextValue.orderBy$?.next(orderBy);
},
[onChange, preference]
[explorerContextValue.orderBy$]
);
return (
<div className={styles.displayMenuContainer}>
<MenuSub
items={
<GroupByList
groupBy={preference.groupBy}
onChange={handleGroupByChange}
/>
}
items={<GroupByList groupBy={groupBy} onChange={handleGroupByChange} />}
>
<div className={styles.subMenuSelectorContainer}>
<span>{t['com.affine.explorer.display-menu.grouping']()}</span>
<span className={styles.subMenuSelectorSelected}>
{preference.groupBy ? (
<GroupByName groupBy={preference.groupBy} />
) : null}
{groupBy ? <GroupByName groupBy={groupBy} /> : null}
</span>
</div>
</MenuSub>
<MenuSub
items={
<OrderByList
orderBy={preference.orderBy}
onChange={handleOrderByChange}
/>
}
items={<OrderByList orderBy={orderBy} onChange={handleOrderByChange} />}
>
<div className={styles.subMenuSelectorContainer}>
<span>{t['com.affine.explorer.display-menu.ordering']()}</span>
<span className={styles.subMenuSelectorSelected}>
{preference.orderBy ? (
<OrderByName orderBy={preference.orderBy} />
) : null}
{orderBy ? <OrderByName orderBy={orderBy} /> : null}
</span>
</div>
</MenuSub>
<Divider size="thinner" />
<DisplayProperties preference={preference} onChange={onChange} />
<DisplayProperties />
<Divider size="thinner" />
<QuickActionsConfig preference={preference} onChange={onChange} />
<QuickActionsConfig />
</div>
);
};
@@ -97,24 +75,15 @@ const ExplorerDisplayMenu = ({
export const ExplorerDisplayMenuButton = ({
style,
className,
preference,
menuProps,
onChange,
}: {
style?: React.CSSProperties;
className?: string;
preference: ExplorerPreference;
onChange?: (preference: ExplorerPreference) => void;
menuProps?: Omit<MenuProps, 'items' | 'children'>;
}) => {
const t = useI18n();
return (
<Menu
items={
<ExplorerDisplayMenu preference={preference} onChange={onChange} />
}
{...menuProps}
>
<Menu items={<ExplorerDisplayMenu />} {...menuProps}>
<Button
className={className}
style={style}
@@ -5,11 +5,11 @@ import {
} from '@affine/core/modules/workspace-property';
import { useI18n } from '@affine/i18n';
import { useLiveData, useService } from '@toeverything/infra';
import { useCallback, useMemo } from 'react';
import { useCallback, useContext, useMemo } from 'react';
import { WorkspacePropertyName } from '../../properties';
import { WorkspacePropertyTypes } from '../../workspace-property-types';
import type { ExplorerPreference } from '../types';
import { DocExplorerContext } from '../context';
import * as styles from './properties.css';
export const filterDisplayProperties = <
@@ -29,20 +29,17 @@ export const filterDisplayProperties = <
}));
};
export const DisplayProperties = ({
preference,
onChange,
}: {
preference: ExplorerPreference;
onChange?: (preference: ExplorerPreference) => void;
}) => {
export const DisplayProperties = () => {
const t = useI18n();
const explorerContextValue = useContext(DocExplorerContext);
const workspacePropertyService = useService(WorkspacePropertyService);
const propertyList = useLiveData(workspacePropertyService.properties$);
const displayProperties = preference.displayProperties;
const showIcon = preference.showDocIcon;
const showBody = preference.showDocPreview;
const displayProperties = useLiveData(
explorerContextValue.displayProperties$
);
const showIcon = useLiveData(explorerContextValue.showDocIcon$);
const showBody = useLiveData(explorerContextValue.showDocPreview$);
const propertiesGroups = useMemo(
() => [
@@ -60,12 +57,9 @@ export const DisplayProperties = ({
const handleDisplayPropertiesChange = useCallback(
(displayProperties: string[]) => {
onChange?.({
...preference,
displayProperties,
});
explorerContextValue.displayProperties$?.next(displayProperties);
},
[onChange, preference]
[explorerContextValue.displayProperties$]
);
const handlePropertyClick = useCallback(
@@ -80,18 +74,12 @@ export const DisplayProperties = ({
);
const toggleIcon = useCallback(() => {
onChange?.({
...preference,
showDocIcon: !showIcon,
});
}, [onChange, preference, showIcon]);
explorerContextValue.showDocIcon$?.next(!showIcon);
}, [explorerContextValue.showDocIcon$, showIcon]);
const toggleBody = useCallback(() => {
onChange?.({
...preference,
showDocPreview: !showBody,
});
}, [onChange, preference, showBody]);
explorerContextValue.showDocPreview$?.next(!showBody);
}, [explorerContextValue.showDocPreview$, showBody]);
return (
<div className={styles.root}>
@@ -1,50 +1,46 @@
import { Checkbox, MenuItem, MenuSub } from '@affine/component';
import { useI18n } from '@affine/i18n';
import { useCallback } from 'react';
import { useLiveData } from '@toeverything/infra';
import { useCallback, useContext } from 'react';
import { type QuickActionKey, quickActions } from '../quick-actions.constants';
import type { ExplorerPreference } from '../types';
import { DocExplorerContext } from '../context';
import { type QuickAction, quickActions } from '../quick-actions.constants';
export const QuickActionsConfig = ({
preference,
onChange,
}: {
preference: ExplorerPreference;
onChange?: (preference: ExplorerPreference) => void;
}) => {
export const QuickActionsConfig = () => {
const t = useI18n();
const toggleAction = useCallback(
(key: QuickActionKey) => {
onChange?.({
...preference,
[key]: !preference[key],
});
},
[preference, onChange]
);
return (
<MenuSub
items={quickActions.map(action => {
if (action.disabled) return null;
return (
<MenuItem
key={action.key}
onClick={e => {
// do not close sub menu
e.preventDefault();
toggleAction(action.key);
}}
prefixIcon={<Checkbox checked={!!preference[action.key]} />}
>
{t.t(action.name)}
</MenuItem>
);
return <QuickActionItem key={action.key} action={action} />;
})}
>
{t['com.affine.all-docs.quick-actions']()}
</MenuSub>
);
};
const QuickActionItem = ({ action }: { action: QuickAction }) => {
const t = useI18n();
const explorerContextValue = useContext(DocExplorerContext);
const value = useLiveData(explorerContextValue[`${action.key}$`]);
const handleClick = useCallback(
(e: React.MouseEvent) => {
e.stopPropagation();
e.preventDefault();
const value = explorerContextValue[`${action.key}$`]?.value;
explorerContextValue[`${action.key}$`]?.next(!value);
},
[action.key, explorerContextValue]
);
return (
<MenuItem prefixIcon={<Checkbox checked={!!value} />} onClick={handleClick}>
{t.t(action.name)}
</MenuItem>
);
};
@@ -66,52 +66,49 @@ class MixId {
}
}
export const DocListItem = ({ ...props }: DocListItemProps) => {
const {
view,
groups,
selectMode,
selectedDocIds,
prevCheckAnchorId,
onSelect,
onToggleSelect,
setPrevCheckAnchorId,
} = useContext(DocExplorerContext);
const contextValue = useContext(DocExplorerContext);
const view = useLiveData(contextValue.view$);
const groups = useLiveData(contextValue.groups$);
const selectMode = useLiveData(contextValue.selectMode$);
const selectedDocIds = useLiveData(contextValue.selectedDocIds$);
const prevCheckAnchorId = useLiveData(contextValue.prevCheckAnchorId$);
const handleMultiSelect = useCallback(
(prevCursor: string, currCursor: string) => {
const flattenList = groups.flatMap(group =>
group.items.map(docId => MixId.create(group.key, docId))
);
const prev = contextValue.selectedDocIds$?.value ?? [];
const prevIndex = flattenList.indexOf(prevCursor);
const currIndex = flattenList.indexOf(currCursor);
onSelect(prev => {
const lowerIndex = Math.min(prevIndex, currIndex);
const upperIndex = Math.max(prevIndex, currIndex);
const lowerIndex = Math.min(prevIndex, currIndex);
const upperIndex = Math.max(prevIndex, currIndex);
const resSet = new Set(prev);
const handledSet = new Set<string>();
for (let i = lowerIndex; i <= upperIndex; i++) {
const mixId = flattenList[i];
const { groupId, docId } = MixId.parse(mixId);
if (groupId === null || docId === null) {
continue;
}
if (handledSet.has(docId) || mixId === prevCursor) {
continue;
}
if (resSet.has(docId)) {
resSet.delete(docId);
} else {
resSet.add(docId);
}
handledSet.add(docId);
const resSet = new Set(prev);
const handledSet = new Set<string>();
for (let i = lowerIndex; i <= upperIndex; i++) {
const mixId = flattenList[i];
const { groupId, docId } = MixId.parse(mixId);
if (groupId === null || docId === null) {
continue;
}
return [...resSet];
});
setPrevCheckAnchorId(currCursor);
if (handledSet.has(docId) || mixId === prevCursor) {
continue;
}
if (resSet.has(docId)) {
resSet.delete(docId);
} else {
resSet.add(docId);
}
handledSet.add(docId);
}
contextValue.selectedDocIds$?.next(Array.from(resSet));
contextValue.prevCheckAnchorId$?.next(currCursor);
},
[groups, onSelect, setPrevCheckAnchorId]
[contextValue, groups]
);
const handleClick = useCallback(
@@ -127,13 +124,13 @@ export const DocListItem = ({ ...props }: DocListItemProps) => {
// do multi select
handleMultiSelect(prevCheckAnchorId, currCursor);
} else {
onToggleSelect(docId);
setPrevCheckAnchorId(currCursor);
contextValue.selectedDocIds$?.next([docId]);
contextValue.prevCheckAnchorId$?.next(currCursor);
}
} else {
if (e.shiftKey) {
onToggleSelect(docId);
setPrevCheckAnchorId(currCursor);
contextValue.selectedDocIds$?.next([docId]);
contextValue.prevCheckAnchorId$?.next(currCursor);
return;
} else {
// as link
@@ -141,14 +138,7 @@ export const DocListItem = ({ ...props }: DocListItemProps) => {
}
}
},
[
handleMultiSelect,
onToggleSelect,
prevCheckAnchorId,
props,
selectMode,
setPrevCheckAnchorId,
]
[contextValue, handleMultiSelect, prevCheckAnchorId, props, selectMode]
);
return (
@@ -196,7 +186,8 @@ const DragHandle = memo(function DragHandle({
preview,
...props
}: HTMLProps<HTMLDivElement> & { preview?: ReactNode }) {
const { selectMode } = useContext(DocExplorerContext);
const contextValue = useContext(DocExplorerContext);
const selectMode = useLiveData(contextValue.selectMode$);
const { dragRef, CustomDragPreview } = useDraggable<AffineDNDData>(
() => ({
@@ -238,12 +229,13 @@ const Select = memo(function Select({
id,
...props
}: HTMLProps<HTMLDivElement>) {
const { selectMode, selectedDocIds, onToggleSelect } =
useContext(DocExplorerContext);
const contextValue = useContext(DocExplorerContext);
const selectMode = useLiveData(contextValue.selectMode$);
const selectedDocIds = useLiveData(contextValue.selectedDocIds$);
const handleSelectChange = useCallback(() => {
id && onToggleSelect(id);
}, [id, onToggleSelect]);
id && contextValue.selectedDocIds$?.next([id]);
}, [id, contextValue]);
if (!id) {
return null;
@@ -263,7 +255,8 @@ const DocIcon = memo(function DocIcon({
id,
...props
}: HTMLProps<HTMLDivElement>) {
const { showDocIcon } = useContext(DocExplorerContext);
const contextValue = useContext(DocExplorerContext);
const showDocIcon = useLiveData(contextValue.showDocIcon$);
if (!showDocIcon) {
return null;
}
@@ -289,7 +282,8 @@ const DocPreview = memo(function DocPreview({
loading,
...props
}: HTMLProps<HTMLDivElement> & { loading?: ReactNode }) {
const { showDocPreview } = useContext(DocExplorerContext);
const contextValue = useContext(DocExplorerContext);
const showDocPreview = useLiveData(contextValue.showDocPreview$);
if (!id || !showDocPreview) return null;
@@ -356,7 +350,8 @@ const randomPreviewSkeleton = () => {
}));
};
export const CardViewDoc = ({ docId }: DocListItemProps) => {
const { selectMode } = useContext(DocExplorerContext);
const contextValue = useContext(DocExplorerContext);
const selectMode = useLiveData(contextValue.selectMode$);
const docsService = useService(DocsService);
const doc = useLiveData(docsService.list.doc$(docId));
const [previewSkeleton] = useState(randomPreviewSkeleton);
@@ -1,6 +1,7 @@
import { Button, IconButton } from '@affine/component';
import { useI18n } from '@affine/i18n';
import { ToggleRightIcon } from '@blocksuite/icons/rc';
import { useLiveData } from '@toeverything/infra';
import clsx from 'clsx';
import {
type HTMLAttributes,
@@ -20,14 +21,12 @@ export const DocGroupHeader = ({
groupId: string;
}) => {
const t = useI18n();
const {
selectMode,
collapsed,
groups,
selectedDocIds,
onToggleCollapse,
onSelect,
} = useContext(DocExplorerContext);
const contextValue = useContext(DocExplorerContext);
const groups = useLiveData(contextValue.groups$);
const selectedDocIds = useLiveData(contextValue.selectedDocIds$);
const collapsedGroups = useLiveData(contextValue.collapsedGroups$);
const selectMode = useLiveData(contextValue.selectMode$);
const group = groups.find(g => g.key === groupId);
const isGroupAllSelected = group?.items.every(id =>
@@ -35,24 +34,30 @@ export const DocGroupHeader = ({
);
const handleToggleCollapse = useCallback(() => {
onToggleCollapse(groupId);
}, [groupId, onToggleCollapse]);
const prev = contextValue.collapsedGroups$.value;
contextValue.collapsedGroups$.next(
prev.includes(groupId)
? prev.filter(id => id !== groupId)
: [...prev, groupId]
);
}, [groupId, contextValue]);
const handleSelectAll = useCallback(() => {
const prev = contextValue.selectedDocIds$.value;
if (isGroupAllSelected) {
onSelect(prev => prev.filter(id => !group?.items.includes(id)));
contextValue.selectedDocIds$.next(
prev.filter(id => !group?.items.includes(id))
);
} else {
onSelect(prev => {
const newSelected = [...prev];
group?.items.forEach(id => {
if (!newSelected.includes(id)) {
newSelected.push(id);
}
});
return newSelected;
const newSelected = [...prev];
group?.items.forEach(id => {
if (!newSelected.includes(id)) {
newSelected.push(id);
}
});
contextValue.selectedDocIds$.next(newSelected);
}
}, [group?.items, isGroupAllSelected, onSelect]);
}, [contextValue, group?.items, isGroupAllSelected]);
const selectedCount = group?.items.filter(id =>
selectedDocIds.includes(id)
@@ -61,7 +66,7 @@ export const DocGroupHeader = ({
return (
<div
className={styles.groupHeader}
data-collapsed={collapsed.includes(groupId)}
data-collapsed={collapsedGroups.includes(groupId)}
>
<div className={clsx(styles.content, className)} {...props} />
{selectMode ? (
@@ -28,7 +28,8 @@ const cardInlinePropertyOrder: WorkspacePropertyType[] = [
];
const useProperties = (docId: string, view: 'list' | 'card') => {
const { displayProperties } = useContext(DocExplorerContext);
const contextValue = useContext(DocExplorerContext);
const displayProperties = useLiveData(contextValue.displayProperties$);
const docsService = useService(DocsService);
const workspacePropertyService = useService(WorkspacePropertyService);
@@ -24,7 +24,8 @@ export const QuickFavorite = memo(function QuickFavorite({
doc,
...iconButtonProps
}: QuickActionProps) {
const { quickFavorite } = useContext(DocExplorerContext);
const contextValue = useContext(DocExplorerContext);
const quickFavorite = useLiveData(contextValue.quickFavorite$);
const favAdapter = useService(CompatibleFavoriteItemsAdapter);
const favourite = useLiveData(favAdapter.isFavorite$(doc.id, 'doc'));
@@ -55,7 +56,8 @@ export const QuickTab = memo(function QuickTab({
doc,
...iconButtonProps
}: QuickActionProps) {
const { quickTab } = useContext(DocExplorerContext);
const contextValue = useContext(DocExplorerContext);
const quickTab = useLiveData(contextValue.quickTab$);
const workbench = useService(WorkbenchService).workbench;
const onOpenInNewTab = useCallback(
(e: React.MouseEvent) => {
@@ -83,7 +85,8 @@ export const QuickSplit = memo(function QuickSplit({
doc,
...iconButtonProps
}: QuickActionProps) {
const { quickSplit } = useContext(DocExplorerContext);
const contextValue = useContext(DocExplorerContext);
const quickSplit = useLiveData(contextValue.quickSplit$);
const workbench = useService(WorkbenchService).workbench;
const onOpenInSplitView = useCallback(
@@ -115,7 +118,8 @@ export const QuickDelete = memo(function QuickDelete({
}: QuickActionProps) {
const t = useI18n();
const { openConfirmModal } = useConfirmModal();
const { quickTrash } = useContext(DocExplorerContext);
const contextValue = useContext(DocExplorerContext);
const quickTrash = useLiveData(contextValue.quickTrash$);
const onMoveToTrash = useCallback(
(e: React.MouseEvent) => {
@@ -162,8 +166,9 @@ export const QuickSelect = memo(function QuickSelect({
doc,
...iconButtonProps
}: QuickActionProps) {
const { quickSelect, selectedDocIds, onToggleSelect } =
useContext(DocExplorerContext);
const contextValue = useContext(DocExplorerContext);
const quickSelect = useLiveData(contextValue.quickSelect$);
const selectedDocIds = useLiveData(contextValue.selectedDocIds$);
const selected = selectedDocIds.includes(doc.id);
@@ -171,9 +176,13 @@ export const QuickSelect = memo(function QuickSelect({
(e: React.MouseEvent) => {
e.stopPropagation();
e.preventDefault();
onToggleSelect(doc.id);
contextValue.selectedDocIds$?.next(
selected
? selectedDocIds.filter(id => id !== doc.id)
: [...selectedDocIds, doc.id]
);
},
[doc.id, onToggleSelect]
[contextValue, doc.id, selected, selectedDocIds]
);
if (!quickSelect) {
@@ -10,7 +10,7 @@ import {
} from './docs-view/quick-actions';
import type { ExplorerPreference } from './types';
export interface QuickAction {
interface QuickActionItem {
name: I18nString;
Component: React.FC<QuickActionProps>;
disabled?: boolean;
@@ -22,7 +22,7 @@ type ExtractPrefixKeys<Obj extends object, Prefix extends string> = {
export type QuickActionKey = ExtractPrefixKeys<ExplorerPreference, 'quick'>;
const QUICK_ACTION_MAP: Record<QuickActionKey, QuickAction> = {
const QUICK_ACTION_MAP: Record<QuickActionKey, QuickActionItem> = {
quickFavorite: {
name: 'com.affine.all-docs.quick-action.favorite',
Component: QuickFavorite,
@@ -50,3 +50,5 @@ export const quickActions = Object.entries(QUICK_ACTION_MAP).map(
return { key: key as QuickActionKey, ...config };
}
);
export type QuickAction = (typeof quickActions)[number];
@@ -144,6 +144,7 @@ export const CreatedByDocListInlineProperty = ({
type="CreatedBy"
size={20}
emptyFallback={null}
showName={false}
/>
);
};
@@ -171,7 +172,7 @@ export const ModifiedByGroupHeader = ({
return (
<PlainTextDocGroupHeader groupId={groupId} docCount={docCount}>
<div className={styles.userLabelContainer}>
<PublicUserLabel id={userId} size={20} />
<PublicUserLabel id={userId} size={20} showName={false} />
</div>
</PlainTextDocGroupHeader>
);
@@ -223,6 +223,7 @@ export const WorkspacePropertyTypes = {
defaultFilter: { method: 'this-week' },
showInDocList: 'inline',
docListProperty: UpdatedDateDocListProperty,
groupHeader: CreatedGroupHeader,
},
createdAt: {
icon: HistoryIcon,