mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-08-03 02:20:19 +08:00
feat(core): add pinned collections to all docs (#12269)
This commit is contained in:
@@ -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;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user