refactor(core): implement doc created/updated by service (#12150)

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

- **New Features**
  - Documents now automatically track and display "created by" and "updated by" user information.
  - Document creation and update timestamps are now managed and shown more accurately.
  - Workspace and document metadata (name, avatar) updates are more responsive and reliable.
  - Document creation supports middleware for customizing properties and behavior.

- **Improvements**
  - Simplified and unified event handling for document list updates, reducing redundant event subscriptions.
  - Enhanced integration of editor and theme settings into the document creation process.
  - Explicit Yjs document initialization for improved workspace stability and reliability.
  - Consolidated journal-related metadata display in document icons and titles for clarity.

- **Bug Fixes**
  - Fixed inconsistencies in how workspace and document names are set and updated.
  - Improved accuracy of "last updated" indicators by handling timestamps automatically.

- **Refactor**
  - Removed deprecated event subjects and direct metadata manipulation in favor of more robust, reactive patterns.
  - Streamlined document creation logic across various features (quick search, journal, recording, etc.).
  - Simplified user avatar display components and removed cloud metadata dependencies.
  - Removed legacy editor setting and theme service dependencies from multiple modules.

- **Chores**
  - Updated internal APIs and interfaces to support new metadata and event handling mechanisms.
  - Cleaned up unused code and dependencies related to editor settings and theme services.
  - Skipped flaky end-to-end test to improve test suite stability.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
EYHN
2025-05-08 07:53:33 +00:00
parent 93d74ff220
commit 2d1600fa00
56 changed files with 496 additions and 458 deletions
@@ -113,6 +113,7 @@ const getOrCreateShellWorkspace = (
if (!docCollection) {
docCollection = new WorkspaceImpl({
id: workspaceId,
rootDoc: new YDoc({ guid: workspaceId }),
blobSource: {
name: 'cloud',
readonly: true,
@@ -1,56 +1,26 @@
import { Avatar, PropertyValue } from '@affine/component';
import { CloudDocMetaService } from '@affine/core/modules/cloud/services/cloud-doc-meta';
import { PropertyValue } from '@affine/component';
import { PublicUserLabel } from '@affine/core/modules/cloud/views/public-user';
import { DocService } from '@affine/core/modules/doc';
import { WorkspaceService } from '@affine/core/modules/workspace';
import { useI18n } from '@affine/i18n';
import { useLiveData, useService } from '@toeverything/infra';
import { useEffect, useMemo } from 'react';
import { userWrapper } from './created-updated-by.css';
const CloudUserAvatar = (props: { type: 'CreatedBy' | 'UpdatedBy' }) => {
const cloudDocMetaService = useService(CloudDocMetaService);
const cloudDocMeta = useLiveData(cloudDocMetaService.cloudDocMeta.meta$);
const isRevalidating = useLiveData(
cloudDocMetaService.cloudDocMeta.isRevalidating$
const CreatedByUpdatedByAvatar = (props: {
type: 'CreatedBy' | 'UpdatedBy';
}) => {
const docService = useService(DocService);
const userId = useLiveData(
props.type === 'CreatedBy'
? docService.doc.createdBy$
: docService.doc.updatedBy$
);
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]);
if (!cloudDocMeta) {
if (isRevalidating) {
// TODO: loading ui
return null;
}
if (error) {
// error ui
return;
}
return null;
}
if (user) {
if (userId) {
return (
<div className={userWrapper}>
<Avatar url={user.avatarUrl || ''} name={user.name} size={22} />
<span>{user.name}</span>
<PublicUserLabel id={userId} />
</div>
);
}
@@ -85,7 +55,7 @@ export const CreatedByValue = () => {
return (
<PropertyValue readonly>
<CloudUserAvatar type="CreatedBy" />
<CreatedByUpdatedByAvatar type="CreatedBy" />
</PropertyValue>
);
};
@@ -104,7 +74,7 @@ export const UpdatedByValue = () => {
return (
<PropertyValue readonly>
<CloudUserAvatar type="UpdatedBy" />
<CreatedByUpdatedByAvatar type="UpdatedBy" />
</PropertyValue>
);
};
@@ -16,16 +16,11 @@ export function useDocCollectionPage(
useEffect(() => {
const group = new DisposableGroup();
group.add(
docCollection.slots.docCreated.subscribe(id => {
if (pageId === id) {
setPage(docCollection.getDoc(id)?.getStore() ?? null);
}
})
);
group.add(
docCollection.slots.docRemoved.subscribe(id => {
if (pageId === id) {
docCollection.slots.docListUpdated.subscribe(() => {
if (!pageId) {
setPage(null);
} else {
setPage(docCollection.getDoc(pageId)?.getStore() ?? null);
}
})
);
@@ -16,16 +16,11 @@ export function useDocCollectionPage(
useEffect(() => {
const group = new DisposableGroup();
group.add(
docCollection.slots.docCreated.subscribe(id => {
if (pageId === id) {
setPage(docCollection.getDoc(id)?.getStore() ?? null);
}
})
);
group.add(
docCollection.slots.docRemoved.subscribe(id => {
if (pageId === id) {
docCollection.slots.docListUpdated.subscribe(() => {
if (!pageId) {
setPage(null);
} else {
setPage(docCollection.getDoc(pageId)?.getStore() ?? null);
}
})
);