diff --git a/dev/docker-compose.yaml b/dev/docker-compose.yaml index 2ad74469fb..90db31d1bb 100644 --- a/dev/docker-compose.yaml +++ b/dev/docker-compose.yaml @@ -29,6 +29,14 @@ services: ports: - 5432:5432 restart: unless-stopped + cockroach: + image: cockroachdb/cockroach:latest-v24.2 + ports: + - "26257:26257" + - "8089:8080" + command: start-single-node --insecure + volumes: + - cockroach_db:/cockroach/cockroach-data" minio: image: 'minio/minio' command: server /data --address ":9000" --console-address ":9001" @@ -78,8 +86,8 @@ services: # - DB_URL=postgresql://postgres:example@postgres:5432 - DB_URL=${MONGO_URL} # - DB_NS=account-2 - - REGION_INFO=|Mongo;pg|Postgres - - TRANSACTOR_URL=ws://host.docker.internal:3333,ws://host.docker.internal:3331;;pg + - REGION_INFO=|Mongo;pg|Postgres;cockroach|CockroachDB + - TRANSACTOR_URL=ws://host.docker.internal:3333,ws://host.docker.internal:3331;;pg,ws://host.docker.internal:3332;;cockroach, - SES_URL= - STORAGE_CONFIG=${STORAGE_CONFIG} - FRONT_URL=http://host.docker.internal:8087 @@ -124,7 +132,6 @@ services: # - DB_URL=postgresql://postgres:example@postgres:5432 - SES_URL= - STORAGE_CONFIG=${STORAGE_CONFIG} - - FRONT_URL=http://host.docker.internal:8087 - RESERVED_DB_NAMES=telegram,gmail,github - MODEL_ENABLED=* - ACCOUNTS_URL=http://host.docker.internal:3000 @@ -160,6 +167,32 @@ services: - INIT_SCRIPT_URL=https://raw.githubusercontent.com/hcengineering/init/main/script.yaml # - INIT_WORKSPACE=onboarding restart: unless-stopped + workspace_cockroach: + image: hardcoreeng/workspace + extra_hosts: + - "host.docker.internal:host-gateway" + links: + - cockroach + - minio + - stats + volumes: + - ./branding.json:/var/cfg/branding.json + environment: + # - WS_OPERATION=create + - SERVER_SECRET=secret + - DB_URL=postgresql://root@host.docker.internal:26257/defaultdb?sslmode=disable + - STATS_URL=http://host.docker.internal:4900 + - SES_URL= + - REGION=cockroach + - STORAGE_CONFIG=${STORAGE_CONFIG} + - RESERVED_DB_NAMES=telegram,gmail,github + - MODEL_ENABLED=* + - ACCOUNTS_URL=http://host.docker.internal:3000 + - BRANDING_PATH=/var/cfg/branding.json + # - PARALLEL=2 + - INIT_SCRIPT_URL=https://raw.githubusercontent.com/hcengineering/init/main/script.yaml + # - INIT_WORKSPACE=onboarding + restart: unless-stopped collaborator: image: hardcoreeng/collaborator extra_hosts: @@ -270,15 +303,47 @@ services: environment: # - SERVER_PROVIDER=uweb # - UWS_HTTP_MAX_HEADERS_SIZE="32768" - - UV_THREADPOOL_SIZE=10 + # - UV_THREADPOOL_SIZE=10 - SERVER_PORT=3331 - SERVER_SECRET=secret - ENABLE_COMPRESSION=true - FULLTEXT_URL=http://host.docker.internal:4701 - STATS_URL=http://host.docker.internal:4900 - DB_URL=postgresql://postgres:example@postgres:5432 - - MONGO_URL=${MONGO_URL} - - 'MONGO_OPTIONS={"appName": "transactor-pg", "maxPoolSize": 1}' + - METRICS_CONSOLE=false + - METRICS_FILE=metrics.txt + - STORAGE_CONFIG=${STORAGE_CONFIG} + - FRONT_URL=http://host.docker.internal:8087 + # - APM_SERVER_URL=http://apm-server:8200 + - SES_URL='' + - ACCOUNTS_URL=http://host.docker.internal:3000 + - LAST_NAME_FIRST=true + - BRANDING_PATH=/var/cfg/branding.json + restart: unless-stopped + transactor_cockroach: + image: hardcoreeng/transactor + extra_hosts: + - "host.docker.internal:host-gateway" + links: + - cockroach + - minio + - account + - stats + # - apm-server + ports: + - 3332:3332 + volumes: + - ./branding.json:/var/cfg/branding.json + environment: + # - SERVER_PROVIDER=uweb + # - UWS_HTTP_MAX_HEADERS_SIZE="32768" + # - UV_THREADPOOL_SIZE=10 + - SERVER_PORT=3332 + - SERVER_SECRET=secret + - ENABLE_COMPRESSION=true + - FULLTEXT_URL=http://host.docker.internal:4702 + - STATS_URL=http://host.docker.internal:4900 + - DB_URL=postgresql://root@host.docker.internal:26257/defaultdb?sslmode=disable - METRICS_CONSOLE=false - METRICS_FILE=metrics.txt - STORAGE_CONFIG=${STORAGE_CONFIG} @@ -333,6 +398,26 @@ services: - STATS_URL=http://host.docker.internal:4900 - REKONI_URL=http://host.docker.internal:4004 - ACCOUNTS_URL=http://host.docker.internal:3000 + fulltext_cockroach: + image: hardcoreeng/fulltext + extra_hosts: + - "host.docker.internal:host-gateway" + restart: unless-stopped + links: + - elastic + - postgres + ports: + - 4702:4702 + environment: + - PORT=4702 + - SERVER_SECRET=secret + - DB_URL=postgresql://root@host.docker.internal:26257/defaultdb?sslmode=disable + - FULLTEXT_DB_URL=http://host.docker.internal:9200 + - ELASTIC_INDEX_NAME=local_storage_index + - STORAGE_CONFIG=${STORAGE_CONFIG} + - STATS_URL=http://host.docker.internal:4900 + - REKONI_URL=http://host.docker.internal:4004 + - ACCOUNTS_URL=http://host.docker.internal:3000 print: image: hardcoreeng/print extra_hosts: @@ -420,3 +505,4 @@ volumes: dbpg: files: elastic: + cockroach_db: diff --git a/dev/tool/src/clean.ts b/dev/tool/src/clean.ts index e3022a3d1a..33ad27850e 100644 --- a/dev/tool/src/clean.ts +++ b/dev/tool/src/clean.ts @@ -20,7 +20,6 @@ import { loadCollaborativeDoc, saveCollaborativeDoc, yDocToBuffer } from '@hceng import contact from '@hcengineering/contact' import core, { type ArrOf, - type AttachedDoc, type BackupClient, type Class, ClassifierKind, @@ -47,7 +46,6 @@ import core, { type StatusCategory, type Tx, type TxCUD, - type TxCollectionCUD, type TxCreateDoc, type TxMixin, TxOperations, @@ -158,16 +156,6 @@ export async function cleanWorkspace ( await db.collection(DOMAIN_TX).deleteMany({ objectId: (tx as TxRemoveDoc).objectId }) processed++ } - if ( - tx._class === core.class.TxCollectionCUD && - (tx as TxCollectionCUD).tx._class === core.class.TxRemoveDoc - ) { - // We need to remove all update and create operations for document - await db.collection(DOMAIN_TX).deleteMany({ - 'tx.objectId': ((tx as TxCollectionCUD).tx as TxRemoveDoc).objectId - }) - processed++ - } } if (processed % 1000 === 0) { console.log('processed', processed) @@ -219,18 +207,13 @@ export async function cleanRemovedTransactions (workspaceId: WorkspaceId, transa try { let count = 0 while (true) { - const removedDocs = await connection.findAll( - core.class.TxCollectionCUD, - { 'tx._class': core.class.TxRemoveDoc }, - { limit: 1000 } - ) + const removedDocs = await connection.findAll(core.class.TxRemoveDoc, {}, { limit: 1000 }) if (removedDocs.length === 0) { break } - const toRemove = await connection.findAll(core.class.TxCollectionCUD, { - 'tx._class': { $in: [core.class.TxCreateDoc, core.class.TxRemoveDoc, core.class.TxUpdateDoc] }, - 'tx.objectId': { $in: removedDocs.map((it) => it.tx.objectId) } + const toRemove = await connection.findAll(core.class.TxCUD, { + objectId: { $in: removedDocs.map((it) => it.objectId) } }) await connection.clean( DOMAIN_TX, @@ -368,20 +351,18 @@ export async function fixCommentDoubleIdCreate (workspaceId: WorkspaceId, transa mode: 'backup' })) as unknown as CoreClient & BackupClient try { - const commentTxes = await connection.findAll(core.class.TxCollectionCUD, { - 'tx._class': core.class.TxCreateDoc, - 'tx.objectClass': chunter.class.ChatMessage + const commentTxes = await connection.findAll(core.class.TxCreateDoc, { + objectClass: chunter.class.ChatMessage }) - const commentTxesRemoved = await connection.findAll(core.class.TxCollectionCUD, { - 'tx._class': core.class.TxRemoveDoc, - 'tx.objectClass': chunter.class.ChatMessage + const commentTxesRemoved = await connection.findAll(core.class.TxRemoveDoc, { + objectClass: chunter.class.ChatMessage }) - const removed = new Map(commentTxesRemoved.map((it) => [it.tx.objectId, it])) + const removed = new Map(commentTxesRemoved.map((it) => [it.objectId, it])) // Do not checked removed const objSet = new Set>() const oldValue = new Map, string>() for (const c of commentTxes) { - const cid = c.tx.objectId + const cid = c.objectId if (removed.has(cid)) { continue } @@ -389,7 +370,7 @@ export async function fixCommentDoubleIdCreate (workspaceId: WorkspaceId, transa objSet.add(cid) if (has) { // We have found duplicate one, let's rename it. - const doc = TxProcessor.createDoc2Doc(c.tx as unknown as TxCreateDoc) + const doc = TxProcessor.createDoc2Doc(c as unknown as TxCreateDoc) if (doc.message !== '' && doc.message.trim() !== '

') { await connection.clean(DOMAIN_TX, [c._id]) if (oldValue.get(cid) === doc.message.trim()) { @@ -398,8 +379,8 @@ export async function fixCommentDoubleIdCreate (workspaceId: WorkspaceId, transa oldValue.set(doc._id, doc.message) console.log('renaming', cid, doc.message) // Remove previous transaction. - c.tx.objectId = generateId() - doc._id = c.tx.objectId as Ref + c.objectId = generateId() + doc._id = c.objectId as Ref await connection.upload(DOMAIN_TX, [c]) // Also we need to create snapsot await connection.upload(DOMAIN_ACTIVITY, [doc]) diff --git a/dev/tool/src/mixin.ts b/dev/tool/src/mixin.ts index d967b9a974..1fb66f142c 100644 --- a/dev/tool/src/mixin.ts +++ b/dev/tool/src/mixin.ts @@ -215,17 +215,7 @@ async function getMixinWithForeignProperties ( { limit: 1, sort: { modifiedOn: SortingOrder.Descending } } ) - const collectionTx = await connection.findAll( - core.class.TxCollectionCUD, - { - 'tx._class': core.class.TxUpdateDoc, - 'tx.objectId': doc._id, - [`tx.operations.${property}`]: { $exists: true } - }, - { limit: 1, sort: { modifiedOn: SortingOrder.Descending } } - ) - - const cModifiedOn = Math.max(updateDocTx[0]?.modifiedOn ?? 0, collectionTx[0]?.modifiedOn ?? 0) + const cModifiedOn = updateDocTx[0]?.modifiedOn ?? 0 const mModifiedOn = mixinTx[0]?.modifiedOn ?? 0 properties.push({ name: property, cValue, mValue, cModifiedOn, mModifiedOn }) diff --git a/models/activity/src/index.ts b/models/activity/src/index.ts index d9a0149cbd..4d967097f8 100644 --- a/models/activity/src/index.ts +++ b/models/activity/src/index.ts @@ -201,11 +201,8 @@ export class TDocUpdateMessageViewlet extends TDoc implements DocUpdateMessageVi @Model(activity.class.ActivityExtension, core.class.Doc, DOMAIN_MODEL) export class TActivityExtension extends TDoc implements ActivityExtension { - @Prop(TypeRef(core.class.Class), core.string.Class) - @Index(IndexKind.Indexed) - ofClass!: Ref> - - components!: Record + ofClass!: Ref> + components!: Record }> } @Model(activity.class.ActivityMessagesFilter, core.class.Doc, DOMAIN_MODEL) diff --git a/models/analytics-collector/src/index.ts b/models/analytics-collector/src/index.ts index aab49c3d8a..e8e0dc38cd 100644 --- a/models/analytics-collector/src/index.ts +++ b/models/analytics-collector/src/index.ts @@ -85,7 +85,7 @@ export function createModel (builder: Builder): void { builder.createDoc(activity.class.ActivityExtension, core.space.Model, { ofClass: analyticsCollector.class.OnboardingChannel, - components: { input: chunter.component.ChatMessageInput } + components: { input: { component: chunter.component.ChatMessageInput } } }) builder.createDoc>( diff --git a/models/chunter/src/index.ts b/models/chunter/src/index.ts index d714ebcee3..2f3c441015 100644 --- a/models/chunter/src/index.ts +++ b/models/chunter/src/index.ts @@ -273,27 +273,27 @@ export function createModel (builder: Builder): void { builder.createDoc(activity.class.ActivityExtension, core.space.Model, { ofClass: chunter.class.Channel, - components: { input: chunter.component.ChatMessageInput } + components: { input: { component: chunter.component.ChatMessageInput } } }) builder.createDoc(activity.class.ActivityExtension, core.space.Model, { ofClass: chunter.class.DirectMessage, - components: { input: chunter.component.ChatMessageInput } + components: { input: { component: chunter.component.ChatMessageInput } } }) builder.createDoc(activity.class.ActivityExtension, core.space.Model, { ofClass: activity.class.DocUpdateMessage, - components: { input: chunter.component.ChatMessageInput } + components: { input: { component: chunter.component.ChatMessageInput } } }) builder.createDoc(activity.class.ActivityExtension, core.space.Model, { ofClass: chunter.class.ChatMessage, - components: { input: chunter.component.ChatMessageInput } + components: { input: { component: chunter.component.ChatMessageInput } } }) builder.createDoc(activity.class.ActivityExtension, core.space.Model, { ofClass: activity.class.ActivityReference, - components: { input: chunter.component.ChatMessageInput } + components: { input: { component: chunter.component.ChatMessageInput } } }) // Indexing diff --git a/models/contact/src/index.ts b/models/contact/src/index.ts index 08e06f05d8..e7a597a31a 100644 --- a/models/contact/src/index.ts +++ b/models/contact/src/index.ts @@ -263,22 +263,22 @@ export function createModel (builder: Builder): void { builder.createDoc(activity.class.ActivityExtension, core.space.Model, { ofClass: contact.class.Contact, - components: { input: chunter.component.ChatMessageInput } + components: { input: { component: chunter.component.ChatMessageInput } } }) builder.createDoc(activity.class.ActivityExtension, core.space.Model, { ofClass: contact.class.Person, - components: { input: chunter.component.ChatMessageInput } + components: { input: { component: chunter.component.ChatMessageInput } } }) builder.createDoc(activity.class.ActivityExtension, core.space.Model, { ofClass: contact.class.Organization, - components: { input: chunter.component.ChatMessageInput } + components: { input: { component: chunter.component.ChatMessageInput } } }) builder.createDoc(activity.class.ActivityExtension, core.space.Model, { ofClass: contact.class.Member, - components: { input: chunter.component.ChatMessageInput } + components: { input: { component: chunter.component.ChatMessageInput } } }) builder.mixin(contact.mixin.Employee, core.class.Class, view.mixin.ObjectFactory, { diff --git a/models/controlled-documents/src/index.ts b/models/controlled-documents/src/index.ts index 9e14cf94f9..20231c6ce5 100644 --- a/models/controlled-documents/src/index.ts +++ b/models/controlled-documents/src/index.ts @@ -514,7 +514,7 @@ export function createModel (builder: Builder): void { builder.createDoc(activity.class.ActivityExtension, core.space.Model, { ofClass: documents.class.DocumentCategory, - components: { input: chunter.component.ChatMessageInput } + components: { input: { component: chunter.component.ChatMessageInput } } }) builder.mixin(documents.class.DocumentCategory, core.class.Class, view.mixin.ObjectPresenter, { @@ -768,7 +768,7 @@ export function defineNotifications (builder: Builder): void { builder.createDoc(activity.class.ActivityExtension, core.space.Model, { ofClass: documents.class.DocumentComment, - components: { input: chunter.component.ChatMessageInput } + components: { input: { component: chunter.component.ChatMessageInput } } }) builder.mixin(documents.class.ControlledDocument, core.class.Class, notification.mixin.ClassCollaborators, { diff --git a/models/core/src/index.ts b/models/core/src/index.ts index ef1b6c943a..26445f9f9d 100644 --- a/models/core/src/index.ts +++ b/models/core/src/index.ts @@ -24,12 +24,7 @@ import { DOMAIN_STATUS, DOMAIN_TRANSIENT, DOMAIN_TX, - systemAccountEmail, - type AttachedDoc, - type Class, - type Doc, - type IndexingConfiguration, - type TxCollectionCUD + systemAccountEmail } from '@hcengineering/core' import { type Builder } from '@hcengineering/model' import { TBenchmarkDoc } from './benchmark' @@ -91,17 +86,7 @@ import { import { defineSpaceType } from './spaceType' import { TDomainStatusPlaceholder, TStatus, TStatusCategory } from './status' import { TUserStatus } from './transient' -import { - TTx, - TTxApplyIf, - TTxCollectionCUD, - TTxCreateDoc, - TTxCUD, - TTxMixin, - TTxRemoveDoc, - TTxUpdateDoc, - TTxWorkspaceEvent -} from './tx' +import { TTx, TTxApplyIf, TTxCreateDoc, TTxCUD, TTxMixin, TTxRemoveDoc, TTxUpdateDoc, TTxWorkspaceEvent } from './tx' export { coreId, DOMAIN_SPACE } from '@hcengineering/core' export * from './core' @@ -122,7 +107,6 @@ export function createModel (builder: Builder): void { TTxCUD, TTxCreateDoc, TAttachedDoc, - TTxCollectionCUD, TTxMixin, TTxUpdateDoc, TTxRemoveDoc, @@ -188,14 +172,6 @@ export function createModel (builder: Builder): void { core.account.System ) - builder.mixin>, IndexingConfiguration>>( - core.class.TxCollectionCUD, - core.class.Class, - core.mixin.IndexConfiguration, - { - indexes: ['tx.objectId', 'tx.operations.attachedTo'] - } - ) builder.createDoc(core.class.DomainIndexConfiguration, core.space.Model, { domain: DOMAIN_TX, disabled: [ diff --git a/models/core/src/migration.ts b/models/core/src/migration.ts index a692f66ab1..6760afb816 100644 --- a/models/core/src/migration.ts +++ b/models/core/src/migration.ts @@ -15,22 +15,26 @@ import { saveCollaborativeDoc } from '@hcengineering/collaboration' import core, { + collaborativeDocParse, + coreId, + DOMAIN_MODEL_TX, DOMAIN_SPACE, DOMAIN_STATUS, DOMAIN_TX, - MeasureMetricsContext, - RateLimiter, - collaborativeDocParse, - coreId, generateId, makeCollaborativeDoc, + MeasureMetricsContext, + RateLimiter, type AnyAttribute, + type Class, type Doc, type Domain, type MeasureContext, + type Ref, type Space, type Status, - type TxCreateDoc + type TxCreateDoc, + type TxCUD } from '@hcengineering/core' import { createDefaultSpace, @@ -285,6 +289,67 @@ export const coreOperation: MigrateOperation = { await client.update(DOMAIN_TX, { '%hash%': { $exists: true } }, { $set: { '%hash%': null } }) await client.update(DOMAIN_SPACE, { '%hash%': { $exists: true } }, { $set: { '%hash%': null } }) } + }, + { + state: 'remove-github-patches', + func: async (client) => { + await client.update( + DOMAIN_TX, + { + objectClass: 'tracker:class:Issue', + collection: 'pullRequests', + 'tx.attributes.patch': { $exists: true } + }, + { $unset: { 'tx.attributes.patch': 1 } } + ) + } + }, + { + state: 'remove-collection-txes', + func: async (client) => { + let processed = 0 + const iterator = await client.traverse>(DOMAIN_TX, { + _class: 'core:class:TxCollectionCUD' as Ref> + }) + while (true) { + const txes = await iterator.next(1000) + if (txes === null || txes.length === 0) break + processed += txes.length + try { + await client.deleteMany(DOMAIN_TX, { + _id: { $in: txes.map((it) => it._id) } + }) + await client.create( + DOMAIN_TX, + txes.map((tx) => { + const { collection, objectId, objectClass } = tx + return { + collection, + attachedTo: objectId, + attachedToClass: objectClass, + ...(tx as any).tx + } + }) + ) + } catch (err: any) { + console.error(err) + } + console.log('processed', processed) + } + await iterator.close() + } + }, + { + state: 'move-model-txes', + func: async (client) => { + await client.move( + DOMAIN_TX, + { + objectSpace: core.space.Model + }, + DOMAIN_MODEL_TX + ) + } } ]) }, diff --git a/models/core/src/tx.ts b/models/core/src/tx.ts index c12cfd1e09..f688761ecf 100644 --- a/models/core/src/tx.ts +++ b/models/core/src/tx.ts @@ -14,7 +14,6 @@ // import { - type AttachedDoc, type Class, type Data, type Doc, @@ -28,7 +27,6 @@ import { type Space, type Tx, type TxApplyIf, - type TxCollectionCUD, type TxCreateDoc, type TxCUD, type TxMixin, @@ -72,12 +70,6 @@ export class TTxCreateDoc extends TTxCUD implements TxCreateDo attributes!: Data } -@Model(core.class.TxCollectionCUD, core.class.TxCUD) -export class TTxCollectionCUD extends TTxCUD implements TxCollectionCUD { - collection!: string - tx!: TxCUD

-} - @Model(core.class.TxMixin, core.class.TxCUD) export class TTxMixin extends TTxCUD implements TxMixin { mixin!: Ref> diff --git a/models/document/src/index.ts b/models/document/src/index.ts index e06e6dbbc3..cacdf4c235 100644 --- a/models/document/src/index.ts +++ b/models/document/src/index.ts @@ -492,7 +492,7 @@ function defineDocument (builder: Builder): void { builder.createDoc(activity.class.ActivityExtension, core.space.Model, { ofClass: document.class.Document, - components: { input: chunter.component.ChatMessageInput } + components: { input: { component: chunter.component.ChatMessageInput } } }) // Search diff --git a/models/document/src/migration.ts b/models/document/src/migration.ts index 530ea6d631..e05caea6f9 100644 --- a/models/document/src/migration.ts +++ b/models/document/src/migration.ts @@ -13,11 +13,11 @@ // limitations under the License. // -import { type CollaborativeDoc, DOMAIN_TX, MeasureMetricsContext, SortingOrder } from '@hcengineering/core' -import { type DocumentSnapshot, type Document, type Teamspace } from '@hcengineering/document' +import { DOMAIN_MODEL_TX, MeasureMetricsContext, SortingOrder, type CollaborativeDoc } from '@hcengineering/core' +import { type Document, type DocumentSnapshot, type Teamspace } from '@hcengineering/document' import { - tryMigrate, migrateSpaceRanks, + tryMigrate, type MigrateOperation, type MigrateUpdate, type MigrationClient, @@ -75,7 +75,7 @@ async function migrateTeamspacesMixins (client: MigrationClient): Promise const newSpaceTypeMixin = document.mixin.DefaultTeamspaceTypeData await client.update( - DOMAIN_TX, + DOMAIN_MODEL_TX, { objectClass: core.class.Attribute, 'attributes.attributeOf': oldSpaceTypeMixin diff --git a/models/drive/src/index.ts b/models/drive/src/index.ts index c262ce8005..6b4b762f38 100644 --- a/models/drive/src/index.ts +++ b/models/drive/src/index.ts @@ -595,7 +595,7 @@ function defineFile (builder: Builder): void { builder.createDoc(activity.class.ActivityExtension, core.space.Model, { ofClass: drive.class.File, - components: { input: chunter.component.ChatMessageInput } + components: { input: { component: chunter.component.ChatMessageInput } } }) // Search diff --git a/models/guest/src/migration.ts b/models/guest/src/migration.ts index 12e08146aa..17e8e31380 100644 --- a/models/guest/src/migration.ts +++ b/models/guest/src/migration.ts @@ -1,9 +1,9 @@ import { AccountRole, - DOMAIN_TX, + DOMAIN_MODEL_TX, + type Account, type Ref, type Space, - type Account, type TxCreateDoc, type TxUpdateDoc } from '@hcengineering/core' @@ -30,13 +30,13 @@ export const guestOperation: MigrateOperation = { 1: AccountRole.Maintainer, 2: AccountRole.Owner } - const createTxes = await client.find>(DOMAIN_TX, { + const createTxes = await client.find>(DOMAIN_MODEL_TX, { _class: core.class.TxCreateDoc, 'attributes.role': { $in: [0, 1, 2] } }) for (const tx of createTxes) { await client.update( - DOMAIN_TX, + DOMAIN_MODEL_TX, { _id: tx._id }, @@ -47,13 +47,13 @@ export const guestOperation: MigrateOperation = { } ) } - const updateTxes = await client.find>(DOMAIN_TX, { + const updateTxes = await client.find>(DOMAIN_MODEL_TX, { _class: core.class.TxUpdateDoc, 'operations.role': { $in: [0, 1, 2] } }) for (const tx of updateTxes) { await client.update( - DOMAIN_TX, + DOMAIN_MODEL_TX, { _id: tx._id }, diff --git a/models/inventory/src/index.ts b/models/inventory/src/index.ts index a34655fe03..f5440ad734 100644 --- a/models/inventory/src/index.ts +++ b/models/inventory/src/index.ts @@ -85,17 +85,17 @@ export function createModel (builder: Builder): void { builder.createDoc(activity.class.ActivityExtension, core.space.Model, { ofClass: inventory.class.Product, - components: { input: chunter.component.ChatMessageInput } + components: { input: { component: chunter.component.ChatMessageInput } } }) builder.createDoc(activity.class.ActivityExtension, core.space.Model, { ofClass: inventory.class.Category, - components: { input: chunter.component.ChatMessageInput } + components: { input: { component: chunter.component.ChatMessageInput } } }) builder.createDoc(activity.class.ActivityExtension, core.space.Model, { ofClass: inventory.class.Variant, - components: { input: chunter.component.ChatMessageInput } + components: { input: { component: chunter.component.ChatMessageInput } } }) builder.mixin(inventory.class.Category, core.class.Class, view.mixin.ObjectPresenter, { diff --git a/models/lead/src/index.ts b/models/lead/src/index.ts index 2bb5bad1a0..cc8e668fa2 100644 --- a/models/lead/src/index.ts +++ b/models/lead/src/index.ts @@ -51,12 +51,12 @@ export function createModel (builder: Builder): void { builder.createDoc(activity.class.ActivityExtension, core.space.Model, { ofClass: lead.class.Lead, - components: { input: chunter.component.ChatMessageInput } + components: { input: { component: chunter.component.ChatMessageInput } } }) builder.createDoc(activity.class.ActivityExtension, core.space.Model, { ofClass: lead.class.Funnel, - components: { input: chunter.component.ChatMessageInput } + components: { input: { component: chunter.component.ChatMessageInput } } }) builder.mixin(lead.class.Funnel, core.class.Class, workbench.mixin.SpaceView, { diff --git a/models/lead/src/migration.ts b/models/lead/src/migration.ts index 59155e68b0..7f77fd49da 100644 --- a/models/lead/src/migration.ts +++ b/models/lead/src/migration.ts @@ -13,7 +13,14 @@ // limitations under the License. // -import { AccountRole, DOMAIN_TX, makeCollaborativeDoc, TxOperations, type Ref, type Status } from '@hcengineering/core' +import { + AccountRole, + DOMAIN_MODEL_TX, + makeCollaborativeDoc, + TxOperations, + type Ref, + type Status +} from '@hcengineering/core' import { leadId, type Lead } from '@hcengineering/lead' import { tryMigrate, @@ -26,7 +33,7 @@ import { import core, { DOMAIN_SPACE } from '@hcengineering/model-core' import contact, { DOMAIN_CONTACT } from '@hcengineering/model-contact' -import task, { DOMAIN_TASK, createSequence, migrateDefaultStatusesBase } from '@hcengineering/model-task' +import task, { createSequence, DOMAIN_TASK, migrateDefaultStatusesBase } from '@hcengineering/model-task' import lead from './plugin' import { defaultLeadStatuses } from './spaceType' @@ -110,7 +117,7 @@ async function migrateDefaultTypeMixins (client: MigrationClient): Promise const newTaskTypeMixin = lead.mixin.LeadTypeData await client.update( - DOMAIN_TX, + DOMAIN_MODEL_TX, { objectClass: core.class.Attribute, 'attributes.attributeOf': oldSpaceTypeMixin diff --git a/models/love/package.json b/models/love/package.json index de93ddff59..04ee30199e 100644 --- a/models/love/package.json +++ b/models/love/package.json @@ -28,8 +28,8 @@ "typescript": "^5.3.3" }, "dependencies": { - "@hcengineering/attachment": "^0.6.14", "@hcengineering/activity": "^0.6.0", + "@hcengineering/attachment": "^0.6.14", "@hcengineering/chunter": "^0.6.20", "@hcengineering/contact": "^0.6.24", "@hcengineering/core": "^0.6.32", @@ -46,6 +46,7 @@ "@hcengineering/notification": "^0.6.23", "@hcengineering/platform": "^0.6.11", "@hcengineering/setting": "^0.6.17", + "@hcengineering/time": "^0.6.0", "@hcengineering/ui": "^0.6.15", "@hcengineering/view": "^0.6.13", "@hcengineering/workbench": "^0.6.16" diff --git a/models/love/src/index.ts b/models/love/src/index.ts index c468e07fc8..8a8295404b 100644 --- a/models/love/src/index.ts +++ b/models/love/src/index.ts @@ -20,7 +20,10 @@ import { DOMAIN_TRANSIENT, IndexKind, type Ref, - type CollaborativeDoc + type CollaborativeDoc, + type Doc, + type Timestamp, + type CollectionSize } from '@hcengineering/core' import { type DevicesPreference, @@ -37,7 +40,8 @@ import { type RoomInfo, type RoomType, type RoomLanguage, - type MeetingMinutes + type MeetingMinutes, + type MeetingStatus } from '@hcengineering/love' import { type Builder, @@ -52,7 +56,9 @@ import { TypeCollaborativeDoc, TypeRef, TypeString, - UX + TypeTimestamp, + UX, + TypeAny } from '@hcengineering/model' import calendar, { TEvent } from '@hcengineering/model-calendar' import core, { TAttachedDoc, TDoc } from '@hcengineering/model-core' @@ -66,6 +72,7 @@ import workbench, { WidgetType } from '@hcengineering/workbench' import activity from '@hcengineering/activity' import chunter from '@hcengineering/chunter' import attachment from '@hcengineering/attachment' +import time, { type ToDo, type Todoable } from '@hcengineering/time' import love from './plugin' @@ -107,6 +114,9 @@ export class TRoom extends TDoc implements Room { @Prop(PropCollection(love.class.MeetingMinutes), love.string.MeetingMinutes) meetings?: number + + @Prop(PropCollection(chunter.class.ChatMessage), activity.string.Messages) + messages?: number } @Model(love.class.Office, love.class.Room) @@ -184,8 +194,13 @@ export class TMeeting extends TEvent implements Meeting { } @Model(love.class.MeetingMinutes, core.class.Doc, DOMAIN_MEETING_MINUTES) -@UX(love.string.MeetingMinutes, love.icon.Cam) -export class TMeetingMinutes extends TAttachedDoc implements MeetingMinutes { +@UX(love.string.MeetingMinutes, love.icon.Cam, undefined, 'createdOn', undefined, love.string.MeetingsMinutes) +export class TMeetingMinutes extends TAttachedDoc implements MeetingMinutes, Todoable { + @Prop(TypeRef(core.class.Doc), love.string.Room, { editor: love.component.MeetingMinutesDocEditor }) + @Index(IndexKind.Indexed) + @ReadOnly() + declare attachedTo: Ref + @Hidden() sid!: string @@ -197,6 +212,12 @@ export class TMeetingMinutes extends TAttachedDoc implements MeetingMinutes { @Index(IndexKind.FullText) description!: CollaborativeDoc + @Prop(TypeAny(love.component.MeetingMinutesStatusPresenter, love.string.Status), love.string.Status, { + editor: love.component.MeetingMinutesStatusPresenter + }) + @ReadOnly() + status!: MeetingStatus + @Prop(Collection(attachment.class.Attachment), attachment.string.Attachments, { shortLabel: attachment.string.Files }) attachments?: number @@ -205,6 +226,18 @@ export class TMeetingMinutes extends TAttachedDoc implements MeetingMinutes { @Prop(PropCollection(chunter.class.ChatMessage), activity.string.Messages) messages?: number + + @Prop(TypeTimestamp(), love.string.MeetingStart, { editor: view.component.TimestampPresenter }) + @ReadOnly() + @Index(IndexKind.IndexedDsc) + declare createdOn: Timestamp + + @Prop(TypeTimestamp(), love.string.MeetingEnd) + @ReadOnly() + meetingEnd?: Timestamp + + @Prop(Collection(time.class.ToDo), getEmbeddedLabel('Action Items')) + todos?: CollectionSize } export default love @@ -418,17 +451,17 @@ export function createModel (builder: Builder): void { builder.createDoc(activity.class.ActivityExtension, core.space.Model, { ofClass: love.class.Room, - components: { input: chunter.component.ChatMessageInput } + components: { input: { component: chunter.component.ChatMessageInput, props: { collection: 'messages' } } } }) builder.createDoc(activity.class.ActivityExtension, core.space.Model, { ofClass: love.class.Office, - components: { input: chunter.component.ChatMessageInput } + components: { input: { component: chunter.component.ChatMessageInput, props: { collection: 'messages' } } } }) builder.createDoc(activity.class.ActivityExtension, core.space.Model, { ofClass: love.class.MeetingMinutes, - components: { input: chunter.component.ChatMessageInput } + components: { input: { component: chunter.component.ChatMessageInput, props: { collection: 'messages' } } } }) builder.mixin(love.class.MeetingMinutes, core.class.Class, activity.mixin.ActivityDoc, {}) @@ -439,6 +472,10 @@ export function createModel (builder: Builder): void { presenter: love.component.MeetingMinutesPresenter }) + builder.mixin(love.class.Room, core.class.Class, view.mixin.ObjectPresenter, { + presenter: love.component.RoomPresenter + }) + builder.mixin(love.class.MeetingMinutes, core.class.Class, view.mixin.CollectionEditor, { editor: love.component.MeetingMinutesSection }) @@ -467,10 +504,11 @@ export function createModel (builder: Builder): void { descriptor: view.viewlet.Table, config: [ '', + { key: 'status', presenter: love.component.MeetingMinutesStatusPresenter, label: love.string.Status }, + 'createdOn', + 'meetingEnd', { key: 'messages', displayProps: { key: 'messages', suffix: true } }, - { key: 'transcription', displayProps: { key: 'transcription', suffix: true } }, - 'modifiedOn', - 'modifiedBy' + { key: 'transcription', displayProps: { key: 'transcription', suffix: true } } ], configOptions: { hiddenKeys: ['description'], @@ -481,6 +519,26 @@ export function createModel (builder: Builder): void { love.viewlet.TableMeetingMinutes ) + builder.createDoc( + view.class.Viewlet, + core.space.Model, + { + attachTo: love.class.MeetingMinutes, + descriptor: view.viewlet.Table, + config: [ + '', + { key: 'status', presenter: love.component.MeetingMinutesStatusPresenter, label: love.string.Status }, + 'createdOn', + 'meetingEnd' + ], + configOptions: { + sortable: true + }, + variant: 'embedded' + }, + love.viewlet.TableMeetingMinutesEmbedded + ) + builder.createDoc( view.class.ViewletDescriptor, core.space.Model, @@ -574,4 +632,8 @@ export function createModel (builder: Builder): void { indexes: [], searchDisabled: true }) + + builder.mixin(love.class.MeetingMinutes, core.class.Class, view.mixin.ObjectPanelFooter, { + editor: love.component.PanelControlBar + }) } diff --git a/models/love/src/migration.ts b/models/love/src/migration.ts index 779aa01052..916c9b96dc 100644 --- a/models/love/src/migration.ts +++ b/models/love/src/migration.ts @@ -16,7 +16,16 @@ import contact from '@hcengineering/contact' import { type Space, TxOperations, type Ref, makeCollaborativeDoc } from '@hcengineering/core' import drive from '@hcengineering/drive' -import { RoomAccess, RoomType, createDefaultRooms, isOffice, loveId, type Floor, type Room } from '@hcengineering/love' +import { + MeetingStatus, + RoomAccess, + RoomType, + createDefaultRooms, + isOffice, + loveId, + type Floor, + type Room +} from '@hcengineering/love' import { createDefaultSpace, migrateSpace, @@ -142,6 +151,16 @@ export const loveOperation: MigrateOperation = { } } } + }, + { + state: 'default-meeting-minutes-status', + func: async (client) => { + await client.update( + DOMAIN_MEETING_MINUTES, + { status: { $exists: false } }, + { status: MeetingStatus.Finished } + ) + } } ]) }, diff --git a/models/notification/src/migration.ts b/models/notification/src/migration.ts index 19bc06698e..56fe3affe2 100644 --- a/models/notification/src/migration.ts +++ b/models/notification/src/migration.ts @@ -13,6 +13,8 @@ // limitations under the License. // +import chunter from '@hcengineering/chunter' +import contact, { type PersonSpace } from '@hcengineering/contact' import core, { DOMAIN_TX, type Class, type Doc, type DocumentQuery, type Ref, type Space } from '@hcengineering/core' import { migrateSpace, @@ -28,11 +30,9 @@ import notification, { type InboxNotification } from '@hcengineering/notification' import { DOMAIN_PREFERENCE } from '@hcengineering/preference' -import contact, { type PersonSpace } from '@hcengineering/contact' -import chunter from '@hcengineering/chunter' -import { DOMAIN_DOC_NOTIFY, DOMAIN_NOTIFICATION, DOMAIN_USER_NOTIFY } from './index' import { DOMAIN_SPACE } from '@hcengineering/model-core' +import { DOMAIN_DOC_NOTIFY, DOMAIN_NOTIFICATION, DOMAIN_USER_NOTIFY } from './index' export async function removeNotifications ( client: MigrationClient, @@ -429,6 +429,12 @@ export const notificationOperation: MigrateOperation = { objectClass: notification.class.BrowserNotification }) } + }, + { + state: 'migrate-dnc-space', + func: async (client) => { + await client.update(DOMAIN_DOC_NOTIFY, { space: core.space.Space }, { space: core.space.Workspace }) + } } ]) }, diff --git a/models/products/src/index.ts b/models/products/src/index.ts index 92946b8c6f..941c871b0e 100644 --- a/models/products/src/index.ts +++ b/models/products/src/index.ts @@ -159,7 +159,7 @@ function defineProduct (builder: Builder): void { builder.createDoc(activity.class.ActivityExtension, core.space.Model, { ofClass: products.class.Product, - components: { input: chunter.component.ChatMessageInput } + components: { input: { component: chunter.component.ChatMessageInput } } }) builder.mixin(products.class.Product, core.class.Class, view.mixin.ObjectEditor, { @@ -299,7 +299,7 @@ function defineProductVersion (builder: Builder): void { builder.createDoc(activity.class.ActivityExtension, core.space.Model, { ofClass: products.class.ProductVersion, - components: { input: chunter.component.ChatMessageInput } + components: { input: { component: chunter.component.ChatMessageInput } } }) builder.mixin(products.class.ProductVersion, core.class.Class, view.mixin.ObjectEditor, { diff --git a/models/recruit/src/index.ts b/models/recruit/src/index.ts index fe4b43e16b..6b8e3f5dfd 100644 --- a/models/recruit/src/index.ts +++ b/models/recruit/src/index.ts @@ -56,17 +56,17 @@ export function createModel (builder: Builder): void { builder.createDoc(activity.class.ActivityExtension, core.space.Model, { ofClass: recruit.class.Vacancy, - components: { input: chunter.component.ChatMessageInput } + components: { input: { component: chunter.component.ChatMessageInput } } }) builder.createDoc(activity.class.ActivityExtension, core.space.Model, { ofClass: recruit.class.Applicant, - components: { input: chunter.component.ChatMessageInput } + components: { input: { component: chunter.component.ChatMessageInput } } }) builder.createDoc(activity.class.ActivityExtension, core.space.Model, { ofClass: recruit.class.Review, - components: { input: chunter.component.ChatMessageInput } + components: { input: { component: chunter.component.ChatMessageInput } } }) builder.mixin(recruit.class.Vacancy, core.class.Class, workbench.mixin.SpaceView, { diff --git a/models/recruit/src/migration.ts b/models/recruit/src/migration.ts index 87313df3db..21c5c15633 100644 --- a/models/recruit/src/migration.ts +++ b/models/recruit/src/migration.ts @@ -15,7 +15,7 @@ import { getCategories } from '@anticrm/skillset' import core, { - DOMAIN_TX, + DOMAIN_MODEL_TX, toIdMap, TxOperations, type Doc, @@ -149,7 +149,7 @@ async function migrateDefaultTypeMixins (client: MigrationClient): Promise const newTaskTypeMixin = recruit.mixin.ApplicantTypeData await client.update( - DOMAIN_TX, + DOMAIN_MODEL_TX, { objectClass: core.class.Attribute, 'attributes.attributeOf': oldSpaceTypeMixin diff --git a/models/recruit/src/types.ts b/models/recruit/src/types.ts index c33b39cc64..c6d390be15 100644 --- a/models/recruit/src/types.ts +++ b/models/recruit/src/types.ts @@ -18,7 +18,7 @@ import { Account, IndexKind, type CollaborativeDoc, - type Collection as Array, + type Collection, type Domain, type Markup, type Ref, @@ -28,7 +28,7 @@ import { type Timestamp } from '@hcengineering/core' import { - Collection, + Collection as TypeCollection, Hidden, Index, Mixin, @@ -70,7 +70,9 @@ export class TVacancy extends TProject implements Vacancy { @Index(IndexKind.FullText) fullDescription!: CollaborativeDoc - @Prop(Collection(attachment.class.Attachment), attachment.string.Attachments, { shortLabel: attachment.string.Files }) + @Prop(TypeCollection(attachment.class.Attachment), attachment.string.Attachments, { + shortLabel: attachment.string.Files + }) attachments?: number @Prop(TypeDate(), recruit.string.Due, recruit.icon.Calendar) @@ -83,13 +85,16 @@ export class TVacancy extends TProject implements Vacancy { @Prop(TypeRef(contact.class.Organization), recruit.string.Company, { icon: contact.icon.Company }) company?: Ref - @Prop(Collection(chunter.class.ChatMessage), chunter.string.Comments) + @Prop(TypeCollection(chunter.class.ChatMessage), chunter.string.Comments) comments?: number @Prop(TypeString(), recruit.string.Vacancy) @Index(IndexKind.FullText) @Hidden() number!: number + + @Prop(TypeCollection(survey.class.Poll), survey.string.Polls) + polls?: Collection } @Mixin(recruit.mixin.Candidate, contact.class.Person) @@ -99,7 +104,7 @@ export class TCandidate extends TPerson implements Candidate { @Index(IndexKind.FullText) title?: string - @Prop(Collection(recruit.class.Applicant), recruit.string.Applications, { + @Prop(TypeCollection(recruit.class.Applicant), recruit.string.Applications, { shortLabel: recruit.string.ApplicationsShort }) applications?: number @@ -114,26 +119,29 @@ export class TCandidate extends TPerson implements Candidate { @Index(IndexKind.FullText) source?: string - @Prop(Collection(tags.class.TagReference, recruit.string.SkillLabel), recruit.string.SkillsLabel, { + @Prop(TypeCollection(tags.class.TagReference, recruit.string.SkillLabel), recruit.string.SkillsLabel, { icon: recruit.icon.Skills, schema: '3' }) skills?: number - @Prop(Collection(recruit.class.Review, recruit.string.Review), recruit.string.Reviews) + @Prop(TypeCollection(recruit.class.Review, recruit.string.Review), recruit.string.Reviews) reviews?: number @Prop( - Collection(recruit.class.ApplicantMatch, getEmbeddedLabel('Vacancy match')), + TypeCollection(recruit.class.ApplicantMatch, getEmbeddedLabel('Vacancy match')), getEmbeddedLabel('Vacancy Matches') ) vacancyMatch?: number + + @Prop(TypeCollection(survey.class.Poll), survey.string.Polls) + polls?: Collection } @Mixin(recruit.mixin.VacancyList, contact.class.Organization) @UX(recruit.string.VacancyList, recruit.icon.RecruitApplication, 'CM', 'name') export class TVacancyList extends TOrganization implements VacancyList { - @Prop(Collection(recruit.class.Vacancy), recruit.string.Vacancies) + @Prop(TypeCollection(recruit.class.Vacancy), recruit.string.Vacancies) vacancies!: number } @@ -162,8 +170,8 @@ export class TApplicant extends TTask implements Applicant { @Index(IndexKind.Indexed) declare status: Ref - @Prop(Collection(survey.class.Poll), survey.string.Polls) - polls?: Array + @Prop(TypeCollection(survey.class.Poll), survey.string.Polls) + polls?: Collection } @Model(recruit.class.ApplicantMatch, core.class.AttachedDoc, DOMAIN_TASK) @@ -211,7 +219,7 @@ export class TReview extends TEvent implements Review { @Prop(TypeRef(contact.class.Organization), recruit.string.Company, { icon: contact.icon.Company }) company?: Ref - @Prop(Collection(recruit.class.Opinion), recruit.string.Opinions) + @Prop(TypeCollection(recruit.class.Opinion), recruit.string.Opinions) opinions?: number } @@ -225,10 +233,12 @@ export class TOpinion extends TAttachedDoc implements Opinion { @Prop(TypeRef(recruit.class.Review), recruit.string.Review) declare attachedTo: Ref - @Prop(Collection(attachment.class.Attachment), attachment.string.Attachments, { shortLabel: attachment.string.Files }) + @Prop(TypeCollection(attachment.class.Attachment), attachment.string.Attachments, { + shortLabel: attachment.string.Files + }) attachments?: number - @Prop(Collection(chunter.class.ChatMessage), chunter.string.Comments) + @Prop(TypeCollection(chunter.class.ChatMessage), chunter.string.Comments) comments?: number @Prop(TypeMarkup(), recruit.string.Description) diff --git a/models/request/src/migration.ts b/models/request/src/migration.ts index f7fbda4185..7ff603449a 100644 --- a/models/request/src/migration.ts +++ b/models/request/src/migration.ts @@ -12,18 +12,18 @@ // See the License for the specific language governing permissions and // limitations under the License. // -import core, { DOMAIN_TX, type Ref, type TxCreateDoc } from '@hcengineering/core' -import request, { requestId, type Request } from '@hcengineering/request' +import contact, { type Person, type PersonAccount } from '@hcengineering/contact' +import core, { DOMAIN_MODEL_TX, type Ref, type TxCreateDoc } from '@hcengineering/core' import { - type MigrateUpdate, - type MigrationDocumentQuery, tryMigrate, type MigrateOperation, + type MigrateUpdate, type MigrationClient, + type MigrationDocumentQuery, type MigrationUpgradeClient, type ModelLogger } from '@hcengineering/model' -import contact, { type Person, type PersonAccount } from '@hcengineering/contact' +import request, { requestId, type Request } from '@hcengineering/request' import { DOMAIN_REQUEST } from '.' @@ -32,7 +32,7 @@ async function migrateRequestPersonAccounts (client: MigrationClient): Promise(DOMAIN_REQUEST, { _class: { $in: descendants } }) - const personAccountsCreateTxes = await client.find(DOMAIN_TX, { + const personAccountsCreateTxes = await client.find(DOMAIN_MODEL_TX, { _class: core.class.TxCreateDoc, objectClass: contact.class.PersonAccount }) diff --git a/models/server-activity/src/index.ts b/models/server-activity/src/index.ts index 5d4820a247..c48dec6f71 100644 --- a/models/server-activity/src/index.ts +++ b/models/server-activity/src/index.ts @@ -36,14 +36,16 @@ export function createModel (builder: Builder): void { builder.createDoc(serverCore.class.Trigger, core.space.Model, { trigger: serverActivity.trigger.OnReactionChanged, txMatch: { - collection: 'reactions', - _class: core.class.TxCollectionCUD + collection: 'reactions' }, isAsync: true }) builder.createDoc(serverCore.class.Trigger, core.space.Model, { trigger: serverActivity.trigger.ActivityMessagesHandler, + txMatch: { + objectClass: { $nin: [activity.class.ActivityMessage, notification.class.DocNotifyContext] } + }, isAsync: true }) @@ -54,8 +56,8 @@ export function createModel (builder: Builder): void { builder.createDoc(serverCore.class.Trigger, core.space.Model, { trigger: serverActivity.trigger.ReferenceTrigger, txMatch: { - 'tx.objectClass': { $ne: activity.class.ActivityReference }, - objectClass: { + objectClass: { $ne: activity.class.ActivityReference }, + attachedToClass: { $nin: [ notification.class.InboxNotification, notification.class.DocNotifyContext, diff --git a/models/server-activity/src/migration.ts b/models/server-activity/src/migration.ts index f570bde1c9..24f1b44ac4 100644 --- a/models/server-activity/src/migration.ts +++ b/models/server-activity/src/migration.ts @@ -18,16 +18,13 @@ import core, { MeasureMetricsContext, SortingOrder, TxFactory, - TxProcessor, toFindResult, toIdMap, - type AttachedDoc, type Class, type Doc, type Ref, type Tx, type TxCUD, - type TxCollectionCUD, type TxCreateDoc } from '@hcengineering/core' import { @@ -85,12 +82,11 @@ async function generateDocUpdateMessageByTx ( tx, control, undefined, - undefined, objectCache ) for (const collectionTx of createCollectionCUDTxes) { - const createTx = collectionTx.tx as TxCreateDoc + const createTx = collectionTx as TxCreateDoc const domain = client.hierarchy.getDomain(createTx.objectClass) await client.create(domain, { @@ -152,15 +148,14 @@ async function createDocUpdateMessages (client: MigrationClient): Promise continue } - if (v._class === core.class.TxCollectionCUD) { + if (v.attachedToClass !== undefined && v.attachedTo !== undefined) { try { - const vcol = v as TxCollectionCUD - const _cl = client.hierarchy.getBaseClass(vcol.tx.objectClass) + const _cl = client.hierarchy.getBaseClass(v.attachedToClass) const s = byClass.get(_cl) ?? new Set() - s.add(vcol.tx.objectId) + s.add(v.attachedTo) byClass.set(_cl, s) } catch { - const objClass = (v as TxCollectionCUD).tx.objectClass + const objClass = v.attachedToClass const has = classNotFound.has(objClass) if (!has) { classNotFound.add(objClass) @@ -198,8 +193,7 @@ async function createDocUpdateMessages (client: MigrationClient): Promise } const transactions = allTransactions.get(d._id) ?? [] for (const tx of transactions) { - const innerTx = TxProcessor.extractTx(tx) as TxCUD - txIds.add(innerTx._id) + txIds.add(tx._id) } } @@ -220,10 +214,8 @@ async function createDocUpdateMessages (client: MigrationClient): Promise } const transactions = allTransactions.get(d._id) ?? [] for (const tx of transactions) { - const innerTx = TxProcessor.extractTx(tx) as TxCUD - - if (!client.hierarchy.hasClass(innerTx.objectClass)) { - const objClass = innerTx.objectClass + if (!client.hierarchy.hasClass(tx.objectClass)) { + const objClass = tx.objectClass const has = classNotFound.has(objClass) if (!has) { classNotFound.add(objClass) diff --git a/models/server-attachment/src/index.ts b/models/server-attachment/src/index.ts index ffabc76d7e..699e983fad 100644 --- a/models/server-attachment/src/index.ts +++ b/models/server-attachment/src/index.ts @@ -27,9 +27,8 @@ export function createModel (builder: Builder): void { builder.createDoc(serverCore.class.Trigger, core.space.Model, { trigger: serverAttachment.trigger.OnAttachmentDelete, txMatch: { - _class: core.class.TxCollectionCUD, - 'tx.objectClass': attachment.class.Attachment, - 'tx._class': core.class.TxRemoveDoc + _class: core.class.TxRemoveDoc, + objectClass: attachment.class.Attachment } }) } diff --git a/models/server-calendar/src/index.ts b/models/server-calendar/src/index.ts index 13d285e317..06567046db 100644 --- a/models/server-calendar/src/index.ts +++ b/models/server-calendar/src/index.ts @@ -44,7 +44,7 @@ export function createModel (builder: Builder): void { builder.createDoc(serverCore.class.Trigger, core.space.Model, { trigger: serverCalendar.trigger.OnEvent, txMatch: { - 'tx.objectClass': calendar.class.Event + objectClass: calendar.class.Event } }) diff --git a/models/server-chunter/src/index.ts b/models/server-chunter/src/index.ts index 5887285e75..4f63c95de9 100644 --- a/models/server-chunter/src/index.ts +++ b/models/server-chunter/src/index.ts @@ -78,18 +78,16 @@ export function createModel (builder: Builder): void { builder.createDoc(serverCore.class.Trigger, core.space.Model, { trigger: serverChunter.trigger.OnChatMessageRemoved, txMatch: { - _class: core.class.TxCollectionCUD, - 'tx._class': core.class.TxRemoveDoc, - 'tx.objectClass': chunter.class.ChatMessage + _class: core.class.TxRemoveDoc, + objectClass: chunter.class.ChatMessage } }) builder.createDoc(serverCore.class.Trigger, core.space.Model, { trigger: serverChunter.trigger.ChatNotificationsHandler, txMatch: { - _class: core.class.TxCollectionCUD, - 'tx._class': core.class.TxCreateDoc, - 'tx.objectClass': chunter.class.ChatMessage + _class: core.class.TxCreateDoc, + objectClass: chunter.class.ChatMessage }, isAsync: true }) diff --git a/models/server-collaboration/src/index.ts b/models/server-collaboration/src/index.ts index 30e6796737..bbccadf756 100644 --- a/models/server-collaboration/src/index.ts +++ b/models/server-collaboration/src/index.ts @@ -23,6 +23,9 @@ export { serverCollaborationId } from '@hcengineering/server-collaboration' export function createModel (builder: Builder): void { builder.createDoc(serverCore.class.Trigger, core.space.Model, { - trigger: serverCollaboration.trigger.OnDelete + trigger: serverCollaboration.trigger.OnDelete, + txMatch: { + _class: core.class.TxRemoveDoc + } }) } diff --git a/models/server-controlled-documents/src/index.ts b/models/server-controlled-documents/src/index.ts index 1bd4370205..535e3a0b4f 100644 --- a/models/server-controlled-documents/src/index.ts +++ b/models/server-controlled-documents/src/index.ts @@ -33,11 +33,10 @@ export function createModel (builder: Builder): void { builder.createDoc(serverCore.class.Trigger, core.space.Model, { trigger: serverDocuments.trigger.OnDocApprovalRequestApproved, txMatch: { - _class: core.class.TxCollectionCUD, - objectClass: documents.class.ControlledDocument, - 'tx._class': core.class.TxUpdateDoc, - 'tx.objectClass': documents.class.DocumentApprovalRequest, - 'tx.operations.status': RequestStatus.Completed + _class: core.class.TxUpdateDoc, + attachedToClass: documents.class.ControlledDocument, + objectClass: documents.class.DocumentApprovalRequest, + 'operations.status': RequestStatus.Completed } }) diff --git a/models/server-gmail/src/index.ts b/models/server-gmail/src/index.ts index 3715b8f060..d1837e989c 100644 --- a/models/server-gmail/src/index.ts +++ b/models/server-gmail/src/index.ts @@ -42,9 +42,8 @@ export function createModel (builder: Builder): void { builder.createDoc(serverCore.class.Trigger, core.space.Model, { trigger: serverGmail.trigger.OnMessageCreate, txMatch: { - _class: core.class.TxCollectionCUD, - 'tx.objectClass': gmail.class.Message, - 'tx._class': core.class.TxCreateDoc + _class: core.class.TxCreateDoc, + objectClass: gmail.class.Message } }) diff --git a/models/server-hr/src/index.ts b/models/server-hr/src/index.ts index da31dc0808..5370433f12 100644 --- a/models/server-hr/src/index.ts +++ b/models/server-hr/src/index.ts @@ -44,27 +44,24 @@ export function createModel (builder: Builder): void { builder.createDoc(serverCore.class.Trigger, core.space.Model, { trigger: serverHr.trigger.OnRequestCreate, txMatch: { - _class: core.class.TxCollectionCUD, - 'tx.objectClass': hr.class.Request, - 'tx._class': core.class.TxCreateDoc + _class: core.class.TxCreateDoc, + objectClass: hr.class.Request } }) builder.createDoc(serverCore.class.Trigger, core.space.Model, { trigger: serverHr.trigger.OnRequestUpdate, txMatch: { - _class: core.class.TxCollectionCUD, - 'tx.objectClass': hr.class.Request, - 'tx._class': core.class.TxUpdateDoc + _class: core.class.TxUpdateDoc, + objectClass: hr.class.Request } }) builder.createDoc(serverCore.class.Trigger, core.space.Model, { trigger: serverHr.trigger.OnRequestRemove, txMatch: { - _class: core.class.TxCollectionCUD, - 'tx.objectClass': hr.class.Request, - 'tx._class': core.class.TxRemoveDoc + _class: core.class.TxRemoveDoc, + objectClass: hr.class.Request } }) @@ -88,9 +85,8 @@ export function createModel (builder: Builder): void { builder.createDoc(serverCore.class.Trigger, core.space.Model, { trigger: serverHr.trigger.OnPublicHolidayCreate, txMatch: { - _class: core.class.TxCollectionCUD, - 'tx.objectClass': hr.class.PublicHoliday, - 'tx._class': core.class.TxCreateDoc + _class: core.class.TxCreateDoc, + objectClass: hr.class.PublicHoliday } }) diff --git a/models/server-notification/src/index.ts b/models/server-notification/src/index.ts index 7a84045db4..02d29ebba8 100644 --- a/models/server-notification/src/index.ts +++ b/models/server-notification/src/index.ts @@ -89,7 +89,10 @@ export function createModel (builder: Builder): void { }) builder.createDoc(serverCore.class.Trigger, core.space.Model, { - trigger: serverNotification.trigger.OnDocRemove + trigger: serverNotification.trigger.OnDocRemove, + txMatch: { + _class: core.class.TxRemoveDoc + } }) builder.createDoc(serverCore.class.Trigger, core.space.Model, { diff --git a/models/server-request/src/index.ts b/models/server-request/src/index.ts index 007cceb17d..57bf3580f8 100644 --- a/models/server-request/src/index.ts +++ b/models/server-request/src/index.ts @@ -25,7 +25,10 @@ export { serverRequestId } from '@hcengineering/server-request' export function createModel (builder: Builder): void { builder.createDoc(serverCore.class.Trigger, core.space.Model, { - trigger: serverRequest.trigger.OnRequest + trigger: serverRequest.trigger.OnRequest, + txMatch: { + objectClass: request.class.Request + } }) builder.mixin(request.class.Request, core.class.Class, serverNotification.mixin.TextPresenter, { diff --git a/models/server-setting/src/index.ts b/models/server-setting/src/index.ts index 8f0b5b3a34..403b10f9e8 100644 --- a/models/server-setting/src/index.ts +++ b/models/server-setting/src/index.ts @@ -68,11 +68,8 @@ export function createModel (builder: Builder): void { builder.createDoc(serverCore.class.Trigger, core.space.Model, { trigger: serverSetting.trigger.OnRoleNameUpdate, txMatch: { - _class: core.class.TxCollectionCUD, - objectSpace: core.space.Model, - collection: 'roles', - 'tx._class': core.class.TxUpdateDoc, - 'tx.objectClass': core.class.Role + _class: core.class.TxUpdateDoc, + objectClass: core.class.Role } }) } diff --git a/models/server-tags/src/index.ts b/models/server-tags/src/index.ts index 842b1d05d7..3d55219b8f 100644 --- a/models/server-tags/src/index.ts +++ b/models/server-tags/src/index.ts @@ -23,7 +23,11 @@ export { serverTagsId } from '@hcengineering/server-tags' export function createModel (builder: Builder): void { builder.createDoc(serverCore.class.Trigger, core.space.Model, { - trigger: serverTags.trigger.onTagReference + trigger: serverTags.trigger.onTagReference, + txMatch: { + _class: { $in: [core.class.TxCreateDoc, core.class.TxRemoveDoc] }, + objectClass: tags.class.TagReference + } }) builder.mixin, ObjectDDParticipant>( diff --git a/models/server-task/src/index.ts b/models/server-task/src/index.ts index 80e2227a88..bda31cf670 100644 --- a/models/server-task/src/index.ts +++ b/models/server-task/src/index.ts @@ -15,6 +15,7 @@ import core from '@hcengineering/core/src/component' import { type Builder } from '@hcengineering/model' +import task from '@hcengineering/task' import serverCore from '@hcengineering/server-core' import serverTask from '@hcengineering/server-task' @@ -22,6 +23,9 @@ export { serverTaskId } from '@hcengineering/server-task' export function createModel (builder: Builder): void { builder.createDoc(serverCore.class.Trigger, core.space.Model, { - trigger: serverTask.trigger.OnStateUpdate + trigger: serverTask.trigger.OnStateUpdate, + txMatch: { + objectClass: task.class.Task + } }) } diff --git a/models/server-telegram/src/index.ts b/models/server-telegram/src/index.ts index 07b58aa547..7ea53af712 100644 --- a/models/server-telegram/src/index.ts +++ b/models/server-telegram/src/index.ts @@ -45,9 +45,8 @@ export function createModel (builder: Builder): void { builder.createDoc(serverCore.class.Trigger, core.space.Model, { trigger: serverTelegram.trigger.OnMessageCreate, txMatch: { - _class: core.class.TxCollectionCUD, - 'tx.objectClass': telegram.class.Message, - 'tx._class': core.class.TxCreateDoc + _class: core.class.TxCreateDoc, + objectClass: telegram.class.Message } }) diff --git a/models/server-time/src/index.ts b/models/server-time/src/index.ts index 152eacf9f0..04298a2009 100644 --- a/models/server-time/src/index.ts +++ b/models/server-time/src/index.ts @@ -44,27 +44,24 @@ export function createModel (builder: Builder): void { builder.createDoc(serverCore.class.Trigger, core.space.Model, { trigger: serverTime.trigger.OnToDoUpdate, txMatch: { - _class: core.class.TxCollectionCUD, - 'tx.objectClass': time.class.ToDo, - 'tx._class': core.class.TxUpdateDoc + _class: core.class.TxUpdateDoc, + objectClass: time.class.ToDo } }) builder.createDoc(serverCore.class.Trigger, core.space.Model, { trigger: serverTime.trigger.OnToDoRemove, txMatch: { - _class: core.class.TxCollectionCUD, - 'tx.objectClass': time.class.ToDo, - 'tx._class': core.class.TxRemoveDoc + _class: core.class.TxRemoveDoc, + objectClass: time.class.ToDo } }) builder.createDoc(serverCore.class.Trigger, core.space.Model, { trigger: serverTime.trigger.OnToDoCreate, txMatch: { - _class: core.class.TxCollectionCUD, - 'tx.objectClass': time.class.ToDo, - 'tx._class': core.class.TxCreateDoc + _class: core.class.TxCreateDoc, + objectClass: time.class.ToDo }, isAsync: true }) @@ -72,18 +69,16 @@ export function createModel (builder: Builder): void { builder.createDoc(serverCore.class.Trigger, core.space.Model, { trigger: serverTime.trigger.OnWorkSlotCreate, txMatch: { - _class: core.class.TxCollectionCUD, - 'tx.objectClass': time.class.WorkSlot, - 'tx._class': core.class.TxCreateDoc + _class: core.class.TxCreateDoc, + objectClass: time.class.WorkSlot } }) builder.createDoc(serverCore.class.Trigger, core.space.Model, { trigger: serverTime.trigger.OnWorkSlotUpdate, txMatch: { - _class: core.class.TxCollectionCUD, - 'tx.objectClass': time.class.WorkSlot, - 'tx._class': core.class.TxUpdateDoc + _class: core.class.TxUpdateDoc, + objectClass: time.class.WorkSlot } }) diff --git a/models/survey/package.json b/models/survey/package.json index fa69730e8f..7363bd615d 100644 --- a/models/survey/package.json +++ b/models/survey/package.json @@ -38,6 +38,7 @@ "@hcengineering/platform": "^0.6.11", "@hcengineering/survey": "^0.6.0", "@hcengineering/survey-resources": "^0.6.0", - "@hcengineering/ui": "^0.6.15" + "@hcengineering/ui": "^0.6.15", + "@hcengineering/view": "^0.6.13" } } diff --git a/models/survey/src/index.ts b/models/survey/src/index.ts index 4db5607ef7..66dec5a80d 100644 --- a/models/survey/src/index.ts +++ b/models/survey/src/index.ts @@ -18,7 +18,7 @@ import { AccountRole } from '@hcengineering/core' import { type Builder } from '@hcengineering/model' import core from '@hcengineering/model-core' import chunter from '@hcengineering/model-chunter' -import view, { type Viewlet } from '@hcengineering/model-view' +import view, { createAction, type Viewlet } from '@hcengineering/model-view' import workbench from '@hcengineering/model-workbench' import { surveyId } from '@hcengineering/survey' import { TPoll, TSurvey } from './types' @@ -79,6 +79,14 @@ export function createModel (builder: Builder): void { survey.viewlet.TableSurvey ) + builder.mixin(survey.class.Survey, core.class.Class, view.mixin.ObjectTitle, { + titleProvider: survey.function.SurveyTitleProvider + }) + + builder.mixin(survey.class.Survey, core.class.Class, view.mixin.LinkProvider, { + encode: survey.function.GetSurveyLink + }) + builder.mixin(survey.class.Survey, core.class.Class, view.mixin.ObjectPanel, { component: survey.component.EditSurveyPanel }) @@ -91,7 +99,7 @@ export function createModel (builder: Builder): void { builder.createDoc(activity.class.ActivityExtension, core.space.Model, { ofClass: survey.class.Survey, - components: { input: chunter.component.ChatMessageInput } + components: { input: { component: chunter.component.ChatMessageInput } } }) builder.createDoc( @@ -100,20 +108,58 @@ export function createModel (builder: Builder): void { { attachTo: survey.class.Poll, descriptor: view.viewlet.Table, - config: ['', 'modifiedOn'], + config: ['', 'isCompleted', 'modifiedOn'], configOptions: { - hiddenKeys: ['name', 'survey', 'results'], + hiddenKeys: ['name', 'survey', 'questions'], sortable: true } }, survey.viewlet.TablePoll ) + builder.mixin(survey.class.Poll, core.class.Class, view.mixin.ObjectTitle, { + titleProvider: survey.function.PollTitleProvider + }) + + builder.mixin(survey.class.Poll, core.class.Class, view.mixin.LinkProvider, { + encode: survey.function.GetPollLink + }) + + builder.mixin(survey.class.Poll, core.class.Class, view.mixin.ObjectPanel, { + component: survey.component.EditPollPanel + }) + builder.mixin(survey.class.Poll, core.class.Class, view.mixin.ObjectPresenter, { presenter: survey.component.PollPresenter }) + builder.mixin(survey.class.Poll, core.class.Class, activity.mixin.ActivityDoc, {}) + + builder.createDoc(activity.class.ActivityExtension, core.space.Model, { + ofClass: survey.class.Poll, + components: { input: { component: chunter.component.ChatMessageInput } } + }) + builder.mixin(survey.class.Poll, core.class.Class, view.mixin.CollectionEditor, { editor: survey.component.PollCollection }) + + createAction( + builder, + { + action: survey.actionImpl.DeletePoll, + label: workbench.string.Delete, + icon: view.icon.Delete, + input: 'any', + category: survey.category.Survey, + target: survey.class.Poll, + context: { + mode: ['context', 'browser'], + group: 'remove' + }, + visibilityTester: view.function.CanDeleteObject, + override: [view.action.Delete] + }, + survey.action.DeletePoll + ) } diff --git a/models/survey/src/plugin.ts b/models/survey/src/plugin.ts index d6011d4409..70968dc5b3 100644 --- a/models/survey/src/plugin.ts +++ b/models/survey/src/plugin.ts @@ -13,8 +13,6 @@ // limitations under the License. // -import { mergeIds } from '@hcengineering/platform' -import { surveyId } from '@hcengineering/survey' import survey from '@hcengineering/survey-resources/src/plugin' -export default mergeIds(surveyId, survey, {}) +export default survey diff --git a/models/survey/src/types.ts b/models/survey/src/types.ts index f71a2e10ef..effa6b10ad 100644 --- a/models/survey/src/types.ts +++ b/models/survey/src/types.ts @@ -14,7 +14,18 @@ // import { IndexKind, type Domain, type Ref } from '@hcengineering/core' -import { ArrOf, Hidden, Index, Model, Prop, TypeRecord, TypeRef, TypeString, UX } from '@hcengineering/model' +import { + ArrOf, + Hidden, + Index, + Model, + Prop, + TypeBoolean, + TypeRecord, + TypeRef, + TypeString, + UX +} from '@hcengineering/model' import core, { TAttachedDoc, TDoc } from '@hcengineering/model-core' import { getEmbeddedLabel } from '@hcengineering/platform' import { type Poll, type Question, type Survey } from '@hcengineering/survey' @@ -48,7 +59,10 @@ export class TPoll extends TAttachedDoc implements Poll { @Prop(TypeString(), survey.string.Prompt) prompt!: string - @Prop(ArrOf(TypeRecord()), getEmbeddedLabel('Answers')) + @Prop(ArrOf(TypeRecord()), getEmbeddedLabel('Questions')) @Hidden() - results?: { question: string, answer: string[] }[] + questions?: Question[] + + @Prop(TypeBoolean(), survey.string.Completed) + isCompleted?: boolean } diff --git a/models/task/src/migration.ts b/models/task/src/migration.ts index 9d0f50ac8f..ffb516d8de 100644 --- a/models/task/src/migration.ts +++ b/models/task/src/migration.ts @@ -15,6 +15,7 @@ import activity, { type DocUpdateMessage } from '@hcengineering/activity' import { + DOMAIN_MODEL_TX, DOMAIN_STATUS, DOMAIN_TX, TxOperations, @@ -25,7 +26,6 @@ import { type Ref, type Space, type Status, - type TxCollectionCUD, type TxCreateDoc, type TxUpdateDoc } from '@hcengineering/core' @@ -95,7 +95,7 @@ export async function migrateDefaultStatusesBase ( // 3. More than one type (one system and one custom) - the tool is running after the WS upgrade. // Not supported for now. Alternatively - Proceed with (2) scenario for the custom one. Delete it in the end. - const defaultTypes = await client.find>(DOMAIN_TX, { + const defaultTypes = await client.find>(DOMAIN_MODEL_TX, { _class: core.class.TxCreateDoc, objectId: defaultTypeId, objectSpace: core.space.Model, @@ -121,7 +121,7 @@ export async function migrateDefaultStatusesBase ( // and not modified by user if (defaultType.attributes.tasks.length === 1 && defaultType.attributes.tasks[0] === defaultTaskTypeId) { const defaultTaskType = ( - await client.find>(DOMAIN_TX, { + await client.find>(DOMAIN_MODEL_TX, { _class: core.class.TxCreateDoc, objectId: defaultTaskTypeId, objectSpace: core.space.Model, @@ -133,7 +133,7 @@ export async function migrateDefaultStatusesBase ( logger.log('Moving the existing default type created by ConfigUser to a system one', '') logger.log('Moving the existing default task type created by ConfigUser to a system one', '') await client.update( - DOMAIN_TX, + DOMAIN_MODEL_TX, { _id: defaultTaskType._id }, { $set: { @@ -143,7 +143,7 @@ export async function migrateDefaultStatusesBase ( ) await client.update( - DOMAIN_TX, + DOMAIN_MODEL_TX, { _id: defaultType._id }, { $set: { @@ -173,7 +173,7 @@ export async function migrateDefaultStatusesBase ( logger.log('Moving the existing default type to a custom one', '') const newId = defaultType.objectId + '-custom' await client.update( - DOMAIN_TX, + DOMAIN_MODEL_TX, { _id: defaultType._id }, { $set: { @@ -183,7 +183,7 @@ export async function migrateDefaultStatusesBase ( } ) await client.update( - DOMAIN_TX, + DOMAIN_MODEL_TX, { objectId: defaultType.objectId, objectSpace: core.space.Model @@ -195,7 +195,7 @@ export async function migrateDefaultStatusesBase ( } ) await client.update( - DOMAIN_TX, + DOMAIN_MODEL_TX, { objectId: { $in: defaultType.attributes.tasks }, objectSpace: core.space.Model, @@ -295,7 +295,7 @@ export async function migrateDefaultStatusesBase ( // 1. Update all update TXes with statuses // 2. Update all push TXes with statuses - const projectTypeStatusesCreates = await client.find>(DOMAIN_TX, { + const projectTypeStatusesCreates = await client.find>(DOMAIN_MODEL_TX, { _class: core.class.TxCreateDoc, objectClass: task.class.ProjectType, objectSpace: core.space.Model, @@ -313,11 +313,11 @@ export async function migrateDefaultStatusesBase ( } counter++ - await client.update(DOMAIN_TX, { _id: ptsCreate._id }, { $set: { 'attributes.statuses': newUpdateStatuses } }) + await client.update(DOMAIN_MODEL_TX, { _id: ptsCreate._id }, { $set: { 'attributes.statuses': newUpdateStatuses } }) } logger.log('projectTypeStatusesCreates updated: ', counter) - const projectTypeStatusesUpdates = await client.find>(DOMAIN_TX, { + const projectTypeStatusesUpdates = await client.find>(DOMAIN_MODEL_TX, { _class: core.class.TxUpdateDoc, objectId: { $in: projectTypeStatusesCreates.map((sc) => sc.objectId) }, objectClass: task.class.ProjectType, @@ -335,11 +335,11 @@ export async function migrateDefaultStatusesBase ( } counter++ - await client.update(DOMAIN_TX, { _id: ptsUpdate._id }, { $set: { 'operations.statuses': newUpdateStatuses } }) + await client.update(DOMAIN_MODEL_TX, { _id: ptsUpdate._id }, { $set: { 'operations.statuses': newUpdateStatuses } }) } logger.log('projectTypeStatusesUpdates updated: ', counter) - const projectTypeStatusesPushes = await client.find>(DOMAIN_TX, { + const projectTypeStatusesPushes = await client.find>(DOMAIN_MODEL_TX, { _class: core.class.TxUpdateDoc, objectId: { $in: projectTypeStatusesCreates.map((sc) => sc.objectId) }, objectClass: task.class.ProjectType, @@ -363,7 +363,11 @@ export async function migrateDefaultStatusesBase ( } counter++ - await client.update(DOMAIN_TX, { _id: ptsUpdate._id }, { $set: { 'operations.$push.statuses': newPushStatus } }) + await client.update( + DOMAIN_MODEL_TX, + { _id: ptsUpdate._id }, + { $set: { 'operations.$push.statuses': newPushStatus } } + ) } logger.log('projectTypeStatusesPushes updated: ', counter) @@ -371,7 +375,7 @@ export async function migrateDefaultStatusesBase ( // 1. Update create TX // 2. Update all update TXes with statuses - const allTaskTypes = await client.find>(DOMAIN_TX, { + const allTaskTypes = await client.find>(DOMAIN_MODEL_TX, { _class: core.class.TxCreateDoc, objectClass: taskTypeClass, 'attributes.ofClass': { $in: baseTaskClasses } @@ -388,11 +392,15 @@ export async function migrateDefaultStatusesBase ( } counter++ - await client.update(DOMAIN_TX, { _id: taskType._id }, { $set: { 'attributes.statuses': newTaskTypeStatuses } }) + await client.update( + DOMAIN_MODEL_TX, + { _id: taskType._id }, + { $set: { 'attributes.statuses': newTaskTypeStatuses } } + ) } logger.log('allTaskTypes updated: ', counter) - const allTaskTypeStatusesUpdates = await client.find>(DOMAIN_TX, { + const allTaskTypeStatusesUpdates = await client.find>(DOMAIN_MODEL_TX, { _class: core.class.TxUpdateDoc, objectClass: taskTypeClass, objectId: { $in: allTaskTypes.map((tt) => tt.objectId) }, @@ -414,7 +422,7 @@ export async function migrateDefaultStatusesBase ( counter++ await client.update( - DOMAIN_TX, + DOMAIN_MODEL_TX, { _id: ttsUpdate._id }, { $set: { 'operations.statuses': newTaskTypeUpdateStatuses } } ) @@ -447,48 +455,6 @@ export async function migrateDefaultStatusesBase ( } logger.log('affectedBaseTasks updated: ', counter) - const baseTaskCreateTxes = await client.find>(DOMAIN_TX, { - _class: core.class.TxCollectionCUD, - 'tx._class': core.class.TxCreateDoc, - 'tx.objectClass': { $in: baseTaskClasses }, - 'tx.attributes.status': { $in: statusIdsBeingMigrated } - }) - - logger.log('Base task create TXes: ', baseTaskCreateTxes.length) - - counter = 0 - for (const baseTaskCreateTx of baseTaskCreateTxes) { - const tx = baseTaskCreateTx.tx as TxCreateDoc - const newStatus = getNewStatus(tx.attributes.status) - - if (newStatus !== tx.attributes.status) { - counter++ - await client.update(DOMAIN_TX, { _id: baseTaskCreateTx._id }, { $set: { 'tx.attributes.status': newStatus } }) - } - } - logger.log('Base task create TXes updated: ', counter) - - const baseTaskUpdateTxes = await client.find>(DOMAIN_TX, { - _class: core.class.TxCollectionCUD, - 'tx._class': core.class.TxUpdateDoc, - 'tx.objectClass': { $in: baseTaskClasses }, - 'tx.operations.status': { $in: statusIdsBeingMigrated } - }) - - logger.log('Base task update TXes: ', baseTaskUpdateTxes.length) - - counter = 0 - for (const baseTaskUpdateTx of baseTaskUpdateTxes) { - const tx = baseTaskUpdateTx.tx as TxUpdateDoc - const newStatus = tx.operations.status !== undefined ? getNewStatus(tx.operations.status) : undefined - - if (newStatus !== tx.operations.status) { - counter++ - await client.update(DOMAIN_TX, { _id: baseTaskUpdateTx._id }, { $set: { 'tx.operations.status': newStatus } }) - } - } - logger.log('Base task update TXes updated: ', counter) - const baseTaskUpdateMessages = await client.find(DOMAIN_ACTIVITY, { _class: activity.class.DocUpdateMessage, action: 'update', @@ -588,6 +554,12 @@ export const taskOperation: MigrateOperation = { { objectId: { $in: missing }, objectSpace: 'task:space:Statuses' }, { $set: { objectSpace: core.space.Model } } ) + await client.update( + DOMAIN_MODEL_TX, + { objectId: { $in: missing }, objectSpace: 'task:space:Statuses' }, + { $set: { objectSpace: core.space.Model } } + ) + await client.move(DOMAIN_TX, { objectId: { $in: missing }, objectSpace: core.space.Model }, DOMAIN_MODEL_TX) } } }, diff --git a/models/test-management/src/index.ts b/models/test-management/src/index.ts index 6b3a4dac7c..43b9e4d93e 100644 --- a/models/test-management/src/index.ts +++ b/models/test-management/src/index.ts @@ -119,7 +119,7 @@ function defineApplication (builder: Builder): void { } ] }, - navHeaderComponent: testManagement.component.NewTestCaseHeader + navHeaderComponent: testManagement.component.TestManagementSpaceHeader }, testManagement.app.TestManagement ) @@ -143,7 +143,7 @@ export function createModel (builder: Builder): void { builder.createDoc(activity.class.ActivityExtension, core.space.Model, { ofClass: testManagement.class.TestProject, - components: { input: chunter.component.ChatMessageInput } + components: { input: { component: chunter.component.ChatMessageInput } } }) defineTestSuite(builder) @@ -218,7 +218,7 @@ function defineTestSuite (builder: Builder): void { builder.createDoc(activity.class.ActivityExtension, core.space.Model, { ofClass: testManagement.class.TestSuite, - components: { input: chunter.component.ChatMessageInput } + components: { input: { component: chunter.component.ChatMessageInput } } }) builder.mixin(testManagement.class.TestSuite, core.class.Class, view.mixin.ObjectEditor, { @@ -283,7 +283,7 @@ function defineTestCase (builder: Builder): void { builder.createDoc(activity.class.ActivityExtension, core.space.Model, { ofClass: testManagement.class.TestCase, - components: { input: chunter.component.ChatMessageInput } + components: { input: { component: chunter.component.ChatMessageInput } } }) builder.mixin(testManagement.class.TestCase, core.class.Class, view.mixin.ObjectEditor, { @@ -388,7 +388,7 @@ function defineTestRun (builder: Builder): void { builder.createDoc(activity.class.ActivityExtension, core.space.Model, { ofClass: testManagement.class.TestRun, - components: { input: chunter.component.ChatMessageInput } + components: { input: { component: chunter.component.ChatMessageInput } } }) builder.mixin(testManagement.class.TestRun, core.class.Class, view.mixin.ObjectEditor, { diff --git a/models/tracker/src/index.ts b/models/tracker/src/index.ts index ed2b36de4f..df061fcb3e 100644 --- a/models/tracker/src/index.ts +++ b/models/tracker/src/index.ts @@ -461,22 +461,22 @@ export function createModel (builder: Builder): void { builder.createDoc(activity.class.ActivityExtension, core.space.Model, { ofClass: tracker.class.Issue, - components: { input: chunter.component.ChatMessageInput } + components: { input: { component: chunter.component.ChatMessageInput } } }) builder.createDoc(activity.class.ActivityExtension, core.space.Model, { ofClass: tracker.class.Milestone, - components: { input: chunter.component.ChatMessageInput } + components: { input: { component: chunter.component.ChatMessageInput } } }) builder.createDoc(activity.class.ActivityExtension, core.space.Model, { ofClass: tracker.class.Component, - components: { input: chunter.component.ChatMessageInput } + components: { input: { component: chunter.component.ChatMessageInput } } }) builder.createDoc(activity.class.ActivityExtension, core.space.Model, { ofClass: tracker.class.IssueTemplate, - components: { input: chunter.component.ChatMessageInput } + components: { input: { component: chunter.component.ChatMessageInput } } }) defineViewlets(builder) diff --git a/models/tracker/src/migration.ts b/models/tracker/src/migration.ts index f6ccd71142..060ac13f44 100644 --- a/models/tracker/src/migration.ts +++ b/models/tracker/src/migration.ts @@ -13,37 +13,37 @@ // limitations under the License. // +import activity, { type DocUpdateMessage } from '@hcengineering/activity' import core, { + AccountRole, + DOMAIN_MODEL_TX, DOMAIN_STATUS, + type Ref, + type Status, type TxCreateDoc, TxOperations, generateId, - toIdMap, - DOMAIN_TX, - type Status, - type Ref, - AccountRole + toIdMap } from '@hcengineering/core' import { + type MigrateOperation, + type MigrationClient, + type MigrationUpgradeClient, type ModelLogger, createOrUpdate, tryMigrate, - tryUpgrade, - type MigrateOperation, - type MigrationClient, - type MigrationUpgradeClient + tryUpgrade } from '@hcengineering/model' -import { DOMAIN_SPACE } from '@hcengineering/model-core' -import activity, { type DocUpdateMessage } from '@hcengineering/activity' import { DOMAIN_ACTIVITY } from '@hcengineering/model-activity' +import { DOMAIN_SPACE } from '@hcengineering/model-core' import { DOMAIN_TASK, migrateDefaultStatusesBase } from '@hcengineering/model-task' import tags from '@hcengineering/tags' import task from '@hcengineering/task' -import { type IssueStatus, TimeReportDayType, trackerId, type Issue, type Project } from '@hcengineering/tracker' +import { type Issue, type IssueStatus, type Project, TimeReportDayType, trackerId } from '@hcengineering/tracker' -import tracker from './plugin' import contact from '@hcengineering/model-contact' import { classicIssueTaskStatuses } from '.' +import tracker from './plugin' async function createDefaultProject (tx: TxOperations): Promise { const current = await tx.findOne(tracker.class.Project, { @@ -280,7 +280,7 @@ async function migrateStatusesToModel (client: MigrationClient): Promise { modifiedBy } - await client.create(DOMAIN_TX, tx) + await client.create(DOMAIN_MODEL_TX, tx) } } @@ -291,7 +291,7 @@ async function migrateDefaultTypeMixins (client: MigrationClient): Promise const newTaskTypeMixin = tracker.mixin.IssueTypeData await client.update( - DOMAIN_TX, + DOMAIN_MODEL_TX, { objectClass: core.class.Attribute, 'attributes.attributeOf': oldSpaceTypeMixin @@ -350,7 +350,7 @@ async function migrateDefaultProjectOwners (client: MigrationClient): Promise { await client.update( - DOMAIN_TX, + DOMAIN_MODEL_TX, { objectClass: task.class.TaskType, 'attributes.ofClass': tracker.class.Issue, @@ -363,7 +363,7 @@ async function migrateIssueStatuses (client: MigrationClient): Promise { } ) await client.update( - DOMAIN_TX, + DOMAIN_MODEL_TX, { objectClass: core.class.Status, 'attributes.ofAttribute': tracker.attribute.IssueStatus diff --git a/models/view/src/index.ts b/models/view/src/index.ts index e3f974c4a2..7d21f3db1c 100644 --- a/models/view/src/index.ts +++ b/models/view/src/index.ts @@ -67,6 +67,7 @@ import { type ListItemPresenter, type ObjectEditor, type ObjectEditorFooter, + type ObjectPanelFooter, type ObjectEditorHeader, type ObjectFactory, type ObjectPanel, @@ -216,6 +217,11 @@ export class TObjectEditorFooter extends TClass implements ObjectEditorFooter { editor!: AnyComponent } +@Mixin(view.mixin.ObjectPanelFooter, core.class.Class) +export class TObjectPanelFooter extends TClass implements ObjectPanelFooter { + editor!: AnyComponent +} + @Mixin(view.mixin.SpaceHeader, core.class.Class) export class TSpaceHeader extends TClass implements SpaceHeader { header!: AnyComponent @@ -445,6 +451,7 @@ export function createModel (builder: Builder): void { TObjectTitle, TObjectEditorHeader, TObjectEditorFooter, + TObjectPanelFooter, TSpaceHeader, TSpaceName, TSpacePresenter, diff --git a/packages/analytics-service/src/logging.ts b/packages/analytics-service/src/logging.ts index 0288c690ed..e29185849b 100644 --- a/packages/analytics-service/src/logging.ts +++ b/packages/analytics-service/src/logging.ts @@ -7,8 +7,6 @@ import { basename, dirname, join } from 'path' import winston from 'winston' import DailyRotateFile from 'winston-daily-rotate-file' -const PLATFORM_OPERATION_LOGGING = process.env.PLATFORM_OPERATION_LOGGING === 'true' - export class SplitLogger implements MeasureLogger { logger: winston.Logger @@ -110,9 +108,7 @@ export class SplitLogger implements MeasureLogger { } logOperation (operation: string, time: number, params: ParamsType): void { - if (PLATFORM_OPERATION_LOGGING) { - this.logger.info({ operation, time, ...params }) - } + this.logger.info(operation, { time, ...params }) } childLogger (name: string, params: Record): MeasureLogger { diff --git a/packages/core/src/__tests__/minmodel.ts b/packages/core/src/__tests__/minmodel.ts index 33f3c04a5f..134fc778fd 100644 --- a/packages/core/src/__tests__/minmodel.ts +++ b/packages/core/src/__tests__/minmodel.ts @@ -189,13 +189,6 @@ export function genMinModel (): TxCUD[] { kind: ClassifierKind.CLASS }) ) - txes.push( - createClass(core.class.TxCollectionCUD, { - label: 'TxCollectionCUD' as IntlString, - extends: core.class.TxCUD, - kind: ClassifierKind.CLASS - }) - ) txes.push( createClass(core.class.Blob, { diff --git a/packages/core/src/classes.ts b/packages/core/src/classes.ts index 027327ab44..4c33aeded2 100644 --- a/packages/core/src/classes.ts +++ b/packages/core/src/classes.ts @@ -326,6 +326,11 @@ export interface TypeAny extends Type { */ export const DOMAIN_MODEL = 'model' as Domain +/** + * @public + */ +export const DOMAIN_MODEL_TX = 'model_tx' as Domain + /** * @public */ diff --git a/packages/core/src/client.ts b/packages/core/src/client.ts index 735a0b0ba5..3dfd42b0cd 100644 --- a/packages/core/src/client.ts +++ b/packages/core/src/client.ts @@ -15,14 +15,14 @@ import { Analytics } from '@hcengineering/analytics' import { BackupClient, DocChunk } from './backup' -import { Account, AttachedDoc, Class, DOMAIN_MODEL, Doc, Domain, Ref, Timestamp } from './classes' +import { Account, Class, DOMAIN_MODEL, Doc, Domain, Ref, Timestamp } from './classes' import core from './component' import { Hierarchy } from './hierarchy' import { MeasureContext, MeasureMetricsContext } from './measurements' import { ModelDb } from './memdb' import type { DocumentQuery, FindOptions, FindResult, FulltextStorage, Storage, TxResult, WithLookup } from './storage' import { SearchOptions, SearchQuery, SearchResult, SortingOrder } from './storage' -import { Tx, TxCUD, TxCollectionCUD } from './tx' +import { Tx, TxCUD } from './tx' import { toFindResult } from './utils' const transactionThreshold = 500 @@ -307,9 +307,8 @@ export async function createClient ( // if we have attachment document create/delete we need to full refresh, since some derived data could be missing for (const tx of atxes) { if ( - tx._class === core.class.TxCollectionCUD && - ((tx as TxCollectionCUD).tx._class === core.class.TxCreateDoc || - (tx as TxCollectionCUD).tx._class === core.class.TxRemoveDoc) + (tx as TxCUD).attachedTo !== undefined && + (tx._class === core.class.TxCreateDoc || tx._class === core.class.TxRemoveDoc) ) { needFullRefresh = true break diff --git a/packages/core/src/component.ts b/packages/core/src/component.ts index baed6b65de..4ed7db7dff 100644 --- a/packages/core/src/component.ts +++ b/packages/core/src/component.ts @@ -60,7 +60,6 @@ import type { Tx, TxApplyIf, TxCUD, - TxCollectionCUD, TxCreateDoc, TxMixin, TxModelUpgrade, @@ -97,7 +96,6 @@ export default plugin(coreId, { TxApplyIf: '' as Ref>, TxCUD: '' as Ref>>, TxCreateDoc: '' as Ref>>, - TxCollectionCUD: '' as Ref>>, TxMixin: '' as Ref>>, TxUpdateDoc: '' as Ref>>, TxRemoveDoc: '' as Ref>>, diff --git a/packages/core/src/measurements/context.ts b/packages/core/src/measurements/context.ts index e5dadc7e23..a8d8dfa19f 100644 --- a/packages/core/src/measurements/context.ts +++ b/packages/core/src/measurements/context.ts @@ -64,22 +64,7 @@ export class MeasureMetricsContext implements MeasureContext { st = Date.now() contextData: object = {} private done (value?: number, override?: boolean): void { - updateMeasure( - this.metrics, - this.st, - this.params, - this.fullParams, - (spend) => { - this.logger.logOperation(this.name, spend, { - ...this.params, - ...(typeof this.fullParams === 'function' ? this.fullParams() : this.fullParams), - ...this.fullParams, - ...(this.logParams ?? {}) - }) - }, - value, - override - ) + updateMeasure(this.metrics, this.st, this.params, this.fullParams, (spend) => {}, value, override) } constructor ( @@ -144,7 +129,7 @@ export class MeasureMetricsContext implements MeasureContext { let needFinally = true try { const value = op(c) - if (value != null && value instanceof Promise) { + if (value instanceof Promise) { needFinally = false return value.finally(() => { c.end() diff --git a/packages/core/src/memdb.ts b/packages/core/src/memdb.ts index e04b712caa..471e940869 100644 --- a/packages/core/src/memdb.ts +++ b/packages/core/src/memdb.ts @@ -15,12 +15,12 @@ import { PlatformError, Severity, Status } from '@hcengineering/platform' import { Lookup, MeasureContext, ReverseLookups, getObjectValue } from '.' -import type { Account, AttachedDoc, Class, Doc, Ref } from './classes' +import type { Account, Class, Doc, Ref } from './classes' import core from './component' import { Hierarchy } from './hierarchy' import { checkMixinKey, matchQuery, resultSort } from './query' import type { DocumentQuery, FindOptions, FindResult, LookupData, Storage, TxResult, WithLookup } from './storage' -import type { Tx, TxCollectionCUD, TxCreateDoc, TxMixin, TxRemoveDoc, TxUpdateDoc } from './tx' +import type { Tx, TxCreateDoc, TxMixin, TxRemoveDoc, TxUpdateDoc } from './tx' import { TxProcessor } from './tx' import { toFindResult } from './utils' @@ -363,25 +363,6 @@ export class ModelDb extends MemDb { case core.class.TxCreateDoc: this.addDoc(TxProcessor.createDoc2Doc(tx as TxCreateDoc, clone)) break - case core.class.TxCollectionCUD: { - // We need update only create transactions to contain attached, attachedToClass. - const cud = tx as TxCollectionCUD> - if (cud.tx._class === core.class.TxCreateDoc) { - const createTx = cud.tx as TxCreateDoc - const d: TxCreateDoc = { - ...createTx, - attributes: { - ...createTx.attributes, - attachedTo: cud.objectId, - attachedToClass: cud.objectClass, - collection: cud.collection - } - } - this.addDoc(TxProcessor.createDoc2Doc(d as TxCreateDoc, clone)) - } - this.addTxes(ctx, [cud.tx], clone) - break - } case core.class.TxUpdateDoc: { const cud = tx as TxUpdateDoc const doc = this.findObject(cud.objectId) diff --git a/packages/core/src/operations.ts b/packages/core/src/operations.ts index c8d40c1c62..725e87c322 100644 --- a/packages/core/src/operations.ts +++ b/packages/core/src/operations.ts @@ -123,7 +123,7 @@ export class TxOperations implements Omit { modifiedBy ) await this.tx(tx) - return tx.tx.objectId as unknown as Ref

+ return tx.objectId as unknown as Ref

} async updateCollection( @@ -148,7 +148,7 @@ export class TxOperations implements Omit { modifiedBy ) await this.tx(tx) - return tx.objectId + return attachedTo } async removeCollection( @@ -171,7 +171,7 @@ export class TxOperations implements Omit { modifiedBy ) await this.tx(tx) - return tx.objectId + return attachedTo } updateDoc( diff --git a/packages/core/src/tx.ts b/packages/core/src/tx.ts index 51202c0fa9..13df6a1b16 100644 --- a/packages/core/src/tx.ts +++ b/packages/core/src/tx.ts @@ -88,6 +88,9 @@ export interface TxModelUpgrade extends Tx {} export interface TxCUD extends Tx { objectId: Ref objectClass: Ref> + attachedTo?: Ref + attachedToClass?: Ref> + collection?: string } /** @@ -97,17 +100,6 @@ export interface TxCreateDoc extends TxCUD { attributes: Data } -/** - * - * Will perform create/update/delete of attached documents. - * - * @public - */ -export interface TxCollectionCUD extends TxCUD { - collection: string - tx: TxCUD

-} - /** * @public */ @@ -327,9 +319,6 @@ export abstract class TxProcessor implements WithTx { case core.class.TxCreateDoc: result.push(await this.txCreateDoc(tx as TxCreateDoc)) break - case core.class.TxCollectionCUD: - result.push(await this.txCollectionCUD(tx as TxCollectionCUD)) - break case core.class.TxUpdateDoc: result.push(await this.txUpdateDoc(tx as TxUpdateDoc)) break @@ -348,9 +337,19 @@ export abstract class TxProcessor implements WithTx { } static createDoc2Doc(tx: TxCreateDoc, doClone = true): T { + const attached = + tx.attachedTo !== undefined + ? { + attachedTo: tx.attachedTo, + attachedToClass: tx.attachedToClass, + collection: tx.collection + } + : {} + // eslint-disable-next-line @typescript-eslint/consistent-type-assertions return { ...(doClone ? clone(tx.attributes) : tx.attributes), + ...attached, _id: tx.objectId, _class: tx.objectClass, space: tx.objectSpace, @@ -400,22 +399,10 @@ export abstract class TxProcessor implements WithTx { static buildDoc2Doc(txes: Tx[]): D | undefined { let doc: Doc - let createTx = txes.find((tx) => tx._class === core.class.TxCreateDoc) - if (createTx === undefined) { - const collectionTxes = txes.filter((tx) => tx._class === core.class.TxCollectionCUD) as Array< - TxCollectionCUD - > - const collectionCreateTx = collectionTxes.find((p) => p.tx._class === core.class.TxCreateDoc) - if (collectionCreateTx === undefined) return - createTx = TxProcessor.extractTx(collectionCreateTx) - } + const createTx = txes.find((tx) => tx._class === core.class.TxCreateDoc) if (createTx === undefined) return - const objectId = (createTx as TxCreateDoc).objectId doc = TxProcessor.createDoc2Doc(createTx as TxCreateDoc) - for (let tx of txes) { - if ((tx as TxCUD).objectId !== objectId && tx._class === core.class.TxCollectionCUD) { - tx = TxProcessor.extractTx(tx) - } + for (const tx of txes) { if (tx._class === core.class.TxUpdateDoc) { doc = TxProcessor.updateDoc2Doc(doc, tx as TxUpdateDoc) } else if (tx._class === core.class.TxMixin) { @@ -431,27 +418,10 @@ export abstract class TxProcessor implements WithTx { _class === core.class.TxCreateDoc || _class === core.class.TxUpdateDoc || _class === core.class.TxRemoveDoc || - _class === core.class.TxCollectionCUD || _class === core.class.TxMixin ) } - static extractTx (tx: Tx): Tx { - if (tx._class === core.class.TxCollectionCUD) { - const ctx = tx as TxCollectionCUD - if (ctx.tx._class === core.class.TxCreateDoc) { - const create = ctx.tx as TxCreateDoc - create.attributes.attachedTo = ctx.objectId - create.attributes.attachedToClass = ctx.objectClass - create.attributes.collection = ctx.collection - return create - } - return ctx.tx - } - - return tx - } - static txHasUpdate(tx: TxUpdateDoc, attribute: string): boolean { const ops = tx.operations if ((ops as any)[attribute] !== undefined) return true @@ -472,24 +442,6 @@ export abstract class TxProcessor implements WithTx { protected abstract txUpdateDoc (tx: TxUpdateDoc): Promise protected abstract txRemoveDoc (tx: TxRemoveDoc): Promise protected abstract txMixin (tx: TxMixin): Promise - - protected txCollectionCUD (tx: TxCollectionCUD): Promise { - // We need update only create transactions to contain attached, attachedToClass. - if (tx.tx._class === core.class.TxCreateDoc) { - const createTx = tx.tx as TxCreateDoc - const d: TxCreateDoc = { - ...createTx, - attributes: { - ...createTx.attributes, - attachedTo: tx.objectId, - attachedToClass: tx.objectClass, - collection: tx.collection - } - } - return this.txCreateDoc(d) - } - return this.tx(tx.tx) - } } /** @@ -534,18 +486,14 @@ export class TxFactory { tx: TxCUD

, modifiedOn?: Timestamp, modifiedBy?: Ref - ): TxCollectionCUD { + ): TxCUD

{ return { - _id: generateId(), - _class: core.class.TxCollectionCUD, - space: this.txSpace, - objectId, - objectClass: _class, - objectSpace: space, - modifiedOn: modifiedOn ?? Date.now(), - modifiedBy: modifiedBy ?? this.account, + ...tx, collection, - tx + attachedTo: objectId, + attachedToClass: _class, + modifiedOn: modifiedOn ?? Date.now(), + modifiedBy: modifiedBy ?? this.account } } diff --git a/packages/panel/src/components/Panel.svelte b/packages/panel/src/components/Panel.svelte index 95faa055ff..3a4dfee969 100644 --- a/packages/panel/src/components/Panel.svelte +++ b/packages/panel/src/components/Panel.svelte @@ -158,6 +158,11 @@ + + + + + {#if isUtils && $$slots.utils} diff --git a/packages/presentation/src/components/MessageBox.svelte b/packages/presentation/src/components/MessageBox.svelte index 78f20ff449..b2470cb000 100644 --- a/packages/presentation/src/components/MessageBox.svelte +++ b/packages/presentation/src/components/MessageBox.svelte @@ -93,6 +93,11 @@ user-select: none; box-shadow: var(--theme-popup-shadow); + @media screen and (max-width: 480px) { + width: 100%; + max-width: 100%; + } + .message { margin-bottom: 1.75rem; color: var(--theme-content-color); diff --git a/packages/presentation/src/utils.ts b/packages/presentation/src/utils.ts index 005e2e6883..b94076d6d3 100644 --- a/packages/presentation/src/utils.ts +++ b/packages/presentation/src/utils.ts @@ -119,7 +119,7 @@ class UIClient extends TxOperations implements Client, OptimisticTxes { this.pendingTxes.delete(t._id) // Only CUD tx can be pending now - const innerTx = TxProcessor.extractTx(t) as TxCUD + const innerTx = t as TxCUD if (innerTx._class === core.class.TxCreateDoc) { // eslint-disable-next-line @typescript-eslint/no-dynamic-delete @@ -206,7 +206,7 @@ class UIClient extends TxOperations implements Client, OptimisticTxes { return } - const innerTx = TxProcessor.extractTx(tx) as TxCUD + const innerTx = tx as TxCUD // Can pre-build some configuration later from the model if this will be too slow. const instantTxes = this.getHierarchy().classHierarchyMixin(innerTx.objectClass, plugin.mixin.InstantTransactions) if (instantTxes?.txClasses.includes(innerTx._class) !== true) { @@ -626,7 +626,7 @@ export async function getAttributeEditor ( function filterKeys (hierarchy: Hierarchy, keys: KeyedAttribute[], ignoreKeys: string[]): KeyedAttribute[] { const docKeys: Set = new Set(hierarchy.getAllAttributes(core.class.AttachedDoc).keys()) - keys = keys.filter((k) => !docKeys.has(k.key)) + keys = keys.filter((k) => !docKeys.has(k.key) || k.attr.editor !== undefined) keys = keys.filter((k) => !ignoreKeys.includes(k.key)) return keys } diff --git a/packages/query/src/__tests__/minmodel.ts b/packages/query/src/__tests__/minmodel.ts index b03547ebd0..fffb687a68 100644 --- a/packages/query/src/__tests__/minmodel.ts +++ b/packages/query/src/__tests__/minmodel.ts @@ -188,13 +188,6 @@ export function genMinModel (): TxCUD[] { kind: ClassifierKind.CLASS }) ) - txes.push( - createClass(core.class.TxCollectionCUD, { - label: 'TxCollectionCUD' as IntlString, - extends: core.class.TxCUD, - kind: ClassifierKind.CLASS - }) - ) txes.push( createClass(core.class.TxMixin, { label: 'TxMixin' as IntlString, diff --git a/packages/query/src/__tests__/query.test.ts b/packages/query/src/__tests__/query.test.ts index 89a05facfb..f8bd46df54 100644 --- a/packages/query/src/__tests__/query.test.ts +++ b/packages/query/src/__tests__/query.test.ts @@ -677,24 +677,33 @@ describe('query', () => { ) ) } - const pp = new Promise((resolve) => { - liveQuery.query( - test.class.TestComment, - { _id: parentComment }, - (result) => { - attempt++ - const comment = result[0] - if (comment !== undefined) { - expect((comment.$lookup as any)?.comments).toHaveLength(childLength - attempt) - } - if (attempt === childLength) { - resolve(null) - } - }, - { lookup: { _id: { comments: test.class.TestComment } } } - ) + + let secondPromise: Promise | undefined + const firstCallback = new Promise((resolve) => { + secondPromise = new Promise((_resolve) => { + liveQuery.query( + test.class.TestComment, + { _id: parentComment }, + (result) => { + attempt++ + if (attempt === 0) { + resolve() + } + const comment = result[0] + if (comment !== undefined) { + expect((comment.$lookup as any)?.comments).toHaveLength(childLength - attempt) + } + if (attempt === childLength) { + _resolve() + } + }, + { lookup: { _id: { comments: test.class.TestComment } } } + ) + }) }) + await firstCallback + for (const child of childs) { await factory.removeCollection( test.class.TestComment, @@ -705,7 +714,7 @@ describe('query', () => { 'comments' ) } - await pp + await secondPromise }) it('lookup query update doc', async () => { diff --git a/packages/query/src/index.ts b/packages/query/src/index.ts index 33ce6edd2d..c88bfe84c5 100644 --- a/packages/query/src/index.ts +++ b/packages/query/src/index.ts @@ -15,7 +15,6 @@ import { Analytics } from '@hcengineering/analytics' import core, { - AttachedDoc, BulkUpdateEvent, Class, Client, @@ -38,7 +37,6 @@ import core, { Space, Timestamp, Tx, - TxCollectionCUD, TxCreateDoc, TxMixin, TxProcessor, @@ -672,41 +670,6 @@ export class LiveQuery implements WithTx, Client { return {} } - protected async txCollectionCUD ( - tx: TxCollectionCUD, - docCache: Map - ): Promise { - for (const queries of this.queries) { - const isTx = this.client.getHierarchy().isDerived(queries[0], core.class.Tx) - for (const q of queries[1]) { - if (isTx) { - // handle add since Txes are immutable - await this.handleDocAdd(q, tx, true, docCache) - continue - } - - if (tx.tx._class === core.class.TxCreateDoc) { - const createTx = tx.tx as TxCreateDoc - const d: TxCreateDoc = { - ...createTx, - attributes: { - ...createTx.attributes, - attachedTo: tx.objectId, - attachedToClass: tx.objectClass, - collection: tx.collection - } - } - await this.handleDocAdd(q, TxProcessor.createDoc2Doc(d), true, docCache) - } else if (tx.tx._class === core.class.TxUpdateDoc) { - await this.handleDocUpdate(q, tx.tx as unknown as TxUpdateDoc, docCache) - } else if (tx.tx._class === core.class.TxRemoveDoc) { - await this.handleDocRemove(q, tx.tx as unknown as TxRemoveDoc) - } - } - } - return {} - } - async txUpdateDoc (tx: TxUpdateDoc, docCache: Map): Promise { for (const queries of this.queries) { const isTx = this.client.getHierarchy().isDerived(queries[0], core.class.Tx) @@ -1278,8 +1241,6 @@ export class LiveQuery implements WithTx, Client { switch (tx._class) { case core.class.TxCreateDoc: return await this.txCreateDoc(tx as TxCreateDoc, docCache) - case core.class.TxCollectionCUD: - return await this.txCollectionCUD(tx as TxCollectionCUD, docCache) case core.class.TxUpdateDoc: return await this.txUpdateDoc(tx as TxUpdateDoc, docCache) case core.class.TxRemoveDoc: diff --git a/packages/theme/styles/_layouts.scss b/packages/theme/styles/_layouts.scss index 54f7d76568..afdc52456e 100644 --- a/packages/theme/styles/_layouts.scss +++ b/packages/theme/styles/_layouts.scss @@ -900,6 +900,7 @@ a.no-line { .leading-3 { line-height: .75rem; } .tracking--05px { letter-spacing: -.5px; } .tracking-1px { letter-spacing: 1px; } +.text-balance { text-wrap: balance; } .over-underline { cursor: pointer; diff --git a/packages/theme/styles/_lumia-colors.scss b/packages/theme/styles/_lumia-colors.scss index 77fc78e02e..a7a8d71b4f 100644 --- a/packages/theme/styles/_lumia-colors.scss +++ b/packages/theme/styles/_lumia-colors.scss @@ -31,6 +31,8 @@ --tag-on-accent-PorpoiseText: #FFFFFF; --tag-accent-SunshineBackground: #FFBD2E; + + --border-color-global-error-border-color: #fb6863; } /* Dark Theme */ diff --git a/packages/theme/styles/_text-editor.scss b/packages/theme/styles/_text-editor.scss index 2e006ee333..c546f3cebf 100644 --- a/packages/theme/styles/_text-editor.scss +++ b/packages/theme/styles/_text-editor.scss @@ -444,4 +444,12 @@ &.delete { background: var(--text-editor-highlighted-node-delete-background-color); } -} \ No newline at end of file +} + +// Hiding the selection of an empty line +.select-text p > br.ProseMirror-trailingBreak::selection { + background: transparent; +} +.select-text .text-editor-image-container { + user-select: all; +} diff --git a/packages/theme/styles/_vars.scss b/packages/theme/styles/_vars.scss index 97df9737e3..7fb56b667d 100644 --- a/packages/theme/styles/_vars.scss +++ b/packages/theme/styles/_vars.scss @@ -33,6 +33,7 @@ --spacing-4: 2rem; --spacing-4_5: 2.25rem; --spacing-5: 2.5rem; + --spacing-5_5: 2.75rem; --spacing-6: 3rem; --spacing-6_5: 3.5rem; --spacing-7: 4rem; diff --git a/packages/theme/styles/components.scss b/packages/theme/styles/components.scss index 4d6339f176..4ea43b9f7f 100644 --- a/packages/theme/styles/components.scss +++ b/packages/theme/styles/components.scss @@ -543,6 +543,11 @@ .hulyModal-footer { padding: var(--spacing-1_5); } + @media screen and (max-width: 480px) { + width: 100vw; + height: var(--app-height); + min-width: 20rem; + } } &.type-aside, &.type-popup { diff --git a/packages/theme/styles/dialogs.scss b/packages/theme/styles/dialogs.scss index b4441ddc4b..15e9d3b89f 100644 --- a/packages/theme/styles/dialogs.scss +++ b/packages/theme/styles/dialogs.scss @@ -50,6 +50,7 @@ } &__title-wrap { + overflow-x: auto; display: flex; align-items: center; min-width: 0; @@ -277,6 +278,12 @@ max-width: 90vw; max-height: 90vh; + @media screen and (max-width: 480px) { + width: 100%; + max-width: 100%; + max-height: 100%; + } + .antiCard-header { padding: .75rem .75rem .375rem; diff --git a/packages/ui/src/components/ModernCheckbox.svelte b/packages/ui/src/components/ModernCheckbox.svelte new file mode 100644 index 0000000000..0f448f5ca1 --- /dev/null +++ b/packages/ui/src/components/ModernCheckbox.svelte @@ -0,0 +1,123 @@ + + +

+ {#if label !== undefined || labelIntl !== undefined || $$slots.default !== undefined} +
+ {#if labelIntl}
+ {/if} + + + + diff --git a/packages/ui/src/components/ModernRadioButton.svelte b/packages/ui/src/components/ModernRadioButton.svelte new file mode 100644 index 0000000000..8d3dd3cc4b --- /dev/null +++ b/packages/ui/src/components/ModernRadioButton.svelte @@ -0,0 +1,111 @@ + + +
+ {#if $$slots['panel-footer']} + + {/if} diff --git a/packages/ui/src/components/PopupInstance.svelte b/packages/ui/src/components/PopupInstance.svelte index bb36c19e07..877591e3ff 100644 --- a/packages/ui/src/components/PopupInstance.svelte +++ b/packages/ui/src/components/PopupInstance.svelte @@ -76,6 +76,12 @@ $: document.body.style.cursor = drag ? 'all-scroll' : 'default' $: docSize = checkAdaptiveMatching($deviceInfo.size, 'md') + $: isFullMobile = + $deviceInfo.isMobile && + $deviceInfo.isPortrait && + ['right', 'top', 'float', 'full', 'content', 'middle', 'centered', 'center', 'full-centered'].some( + (el) => element === el + ) function _update (result: any): void { if (onUpdate !== undefined) onUpdate(result) @@ -100,7 +106,7 @@ contentPanel: HTMLElement | undefined ): void => { const device: DeviceOptions = $deviceInfo - if ((fullSize || docSize) && (element === 'float' || element === 'centered')) { + if (((fullSize || docSize) && (element === 'float' || element === 'centered')) || isFullMobile) { options = fitPopupElement(modalHTML, device, 'full', contentPanel, clientWidth, clientHeight) options.props.maxHeight = '100vh' if (!modalHTML.classList.contains('fullsize')) modalHTML.classList.add('fullsize') diff --git a/packages/ui/src/components/Scroller.svelte b/packages/ui/src/components/Scroller.svelte index 56c8dc1c32..039f07bb67 100644 --- a/packages/ui/src/components/Scroller.svelte +++ b/packages/ui/src/components/Scroller.svelte @@ -48,6 +48,8 @@ export let disablePointerEventsOnScroll = false export let onScroll: ((params: ScrollParams) => void) | undefined = undefined export let onResize: (() => void) | undefined = undefined + export let containerName: string | undefined = undefined + export let containerType: 'size' | 'inline-size' | undefined = containerName !== undefined ? 'inline-size' : undefined export function scroll (top: number, left?: number, behavior: 'auto' | 'smooth' = 'auto') { if (divScroll) { @@ -598,6 +600,8 @@ : 'row'} style:height={contentDirection === 'vertical-reverse' ? 'max-content' : noStretch ? 'auto' : '100%'} style:align-items={align} + style:container-name={containerName} + style:container-type={containerType} class:disableEvents={isScrolling && disablePointerEventsOnScroll} use:resizeObserver={() => { checkAutoScroll() diff --git a/packages/ui/src/index.ts b/packages/ui/src/index.ts index 53e08dd656..4e6151b557 100644 --- a/packages/ui/src/index.ts +++ b/packages/ui/src/index.ts @@ -158,6 +158,8 @@ export { default as HotkeyGroup } from './components/HotkeyGroup.svelte' export { default as ModernWizardDialog } from './components/wizard/ModernWizardDialog.svelte' export { default as ModernWizardBar } from './components/wizard/ModernWizardBar.svelte' export { default as ModernTab } from './components/ModernTab.svelte' +export { default as ModernCheckbox } from './components/ModernCheckbox.svelte' +export { default as ModernRadioButton } from './components/ModernRadioButton.svelte' export { default as IconAdd } from './components/icons/Add.svelte' export { default as IconCircleAdd } from './components/icons/CircleAdd.svelte' diff --git a/packages/ui/src/popups.ts b/packages/ui/src/popups.ts index 8d475609fc..d5447490e5 100644 --- a/packages/ui/src/popups.ts +++ b/packages/ui/src/popups.ts @@ -372,7 +372,8 @@ export function fitPopupElement ( newProps.right = '1px' show = true } else if (element === 'full-centered') { - newProps.top = '20px' + const rect = contentPanel !== undefined ? contentPanel.getBoundingClientRect() : { top: 0 } + newProps.top = `${Math.max(20, rect.top + 1)}px` newProps.bottom = '20px' newProps.left = '20px' newProps.right = '20px' diff --git a/plugins/activity-resources/src/components/ActivityExtension.svelte b/plugins/activity-resources/src/components/ActivityExtension.svelte index d79034a5ef..908bd5a16d 100644 --- a/plugins/activity-resources/src/components/ActivityExtension.svelte +++ b/plugins/activity-resources/src/components/ActivityExtension.svelte @@ -24,5 +24,11 @@ {#if extension} - + {/if} diff --git a/plugins/activity-resources/src/components/doc-update-message/attributes/SetAttributesPresenter.svelte b/plugins/activity-resources/src/components/doc-update-message/attributes/SetAttributesPresenter.svelte index e50a586b7a..222ec50551 100644 --- a/plugins/activity-resources/src/components/doc-update-message/attributes/SetAttributesPresenter.svelte +++ b/plugins/activity-resources/src/components/doc-update-message/attributes/SetAttributesPresenter.svelte @@ -30,7 +30,7 @@ $: attrViewletConfig = viewlet?.config?.[attributeModel.key] $: attributeIcon = attrViewletConfig?.icon ?? attributeModel.icon ?? IconEdit - $: isUnset = values.length > 0 && !values.some((value) => value !== null && value !== '') + $: isUnset = values.length > 0 && !values.some((value) => value != null && value !== '') $: isTextType = getIsTextType(attributeModel) @@ -42,7 +42,7 @@ {#if isUnset} -
+