Indexer step 1 (#6798)

Signed-off-by: Denis Bykhov <bykhov.denis@gmail.com>
This commit is contained in:
Denis Bykhov 2024-10-04 15:07:02 +05:00 committed by GitHub
parent ec696d029b
commit 70a1666a57
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
24 changed files with 207 additions and 149 deletions

View File

@ -19,7 +19,8 @@ import {
makeCollaborativeDoc,
type TxOperations,
type Blob,
collaborativeDocParse
collaborativeDocParse,
type Data
} from '@hcengineering/core'
import { yDocToBuffer } from '@hcengineering/collaboration'
import document, { type Document, type Teamspace, getFirstRank } from '@hcengineering/document'
@ -325,16 +326,17 @@ async function createDBPageWithAttachments (
documentMetaMap?: Map<string, DocumentMetadata>
): Promise<void> {
const pageId = docMeta.id as Ref<Document>
const collabId = makeCollaborativeDoc(pageId, 'content')
const collabId = makeCollaborativeDoc(pageId, 'description')
const parentId = parentMeta !== undefined ? (parentMeta.id as Ref<Document>) : document.ids.NoParent
const lastRank = await getFirstRank(client, space, parentId)
const rank = makeRank(lastRank, undefined)
const object: AttachedData<Document> = {
name: docMeta.name,
content: collabId,
const object: Data<Document> = {
title: docMeta.name,
description: collabId,
parent: parentId,
attachments: 0,
children: 0,
embeddings: 0,
@ -344,15 +346,7 @@ async function createDBPageWithAttachments (
rank
}
await client.addCollection(
document.class.Document,
space,
parentId,
document.class.Document,
'children',
object,
pageId
)
await client.createDoc(document.class.Document, space, object, pageId)
const dbPage: DocumentMetadata = {
id: pageId,
@ -466,7 +460,7 @@ async function importPageDocument (
}
const id = docMeta.id as Ref<Document>
const collabId = makeCollaborativeDoc(id, 'content')
const collabId = makeCollaborativeDoc(id, 'description')
const yDoc = jsonToYDocNoSchema(json, 'content')
const { documentId } = collaborativeDocParse(collabId)
const buffer = yDocToBuffer(yDoc)
@ -482,14 +476,15 @@ async function importPageDocument (
await uploadFile(docMeta.id, form)
const parentId = parentMeta?.id ?? document.ids.NoParent
const parent = (parentMeta?.id as Ref<Document>) ?? document.ids.NoParent
const lastRank = await getFirstRank(client, space, parentId as Ref<Document>)
const lastRank = await getFirstRank(client, space, parent)
const rank = makeRank(lastRank, undefined)
const attachedData: AttachedData<Document> = {
name: docMeta.name,
content: collabId,
const attachedData: Data<Document> = {
title: docMeta.name,
description: collabId,
parent,
attachments: 0,
children: 0,
embeddings: 0,
@ -499,15 +494,7 @@ async function importPageDocument (
rank
}
await client.addCollection(
document.class.Document,
space,
parentId as Ref<Document>,
document.class.Document,
'children',
attachedData,
id
)
await client.createDoc(document.class.Document, space, attachedData, id)
}
function preProcessMarkdown (json: MarkupNode, documentMetaMap: Map<string, DocumentMetadata>): void {

View File

@ -14,6 +14,8 @@
//
import {
type Card,
type CollaborativeDoc,
DOMAIN_BLOB,
DOMAIN_CONFIGURATION,
DOMAIN_DOC_INDEX_STATE,
@ -60,6 +62,7 @@ import {
Prop,
ReadOnly,
TypeBoolean,
TypeCollaborativeDoc,
TypeFileSize,
TypeIntlString,
TypeRecord,
@ -111,6 +114,22 @@ export class TDoc extends TObj implements Doc {
createdOn!: Timestamp
}
@Model(core.class.Card, core.class.Obj)
@UX(core.string.Object)
export class TCard extends TDoc implements Card {
@Prop(TypeString(), core.string.Name)
title!: string
@Prop(TypeCollaborativeDoc(), core.string.Description)
description!: CollaborativeDoc | null
@Prop(TypeString(), core.string.Id)
identifier?: string | undefined
@Prop(TypeRef(core.class.Card), core.string.AttachedTo)
parent?: Ref<Card> | null
}
@Model(core.class.AttachedDoc, core.class.Doc)
export class TAttachedDoc extends TDoc implements AttachedDoc {
@Prop(TypeRef(core.class.Doc), core.string.AttachedTo)

View File

@ -44,6 +44,7 @@ import {
TConfiguration,
TConfigurationElement,
TDoc,
TCard,
TDocIndexState,
TDomainIndexConfiguration,
TEnum,
@ -160,6 +161,7 @@ export function createModel (builder: Builder): void {
TEnum,
TTypeAny,
TTypeRelatedDocument,
TCard,
TDocIndexState,
TFullTextSearchContext,
TConfiguration,

View File

@ -15,7 +15,7 @@
import activity from '@hcengineering/activity'
import type { Class, CollaborativeDoc, CollectionSize, Domain, Rank, Role, RolesAssignment } from '@hcengineering/core'
import { IndexKind, Account, Ref, AccountRole } from '@hcengineering/core'
import { Account, AccountRole, IndexKind, Ref } from '@hcengineering/core'
import {
type Document,
type DocumentEmbedding,
@ -29,19 +29,19 @@ import {
Collection,
Hidden,
Index,
Mixin,
Model,
Prop,
TypeCollaborativeDoc,
TypeCollaborativeDocVersion,
TypeNumber,
TypeRef,
TypeString,
UX,
TypeCollaborativeDoc,
TypeCollaborativeDocVersion,
Mixin
UX
} from '@hcengineering/model'
import attachment, { TAttachment } from '@hcengineering/model-attachment'
import chunter from '@hcengineering/model-chunter'
import core, { TAttachedDoc, TTypedSpace } from '@hcengineering/model-core'
import core, { TCard, TTypedSpace } from '@hcengineering/model-core'
import { createPublicLinkAction } from '@hcengineering/model-guest'
import { generateClassNotificationTypes } from '@hcengineering/model-notification'
import preference, { TPreference } from '@hcengineering/model-preference'
@ -50,7 +50,7 @@ import tracker from '@hcengineering/model-tracker'
import view, { actionTemplates, createAction } from '@hcengineering/model-view'
import workbench from '@hcengineering/model-workbench'
import notification from '@hcengineering/notification'
import { getEmbeddedLabel, type Asset } from '@hcengineering/platform'
import { type Asset, getEmbeddedLabel } from '@hcengineering/platform'
import tags from '@hcengineering/tags'
import time, { type ToDo, type Todoable } from '@hcengineering/time'
import document from './plugin'
@ -69,31 +69,23 @@ export class TDocumentEmbedding extends TAttachment implements DocumentEmbedding
declare attachedToClass: Ref<Class<Document>>
}
@Model(document.class.Document, core.class.AttachedDoc, DOMAIN_DOCUMENT)
@Model(document.class.Document, core.class.Card, DOMAIN_DOCUMENT)
@UX(document.string.Document, document.icon.Document, undefined, 'name', undefined, document.string.Documents)
export class TDocument extends TAttachedDoc implements Document, Todoable {
export class TDocument extends TCard implements Document, Todoable {
@Prop(TypeRef(document.class.Document), document.string.ParentDocument)
declare attachedTo: Ref<Document>
@Prop(TypeRef(core.class.Class), core.string.AttachedToClass)
@Hidden()
declare attachedToClass: Ref<Class<Document>>
declare parent: Ref<Document>
@Prop(TypeRef(core.class.Space), core.string.Space)
@Index(IndexKind.Indexed)
@Hidden()
declare space: Ref<Teamspace>
@Prop(TypeString(), core.string.Collection)
@Hidden()
override collection: 'children' = 'children'
@Prop(TypeString(), document.string.Name)
@Index(IndexKind.FullText)
name!: string
declare title: string
@Prop(TypeCollaborativeDoc(), document.string.Document)
content!: CollaborativeDoc
declare description: CollaborativeDoc
@Prop(TypeRef(core.class.Account), document.string.LockedBy)
@Hidden()
@ -136,31 +128,24 @@ export class TDocument extends TAttachedDoc implements Document, Todoable {
rank!: Rank
}
@Model(document.class.DocumentSnapshot, core.class.AttachedDoc, DOMAIN_DOCUMENT)
@Model(document.class.DocumentSnapshot, core.class.Card, DOMAIN_DOCUMENT)
@UX(document.string.Version)
export class TDocumentSnapshot extends TAttachedDoc implements DocumentSnapshot {
export class TDocumentSnapshot extends TCard implements DocumentSnapshot {
@Prop(TypeRef(document.class.Document), document.string.ParentDocument)
declare attachedTo: Ref<Document>
@Prop(TypeRef(core.class.Class), core.string.AttachedToClass)
declare attachedToClass: Ref<Class<Document>>
declare parent: Ref<Document>
@Prop(TypeRef(core.class.Space), core.string.Space)
@Index(IndexKind.Indexed)
@Hidden()
declare space: Ref<Teamspace>
@Prop(TypeString(), core.string.Collection)
@Hidden()
override collection: 'snapshots' = 'snapshots'
@Prop(TypeString(), document.string.Name)
@Index(IndexKind.FullText)
name!: string
declare title: string
@Prop(TypeCollaborativeDocVersion(), document.string.Document)
@Hidden()
content!: CollaborativeDoc
declare description: CollaborativeDoc
}
@Model(document.class.SavedDocument, preference.class.Preference)

View File

@ -14,21 +14,21 @@
//
import { DOMAIN_TX, MeasureMetricsContext, SortingOrder } from '@hcengineering/core'
import { type Document, type Teamspace } from '@hcengineering/document'
import { type DocumentSnapshot, type Document, type Teamspace } from '@hcengineering/document'
import {
tryMigrate,
type MigrateOperation,
type MigrationClient,
type MigrationUpgradeClient,
type MigrateUpdate,
type MigrationClient,
type MigrationDocumentQuery,
tryMigrate
type MigrationUpgradeClient
} from '@hcengineering/model'
import core, { DOMAIN_SPACE } from '@hcengineering/model-core'
import { type Asset } from '@hcengineering/platform'
import { makeRank } from '@hcengineering/rank'
import document, { documentId, DOMAIN_DOCUMENT } from './index'
import { loadCollaborativeDoc, saveCollaborativeDoc, yDocCopyXmlField } from '@hcengineering/collaboration'
import document, { documentId, DOMAIN_DOCUMENT } from './index'
async function migrateDocumentIcons (client: MigrationClient): Promise<void> {
await client.update<Teamspace>(
@ -111,9 +111,9 @@ async function migrateContentField (client: MigrationClient): Promise<void> {
for (const document of documents) {
try {
const ydoc = await loadCollaborativeDoc(storage, client.workspaceId, document.content, ctx)
const ydoc = await loadCollaborativeDoc(storage, client.workspaceId, document.description, ctx)
if (ydoc === undefined) {
ctx.error('document content not found', { document: document.name })
ctx.error('document content not found', { document: document.title })
continue
}
@ -123,9 +123,9 @@ async function migrateContentField (client: MigrationClient): Promise<void> {
yDocCopyXmlField(ydoc, '', 'content')
await saveCollaborativeDoc(storage, client.workspaceId, document.content, ydoc, ctx)
await saveCollaborativeDoc(storage, client.workspaceId, document.description, ydoc, ctx)
} catch (err) {
ctx.error('error document content migration', { error: err, document: document.name })
ctx.error('error document content migration', { error: err, document: document.title })
}
}
}
@ -154,6 +154,54 @@ async function migrateRank (client: MigrationClient): Promise<void> {
await client.bulk(DOMAIN_DOCUMENT, operations)
}
async function renameFields (client: MigrationClient): Promise<void> {
const documents = await client.find<Document>(DOMAIN_DOCUMENT, {
_class: document.class.Document,
content: { $exists: true }
})
for (const document of documents) {
await client.update(
DOMAIN_DOCUMENT,
{ _id: document._id },
{
$rename: {
attachedTo: 'parent',
content: 'description',
name: 'title'
},
$unset: {
attachedToClass: '',
collection: ''
}
}
)
}
const spnapshots = await client.find<DocumentSnapshot>(DOMAIN_DOCUMENT, {
_class: document.class.DocumentSnapshot,
content: { $exists: true }
})
for (const snapshot of spnapshots) {
await client.update(
DOMAIN_DOCUMENT,
{ _id: snapshot._id },
{
$rename: {
attachedTo: 'parent',
content: 'description',
name: 'title'
},
$unset: {
attachedToClass: '',
collection: ''
}
}
)
}
}
export const documentOperation: MigrateOperation = {
async migrate (client: MigrationClient): Promise<void> {
await tryMigrate(client, documentId, [
@ -176,6 +224,10 @@ export const documentOperation: MigrateOperation = {
{
state: 'migrateRank',
func: migrateRank
},
{
state: 'renameFields',
func: renameFields
}
])
},

View File

@ -16,6 +16,7 @@
import type { Asset, IntlString, Plugin } from '@hcengineering/platform'
import type { DocumentQuery } from './storage'
import { CollaborativeDoc } from './collaboration'
/**
* @public
@ -73,6 +74,13 @@ export interface Doc<S extends Space = Space> extends Obj {
createdOn?: Timestamp // Marked as optional since it will be filled by platform.
}
export interface Card extends Doc {
title: string
description: CollaborativeDoc | null
identifier?: string
parent?: Ref<Card> | null
}
/**
* @public
*/

View File

@ -21,6 +21,7 @@ import type {
ArrOf,
AttachedDoc,
Blob,
Card,
Class,
Collection,
Configuration,
@ -82,6 +83,7 @@ export default plugin(coreId, {
class: {
Obj: '' as Ref<Class<Obj>>,
Doc: '' as Ref<Class<Doc>>,
Card: '' as Ref<Class<Card>>,
Blob: '' as Ref<Class<Blob>>,
AttachedDoc: '' as Ref<Class<AttachedDoc>>,
Class: '' as Ref<Class<Class<Obj>>>,

View File

@ -15,9 +15,10 @@
//
-->
<script lang="ts">
import { AttachedData, Ref, generateId } from '@hcengineering/core'
import { Document, Teamspace, DocumentEvents } from '@hcengineering/document'
import { Card, SpaceSelector, getClient } from '@hcengineering/presentation'
import { Analytics } from '@hcengineering/analytics'
import { Data, generateId, Ref } from '@hcengineering/core'
import { Document, DocumentEvents, Teamspace } from '@hcengineering/document'
import { Card, getClient, SpaceSelector } from '@hcengineering/presentation'
import {
Button,
createFocusManager,
@ -31,14 +32,13 @@
import view from '@hcengineering/view'
import { IconPicker, ObjectBox } from '@hcengineering/view-resources'
import { createEventDispatcher } from 'svelte'
import { Analytics } from '@hcengineering/analytics'
import document from '../plugin'
import { createEmptyDocument } from '../utils'
import TeamspacePresenter from './teamspace/TeamspacePresenter.svelte'
export function canClose (): boolean {
return object.name === ''
return object.title === ''
}
export let space: Ref<Teamspace>
@ -46,8 +46,8 @@
const id: Ref<Document> = generateId()
const object: Pick<AttachedData<Document>, 'name' | 'icon' | 'color'> = {
name: ''
const object: Pick<Data<Document>, 'title' | 'icon' | 'color'> = {
title: ''
}
const dispatch = createEventDispatcher()
@ -57,7 +57,7 @@
let _parent = parent
$: if (_space !== space) _parent = undefined
$: canSave = getTitle(object.name).length > 0 && _space !== undefined
$: canSave = getTitle(object.title).length > 0 && _space !== undefined
function chooseIcon (): void {
const { icon, color } = object
@ -75,7 +75,7 @@
}
async function create (): Promise<void> {
await createEmptyDocument(client, id, _space, _parent ?? document.ids.NoParent, object)
await createEmptyDocument(client, id, _space, _parent, object)
Analytics.handleEvent(DocumentEvents.DocumentCreated, { id, parent: _parent })
dispatch('close', id)
}
@ -140,7 +140,7 @@
</div>
<EditBox
placeholder={document.string.DocumentNamePlaceholder}
bind:value={object.name}
bind:value={object.title}
kind={'large-style'}
autoFocus
focusIndex={1}

View File

@ -56,12 +56,12 @@
</script>
<CollaboratorEditor
collaborativeDoc={object.content}
collaborativeDoc={object.description}
objectClass={object._class}
objectId={object._id}
objectSpace={object.space}
objectAttr="content"
field="content"
objectAttr="description"
field="description"
{user}
{userComponent}
{focusIndex}

View File

@ -28,6 +28,6 @@
<DocumentIcon {value} size={'medium'} defaultIcon={document.icon.Document} />
</div>
<span class="overflow-label">
{value.name}
{value.title}
</span>
</div>

View File

@ -52,13 +52,13 @@
</div>
{/if}
<span class="label nowrap" class:no-underline={noUnderline || disabled} class:fs-bold={accent}>
{value.name}
{value.title}
</span>
</div>
</DocNavLink>
{:else if type === 'text'}
<span class="overflow-label" use:tooltip={{ label: document.string.Document }}>
{value.name}
{value.title}
</span>
{/if}
{/if}

View File

@ -27,5 +27,5 @@
<div class="icon">
<Icon icon={document.icon.DocumentApplication} size={'small'} />
</div>
{value.name}
{value.title}
</div>

View File

@ -80,7 +80,7 @@
const client = getClient()
let doc: WithLookup<Document> | undefined
let name = ''
let title = ''
let innerWidth: number
let headings: Heading[] = []
@ -141,10 +141,10 @@
$: _id !== undefined &&
query.query(document.class.Document, { _id }, async (result) => {
;[doc] = result
name = doc?.name ?? ''
title = doc?.title ?? ''
})
$: canSave = name.trim().length > 0
$: canSave = title.trim().length > 0
async function saveTitle (ev: Event): Promise<void> {
ev.preventDefault()
@ -153,10 +153,10 @@
return
}
const nameTrimmed = name.trim()
const nameTrimmed = title.trim()
if (nameTrimmed.length > 0 && nameTrimmed !== doc.name) {
await client.update(doc, { name: nameTrimmed })
if (nameTrimmed.length > 0 && nameTrimmed !== doc.title) {
await client.update(doc, { title: nameTrimmed })
}
}
@ -322,7 +322,7 @@
<DocumentTitle
focusIndex={1}
fill
bind:value={name}
bind:value={title}
{readonly}
placeholder={document.string.DocumentNamePlaceholder}
on:blur={(evt) => saveTitle(evt)}

View File

@ -29,7 +29,7 @@
const dispatch = createEventDispatcher()
let space: Ref<Teamspace> = value.space
let parent: Ref<Document> = value.attachedTo
let parent: Ref<Document> = value.parent
let children: Ref<Document>[] = []
$: void updateChildren(value)
@ -58,15 +58,15 @@
async function findChildren (doc: Document): Promise<Array<Ref<Document>>> {
const documents = await client.findAll(
document.class.Document,
{ space: doc.space, attachedTo: { $ne: document.ids.NoParent } },
{ projection: { _id: 1, attachedTo: 1 } }
{ space: doc.space, parent: { $ne: document.ids.NoParent } },
{ projection: { _id: 1, parent: 1 } }
)
const byParent = new Map<Ref<Document>, Array<Ref<Document>>>()
for (const document of documents) {
const group = byParent.get(document.attachedTo) ?? []
const group = byParent.get(document.parent) ?? []
group.push(document._id)
byParent.set(document.attachedTo, group)
byParent.set(document.parent, group)
}
const result: Ref<Document>[] = []
@ -83,7 +83,7 @@
return result
}
$: canSave = space !== value.space || parent !== value.attachedTo
$: canSave = space !== value.space || parent !== value.parent
</script>
<Card

View File

@ -21,7 +21,7 @@
{#if value}
<div class="flex-col">
<span class="overflow-label mt-10px">
{value.name}
{value.title}
</span>
</div>
{/if}

View File

@ -109,7 +109,7 @@
: {
fill: doc.color !== undefined ? getPlatformColorDef(doc.color, $themeStore.dark).icon : 'currentColor'
}}
title={doc.name}
title={doc.title}
selected={selected === doc._id && draggedItem === undefined}
isFold
{level}

View File

@ -99,9 +99,9 @@
descendants.clear()
for (const doc of result) {
const current = descendants.get(doc.attachedTo) ?? []
const current = descendants.get(doc.parent) ?? []
current.push(doc)
descendants.set(doc.attachedTo, current)
descendants.set(doc.parent, current)
documentById.set(doc._id, doc)
}
@ -243,7 +243,7 @@
void moveDocumentBefore(doc, target)
} else if (pos === 'after') {
void moveDocumentAfter(doc, target)
} else if (doc.attachedTo !== object) {
} else if (doc.parent !== object) {
void moveDocument(doc, target.space, target._id)
}
}
@ -315,7 +315,7 @@
: {
fill: item.color !== undefined ? getPlatformColorDef(item.color, $themeStore.dark).icon : 'currentColor'
}}
title={item.name}
title={item.title}
selected
isFold
empty

View File

@ -30,7 +30,7 @@
<div class="container flex-col flex-gap-2 flex-no-shrink">
<div class="flex-between h-8">
<div class="fs-bold overflow-label">
{value.name}
{value.title}
</div>
<div class="time">
<TimeSince value={value.createdOn} />

View File

@ -57,7 +57,7 @@ import {
const toObjectSearchResult = (e: WithLookup<Document>): ObjectSearchResult => ({
doc: e,
title: e.name,
title: e.title,
icon: document.icon.Document,
component: DocumentItem
})

View File

@ -14,8 +14,8 @@
//
import {
type AttachedData,
type Client,
type Data,
type QuerySelector,
type Ref,
SortingOrder,
@ -26,7 +26,7 @@ import { type Document, type Teamspace, documentId, getFirstRank } from '@hcengi
import { getMetadata, translate } from '@hcengineering/platform'
import presentation, { getClient } from '@hcengineering/presentation'
import { makeRank } from '@hcengineering/rank'
import { getCurrentResolvedLocation, getPanelURI, type Location, type ResolvedLocation } from '@hcengineering/ui'
import { type Location, type ResolvedLocation, getCurrentResolvedLocation, getPanelURI } from '@hcengineering/ui'
import { accessDeniedStore } from '@hcengineering/view-resources'
import { workbenchId } from '@hcengineering/workbench'
import slugify from 'slugify'
@ -39,46 +39,47 @@ export async function moveDocument (doc: Document, space: Ref<Teamspace>, parent
const prevRank = await getFirstRank(client, space, parent)
const rank = makeRank(prevRank, undefined)
await client.update(doc, { space, attachedTo: parent, rank })
await client.update(doc, { space, parent, rank })
}
export async function moveDocumentBefore (doc: Document, before: Document): Promise<void> {
const client = getClient()
const { space, attachedTo } = before
const { space, parent } = before
const query = { rank: { $lt: before.rank } as unknown as QuerySelector<Document['rank']> }
const lastRank = await getFirstRank(client, space, attachedTo, SortingOrder.Descending, query)
const lastRank = await getFirstRank(client, space, parent, SortingOrder.Descending, query)
const rank = makeRank(lastRank, before.rank)
await client.update(doc, { space, attachedTo, rank })
await client.update(doc, { space, parent, rank })
}
export async function moveDocumentAfter (doc: Document, after: Document): Promise<void> {
const client = getClient()
const { space, attachedTo } = after
const { space, parent } = after
const query = { rank: { $gt: after.rank } as unknown as QuerySelector<Document['rank']> }
const nextRank = await getFirstRank(client, space, attachedTo, SortingOrder.Ascending, query)
const nextRank = await getFirstRank(client, space, parent, SortingOrder.Ascending, query)
const rank = makeRank(after.rank, nextRank)
await client.update(doc, { space, attachedTo, rank })
await client.update(doc, { space, parent, rank })
}
export async function createEmptyDocument (
client: TxOperations,
id: Ref<Document>,
space: Ref<Teamspace>,
parent: Ref<Document>,
data: Partial<Pick<AttachedData<Document>, 'name' | 'icon' | 'color'>> = {}
parentId?: Ref<Document>,
data: Partial<Pick<Data<Document>, 'title' | 'icon' | 'color'>> = {}
): Promise<void> {
const name = await translate(document.string.Untitled, {})
const title = await translate(document.string.Untitled, {})
const parent = parentId ?? document.ids.NoParent
const lastRank = await getFirstRank(client, space, parent)
const rank = makeRank(lastRank, undefined)
const object: AttachedData<Document> = {
name,
content: makeCollaborativeDoc(id, 'content'),
const object: Data<Document> = {
title,
description: makeCollaborativeDoc(id, 'description'),
attachments: 0,
children: 0,
embeddings: 0,
@ -86,18 +87,11 @@ export async function createEmptyDocument (
comments: 0,
references: 0,
rank,
parent: parent ?? document.ids.NoParent,
...data
}
await client.addCollection(
document.class.Document,
space,
parent ?? document.ids.NoParent,
document.class.Document,
'children',
object,
id
)
await client.createDoc(document.class.Document, space, object, id)
}
export async function resolveLocation (loc: Location): Promise<ResolvedLocation | undefined> {
@ -171,7 +165,7 @@ export function getDocumentLink (doc: Document): Location {
}
export function getDocumentLinkId (doc: Document): string {
const slug = slugify(doc.name, { lower: true })
const slug = slugify(doc.title, { lower: true })
return `${slug}-${doc._id}`
}
@ -188,5 +182,5 @@ export function parseDocumentId (shortLink?: string): Ref<Document> | undefined
export async function documentTitleProvider (client: Client, ref: Ref<Document>, doc?: Document): Promise<string> {
const object = doc ?? (await client.findOne(document.class.Document, { _id: ref }))
return object?.name ?? ''
return object?.title ?? ''
}

View File

@ -14,7 +14,7 @@
//
import { Attachment } from '@hcengineering/attachment'
import { Account, AttachedDoc, Class, CollaborativeDoc, Rank, Ref, TypedSpace } from '@hcengineering/core'
import { Account, Card, Class, CollaborativeDoc, Rank, Ref, TypedSpace } from '@hcengineering/core'
import { Preference } from '@hcengineering/preference'
import { IconProps } from '@hcengineering/view'
@ -22,11 +22,12 @@ import { IconProps } from '@hcengineering/view'
export interface Teamspace extends TypedSpace, IconProps {}
/** @public */
export interface Document extends AttachedDoc<Document, 'children', Teamspace>, IconProps {
attachedTo: Ref<Document>
export interface Document extends Card, IconProps {
parent: Ref<Document>
name: string
content: CollaborativeDoc
description: CollaborativeDoc
space: Ref<Teamspace>
lockedBy?: Ref<Account> | null
@ -42,10 +43,10 @@ export interface Document extends AttachedDoc<Document, 'children', Teamspace>,
}
/** @public */
export interface DocumentSnapshot extends AttachedDoc<Document, 'snapshots', Teamspace> {
attachedTo: Ref<Document>
name: string
content: CollaborativeDoc
export interface DocumentSnapshot extends Card {
parent: Ref<Document>
title: string
description: CollaborativeDoc
}
/** @public */

View File

@ -21,13 +21,13 @@ import { type Document, type Teamspace } from './types'
export async function getFirstRank (
client: TxOperations,
space: Ref<Teamspace>,
attachedTo: Ref<Document>,
parent: Ref<Document>,
sort: SortingOrder = SortingOrder.Descending,
extra: DocumentQuery<Document> = {}
): Promise<Rank | undefined> {
const doc = await client.findOne(
document.class.Document,
{ space, attachedTo, ...extra },
{ space, parent, ...extra },
{ sort: { rank: sort }, projection: { rank: 1 } }
)

View File

@ -365,7 +365,15 @@
remoteProvider.awareness?.setLocalStateField('lastUpdate', Date.now())
}
function parseField (collaborativeDoc: CollaborativeDoc): string | undefined {
if (collaborativeDoc === undefined) return undefined
const _id = collaborativeDoc.split(':')
if (_id === undefined) return undefined
return _id[0]?.split('%')?.[1]
}
onMount(async () => {
const _field = parseField(collaborativeDoc) ?? field
await ph
editor = new Editor({
@ -398,7 +406,7 @@
Placeholder.configure({ placeholder: placeHolderStr }),
Collaboration.configure({
document: ydoc,
field
field: _field
}),
CollaborationCursor.configure({
provider: remoteProvider,

View File

@ -11,7 +11,7 @@ import serverCore, { TriggerControl } from '@hcengineering/server-core'
import slugify from 'slugify'
function getDocumentId (doc: Document): string {
const slug = slugify(doc.name, { lower: true })
const slug = slugify(doc.title, { lower: true })
return `${slug}-${doc._id}`
}
@ -23,7 +23,7 @@ export async function documentHTMLPresenter (doc: Doc, control: TriggerControl):
const front = control.branding?.front ?? getMetadata(serverCore.metadata.FrontUrl) ?? ''
const path = `${workbenchId}/${control.workspace.workspaceUrl}/${documentId}/${getDocumentId(document)}`
const link = concatLink(front, path)
return `<a href="${link}">${document.name}</a>`
return `<a href="${link}">${document.title}</a>`
}
export async function documentLinkIdProvider (doc: Document): Promise<string> {
@ -35,7 +35,7 @@ export async function documentLinkIdProvider (doc: Document): Promise<string> {
*/
export async function documentTextPresenter (doc: Doc): Promise<string> {
const document = doc as Document
return document.name
return document.title
}
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type