mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-07-18 02:26:21 +08:00
feat: add editor record (#7938)
fix CLOUD-58, CLOUD-61, CLOUD-62, PD-1607, PD-1608
This commit is contained in:
@@ -89,6 +89,7 @@ export const iconNames = [
|
||||
'edgeless',
|
||||
'journal',
|
||||
'payment',
|
||||
'createdEdited',
|
||||
] as const satisfies fromLibIconName<LibIconComponentName>[];
|
||||
|
||||
export type PagePropertyIcon = (typeof iconNames)[number];
|
||||
@@ -109,6 +110,10 @@ export const getDefaultIconName = (
|
||||
return 'checkBoxCheckLinear';
|
||||
case 'number':
|
||||
return 'number';
|
||||
case 'createdBy':
|
||||
return 'createdEdited';
|
||||
case 'updatedBy':
|
||||
return 'createdEdited';
|
||||
default:
|
||||
return 'text';
|
||||
}
|
||||
|
||||
+8
@@ -35,9 +35,16 @@ export const newPropertyTypes: PagePropertyType[] = [
|
||||
PagePropertyType.Number,
|
||||
PagePropertyType.Checkbox,
|
||||
PagePropertyType.Date,
|
||||
PagePropertyType.CreatedBy,
|
||||
PagePropertyType.UpdatedBy,
|
||||
// TODO(@Peng): add more
|
||||
];
|
||||
|
||||
export const readonlyPropertyTypes: PagePropertyType[] = [
|
||||
PagePropertyType.CreatedBy,
|
||||
PagePropertyType.UpdatedBy,
|
||||
];
|
||||
|
||||
export class PagePropertiesMetaManager {
|
||||
constructor(private readonly adapter: WorkspacePropertiesAdapter) {}
|
||||
|
||||
@@ -95,6 +102,7 @@ export class PagePropertiesMetaManager {
|
||||
type,
|
||||
order: newOrder,
|
||||
icon: icon ?? getDefaultIconName(type),
|
||||
readonly: readonlyPropertyTypes.includes(type) || undefined,
|
||||
} as const;
|
||||
this.customPropertiesSchema[id] = property;
|
||||
return property;
|
||||
|
||||
+115
-3
@@ -1,14 +1,28 @@
|
||||
import { Checkbox, DatePicker, Menu } from '@affine/component';
|
||||
import { Avatar, Checkbox, DatePicker, Menu } from '@affine/component';
|
||||
import { CloudDocMetaService } from '@affine/core/modules/cloud/services/cloud-doc-meta';
|
||||
import type {
|
||||
PageInfoCustomProperty,
|
||||
PageInfoCustomPropertyMeta,
|
||||
PagePropertyType,
|
||||
} from '@affine/core/modules/properties/services/schema';
|
||||
import { WorkspaceFlavour } from '@affine/env/workspace';
|
||||
import { i18nTime, useI18n } from '@affine/i18n';
|
||||
import { DocService, useService } from '@toeverything/infra';
|
||||
import {
|
||||
DocService,
|
||||
useLiveData,
|
||||
useService,
|
||||
WorkspaceService,
|
||||
} from '@toeverything/infra';
|
||||
import { noop } from 'lodash-es';
|
||||
import type { ChangeEventHandler } from 'react';
|
||||
import { useCallback, useContext, useEffect, useRef, useState } from 'react';
|
||||
import {
|
||||
useCallback,
|
||||
useContext,
|
||||
useEffect,
|
||||
useMemo,
|
||||
useRef,
|
||||
useState,
|
||||
} from 'react';
|
||||
|
||||
import { managerContext } from './common';
|
||||
import * as styles from './styles.css';
|
||||
@@ -190,6 +204,102 @@ export const TagsValue = () => {
|
||||
);
|
||||
};
|
||||
|
||||
const CloudUserAvatar = (props: { type: 'CreatedBy' | 'UpdatedBy' }) => {
|
||||
const cloudDocMetaService = useService(CloudDocMetaService);
|
||||
const cloudDocMeta = useLiveData(cloudDocMetaService.cloudDocMeta.meta$);
|
||||
const isRevalidating = useLiveData(
|
||||
cloudDocMetaService.cloudDocMeta.isRevalidating$
|
||||
);
|
||||
const error = useLiveData(cloudDocMetaService.cloudDocMeta.error$);
|
||||
|
||||
useEffect(() => {
|
||||
cloudDocMetaService.cloudDocMeta.revalidate();
|
||||
}, [cloudDocMetaService]);
|
||||
|
||||
const user = useMemo(() => {
|
||||
if (!cloudDocMeta) return null;
|
||||
if (props.type === 'CreatedBy' && cloudDocMeta.createdBy) {
|
||||
return {
|
||||
name: cloudDocMeta.createdBy.name,
|
||||
avatarUrl: cloudDocMeta.createdBy.avatarUrl,
|
||||
};
|
||||
} else if (props.type === 'UpdatedBy' && cloudDocMeta.updatedBy) {
|
||||
return {
|
||||
name: cloudDocMeta.updatedBy.name,
|
||||
avatarUrl: cloudDocMeta.updatedBy.avatarUrl,
|
||||
};
|
||||
}
|
||||
return null;
|
||||
}, [cloudDocMeta, props.type]);
|
||||
|
||||
const t = useI18n();
|
||||
|
||||
if (!cloudDocMeta) {
|
||||
if (isRevalidating) {
|
||||
// TODO: loading ui
|
||||
return null;
|
||||
}
|
||||
if (error) {
|
||||
// error ui
|
||||
return;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
if (user) {
|
||||
return (
|
||||
<>
|
||||
<Avatar url={user.avatarUrl || ''} name={user.name} size={20} />
|
||||
<span>{user.name}</span>
|
||||
</>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<>
|
||||
<Avatar name="?" size={20} />
|
||||
<span>
|
||||
{t['com.affine.page-properties.property-user-avatar-no-record']()}
|
||||
</span>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export const LocalUserValue = () => {
|
||||
const t = useI18n();
|
||||
return <span>{t['com.affine.page-properties.local-user']()}</span>;
|
||||
};
|
||||
|
||||
export const CreatedUserValue = () => {
|
||||
const workspaceService = useService(WorkspaceService);
|
||||
const isCloud =
|
||||
workspaceService.workspace.flavour === WorkspaceFlavour.AFFINE_CLOUD;
|
||||
|
||||
if (!isCloud) {
|
||||
return <LocalUserValue />;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={styles.propertyRowValueUserCell}>
|
||||
<CloudUserAvatar type="CreatedBy" />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export const UpdatedUserValue = () => {
|
||||
const workspaceService = useService(WorkspaceService);
|
||||
const isCloud =
|
||||
workspaceService.workspace.flavour === WorkspaceFlavour.AFFINE_CLOUD;
|
||||
|
||||
if (!isCloud) {
|
||||
return <LocalUserValue />;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={styles.propertyRowValueUserCell}>
|
||||
<CloudUserAvatar type="UpdatedBy" />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export const propertyValueRenderers: Record<
|
||||
PagePropertyType,
|
||||
typeof DateValue
|
||||
@@ -198,6 +308,8 @@ export const propertyValueRenderers: Record<
|
||||
checkbox: CheckboxValue,
|
||||
text: TextValue,
|
||||
number: NumberValue,
|
||||
createdBy: CreatedUserValue,
|
||||
updatedBy: UpdatedUserValue,
|
||||
// TODO(@Peng): fix following
|
||||
tags: TagsValue,
|
||||
progress: TextValue,
|
||||
|
||||
@@ -361,6 +361,16 @@ export const propertyRowValueTextCell = style([
|
||||
},
|
||||
]);
|
||||
|
||||
export const propertyRowValueUserCell = style([
|
||||
propertyRowValueCell,
|
||||
{
|
||||
border: 'none',
|
||||
overflow: 'hidden',
|
||||
columnGap: '0.5rem',
|
||||
alignItems: 'center',
|
||||
},
|
||||
]);
|
||||
|
||||
export const propertyRowValueTextarea = style([
|
||||
propertyRowValueCell,
|
||||
{
|
||||
|
||||
+28
-6
@@ -279,29 +279,51 @@ const CustomPropertyRowsList = ({
|
||||
|
||||
return <CustomPropertyRows properties={filtered} statistics={statistics} />;
|
||||
} else {
|
||||
const required = properties.filter(property => property.required);
|
||||
const optional = properties.filter(property => !property.required);
|
||||
const partition = Object.groupBy(properties, p =>
|
||||
p.required ? 'required' : p.readonly ? 'readonly' : 'optional'
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
{required.length > 0 ? (
|
||||
{partition.required && partition.required.length > 0 ? (
|
||||
<>
|
||||
<div className={styles.subListHeader}>
|
||||
{t[
|
||||
'com.affine.settings.workspace.properties.required-properties'
|
||||
]()}
|
||||
</div>
|
||||
<CustomPropertyRows properties={required} statistics={statistics} />
|
||||
<CustomPropertyRows
|
||||
properties={partition.required}
|
||||
statistics={statistics}
|
||||
/>
|
||||
</>
|
||||
) : null}
|
||||
|
||||
{optional.length > 0 ? (
|
||||
{partition.optional && partition.optional.length > 0 ? (
|
||||
<>
|
||||
<div className={styles.subListHeader}>
|
||||
{t[
|
||||
'com.affine.settings.workspace.properties.general-properties'
|
||||
]()}
|
||||
</div>
|
||||
<CustomPropertyRows properties={optional} statistics={statistics} />
|
||||
<CustomPropertyRows
|
||||
properties={partition.optional}
|
||||
statistics={statistics}
|
||||
/>
|
||||
</>
|
||||
) : null}
|
||||
|
||||
{partition.readonly && partition.readonly.length > 0 ? (
|
||||
<>
|
||||
<div className={styles.subListHeader}>
|
||||
{t[
|
||||
'com.affine.settings.workspace.properties.readonly-properties'
|
||||
]()}
|
||||
</div>
|
||||
<CustomPropertyRows
|
||||
properties={partition.readonly}
|
||||
statistics={statistics}
|
||||
/>
|
||||
</>
|
||||
) : null}
|
||||
</>
|
||||
|
||||
Reference in New Issue
Block a user