feat(core): add title order by (#12696)

<!-- This is an auto-generated comment: release notes by coderabbit.ai -->
## 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.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
EYHN
2025-06-04 02:41:55 +00:00
parent 99198e246b
commit 160e4c2a38
6 changed files with 73 additions and 3 deletions

View File

@@ -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<Set<string>>,
params: OrderByParams
): Observable<string[]> {
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);
})
);
}
}

View File

@@ -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]);
}

View File

@@ -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,

View File

@@ -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'),