diff --git a/.vscode/launch.json b/.vscode/launch.json index 3c84e267dd..12c7f49f94 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -35,9 +35,11 @@ "args": ["src/__start.ts"], "env": { "FULLTEXT_URL": "http://localhost:4700", - "MONGO_URL": "mongodb://localhost:27017", - "DB_URL": "mongodb://localhost:27017", + // "MONGO_URL": "mongodb://localhost:27017", + // "DB_URL": "mongodb://localhost:27017", // "DB_URL": "postgresql://postgres:example@localhost:5432", + "DB_URL": "postgresql://root@host.docker.internal:26257/defaultdb?sslmode=disable", + "SERVER_PORT": "3332", "APM_SERVER_URL2": "http://localhost:8200", "METRICS_CONSOLE": "false", "METRICS_FILE": "${workspaceRoot}/metrics.txt", // Show metrics in console evert 30 seconds., @@ -253,7 +255,7 @@ "name": "Debug backup tool", "type": "node", "request": "launch", - "args": ["src/__start.ts", "backup", "../temp/backup-test", "platform", "--force"], + "args": ["src/__start.ts", "backup-restore", "../../../hardware/dump/alex-staff-agency", "w-haiodo-alex-staff-c-673ee7ab-87df5406ea-2b8b4d", "--skip", "blob"], "env": { "MINIO_ACCESS_KEY": "minioadmin", "MINIO_SECRET_KEY": "minioadmin", @@ -295,6 +297,31 @@ "outputCapture": "std", "cwd": "${workspaceRoot}/dev/tool" }, + { + "name": "Debug tool upgrade PG(tests)", + "type": "node", + "request": "launch", + "args": ["src/__start.ts", "upgrade", "--force"], + "env": { + "SERVER_SECRET": "secret", + "MINIO_ACCESS_KEY": "minioadmin", + "MINIO_SECRET_KEY": "minioadmin", + "MINIO_ENDPOINT": "localhost:9002", + "TRANSACTOR_URL": "ws://localhost:3334", + "ACCOUNT_DB_URL": "postgresql://postgres:example@localhost:5433", + "DB_URL": "postgresql://postgres:example@localhost:5433", + "MONGO_URL": "mongodb://localhost:27018", + "ACCOUNTS_URL": "http://localhost:3003", + "TELEGRAM_DATABASE": "telegram-service", + "ELASTIC_URL": "http://localhost:9201", + "REKONI_URL": "http://localhost:4004", + "MODEL_VERSION": "0.6.287" + }, + "runtimeArgs": ["--nolazy", "-r", "ts-node/register"], + "sourceMaps": true, + "outputCapture": "std", + "cwd": "${workspaceRoot}/dev/tool" + }, { "name": "Debug tool move", "type": "node", diff --git a/common/config/rush/pnpm-lock.yaml b/common/config/rush/pnpm-lock.yaml index 9ae464a246..13b158e773 100644 --- a/common/config/rush/pnpm-lock.yaml +++ b/common/config/rush/pnpm-lock.yaml @@ -1075,7 +1075,7 @@ dependencies: version: file:projects/time-resources.tgz(@tiptap/core@2.6.6)(@tiptap/pm@2.6.6)(@types/node@20.11.19)(esbuild@0.20.1)(postcss-load-config@4.0.2)(postcss@8.4.35)(ts-node@10.9.2) '@rush-temp/tool': specifier: file:./projects/tool.tgz - version: file:projects/tool.tgz(bufferutil@4.0.8)(utf-8-validate@6.0.4) + version: file:projects/tool.tgz '@rush-temp/tracker': specifier: file:./projects/tracker.tgz version: file:projects/tracker.tgz(@types/node@20.11.19)(esbuild@0.20.1)(ts-node@10.9.2) @@ -1766,9 +1766,6 @@ dependencies: pdfjs-dist: specifier: 2.12.313 version: 2.12.313 - pg: - specifier: 8.12.0 - version: 8.12.0 png-chunks-extract: specifier: ^1.0.0 version: 1.0.0 @@ -1782,8 +1779,8 @@ dependencies: specifier: ^7.0.2 version: 7.3.4(postcss@8.4.35)(typescript@5.3.3)(webpack@5.90.3) postgres: - specifier: ^3.4.4 - version: 3.4.4 + specifier: ^3.4.5 + version: 3.4.5 posthog-js: specifier: ~1.122.0 version: 1.122.0 @@ -16887,8 +16884,8 @@ packages: resolution: {integrity: sha512-i/hbxIE9803Alj/6ytL7UHQxRvZkI9O4Sy+J3HGc4F4oo/2eQAjTSNJ0bfxyse3bH0nuVesCk+3IRLaMtG3H6w==} dev: false - /postgres@3.4.4: - resolution: {integrity: sha512-IbyN+9KslkqcXa8AO9fxpk97PA4pzewvpi2B3Dwy9u4zpV32QicaEdgmF3eSQUzdRk7ttDHQejNgAEr4XoeH4A==} + /postgres@3.4.5: + resolution: {integrity: sha512-cDWgoah1Gez9rN3H4165peY9qfpEo+SA61oQv65O3cRUE1pOEoJWwddwcqKE8XZYjbblOJlYDlLV4h67HrEVDg==} engines: {node: '>=12'} dev: false @@ -21012,7 +21009,7 @@ packages: dev: false file:projects/account.tgz(@types/node@20.11.19)(esbuild@0.20.1)(ts-node@10.9.2): - resolution: {integrity: sha512-7ojvPVqMSMXTMLyJeAIRk/7DFH31sUkEO0taWQx2aMHp7D5pfaIhFwWUFEwIAiszGSxb45SHxQNJqeRZ/+IDdw==, tarball: file:projects/account.tgz} + resolution: {integrity: sha512-R9WSNuYeIemHJ7eAv7sCyf8IZHZott9CY1b/KJLAknaJfozXLQ6yOYszJjS/iWQRPl4TFFLgYsQaLWd1gDiqww==, tarball: file:projects/account.tgz} id: file:projects/account.tgz name: '@rush-temp/account' version: 0.0.0 @@ -21029,7 +21026,7 @@ packages: jest: 29.7.0(@types/node@20.11.19)(ts-node@10.9.2) mongodb: 6.10.0 otp-generator: 4.0.1 - postgres: 3.4.4 + postgres: 3.4.5 prettier: 3.2.5 ts-jest: 29.1.2(esbuild@0.20.1)(jest@29.7.0)(typescript@5.3.3) typescript: 5.3.3 @@ -22174,7 +22171,7 @@ packages: dev: false file:projects/cloud-datalake.tgz(@types/node@20.11.19)(bufferutil@4.0.8)(esbuild@0.20.1)(ts-node@10.9.2)(utf-8-validate@6.0.4): - resolution: {integrity: sha512-KdKIEaVTjeWtCEUYfkvyrIYxglGcfM8iICOnTxRzPiVmOIP3kswu7PaUKeH/xeFZQcvRBvhtNI7XT0dBr5mODA==, tarball: file:projects/cloud-datalake.tgz} + resolution: {integrity: sha512-4xq66fA2IsBhSePKZhWWfD1oCMF7eavO8dkefUDupGqhNUr/gv+4zPrsvoJZ2x5ZozqNvBHzbDQQeIuxg1uxNQ==, tarball: file:projects/cloud-datalake.tgz} id: file:projects/cloud-datalake.tgz name: '@rush-temp/cloud-datalake' version: 0.0.0 @@ -22191,7 +22188,7 @@ packages: eslint-plugin-promise: 6.1.1(eslint@8.56.0) itty-router: 5.0.18 jest: 29.7.0(@types/node@20.11.19)(ts-node@10.9.2) - postgres: 3.4.4 + postgres: 3.4.5 prettier: 3.2.5 ts-jest: 29.1.2(esbuild@0.20.1)(jest@29.7.0)(typescript@5.6.2) typescript: 5.6.2 @@ -27301,7 +27298,7 @@ packages: dev: false file:projects/postgres.tgz(esbuild@0.20.1)(ts-node@10.9.2): - resolution: {integrity: sha512-/6jhoJPjD7X4j5u87Epy0kCDI5gedKzlzCM/anvCuB57Igtpi6Ji1cX6LrzyJ33gbswI8tlygVs4miiYWToa5g==, tarball: file:projects/postgres.tgz} + resolution: {integrity: sha512-L+wKYPpR4mHAuZkVGw2tuKwfngukoXRgeSCMf5TOn5bIFkwqV+HUpi0O4iDCIizyFvGftWdPQARYFRvRa0qlXg==, tarball: file:projects/postgres.tgz} id: file:projects/postgres.tgz name: '@rush-temp/postgres' version: 0.0.0 @@ -27318,7 +27315,7 @@ packages: eslint-plugin-promise: 6.1.1(eslint@8.56.0) jest: 29.7.0(@types/node@20.11.19)(ts-node@10.9.2) pg: 8.12.0 - postgres: 3.4.4 + postgres: 3.4.5 prettier: 3.2.5 ts-jest: 29.1.2(esbuild@0.20.1)(jest@29.7.0)(typescript@5.3.3) typescript: 5.3.3 @@ -27329,7 +27326,6 @@ packages: - babel-plugin-macros - esbuild - node-notifier - - pg-native - supports-color - ts-node dev: false @@ -31936,9 +31932,8 @@ packages: - ts-node dev: false - file:projects/tool.tgz(bufferutil@4.0.8)(utf-8-validate@6.0.4): - resolution: {integrity: sha512-jFXGlkB1O4yIpKHye9XX55H4xZNemZfh3KaoCr5CKcaNIE5qZV8PGRihn2SuMNt5Y10PP2cUMXhJcBEO8tfNPQ==, tarball: file:projects/tool.tgz} - id: file:projects/tool.tgz + file:projects/tool.tgz: + resolution: {integrity: sha512-J1h1FNTEQWAGhkKNN3srbnXslsYEAoTnynbgY+6H1XzfABHP8gP0+g6aySbSfMn2HDM8TKfyPh/o9DGHs3tEfw==, tarball: file:projects/tool.tgz} name: '@rush-temp/tool' version: 0.0.0 dependencies: @@ -31947,11 +31942,11 @@ packages: '@types/mime-types': 2.1.4 '@types/minio': 7.0.18 '@types/node': 20.11.19 - '@types/pg': 8.11.6 '@types/request': 2.48.12 '@types/ws': 8.5.11 '@typescript-eslint/eslint-plugin': 6.21.0(@typescript-eslint/parser@6.21.0)(eslint@8.56.0)(typescript@5.3.3) '@typescript-eslint/parser': 6.21.0(eslint@8.56.0)(typescript@5.3.3) + bufferutil: 4.0.8 commander: 8.3.0 cross-env: 7.0.3 csv-parse: 5.1.0 @@ -31968,12 +31963,14 @@ packages: libphonenumber-js: 1.10.56 mime-types: 2.1.35 mongodb: 6.10.0 - pg: 8.12.0 - postgres: 3.4.4 + msgpackr: 1.11.0 + msgpackr-extract: 3.0.3 + postgres: 3.4.5 prettier: 3.2.5 ts-jest: 29.1.2(esbuild@0.20.1)(jest@29.7.0)(typescript@5.3.3) ts-node: 10.9.2(@types/node@20.11.19)(typescript@5.3.3) typescript: 5.3.3 + utf-8-validate: 6.0.4 ws: 8.18.0(bufferutil@4.0.8)(utf-8-validate@6.0.4) transitivePeerDependencies: - '@aws-sdk/credential-providers' @@ -31984,7 +31981,6 @@ packages: - '@swc/wasm' - babel-jest - babel-plugin-macros - - bufferutil - gcp-metadata - kerberos - mongodb-client-encryption @@ -31992,7 +31988,6 @@ packages: - snappy - socks - supports-color - - utf-8-validate dev: false file:projects/tracker-assets.tgz(esbuild@0.20.1)(ts-node@10.9.2): diff --git a/common/scripts/each-diff.sh b/common/scripts/each-diff.sh index 35b367f18b..8fc1d9dada 100755 --- a/common/scripts/each-diff.sh +++ b/common/scripts/each-diff.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash -FILES=$(git diff origin/main --name-only --diff-filter=ACMR | sed 's| |\\ |g') +FILES=$(git diff origin/develop --name-only --diff-filter=ACMR | sed 's| |\\ |g') [ -z "$FILES" ] && exit 0 roots=$(rush list -p --json | grep "path" | cut -f 2 -d ':' | cut -f 2 -d '"') diff --git a/common/scripts/dump_mongo.sh b/common/scripts/mongo_dump.sh similarity index 56% rename from common/scripts/dump_mongo.sh rename to common/scripts/mongo_dump.sh index a1fafd7f80..05b3e58d2a 100755 --- a/common/scripts/dump_mongo.sh +++ b/common/scripts/mongo_dump.sh @@ -3,6 +3,8 @@ MONGO_URL="mongodb://127.0.0.1:27017" DAYS=365 dump='dump' +trap "exit" INT + mkdir -p ${dump} mkdir -p ${dump}/workspaces @@ -14,15 +16,10 @@ mongosh ${MONGO_URL} --eval "$get_dbs" > ${dump}/databases.list mongodump --uri="${MONGO_URL}" --gzip --db account --archive="${dump}/account.gz" mongodump --uri="${MONGO_URL}" --gzip --db '%ai-bot' --archive="${dump}/ai-bot.gz" mongodump --uri="${MONGO_URL}" --gzip --db '%github' --archive="${dump}/github.gz" - -echo '#restore script' > ${dump}/restore.sh -echo "MONGO_URL=\"${MONGO_URL}\"" >> ${dump}/restore.sh -echo "do_drop=" >> ${dump}/restore.sh -echo "#do_drop=--drop" >> ${dump}/restore.sh - -echo 'mongorestore --uri="${MONGO_URL}" --gzip ${do_drop} --nsFrom "account" --nsTo "account" --archive=./account.gz ' >> ${dump}/restore.sh -echo 'mongorestore --uri="${MONGO_URL}" --gzip ${do_drop} --nsFrom "%ai-bot" --nsTo "%ai-bot" --archive=./ai-bot.gz ' >> ${dump}/restore.sh -echo 'mongorestore --uri="${MONGO_URL}" --gzip ${do_drop} --nsFrom "%github" --nsTo "%github" --archive=./github.gz ' >> ${dump}/restore.sh +mongodump --uri="${MONGO_URL}" --gzip --db 'calendar-service' --archive="${dump}/calendar-service.gz" +mongodump --uri="${MONGO_URL}" --gzip --db 'analytics-collector' --archive="${dump}/analytics-collector.gz" +mongodump --uri="${MONGO_URL}" --gzip --db 'gmail-service' --archive="${dump}/gmail-service.gz" +mongodump --uri="${MONGO_URL}" --gzip --db 'telegram-service' --archive="${dump}/telegram-service.gz" while IFS= read -r line; do arr=($line) @@ -35,6 +32,5 @@ while IFS= read -r line; do else mongodump --uri="$MONGO_URL" --gzip --db $db --archive=$aName fi - echo "mongorestore --uri=\"\${MONGO_URL}\" --gzip \${do_drop} --nsFrom \"${db}\" --nsTo \"${db}\" --archive=\"./workspaces/${db}-${lastVisit}.gz\"" >> ${dump}/restore.sh done < ${dump}/databases.list diff --git a/common/scripts/mongo_restore.sh b/common/scripts/mongo_restore.sh new file mode 100644 index 0000000000..de5cf2cf35 --- /dev/null +++ b/common/scripts/mongo_restore.sh @@ -0,0 +1,34 @@ +MONGO_URL="mongodb://127.0.0.1:27017" +do_drop= +#export do_drop=--drop + +postfix= +#postfix='-huly' + +trap "exit" INT + +mongorestore --uri="${MONGO_URL}" --gzip ${do_drop} --nsFrom "account.*" --nsTo "%account${postfix}.*" --archive=./account.gz +mongorestore --uri="${MONGO_URL}" --gzip ${do_drop} --nsFrom "%ai-bot.*" --nsTo "%ai-bot${postfix}.*" --archive=./ai-bot.gz +mongorestore --uri="${MONGO_URL}" --gzip ${do_drop} --nsFrom "%github.*" --nsTo "%github${postfix}.*" --archive=./github.gz +mongorestore --uri="${MONGO_URL}" --gzip ${do_drop} --nsFrom "calendar-service.*" --nsTo "%calendar-service${postfix}.*" --archive=./calendar-service.gz +mongorestore --uri="${MONGO_URL}" --gzip ${do_drop} --nsFrom "analytics-collector.*" --nsTo "%analytics-collector${postfix}.*" --archive=./analytics-collector.gz +mongorestore --uri="${MONGO_URL}" --gzip ${do_drop} --nsFrom "gmail-service.*" --nsTo "%gmail-service${postfix}.*" --archive=./gmail-service.gz +mongorestore --uri="${MONGO_URL}" --gzip ${do_drop} --nsFrom "telegram-service.*" --nsTo "%telegram-service${postfix}.*" --archive=./telegram-service.gz + + +while IFS= read -r line; do + arr=($line) + db="${arr[0]}" + lastVisit="${arr[1]}" + aName="./workspaces/$db-$lastVisit.gz" + stampName="./workspaces/$db-$lastVisit.gz.restored" + if [ -f $aName ]; then + if [ -f $stampName ]; then + echo "DB already restored $aName" + else + echo "Restoring DB $aName" + mongorestore --uri="${MONGO_URL}" --gzip ${do_drop} --nsFrom "${db}" --nsTo "${db}" --archive="./workspaces/${db}-${lastVisit}.gz" + touch ${stampName} + fi + fi +done < ./databases.list diff --git a/dev/tool/Dockerfile b/dev/tool/Dockerfile index 8bf3db5802..e5d2e79894 100644 --- a/dev/tool/Dockerfile +++ b/dev/tool/Dockerfile @@ -2,6 +2,8 @@ FROM node:20 WORKDIR /usr/src/app +RUN npm install --ignore-scripts=false --verbose bufferutil utf-8-validate @mongodb-js/zstd snappy msgpackr msgpackr-extract --unsafe-perm + COPY bundle/bundle.js ./ CMD [ "bash" ] diff --git a/dev/tool/package.json b/dev/tool/package.json index c92223a0f5..7e61c8e731 100644 --- a/dev/tool/package.json +++ b/dev/tool/package.json @@ -158,7 +158,11 @@ "libphonenumber-js": "^1.9.46", "mime-types": "~2.1.34", "mongodb": "^6.10.0", - "postgres": "^3.4.4", - "ws": "^8.18.0" + "postgres": "^3.4.5", + "ws": "^8.18.0", + "bufferutil": "^4.0.8", + "utf-8-validate": "^6.0.4", + "msgpackr": "^1.11.0", + "msgpackr-extract": "^3.0.3" } } diff --git a/dev/tool/src/index.ts b/dev/tool/src/index.ts index 78824be277..d49d0380ad 100644 --- a/dev/tool/src/index.ts +++ b/dev/tool/src/index.ts @@ -127,6 +127,7 @@ import { fixJsonMarkup, migrateMarkup } from './markup' import { fixMixinForeignAttributes, showMixinForeignAttributes } from './mixin' import { fixAccountEmails, renameAccount } from './renameAccount' import { moveFiles, showLostFiles } from './storage' +import { getModelVersion } from '@hcengineering/model-all' const colorConstants = { colorRed: '\u001b[31m', @@ -217,7 +218,7 @@ export function devTool ( program.command('version').action(() => { console.log( - `tools git_version: ${process.env.GIT_REVISION ?? ''} model_version: ${process.env.MODEL_VERSION ?? ''}` + `tools git_version: ${process.env.GIT_REVISION ?? ''} model_version: ${process.env.MODEL_VERSION ?? ''} ${JSON.stringify(getModelVersion())}` ) }) @@ -363,34 +364,42 @@ export function devTool ( .requiredOption('-w, --workspaceName ', 'Workspace name') .option('-e, --email ', 'Author email', 'platform@email.com') .option('-i, --init ', 'Init from workspace') + .option('-r, --region ', 'Region') .option('-b, --branding ', 'Branding key') - .action(async (workspace, cmd: { email: string, workspaceName: string, init?: string, branding?: string }) => { - const { dbUrl, txes, version, migrateOperations } = prepareTools() - await withDatabase(dbUrl, async (db) => { - const measureCtx = new MeasureMetricsContext('create-workspace', {}) - const brandingObj = - cmd.branding !== undefined || cmd.init !== undefined ? { key: cmd.branding, initWorkspace: cmd.init } : null - const wsInfo = await createWorkspaceRecord(measureCtx, db, brandingObj, cmd.email, cmd.workspaceName, workspace) + .action( + async ( + workspace, + cmd: { email: string, workspaceName: string, init?: string, branding?: string, region?: string } + ) => { + const { dbUrl, txes, version, migrateOperations } = prepareTools() + await withDatabase(dbUrl, async (db) => { + const measureCtx = new MeasureMetricsContext('create-workspace', {}) + const brandingObj = + cmd.branding !== undefined || cmd.init !== undefined ? { key: cmd.branding, initWorkspace: cmd.init } : null + const wsInfo = await createWorkspaceRecord( + measureCtx, + db, + brandingObj, + cmd.email, + cmd.workspaceName, + workspace, + cmd.region, + 'manual-creation' + ) - // update the record so it's not taken by one of the workers for the next 60 seconds - await updateWorkspace(db, wsInfo, { - mode: 'creating', - progress: 0, - lastProcessingTime: Date.now() + 1000 * 60 + await createWorkspace(measureCtx, version, brandingObj, wsInfo, txes, migrateOperations, undefined, true) + + await updateWorkspace(db, wsInfo, { + mode: 'active', + progress: 100, + disabled: false, + version + }) + + console.log('create-workspace done') }) - - await createWorkspace(measureCtx, version, brandingObj, wsInfo, txes, migrateOperations, undefined, true) - - await updateWorkspace(db, wsInfo, { - mode: 'active', - progress: 100, - disabled: false, - version - }) - - console.log('create-workspace done') - }) - }) + } + ) program .command('set-user-role ') diff --git a/models/all/src/index.ts b/models/all/src/index.ts index afbd4957fb..d8faff2804 100644 --- a/models/all/src/index.ts +++ b/models/all/src/index.ts @@ -114,7 +114,7 @@ interface ConfigurablePlugin extends Omit, 'pluginId' type BuilderConfig = [(b: Builder) => void, Plugin] | [(b: Builder) => void, Plugin, ConfigurablePlugin | undefined] export function getModelVersion (): Data { - const rawVersion = (process.env.MODEL_VERSION ?? '0.6.0').trim().replace('v', '').split('.') + const rawVersion = (process.env.MODEL_VERSION ?? '0.6.0').replace('"', '').trim().replace('v', '').split('.') if (rawVersion.length === 3) { return { major: parseInt(rawVersion[0]), diff --git a/models/attachment/src/index.ts b/models/attachment/src/index.ts index f7bf8cf1a4..163ce99ac9 100644 --- a/models/attachment/src/index.ts +++ b/models/attachment/src/index.ts @@ -14,9 +14,10 @@ // import activity from '@hcengineering/activity' -import type { Attachment, AttachmentMetadata, Photo, SavedAttachments } from '@hcengineering/attachment' -import { IndexKind, type Blob, type Domain, type Ref } from '@hcengineering/core' +import type { Attachment, AttachmentMetadata, Drawing, Photo, SavedAttachments } from '@hcengineering/attachment' +import { IndexKind, type Blob, type Class, type Doc, type Domain, type Ref } from '@hcengineering/core' import { + Hidden, Index, Model, Prop, @@ -28,10 +29,11 @@ import { UX, type Builder } from '@hcengineering/model' -import core, { TAttachedDoc } from '@hcengineering/model-core' +import core, { TAttachedDoc, TDoc } from '@hcengineering/model-core' import preference, { TPreference } from '@hcengineering/model-preference' import view, { createAction } from '@hcengineering/model-view' import workbench, { WidgetType } from '@hcengineering/workbench' +import { getEmbeddedLabel } from '@hcengineering/platform' import presentation from '@hcengineering/model-presentation' import attachment from './plugin' @@ -80,8 +82,24 @@ export class TSavedAttachments extends TPreference implements SavedAttachments { declare attachedTo: Ref } +@Model(attachment.class.Drawing, core.class.Doc, DOMAIN_ATTACHMENT) +export class TDrawing extends TDoc implements Drawing { + @Prop(TypeRef(core.class.Doc), getEmbeddedLabel('Parent')) + @Index(IndexKind.Indexed) + @Hidden() + parent!: Ref + + @Prop(TypeRef(core.class.Class), getEmbeddedLabel('Parent class')) + @Index(IndexKind.Indexed) + @Hidden() + parentClass!: Ref> + + @Prop(TypeString(), getEmbeddedLabel('Content')) + content?: string +} + export function createModel (builder: Builder): void { - builder.createModel(TAttachment, TPhoto, TSavedAttachments) + builder.createModel(TAttachment, TDrawing, TPhoto, TSavedAttachments) builder.mixin(attachment.class.Attachment, core.class.Class, view.mixin.ObjectPresenter, { presenter: attachment.component.AttachmentPresenter diff --git a/models/love/src/index.ts b/models/love/src/index.ts index 567edb65c4..0467e74658 100644 --- a/models/love/src/index.ts +++ b/models/love/src/index.ts @@ -16,14 +16,15 @@ import contact, { type Employee, type Person } from '@hcengineering/contact' import { AccountRole, + type CollaborativeDoc, + type CollectionSize, + DateRangeMode, + type Doc, type Domain, DOMAIN_TRANSIENT, IndexKind, type Ref, - type CollaborativeDoc, - type Doc, - type Timestamp, - type CollectionSize + type Timestamp } from '@hcengineering/core' import { type DevicesPreference, @@ -32,16 +33,16 @@ import { type JoinRequest, loveId, type Meeting, + type MeetingMinutes, + type MeetingStatus, type Office, type ParticipantInfo, type RequestStatus, type Room, type RoomAccess, type RoomInfo, - type RoomType, type RoomLanguage, - type MeetingMinutes, - type MeetingStatus + type RoomType } from '@hcengineering/love' import { type Builder, @@ -53,18 +54,18 @@ import { Model, Prop, ReadOnly, + TypeAny, TypeCollaborativeDoc, + TypeDate, TypeRef, TypeString, - TypeTimestamp, - UX, - TypeAny + UX } from '@hcengineering/model' import calendar, { TEvent } from '@hcengineering/model-calendar' import core, { TAttachedDoc, TDoc } from '@hcengineering/model-core' import preference, { TPreference } from '@hcengineering/model-preference' import presentation from '@hcengineering/model-presentation' -import view, { createAction } from '@hcengineering/model-view' +import view, { createAction, createAttributePresenter } from '@hcengineering/model-view' import notification from '@hcengineering/notification' import { getEmbeddedLabel } from '@hcengineering/platform' import setting from '@hcengineering/setting' @@ -228,12 +229,12 @@ export class TMeetingMinutes extends TAttachedDoc implements MeetingMinutes, Tod @Prop(PropCollection(chunter.class.ChatMessage), activity.string.Messages) messages?: number - @Prop(TypeTimestamp(), love.string.MeetingStart, { editor: view.component.TimestampPresenter }) + @Prop(TypeDate(DateRangeMode.DATETIME), love.string.MeetingStart, { editor: view.component.DateTimePresenter }) @ReadOnly() @Index(IndexKind.IndexedDsc) declare createdOn: Timestamp - @Prop(TypeTimestamp(), love.string.MeetingEnd) + @Prop(TypeDate(DateRangeMode.DATETIME), love.string.MeetingEnd, { editor: view.component.DateTimePresenter }) @ReadOnly() meetingEnd?: Timestamp @@ -506,10 +507,10 @@ export function createModel (builder: Builder): void { 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 } } + { key: 'transcription', displayProps: { key: 'transcription', suffix: true } }, + 'createdOn', + 'meetingEnd' ], configOptions: { hiddenKeys: ['description'], @@ -529,10 +530,13 @@ export function createModel (builder: Builder): void { config: [ '', { key: 'status', presenter: love.component.MeetingMinutesStatusPresenter, label: love.string.Status }, + { key: 'messages', displayProps: { key: 'messages', suffix: true } }, + { key: 'transcription', displayProps: { key: 'transcription', suffix: true } }, 'createdOn', 'meetingEnd' ], configOptions: { + hiddenKeys: ['description'], sortable: true }, variant: 'embedded' @@ -637,4 +641,19 @@ export function createModel (builder: Builder): void { builder.mixin(love.class.MeetingMinutes, core.class.Class, view.mixin.ObjectPanelFooter, { editor: love.component.PanelControlBar }) + + createAttributePresenter( + builder, + view.component.DateTimePresenter, + love.class.MeetingMinutes, + 'createdOn', + 'attribute' + ) + createAttributePresenter( + builder, + view.component.DateTimePresenter, + love.class.MeetingMinutes, + 'meetingEnd', + 'attribute' + ) } diff --git a/models/view/src/plugin.ts b/models/view/src/plugin.ts index c1f25d7a67..3c0b6a2bf7 100644 --- a/models/view/src/plugin.ts +++ b/models/view/src/plugin.ts @@ -64,6 +64,7 @@ export default mergeIds(viewId, view, { TimestampPresenter: '' as AnyComponent, DateEditor: '' as AnyComponent, DatePresenter: '' as AnyComponent, + DateTimePresenter: '' as AnyComponent, TableBrowser: '' as AnyComponent, RolePresenter: '' as AnyComponent, YoutubePresenter: '' as AnyComponent, diff --git a/packages/api-client/src/client.ts b/packages/api-client/src/client.ts index d4b223ad34..a39c0bc8df 100644 --- a/packages/api-client/src/client.ts +++ b/packages/api-client/src/client.ts @@ -34,15 +34,22 @@ import { AttachedData, Mixin, MixinUpdate, - MixinData + MixinData, + generateId } from '@hcengineering/core' import client, { clientId } from '@hcengineering/client' import { addLocation, getResource } from '@hcengineering/platform' import { login, selectWorkspace } from './account' import { type ServerConfig, loadServerConfig } from './config' -import { type MarkupOperations, type MarkupFormat, type MarkupRef, createMarkupOperations } from './markup' -import { type PlatformClient, type ConnectOptions } from './types' +import { + type MarkupFormat, + type MarkupOperations, + type MarkupRef, + MarkupContent, + createMarkupOperations +} from './markup' +import { type PlatformClient, type ConnectOptions, WithMarkup } from './types' /** * Create platform client @@ -121,41 +128,62 @@ class PlatformClientImpl implements PlatformClient { await this.connection.close() } - // TxOperations + private async processMarkup(id: Ref, data: WithMarkup): Promise { + const result: any = {} + + for (const [key, value] of Object.entries(data)) { + if (value instanceof MarkupContent) { + result[key] = this.markup.uploadMarkup(id, key, value.content, value.kind) + } else { + result[key] = value + } + } + + return result as T + } + + // DocOperations async createDoc( _class: Ref>, space: Ref, - attributes: Data, + attributes: WithMarkup>, id?: Ref ): Promise> { - return await this.client.createDoc(_class, space, attributes, id) + id ??= generateId() + const data = await this.processMarkup>(id, attributes) + return await this.client.createDoc(_class, space, data, id) } async updateDoc( _class: Ref>, space: Ref, objectId: Ref, - operations: DocumentUpdate, + operations: WithMarkup>, retrieve?: boolean ): Promise { - return await this.client.updateDoc(_class, space, objectId, operations, retrieve) + const update = await this.processMarkup>(objectId, operations) + return await this.client.updateDoc(_class, space, objectId, update, retrieve) } async removeDoc(_class: Ref>, space: Ref, objectId: Ref): Promise { return await this.client.removeDoc(_class, space, objectId) } + // CollectionOperations + async addCollection( _class: Ref>, space: Ref, attachedTo: Ref, attachedToClass: Ref>, collection: Extract | string, - attributes: AttachedData

, + attributes: WithMarkup>, id?: Ref

): Promise> { - return await this.client.addCollection(_class, space, attachedTo, attachedToClass, collection, attributes, id) + id ??= generateId() + const data = await this.processMarkup>(id, attributes) + return await this.client.addCollection(_class, space, attachedTo, attachedToClass, collection, data, id) } async updateCollection( @@ -165,9 +193,10 @@ class PlatformClientImpl implements PlatformClient { attachedTo: Ref, attachedToClass: Ref>, collection: Extract | string, - operations: DocumentUpdate

, + operations: WithMarkup>, retrieve?: boolean ): Promise> { + const update = await this.processMarkup>(objectId, operations) return await this.client.updateCollection( _class, space, @@ -175,7 +204,7 @@ class PlatformClientImpl implements PlatformClient { attachedTo, attachedToClass, collection, - operations, + update, retrieve ) } @@ -191,14 +220,17 @@ class PlatformClientImpl implements PlatformClient { return await this.client.removeCollection(_class, space, objectId, attachedTo, attachedToClass, collection) } + // MixinOperations + async createMixin( objectId: Ref, objectClass: Ref>, objectSpace: Ref, mixin: Ref>, - attributes: MixinData + attributes: WithMarkup> ): Promise { - return await this.client.createMixin(objectId, objectClass, objectSpace, mixin, attributes) + const data = await this.processMarkup>(objectId, attributes) + return await this.client.createMixin(objectId, objectClass, objectSpace, mixin, data) } async updateMixin( @@ -206,9 +238,10 @@ class PlatformClientImpl implements PlatformClient { objectClass: Ref>, objectSpace: Ref, mixin: Ref>, - attributes: MixinUpdate + attributes: WithMarkup> ): Promise { - return await this.client.updateMixin(objectId, objectClass, objectSpace, mixin, attributes) + const update = await this.processMarkup>(objectId, attributes) + return await this.client.updateMixin(objectId, objectClass, objectSpace, mixin, update) } // Markup diff --git a/packages/api-client/src/markup/types.ts b/packages/api-client/src/markup/types.ts index cd1d16daf0..a641f9fd06 100644 --- a/packages/api-client/src/markup/types.ts +++ b/packages/api-client/src/markup/types.ts @@ -21,6 +21,24 @@ export type MarkupRef = CollaborativeDoc /** @public */ export type MarkupFormat = 'markup' | 'html' | 'markdown' +/** @public */ +export class MarkupContent { + constructor ( + readonly content: string, + readonly kind: MarkupFormat + ) {} +} + +/** @public */ +export function html (content: string): MarkupContent { + return new MarkupContent(content, 'html') +} + +/** @public */ +export function markdown (content: string): MarkupContent { + return new MarkupContent(content, 'markdown') +} + /** * Provides operations for managing markup (rich-text) content. * @public */ diff --git a/packages/api-client/src/types.ts b/packages/api-client/src/types.ts index 820ae9c48f..4a7cd0a66a 100644 --- a/packages/api-client/src/types.ts +++ b/packages/api-client/src/types.ts @@ -15,6 +15,7 @@ import { type ClientSocketFactory } from '@hcengineering/client' import { + CollaborativeDoc, type AttachedData, type AttachedDoc, type Class, @@ -34,7 +35,18 @@ import { type TxResult, type WithLookup } from '@hcengineering/core' -import { type MarkupOperations } from './markup' +import { type MarkupContent, type MarkupOperations } from './markup' + +type WithPropertyType = { + [P in keyof T]: T[P] extends X ? Y : T[P] +} + +/** @public */ +export type WithMarkup = WithPropertyType< +WithPropertyType, +CollaborativeDoc, +MarkupContent +> /** * Platform API client @@ -77,7 +89,7 @@ export interface DocOperations { createDoc: ( _class: Ref>, space: Ref, - attributes: Data, + attributes: WithMarkup>, id?: Ref ) => Promise> @@ -85,7 +97,7 @@ export interface DocOperations { _class: Ref>, space: Ref, objectId: Ref, - operations: DocumentUpdate, + operations: WithMarkup>, retrieve?: boolean ) => Promise @@ -102,7 +114,7 @@ export interface CollectionOperations { attachedTo: Ref, attachedToClass: Ref>, collection: Extract | string, - attributes: AttachedData

, + attributes: WithMarkup>, id?: Ref

) => Promise> @@ -113,7 +125,7 @@ export interface CollectionOperations { attachedTo: Ref, attachedToClass: Ref>, collection: Extract | string, - operations: DocumentUpdate

, + operations: WithMarkup>, retrieve?: boolean ) => Promise> @@ -136,7 +148,7 @@ export interface MixinOperations { objectClass: Ref>, objectSpace: Ref, mixin: Ref>, - attributes: MixinData + attributes: WithMarkup> ) => Promise updateMixin: ( @@ -144,7 +156,7 @@ export interface MixinOperations { objectClass: Ref>, objectSpace: Ref, mixin: Ref>, - attributes: MixinUpdate + attributes: WithMarkup> ) => Promise } diff --git a/packages/core/src/classes.ts b/packages/core/src/classes.ts index 4c33aeded2..739ecebc11 100644 --- a/packages/core/src/classes.ts +++ b/packages/core/src/classes.ts @@ -648,6 +648,7 @@ export interface DomainIndexConfiguration extends Doc { } export type WorkspaceMode = + | 'manual-creation' | 'pending-creation' | 'creating' | 'upgrading' diff --git a/packages/kanban/src/components/Kanban.svelte b/packages/kanban/src/components/Kanban.svelte index ab641e6688..a787d8327c 100644 --- a/packages/kanban/src/components/Kanban.svelte +++ b/packages/kanban/src/components/Kanban.svelte @@ -82,12 +82,13 @@ return } - if (!dontUpdateRank && dragCardInitialRank !== dragCard.rank) { + if (!dontUpdateRank && dragCardInitialRank !== dragCard.rank && dragCardInitialRank !== undefined) { const dragCardRank = dragCard.rank updates = { ...updates, rank: dragCardRank } + dragCard.rank = dragCardInitialRank } if (Object.keys(updates).length > 0) { await client.diffUpdate(dragCard, updates) @@ -175,6 +176,15 @@ return false } + interface DragCardOverPos { + dragCardId: Ref + dragCardPos: number + overCardId: Ref + overCardPos: number + } + + let cardOverPos: DragCardOverPos | undefined + function cardDragOver (evt: CardDragEvent, object: Item, state: CategoryType): void { if (dragCard !== undefined && !dontUpdateRank) { const updates = getUpdateProps(dragCard, state) @@ -183,9 +193,29 @@ } if (object._id !== dragCard._id) { let arr = getGroupByValues(groupByDocs, state) ?? [] - const dragCardIndex = arr.findIndex((p) => p._id === dragCard?._id) - const targetIndex = arr.findIndex((p) => p._id === object._id) + let dragCardIndex = -1 + let targetIndex = -1 if ( + cardOverPos !== undefined && + cardOverPos.overCardId === object._id && + cardOverPos.dragCardId === dragCard._id + ) { + dragCardIndex = cardOverPos.dragCardPos + targetIndex = cardOverPos.overCardPos + } else { + dragCardIndex = arr.findIndex((p) => p._id === dragCard?._id) + targetIndex = arr.findIndex((p) => p._id === object._id) + cardOverPos = { + dragCardId: dragCard._id, + dragCardPos: dragCardIndex, + overCardId: object._id, + overCardPos: targetIndex + } + } + + if ( + dragCardIndex !== -1 && + targetIndex !== -1 && dragswap(evt, targetIndex, dragCardIndex) && arr[targetIndex] !== undefined && arr[dragCardIndex] !== undefined @@ -194,20 +224,28 @@ arr = [...arr.slice(0, targetIndex), dragCard, ...arr.slice(targetIndex)] setGroupByValues(groupByDocs, state, arr) groupByDocs = groupByDocs + cardOverPos = undefined } } } } - function cardDrop (evt: CardDragEvent, object: Item, state: CategoryType): void { + + async function cardDrop (evt: CardDragEvent, object: Item, state: CategoryType): Promise { if (!dontUpdateRank && dragCard !== undefined) { const arr = getGroupByValues(groupByDocs, state) ?? [] const s = arr.findIndex((p) => p._id === dragCard?._id) if (s !== -1) { dragCard.rank = makeRank(arr[s - 1]?.rank, arr[s + 1]?.rank) + const updates = getUpdateProps(dragCard, state) + + if (updates === undefined) { + await client.update(dragCard, { rank: dragCard.rank }) + } } } isDragging = false } + async function onDragStart (object: Item, state: CategoryType): Promise { dragCardInitialState = state dragCardState = state @@ -383,7 +421,7 @@ cardDragOver(evt, obj, state) }} cardDrop={(evt, obj) => { - cardDrop(evt, obj, state) + void cardDrop(evt, obj, state) }} {onDragStart} {showMenu} diff --git a/packages/kanban/src/components/KanbanRow.svelte b/packages/kanban/src/components/KanbanRow.svelte index 40ca360441..c9a78b8cbb 100644 --- a/packages/kanban/src/components/KanbanRow.svelte +++ b/packages/kanban/src/components/KanbanRow.svelte @@ -84,21 +84,23 @@ : state } - $: void limiter.add(async () => { - docQuery.query( - _class, - groupQuery, - (res) => { - limitedObjects = toIdMap(res) - }, - { ...options, limit } - ) - }) + $: docQuery.query( + _class, + groupQuery, + (res) => { + limitedObjects = toIdMap(res) + }, + { ...options, limit } + ) + + function getObject (_id: Ref, limitedObjects: IdMap): DocWithRank | undefined { + return limitedObjects.get(_id) ?? (_id === dragCard?._id ? dragCard : undefined) + } {#each stateObjects as objectRef, i (objectRef._id)} {@const dragged = isDragging && objectRef._id === dragCard?._id} - {@const object = limitedObjects.get(objectRef._id) ?? (objectRef._id === dragCard?._id ? dragCard : undefined)} + {@const object = getObject(objectRef._id, limitedObjects)} {#if object !== undefined}

+ + +{#if active} +
{ + updateToolbarPosition(readonly, board, toolbar) + }} + use:drawing={{ + readonly, + imageWidth, + imageHeight, + drawingData, + saveDrawing, + drawingTool, + penColor + }} + > + {#if !readonly} +
+ + {/each} +
+ {/if} + +
+{:else} + +{/if} + + diff --git a/packages/presentation/src/components/FilePreviewPopup.svelte b/packages/presentation/src/components/FilePreviewPopup.svelte index 4c0bc95191..5dfb00a309 100644 --- a/packages/presentation/src/components/FilePreviewPopup.svelte +++ b/packages/presentation/src/components/FilePreviewPopup.svelte @@ -15,7 +15,7 @@ @@ -65,6 +90,15 @@ + {#if props.drawingAvailable === true} +
{/if} {:else} -
- -
+ {#if shouldShowAvatar} +
+ +
+ {/if} {#if value !== undefined && value !== null && value.toString() !== ''} {#if withDate} {new Date(value).getDate()} @@ -524,7 +526,7 @@ border-radius: 0.375rem; transition-property: border, background-color, color, box-shadow; transition-duration: 0.15s; - cursor: pointer; + cursor: default; &.noPadding { padding: 0; diff --git a/plugins/attachment-resources/src/components/AttachmentActions.svelte b/plugins/attachment-resources/src/components/AttachmentActions.svelte index 53c0e9f5f6..2c23682ae9 100644 --- a/plugins/attachment-resources/src/components/AttachmentActions.svelte +++ b/plugins/attachment-resources/src/components/AttachmentActions.svelte @@ -16,21 +16,15 @@ import { type Attachment } from '@hcengineering/attachment' import type { WithLookup } from '@hcengineering/core' import { getResource } from '@hcengineering/platform' - import presentation, { - FilePreviewPopup, - canPreviewFile, - getFileUrl, - getPreviewAlignment, - previewTypes - } from '@hcengineering/presentation' - import { IconMoreH, Menu, Action as UIAction, closeTooltip, showPopup, tooltip } from '@hcengineering/ui' + import presentation, { canPreviewFile, getFileUrl, previewTypes } from '@hcengineering/presentation' + import { IconMoreH, Menu, Action as UIAction, showPopup, tooltip } from '@hcengineering/ui' import view, { Action } from '@hcengineering/view' import workbench from '@hcengineering/workbench' import AttachmentAction from './AttachmentAction.svelte' import FileDownload from './icons/FileDownload.svelte' import attachmentPlugin from '../plugin' - import { openAttachmentInSidebar } from '../utils' + import { openAttachmentInSidebar, showAttachmentPreviewPopup } from '../utils' export let attachment: WithLookup export let isSaved = false @@ -60,18 +54,7 @@ window.open((e.target as HTMLAnchorElement).href, '_blank') return } - closeTooltip() - - showPopup( - FilePreviewPopup, - { - file: attachment.file, - contentType: attachment.type, - name: attachment.name, - metadata: attachment.metadata - }, - getPreviewAlignment(attachment.type ?? '') - ) + showAttachmentPreviewPopup(attachment) } $: saveAttachmentAction = isSaved @@ -92,7 +75,7 @@ } } - const showMenu = (ev: Event) => { + const showMenu = (ev: Event): void => { const actions: UIAction[] = [] if (canPreview) { actions.push(openAction) diff --git a/plugins/attachment-resources/src/components/AttachmentGalleryPresenter.svelte b/plugins/attachment-resources/src/components/AttachmentGalleryPresenter.svelte index 07e83fc2db..c12836f692 100644 --- a/plugins/attachment-resources/src/components/AttachmentGalleryPresenter.svelte +++ b/plugins/attachment-resources/src/components/AttachmentGalleryPresenter.svelte @@ -15,10 +15,9 @@ diff --git a/plugins/attachment-resources/src/components/AttachmentPresenter.svelte b/plugins/attachment-resources/src/components/AttachmentPresenter.svelte index e68b597293..de0c89ab3d 100644 --- a/plugins/attachment-resources/src/components/AttachmentPresenter.svelte +++ b/plugins/attachment-resources/src/components/AttachmentPresenter.svelte @@ -18,17 +18,16 @@ import core, { type WithLookup } from '@hcengineering/core' import presentation, { canPreviewFile, - FilePreviewPopup, getBlobRef, getFileUrl, previewTypes, sizeToWidth } from '@hcengineering/presentation' - import { Label, closeTooltip, showPopup } from '@hcengineering/ui' + import { Label } from '@hcengineering/ui' import { permissionsStore } from '@hcengineering/view-resources' import filesize from 'filesize' import { createEventDispatcher } from 'svelte' - import { getType, openAttachmentInSidebar } from '../utils' + import { getType, openAttachmentInSidebar, showAttachmentPreviewPopup } from '../utils' import AttachmentName from './AttachmentName.svelte' @@ -78,18 +77,8 @@ window.open((e.target as HTMLAnchorElement).href, '_blank') return } - closeTooltip() if (value.type.startsWith('image/') || value.type.startsWith('video/') || value.type.startsWith('audio/')) { - showPopup( - FilePreviewPopup, - { - file: value.file, - contentType: value.type, - name: value.name, - metadata: value.metadata - }, - 'centered' - ) + showAttachmentPreviewPopup(value) } else { await openAttachmentInSidebar(value) } diff --git a/plugins/attachment-resources/src/components/AttachmentPreview.svelte b/plugins/attachment-resources/src/components/AttachmentPreview.svelte index 8afc8e0c44..2186bff0a7 100644 --- a/plugins/attachment-resources/src/components/AttachmentPreview.svelte +++ b/plugins/attachment-resources/src/components/AttachmentPreview.svelte @@ -15,14 +15,12 @@ -->
@@ -81,7 +98,7 @@ focusIndex={1} />
- {#if object._id !== $myOffice?._id && ($currentRoom?._id !== object._id || connecting)} + {#if showConnectionButton(object, $isConnected, $myOffice, $currentRoom)} {/if} diff --git a/plugins/love-resources/src/components/MeetingMinutesDocEditor.svelte b/plugins/love-resources/src/components/MeetingMinutesDocEditor.svelte index 0f33ec2e5d..d3ba1c66af 100644 --- a/plugins/love-resources/src/components/MeetingMinutesDocEditor.svelte +++ b/plugins/love-resources/src/components/MeetingMinutesDocEditor.svelte @@ -33,7 +33,7 @@ {#if doc} - + {#if data} - + {/if} diff --git a/plugins/love-resources/src/components/RoomLanguageSelector.svelte b/plugins/love-resources/src/components/RoomLanguageSelector.svelte index d21634b9c1..4e0e527c60 100644 --- a/plugins/love-resources/src/components/RoomLanguageSelector.svelte +++ b/plugins/love-resources/src/components/RoomLanguageSelector.svelte @@ -30,6 +30,7 @@ let container: HTMLElement let selectedItem: RoomLanguage = room.language + $: selectedItem = room.language let items: DropdownIntlItem[] = [] $: items = Object.entries(languagesDisplayData).map(([lang, data]) => ({ diff --git a/plugins/love-resources/src/components/RoomPreview.svelte b/plugins/love-resources/src/components/RoomPreview.svelte index a1783be146..4a5fd4297e 100644 --- a/plugins/love-resources/src/components/RoomPreview.svelte +++ b/plugins/love-resources/src/components/RoomPreview.svelte @@ -16,16 +16,15 @@ import { Person, type PersonAccount } from '@hcengineering/contact' import { Avatar, personByIdStore } from '@hcengineering/contact-resources' import { IdMap, getCurrentAccount } from '@hcengineering/core' - import { isOffice, ParticipantInfo, Room, RoomAccess, RoomType } from '@hcengineering/love' + import { isOffice, ParticipantInfo, Room, RoomAccess, RoomType, MeetingStatus } from '@hcengineering/love' import { Icon, Label, eventToHTMLElement, showPopup } from '@hcengineering/ui' import { createEventDispatcher } from 'svelte' import { getClient } from '@hcengineering/presentation' import { openDoc } from '@hcengineering/view-resources' - import { get } from 'svelte/store' import love from '../plugin' import { myInfo, selectedRoomPlace, currentRoom, currentMeetingMinutes } from '../stores' - import { getRoomLabel, lk } from '../utils' + import { getRoomLabel, lk, isConnected } from '../utils' import PersonActionPopup from './PersonActionPopup.svelte' import RoomLanguage from './RoomLanguage.svelte' @@ -67,14 +66,20 @@ async function openRoom (x: number, y: number): Promise { const client = getClient() const hierarchy = client.getHierarchy() - if ($currentRoom?._id === room._id) { + if ($isConnected && $currentRoom?._id === room._id) { const sid = await lk.getSid() - const meetingMinutes = - get(currentMeetingMinutes) ?? (await client.findOne(love.class.MeetingMinutes, { sid, attachedTo: room._id })) - if (meetingMinutes === undefined) { + let meeting = $currentMeetingMinutes + if (meeting?.sid !== sid || meeting?.attachedTo !== room._id || meeting?.status !== MeetingStatus.Active) { + meeting = await client.findOne(love.class.MeetingMinutes, { + sid, + attachedTo: room._id, + status: MeetingStatus.Active + }) + } + if (meeting === undefined) { await openDoc(hierarchy, room) } else { - await openDoc(hierarchy, meetingMinutes) + await openDoc(hierarchy, meeting) } } else { selectedRoomPlace.set({ _id: room._id, x, y }) diff --git a/plugins/love-resources/src/components/widget/MeetingWidget.svelte b/plugins/love-resources/src/components/widget/MeetingWidget.svelte index 91b854c640..efb67682a8 100644 --- a/plugins/love-resources/src/components/widget/MeetingWidget.svelte +++ b/plugins/love-resources/src/components/widget/MeetingWidget.svelte @@ -15,13 +15,13 @@ -{#if onChange !== undefined} +{#if onChange !== undefined && !readonly} onChange?.(e.detail)} {shouldShowAvatar} @@ -34,5 +39,5 @@ {inline} /> {:else} - + {/if} diff --git a/plugins/view-resources/src/components/DateTimePresenter.svelte b/plugins/view-resources/src/components/DateTimePresenter.svelte new file mode 100644 index 0000000000..da84c3e115 --- /dev/null +++ b/plugins/view-resources/src/components/DateTimePresenter.svelte @@ -0,0 +1,38 @@ + + + + diff --git a/plugins/view-resources/src/components/EditDoc.svelte b/plugins/view-resources/src/components/EditDoc.svelte index ab3082b50f..2e7758a0a5 100644 --- a/plugins/view-resources/src/components/EditDoc.svelte +++ b/plugins/view-resources/src/components/EditDoc.svelte @@ -98,7 +98,9 @@ $: if (_class !== oldClass) { oldClass = _class + realObjectClass = _class mainEditor = undefined + fieldEditors = [] } let keys: KeyedAttribute[] = [] @@ -289,7 +291,7 @@ - {#if headerEditor !== undefined} + {#if headerEditor !== undefined && object._id === _id} { - const readonlyParams = readonly - ? { - readonly: true, - editable: false, - disabled: true - } - : {} + const readonlyParams = + readonly || (attribute?.attribute?.readonly ?? false) + ? { + readonly: true, + editable: false, + disabled: true + } + : {} if (attribute.collectionAttr) { return { object, ...attribute.props, ...readonlyParams } } diff --git a/plugins/view-resources/src/components/viewer/ImageViewer.svelte b/plugins/view-resources/src/components/viewer/ImageViewer.svelte index f894012eb6..fd5ef2ca80 100644 --- a/plugins/view-resources/src/components/viewer/ImageViewer.svelte +++ b/plugins/view-resources/src/components/viewer/ImageViewer.svelte @@ -14,7 +14,7 @@ -->