-
-
Notifications
You must be signed in to change notification settings - Fork 2.8k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
16 changed files
with
510 additions
and
16 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
2 changes: 1 addition & 1 deletion
2
packages/frontend/core/src/components/doc-properties/types/types.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,7 @@ | ||
import type { DocCustomPropertyInfo } from '@toeverything/infra'; | ||
|
||
export interface PropertyValueProps { | ||
propertyInfo: DocCustomPropertyInfo; | ||
propertyInfo?: DocCustomPropertyInfo; | ||
value: any; | ||
onChange: (value: any) => void; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,10 +1,22 @@ | ||
import { type Framework, WorkspaceScope } from '@toeverything/infra'; | ||
import { | ||
DocsService, | ||
type Framework, | ||
WorkspaceScope, | ||
} from '@toeverything/infra'; | ||
|
||
import { DocsSearchService } from '../docs-search'; | ||
import { DocInfoModal } from './entities/modal'; | ||
import { DocDatabaseBacklinksService } from './services/doc-database-backlinks'; | ||
import { DocInfoService } from './services/doc-info'; | ||
|
||
export { DocDatabaseBacklinkInfo } from './views/database-properties/doc-database-backlink-info'; | ||
|
||
export { DocInfoService }; | ||
|
||
export function configureDocInfoModule(framework: Framework) { | ||
framework.scope(WorkspaceScope).service(DocInfoService).entity(DocInfoModal); | ||
framework | ||
.scope(WorkspaceScope) | ||
.service(DocInfoService) | ||
.service(DocDatabaseBacklinksService, [DocsService, DocsSearchService]) | ||
.entity(DocInfoModal); | ||
} |
136 changes: 136 additions & 0 deletions
136
packages/frontend/core/src/modules/doc-info/services/doc-database-backlinks.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,136 @@ | ||
import { | ||
DatabaseBlockDataSource, | ||
type DatabaseBlockModel, | ||
} from '@blocksuite/affine/blocks'; | ||
import type { DocsService } from '@toeverything/infra'; | ||
import { Service } from '@toeverything/infra'; | ||
import { combineLatest, map, Observable, switchMap } from 'rxjs'; | ||
|
||
import type { Backlink } from '../../doc-link'; | ||
import type { DocsSearchService } from '../../docs-search'; | ||
import type { DatabaseRow, DatabaseValueCell } from '../types'; | ||
import { signalToObservable } from '../utils'; | ||
|
||
export class DocDatabaseBacklinksService extends Service { | ||
constructor( | ||
private readonly docsService: DocsService, | ||
private readonly docsSearchService: DocsSearchService | ||
) { | ||
super(); | ||
} | ||
|
||
private async ensureDocLoaded(docId: string) { | ||
const docRef = this.docsService.open(docId); | ||
if (!docRef.doc.blockSuiteDoc.ready) { | ||
docRef.doc.blockSuiteDoc.load(); | ||
} | ||
docRef.doc.setPriorityLoad(10); | ||
await docRef.doc.waitForSyncReady(); | ||
return docRef; | ||
} | ||
|
||
private watchRowCells$(dbModel: DatabaseBlockModel, rowId: string) { | ||
const dataSource = new DatabaseBlockDataSource(dbModel); | ||
|
||
console.log('watchRowCells$', dataSource.propertyMetas); | ||
|
||
const hydratedRows$ = combineLatest([ | ||
signalToObservable(dataSource.rows$), | ||
signalToObservable(dataSource.properties$), | ||
]).pipe( | ||
map(([rowIds, propertyIds]) => { | ||
const rowExists = rowIds.some(id => id === rowId); | ||
if (!rowExists) { | ||
return undefined; | ||
} | ||
return propertyIds | ||
.map<DatabaseValueCell | undefined>(id => { | ||
const type = dataSource.propertyTypeGet(id); | ||
return { | ||
value: dataSource.cellValueGet(rowId, id), | ||
property: { | ||
id, | ||
type, | ||
name: dataSource.propertyNameGet(id), | ||
additionalData: dataSource.propertyDataGet(id), | ||
}, | ||
}; | ||
}) | ||
.filter((p: any): p is DatabaseValueCell => !!p) | ||
.toSorted((a, b) => | ||
(a.property.name ?? '').localeCompare(b.property.name ?? '') | ||
); | ||
}) | ||
); | ||
|
||
return [hydratedRows$, dataSource] as const; | ||
} | ||
|
||
// for each backlink, | ||
// 1. check if it is in a database block | ||
// 2. if it is, return the related database row | ||
// 3. if it is not, return undefined | ||
private watchDatabaseRow$(backlink: Backlink) { | ||
return new Observable<DatabaseRow | undefined>(subscriber => { | ||
let disposed = false; | ||
let unsubscribe = () => {}; | ||
const docRef = this.docsService.open(backlink.docId); | ||
const run = async () => { | ||
await this.ensureDocLoaded(backlink.docId); | ||
if (disposed) { | ||
return; | ||
} | ||
const block = docRef.doc.blockSuiteDoc.getBlock(backlink.blockId); | ||
const parent = block?.model.parent; | ||
if (parent?.flavour === 'affine:database') { | ||
const dbModel = parent as DatabaseBlockModel; | ||
const [cells$, dataSource] = this.watchRowCells$( | ||
dbModel, | ||
backlink.blockId | ||
); | ||
const subscription = cells$.subscribe(cells => { | ||
if (cells) { | ||
subscriber.next({ | ||
cells, | ||
id: backlink.blockId, | ||
doc: docRef.doc, | ||
docId: backlink.docId, | ||
databaseId: dbModel.id, | ||
databaseName: dbModel.title.yText.toString(), | ||
dataSource: dataSource, | ||
}); | ||
} else { | ||
subscriber.next(undefined); | ||
} | ||
}); | ||
unsubscribe = () => subscription.unsubscribe(); | ||
} | ||
}; | ||
|
||
run().catch(e => { | ||
console.error(`failed to get database info:`, e); | ||
docRef.release(); | ||
}); | ||
|
||
return () => { | ||
docRef.release(); | ||
disposed = true; | ||
unsubscribe(); | ||
}; | ||
}); | ||
} | ||
|
||
// backlinks (docid:blockid) -> related db rows (DatabaseRow[]) | ||
watchDbBacklinkRows$(docId: string) { | ||
return this.docsSearchService.watchRefsTo(docId).pipe( | ||
switchMap(backlinks => { | ||
return combineLatest( | ||
backlinks.map(backlink => { | ||
return this.watchDatabaseRow$(backlink); | ||
}) | ||
); | ||
}), | ||
map(rows => rows.filter((row): row is DatabaseRow => Boolean(row))) | ||
); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
import type { DatabaseBlockDataSource } from '@blocksuite/affine/blocks'; | ||
import type { Doc, DocCustomPropertyInfo } from '@toeverything/infra'; | ||
|
||
// make database property type to be compatible with DocCustomPropertyInfo | ||
export type DatabaseProperty = Required< | ||
Pick<DocCustomPropertyInfo, 'id' | 'name' | 'type' | 'additionalData'> | ||
>; | ||
|
||
export interface DatabaseValueCell { | ||
property: DatabaseProperty; | ||
value: unknown; | ||
} | ||
|
||
export interface DatabaseRow { | ||
cells: DatabaseValueCell[]; | ||
id: string; // row id (block id) | ||
doc: Doc; // the doc that contains the database. required for editing etc. | ||
docId: string; // for rendering the doc reference | ||
dataSource: DatabaseBlockDataSource; | ||
databaseId: string; | ||
databaseName: string; // the title | ||
} | ||
|
||
export interface DatabaseCellRendererProps { | ||
rowId: string; | ||
cell: DatabaseValueCell; | ||
dataSource: DatabaseBlockDataSource; | ||
doc: Doc; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
import { BlockStdScope } from '@blocksuite/affine/block-std'; | ||
import { PageEditorBlockSpecs } from '@blocksuite/affine/blocks'; | ||
import type { Doc } from '@blocksuite/affine/store'; | ||
import { useMemo } from 'react'; | ||
import { Observable } from 'rxjs'; | ||
|
||
interface ReadonlySignal<T> { | ||
subscribe: (fn: (value: T) => void) => () => void; | ||
} | ||
|
||
export function signalToObservable<T>( | ||
signal: ReadonlySignal<T> | ||
): Observable<T> { | ||
return new Observable(subscriber => { | ||
const unsub = signal.subscribe(value => { | ||
subscriber.next(value); | ||
}); | ||
return () => { | ||
unsub(); | ||
}; | ||
}); | ||
} | ||
|
||
// todo(pengx17): use rc pool? | ||
export function createBlockStdScope(doc: Doc) { | ||
const std = new BlockStdScope({ | ||
doc, | ||
extensions: PageEditorBlockSpecs, | ||
}); | ||
return std; | ||
} | ||
|
||
export function useBlockStdScope(doc: Doc) { | ||
return useMemo(() => createBlockStdScope(doc), [doc]); | ||
} |
19 changes: 19 additions & 0 deletions
19
packages/frontend/core/src/modules/doc-info/views/database-properties/cells/checkbox.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
import { CheckboxValue } from '@affine/core/components/doc-properties/types/checkbox'; | ||
|
||
import type { DatabaseCellRendererProps } from '../../../types'; | ||
|
||
export const CheckboxCell = ({ | ||
cell, | ||
rowId, | ||
dataSource, | ||
}: DatabaseCellRendererProps) => { | ||
return ( | ||
<CheckboxValue | ||
// todo(pengx17): better internal impl | ||
value={cell.value ? 'true' : 'false'} | ||
onChange={v => { | ||
dataSource.cellValueChange(rowId, cell.property.id, v === 'true'); | ||
}} | ||
/> | ||
); | ||
}; |
75 changes: 75 additions & 0 deletions
75
packages/frontend/core/src/modules/doc-info/views/database-properties/cells/constant.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
import type { I18nString } from '@affine/i18n'; | ||
import { | ||
CheckBoxCheckLinearIcon, | ||
DateTimeIcon, | ||
LinkIcon, | ||
NumberIcon, | ||
ProgressIcon, | ||
SelectIcon, | ||
TextIcon, | ||
} from '@blocksuite/icons/rc'; | ||
|
||
import type { DatabaseCellRendererProps } from '../../../types'; | ||
import { CheckboxCell } from './checkbox'; | ||
import { DateCell } from './date'; | ||
import { LinkCell } from './link'; | ||
import { NumberCell } from './number'; | ||
import { RichTextCell } from './rich-text'; | ||
|
||
export const DatabaseRendererTypes = { | ||
'rich-text': { | ||
icon: TextIcon, | ||
Renderer: RichTextCell, | ||
name: 'com.affine.page-properties.property.text', | ||
}, | ||
checkbox: { | ||
icon: CheckBoxCheckLinearIcon, | ||
Renderer: CheckboxCell, | ||
name: 'com.affine.page-properties.property.checkbox', | ||
}, | ||
date: { | ||
icon: DateTimeIcon, | ||
Renderer: DateCell, | ||
name: 'com.affine.page-properties.property.date', | ||
}, | ||
number: { | ||
icon: NumberIcon, | ||
Renderer: NumberCell, | ||
name: 'com.affine.page-properties.property.number', | ||
}, | ||
link: { | ||
icon: LinkIcon, | ||
Renderer: LinkCell, | ||
name: 'com.affine.page-properties.property.link', | ||
}, | ||
progress: { | ||
icon: ProgressIcon, | ||
Renderer: NumberCell, | ||
name: 'com.affine.page-properties.property.progress', | ||
}, | ||
select: { | ||
icon: SelectIcon, | ||
Renderer: ({ cell }) => { | ||
return cell.value.toString(); | ||
}, | ||
name: 'com.affine.page-properties.property.select', | ||
}, | ||
'multi-select': { | ||
icon: SelectIcon, | ||
Renderer: ({ cell }) => { | ||
return cell.value.toString(); | ||
}, | ||
name: 'com.affine.page-properties.property.multi-select', | ||
}, | ||
} as Record< | ||
string, | ||
{ | ||
icon: React.FC<React.SVGProps<SVGSVGElement>>; | ||
Renderer: React.FC<DatabaseCellRendererProps>; | ||
name: I18nString; | ||
} | ||
>; | ||
|
||
export const isSupportedDatabaseRendererType = (type?: string): boolean => { | ||
return type ? type in DatabaseRendererTypes : false; | ||
}; |
Oops, something went wrong.