mirror of
https://github.com/hcengineering/platform.git
synced 2024-11-22 03:14:40 +03:00
Indexer step 1 (#6798)
Signed-off-by: Denis Bykhov <bykhov.denis@gmail.com>
This commit is contained in:
parent
ec696d029b
commit
70a1666a57
@ -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 {
|
||||
|
@ -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)
|
||||
|
@ -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,
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
}
|
||||
])
|
||||
},
|
||||
|
@ -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
|
||||
*/
|
||||
|
@ -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>>>,
|
||||
|
@ -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}
|
||||
|
@ -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}
|
||||
|
@ -28,6 +28,6 @@
|
||||
<DocumentIcon {value} size={'medium'} defaultIcon={document.icon.Document} />
|
||||
</div>
|
||||
<span class="overflow-label">
|
||||
{value.name}
|
||||
{value.title}
|
||||
</span>
|
||||
</div>
|
||||
|
@ -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}
|
||||
|
@ -27,5 +27,5 @@
|
||||
<div class="icon">
|
||||
<Icon icon={document.icon.DocumentApplication} size={'small'} />
|
||||
</div>
|
||||
{value.name}
|
||||
{value.title}
|
||||
</div>
|
||||
|
@ -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)}
|
||||
|
@ -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
|
||||
|
@ -21,7 +21,7 @@
|
||||
{#if value}
|
||||
<div class="flex-col">
|
||||
<span class="overflow-label mt-10px">
|
||||
{value.name}
|
||||
{value.title}
|
||||
</span>
|
||||
</div>
|
||||
{/if}
|
||||
|
@ -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}
|
||||
|
@ -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
|
||||
|
@ -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} />
|
||||
|
@ -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
|
||||
})
|
||||
|
@ -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 ?? ''
|
||||
}
|
||||
|
@ -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 */
|
||||
|
@ -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 } }
|
||||
)
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user