From 160e4c2a3868e775211cd3b4a0b9c62681bd7871 Mon Sep 17 00:00:00 2001 From: EYHN Date: Wed, 4 Jun 2025 02:41:55 +0000 Subject: [PATCH] feat(core): add title order by (#12696) ## Summary by CodeRabbit - **New Features** - Added support for ordering documents by their title. - Introduced a new "title" system property type with an associated icon and display name. - **Improvements** - Enhanced system property types to allow more flexible filtering options. - Improved filter condition handling to show an unknown filter UI when filtering methods or values are unavailable. --- .../components/filter/conditions/system.tsx | 10 +++++++ .../components/system-property-types/index.ts | 10 +++++-- .../collection-rules/impls/order-by/title.ts | 29 +++++++++++++++++++ .../src/modules/collection-rules/index.ts | 4 ++- .../core/src/modules/doc/services/docs.ts | 4 +++ .../core/src/modules/doc/stores/docs.ts | 19 ++++++++++++ 6 files changed, 73 insertions(+), 3 deletions(-) create mode 100644 packages/frontend/core/src/modules/collection-rules/impls/order-by/title.ts diff --git a/packages/frontend/core/src/components/filter/conditions/system.tsx b/packages/frontend/core/src/components/filter/conditions/system.tsx index 29d14df8e..db57d8001 100644 --- a/packages/frontend/core/src/components/filter/conditions/system.tsx +++ b/packages/frontend/core/src/components/filter/conditions/system.tsx @@ -37,6 +37,16 @@ export const SystemFilterCondition = ({ const methods = type.filterMethod; const Value = type.filterValue; + if (!methods || !Value) { + return ( + + ); + } + return ( void; diff --git a/packages/frontend/core/src/modules/collection-rules/impls/order-by/title.ts b/packages/frontend/core/src/modules/collection-rules/impls/order-by/title.ts new file mode 100644 index 000000000..5f12a9879 --- /dev/null +++ b/packages/frontend/core/src/modules/collection-rules/impls/order-by/title.ts @@ -0,0 +1,29 @@ +import type { DocsService } from '@affine/core/modules/doc'; +import { Service } from '@toeverything/infra'; +import { map, type Observable } from 'rxjs'; + +import type { OrderByProvider } from '../../provider'; +import type { OrderByParams } from '../../types'; + +export class TitleOrderByProvider extends Service implements OrderByProvider { + constructor(private readonly docsService: DocsService) { + super(); + } + orderBy$( + _items$: Observable>, + params: OrderByParams + ): Observable { + const isDesc = params.desc; + return this.docsService.allDocTitle$().pipe( + map(o => { + return o + .sort( + (a, b) => + (a.title === b.title ? 0 : a.title > b.title ? 1 : -1) * + (isDesc ? -1 : 1) + ) + .map(i => i.id); + }) + ); + } +} diff --git a/packages/frontend/core/src/modules/collection-rules/index.ts b/packages/frontend/core/src/modules/collection-rules/index.ts index 4a56cb4d9..f0cb02935 100644 --- a/packages/frontend/core/src/modules/collection-rules/index.ts +++ b/packages/frontend/core/src/modules/collection-rules/index.ts @@ -61,6 +61,7 @@ import { SystemOrderByProvider } from './impls/order-by/system'; import { TagsOrderByProvider } from './impls/order-by/tags'; import { TemplateOrderByProvider } from './impls/order-by/template'; import { TextPropertyOrderByProvider } from './impls/order-by/text'; +import { TitleOrderByProvider } from './impls/order-by/title'; import { UpdatedAtOrderByProvider } from './impls/order-by/updated-at'; import { UpdatedByOrderByProvider } from './impls/order-by/updated-by'; import { FilterProvider, GroupByProvider, OrderByProvider } from './provider'; @@ -365,5 +366,6 @@ export function configureCollectionRulesModule(framework: Framework) { ]) .impl(OrderByProvider('system:template'), TemplateOrderByProvider, [ DocsService, - ]); + ]) + .impl(OrderByProvider('system:title'), TitleOrderByProvider, [DocsService]); } diff --git a/packages/frontend/core/src/modules/doc/services/docs.ts b/packages/frontend/core/src/modules/doc/services/docs.ts index d63820635..95ca6d81a 100644 --- a/packages/frontend/core/src/modules/doc/services/docs.ts +++ b/packages/frontend/core/src/modules/doc/services/docs.ts @@ -81,6 +81,10 @@ export class DocsService extends Service { return this.store.watchTrashDocIds(); } + allDocTitle$() { + return this.store.watchAllDocTitle(); + } + constructor( private readonly store: DocsStore, private readonly docPropertiesStore: DocPropertiesStore, diff --git a/packages/frontend/core/src/modules/doc/stores/docs.ts b/packages/frontend/core/src/modules/doc/stores/docs.ts index c5f1ac36c..8a6947240 100644 --- a/packages/frontend/core/src/modules/doc/stores/docs.ts +++ b/packages/frontend/core/src/modules/doc/stores/docs.ts @@ -142,6 +142,25 @@ export class DocsStore extends Store { ); } + watchAllDocTitle() { + return yjsGetPath( + this.workspaceService.workspace.rootYDoc.getMap('meta'), + 'pages' + ).pipe( + switchMap(pages => yjsObservePath(pages, '*.title')), + map(pages => { + if (pages instanceof YArray) { + return pages.map(v => ({ + id: v.get('id') as string, + title: (v.get('title') ?? '') as string, + })); + } else { + return []; + } + }) + ); + } + watchNonTrashDocIds() { return yjsGetPath( this.workspaceService.workspace.rootYDoc.getMap('meta'),