feat(core): add pinned collections to all docs (#12269)

This commit is contained in:
EYHN
2025-05-14 18:18:43 +09:00
committed by GitHub
parent 6eab1a6cb2
commit 61b99c5934
16 changed files with 558 additions and 47 deletions
@@ -1,20 +1,27 @@
export { Collection } from './entities/collection';
export type { CollectionMeta } from './services/collection';
export { CollectionService } from './services/collection';
export { PinnedCollectionService } from './services/pinned-collection';
export type { CollectionInfo } from './stores/collection';
export type { PinnedCollectionRecord } from './stores/pinned-collection';
import { type Framework } from '@toeverything/infra';
import { CollectionRulesService } from '../collection-rules';
import { WorkspaceDBService } from '../db';
import { WorkspaceScope, WorkspaceService } from '../workspace';
import { Collection } from './entities/collection';
import { CollectionService } from './services/collection';
import { PinnedCollectionService } from './services/pinned-collection';
import { CollectionStore } from './stores/collection';
import { PinnedCollectionStore } from './stores/pinned-collection';
export function configureCollectionModule(framework: Framework) {
framework
.scope(WorkspaceScope)
.service(CollectionService, [CollectionStore])
.store(CollectionStore, [WorkspaceService])
.entity(Collection, [CollectionStore, CollectionRulesService]);
.entity(Collection, [CollectionStore, CollectionRulesService])
.store(PinnedCollectionStore, [WorkspaceDBService])
.service(PinnedCollectionService, [PinnedCollectionStore]);
}
@@ -0,0 +1,71 @@
import {
generateFractionalIndexingKeyBetween,
LiveData,
Service,
} from '@toeverything/infra';
import type {
PinnedCollectionRecord,
PinnedCollectionStore,
} from '../stores/pinned-collection';
export class PinnedCollectionService extends Service {
constructor(private readonly pinnedCollectionStore: PinnedCollectionStore) {
super();
}
pinnedCollections$ = LiveData.from<PinnedCollectionRecord[]>(
this.pinnedCollectionStore.watchPinnedCollections(),
[]
);
sortedPinnedCollections$ = this.pinnedCollections$.map(records =>
records.toSorted((a, b) => {
return a.index > b.index ? 1 : -1;
})
);
addPinnedCollection(record: PinnedCollectionRecord) {
this.pinnedCollectionStore.addPinnedCollection(record);
}
removePinnedCollection(collectionId: string) {
this.pinnedCollectionStore.removePinnedCollection(collectionId);
}
indexAt(at: 'before' | 'after', targetId?: string) {
if (!targetId) {
if (at === 'before') {
const first = this.sortedPinnedCollections$.value.at(0);
return generateFractionalIndexingKeyBetween(null, first?.index || null);
} else {
const last = this.sortedPinnedCollections$.value.at(-1);
return generateFractionalIndexingKeyBetween(last?.index || null, null);
}
} else {
const sortedChildren = this.sortedPinnedCollections$.value;
const targetIndex = sortedChildren.findIndex(
node => node.collectionId === targetId
);
if (targetIndex === -1) {
throw new Error('Target node not found');
}
const target = sortedChildren[targetIndex];
const before: PinnedCollectionRecord | null =
sortedChildren[targetIndex - 1] || null;
const after: PinnedCollectionRecord | null =
sortedChildren[targetIndex + 1] || null;
if (at === 'before') {
return generateFractionalIndexingKeyBetween(
before?.index || null,
target.index
);
} else {
return generateFractionalIndexingKeyBetween(
target.index,
after?.index || null
);
}
}
}
}
@@ -0,0 +1,30 @@
import { Store } from '@toeverything/infra';
import type { Observable } from 'rxjs';
import type { WorkspaceDBService } from '../../db';
export interface PinnedCollectionRecord {
collectionId: string;
index: string;
}
export class PinnedCollectionStore extends Store {
constructor(private readonly workspaceDBService: WorkspaceDBService) {
super();
}
watchPinnedCollections(): Observable<PinnedCollectionRecord[]> {
return this.workspaceDBService.db.pinnedCollections.find$();
}
addPinnedCollection(record: PinnedCollectionRecord) {
this.workspaceDBService.db.pinnedCollections.create({
collectionId: record.collectionId,
index: record.index,
});
}
removePinnedCollection(collectionId: string) {
this.workspaceDBService.db.pinnedCollections.delete(collectionId);
}
}
@@ -42,6 +42,10 @@ export const AFFiNE_WORKSPACE_DB_SCHEMA = {
isDeleted: f.boolean().optional(),
// we will keep deleted properties in the database, for override legacy data
},
pinnedCollections: {
collectionId: f.string().primaryKey(),
index: f.string(),
},
} as const satisfies DBSchemaBuilder;
export type AFFiNEWorkspaceDbSchema = typeof AFFiNE_WORKSPACE_DB_SCHEMA;