mirror of
https://github.com/hcengineering/platform.git
synced 2026-09-20 16:47:50 +02:00
Merge branch 'staging-new'
Signed-off-by: Alexander Onnikov <Alexander.Onnikov@xored.com>
This commit is contained in:
Vendored
+5
-21
@@ -104,9 +104,12 @@
|
||||
"UPLOAD_URL": "/files",
|
||||
"AI_BOT_URL": "http://localhost:4010",
|
||||
"STATS_URL": "http://huly.local:4900",
|
||||
"QUEUE_CONFIG": "localhost:19092",
|
||||
"QUEUE_CONFIG": "huly.local:19092",
|
||||
"REGION": "cockroach",
|
||||
"QUEUE_REGION": "cockroach",
|
||||
"FILES_URL": "http://huly.local:4030/blob/:workspace/:blobId/:filename",
|
||||
"OTEL_EXPORTER_OTLP_ENDPOINT": "http://huly.local:4318/v1/traces"
|
||||
"OTEL_EXPORTER_OTLP_ENDPOINT": "http://huly.local:4318/v1/traces",
|
||||
"BRANDING_PATH": "${workspaceRoot}/dev/branding.json"
|
||||
},
|
||||
"runtimeArgs": ["--nolazy", "-r", "ts-node/register"],
|
||||
"runtimeVersion": "20",
|
||||
@@ -972,25 +975,6 @@
|
||||
"runtimeArgs": ["--nolazy", "-r", "ts-node/register"],
|
||||
"sourceMaps": true,
|
||||
"cwd": "${workspaceRoot}/services/export/pod-export"
|
||||
},
|
||||
{
|
||||
"name": "Msg2File",
|
||||
"type": "node",
|
||||
"request": "launch",
|
||||
"args": ["src/index.ts"],
|
||||
"env": {
|
||||
"ACCOUNTS_URL": "http://localhost:3000",
|
||||
"DB_URL": "postgresql://root@localhost:26257/defaultdb?sslmode=disable",
|
||||
"PORT": "9087",
|
||||
"SECRET": "secret",
|
||||
"SERVICE_ID": "msg2file-service",
|
||||
"STORAGE_CONFIG": "datalake|http://huly.local:4030"
|
||||
},
|
||||
"runtimeVersion": "20",
|
||||
"runtimeArgs": ["--nolazy", "-r", "ts-node/register"],
|
||||
"sourceMaps": true,
|
||||
"outputCapture": "std",
|
||||
"cwd": "${workspaceRoot}/services/msg2file"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
Generated
+106
-210
@@ -36,13 +36,13 @@ importers:
|
||||
version: 8.4.1
|
||||
'@hocuspocus/provider':
|
||||
specifier: ^2.15.2
|
||||
version: 2.15.2(bufferutil@4.0.8)(utf-8-validate@6.0.4)(y-protocols@1.0.6(yjs@13.6.23))(yjs@13.6.23)
|
||||
version: 2.15.2(bufferutil@4.0.8)(utf-8-validate@6.0.4)(y-protocols@1.0.6(yjs@13.6.27))(yjs@13.6.27)
|
||||
'@hocuspocus/server':
|
||||
specifier: ^2.15.2
|
||||
version: 2.15.2(bufferutil@4.0.8)(utf-8-validate@6.0.4)(y-protocols@1.0.6(yjs@13.6.23))(yjs@13.6.23)
|
||||
version: 2.15.2(bufferutil@4.0.8)(utf-8-validate@6.0.4)(y-protocols@1.0.6(yjs@13.6.27))(yjs@13.6.27)
|
||||
'@hocuspocus/transformer':
|
||||
specifier: ^2.15.2
|
||||
version: 2.15.2(@tiptap/core@2.11.7(@tiptap/pm@2.11.7))(@tiptap/pm@2.11.7)(y-prosemirror@1.3.6(prosemirror-model@1.24.1)(prosemirror-state@1.4.3)(prosemirror-view@1.37.2)(y-protocols@1.0.6(yjs@13.6.23))(yjs@13.6.23))(yjs@13.6.23)
|
||||
version: 2.15.2(@tiptap/core@2.11.7(@tiptap/pm@2.11.7))(@tiptap/pm@2.11.7)(y-prosemirror@1.3.7(prosemirror-model@1.24.1)(prosemirror-state@1.4.3)(prosemirror-view@1.37.2)(y-protocols@1.0.6(yjs@13.6.27))(yjs@13.6.27))(yjs@13.6.27)
|
||||
'@koa/cors':
|
||||
specifier: ^5.0.0
|
||||
version: 5.0.0
|
||||
@@ -267,7 +267,7 @@ importers:
|
||||
version: file:projects/collaboration.tgz(@babel/core@7.23.9)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.23.9))(esbuild@0.25.9)(ts-node@10.9.2(@swc/core@1.13.5)(@types/node@22.15.29)(typescript@5.8.3))
|
||||
'@rush-temp/collaborator':
|
||||
specifier: file:./projects/collaborator.tgz
|
||||
version: file:projects/collaborator.tgz(@babel/core@7.23.9)(@jest/types@29.6.3)(@swc/core@1.13.5)(@tiptap/pm@2.11.7)(babel-jest@29.7.0(@babel/core@7.23.9))(bufferutil@4.0.8)(gcp-metadata@5.3.0(encoding@0.1.13))(snappy@7.2.2)(socks@2.8.3)(utf-8-validate@6.0.4)(y-prosemirror@1.3.6(prosemirror-model@1.24.1)(prosemirror-state@1.4.3)(prosemirror-view@1.37.2)(y-protocols@1.0.6(yjs@13.6.23))(yjs@13.6.23))(y-protocols@1.0.6(yjs@13.6.23))
|
||||
version: file:projects/collaborator.tgz(@babel/core@7.23.9)(@jest/types@29.6.3)(@swc/core@1.13.5)(@tiptap/pm@2.11.7)(babel-jest@29.7.0(@babel/core@7.23.9))(bufferutil@4.0.8)(gcp-metadata@5.3.0(encoding@0.1.13))(snappy@7.2.2)(socks@2.8.3)(utf-8-validate@6.0.4)(y-prosemirror@1.3.7(prosemirror-model@1.24.1)(prosemirror-state@1.4.3)(prosemirror-view@1.37.2)(y-protocols@1.0.6(yjs@13.6.27))(yjs@13.6.27))(y-protocols@1.0.6(yjs@13.6.27))
|
||||
'@rush-temp/collaborator-client':
|
||||
specifier: file:./projects/collaborator-client.tgz
|
||||
version: file:projects/collaborator-client.tgz(@babel/core@7.23.9)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.23.9))(ts-node@10.9.2(@swc/core@1.13.5)(@types/node@22.15.29)(typescript@5.8.3))
|
||||
@@ -304,9 +304,6 @@ importers:
|
||||
'@rush-temp/communication-types':
|
||||
specifier: file:./projects/communication-types.tgz
|
||||
version: file:projects/communication-types.tgz(@types/node@22.15.29)(ts-node@10.9.2(@swc/core@1.13.5)(@types/node@22.15.29)(typescript@5.8.3))
|
||||
'@rush-temp/communication-yaml':
|
||||
specifier: file:./projects/communication-yaml.tgz
|
||||
version: file:projects/communication-yaml.tgz(@types/node@22.15.29)(ts-node@10.9.2(@swc/core@1.13.5)(@types/node@22.15.29)(typescript@5.8.3))
|
||||
'@rush-temp/contact':
|
||||
specifier: file:./projects/contact.tgz
|
||||
version: file:projects/contact.tgz(@babel/core@7.23.9)(@jest/types@29.6.3)(@types/node@22.15.29)(babel-jest@29.7.0(@babel/core@7.23.9))(esbuild@0.25.9)(ts-node@10.9.2(@swc/core@1.13.5)(@types/node@22.15.29)(typescript@5.8.3))
|
||||
@@ -912,7 +909,7 @@ importers:
|
||||
version: file:projects/pod-fulltext.tgz(@babel/core@7.23.9)(@jest/types@29.6.3)(@swc/core@1.13.5)(babel-jest@29.7.0(@babel/core@7.23.9))
|
||||
'@rush-temp/pod-github':
|
||||
specifier: file:./projects/pod-github.tgz
|
||||
version: file:projects/pod-github.tgz(@babel/core@7.23.9)(@jest/types@29.6.3)(@swc/core@1.13.5)(babel-jest@29.7.0(@babel/core@7.23.9))(bufferutil@4.0.8)(gcp-metadata@5.3.0(encoding@0.1.13))(snappy@7.2.2)(socks@2.8.3)(utf-8-validate@6.0.4)(y-prosemirror@1.3.6(prosemirror-model@1.24.1)(prosemirror-state@1.4.3)(prosemirror-view@1.37.2)(y-protocols@1.0.6(yjs@13.6.23))(yjs@13.6.23))
|
||||
version: file:projects/pod-github.tgz(@babel/core@7.23.9)(@jest/types@29.6.3)(@swc/core@1.13.5)(babel-jest@29.7.0(@babel/core@7.23.9))(bufferutil@4.0.8)(gcp-metadata@5.3.0(encoding@0.1.13))(snappy@7.2.2)(socks@2.8.3)(utf-8-validate@6.0.4)(y-prosemirror@1.3.7(prosemirror-model@1.24.1)(prosemirror-state@1.4.3)(prosemirror-view@1.37.2)(y-protocols@1.0.6(yjs@13.6.27))(yjs@13.6.27))
|
||||
'@rush-temp/pod-gmail':
|
||||
specifier: file:./projects/pod-gmail.tgz
|
||||
version: file:projects/pod-gmail.tgz(@babel/core@7.23.9)(@jest/types@29.6.3)(@swc/core@1.13.5)(babel-jest@29.7.0(@babel/core@7.23.9))(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.4)
|
||||
@@ -928,9 +925,6 @@ importers:
|
||||
'@rush-temp/pod-media':
|
||||
specifier: file:./projects/pod-media.tgz
|
||||
version: file:projects/pod-media.tgz(@babel/core@7.23.9)(@jest/types@29.6.3)(@swc/core@1.13.5)(babel-jest@29.7.0(@babel/core@7.23.9))
|
||||
'@rush-temp/pod-msg2file':
|
||||
specifier: file:./projects/pod-msg2file.tgz
|
||||
version: file:projects/pod-msg2file.tgz(@babel/core@7.23.9)(@jest/types@29.6.3)(@swc/core@1.13.5)(babel-jest@29.7.0(@babel/core@7.23.9))
|
||||
'@rush-temp/pod-notification':
|
||||
specifier: file:./projects/pod-notification.tgz
|
||||
version: file:projects/pod-notification.tgz(@babel/core@7.23.9)(@jest/types@29.6.3)(@swc/core@1.13.5)(babel-jest@29.7.0(@babel/core@7.23.9))
|
||||
@@ -1413,7 +1407,7 @@ importers:
|
||||
version: file:projects/text-markdown.tgz(@babel/core@7.23.9)(@jest/types@29.6.3)(@types/node@22.15.29)(babel-jest@29.7.0(@babel/core@7.23.9))(esbuild@0.25.9)(ts-node@10.9.2(@swc/core@1.13.5)(@types/node@22.15.29)(typescript@5.8.3))
|
||||
'@rush-temp/text-ydoc':
|
||||
specifier: file:./projects/text-ydoc.tgz
|
||||
version: file:projects/text-ydoc.tgz(@babel/core@7.23.9)(@jest/types@29.6.3)(@types/node@22.15.29)(babel-jest@29.7.0(@babel/core@7.23.9))(bufferutil@4.0.8)(esbuild@0.25.9)(prosemirror-model@1.24.1)(prosemirror-state@1.4.3)(prosemirror-view@1.37.2)(ts-node@10.9.2(@swc/core@1.13.5)(@types/node@22.15.29)(typescript@5.8.3))(utf-8-validate@6.0.4)(y-protocols@1.0.6(yjs@13.6.23))
|
||||
version: file:projects/text-ydoc.tgz(@babel/core@7.23.9)(@jest/types@29.6.3)(@types/node@22.15.29)(babel-jest@29.7.0(@babel/core@7.23.9))(bufferutil@4.0.8)(esbuild@0.25.9)(prosemirror-model@1.24.1)(prosemirror-state@1.4.3)(prosemirror-view@1.37.2)(ts-node@10.9.2(@swc/core@1.13.5)(@types/node@22.15.29)(typescript@5.8.3))(utf-8-validate@6.0.4)(y-protocols@1.0.6(yjs@13.6.27))
|
||||
'@rush-temp/theme':
|
||||
specifier: file:./projects/theme.tgz
|
||||
version: file:projects/theme.tgz(@babel/core@7.23.9)(@jest/types@29.6.3)(@types/node@22.15.29)(babel-jest@29.7.0(@babel/core@7.23.9))(esbuild@0.25.9)(postcss-load-config@4.0.2(postcss@8.5.3)(ts-node@10.9.2(@swc/core@1.13.5)(@types/node@22.15.29)(typescript@5.8.3)))(postcss@8.5.3)(ts-node@10.9.2(@swc/core@1.13.5)(@types/node@22.15.29)(typescript@5.8.3))
|
||||
@@ -1539,10 +1533,10 @@ importers:
|
||||
version: 2.11.7(@tiptap/core@2.11.7(@tiptap/pm@2.11.7))(@tiptap/extension-code-block@2.11.7(@tiptap/core@2.11.7(@tiptap/pm@2.11.7))(@tiptap/pm@2.11.7))(@tiptap/pm@2.11.7)(highlight.js@11.11.1)(lowlight@3.3.0)
|
||||
'@tiptap/extension-collaboration':
|
||||
specifier: ^2.11.7
|
||||
version: 2.11.7(@tiptap/core@2.11.7(@tiptap/pm@2.11.7))(@tiptap/pm@2.11.7)(y-prosemirror@1.3.6(prosemirror-model@1.24.1)(prosemirror-state@1.4.3)(prosemirror-view@1.37.2)(y-protocols@1.0.6(yjs@13.6.23))(yjs@13.6.23))
|
||||
version: 2.11.7(@tiptap/core@2.11.7(@tiptap/pm@2.11.7))(@tiptap/pm@2.11.7)(y-prosemirror@1.3.7(prosemirror-model@1.24.1)(prosemirror-state@1.4.3)(prosemirror-view@1.37.2)(y-protocols@1.0.6(yjs@13.6.27))(yjs@13.6.27))
|
||||
'@tiptap/extension-collaboration-cursor':
|
||||
specifier: ^2.11.7
|
||||
version: 2.11.7(@tiptap/core@2.11.7(@tiptap/pm@2.11.7))(y-prosemirror@1.3.6(prosemirror-model@1.24.1)(prosemirror-state@1.4.3)(prosemirror-view@1.37.2)(y-protocols@1.0.6(yjs@13.6.23))(yjs@13.6.23))
|
||||
version: 2.11.7(@tiptap/core@2.11.7(@tiptap/pm@2.11.7))(y-prosemirror@1.3.7(prosemirror-model@1.24.1)(prosemirror-state@1.4.3)(prosemirror-view@1.37.2)(y-protocols@1.0.6(yjs@13.6.27))(yjs@13.6.27))
|
||||
'@tiptap/extension-document':
|
||||
specifier: ^2.11.7
|
||||
version: 2.11.7(@tiptap/core@2.11.7(@tiptap/pm@2.11.7))
|
||||
@@ -1732,9 +1726,6 @@ importers:
|
||||
'@types/node':
|
||||
specifier: ^22.15.29
|
||||
version: 22.15.29
|
||||
'@types/node-cron':
|
||||
specifier: ^3.0.11
|
||||
version: 3.0.11
|
||||
'@types/nodemailer':
|
||||
specifier: ^6.4.17
|
||||
version: 6.4.17
|
||||
@@ -2032,6 +2023,9 @@ importers:
|
||||
graphql-binding:
|
||||
specifier: ^2.5.2
|
||||
version: 2.5.2
|
||||
hash-it:
|
||||
specifier: ^6.0.0
|
||||
version: 6.0.0
|
||||
heic-decode:
|
||||
specifier: ^2.1.0
|
||||
version: 2.1.0
|
||||
@@ -2102,8 +2096,8 @@ importers:
|
||||
specifier: ~1.0.4
|
||||
version: 1.0.5
|
||||
lib0:
|
||||
specifier: ^0.2.88
|
||||
version: 0.2.89
|
||||
specifier: ^0.2.109
|
||||
version: 0.2.114
|
||||
libphonenumber-js:
|
||||
specifier: ^1.9.46
|
||||
version: 1.10.56
|
||||
@@ -2155,9 +2149,6 @@ importers:
|
||||
msgpackr-extract:
|
||||
specifier: ^3.0.3
|
||||
version: 3.0.3
|
||||
node-cron:
|
||||
specifier: ^3.0.3
|
||||
version: 3.0.3
|
||||
node-forge:
|
||||
specifier: ^1.3.1
|
||||
version: 1.3.1
|
||||
@@ -2391,19 +2382,19 @@ importers:
|
||||
version: 8.18.2(bufferutil@4.0.8)(utf-8-validate@6.0.4)
|
||||
y-indexeddb:
|
||||
specifier: ^9.0.12
|
||||
version: 9.0.12(yjs@13.6.23)
|
||||
version: 9.0.12(yjs@13.6.27)
|
||||
y-prosemirror:
|
||||
specifier: ^1.3.6
|
||||
version: 1.3.6(prosemirror-model@1.24.1)(prosemirror-state@1.4.3)(prosemirror-view@1.37.2)(y-protocols@1.0.6(yjs@13.6.23))(yjs@13.6.23)
|
||||
specifier: ^1.3.7
|
||||
version: 1.3.7(prosemirror-model@1.24.1)(prosemirror-state@1.4.3)(prosemirror-view@1.37.2)(y-protocols@1.0.6(yjs@13.6.27))(yjs@13.6.27)
|
||||
y-protocols:
|
||||
specifier: ^1.0.6
|
||||
version: 1.0.6(yjs@13.6.23)
|
||||
version: 1.0.6(yjs@13.6.27)
|
||||
y-websocket:
|
||||
specifier: ^2.1.0
|
||||
version: 2.1.0(yjs@13.6.23)
|
||||
version: 2.1.0(yjs@13.6.27)
|
||||
yjs:
|
||||
specifier: ^13.6.23
|
||||
version: 13.6.23
|
||||
specifier: ^13.6.27
|
||||
version: 13.6.27
|
||||
zod:
|
||||
specifier: ^3.22.4
|
||||
version: 3.24.2
|
||||
@@ -4564,7 +4555,7 @@ packages:
|
||||
version: 0.0.0
|
||||
|
||||
'@rush-temp/collaboration@file:projects/collaboration.tgz':
|
||||
resolution: {integrity: sha512-aDcBIrPlESppS8EWtyzKhLJSC7IEHzPdLxtA05aZtp63v6+LRARuzfhT1x53KJ1u03/2ttjGu/ts7dVEnct2kw==, tarball: file:projects/collaboration.tgz}
|
||||
resolution: {integrity: sha512-Ij/ksdlgcTdsHBU00nzKWoYkQNM0RMJdaPR1ZSmOMlKe9lndge3UT23w3G0KHXelXlkzBxKfKWPEfnAy1biedw==, tarball: file:projects/collaboration.tgz}
|
||||
version: 0.0.0
|
||||
|
||||
'@rush-temp/collaborator-client@file:projects/collaborator-client.tgz':
|
||||
@@ -4572,7 +4563,7 @@ packages:
|
||||
version: 0.0.0
|
||||
|
||||
'@rush-temp/collaborator@file:projects/collaborator.tgz':
|
||||
resolution: {integrity: sha512-cGZGxft0YIkGzuUQIhYORUQ00fi/rhyhJnA0TMYMniRDfLLewBPvRtyXIHSHVZG1AJ1TWyyp+Xe+MrKfGYDzFw==, tarball: file:projects/collaborator.tgz}
|
||||
resolution: {integrity: sha512-+WZ7aCQzJBE8eF+5MM4ruyqa73msf/U6AMvX598LOHy6WwTL/id/IXTSyult37VBTYpCa64I6BhD77gTV8D4Rw==, tarball: file:projects/collaborator.tgz}
|
||||
version: 0.0.0
|
||||
|
||||
'@rush-temp/communication-assets@file:projects/communication-assets.tgz':
|
||||
@@ -4580,7 +4571,7 @@ packages:
|
||||
version: 0.0.0
|
||||
|
||||
'@rush-temp/communication-client-query@file:projects/communication-client-query.tgz':
|
||||
resolution: {integrity: sha512-6OA/sAql1ZLhhDX9J2yZOAEgfXqSxYh6yxpH/YGrC3PfYyhVAoe7piCjpB+JpKR5wL1kzpl1eP7Bj0IEq2jNJQ==, tarball: file:projects/communication-client-query.tgz}
|
||||
resolution: {integrity: sha512-+68bXbpIU6XTujL5Dj3pFCdFelV7mE+Ln6zZEvf2NPHhAcJqy3+KjHQyIbytnWhCFsGyk+HE3S08ps6Kl6TKtw==, tarball: file:projects/communication-client-query.tgz}
|
||||
version: 0.0.0
|
||||
|
||||
'@rush-temp/communication-cockroach@file:projects/communication-cockroach.tgz':
|
||||
@@ -4588,7 +4579,7 @@ packages:
|
||||
version: 0.0.0
|
||||
|
||||
'@rush-temp/communication-query@file:projects/communication-query.tgz':
|
||||
resolution: {integrity: sha512-sXcvSDoXmgZH233jFRePio23qQosvQZoACKqKgJ2Obr+oY4ZzJ2uX7DM0lyIKbhFUuLNFl56u2RfM5ra8SxRDg==, tarball: file:projects/communication-query.tgz}
|
||||
resolution: {integrity: sha512-KMDSFpLNW1ekFoAf+BbRxPeRYnMUjYu+l80F+fgtvkKsX/6oKtb6xRhQyVh4EQiRlQBaMV//1b2d7cBwuF5HDw==, tarball: file:projects/communication-query.tgz}
|
||||
version: 0.0.0
|
||||
|
||||
'@rush-temp/communication-resources@file:projects/communication-resources.tgz':
|
||||
@@ -4604,21 +4595,17 @@ packages:
|
||||
version: 0.0.0
|
||||
|
||||
'@rush-temp/communication-server@file:projects/communication-server.tgz':
|
||||
resolution: {integrity: sha512-KVc53phC1b9CNptvJmMb706t74gsCiEQGWvCpvTlRWMd3q+pj7i2994gJ0/KzuCojcHxPYVn2QxNojpNSV66Eg==, tarball: file:projects/communication-server.tgz}
|
||||
resolution: {integrity: sha512-HfKzdbndaS3opK1inqcTOrVnKvU7PS2asLmKuR4JHooiTTvFFCHcXXYO++t1QmdGmankZRn3Qsq59CIt/UPgsA==, tarball: file:projects/communication-server.tgz}
|
||||
version: 0.0.0
|
||||
|
||||
'@rush-temp/communication-shared@file:projects/communication-shared.tgz':
|
||||
resolution: {integrity: sha512-SustnNpr/eopCXmm0ZahvUZXZPcD7psSWM+CkWVwiPlLVPGWMVUURFOcnC94F3OhY+7qu9N0Rgr3gDvr4VKXWA==, tarball: file:projects/communication-shared.tgz}
|
||||
resolution: {integrity: sha512-/ZUh5sviGQkQGJnOe6ImJHpKCcn7xNyyMeOcl4Tr3TqXjsH+uOQ0MAFI0Ul4f1LI+dGIrp1QsZWSVWvEVprRQQ==, tarball: file:projects/communication-shared.tgz}
|
||||
version: 0.0.0
|
||||
|
||||
'@rush-temp/communication-types@file:projects/communication-types.tgz':
|
||||
resolution: {integrity: sha512-uzZ82V1R+h+TmxBCrmKaAfUgoiX3Z8ciPGrkc42M++M5d5mlfIjTvPh0O+y50pHoFuQ5NQ3pNIjeDkrJE8Dp0w==, tarball: file:projects/communication-types.tgz}
|
||||
version: 0.0.0
|
||||
|
||||
'@rush-temp/communication-yaml@file:projects/communication-yaml.tgz':
|
||||
resolution: {integrity: sha512-D+fijwXQM2oMeT6QOmkRVLik5uyVCNsml07rz5RvKcPEpbqRh3YBtIvcUABoCStLvNTIzYuXnqbCHzSl/ibidw==, tarball: file:projects/communication-yaml.tgz}
|
||||
version: 0.0.0
|
||||
|
||||
'@rush-temp/communication@file:projects/communication.tgz':
|
||||
resolution: {integrity: sha512-kO/x+NBE4Nmtz5KhHO0kxty/0N1A2PH4dNVTdOviEV4mVAhFm7DkgWHu0SwWJvQrCzsB0xEga3/CGuGjcPyMjA==, tarball: file:projects/communication.tgz}
|
||||
version: 0.0.0
|
||||
@@ -4684,7 +4671,7 @@ packages:
|
||||
version: 0.0.0
|
||||
|
||||
'@rush-temp/desktop@file:projects/desktop.tgz':
|
||||
resolution: {integrity: sha512-kMVLNqalurSBKidY00TqK2GIBSaEvgAKScF/32N4vsmx+pzMBkE6U63HiW+RxsqVC/ig9QikeohoDXx7Ps9kHg==, tarball: file:projects/desktop.tgz}
|
||||
resolution: {integrity: sha512-EIuP2KmLDWhA4J63srKU8MI9wWCUTpNtCPGaS9P4guXJdH55cV8cqbWYbEYuWqsa28YPPoS1hel73H92Mf7nKw==, tarball: file:projects/desktop.tgz}
|
||||
version: 0.0.0
|
||||
|
||||
'@rush-temp/devmodel-resources@file:projects/devmodel-resources.tgz':
|
||||
@@ -4912,7 +4899,7 @@ packages:
|
||||
version: 0.0.0
|
||||
|
||||
'@rush-temp/love-resources@file:projects/love-resources.tgz':
|
||||
resolution: {integrity: sha512-uB1RrbistrQwA5PIld+dgw+QnpTtS5i6wdiSAemCBe2ay5hTKExnmINPrgM/p7FEkya2sq3IVZXzric/ZYwnng==, tarball: file:projects/love-resources.tgz}
|
||||
resolution: {integrity: sha512-d/rTH5piCl8x3YfKoZ2yIWklH/MU9h8WrccgwV9VS3a1lYQ2i6l+JVpO/hdMGTN6llF3REFNnMYndfcu8Siy4w==, tarball: file:projects/love-resources.tgz}
|
||||
version: 0.0.0
|
||||
|
||||
'@rush-temp/love@file:projects/love.tgz':
|
||||
@@ -4976,7 +4963,7 @@ packages:
|
||||
version: 0.0.0
|
||||
|
||||
'@rush-temp/model-all@file:projects/model-all.tgz':
|
||||
resolution: {integrity: sha512-u5b+jP4WxAVIk6DlawxySE+qDG7iz7YD3dTT3LCgR3Ml5RkMOlej4YtbO0YQmX4wp4l4gMJKdRCgBCpVfytksg==, tarball: file:projects/model-all.tgz}
|
||||
resolution: {integrity: sha512-1IC5c/j2fgqgamtZGZLUBHUuzMaqM6eguG6WTbmdTrSW8YjfUYeutKSRDRykbF6SilzaGS8eKxhNpZTIRT20fA==, tarball: file:projects/model-all.tgz}
|
||||
version: 0.0.0
|
||||
|
||||
'@rush-temp/model-analytics-collector@file:projects/model-analytics-collector.tgz':
|
||||
@@ -5424,7 +5411,7 @@ packages:
|
||||
version: 0.0.0
|
||||
|
||||
'@rush-temp/pod-fulltext@file:projects/pod-fulltext.tgz':
|
||||
resolution: {integrity: sha512-0G3FKVxHWnyPuHWVBiR3Xtns3rLxGTC6ferQCX8NSC3dGOGOvk4TZgB8leohmJavwwxdZQKk1QFJqmXvHux10A==, tarball: file:projects/pod-fulltext.tgz}
|
||||
resolution: {integrity: sha512-kPi4ermTxQtUKZ5xzbJgT07rLj2gG6m4YQgArWiONz47ooSoizi/Sg8Pu2FFyVV0klzQwqUOseuoVqgdySnFNg==, tarball: file:projects/pod-fulltext.tgz}
|
||||
version: 0.0.0
|
||||
|
||||
'@rush-temp/pod-github@file:projects/pod-github.tgz':
|
||||
@@ -5451,10 +5438,6 @@ packages:
|
||||
resolution: {integrity: sha512-5LRHcx/zPNVCJbIBfjigt+of64jfGrH5fjoDyvhASTEJbtG4RGRj8kKZoUI+1JHibDZzcVn2NLc+3nFMhksreA==, tarball: file:projects/pod-media.tgz}
|
||||
version: 0.0.0
|
||||
|
||||
'@rush-temp/pod-msg2file@file:projects/pod-msg2file.tgz':
|
||||
resolution: {integrity: sha512-7b2GrlsmevpahNIADhqNUBpshocrdjlc2L0kf2BAFVSEFpjzUtRju2XqwwoZ2l7xA0Z/b+Un9PHt+XYMWcNzlg==, tarball: file:projects/pod-msg2file.tgz}
|
||||
version: 0.0.0
|
||||
|
||||
'@rush-temp/pod-notification@file:projects/pod-notification.tgz':
|
||||
resolution: {integrity: sha512-1EeImD9ZNRxWq0jDp6o50hOpuoB0sNI54V/V1pm5VONdRF0S2lVzPCr35YehJ5BJcvbhy22au/7rk9XadoYI9w==, tarball: file:projects/pod-notification.tgz}
|
||||
version: 0.0.0
|
||||
@@ -5524,7 +5507,7 @@ packages:
|
||||
version: 0.0.0
|
||||
|
||||
'@rush-temp/presentation@file:projects/presentation.tgz':
|
||||
resolution: {integrity: sha512-nMn7FUSF2F6YuJAstYbvV8Yi9upkKqeSccsU3EOrKjJm1c57sa7x8i4K3Z6l4RoSQk86/F5CrzZVgqIMKCjF0w==, tarball: file:projects/presentation.tgz}
|
||||
resolution: {integrity: sha512-r1AG89qg5hvx+usQjP8KLtkNL/j5z/8OHDcURyAb6yOhWAWNkY5Frs05L9qcPsJoN6raUaZ164jzlk0P2SiPqQ==, tarball: file:projects/presentation.tgz}
|
||||
version: 0.0.0
|
||||
|
||||
'@rush-temp/print-assets@file:projects/print-assets.tgz':
|
||||
@@ -5552,7 +5535,7 @@ packages:
|
||||
version: 0.0.0
|
||||
|
||||
'@rush-temp/prod@file:projects/prod.tgz':
|
||||
resolution: {integrity: sha512-OvmMqiOyuDeKVGHc7g8UxMlvr9WAJsqtqICsv6kcx9vSvB7bB0iXOLY9CevyOOBDk5Y6OvRKWJKeY9pif+pjMg==, tarball: file:projects/prod.tgz}
|
||||
resolution: {integrity: sha512-LkECK91CfEPLwDVaCQWN4XSeBlM1jXqboKuXtu7ftZkgG66wtt1/AlVDqwRY6o1iuvVNvuTJBSp03DMfDzBIKw==, tarball: file:projects/prod.tgz}
|
||||
version: 0.0.0
|
||||
|
||||
'@rush-temp/products-assets@file:projects/products-assets.tgz':
|
||||
@@ -5804,7 +5787,7 @@ packages:
|
||||
version: 0.0.0
|
||||
|
||||
'@rush-temp/server-indexer@file:projects/server-indexer.tgz':
|
||||
resolution: {integrity: sha512-OYx2FWCD6jtTnsKmYnhhS3BsvhtCN9pIYkvIoSe0mzZi3xEAhnwu2T59+JpZgRO1fvD1yVxBy7nw0hyFtz03Hg==, tarball: file:projects/server-indexer.tgz}
|
||||
resolution: {integrity: sha512-InjSApXF2jmClorioQIIV+JxbKtuYjl3a9i4eetEWuG2z7R+Ya7L6f98cqhEhYkdA4hM5TTZYbS+BcRLWlSqQg==, tarball: file:projects/server-indexer.tgz}
|
||||
version: 0.0.0
|
||||
|
||||
'@rush-temp/server-inventory-resources@file:projects/server-inventory-resources.tgz':
|
||||
@@ -6068,7 +6051,7 @@ packages:
|
||||
version: 0.0.0
|
||||
|
||||
'@rush-temp/text-core@file:projects/text-core.tgz':
|
||||
resolution: {integrity: sha512-WezIZjq0OzkKSAy6wMqDFrvEsZ0HlLXzmctJ8w/io4+ywOUO2Cb9ZDXn80wwr5t3TFdb2rvfg1lMM5xDuC+RfA==, tarball: file:projects/text-core.tgz}
|
||||
resolution: {integrity: sha512-wSRif2622acwkDHy2FtsEDhnDTsl8hydf/MFYSgAri0l5sQ6qOPHLuh8FZ2yf81EWTLfK/iuez70cAlYwkypMw==, tarball: file:projects/text-core.tgz}
|
||||
version: 0.0.0
|
||||
|
||||
'@rush-temp/text-editor-assets@file:projects/text-editor-assets.tgz':
|
||||
@@ -6076,7 +6059,7 @@ packages:
|
||||
version: 0.0.0
|
||||
|
||||
'@rush-temp/text-editor-resources@file:projects/text-editor-resources.tgz':
|
||||
resolution: {integrity: sha512-vb8UXE1DL6LyA3M51Pj4uKcuxdoaPEYFcNa/P7i0zR8XsebrOJZWNAhvtIsH1NKmNycLBNhvmTxKKEM1XEaiLA==, tarball: file:projects/text-editor-resources.tgz}
|
||||
resolution: {integrity: sha512-2aRRXC4H573M3+OR/vRe5pGoA4tAB3jEbufqAe4/EgKkUHvRlGVZBBUyDAfYtsIHQd7IrAEEVK1NQoC6iZE1Lg==, tarball: file:projects/text-editor-resources.tgz}
|
||||
version: 0.0.0
|
||||
|
||||
'@rush-temp/text-editor@file:projects/text-editor.tgz':
|
||||
@@ -6092,7 +6075,7 @@ packages:
|
||||
version: 0.0.0
|
||||
|
||||
'@rush-temp/text-ydoc@file:projects/text-ydoc.tgz':
|
||||
resolution: {integrity: sha512-vKLzX1+/6aZmMzsR213dQDXADDE4cDw3M2Ofkn3r7QKV6GidHGHipTlBO4jjsCbYK4G0L1QIf8ZHi7QhpE8icQ==, tarball: file:projects/text-ydoc.tgz}
|
||||
resolution: {integrity: sha512-EdNN9bQUsIb2qPGpotFbm5xIQDH1GJilcCiV66EVTlrsRkQSAjqVDPZyhQALE8200gR8K7OCSayKoQPnb/nPEA==, tarball: file:projects/text-ydoc.tgz}
|
||||
version: 0.0.0
|
||||
|
||||
'@rush-temp/text@file:projects/text.tgz':
|
||||
@@ -6116,7 +6099,7 @@ packages:
|
||||
version: 0.0.0
|
||||
|
||||
'@rush-temp/tool@file:projects/tool.tgz':
|
||||
resolution: {integrity: sha512-bsqWzFX5qr02kfG1zQZOgHsGWzR/Z66VC16ZoQnXs6mkdHFSmQ2p2l8eNL/aYPu2qfrRkMNAnI8cu6aULfKuDg==, tarball: file:projects/tool.tgz}
|
||||
resolution: {integrity: sha512-FwR6SJSyoZvzT/0uIoItaG+S6N+ofaqtNJ4ZXRJ8f454jzOGrzjgjL+BupOX+xJCY4w9zNfpMOIke7e9LPX5pA==, tarball: file:projects/tool.tgz}
|
||||
version: 0.0.0
|
||||
|
||||
'@rush-temp/tracker-assets@file:projects/tracker-assets.tgz':
|
||||
@@ -7176,9 +7159,6 @@ packages:
|
||||
'@types/mysql@2.15.27':
|
||||
resolution: {integrity: sha512-YfWiV16IY0OeBfBCk8+hXKmdTKrKlwKN1MNKAPBu5JYxLwBEZl7QzeEpGnlZb3VMGJrrGmB84gXiH+ofs/TezA==}
|
||||
|
||||
'@types/node-cron@3.0.11':
|
||||
resolution: {integrity: sha512-0ikrnug3/IyneSHqCBeslAhlK2aBfYek1fGo4bP4QnZPmiqSGRK+Oy7ZMisLWkesffJvQ1cqAcBnJC+8+nxIAg==}
|
||||
|
||||
'@types/node-fetch@2.6.12':
|
||||
resolution: {integrity: sha512-8nneRWKCg3rMtF69nLQJnOYUcbafYeFSjqkw3jCRLsqkWFlHaoQrr5mXmofFGOx3DKn7UfmBMyov8ySvLRVldA==}
|
||||
|
||||
@@ -10177,6 +10157,9 @@ packages:
|
||||
has-unicode@2.0.1:
|
||||
resolution: {integrity: sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==}
|
||||
|
||||
hash-it@6.0.0:
|
||||
resolution: {integrity: sha512-KHzmSFx1KwyMPw0kXeeUD752q/Kfbzhy6dAZrjXV9kAIXGqzGvv8vhkUqj+2MGZldTo0IBpw6v7iWE7uxsvH0w==}
|
||||
|
||||
hasown@2.0.1:
|
||||
resolution: {integrity: sha512-1/th4MHjnwncwXsIW6QMzlvYL9kG5e/CpVvLRZe4XPa8TOUNbCELqmvhDmnkNsAjwaG4+I8gJJL0JBvTTLO9qA==}
|
||||
engines: {node: '>= 0.4'}
|
||||
@@ -11170,18 +11153,8 @@ packages:
|
||||
lexorank@1.0.5:
|
||||
resolution: {integrity: sha512-K1B/Yr/gIU0wm68hk/yB0p/mv6xM3ShD5aci42vOwcjof8slG8Kpo3Q7+1WTv7DaRHKWRgLPqrFDt+4GtuFAtA==}
|
||||
|
||||
lib0@0.2.109:
|
||||
resolution: {integrity: sha512-jP0gbnyW0kwlx1Atc4dcHkBbrVAkdHjuyHxtClUPYla7qCmwIif1qZ6vQeJdR5FrOVdn26HvQT0ko01rgW7/Xw==}
|
||||
engines: {node: '>=16'}
|
||||
hasBin: true
|
||||
|
||||
lib0@0.2.89:
|
||||
resolution: {integrity: sha512-5j19vcCjsQhvLG6mcDD+nprtJUCbmqLz5Hzt5xgi9SV6RIW/Dty7ZkVZHGBuPOADMKjQuKDvuQTH495wsmw8DQ==}
|
||||
engines: {node: '>=16'}
|
||||
hasBin: true
|
||||
|
||||
lib0@0.2.99:
|
||||
resolution: {integrity: sha512-vwztYuUf1uf/1zQxfzRfO5yzfNKhTtgOByCruuiQQxWQXnPb8Itaube5ylofcV0oM0aKal9Mv+S1s1Ky0UYP1w==}
|
||||
lib0@0.2.114:
|
||||
resolution: {integrity: sha512-gcxmNFzA4hv8UYi8j43uPlQ7CGcyMJ2KQb5kZASw6SnAKAf10hK12i2fjrS3Cl/ugZa5Ui6WwIu1/6MIXiHttQ==}
|
||||
engines: {node: '>=16'}
|
||||
hasBin: true
|
||||
|
||||
@@ -11808,10 +11781,6 @@ packages:
|
||||
node-api-version@0.2.0:
|
||||
resolution: {integrity: sha512-fthTTsi8CxaBXMaBAD7ST2uylwvsnYxh2PfaScwpMhos6KlSFajXQPcM4ogNE1q2s3Lbz9GCGqeIHC+C6OZnKg==}
|
||||
|
||||
node-cron@3.0.3:
|
||||
resolution: {integrity: sha512-dOal67//nohNgYWb+nWmg5dkFdIwDm8EpeGYMekPMrngV3637lqnX0lbUcCtgibHTz6SEz7DAIjKvKDFYCnO1A==}
|
||||
engines: {node: '>=6.0.0'}
|
||||
|
||||
node-domexception@1.0.0:
|
||||
resolution: {integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==}
|
||||
engines: {node: '>=10.5.0'}
|
||||
@@ -14469,8 +14438,8 @@ packages:
|
||||
peerDependencies:
|
||||
yjs: ^13.0.0
|
||||
|
||||
y-prosemirror@1.3.6:
|
||||
resolution: {integrity: sha512-vtS2rv8+ll/TBQRqwUiqflgSuN/DhfvUQX0r5O3o5i0pO6K4pSNgFtVkOKtNWPBVkS6l9BDQjbtnDNftZnxq7Q==}
|
||||
y-prosemirror@1.3.7:
|
||||
resolution: {integrity: sha512-NpM99WSdD4Fx4if5xOMDpPtU3oAmTSjlzh5U4353ABbRHl1HtAFUx6HlebLZfyFxXN9jzKMDkVbcRjqOZVkYQg==}
|
||||
engines: {node: '>=16.0.0', npm: '>=8.0.0'}
|
||||
peerDependencies:
|
||||
prosemirror-model: ^1.7.1
|
||||
@@ -14538,8 +14507,8 @@ packages:
|
||||
yauzl@2.10.0:
|
||||
resolution: {integrity: sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==}
|
||||
|
||||
yjs@13.6.23:
|
||||
resolution: {integrity: sha512-ExtnT5WIOVpkL56bhLeisG/N5c4fmzKn4k0ROVfJa5TY2QHbH7F0Wu2T5ZhR7ErsFWQEFafyrnSI8TPKVF9Few==}
|
||||
yjs@13.6.27:
|
||||
resolution: {integrity: sha512-OIDwaflOaq4wC6YlPBy2L6ceKeKuF7DeTxx+jPzv1FHn9tCZ0ZwSRnUBxD05E3yed46fv/FWJbvR+Ud7x0L7zw==}
|
||||
engines: {node: '>=16.0.0', npm: '>=8.0.0'}
|
||||
|
||||
ylru@1.3.2:
|
||||
@@ -15620,41 +15589,41 @@ snapshots:
|
||||
|
||||
'@hocuspocus/common@2.15.2':
|
||||
dependencies:
|
||||
lib0: 0.2.99
|
||||
lib0: 0.2.114
|
||||
|
||||
'@hocuspocus/provider@2.15.2(bufferutil@4.0.8)(utf-8-validate@6.0.4)(y-protocols@1.0.6(yjs@13.6.23))(yjs@13.6.23)':
|
||||
'@hocuspocus/provider@2.15.2(bufferutil@4.0.8)(utf-8-validate@6.0.4)(y-protocols@1.0.6(yjs@13.6.27))(yjs@13.6.27)':
|
||||
dependencies:
|
||||
'@hocuspocus/common': 2.15.2
|
||||
'@lifeomic/attempt': 3.0.3
|
||||
lib0: 0.2.99
|
||||
lib0: 0.2.114
|
||||
ws: 8.18.2(bufferutil@4.0.8)(utf-8-validate@6.0.4)
|
||||
y-protocols: 1.0.6(yjs@13.6.23)
|
||||
yjs: 13.6.23
|
||||
y-protocols: 1.0.6(yjs@13.6.27)
|
||||
yjs: 13.6.27
|
||||
transitivePeerDependencies:
|
||||
- bufferutil
|
||||
- utf-8-validate
|
||||
|
||||
'@hocuspocus/server@2.15.2(bufferutil@4.0.8)(utf-8-validate@6.0.4)(y-protocols@1.0.6(yjs@13.6.23))(yjs@13.6.23)':
|
||||
'@hocuspocus/server@2.15.2(bufferutil@4.0.8)(utf-8-validate@6.0.4)(y-protocols@1.0.6(yjs@13.6.27))(yjs@13.6.27)':
|
||||
dependencies:
|
||||
'@hocuspocus/common': 2.15.2
|
||||
async-lock: 1.4.1
|
||||
kleur: 4.1.5
|
||||
lib0: 0.2.99
|
||||
lib0: 0.2.114
|
||||
uuid: 11.1.0
|
||||
ws: 8.18.2(bufferutil@4.0.8)(utf-8-validate@6.0.4)
|
||||
y-protocols: 1.0.6(yjs@13.6.23)
|
||||
yjs: 13.6.23
|
||||
y-protocols: 1.0.6(yjs@13.6.27)
|
||||
yjs: 13.6.27
|
||||
transitivePeerDependencies:
|
||||
- bufferutil
|
||||
- utf-8-validate
|
||||
|
||||
'@hocuspocus/transformer@2.15.2(@tiptap/core@2.11.7(@tiptap/pm@2.11.7))(@tiptap/pm@2.11.7)(y-prosemirror@1.3.6(prosemirror-model@1.24.1)(prosemirror-state@1.4.3)(prosemirror-view@1.37.2)(y-protocols@1.0.6(yjs@13.6.23))(yjs@13.6.23))(yjs@13.6.23)':
|
||||
'@hocuspocus/transformer@2.15.2(@tiptap/core@2.11.7(@tiptap/pm@2.11.7))(@tiptap/pm@2.11.7)(y-prosemirror@1.3.7(prosemirror-model@1.24.1)(prosemirror-state@1.4.3)(prosemirror-view@1.37.2)(y-protocols@1.0.6(yjs@13.6.27))(yjs@13.6.27))(yjs@13.6.27)':
|
||||
dependencies:
|
||||
'@tiptap/core': 2.11.7(@tiptap/pm@2.11.7)
|
||||
'@tiptap/pm': 2.11.7
|
||||
'@tiptap/starter-kit': 2.11.7
|
||||
y-prosemirror: 1.3.6(prosemirror-model@1.24.1)(prosemirror-state@1.4.3)(prosemirror-view@1.37.2)(y-protocols@1.0.6(yjs@13.6.23))(yjs@13.6.23)
|
||||
yjs: 13.6.23
|
||||
y-prosemirror: 1.3.7(prosemirror-model@1.24.1)(prosemirror-state@1.4.3)(prosemirror-view@1.37.2)(y-protocols@1.0.6(yjs@13.6.27))(yjs@13.6.27)
|
||||
yjs: 13.6.27
|
||||
|
||||
'@humanwhocodes/config-array@0.11.14':
|
||||
dependencies:
|
||||
@@ -18947,7 +18916,7 @@ snapshots:
|
||||
prettier: 3.2.5
|
||||
ts-jest: 29.1.2(@babel/core@7.23.9)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.23.9))(esbuild@0.25.9)(jest@29.7.0(@types/node@22.15.29)(ts-node@10.9.2(@swc/core@1.13.5)(@types/node@22.15.29)(typescript@5.8.3)))(typescript@5.8.3)
|
||||
typescript: 5.8.3
|
||||
yjs: 13.6.23
|
||||
yjs: 13.6.27
|
||||
transitivePeerDependencies:
|
||||
- '@babel/core'
|
||||
- '@jest/types'
|
||||
@@ -18984,10 +18953,10 @@ snapshots:
|
||||
- supports-color
|
||||
- ts-node
|
||||
|
||||
'@rush-temp/collaborator@file:projects/collaborator.tgz(@babel/core@7.23.9)(@jest/types@29.6.3)(@swc/core@1.13.5)(@tiptap/pm@2.11.7)(babel-jest@29.7.0(@babel/core@7.23.9))(bufferutil@4.0.8)(gcp-metadata@5.3.0(encoding@0.1.13))(snappy@7.2.2)(socks@2.8.3)(utf-8-validate@6.0.4)(y-prosemirror@1.3.6(prosemirror-model@1.24.1)(prosemirror-state@1.4.3)(prosemirror-view@1.37.2)(y-protocols@1.0.6(yjs@13.6.23))(yjs@13.6.23))(y-protocols@1.0.6(yjs@13.6.23))':
|
||||
'@rush-temp/collaborator@file:projects/collaborator.tgz(@babel/core@7.23.9)(@jest/types@29.6.3)(@swc/core@1.13.5)(@tiptap/pm@2.11.7)(babel-jest@29.7.0(@babel/core@7.23.9))(bufferutil@4.0.8)(gcp-metadata@5.3.0(encoding@0.1.13))(snappy@7.2.2)(socks@2.8.3)(utf-8-validate@6.0.4)(y-prosemirror@1.3.7(prosemirror-model@1.24.1)(prosemirror-state@1.4.3)(prosemirror-view@1.37.2)(y-protocols@1.0.6(yjs@13.6.27))(yjs@13.6.27))(y-protocols@1.0.6(yjs@13.6.27))':
|
||||
dependencies:
|
||||
'@hocuspocus/server': 2.15.2(bufferutil@4.0.8)(utf-8-validate@6.0.4)(y-protocols@1.0.6(yjs@13.6.23))(yjs@13.6.23)
|
||||
'@hocuspocus/transformer': 2.15.2(@tiptap/core@2.11.7(@tiptap/pm@2.11.7))(@tiptap/pm@2.11.7)(y-prosemirror@1.3.6(prosemirror-model@1.24.1)(prosemirror-state@1.4.3)(prosemirror-view@1.37.2)(y-protocols@1.0.6(yjs@13.6.23))(yjs@13.6.23))(yjs@13.6.23)
|
||||
'@hocuspocus/server': 2.15.2(bufferutil@4.0.8)(utf-8-validate@6.0.4)(y-protocols@1.0.6(yjs@13.6.27))(yjs@13.6.27)
|
||||
'@hocuspocus/transformer': 2.15.2(@tiptap/core@2.11.7(@tiptap/pm@2.11.7))(@tiptap/pm@2.11.7)(y-prosemirror@1.3.7(prosemirror-model@1.24.1)(prosemirror-state@1.4.3)(prosemirror-view@1.37.2)(y-protocols@1.0.6(yjs@13.6.27))(yjs@13.6.27))(yjs@13.6.27)
|
||||
'@tiptap/core': 2.11.7(@tiptap/pm@2.11.7)
|
||||
'@tiptap/html': 2.11.7(@tiptap/core@2.11.7(@tiptap/pm@2.11.7))(@tiptap/pm@2.11.7)
|
||||
'@types/body-parser': 1.19.5
|
||||
@@ -19016,7 +18985,7 @@ snapshots:
|
||||
ts-node: 10.9.2(@swc/core@1.13.5)(@types/node@22.15.29)(typescript@5.8.3)
|
||||
typescript: 5.8.3
|
||||
ws: 8.18.2(bufferutil@4.0.8)(utf-8-validate@6.0.4)
|
||||
yjs: 13.6.23
|
||||
yjs: 13.6.27
|
||||
transitivePeerDependencies:
|
||||
- '@aws-sdk/credential-providers'
|
||||
- '@babel/core'
|
||||
@@ -19286,29 +19255,6 @@ snapshots:
|
||||
- supports-color
|
||||
- ts-node
|
||||
|
||||
'@rush-temp/communication-yaml@file:projects/communication-yaml.tgz(@types/node@22.15.29)(ts-node@10.9.2(@swc/core@1.13.5)(@types/node@22.15.29)(typescript@5.8.3))':
|
||||
dependencies:
|
||||
'@types/js-yaml': 4.0.9
|
||||
'@typescript-eslint/eslint-plugin': 6.21.0(@typescript-eslint/parser@6.21.0(eslint@8.56.0)(typescript@5.8.3))(eslint@8.56.0)(typescript@5.8.3)
|
||||
'@typescript-eslint/parser': 6.21.0(eslint@8.56.0)(typescript@5.8.3)
|
||||
esbuild: 0.25.9
|
||||
esbuild-plugin-copy: 2.1.1(esbuild@0.25.9)
|
||||
eslint: 8.56.0
|
||||
eslint-config-standard-with-typescript: 40.0.0(@typescript-eslint/eslint-plugin@6.21.0(@typescript-eslint/parser@6.21.0(eslint@8.56.0)(typescript@5.8.3))(eslint@8.56.0)(typescript@5.8.3))(eslint-plugin-import@2.29.1(eslint@8.56.0))(eslint-plugin-n@15.7.0(eslint@8.56.0))(eslint-plugin-promise@6.1.1(eslint@8.56.0))(eslint@8.56.0)(typescript@5.8.3)
|
||||
eslint-plugin-import: 2.29.1(eslint@8.56.0)
|
||||
eslint-plugin-n: 15.7.0(eslint@8.56.0)
|
||||
eslint-plugin-promise: 6.1.1(eslint@8.56.0)
|
||||
jest: 29.7.0(@types/node@22.15.29)(ts-node@10.9.2(@swc/core@1.13.5)(@types/node@22.15.29)(typescript@5.8.3))
|
||||
js-yaml: 4.1.0
|
||||
prettier: 3.2.5
|
||||
typescript: 5.8.3
|
||||
transitivePeerDependencies:
|
||||
- '@types/node'
|
||||
- babel-plugin-macros
|
||||
- node-notifier
|
||||
- supports-color
|
||||
- ts-node
|
||||
|
||||
'@rush-temp/communication@file:projects/communication.tgz(@babel/core@7.23.9)(@jest/types@29.6.3)(@types/node@22.15.29)(babel-jest@29.7.0(@babel/core@7.23.9))(esbuild@0.25.9)(ts-node@10.9.2(@swc/core@1.13.5)(@types/node@22.15.29)(typescript@5.8.3))':
|
||||
dependencies:
|
||||
'@types/jest': 29.5.12
|
||||
@@ -25144,7 +25090,7 @@ snapshots:
|
||||
- node-notifier
|
||||
- supports-color
|
||||
|
||||
'@rush-temp/pod-github@file:projects/pod-github.tgz(@babel/core@7.23.9)(@jest/types@29.6.3)(@swc/core@1.13.5)(babel-jest@29.7.0(@babel/core@7.23.9))(bufferutil@4.0.8)(gcp-metadata@5.3.0(encoding@0.1.13))(snappy@7.2.2)(socks@2.8.3)(utf-8-validate@6.0.4)(y-prosemirror@1.3.6(prosemirror-model@1.24.1)(prosemirror-state@1.4.3)(prosemirror-view@1.37.2)(y-protocols@1.0.6(yjs@13.6.23))(yjs@13.6.23))':
|
||||
'@rush-temp/pod-github@file:projects/pod-github.tgz(@babel/core@7.23.9)(@jest/types@29.6.3)(@swc/core@1.13.5)(babel-jest@29.7.0(@babel/core@7.23.9))(bufferutil@4.0.8)(gcp-metadata@5.3.0(encoding@0.1.13))(snappy@7.2.2)(socks@2.8.3)(utf-8-validate@6.0.4)(y-prosemirror@1.3.7(prosemirror-model@1.24.1)(prosemirror-state@1.4.3)(prosemirror-view@1.37.2)(y-protocols@1.0.6(yjs@13.6.27))(yjs@13.6.27))':
|
||||
dependencies:
|
||||
'@octokit/types': 12.6.0
|
||||
'@octokit/webhooks': 12.2.0
|
||||
@@ -25153,8 +25099,8 @@ snapshots:
|
||||
'@tiptap/extension-bubble-menu': 2.11.7(@tiptap/core@2.11.7(@tiptap/pm@2.11.7))(@tiptap/pm@2.11.7)
|
||||
'@tiptap/extension-code': 2.11.7(@tiptap/core@2.11.7(@tiptap/pm@2.11.7))
|
||||
'@tiptap/extension-code-block': 2.11.7(@tiptap/core@2.11.7(@tiptap/pm@2.11.7))(@tiptap/pm@2.11.7)
|
||||
'@tiptap/extension-collaboration': 2.11.7(@tiptap/core@2.11.7(@tiptap/pm@2.11.7))(@tiptap/pm@2.11.7)(y-prosemirror@1.3.6(prosemirror-model@1.24.1)(prosemirror-state@1.4.3)(prosemirror-view@1.37.2)(y-protocols@1.0.6(yjs@13.6.23))(yjs@13.6.23))
|
||||
'@tiptap/extension-collaboration-cursor': 2.11.7(@tiptap/core@2.11.7(@tiptap/pm@2.11.7))(y-prosemirror@1.3.6(prosemirror-model@1.24.1)(prosemirror-state@1.4.3)(prosemirror-view@1.37.2)(y-protocols@1.0.6(yjs@13.6.23))(yjs@13.6.23))
|
||||
'@tiptap/extension-collaboration': 2.11.7(@tiptap/core@2.11.7(@tiptap/pm@2.11.7))(@tiptap/pm@2.11.7)(y-prosemirror@1.3.7(prosemirror-model@1.24.1)(prosemirror-state@1.4.3)(prosemirror-view@1.37.2)(y-protocols@1.0.6(yjs@13.6.27))(yjs@13.6.27))
|
||||
'@tiptap/extension-collaboration-cursor': 2.11.7(@tiptap/core@2.11.7(@tiptap/pm@2.11.7))(y-prosemirror@1.3.7(prosemirror-model@1.24.1)(prosemirror-state@1.4.3)(prosemirror-view@1.37.2)(y-protocols@1.0.6(yjs@13.6.27))(yjs@13.6.27))
|
||||
'@tiptap/extension-gapcursor': 2.11.7(@tiptap/core@2.11.7(@tiptap/pm@2.11.7))(@tiptap/pm@2.11.7)
|
||||
'@tiptap/extension-heading': 2.11.7(@tiptap/core@2.11.7(@tiptap/pm@2.11.7))
|
||||
'@tiptap/extension-highlight': 2.11.7(@tiptap/core@2.11.7(@tiptap/pm@2.11.7))
|
||||
@@ -25435,47 +25381,6 @@ snapshots:
|
||||
- node-notifier
|
||||
- supports-color
|
||||
|
||||
'@rush-temp/pod-msg2file@file:projects/pod-msg2file.tgz(@babel/core@7.23.9)(@jest/types@29.6.3)(@swc/core@1.13.5)(babel-jest@29.7.0(@babel/core@7.23.9))':
|
||||
dependencies:
|
||||
'@tsconfig/node16': 1.0.4
|
||||
'@types/cors': 2.8.17
|
||||
'@types/express': 4.17.21
|
||||
'@types/jest': 29.5.12
|
||||
'@types/js-yaml': 4.0.9
|
||||
'@types/node': 22.15.29
|
||||
'@types/node-cron': 3.0.11
|
||||
'@types/uuid': 8.3.4
|
||||
'@typescript-eslint/eslint-plugin': 6.21.0(@typescript-eslint/parser@6.21.0(eslint@8.56.0)(typescript@5.8.3))(eslint@8.56.0)(typescript@5.8.3)
|
||||
'@typescript-eslint/parser': 6.21.0(eslint@8.56.0)(typescript@5.8.3)
|
||||
cors: 2.8.5
|
||||
dotenv: 16.0.3
|
||||
esbuild: 0.25.9
|
||||
eslint: 8.56.0
|
||||
eslint-config-standard-with-typescript: 40.0.0(@typescript-eslint/eslint-plugin@6.21.0(@typescript-eslint/parser@6.21.0(eslint@8.56.0)(typescript@5.8.3))(eslint@8.56.0)(typescript@5.8.3))(eslint-plugin-import@2.29.1(eslint@8.56.0))(eslint-plugin-n@15.7.0(eslint@8.56.0))(eslint-plugin-promise@6.1.1(eslint@8.56.0))(eslint@8.56.0)(typescript@5.8.3)
|
||||
eslint-plugin-import: 2.29.1(eslint@8.56.0)
|
||||
eslint-plugin-n: 15.7.0(eslint@8.56.0)
|
||||
eslint-plugin-node: 11.1.0(eslint@8.56.0)
|
||||
eslint-plugin-promise: 6.1.1(eslint@8.56.0)
|
||||
express: 4.21.2
|
||||
jest: 29.7.0(@types/node@22.15.29)(ts-node@10.9.2(@swc/core@1.13.5)(@types/node@22.15.29)(typescript@5.8.3))
|
||||
js-yaml: 4.1.0
|
||||
node-cron: 3.0.3
|
||||
postgres: 3.4.7
|
||||
prettier: 3.2.5
|
||||
ts-jest: 29.1.2(@babel/core@7.23.9)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.23.9))(esbuild@0.25.9)(jest@29.7.0(@types/node@22.15.29)(ts-node@10.9.2(@swc/core@1.13.5)(@types/node@22.15.29)(typescript@5.8.3)))(typescript@5.8.3)
|
||||
ts-node: 10.9.2(@swc/core@1.13.5)(@types/node@22.15.29)(typescript@5.8.3)
|
||||
typescript: 5.8.3
|
||||
uuid: 8.3.2
|
||||
transitivePeerDependencies:
|
||||
- '@babel/core'
|
||||
- '@jest/types'
|
||||
- '@swc/core'
|
||||
- '@swc/wasm'
|
||||
- babel-jest
|
||||
- babel-plugin-macros
|
||||
- node-notifier
|
||||
- supports-color
|
||||
|
||||
'@rush-temp/pod-notification@file:projects/pod-notification.tgz(@babel/core@7.23.9)(@jest/types@29.6.3)(@swc/core@1.13.5)(babel-jest@29.7.0(@babel/core@7.23.9))':
|
||||
dependencies:
|
||||
'@tsconfig/node16': 1.0.4
|
||||
@@ -26133,7 +26038,7 @@ snapshots:
|
||||
ts-jest: 29.1.2(@babel/core@7.23.9)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.23.9))(esbuild@0.25.9)(jest@29.7.0(@types/node@22.15.29)(ts-node@10.9.2(@swc/core@1.13.5)(@types/node@22.15.29)(typescript@5.8.3)))(typescript@5.8.3)
|
||||
typescript: 5.8.3
|
||||
uuid: 8.3.2
|
||||
yjs: 13.6.23
|
||||
yjs: 13.6.27
|
||||
transitivePeerDependencies:
|
||||
- '@babel/core'
|
||||
- '@jest/types'
|
||||
@@ -29891,6 +29796,7 @@ snapshots:
|
||||
eslint-plugin-n: 15.7.0(eslint@8.56.0)
|
||||
eslint-plugin-promise: 6.1.1(eslint@8.56.0)
|
||||
fast-equals: 5.2.2
|
||||
hash-it: 6.0.0
|
||||
jest: 29.7.0(@types/node@22.15.29)(ts-node@10.9.2(@swc/core@1.13.5)(@types/node@22.15.29)(typescript@5.8.3))
|
||||
jest-environment-jsdom: 29.7.0(bufferutil@4.0.8)(utf-8-validate@6.0.4)
|
||||
prettier: 3.2.5
|
||||
@@ -29937,15 +29843,15 @@ snapshots:
|
||||
|
||||
'@rush-temp/text-editor-resources@file:projects/text-editor-resources.tgz(@babel/core@7.23.9)(@jest/types@29.6.3)(@types/node@22.15.29)(babel-jest@29.7.0(@babel/core@7.23.9))(bufferutil@4.0.8)(esbuild@0.25.9)(highlight.js@11.11.1)(postcss-load-config@4.0.2(postcss@8.5.3)(ts-node@10.9.2(@swc/core@1.13.5)(@types/node@22.15.29)(typescript@5.8.3)))(postcss@8.5.3)(prosemirror-inputrules@1.4.0)(prosemirror-model@1.24.1)(prosemirror-state@1.4.3)(prosemirror-view@1.37.2)(ts-node@10.9.2(@swc/core@1.13.5)(@types/node@22.15.29)(typescript@5.8.3))(utf-8-validate@6.0.4)':
|
||||
dependencies:
|
||||
'@hocuspocus/provider': 2.15.2(bufferutil@4.0.8)(utf-8-validate@6.0.4)(y-protocols@1.0.6(yjs@13.6.23))(yjs@13.6.23)
|
||||
'@hocuspocus/provider': 2.15.2(bufferutil@4.0.8)(utf-8-validate@6.0.4)(y-protocols@1.0.6(yjs@13.6.27))(yjs@13.6.27)
|
||||
'@tiptap/core': 2.11.7(@tiptap/pm@2.11.7)
|
||||
'@tiptap/extension-bubble-menu': 2.11.7(@tiptap/core@2.11.7(@tiptap/pm@2.11.7))(@tiptap/pm@2.11.7)
|
||||
'@tiptap/extension-bullet-list': 2.11.7(@tiptap/core@2.11.7(@tiptap/pm@2.11.7))
|
||||
'@tiptap/extension-code': 2.11.7(@tiptap/core@2.11.7(@tiptap/pm@2.11.7))
|
||||
'@tiptap/extension-code-block': 2.11.7(@tiptap/core@2.11.7(@tiptap/pm@2.11.7))(@tiptap/pm@2.11.7)
|
||||
'@tiptap/extension-code-block-lowlight': 2.11.7(@tiptap/core@2.11.7(@tiptap/pm@2.11.7))(@tiptap/extension-code-block@2.11.7(@tiptap/core@2.11.7(@tiptap/pm@2.11.7))(@tiptap/pm@2.11.7))(@tiptap/pm@2.11.7)(highlight.js@11.11.1)(lowlight@3.3.0)
|
||||
'@tiptap/extension-collaboration': 2.11.7(@tiptap/core@2.11.7(@tiptap/pm@2.11.7))(@tiptap/pm@2.11.7)(y-prosemirror@1.3.6(prosemirror-model@1.24.1)(prosemirror-state@1.4.3)(prosemirror-view@1.37.2)(y-protocols@1.0.6(yjs@13.6.23))(yjs@13.6.23))
|
||||
'@tiptap/extension-collaboration-cursor': 2.11.7(@tiptap/core@2.11.7(@tiptap/pm@2.11.7))(y-prosemirror@1.3.6(prosemirror-model@1.24.1)(prosemirror-state@1.4.3)(prosemirror-view@1.37.2)(y-protocols@1.0.6(yjs@13.6.23))(yjs@13.6.23))
|
||||
'@tiptap/extension-collaboration': 2.11.7(@tiptap/core@2.11.7(@tiptap/pm@2.11.7))(@tiptap/pm@2.11.7)(y-prosemirror@1.3.7(prosemirror-model@1.24.1)(prosemirror-state@1.4.3)(prosemirror-view@1.37.2)(y-protocols@1.0.6(yjs@13.6.27))(yjs@13.6.27))
|
||||
'@tiptap/extension-collaboration-cursor': 2.11.7(@tiptap/core@2.11.7(@tiptap/pm@2.11.7))(y-prosemirror@1.3.7(prosemirror-model@1.24.1)(prosemirror-state@1.4.3)(prosemirror-view@1.37.2)(y-protocols@1.0.6(yjs@13.6.27))(yjs@13.6.27))
|
||||
'@tiptap/extension-hard-break': 2.11.7(@tiptap/core@2.11.7(@tiptap/pm@2.11.7))
|
||||
'@tiptap/extension-heading': 2.11.7(@tiptap/core@2.11.7(@tiptap/pm@2.11.7))
|
||||
'@tiptap/extension-highlight': 2.11.7(@tiptap/core@2.11.7(@tiptap/pm@2.11.7))
|
||||
@@ -29978,7 +29884,7 @@ snapshots:
|
||||
eslint-plugin-svelte: 2.35.1(eslint@8.56.0)(svelte@4.2.20)(ts-node@10.9.2(@swc/core@1.13.5)(@types/node@22.15.29)(typescript@5.8.3))
|
||||
fast-equals: 5.2.2
|
||||
jest: 29.7.0(@types/node@22.15.29)(ts-node@10.9.2(@swc/core@1.13.5)(@types/node@22.15.29)(typescript@5.8.3))
|
||||
lib0: 0.2.89
|
||||
lib0: 0.2.114
|
||||
lowlight: 3.3.0
|
||||
mermaid: 11.4.1
|
||||
prettier: 3.2.5
|
||||
@@ -29995,11 +29901,11 @@ snapshots:
|
||||
tippy.js: 6.3.7
|
||||
ts-jest: 29.1.2(@babel/core@7.23.9)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.23.9))(esbuild@0.25.9)(jest@29.7.0(@types/node@22.15.29)(ts-node@10.9.2(@swc/core@1.13.5)(@types/node@22.15.29)(typescript@5.8.3)))(typescript@5.8.3)
|
||||
typescript: 5.8.3
|
||||
y-indexeddb: 9.0.12(yjs@13.6.23)
|
||||
y-prosemirror: 1.3.6(prosemirror-model@1.24.1)(prosemirror-state@1.4.3)(prosemirror-view@1.37.2)(y-protocols@1.0.6(yjs@13.6.23))(yjs@13.6.23)
|
||||
y-protocols: 1.0.6(yjs@13.6.23)
|
||||
y-websocket: 2.1.0(yjs@13.6.23)
|
||||
yjs: 13.6.23
|
||||
y-indexeddb: 9.0.12(yjs@13.6.27)
|
||||
y-prosemirror: 1.3.7(prosemirror-model@1.24.1)(prosemirror-state@1.4.3)(prosemirror-view@1.37.2)(y-protocols@1.0.6(yjs@13.6.27))(yjs@13.6.27)
|
||||
y-protocols: 1.0.6(yjs@13.6.27)
|
||||
y-websocket: 2.1.0(yjs@13.6.27)
|
||||
yjs: 13.6.27
|
||||
transitivePeerDependencies:
|
||||
- '@babel/core'
|
||||
- '@jest/types'
|
||||
@@ -30108,7 +30014,7 @@ snapshots:
|
||||
- supports-color
|
||||
- ts-node
|
||||
|
||||
'@rush-temp/text-ydoc@file:projects/text-ydoc.tgz(@babel/core@7.23.9)(@jest/types@29.6.3)(@types/node@22.15.29)(babel-jest@29.7.0(@babel/core@7.23.9))(bufferutil@4.0.8)(esbuild@0.25.9)(prosemirror-model@1.24.1)(prosemirror-state@1.4.3)(prosemirror-view@1.37.2)(ts-node@10.9.2(@swc/core@1.13.5)(@types/node@22.15.29)(typescript@5.8.3))(utf-8-validate@6.0.4)(y-protocols@1.0.6(yjs@13.6.23))':
|
||||
'@rush-temp/text-ydoc@file:projects/text-ydoc.tgz(@babel/core@7.23.9)(@jest/types@29.6.3)(@types/node@22.15.29)(babel-jest@29.7.0(@babel/core@7.23.9))(bufferutil@4.0.8)(esbuild@0.25.9)(prosemirror-model@1.24.1)(prosemirror-state@1.4.3)(prosemirror-view@1.37.2)(ts-node@10.9.2(@swc/core@1.13.5)(@types/node@22.15.29)(typescript@5.8.3))(utf-8-validate@6.0.4)(y-protocols@1.0.6(yjs@13.6.27))':
|
||||
dependencies:
|
||||
'@types/jest': 29.5.12
|
||||
'@typescript-eslint/eslint-plugin': 6.21.0(@typescript-eslint/parser@6.21.0(eslint@8.56.0)(typescript@5.8.3))(eslint@8.56.0)(typescript@5.8.3)
|
||||
@@ -30121,11 +30027,12 @@ snapshots:
|
||||
fast-equals: 5.2.2
|
||||
jest: 29.7.0(@types/node@22.15.29)(ts-node@10.9.2(@swc/core@1.13.5)(@types/node@22.15.29)(typescript@5.8.3))
|
||||
jest-environment-jsdom: 29.7.0(bufferutil@4.0.8)(utf-8-validate@6.0.4)
|
||||
lib0: 0.2.114
|
||||
prettier: 3.2.5
|
||||
ts-jest: 29.1.2(@babel/core@7.23.9)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.23.9))(esbuild@0.25.9)(jest@29.7.0(@types/node@22.15.29)(ts-node@10.9.2(@swc/core@1.13.5)(@types/node@22.15.29)(typescript@5.8.3)))(typescript@5.8.3)
|
||||
typescript: 5.8.3
|
||||
y-prosemirror: 1.3.6(prosemirror-model@1.24.1)(prosemirror-state@1.4.3)(prosemirror-view@1.37.2)(y-protocols@1.0.6(yjs@13.6.23))(yjs@13.6.23)
|
||||
yjs: 13.6.23
|
||||
y-prosemirror: 1.3.7(prosemirror-model@1.24.1)(prosemirror-state@1.4.3)(prosemirror-view@1.37.2)(y-protocols@1.0.6(yjs@13.6.27))(yjs@13.6.27)
|
||||
yjs: 13.6.27
|
||||
transitivePeerDependencies:
|
||||
- '@babel/core'
|
||||
- '@jest/types'
|
||||
@@ -30348,6 +30255,7 @@ snapshots:
|
||||
'@elastic/elasticsearch': 7.17.14
|
||||
'@faker-js/faker': 8.4.1
|
||||
'@types/jest': 29.5.12
|
||||
'@types/js-yaml': 4.0.9
|
||||
'@types/mime-types': 2.1.4
|
||||
'@types/minio': 7.0.18
|
||||
'@types/node': 22.15.29
|
||||
@@ -31519,16 +31427,16 @@ snapshots:
|
||||
dependencies:
|
||||
'@tiptap/core': 2.11.7(@tiptap/pm@2.11.7)
|
||||
|
||||
'@tiptap/extension-collaboration-cursor@2.11.7(@tiptap/core@2.11.7(@tiptap/pm@2.11.7))(y-prosemirror@1.3.6(prosemirror-model@1.24.1)(prosemirror-state@1.4.3)(prosemirror-view@1.37.2)(y-protocols@1.0.6(yjs@13.6.23))(yjs@13.6.23))':
|
||||
'@tiptap/extension-collaboration-cursor@2.11.7(@tiptap/core@2.11.7(@tiptap/pm@2.11.7))(y-prosemirror@1.3.7(prosemirror-model@1.24.1)(prosemirror-state@1.4.3)(prosemirror-view@1.37.2)(y-protocols@1.0.6(yjs@13.6.27))(yjs@13.6.27))':
|
||||
dependencies:
|
||||
'@tiptap/core': 2.11.7(@tiptap/pm@2.11.7)
|
||||
y-prosemirror: 1.3.6(prosemirror-model@1.24.1)(prosemirror-state@1.4.3)(prosemirror-view@1.37.2)(y-protocols@1.0.6(yjs@13.6.23))(yjs@13.6.23)
|
||||
y-prosemirror: 1.3.7(prosemirror-model@1.24.1)(prosemirror-state@1.4.3)(prosemirror-view@1.37.2)(y-protocols@1.0.6(yjs@13.6.27))(yjs@13.6.27)
|
||||
|
||||
'@tiptap/extension-collaboration@2.11.7(@tiptap/core@2.11.7(@tiptap/pm@2.11.7))(@tiptap/pm@2.11.7)(y-prosemirror@1.3.6(prosemirror-model@1.24.1)(prosemirror-state@1.4.3)(prosemirror-view@1.37.2)(y-protocols@1.0.6(yjs@13.6.23))(yjs@13.6.23))':
|
||||
'@tiptap/extension-collaboration@2.11.7(@tiptap/core@2.11.7(@tiptap/pm@2.11.7))(@tiptap/pm@2.11.7)(y-prosemirror@1.3.7(prosemirror-model@1.24.1)(prosemirror-state@1.4.3)(prosemirror-view@1.37.2)(y-protocols@1.0.6(yjs@13.6.27))(yjs@13.6.27))':
|
||||
dependencies:
|
||||
'@tiptap/core': 2.11.7(@tiptap/pm@2.11.7)
|
||||
'@tiptap/pm': 2.11.7
|
||||
y-prosemirror: 1.3.6(prosemirror-model@1.24.1)(prosemirror-state@1.4.3)(prosemirror-view@1.37.2)(y-protocols@1.0.6(yjs@13.6.23))(yjs@13.6.23)
|
||||
y-prosemirror: 1.3.7(prosemirror-model@1.24.1)(prosemirror-state@1.4.3)(prosemirror-view@1.37.2)(y-protocols@1.0.6(yjs@13.6.27))(yjs@13.6.27)
|
||||
|
||||
'@tiptap/extension-document@2.11.7(@tiptap/core@2.11.7(@tiptap/pm@2.11.7))':
|
||||
dependencies:
|
||||
@@ -32146,8 +32054,6 @@ snapshots:
|
||||
dependencies:
|
||||
'@types/node': 22.15.29
|
||||
|
||||
'@types/node-cron@3.0.11': {}
|
||||
|
||||
'@types/node-fetch@2.6.12':
|
||||
dependencies:
|
||||
'@types/node': 22.15.29
|
||||
@@ -35811,6 +35717,8 @@ snapshots:
|
||||
|
||||
has-unicode@2.0.1: {}
|
||||
|
||||
hash-it@6.0.0: {}
|
||||
|
||||
hasown@2.0.1:
|
||||
dependencies:
|
||||
function-bind: 1.1.2
|
||||
@@ -37085,15 +36993,7 @@ snapshots:
|
||||
|
||||
lexorank@1.0.5: {}
|
||||
|
||||
lib0@0.2.109:
|
||||
dependencies:
|
||||
isomorphic.js: 0.2.5
|
||||
|
||||
lib0@0.2.89:
|
||||
dependencies:
|
||||
isomorphic.js: 0.2.5
|
||||
|
||||
lib0@0.2.99:
|
||||
lib0@0.2.114:
|
||||
dependencies:
|
||||
isomorphic.js: 0.2.5
|
||||
|
||||
@@ -37734,10 +37634,6 @@ snapshots:
|
||||
dependencies:
|
||||
semver: 7.7.2
|
||||
|
||||
node-cron@3.0.3:
|
||||
dependencies:
|
||||
uuid: 8.3.2
|
||||
|
||||
node-domexception@1.0.0: {}
|
||||
|
||||
node-fetch@2.7.0(encoding@0.1.13):
|
||||
@@ -40752,41 +40648,41 @@ snapshots:
|
||||
|
||||
xtend@4.0.2: {}
|
||||
|
||||
y-indexeddb@9.0.12(yjs@13.6.23):
|
||||
y-indexeddb@9.0.12(yjs@13.6.27):
|
||||
dependencies:
|
||||
lib0: 0.2.89
|
||||
yjs: 13.6.23
|
||||
yjs: 13.6.27
|
||||
|
||||
y-leveldb@0.1.2(yjs@13.6.23):
|
||||
y-leveldb@0.1.2(yjs@13.6.27):
|
||||
dependencies:
|
||||
level: 6.0.1
|
||||
lib0: 0.2.99
|
||||
yjs: 13.6.23
|
||||
lib0: 0.2.109
|
||||
yjs: 13.6.27
|
||||
optional: true
|
||||
|
||||
y-prosemirror@1.3.6(prosemirror-model@1.24.1)(prosemirror-state@1.4.3)(prosemirror-view@1.37.2)(y-protocols@1.0.6(yjs@13.6.23))(yjs@13.6.23):
|
||||
y-prosemirror@1.3.7(prosemirror-model@1.24.1)(prosemirror-state@1.4.3)(prosemirror-view@1.37.2)(y-protocols@1.0.6(yjs@13.6.27))(yjs@13.6.27):
|
||||
dependencies:
|
||||
lib0: 0.2.109
|
||||
prosemirror-model: 1.24.1
|
||||
prosemirror-state: 1.4.3
|
||||
prosemirror-view: 1.37.2
|
||||
y-protocols: 1.0.6(yjs@13.6.23)
|
||||
yjs: 13.6.23
|
||||
y-protocols: 1.0.6(yjs@13.6.27)
|
||||
yjs: 13.6.27
|
||||
|
||||
y-protocols@1.0.6(yjs@13.6.23):
|
||||
y-protocols@1.0.6(yjs@13.6.27):
|
||||
dependencies:
|
||||
lib0: 0.2.89
|
||||
yjs: 13.6.23
|
||||
yjs: 13.6.27
|
||||
|
||||
y-websocket@2.1.0(yjs@13.6.23):
|
||||
y-websocket@2.1.0(yjs@13.6.27):
|
||||
dependencies:
|
||||
lib0: 0.2.89
|
||||
lib0: 0.2.114
|
||||
lodash.debounce: 4.0.8
|
||||
y-protocols: 1.0.6(yjs@13.6.23)
|
||||
yjs: 13.6.23
|
||||
y-protocols: 1.0.6(yjs@13.6.27)
|
||||
yjs: 13.6.27
|
||||
optionalDependencies:
|
||||
ws: 6.2.2
|
||||
y-leveldb: 0.1.2(yjs@13.6.23)
|
||||
y-leveldb: 0.1.2(yjs@13.6.27)
|
||||
|
||||
y18n@4.0.3: {}
|
||||
|
||||
@@ -40841,9 +40737,9 @@ snapshots:
|
||||
buffer-crc32: 0.2.13
|
||||
fd-slicer: 1.1.0
|
||||
|
||||
yjs@13.6.23:
|
||||
yjs@13.6.27:
|
||||
dependencies:
|
||||
lib0: 0.2.99
|
||||
lib0: 0.2.109
|
||||
|
||||
ylru@1.3.2: {}
|
||||
|
||||
|
||||
@@ -19,7 +19,6 @@ rush docker:build -p 20 \
|
||||
--to @hcengineering/pod-datalake \
|
||||
--to @hcengineering/pod-mail-worker \
|
||||
--to @hcengineering/pod-export \
|
||||
--to @hcengineering/pod-msg2file \
|
||||
--to @hcengineering/pod-media \
|
||||
--to @hcengineering/pod-preview \
|
||||
--to @hcengineering/pod-external \
|
||||
|
||||
+1
-1
Submodule communication updated: eb30332c8b...961934e8dd
Binary file not shown.
|
After Width: | Height: | Size: 99 KiB |
@@ -14,9 +14,8 @@
|
||||
//
|
||||
|
||||
describe('node environment self checking', () => {
|
||||
test('check Jest\'s node environment', () => {
|
||||
const isNodeAvailable = typeof process !== 'undefined';
|
||||
expect(isNodeAvailable).toBe(true);
|
||||
})
|
||||
|
||||
test('check Jest\'s node environment', () => {
|
||||
const isNodeAvailable = typeof process !== 'undefined'
|
||||
expect(isNodeAvailable).toBe(true)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -0,0 +1,198 @@
|
||||
//
|
||||
// Copyright © 2025 Hardcore Engineering Inc.
|
||||
//
|
||||
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License. You may
|
||||
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
//
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import { Settings } from '../../main/settings'
|
||||
import { PackedConfig } from '../../main/config'
|
||||
|
||||
const createMockStore = (): { get: jest.Mock, set: jest.Mock } => {
|
||||
const store: Record<string, any> = {}
|
||||
return {
|
||||
get: jest.fn((key: string) => store[key]),
|
||||
set: jest.fn((key: string, value: any) => { store[key] = value })
|
||||
}
|
||||
}
|
||||
|
||||
jest.mock('electron-store', () => {
|
||||
return jest.fn()
|
||||
})
|
||||
|
||||
describe('Settings', () => {
|
||||
let originalEnvironment: NodeJS.ProcessEnv
|
||||
let stubStoreInstance: ReturnType<typeof createMockStore>
|
||||
|
||||
beforeEach(() => {
|
||||
originalEnvironment = process.env
|
||||
process.env = { ...originalEnvironment }
|
||||
stubStoreInstance = createMockStore()
|
||||
jest.clearAllMocks()
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
process.env = originalEnvironment
|
||||
jest.clearAllMocks()
|
||||
})
|
||||
|
||||
describe('readServerUrl', () => {
|
||||
test('isDev is true', () => {
|
||||
const systemUnderTest = new Settings(stubStoreInstance as any, true)
|
||||
|
||||
const actualUrl = systemUnderTest.readServerUrl()
|
||||
|
||||
expect(actualUrl).toBe('http://huly.local:8087')
|
||||
})
|
||||
|
||||
test('isDev is true and FRONT_URL is set', () => {
|
||||
process.env.FRONT_URL = 'http://custom-dev.local:3000'
|
||||
const systemUnderTest = new Settings(stubStoreInstance as any, true)
|
||||
|
||||
const actualUrl = systemUnderTest.readServerUrl()
|
||||
|
||||
expect(actualUrl).toBe('http://custom-dev.local:3000')
|
||||
})
|
||||
|
||||
test('isDev is false and server is in store', () => {
|
||||
const expectedUrl = 'https://stored.server.com'
|
||||
stubStoreInstance.get.mockReturnValue(expectedUrl)
|
||||
const systemUnderTest = new Settings(stubStoreInstance as any, false)
|
||||
|
||||
const actualUrl = systemUnderTest.readServerUrl()
|
||||
|
||||
expect(actualUrl).toBe(expectedUrl)
|
||||
})
|
||||
|
||||
test('store is empty and packed config exists', () => {
|
||||
stubStoreInstance.get.mockReturnValue(undefined)
|
||||
const expectedUrl = 'https://packed.server.com'
|
||||
const packedConfig: PackedConfig = { server: expectedUrl }
|
||||
const systemUnderTest = new Settings(stubStoreInstance as any, false, packedConfig)
|
||||
|
||||
const actualUrl = systemUnderTest.readServerUrl()
|
||||
|
||||
expect(actualUrl).toBe(expectedUrl)
|
||||
})
|
||||
|
||||
test('store and packed config are empty', () => {
|
||||
stubStoreInstance.get.mockReturnValue(undefined)
|
||||
const expectedUrl = 'https://env.server.com'
|
||||
process.env.FRONT_URL = expectedUrl
|
||||
const systemUnderTest = new Settings(stubStoreInstance as any, false)
|
||||
|
||||
const actualUrl = systemUnderTest.readServerUrl()
|
||||
|
||||
expect(actualUrl).toBe(expectedUrl)
|
||||
})
|
||||
|
||||
test('all options are unavailable', () => {
|
||||
stubStoreInstance.get.mockReturnValue(undefined)
|
||||
delete process.env.FRONT_URL
|
||||
const systemUnderTest = new Settings(stubStoreInstance as any, false)
|
||||
|
||||
const result = systemUnderTest.readServerUrl()
|
||||
|
||||
expect(result).toBe('https://huly.app')
|
||||
})
|
||||
|
||||
test('all (store, packed config, environment) options are available', () => {
|
||||
const expectedUrl = 'https://store.priority.com'
|
||||
stubStoreInstance.get.mockReturnValue(expectedUrl)
|
||||
process.env.FRONT_URL = 'https://env.server.com'
|
||||
const packedConfig: PackedConfig = { server: 'https://packed.server.com' }
|
||||
const systemUnderTest = new Settings(stubStoreInstance as any, false, packedConfig)
|
||||
|
||||
const actualUrl = systemUnderTest.readServerUrl()
|
||||
|
||||
expect(actualUrl).toBe(expectedUrl)
|
||||
})
|
||||
})
|
||||
|
||||
describe('read/write', () => {
|
||||
let systemUnderTest: Settings
|
||||
beforeEach(() => {
|
||||
systemUnderTest = new Settings(stubStoreInstance as any, false)
|
||||
})
|
||||
|
||||
describe('isMinimizeToTrayEnabled', () => {
|
||||
test.each([
|
||||
{ storedValue: true, expected: true, description: 'stored value is true' },
|
||||
{ storedValue: false, expected: false, description: 'stored value is false' }
|
||||
])('$description', ({ storedValue, expected }) => {
|
||||
stubStoreInstance.get.mockReturnValue(storedValue)
|
||||
|
||||
const actualValue = systemUnderTest.isMinimizeToTrayEnabled()
|
||||
|
||||
expect(actualValue).toBe(expected)
|
||||
})
|
||||
|
||||
test.each([
|
||||
{ storedValue: undefined, description: 'no value is stored (undefined)' },
|
||||
{ storedValue: null, description: 'stored value is null' }
|
||||
])('$description', ({ storedValue }) => {
|
||||
stubStoreInstance.get.mockReturnValue(storedValue)
|
||||
|
||||
const actualValue = systemUnderTest.isMinimizeToTrayEnabled()
|
||||
|
||||
expect(actualValue).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
describe('setMinimizeToTrayEnabled', () => {
|
||||
test.each([
|
||||
{ value: true, description: 'stored value is true' },
|
||||
{ value: false, description: 'stored value is false' }
|
||||
])('$description', ({ value }) => {
|
||||
systemUnderTest.setMinimizeToTrayEnabled(value)
|
||||
|
||||
expect(systemUnderTest.isMinimizeToTrayEnabled()).toBe(value)
|
||||
})
|
||||
})
|
||||
|
||||
describe('setServerUrl', () => {
|
||||
test('write than read', () => {
|
||||
const expectedUrl = 'https://new.server.com'
|
||||
|
||||
systemUnderTest.setServerUrl(expectedUrl)
|
||||
|
||||
expect(systemUnderTest.readServerUrl()).toBe(expectedUrl)
|
||||
})
|
||||
})
|
||||
|
||||
describe('getWindowBounds', () => {
|
||||
test('read', () => {
|
||||
const expectedValue = { x: 100, y: 300, width: 500, height: 700 }
|
||||
stubStoreInstance.get.mockReturnValue(expectedValue)
|
||||
|
||||
const actulValue = systemUnderTest.getWindowBounds()
|
||||
|
||||
expect(actulValue).toEqual(expectedValue)
|
||||
})
|
||||
|
||||
test('no bounds are stored', () => {
|
||||
stubStoreInstance.get.mockReturnValue(undefined)
|
||||
|
||||
expect(systemUnderTest.getWindowBounds()).toBeUndefined()
|
||||
})
|
||||
})
|
||||
|
||||
describe('setWindowBounds', () => {
|
||||
test('write than read', () => {
|
||||
const expectedValue = { x: 100, y: 200, width: 800, height: 600 }
|
||||
|
||||
systemUnderTest.setWindowBounds(expectedValue)
|
||||
|
||||
expect(systemUnderTest.getWindowBounds()).toBe(expectedValue)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,67 @@
|
||||
//
|
||||
// Copyright © 2025 Hardcore Engineering Inc.
|
||||
//
|
||||
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License. You may
|
||||
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
//
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import { getBadgeIconInfo } from '../../main/trayUtils'
|
||||
|
||||
describe('getBadgeIconInfo', () => {
|
||||
const baseTitle = 'Huly'
|
||||
|
||||
it('count of 0', () => {
|
||||
const result = getBadgeIconInfo(0, baseTitle)
|
||||
expect(result).toEqual({
|
||||
fileName: '',
|
||||
tooltip: baseTitle
|
||||
})
|
||||
})
|
||||
|
||||
it('negative count', () => {
|
||||
const result = getBadgeIconInfo(-5, baseTitle)
|
||||
expect(result).toEqual({
|
||||
fileName: '',
|
||||
tooltip: baseTitle
|
||||
})
|
||||
})
|
||||
|
||||
it.each([1, 2, 3, 4, 5, 6, 7, 8, 9])('count of %i', (count: number) => {
|
||||
const result = getBadgeIconInfo(count, baseTitle)
|
||||
expect(result).toEqual({
|
||||
fileName: 'TrayIconWithBadge.ico',
|
||||
tooltip: `${baseTitle}: ${count} unread`
|
||||
})
|
||||
})
|
||||
|
||||
describe('badge count 10-98', () => {
|
||||
const testCases: number[] = []
|
||||
for (let i = 10; i <= 98; i++) {
|
||||
testCases.push(i)
|
||||
}
|
||||
|
||||
it.each(testCases)('count of %i', (count: number) => {
|
||||
const result = getBadgeIconInfo(count, baseTitle)
|
||||
expect(result).toEqual({
|
||||
fileName: 'TrayIconWithBadge.ico',
|
||||
tooltip: `${baseTitle}: ${count} unread`
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
it.each([99, 100, 200, 301])('count of %i', (count: number) => {
|
||||
const result = getBadgeIconInfo(count, baseTitle)
|
||||
expect(result).toEqual({
|
||||
fileName: 'TrayIconWithBadge.ico',
|
||||
tooltip: `${baseTitle}: ${count} unread`
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -15,12 +15,12 @@
|
||||
import * as path from 'path'
|
||||
import * as fs from 'fs'
|
||||
|
||||
interface PackedConfig {
|
||||
export interface PackedConfig {
|
||||
server?: string
|
||||
updatesChannelKey?: string
|
||||
}
|
||||
|
||||
const configPath = path.join(process.resourcesPath, 'config/config.json')
|
||||
const configPath = path.join(process.resourcesPath, 'config', 'config.json')
|
||||
|
||||
export function readPackedConfig (): PackedConfig | undefined {
|
||||
if (fs.existsSync(configPath)) {
|
||||
|
||||
@@ -13,10 +13,11 @@
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import { app, BrowserWindow } from 'electron'
|
||||
import { BrowserWindow } from 'electron'
|
||||
import { MenuBarAction, CommandLogout, CommandSelectWorkspace, CommandOpenSettings } from '../ui/types'
|
||||
import { TrayController } from './tray'
|
||||
|
||||
export function dispatchMenuBarAction (mainWindow: BrowserWindow | undefined, action: MenuBarAction): void {
|
||||
export function dispatchMenuBarAction (mainWindow: BrowserWindow | undefined, action: MenuBarAction, tray: TrayController | undefined): void {
|
||||
if (mainWindow == null) {
|
||||
return
|
||||
}
|
||||
@@ -42,7 +43,7 @@ export function dispatchMenuBarAction (mainWindow: BrowserWindow | undefined, ac
|
||||
mainWindow.webContents.send(CommandLogout)
|
||||
break
|
||||
case 'exit':
|
||||
app.quit()
|
||||
mainWindow.close()
|
||||
break
|
||||
case 'undo':
|
||||
mainWindow.webContents.undo()
|
||||
@@ -86,6 +87,13 @@ export function dispatchMenuBarAction (mainWindow: BrowserWindow | undefined, ac
|
||||
case 'toggle-fullscreen':
|
||||
mainWindow.setFullScreen(!mainWindow.isFullScreen())
|
||||
break
|
||||
case 'toggle-minimize-to-tray': {
|
||||
if (tray != null) {
|
||||
const newSetting = tray.toggleMinimizeToTray()
|
||||
mainWindow.webContents.send('minimize-to-tray-setting-changed', newSetting)
|
||||
}
|
||||
break
|
||||
}
|
||||
default:
|
||||
console.log('unknown menu action:', action)
|
||||
}
|
||||
|
||||
@@ -13,9 +13,9 @@
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import { config } from 'dotenv'
|
||||
import { app } from 'electron'
|
||||
import path from 'path'
|
||||
|
||||
import { main } from './main'
|
||||
|
||||
config()
|
||||
void main()
|
||||
export function getFileInPublicBundledFolder (fileName: string): string {
|
||||
return path.join(app.getAppPath(), 'dist', 'ui', 'public', fileName)
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
//
|
||||
// Copyright © 2024 Hardcore Engineering Inc.
|
||||
//
|
||||
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License. You may
|
||||
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
//
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import Store from 'electron-store'
|
||||
import { PackedConfig } from './config'
|
||||
|
||||
export class Settings {
|
||||
private static readonly SETTINGS_KEY_SERVER = 'server'
|
||||
private static readonly SETTINGS_KEY_MINIMIZE_TO_TRAY = 'minimize-to-tray'
|
||||
private static readonly SETTINGS_KEY_WINDOW_BOUNDS = 'windowBounds'
|
||||
|
||||
private readonly store: Store
|
||||
private readonly isDev: boolean
|
||||
private readonly packedConfig?: PackedConfig
|
||||
|
||||
constructor (store: Store, isDev: boolean, packedConfig?: PackedConfig) {
|
||||
this.store = store
|
||||
this.isDev = isDev
|
||||
this.packedConfig = packedConfig
|
||||
}
|
||||
|
||||
readServerUrl (): string {
|
||||
if (this.isDev) {
|
||||
return process.env.FRONT_URL ?? 'http://huly.local:8087'
|
||||
}
|
||||
return (
|
||||
(this.store as any).get(Settings.SETTINGS_KEY_SERVER) as string ??
|
||||
this.packedConfig?.server ??
|
||||
process.env.FRONT_URL ??
|
||||
'https://huly.app'
|
||||
)
|
||||
}
|
||||
|
||||
isMinimizeToTrayEnabled (): boolean {
|
||||
return (this.store as any).get(Settings.SETTINGS_KEY_MINIMIZE_TO_TRAY) as boolean ?? false
|
||||
}
|
||||
|
||||
setMinimizeToTrayEnabled (enabled: boolean): void {
|
||||
(this.store as any).set(Settings.SETTINGS_KEY_MINIMIZE_TO_TRAY, enabled)
|
||||
}
|
||||
|
||||
setServerUrl (serverUrl: string): void {
|
||||
(this.store as any).set(Settings.SETTINGS_KEY_SERVER, serverUrl)
|
||||
}
|
||||
|
||||
getWindowBounds (): any {
|
||||
return (this.store as any).get(Settings.SETTINGS_KEY_WINDOW_BOUNDS)
|
||||
}
|
||||
|
||||
setWindowBounds (bounds: any): void {
|
||||
(this.store as any).set(Settings.SETTINGS_KEY_WINDOW_BOUNDS, bounds)
|
||||
}
|
||||
}
|
||||
+559
-508
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,133 @@
|
||||
//
|
||||
// Copyright © 2025 Hardcore Engineering Inc.
|
||||
//
|
||||
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License. You may
|
||||
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
//
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import { Menu, NativeImage, nativeImage, Tray } from 'electron'
|
||||
import { WindowAction } from '../ui/types'
|
||||
import { Settings } from './settings'
|
||||
import { getFileInPublicBundledFolder } from './path'
|
||||
import { getBadgeIconInfo } from './trayUtils'
|
||||
|
||||
export class TrayController {
|
||||
private tray: Tray | undefined = undefined
|
||||
private readonly defaultIcon: NativeImage
|
||||
private readonly notificationIcon: NativeImage
|
||||
private badgeCount: number = 0
|
||||
|
||||
constructor (
|
||||
private readonly settings: Settings,
|
||||
private readonly activateWindow: WindowAction,
|
||||
private readonly quitApplication: WindowAction) {
|
||||
this.defaultIcon = nativeImage.createFromPath(getFileInPublicBundledFolder('AppIcon.ico'))
|
||||
this.defaultIcon.setTemplateImage(true)
|
||||
|
||||
this.notificationIcon = nativeImage.createFromPath(getFileInPublicBundledFolder('AppIconNotification.png'))
|
||||
this.notificationIcon.setTemplateImage(true)
|
||||
|
||||
if (this.settings.isMinimizeToTrayEnabled()) {
|
||||
this.instantiateTray()
|
||||
}
|
||||
}
|
||||
|
||||
public updateTrayBadge (count: number): void {
|
||||
const countChanged = this.badgeCount !== count
|
||||
this.badgeCount = count
|
||||
if (!countChanged) {
|
||||
return
|
||||
}
|
||||
|
||||
this.evaluateIcon(count)
|
||||
}
|
||||
|
||||
public setMinimizeToTrayEnabled (enabled: boolean): void {
|
||||
this.settings.setMinimizeToTrayEnabled(enabled)
|
||||
if (enabled) {
|
||||
if (this.tray == null) {
|
||||
this.instantiateTray()
|
||||
}
|
||||
} else {
|
||||
if (this.tray != null) {
|
||||
this.tray.destroy()
|
||||
this.tray = undefined
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public toggleMinimizeToTray (): boolean {
|
||||
const currentSetting = this.settings.isMinimizeToTrayEnabled()
|
||||
const newSetting = !currentSetting
|
||||
this.setMinimizeToTrayEnabled(newSetting)
|
||||
return newSetting
|
||||
}
|
||||
|
||||
private instantiateTray (): void {
|
||||
this.tray = createTray(
|
||||
() => { this.activateWindow() },
|
||||
() => { this.quitApplication() },
|
||||
this.defaultIcon
|
||||
)
|
||||
this.evaluateIcon(this.badgeCount)
|
||||
}
|
||||
|
||||
private evaluateIcon (badgeCount: number): void {
|
||||
if (this.tray == null) {
|
||||
return
|
||||
}
|
||||
|
||||
const iconInfo = getBadgeIconInfo(badgeCount, BASE_TITLE)
|
||||
if (badgeCount > 0) {
|
||||
const iconFilePath = getFileInPublicBundledFolder(iconInfo.fileName)
|
||||
const icon = nativeImage.createFromPath(iconFilePath)
|
||||
this.tray.setImage(icon)
|
||||
} else {
|
||||
this.tray.setImage(this.defaultIcon)
|
||||
}
|
||||
|
||||
this.tray.setToolTip(iconInfo.tooltip)
|
||||
}
|
||||
}
|
||||
|
||||
const BASE_TITLE = 'Huly'
|
||||
|
||||
function createTray (activateWindow: WindowAction, quitApplication: WindowAction, icon: NativeImage): Tray {
|
||||
const tray = new Tray(icon)
|
||||
|
||||
const contextMenu = Menu.buildFromTemplate([
|
||||
{
|
||||
label: 'Show',
|
||||
click: () => {
|
||||
activateWindow()
|
||||
}
|
||||
},
|
||||
{
|
||||
label: 'Exit',
|
||||
click: () => {
|
||||
quitApplication()
|
||||
}
|
||||
}
|
||||
])
|
||||
|
||||
tray.setContextMenu(contextMenu)
|
||||
tray.setToolTip(BASE_TITLE)
|
||||
|
||||
tray.on('double-click', () => {
|
||||
activateWindow()
|
||||
})
|
||||
|
||||
tray.on('click', (): void => {
|
||||
activateWindow()
|
||||
})
|
||||
|
||||
return tray
|
||||
}
|
||||
@@ -13,28 +13,21 @@
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import { Readable } from 'stream'
|
||||
import { ParsedFile } from '@hcengineering/communication-types'
|
||||
import { parseYaml } from '@hcengineering/communication-yaml'
|
||||
|
||||
export async function parseFileStream (stream: Readable): Promise<ParsedFile> {
|
||||
return await new Promise((resolve, reject) => {
|
||||
let yamlData = ''
|
||||
|
||||
stream.on('data', (chunk) => {
|
||||
yamlData += chunk.toString()
|
||||
})
|
||||
|
||||
stream.on('end', () => {
|
||||
try {
|
||||
resolve(parseYaml(yamlData))
|
||||
} catch (error) {
|
||||
reject(error)
|
||||
}
|
||||
})
|
||||
|
||||
stream.on('error', (error) => {
|
||||
reject(error)
|
||||
})
|
||||
})
|
||||
export interface BadgeIconInfo {
|
||||
fileName: string
|
||||
tooltip: string
|
||||
}
|
||||
|
||||
export function getBadgeIconInfo (badgeCount: number, baseTitle: string): BadgeIconInfo {
|
||||
if (badgeCount <= 0) {
|
||||
return {
|
||||
fileName: '',
|
||||
tooltip: baseTitle
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
fileName: 'TrayIconWithBadge.ico',
|
||||
tooltip: `${baseTitle}: ${badgeCount} unread`
|
||||
}
|
||||
}
|
||||
@@ -14,106 +14,103 @@
|
||||
//
|
||||
|
||||
import { app, JumpListItem, JumpListCategory } from 'electron'
|
||||
import { Command, CommandOpenSettings, CommandOpenInbox, CommandOpenApplication, JumpListSpares } from '../ui/types'
|
||||
import { Command, CommandOpenSettings, CommandOpenInbox, CommandOpenApplication, JumpListSpares, WindowAction, SendCommandDelegate } from '../ui/types'
|
||||
import * as path from 'path'
|
||||
|
||||
const JUMP_COMMANDS = {
|
||||
INBOX: '--jump-to-inbox',
|
||||
SETTINGS: '--jump-to-settings',
|
||||
SETTINGS: '--jump-to-settings'
|
||||
} as const
|
||||
|
||||
type JumpCommand = typeof JUMP_COMMANDS[keyof typeof JUMP_COMMANDS]
|
||||
|
||||
const JUMP_TO_APP_COMMAND_PREFIX = '--jump-to-app='
|
||||
|
||||
function getIconPath(iconName: string): string {
|
||||
return path.join(process.resourcesPath, 'icons', iconName)
|
||||
function getIconPath (iconName: string): string {
|
||||
return path.join(process.resourcesPath, 'icons', iconName)
|
||||
}
|
||||
|
||||
function addFixedFunctionJumpListItems(spares: JumpListSpares, tasks: JumpListItem[]): void {
|
||||
function addFixedFunctionJumpListItems (spares: JumpListSpares, tasks: JumpListItem[]): void {
|
||||
tasks.push({
|
||||
type: 'task',
|
||||
program: process.execPath,
|
||||
args: JUMP_COMMANDS.SETTINGS,
|
||||
iconPath: getIconPath('SettingsIcon.ico'),
|
||||
title: spares.settingsLabel,
|
||||
iconIndex: 0
|
||||
})
|
||||
tasks.push({
|
||||
type: 'task',
|
||||
program: process.execPath,
|
||||
args: JUMP_COMMANDS.INBOX,
|
||||
iconPath: getIconPath('InboxIcon.ico'),
|
||||
title: spares.inboxLabel,
|
||||
iconIndex: 0
|
||||
})
|
||||
}
|
||||
|
||||
export function rebuildJumpList (spares: JumpListSpares): void {
|
||||
const tasks: JumpListItem[] = []
|
||||
|
||||
addFixedFunctionJumpListItems(spares, tasks)
|
||||
|
||||
if (spares.applications.length > 0) {
|
||||
tasks.push({
|
||||
type: 'task',
|
||||
program: process.execPath,
|
||||
args: JUMP_COMMANDS.SETTINGS,
|
||||
iconPath: getIconPath('SettingsIcon.ico'),
|
||||
title: spares.settingsLabel,
|
||||
iconIndex: 0,
|
||||
type: 'separator'
|
||||
})
|
||||
}
|
||||
|
||||
for (const application of spares.applications) {
|
||||
const iconFileName = application.id.replaceAll(':', '_') + '.ico'
|
||||
const cliArguments = `${JUMP_TO_APP_COMMAND_PREFIX}${application.alias}`
|
||||
tasks.push({
|
||||
type: 'task',
|
||||
program: process.execPath,
|
||||
args: JUMP_COMMANDS.INBOX,
|
||||
iconPath: getIconPath('InboxIcon.ico'),
|
||||
title: spares.inboxLabel,
|
||||
iconIndex: 0,
|
||||
type: 'task',
|
||||
program: process.execPath,
|
||||
args: cliArguments,
|
||||
iconPath: getIconPath(iconFileName),
|
||||
title: application.title,
|
||||
iconIndex: 0
|
||||
})
|
||||
}
|
||||
|
||||
const category: JumpListCategory = {
|
||||
type: 'tasks',
|
||||
items: tasks
|
||||
}
|
||||
|
||||
app.setJumpList([category])
|
||||
}
|
||||
|
||||
export function rebuildJumpList(spares: JumpListSpares): void {
|
||||
const tasks: JumpListItem[] = [];
|
||||
export function setupWindowsSpecific (activateWindow: WindowAction, sendCommand: SendCommandDelegate): void {
|
||||
app.setAppUserModelId(app.getName())
|
||||
|
||||
addFixedFunctionJumpListItems(spares, tasks)
|
||||
|
||||
if (spares.applications.length > 0) {
|
||||
tasks.push({
|
||||
type: 'separator'
|
||||
})
|
||||
}
|
||||
|
||||
for (const application of spares.applications) {
|
||||
const iconFileName = application.id.replaceAll(":", "_") + '.ico'
|
||||
const cliArguments = `${JUMP_TO_APP_COMMAND_PREFIX}${application.alias}`
|
||||
tasks.push({
|
||||
type: 'task',
|
||||
program: process.execPath,
|
||||
args: cliArguments,
|
||||
iconPath: getIconPath(iconFileName),
|
||||
title: application.title,
|
||||
iconIndex: 0,
|
||||
})
|
||||
}
|
||||
|
||||
const category: JumpListCategory = {
|
||||
type: 'tasks',
|
||||
items: tasks,
|
||||
}
|
||||
|
||||
app.setJumpList([category])
|
||||
}
|
||||
|
||||
export function setupWindowsSpecific(activateWindow: () => void, sendCommand: (cmd: Command, ...args: any[]) => void): void {
|
||||
|
||||
app.setAppUserModelId(app.getName())
|
||||
|
||||
app.on('second-instance', (_event: any, commandLine: any, _workingDirectory: any) => {
|
||||
const commandArgument = commandLine[1] as string
|
||||
if (typeof commandArgument === 'string' && commandArgument.startsWith(JUMP_TO_APP_COMMAND_PREFIX)) {
|
||||
const applicationId = commandArgument.replace(JUMP_TO_APP_COMMAND_PREFIX, '')
|
||||
sendCommand(CommandOpenApplication, [applicationId])
|
||||
|
||||
} else {
|
||||
const jumpCommandCode = commandArgument as JumpCommand
|
||||
let command: Command | undefined
|
||||
switch (jumpCommandCode) {
|
||||
case JUMP_COMMANDS.INBOX:
|
||||
command = CommandOpenInbox
|
||||
break;
|
||||
case JUMP_COMMANDS.SETTINGS:
|
||||
command = CommandOpenSettings
|
||||
break;
|
||||
default: {
|
||||
// compile-time check: if jumpCommand is not a known value, this line errors
|
||||
const _exhaustive: never = jumpCommandCode
|
||||
return; // or handle error
|
||||
}
|
||||
}
|
||||
|
||||
if (command) {
|
||||
sendCommand(command)
|
||||
}
|
||||
app.on('second-instance', (_event: any, commandLine: any, _workingDirectory: any) => {
|
||||
const commandArgument = commandLine[1] as string
|
||||
if (typeof commandArgument === 'string' && commandArgument.startsWith(JUMP_TO_APP_COMMAND_PREFIX)) {
|
||||
const applicationId = commandArgument.replace(JUMP_TO_APP_COMMAND_PREFIX, '')
|
||||
sendCommand(CommandOpenApplication, [applicationId])
|
||||
} else {
|
||||
const jumpCommandCode = commandArgument as JumpCommand
|
||||
let command: Command | undefined
|
||||
switch (jumpCommandCode) {
|
||||
case JUMP_COMMANDS.INBOX:
|
||||
command = CommandOpenInbox
|
||||
break
|
||||
case JUMP_COMMANDS.SETTINGS:
|
||||
command = CommandOpenSettings
|
||||
break
|
||||
default: {
|
||||
// compile-time check: if jumpCommand is not a known value, this line errors
|
||||
const _exhaustive: never = jumpCommandCode
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
activateWindow()
|
||||
})
|
||||
|
||||
}
|
||||
if (command !== undefined) {
|
||||
sendCommand(command)
|
||||
}
|
||||
}
|
||||
|
||||
activateWindow()
|
||||
})
|
||||
}
|
||||
|
||||
+32
-15
@@ -2,7 +2,7 @@ import { loginId } from '@hcengineering/login'
|
||||
import { loveId } from '@hcengineering/love'
|
||||
import { timeId } from '@hcengineering/time'
|
||||
|
||||
import { getEmbeddedLabel, getMetadata, translate } from '@hcengineering/platform'
|
||||
import { getEmbeddedLabel, getMetadata, getResource, translate } from '@hcengineering/platform'
|
||||
import presentation, { MessageBox, setDownloadProgress, getClient } from '@hcengineering/presentation'
|
||||
import setting, { settingId } from '@hcengineering/setting'
|
||||
import {
|
||||
@@ -20,14 +20,14 @@ import { handleDownloadItem } from '@hcengineering/desktop-downloads'
|
||||
import notification, { notificationId } from '@hcengineering/notification'
|
||||
import { chatId } from '@hcengineering/chat'
|
||||
import workbench, { workbenchId, logOut } from '@hcengineering/workbench'
|
||||
import { encodeObjectURI } from '@hcengineering/view'
|
||||
import view, { Action, encodeObjectURI } from '@hcengineering/view'
|
||||
import { resolveLocation } from '@hcengineering/notification-resources'
|
||||
|
||||
import { isOwnerOrMaintainer, getCurrentAccount } from '@hcengineering/core'
|
||||
import { isOwnerOrMaintainer, getCurrentAccount, Ref } from '@hcengineering/core'
|
||||
import { configurePlatform } from './platform'
|
||||
import { setupTitleBarMenu } from './titleBarMenu'
|
||||
import { defineScreenShare, defineGetDisplayMedia } from './screenShare'
|
||||
import { CommandLogout, CommandSelectWorkspace, CommandOpenSettings, CommandOpenInbox, CommandOpenPlanner, CommandOpenOffice, CommandOpenApplication, LaunchApplication, NotificationParams } from './types'
|
||||
import { CommandLogout, CommandSelectWorkspace, CommandOpenSettings, CommandOpenInbox, CommandOpenPlanner, CommandOpenOffice, CommandOpenApplication, LaunchApplication, NotificationParams, CommandCloseTab } from './types'
|
||||
import { ipcMainExposed } from './typesUtils'
|
||||
import { themeStore, ThemeVariant } from '@hcengineering/theme'
|
||||
import type { Application } from '@hcengineering/workbench'
|
||||
@@ -37,6 +37,19 @@ function currentOsIsWindows (): boolean {
|
||||
return (window as any).windowsPlatform === true
|
||||
}
|
||||
|
||||
async function executePlatformAction (action: Ref<Action>): Promise<void> {
|
||||
try {
|
||||
const client = getClient()
|
||||
const actions = client.getModel().findAllSync<Action>(view.class.Action, { _id: action })
|
||||
if (actions.length > 0) {
|
||||
const actionBody = await getResource(actions[0].action)
|
||||
await actionBody(undefined)
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('failed to execute platform action', error)
|
||||
}
|
||||
}
|
||||
|
||||
defineScreenShare()
|
||||
defineGetDisplayMedia()
|
||||
|
||||
@@ -46,18 +59,18 @@ window.addEventListener('DOMContentLoaded', () => {
|
||||
if (currentOsIsWindows()) {
|
||||
const titleBarRoot = document.getElementById('desktop-app-titlebar-root')
|
||||
if (titleBarRoot != null) {
|
||||
const menuBar = setupTitleBarMenu(ipcMain, titleBarRoot)
|
||||
void setupTitleBarMenu(ipcMain, titleBarRoot).then((menuBar) => {
|
||||
themeStore.subscribe((themeOptions) => {
|
||||
if (themeOptions != null) {
|
||||
menuBar.setTheme(themeOptions.variant)
|
||||
}
|
||||
})
|
||||
|
||||
themeStore.subscribe((themeOptions) => {
|
||||
if (themeOptions != null) {
|
||||
menuBar.setTheme(themeOptions.variant)
|
||||
}
|
||||
})
|
||||
|
||||
void ipcMain.isOsUsingDarkTheme().then((isDarkTheme) => {
|
||||
menuBar.setTheme(isDarkTheme ? ThemeVariant.Dark : ThemeVariant.Light)
|
||||
}).catch(() => {
|
||||
menuBar.setTheme(ThemeVariant.Light) // fallback
|
||||
void ipcMain.isOsUsingDarkTheme().then((isDarkTheme) => {
|
||||
menuBar.setTheme(isDarkTheme ? ThemeVariant.Dark : ThemeVariant.Light)
|
||||
}).catch(() => {
|
||||
menuBar.setTheme(ThemeVariant.Light) // fallback
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -124,6 +137,10 @@ window.addEventListener('DOMContentLoaded', () => {
|
||||
openScreen(loveId)
|
||||
})
|
||||
|
||||
ipcMain.on(CommandCloseTab, () => {
|
||||
void executePlatformAction(workbench.action.CloseCurrentTab)
|
||||
})
|
||||
|
||||
ipcMain.on(CommandOpenPlanner, () => {
|
||||
openScreen(timeId)
|
||||
})
|
||||
|
||||
@@ -298,6 +298,7 @@ export async function configurePlatform (onWorkbenchConnect?: () => Promise<void
|
||||
setMetadata(presentation.metadata.MailUrl, config.MAIL_URL)
|
||||
setMetadata(recorder.metadata.StreamUrl, config.STREAM_URL ?? '')
|
||||
setMetadata(presentation.metadata.StatsUrl, config.STATS_URL)
|
||||
setMetadata(presentation.metadata.HulylakeUrl, config.HULYLAKE_URL ?? '')
|
||||
setMetadata(presentation.metadata.PulseUrl, config.PULSE_URL ?? '')
|
||||
|
||||
setMetadata(textEditor.metadata.Collaborator, config.COLLABORATOR ?? '')
|
||||
@@ -338,7 +339,7 @@ export async function configurePlatform (onWorkbenchConnect?: () => Promise<void
|
||||
|
||||
setMetadata(billingPlugin.metadata.BillingURL, config.BILLING_URL ?? '')
|
||||
|
||||
const languages = myBranding.languages !== undefined && myBranding.languages !== '' ? myBranding.languages.split(',').map((l) => l.trim()) : ['en', 'ru', 'es', 'pt', 'zh', 'fr', 'cs', 'it', 'de', 'ja']
|
||||
const languages = myBranding.languages !== undefined && myBranding.languages !== '' ? myBranding.languages.split(',').map((l) => l.trim()) : ['en', 'ru', 'es', 'pt', 'zh', 'fr', 'cs', 'it', 'de', 'ja', 'tr']
|
||||
|
||||
setMetadata(uiPlugin.metadata.Languages, languages)
|
||||
|
||||
|
||||
+17
-14
@@ -1,7 +1,7 @@
|
||||
// preload.js
|
||||
|
||||
import { contextBridge, ipcRenderer } from 'electron'
|
||||
import { BrandingMap, Config, IPCMainExposed, MenuBarAction, NotificationParams, JumpListSpares } from './types'
|
||||
import { BrandingMap, Config, IPCMainExposed, JumpListSpares, MenuBarAction, NotificationParams } from './types'
|
||||
|
||||
/**
|
||||
* @public
|
||||
@@ -18,27 +18,23 @@ export function concatLink (host: string, path: string): string {
|
||||
}
|
||||
|
||||
async function loadServerConfig (url: string): Promise<any> {
|
||||
let retries = 5
|
||||
let retries = 1
|
||||
let res: Response | undefined
|
||||
|
||||
do {
|
||||
while (true) {
|
||||
try {
|
||||
res = await fetch(url, {
|
||||
keepalive: true
|
||||
})
|
||||
if (res === undefined) {
|
||||
// In theory should never get here
|
||||
throw new Error('Failed to load server config')
|
||||
}
|
||||
break
|
||||
} catch (e) {
|
||||
retries--
|
||||
if (retries === 0) {
|
||||
throw new Error(`Failed to load server config: ${e}`)
|
||||
}
|
||||
await new Promise((resolve) => setTimeout(resolve, 1000 * (5 - retries)))
|
||||
retries++
|
||||
await new Promise((resolve) => setTimeout(resolve, 1000 * Math.min(5, retries)))
|
||||
}
|
||||
} while (retries > 0)
|
||||
|
||||
if (res === undefined) {
|
||||
// In theory should never get here
|
||||
throw new Error('Failed to load server config')
|
||||
}
|
||||
|
||||
return await res.json()
|
||||
@@ -181,6 +177,13 @@ const expose: IPCMainExposed = {
|
||||
cancelBackup: () => { ipcRenderer.send('cancel-backup') },
|
||||
startBackup: (token, endpoint, wsIds) => { ipcRenderer.send('start-backup', token, endpoint, wsIds) },
|
||||
|
||||
rebuildJumpList: (spares: JumpListSpares) => { ipcRenderer.send('rebuild-user-jump-list', spares) }
|
||||
rebuildJumpList: (spares: JumpListSpares) => { ipcRenderer.send('rebuild-user-jump-list', spares) },
|
||||
|
||||
isMinimizeToTrayEnabled: async () => {
|
||||
return await ipcRenderer.invoke('get-minimize-to-tray-enabled')
|
||||
},
|
||||
onMinimizeToTraySettingChanged: (callback: (enabled: boolean) => void) => {
|
||||
ipcRenderer.on('minimize-to-tray-setting-changed', (_event, enabled: boolean) => { callback(enabled) })
|
||||
}
|
||||
}
|
||||
contextBridge.exposeInMainWorld('electron', expose)
|
||||
|
||||
@@ -18,9 +18,14 @@ import { isMenuBarAction } from './typesUtils'
|
||||
import { TitleBarMenuState } from './titleBarMenuState'
|
||||
import { ThemeVariant, type ThemeVariantType } from '@hcengineering/theme'
|
||||
|
||||
export function setupTitleBarMenu (ipcMain: IPCMainExposed, root: HTMLElement): MenuBar {
|
||||
const ToggleMinimizeToTrayAction: MenuBarAction = 'toggle-minimize-to-tray'
|
||||
|
||||
const LabelMinimizeToTrayEnabled = '☑ Minimize to tray'
|
||||
const LabelMinimizeToTrayDisabled = '☐ Minimize to tray'
|
||||
|
||||
export async function setupTitleBarMenu (ipcMain: IPCMainExposed, root: HTMLElement): Promise<MenuBar> {
|
||||
const themeManager = new ThemeManager(ThemeVariant.Light)
|
||||
const menuManager = new MenuBarManager(root)
|
||||
const menuManager = new MenuBarManager(root, await ipcMain.isMinimizeToTrayEnabled())
|
||||
|
||||
const menuBar = menuManager.getView()
|
||||
|
||||
@@ -35,6 +40,13 @@ export function setupTitleBarMenu (ipcMain: IPCMainExposed, root: HTMLElement):
|
||||
|
||||
menuManager.attachEventListeners(ipcMain)
|
||||
|
||||
ipcMain.onMinimizeToTraySettingChanged((enabled) => {
|
||||
const toggle = root.querySelector(`[data-action="${ToggleMinimizeToTrayAction}"]`)
|
||||
if (toggle != null) {
|
||||
toggle.textContent = enabled ? LabelMinimizeToTrayEnabled : LabelMinimizeToTrayDisabled
|
||||
}
|
||||
})
|
||||
|
||||
ipcMain.onWindowStateChange((_event, state) => {
|
||||
const maximizeButton = root.querySelector('#maximize-button')
|
||||
if (maximizeButton != null) {
|
||||
@@ -58,7 +70,7 @@ export class MenuBar {
|
||||
}
|
||||
}
|
||||
|
||||
export function buildHulyApplicationMenu (): HTMLElement {
|
||||
export function buildHulyApplicationMenu (minimizeToTrayEnabled: boolean): HTMLElement {
|
||||
const menuBuilder = new MenuBuilder()
|
||||
|
||||
const MenuFileIndex = 0
|
||||
@@ -93,6 +105,11 @@ export function buildHulyApplicationMenu (): HTMLElement {
|
||||
.addSeparator(MenuViewIndex)
|
||||
.addMenuItem(MenuViewIndex, 'Toggle Fullscreen', 'toggle-fullscreen', 'F11', 'l')
|
||||
|
||||
const MenuWindowIndex = 3
|
||||
const ToggleMinimizeToTrayLabel = minimizeToTrayEnabled ? LabelMinimizeToTrayEnabled : LabelMinimizeToTrayDisabled
|
||||
menuBuilder.addTopLevelMenu('Window', 'w')
|
||||
.addMenuItem(MenuWindowIndex, ToggleMinimizeToTrayLabel, 'toggle-minimize-to-tray', undefined, 'm')
|
||||
|
||||
return menuBuilder.build()
|
||||
}
|
||||
|
||||
@@ -140,7 +157,7 @@ export class MenuBuilder {
|
||||
label: string,
|
||||
action: MenuBarAction,
|
||||
shortcut?: string,
|
||||
acceleratorChar: string | null = null
|
||||
acceleratorChar: string | undefined = undefined
|
||||
): this {
|
||||
if (topLevelMenuIndex >= 0 && topLevelMenuIndex < this.menus.length) {
|
||||
const item: MenuItem = {
|
||||
@@ -281,7 +298,7 @@ class MenuBarManager {
|
||||
private readonly StateStyleKeyboardSelected = 'desktop-app-keyboard-selected'
|
||||
private readonly StateStyleAltModeActive = 'desktop-app-alt-active'
|
||||
|
||||
constructor (private readonly root: HTMLElement) {
|
||||
constructor (private readonly root: HTMLElement, minimizeToTrayEnabled: boolean) {
|
||||
this.state = new TitleBarMenuState(
|
||||
() => this.topLevelMenus().length,
|
||||
(topLevelMenuIndex: number) => {
|
||||
@@ -290,7 +307,7 @@ class MenuBarManager {
|
||||
}
|
||||
)
|
||||
|
||||
this.view = buildHulyApplicationMenu()
|
||||
this.view = buildHulyApplicationMenu(minimizeToTrayEnabled)
|
||||
}
|
||||
|
||||
public getView (): HTMLElement {
|
||||
|
||||
@@ -14,113 +14,113 @@
|
||||
//
|
||||
|
||||
export class TitleBarMenuState {
|
||||
private altModeActive: boolean = false
|
||||
private topLevelMenuExpanded: boolean = false
|
||||
private focusedTopLevelMenuIndex: number | null = null
|
||||
private focusedChildMenuIndex: number | null = null
|
||||
private altModeActive: boolean = false
|
||||
private topLevelMenuExpanded: boolean = false
|
||||
private focusedTopLevelMenuIndex: number | null = null
|
||||
private focusedChildMenuIndex: number | null = null
|
||||
|
||||
private readonly topLevelMenuCount: () => number;
|
||||
private readonly menuItemsCount: (topLevelMenuIndex: number) => number;
|
||||
private readonly topLevelMenuCount: () => number
|
||||
private readonly menuItemsCount: (topLevelMenuIndex: number) => number
|
||||
|
||||
public constructor(
|
||||
topLevelMenuCount: () => number,
|
||||
menuItemsCount: (topLevelMenuIndex: number) => number
|
||||
) {
|
||||
this.topLevelMenuCount = topLevelMenuCount
|
||||
this.menuItemsCount = menuItemsCount
|
||||
public constructor (
|
||||
topLevelMenuCount: () => number,
|
||||
menuItemsCount: (topLevelMenuIndex: number) => number
|
||||
) {
|
||||
this.topLevelMenuCount = topLevelMenuCount
|
||||
this.menuItemsCount = menuItemsCount
|
||||
}
|
||||
|
||||
public focusChildMenu (): void {
|
||||
if (this.focusedTopLevelMenuIndex != null && this.topLevelMenuExpanded) {
|
||||
this.focusedChildMenuIndex = 0
|
||||
}
|
||||
}
|
||||
|
||||
public expandTopLevelMenu (topLevelMenuIndex: number): void {
|
||||
if (topLevelMenuIndex < 0 || topLevelMenuIndex >= this.topLevelMenuCount()) {
|
||||
return
|
||||
}
|
||||
|
||||
public focusChildMenu() {
|
||||
if (null != this.focusedTopLevelMenuIndex && this.topLevelMenuExpanded) {
|
||||
this.focusedChildMenuIndex = 0
|
||||
}
|
||||
if (this.focusedTopLevelMenuIndex === topLevelMenuIndex && this.topLevelMenuExpanded) {
|
||||
this.closeAll()
|
||||
} else {
|
||||
this.focusedTopLevelMenuIndex = topLevelMenuIndex
|
||||
this.topLevelMenuExpanded = true
|
||||
}
|
||||
}
|
||||
|
||||
public exitAltMode (): void {
|
||||
this.altModeActive = false
|
||||
}
|
||||
|
||||
public enterAltMode (topLevelMenuIndex: number | null): void {
|
||||
this.altModeActive = true
|
||||
|
||||
if (topLevelMenuIndex == null || topLevelMenuIndex < 0 || topLevelMenuIndex >= this.topLevelMenuCount()) {
|
||||
return
|
||||
}
|
||||
|
||||
public expandTopLevelMenu(topLevelMenuIndex: number): void {
|
||||
if (0 > topLevelMenuIndex || topLevelMenuIndex >= this.topLevelMenuCount()) {
|
||||
return
|
||||
}
|
||||
|
||||
if (this.focusedTopLevelMenuIndex === topLevelMenuIndex && this.topLevelMenuExpanded) {
|
||||
this.closeAll()
|
||||
} else {
|
||||
this.focusedTopLevelMenuIndex = topLevelMenuIndex
|
||||
this.topLevelMenuExpanded = true
|
||||
}
|
||||
this.focusedTopLevelMenuIndex = topLevelMenuIndex
|
||||
}
|
||||
|
||||
public closeAll (): void {
|
||||
this.altModeActive = false
|
||||
this.topLevelMenuExpanded = false
|
||||
this.focusedTopLevelMenuIndex = null
|
||||
this.focusedChildMenuIndex = null
|
||||
}
|
||||
|
||||
public defocus (): void {
|
||||
if (this.topLevelMenuExpanded) {
|
||||
this.topLevelMenuExpanded = false
|
||||
this.focusedChildMenuIndex = null
|
||||
} else {
|
||||
this.closeAll()
|
||||
}
|
||||
}
|
||||
|
||||
public moveFocusHorizontal (increment: number): void {
|
||||
if (Math.abs(increment) !== 1 || this.focusedTopLevelMenuIndex == null) {
|
||||
return
|
||||
}
|
||||
|
||||
public exitAltMode(): void {
|
||||
this.altModeActive = false
|
||||
const topLevelMenuCount = this.topLevelMenuCount()
|
||||
this.focusedTopLevelMenuIndex = (this.focusedTopLevelMenuIndex + topLevelMenuCount + increment) % topLevelMenuCount
|
||||
|
||||
if (this.topLevelMenuExpanded) {
|
||||
this.focusedChildMenuIndex = 0
|
||||
}
|
||||
}
|
||||
|
||||
public moveFocusVertical (increment: number): void {
|
||||
if (Math.abs(increment) !== 1 || this.focusedTopLevelMenuIndex == null) {
|
||||
return
|
||||
}
|
||||
|
||||
public enterAltMode(topLevelMenuIndex: number | null): void {
|
||||
this.altModeActive = true
|
||||
|
||||
if (null == topLevelMenuIndex || 0 > topLevelMenuIndex || topLevelMenuIndex >= this.topLevelMenuCount()) {
|
||||
return
|
||||
}
|
||||
|
||||
this.focusedTopLevelMenuIndex = topLevelMenuIndex
|
||||
const menuItemsCount = this.menuItemsCount(this.focusedTopLevelMenuIndex)
|
||||
|
||||
this.topLevelMenuExpanded = true
|
||||
|
||||
if (menuItemsCount === 0) {
|
||||
return
|
||||
}
|
||||
|
||||
public closeAll(): void {
|
||||
this.altModeActive = false
|
||||
this.topLevelMenuExpanded = false
|
||||
this.focusedTopLevelMenuIndex = null
|
||||
this.focusedChildMenuIndex = null
|
||||
}
|
||||
this.focusedChildMenuIndex = ((this.focusedChildMenuIndex ?? -1) + menuItemsCount + increment) % menuItemsCount
|
||||
}
|
||||
|
||||
public defocus(): void {
|
||||
if (this.topLevelMenuExpanded) {
|
||||
this.topLevelMenuExpanded = false
|
||||
this.focusedChildMenuIndex = null
|
||||
} else {
|
||||
this.closeAll();
|
||||
}
|
||||
}
|
||||
public get isAltModeActive (): boolean {
|
||||
return this.altModeActive
|
||||
}
|
||||
|
||||
public moveFocusHorizontal(increment: number): void {
|
||||
if (Math.abs(increment) != 1 || null == this.focusedTopLevelMenuIndex) {
|
||||
return;
|
||||
}
|
||||
|
||||
const topLevelMenuCount = this.topLevelMenuCount()
|
||||
this.focusedTopLevelMenuIndex = (this.focusedTopLevelMenuIndex + topLevelMenuCount + increment) % topLevelMenuCount
|
||||
|
||||
if (this.topLevelMenuExpanded) {
|
||||
this.focusedChildMenuIndex = 0
|
||||
}
|
||||
}
|
||||
public get isTopLevelMenuExpanded (): boolean {
|
||||
return this.topLevelMenuExpanded
|
||||
}
|
||||
|
||||
public moveFocusVertical(increment: number): void {
|
||||
if (Math.abs(increment) != 1 || null == this.focusedTopLevelMenuIndex) {
|
||||
return;
|
||||
}
|
||||
public get FocusedTopLevelMenuIndex (): number | null {
|
||||
return this.focusedTopLevelMenuIndex
|
||||
}
|
||||
|
||||
const menuItemsCount = this.menuItemsCount(this.focusedTopLevelMenuIndex)
|
||||
|
||||
this.topLevelMenuExpanded = true
|
||||
|
||||
if (menuItemsCount === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.focusedChildMenuIndex = ((this.focusedChildMenuIndex ?? -1) + menuItemsCount + increment) % menuItemsCount
|
||||
}
|
||||
|
||||
public get isAltModeActive(): boolean {
|
||||
return this.altModeActive
|
||||
}
|
||||
|
||||
public get isTopLevelMenuExpanded(): boolean {
|
||||
return this.topLevelMenuExpanded
|
||||
}
|
||||
|
||||
public get FocusedTopLevelMenuIndex(): number | null {
|
||||
return this.focusedTopLevelMenuIndex
|
||||
}
|
||||
|
||||
public get FocusedChildMenuIndex(): number | null {
|
||||
return this.focusedChildMenuIndex
|
||||
}
|
||||
}
|
||||
public get FocusedChildMenuIndex (): number | null {
|
||||
return this.focusedChildMenuIndex
|
||||
}
|
||||
}
|
||||
|
||||
+30
-3
@@ -56,6 +56,7 @@ export interface Config {
|
||||
PULSE_URL?: string
|
||||
PASSWORD_STRICTNESS?: 'very_strict' | 'strict' | 'normal' | 'none'
|
||||
EXCLUDED_APPLICATIONS_FOR_ANONYMOUS?: string
|
||||
HULYLAKE_URL?: string
|
||||
}
|
||||
|
||||
export interface Branding {
|
||||
@@ -84,6 +85,7 @@ export const CommandOpenPlanner = 'open-planner' as const
|
||||
export const CommandSelectWorkspace = 'select-workspace' as const
|
||||
export const CommandLogout = 'logout' as const
|
||||
export const CommandOpenApplication = 'open-application' as const
|
||||
export const CommandCloseTab = 'close-tab' as const
|
||||
|
||||
export type Command =
|
||||
typeof CommandOpenSettings |
|
||||
@@ -92,7 +94,8 @@ export type Command =
|
||||
typeof CommandOpenPlanner |
|
||||
typeof CommandSelectWorkspace |
|
||||
typeof CommandLogout |
|
||||
typeof CommandOpenApplication
|
||||
typeof CommandOpenApplication |
|
||||
typeof CommandCloseTab
|
||||
|
||||
export interface NotificationParams {
|
||||
title: string
|
||||
@@ -104,8 +107,26 @@ export interface NotificationParams {
|
||||
objectClass?: Ref<Class<Doc>>
|
||||
}
|
||||
|
||||
export const MenuBarActions = ['settings', 'select-workspace', 'logout', 'exit', 'undo', 'redo', 'cut', 'copy', 'paste', 'delete', 'select-all', 'reload', 'force-reload', 'toggle-devtools',
|
||||
'zoom-in', 'zoom-out', 'restore-size', 'toggle-fullscreen'] as const
|
||||
export const MenuBarActions = [
|
||||
'settings',
|
||||
'select-workspace',
|
||||
'logout',
|
||||
'exit',
|
||||
'undo',
|
||||
'redo',
|
||||
'cut',
|
||||
'copy',
|
||||
'paste',
|
||||
'delete',
|
||||
'select-all',
|
||||
'reload',
|
||||
'force-reload',
|
||||
'toggle-devtools',
|
||||
'zoom-in',
|
||||
'zoom-out',
|
||||
'restore-size',
|
||||
'toggle-fullscreen',
|
||||
'toggle-minimize-to-tray'] as const
|
||||
|
||||
export type MenuBarAction = typeof MenuBarActions[number]
|
||||
|
||||
@@ -151,4 +172,10 @@ export interface IPCMainExposed {
|
||||
executeMenuBarAction: (action: MenuBarAction) => void
|
||||
|
||||
rebuildJumpList: (spares: JumpListSpares) => void
|
||||
|
||||
isMinimizeToTrayEnabled: () => Promise<boolean>
|
||||
onMinimizeToTraySettingChanged: (callback: (enabled: boolean) => void) => void
|
||||
}
|
||||
|
||||
export type SendCommandDelegate = (cmd: Command, ...args: any[]) => void
|
||||
export type WindowAction = () => void
|
||||
|
||||
+9
-11
@@ -1,11 +1,9 @@
|
||||
$MODEL_VERSION = node ../common/scripts/show_version.js | Out-String
|
||||
$MODEL_VERSION = $MODEL_VERSION.Trim()
|
||||
|
||||
$VERSION = node ../common/scripts/show_tag.js | Out-String
|
||||
$VERSION = $VERSION.Trim()
|
||||
|
||||
# Set environment variable
|
||||
$env:NODE_ENV = "development"
|
||||
|
||||
# Run Electron
|
||||
electron --no-sandbox ./
|
||||
$MODEL_VERSION = node ../common/scripts/show_version.js | Out-String
|
||||
$MODEL_VERSION = $MODEL_VERSION.Trim()
|
||||
|
||||
$VERSION = node ../common/scripts/show_tag.js | Out-String
|
||||
$VERSION = $VERSION.Trim()
|
||||
|
||||
$env:NODE_ENV = "development"
|
||||
|
||||
electron --no-sandbox ./
|
||||
|
||||
@@ -236,7 +236,6 @@ services:
|
||||
- LAST_NAME_FIRST=true
|
||||
- BRANDING_PATH=/var/cfg/branding.json
|
||||
- AI_BOT_URL=http://huly.local:4010
|
||||
- MSG2FILE_URL=http://huly.local:9087
|
||||
- COMMUNICATION_TIME_LOGGING_ENABLED=true
|
||||
restart: unless-stopped
|
||||
fulltext_cockroach:
|
||||
@@ -285,6 +284,22 @@ services:
|
||||
- DB_URL=${DB_CR_URL}
|
||||
- BUCKETS=blobs,eu|http://minio:9000?accessKey=minioadmin&secretKey=minioadmin
|
||||
restart: unless-stopped
|
||||
redis:
|
||||
image: redis:8.0.2-alpine3.21
|
||||
ports:
|
||||
- 6379:6379
|
||||
restart: unless-stopped
|
||||
hulypulse:
|
||||
image: hardcoreeng/hulypulse
|
||||
depends_on:
|
||||
redis:
|
||||
condition: service_started
|
||||
ports:
|
||||
- 8099:8099
|
||||
environment:
|
||||
- HULY_REDIS_URLS=redis://redis:6379
|
||||
- HULY_BIND_PORT=8099
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
db:
|
||||
dbpg:
|
||||
|
||||
+3
-16
@@ -286,6 +286,7 @@ services:
|
||||
# - DISABLE_SIGNUP=true
|
||||
- OTEL_EXPORTER_OTLP_ENDPOINT=http://jaeger:4318/v1/traces
|
||||
- PULSE_URL=ws://huly.local:8099/ws
|
||||
- HULYLAKE_URL=http://huly.local:8096
|
||||
restart: unless-stopped
|
||||
transactor_cockroach:
|
||||
image: hardcoreeng/transactor
|
||||
@@ -321,13 +322,13 @@ services:
|
||||
- LAST_NAME_FIRST=true
|
||||
- BRANDING_PATH=/var/cfg/branding.json
|
||||
- AI_BOT_URL=http://huly.local:4010
|
||||
- MSG2FILE_URL=http://huly.local:9087
|
||||
- COMMUNICATION_TIME_LOGGING_ENABLED=true
|
||||
- RATE_LIMIT_MAX=250 # 250 requests per 30 seconds
|
||||
- RATE_LIMIT_WINDOW=30000
|
||||
- FILES_URL=http://huly.local:4030/blob/:workspace/:blobId/:filename
|
||||
- COMMUNICATION_API_ENABLED=true
|
||||
- OTEL_EXPORTER_OTLP_ENDPOINT=http://jaeger:4318/v1/traces
|
||||
- HYLYLAKE_URL=http://huly.local:8096
|
||||
restart: unless-stopped
|
||||
rekoni:
|
||||
image: hardcoreeng/rekoni-service
|
||||
@@ -366,6 +367,7 @@ services:
|
||||
- REKONI_URL=http://huly.local:4004
|
||||
- ACCOUNTS_URL=http://huly.local:3000
|
||||
- OTEL_EXPORTER_OTLP_ENDPOINT=http://jaeger:4318/v1/traces
|
||||
- HULYLAKE_URL=http://huly.local:8096
|
||||
print:
|
||||
image: hardcoreeng/print
|
||||
extra_hosts:
|
||||
@@ -440,21 +442,6 @@ services:
|
||||
# - POSTHOG_HOST=${POSTHOG_HOST}
|
||||
# - POSTHOG_API_KEY=${POSTHOG_API_KEY}
|
||||
# - MAX_PAYLOAD_SIZE=10mb
|
||||
msg2file:
|
||||
image: hardcoreeng/msg2file
|
||||
ports:
|
||||
- 9087:9087
|
||||
extra_hosts:
|
||||
- 'huly.local:host-gateway'
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
- ACCOUNTS_URL=http://huly.local:3000
|
||||
- DB_URL=postgresql://root@huly.local:26257/defaultdb?sslmode=disable
|
||||
- PORT=9087
|
||||
- SECRET=secret
|
||||
- SERVICE_ID=msg2file-service
|
||||
- STORAGE_CONFIG=${STORAGE_CONFIG}
|
||||
- OTEL_EXPORTER_OTLP_ENDPOINT=http://jaeger:4318/v1/traces
|
||||
export:
|
||||
image: hardcoreeng/export
|
||||
extra_hosts:
|
||||
|
||||
@@ -28,5 +28,6 @@
|
||||
"COMMUNICATION_API_ENABLED": "true",
|
||||
"BACKUP_URL": "http://huly.local:4039/api/backup",
|
||||
"PULSE_URL": "ws://huly.local:8099/ws",
|
||||
"HULYLAKE_URL": "http://huly.local:8096",
|
||||
"EXCLUDED_APPLICATIONS_FOR_ANONYMOUS": "[\"chunter\", \"notification\"]"
|
||||
}
|
||||
|
||||
@@ -201,7 +201,8 @@ export interface Config {
|
||||
COMMUNICATION_API_ENABLED?: string
|
||||
BILLING_URL?: string,
|
||||
EXCLUDED_APPLICATIONS_FOR_ANONYMOUS?: string,
|
||||
PULSE_URL?: string
|
||||
PULSE_URL?: string,
|
||||
HULYLAKE_URL?: string
|
||||
}
|
||||
|
||||
export interface Branding {
|
||||
@@ -491,10 +492,11 @@ export async function configurePlatform() {
|
||||
setMetadata(billingPlugin.metadata.BillingURL, config.BILLING_URL ?? '')
|
||||
|
||||
setMetadata(presentation.metadata.PulseUrl, config.PULSE_URL)
|
||||
setMetadata(presentation.metadata.HulylakeUrl, config.HULYLAKE_URL ?? '')
|
||||
|
||||
const languages = myBranding.languages
|
||||
? (myBranding.languages as string).split(',').map((l) => l.trim())
|
||||
: ['en', 'ru', 'es', 'pt', 'zh', 'fr', 'cs', 'it', 'de', 'ja']
|
||||
: ['en', 'ru', 'es', 'pt', 'zh', 'fr', 'cs', 'it', 'de', 'ja', 'tr']
|
||||
|
||||
setMetadata(uiPlugin.metadata.Languages, languages)
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
"docker:staging": "../../common/scripts/docker_tag.sh hardcoreeng/tool staging",
|
||||
"docker:push": "../../common/scripts/docker_tag.sh hardcoreeng/tool",
|
||||
"run-local-mongo": "rush bundle --to @hcengineering/tool >/dev/null && cross-env SERVER_SECRET=secret FULLTEXT_URL=http://localhost:4700 ACCOUNTS_URL=http://localhost:3000 TRANSACTOR_URL=ws://localhost:3333 MINIO_ACCESS_KEY=minioadmin MINIO_SECRET_KEY=minioadmin MINIO_ENDPOINT=localhost ACCOUNT_DB_URL=mongodb://localhost:27017 DB_URL=mongodb://localhost:27017 TELEGRAM_DATABASE=telegram-service REKONI_URL=http://localhost:4004 MODEL_VERSION=$(node ../../common/scripts/show_version.js) GIT_REVISION=$(git describe --all --long) QUEUE_CONFIG='localhost:19092' node --expose-gc --max-old-space-size=18000 ./bundle/bundle.js",
|
||||
"run-local": "rush bundle --to @hcengineering/tool >/dev/null && cross-env SERVER_SECRET=secret FULLTEXT_URL=http://localhost:4702 ACCOUNTS_URL=http://localhost:3000 TRANSACTOR_URL=ws://localhost:3332 STORAGE_CONFIG='datalake|http://huly.local:4030' ACCOUNT_DB_URL=postgresql://root@huly.local:26257/defaultdb?sslmode=disable DB_URL=postgresql://root@huly.local:26257/defaultdb?sslmode=disable TELEGRAM_DATABASE=telegram-service REKONI_URL=http://localhost:4004 REGION_INFO='cockroach|CockroachDB' MODEL_VERSION=$(node ../../common/scripts/show_version.js) GIT_REVISION=$(git describe --all --long) QUEUE_CONFIG='localhost:19092' node --expose-gc --max-old-space-size=18000 $TOOL_OPT ./bundle/bundle.js",
|
||||
"run-local": "rush bundle --to @hcengineering/tool >/dev/null && cross-env SERVER_SECRET=secret FULLTEXT_URL=http://localhost:4702 ACCOUNTS_URL=http://localhost:3000 TRANSACTOR_URL=ws://localhost:3332 STORAGE_CONFIG='datalake|http://huly.local:4030' HULYLAKE_URL=http://huly.local:8096 ACCOUNT_DB_URL=postgresql://root@huly.local:26257/defaultdb?sslmode=disable DB_URL=postgresql://root@huly.local:26257/defaultdb?sslmode=disable TELEGRAM_DATABASE=telegram-service REKONI_URL=http://localhost:4004 REGION_INFO='cockroach|CockroachDB' MODEL_VERSION=$(node ../../common/scripts/show_version.js) GIT_REVISION=$(git describe --all --long) QUEUE_CONFIG='localhost:19092' node --expose-gc --max-old-space-size=18000 $TOOL_OPT ./bundle/bundle.js",
|
||||
"run-local-brk": "rush bundle --to @hcengineering/tool >/dev/null && cross-env SERVER_SECRET=secret ACCOUNTS_URL=http://localhost:3000 TRANSACTOR_URL=ws://localhost:3333 MINIO_ACCESS_KEY=minioadmin MINIO_SECRET_KEY=minioadmin MINIO_ENDPOINT=localhost ACCOUNT_DB_URL=mongodb://localhost:27017 DB_URL=mongodb://localhost:27017 TELEGRAM_DATABASE=telegram-service REKONI_URL=http://localhost:4004 MODEL_VERSION=$(node ../../common/scripts/show_version.js) GIT_REVISION=$(git describe --all --long) node --inspect-brk --enable-source-maps --max-old-space-size=18000 ./bundle/bundle.js",
|
||||
"run": "rush bundle --to @hcengineering/tool >/dev/null && cross-env node --max-old-space-size=8000 ./bundle/bundle.js",
|
||||
"upgrade-mongo": "rushx run-local-mongo upgrade-workspace -- $1",
|
||||
@@ -55,7 +55,8 @@
|
||||
"@types/request": "~2.48.8",
|
||||
"jest": "^29.7.0",
|
||||
"ts-jest": "^29.1.1",
|
||||
"@types/jest": "^29.5.5"
|
||||
"@types/jest": "^29.5.5",
|
||||
"@types/js-yaml": "^4.0.9"
|
||||
},
|
||||
"dependencies": {
|
||||
"@elastic/elasticsearch": "^7.17.14",
|
||||
@@ -180,6 +181,8 @@
|
||||
"msgpackr-extract": "^3.0.3",
|
||||
"@hcengineering/kafka": "^0.6.0",
|
||||
"@hcengineering/api-client": "^0.6.0",
|
||||
"@faker-js/faker": "^8.4.1"
|
||||
"@faker-js/faker": "^8.4.1",
|
||||
"@hcengineering/hulylake-client": "^0.6.0",
|
||||
"@hcengineering/communication-types": "^0.1.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,895 @@
|
||||
// Copyright © 2025 Hardcore Engineering Inc.
|
||||
//
|
||||
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License. You may
|
||||
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
//
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { type Workspace } from '@hcengineering/account'
|
||||
import { type HulylakeClient, type JsonPatch } from '@hcengineering/hulylake-client'
|
||||
import type postgres from 'postgres'
|
||||
import {
|
||||
generateUuid,
|
||||
groupByArray,
|
||||
type MeasureContext,
|
||||
notEmpty,
|
||||
type PersonId,
|
||||
type PersonUuid,
|
||||
type WorkspaceUuid
|
||||
} from '@hcengineering/core'
|
||||
import {
|
||||
type Attachment,
|
||||
type AttachmentData,
|
||||
type AttachmentDoc,
|
||||
type AttachmentID,
|
||||
type AttachmentUpdateData,
|
||||
type BlobID,
|
||||
type CardID,
|
||||
type CardType,
|
||||
type Emoji,
|
||||
type Markdown,
|
||||
type MessageDoc,
|
||||
type MessageExtra,
|
||||
type MessageID,
|
||||
type MessagesDoc,
|
||||
type MessagesGroup,
|
||||
type MessagesGroupDoc,
|
||||
type MessagesGroupsDoc,
|
||||
MessageType,
|
||||
type SocialID,
|
||||
type ThreadDoc
|
||||
} from '@hcengineering/communication-types'
|
||||
import { type AccountClient } from '@hcengineering/account-client'
|
||||
|
||||
const MAX_MESSAGES_SIZE = 95 * 1024
|
||||
|
||||
const MESSAGES_TABLE = 'communication.message'
|
||||
const PATCH_TABLE = 'communication.patch'
|
||||
const ATTACHMENT_TABLE = 'communication.attachment'
|
||||
const REACTION_TABLE = 'communication.reaction'
|
||||
const THREAD_INDEX_TABLE = 'communication.thread_index'
|
||||
|
||||
export async function migrateWorkspaceMessages (
|
||||
ctx: MeasureContext,
|
||||
ws: Workspace,
|
||||
card: CardID | undefined,
|
||||
db: postgres.Sql,
|
||||
hulylake: HulylakeClient,
|
||||
accountClient: AccountClient,
|
||||
personUuidBySocialId: Map<PersonId, PersonUuid>
|
||||
): Promise<void> {
|
||||
await migrateMessages(ctx, ws, card, db, hulylake, accountClient, personUuidBySocialId)
|
||||
}
|
||||
|
||||
async function migrateMessages (
|
||||
ctx: MeasureContext,
|
||||
ws: Workspace,
|
||||
card: CardID | undefined,
|
||||
db: postgres.Sql,
|
||||
hulylake: HulylakeClient,
|
||||
accountClient: AccountClient,
|
||||
personUuidBySocialId: Map<PersonId, PersonUuid>
|
||||
): Promise<void> {
|
||||
const force = card != null && card !== ''
|
||||
const cards = card != null && card !== '' ? [card] : await getMessagesCards(ws.uuid, db)
|
||||
ctx.info(`Cards for migration: ${cards.length}, force: ${force}`)
|
||||
let i = 0
|
||||
for (const card of cards) {
|
||||
try {
|
||||
i++
|
||||
const groups = await getGroups(ctx, hulylake, card)
|
||||
const lastGroup = groups[groups.length - 1]
|
||||
|
||||
ctx.info(`Migrating messages for card ${card} index ${i} from ${lastGroup?.toDate?.toISOString() ?? 'null'}`)
|
||||
const cursor = getMessagesCursor(ws.uuid, card, db, force ? undefined : lastGroup?.toDate)
|
||||
|
||||
for await (const messages of cursor) {
|
||||
ctx.info(`Messages count ${messages.length}`)
|
||||
if (messages.length === 0) continue
|
||||
const oldMessages = messages.map(deserializeOldMessage).filter(notEmpty)
|
||||
await migrateMessagesBatch(ctx, card, hulylake, accountClient, personUuidBySocialId, oldMessages)
|
||||
}
|
||||
} catch (e) {
|
||||
ctx.error('Failed to migrate messages for card', card)
|
||||
ctx.error('Error', { error: e })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function migrateMessagesBatch (
|
||||
ctx: MeasureContext,
|
||||
cardId: CardID,
|
||||
hulylake: HulylakeClient,
|
||||
accountClient: AccountClient,
|
||||
personUuidBySocialId: Map<PersonId, PersonUuid>,
|
||||
messages: OldMessage[]
|
||||
): Promise<void> {
|
||||
const newMessages: MessageDoc[] = []
|
||||
for (const oldMessage of messages) {
|
||||
const newMessageDoc = await oldMessageToNewMessageDoc(oldMessage, accountClient, personUuidBySocialId)
|
||||
newMessages.push(newMessageDoc)
|
||||
}
|
||||
const chunks = chunkMessagesBySize(ctx, newMessages)
|
||||
for (const chunk of chunks) {
|
||||
const fromDate = chunk.from
|
||||
const toDate = chunk.to
|
||||
const blobId = generateUuid() as BlobID
|
||||
const newGroupDoc: MessagesGroupDoc = {
|
||||
cardId,
|
||||
blobId,
|
||||
fromDate: fromDate.toISOString(),
|
||||
toDate: toDate.toISOString(),
|
||||
count: chunk.count
|
||||
}
|
||||
const newMessagesDoc: MessagesDoc = {
|
||||
cardId,
|
||||
fromDate: fromDate.toISOString(),
|
||||
toDate: toDate.toISOString(),
|
||||
messages: chunk.chunk,
|
||||
language: 'original'
|
||||
}
|
||||
|
||||
const jsonPatches: JsonPatch[] = [
|
||||
{
|
||||
hop: 'add',
|
||||
path: `/${blobId}`,
|
||||
value: newGroupDoc,
|
||||
safe: true
|
||||
} as const
|
||||
]
|
||||
|
||||
await hulylake.patchJson(`${cardId}/messages/groups`, jsonPatches, undefined, {
|
||||
maxRetries: 3,
|
||||
isRetryable: () => true,
|
||||
delayStrategy: {
|
||||
getDelay: () => 1000
|
||||
}
|
||||
})
|
||||
await hulylake.putJson(`${cardId}/messages/${blobId}`, newMessagesDoc, undefined, {
|
||||
maxRetries: 3,
|
||||
isRetryable: () => true,
|
||||
delayStrategy: {
|
||||
getDelay: () => 1000
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
async function getGroups (ctx: MeasureContext, hulylake: HulylakeClient, cardId: CardID): Promise<MessagesGroup[]> {
|
||||
const res = await hulylake.getJson<MessagesGroupsDoc>(`${cardId}/messages/groups`, {
|
||||
maxRetries: 3,
|
||||
isRetryable: () => true,
|
||||
delayStrategy: {
|
||||
getDelay: () => 500
|
||||
}
|
||||
})
|
||||
if (res.body != null) {
|
||||
return deserializeMessagesGroups(res.body)
|
||||
}
|
||||
|
||||
await hulylake.putJson(`${cardId}/messages/groups`, {}, undefined, {
|
||||
maxRetries: 3,
|
||||
isRetryable: () => true,
|
||||
delayStrategy: {
|
||||
getDelay: () => 1000
|
||||
}
|
||||
})
|
||||
return []
|
||||
}
|
||||
|
||||
function chunkMessagesBySize (
|
||||
ctx: MeasureContext,
|
||||
messages: MessageDoc[]
|
||||
): Array<{ chunk: Record<MessageID, MessageDoc>, from: Date, to: Date, count: number }> {
|
||||
const chunks: Array<{ chunk: Record<MessageID, MessageDoc>, from: Date, to: Date, count: number }> = []
|
||||
|
||||
let current: { chunk: Record<MessageID, MessageDoc>, from?: Date, to?: Date, count: number } = {
|
||||
chunk: {},
|
||||
count: 0
|
||||
}
|
||||
|
||||
for (const msg of messages) {
|
||||
current.chunk[msg.id] = msg
|
||||
|
||||
if (sizeOfJson(current.chunk) <= MAX_MESSAGES_SIZE) {
|
||||
const d = new Date(msg.created)
|
||||
current.count += 1
|
||||
current.from = current.from != null ? (d < current.from ? d : current.from) : d
|
||||
current.to = current.to != null ? (d > current.to ? d : current.to) : d
|
||||
continue
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
|
||||
delete current.chunk[msg.id]
|
||||
|
||||
if (sizeOfJson(msg) > MAX_MESSAGES_SIZE) {
|
||||
ctx.warn('Message size is too big, skipping')
|
||||
continue
|
||||
}
|
||||
|
||||
if (Object.keys(current.chunk).length === 0) {
|
||||
ctx.warn('Message size is too big, skipping')
|
||||
continue
|
||||
}
|
||||
|
||||
if (current.from != null && current.to != null) {
|
||||
chunks.push({
|
||||
chunk: current.chunk,
|
||||
from: current.from,
|
||||
to: current.to,
|
||||
count: current.count
|
||||
})
|
||||
}
|
||||
|
||||
current = {
|
||||
chunk: {},
|
||||
count: 0
|
||||
}
|
||||
current.chunk[msg.id] = msg
|
||||
|
||||
if (sizeOfJson(current.chunk) <= MAX_MESSAGES_SIZE) {
|
||||
const d = new Date(msg.created)
|
||||
current.from = current.from != null ? (d < current.from ? d : current.from) : d
|
||||
current.to = current.to != null ? (d > current.to ? d : current.to) : d
|
||||
current.count = 1
|
||||
} else {
|
||||
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
|
||||
delete current.chunk[msg.id]
|
||||
ctx.warn(`Message ${msg.id} still exceeds limit, skipping`)
|
||||
}
|
||||
}
|
||||
|
||||
if (current.from != null && current.to != null) {
|
||||
chunks.push({
|
||||
chunk: current.chunk,
|
||||
from: current.from,
|
||||
to: current.to,
|
||||
count: current.count
|
||||
})
|
||||
}
|
||||
|
||||
return chunks
|
||||
}
|
||||
|
||||
async function oldMessageToNewMessageDoc (
|
||||
oldMessage: OldMessage,
|
||||
accountClient: AccountClient,
|
||||
personUuidBySocialId: Map<PersonId, PersonUuid>
|
||||
): Promise<MessageDoc> {
|
||||
const reactions: Record<Emoji, Record<PersonUuid, { count: number, date: string }>> = {}
|
||||
const attachments: Record<AttachmentID, AttachmentDoc> = {}
|
||||
const threads: Record<CardID, ThreadDoc> = {}
|
||||
|
||||
const reactionsByEmoji = groupByArray(oldMessage.reactions, (it) => it.reaction)
|
||||
for (const [_emoji, oldReactions] of reactionsByEmoji.entries()) {
|
||||
const emoji = _emoji as Emoji
|
||||
if (reactions[emoji] == null) {
|
||||
reactions[emoji] = {}
|
||||
}
|
||||
for (const oldReaction of oldReactions) {
|
||||
const personUuid = await getPersonUuidBySocialId(accountClient, personUuidBySocialId, oldReaction.creator)
|
||||
|
||||
if (personUuid == null) continue
|
||||
if (reactions[emoji][personUuid] == null) {
|
||||
reactions[emoji][personUuid] = {
|
||||
count: 1,
|
||||
date: oldReaction.created.toISOString()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const oldAttachment of oldMessage.attachments) {
|
||||
attachments[oldAttachment.id] = {
|
||||
id: oldAttachment.id,
|
||||
mimeType:
|
||||
oldAttachment.mimeType ??
|
||||
(oldAttachment as any)?.params?.mimeType ??
|
||||
(oldAttachment as any).type ??
|
||||
(oldAttachment as any)?.params?.type ??
|
||||
'',
|
||||
params: oldAttachment.params,
|
||||
creator: oldAttachment.creator,
|
||||
created: oldAttachment.created.toISOString(),
|
||||
modified: oldAttachment.modified?.toISOString() ?? null
|
||||
}
|
||||
}
|
||||
|
||||
if (oldMessage.thread != null) {
|
||||
threads[oldMessage.thread.threadId] = {
|
||||
threadId: oldMessage.thread.threadId,
|
||||
threadType: oldMessage.thread.threadType,
|
||||
repliesCount: oldMessage.thread.repliesCount,
|
||||
lastReplyDate: oldMessage.thread.lastReply?.toISOString() ?? null,
|
||||
repliedPersons: {}
|
||||
}
|
||||
}
|
||||
return {
|
||||
id: oldMessage.id,
|
||||
cardId: oldMessage.cardId,
|
||||
type: oldMessage.type === 'activity' ? MessageType.Activity : MessageType.Text,
|
||||
content: oldMessage.content,
|
||||
extra: oldMessage.extra ?? {},
|
||||
creator: oldMessage.creator,
|
||||
created: oldMessage.created.toISOString(),
|
||||
modified: oldMessage.edited?.toISOString() ?? null,
|
||||
reactions,
|
||||
attachments,
|
||||
threads
|
||||
}
|
||||
}
|
||||
|
||||
function sizeOfJson (obj: unknown): number {
|
||||
return Buffer.byteLength(JSON.stringify(obj), 'utf8')
|
||||
}
|
||||
|
||||
async function getPersonUuidBySocialId (
|
||||
ccountClient: AccountClient,
|
||||
personUuidBySocialId: Map<PersonId, PersonUuid>,
|
||||
socialId: PersonId
|
||||
): Promise<PersonUuid | undefined> {
|
||||
if (personUuidBySocialId.has(socialId)) {
|
||||
return personUuidBySocialId.get(socialId)
|
||||
}
|
||||
const personUuid = await ccountClient.findPersonBySocialId(socialId)
|
||||
if (personUuid != null) {
|
||||
personUuidBySocialId.set(socialId, personUuid)
|
||||
}
|
||||
|
||||
return personUuid
|
||||
}
|
||||
|
||||
async function getMessagesCards (ws: WorkspaceUuid, db: postgres.Sql): Promise<CardID[]> {
|
||||
const table = db(MESSAGES_TABLE)
|
||||
const res = await db`
|
||||
SELECT DISTINCT card_id
|
||||
FROM ${table}
|
||||
WHERE workspace_id = ${ws}
|
||||
`
|
||||
|
||||
return res.map((r) => r.card_id)
|
||||
}
|
||||
|
||||
function getMessagesCursor (
|
||||
ws: WorkspaceUuid,
|
||||
cardId: CardID,
|
||||
db: postgres.Sql,
|
||||
from?: Date
|
||||
): AsyncIterable<postgres.Row[]> {
|
||||
const messagesTable = db(MESSAGES_TABLE)
|
||||
const threadIndexTable = db(THREAD_INDEX_TABLE)
|
||||
const attachmentTable = db(ATTACHMENT_TABLE)
|
||||
const reactionTable = db(REACTION_TABLE)
|
||||
const patchTable = db(PATCH_TABLE)
|
||||
|
||||
return db`WITH
|
||||
limited_messages AS (
|
||||
SELECT *
|
||||
FROM ${messagesTable} m
|
||||
WHERE m.workspace_id = ${ws}
|
||||
AND m.card_id = ${cardId}
|
||||
${from != null ? db`AND m.created > ${from}` : db``}
|
||||
ORDER BY m.created ASC
|
||||
),
|
||||
|
||||
agg_attachments AS (
|
||||
SELECT
|
||||
a.workspace_id,
|
||||
a.card_id,
|
||||
a.message_id,
|
||||
jsonb_agg(jsonb_build_object(
|
||||
'id', a.id,
|
||||
'type', a.type,
|
||||
'params', a.params,
|
||||
'creator', a.creator,
|
||||
'created', a.created,
|
||||
'modified', a.modified
|
||||
)) AS attachments
|
||||
FROM ${attachmentTable} a
|
||||
INNER JOIN limited_messages m
|
||||
ON m.workspace_id = a.workspace_id
|
||||
AND m.card_id = a.card_id
|
||||
AND m.id = a.message_id
|
||||
GROUP BY a.workspace_id, a.card_id, a.message_id
|
||||
),
|
||||
|
||||
agg_reactions AS (
|
||||
SELECT
|
||||
r.workspace_id,
|
||||
r.card_id,
|
||||
r.message_id,
|
||||
jsonb_agg(jsonb_build_object(
|
||||
'reaction', r.reaction,
|
||||
'creator', r.creator,
|
||||
'created', r.created
|
||||
)) AS reactions
|
||||
FROM ${reactionTable} r
|
||||
INNER JOIN limited_messages m
|
||||
ON m.workspace_id = r.workspace_id
|
||||
AND m.card_id = r.card_id
|
||||
AND m.id = r.message_id
|
||||
GROUP BY r.workspace_id, r.card_id, r.message_id
|
||||
),
|
||||
|
||||
agg_patches AS (
|
||||
SELECT
|
||||
p.workspace_id,
|
||||
p.card_id,
|
||||
p.message_id,
|
||||
jsonb_agg(
|
||||
jsonb_build_object(
|
||||
'type', p.type,
|
||||
'data', p.data,
|
||||
'creator', p.creator,
|
||||
'created', p.created
|
||||
) ORDER BY p.created ASC
|
||||
) AS patches
|
||||
FROM ${patchTable} p
|
||||
INNER JOIN limited_messages m
|
||||
ON m.workspace_id = p.workspace_id
|
||||
AND m.card_id = p.card_id
|
||||
AND m.id = p.message_id
|
||||
GROUP BY p.workspace_id, p.card_id, p.message_id
|
||||
)
|
||||
|
||||
SELECT m.id::text,
|
||||
m.card_id,
|
||||
m.type,
|
||||
m.content,
|
||||
m.creator,
|
||||
m.created,
|
||||
m.data,
|
||||
t.thread_id AS thread_id,
|
||||
t.thread_type AS thread_type,
|
||||
t.replies_count::int AS replies_count,
|
||||
t.last_reply AS last_reply,
|
||||
COALESCE(a.attachments, '[]'::jsonb) AS attachments,
|
||||
COALESCE(r.reactions, '[]'::jsonb) AS reactions,
|
||||
COALESCE(p.patches, '[]'::jsonb) AS patches
|
||||
FROM limited_messages m
|
||||
LEFT JOIN ${threadIndexTable} t
|
||||
ON t.workspace_id = m.workspace_id
|
||||
AND t.card_id = m.card_id
|
||||
AND t.message_id = m.id
|
||||
LEFT JOIN agg_attachments a
|
||||
ON a.workspace_id = m.workspace_id
|
||||
AND a.card_id = m.card_id
|
||||
AND a.message_id = m.id
|
||||
LEFT JOIN agg_reactions r
|
||||
ON r.workspace_id = m.workspace_id
|
||||
AND r.card_id = m.card_id
|
||||
AND r.message_id = m.id
|
||||
LEFT JOIN agg_patches p
|
||||
ON p.workspace_id = m.workspace_id
|
||||
AND p.card_id = m.card_id
|
||||
AND p.message_id = m.id
|
||||
ORDER BY m.created ASC;`.cursor(200)
|
||||
}
|
||||
|
||||
function deserializeMessagesGroups (groups: MessagesGroupsDoc): MessagesGroup[] {
|
||||
return Object.values(groups)
|
||||
.map((group) => {
|
||||
const g: MessagesGroup = {
|
||||
cardId: group.cardId,
|
||||
blobId: group.blobId,
|
||||
fromDate: new Date(group.fromDate),
|
||||
toDate: new Date(group.toDate),
|
||||
count: Number(group.count)
|
||||
}
|
||||
|
||||
return g
|
||||
})
|
||||
.sort((a, b) => a.fromDate.getTime() - b.fromDate.getTime())
|
||||
}
|
||||
|
||||
function deserializePatch (raw: any): Patch {
|
||||
return {
|
||||
type: raw.type,
|
||||
messageId: String(raw.message_id) as MessageID,
|
||||
data: raw.data,
|
||||
creator: raw.creator,
|
||||
created: new Date(raw.created)
|
||||
}
|
||||
}
|
||||
|
||||
function deserializeOldMessage (raw: any): OldMessage | undefined {
|
||||
const patches: Patch[] = (raw.patches ?? []).map((it: any) => deserializePatch(it))
|
||||
const rawMessage: OldMessage = {
|
||||
id: String(raw.id) as MessageID,
|
||||
type: raw.type,
|
||||
cardId: raw.card_id,
|
||||
content: raw.content,
|
||||
creator: raw.creator,
|
||||
created: new Date(raw.created),
|
||||
removed: false,
|
||||
extra: raw.data,
|
||||
thread:
|
||||
raw.thread_id != null && raw.thread_type != null
|
||||
? {
|
||||
cardId: raw.card_id,
|
||||
messageId: String(raw.id) as MessageID,
|
||||
threadId: raw.thread_id,
|
||||
threadType: raw.thread_type,
|
||||
repliesCount: raw.replies_count != null ? Number(raw.replies_count) : 0,
|
||||
lastReply: raw.last_reply ?? new Date()
|
||||
}
|
||||
: undefined,
|
||||
reactions: (raw.reactions ?? []).map(deserializeReaction),
|
||||
attachments: (raw.attachments ?? []).map(deserializeAttachment)
|
||||
}
|
||||
|
||||
if (patches.length === 0) {
|
||||
return rawMessage
|
||||
}
|
||||
|
||||
return applyPatches(
|
||||
rawMessage,
|
||||
patches.filter((it) => it.type === PatchType.update || it.type === PatchType.remove)
|
||||
)
|
||||
}
|
||||
|
||||
function deserializeReaction (raw: any): OldReaction {
|
||||
return {
|
||||
reaction: raw.reaction,
|
||||
creator: raw.creator,
|
||||
created: new Date(raw.created)
|
||||
}
|
||||
}
|
||||
|
||||
function deserializeAttachment (raw: any): Attachment {
|
||||
return {
|
||||
id: String(raw.id) as AttachmentID,
|
||||
type: raw.type,
|
||||
params: raw.params,
|
||||
creator: raw.creator,
|
||||
created: new Date(raw.created),
|
||||
modified: raw.modified != null ? new Date(raw.modified) : undefined
|
||||
} as any as Attachment
|
||||
}
|
||||
|
||||
export function applyPatches (message: OldMessage, patches: Patch[]): OldMessage | undefined {
|
||||
if (patches.length === 0) return message
|
||||
|
||||
let result: OldMessage | undefined = message
|
||||
for (const p of patches) {
|
||||
result = applyPatch(message, p)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
function applyPatch (message: OldMessage, patch: Patch): OldMessage | undefined {
|
||||
if (message.removed) {
|
||||
return undefined
|
||||
}
|
||||
|
||||
switch (patch.type) {
|
||||
case PatchType.update: {
|
||||
if (patch.created.getTime() < (message.edited?.getTime() ?? 0)) {
|
||||
return message
|
||||
}
|
||||
return {
|
||||
...message,
|
||||
edited: patch.created,
|
||||
content: patch.data.content ?? message.content,
|
||||
extra: patch.data.extra ?? message.extra
|
||||
}
|
||||
}
|
||||
case PatchType.remove: {
|
||||
return undefined
|
||||
}
|
||||
case PatchType.reaction:
|
||||
return patchReactions(message, patch)
|
||||
case PatchType.attachment:
|
||||
return patchAttachments(message, patch)
|
||||
case PatchType.thread:
|
||||
return patchThread(message, patch)
|
||||
}
|
||||
}
|
||||
|
||||
function patchAttachments (message: OldMessage, patch: AttachmentPatch): OldMessage {
|
||||
if (patch.data.operation === 'add') {
|
||||
return addAttachments(message, patch.data.attachments, patch.created, patch.creator)
|
||||
} else if (patch.data.operation === 'remove') {
|
||||
return removeAttachments(message, patch.data.ids)
|
||||
} else if (patch.data.operation === 'set') {
|
||||
return setAttachments(message, patch.data.attachments, patch.created, patch.creator)
|
||||
} else if (patch.data.operation === 'update') {
|
||||
return updateAttachments(message, patch.data.attachments, patch.created)
|
||||
}
|
||||
return message
|
||||
}
|
||||
|
||||
function patchReactions (message: OldMessage, patch: ReactionPatch): OldMessage {
|
||||
if (patch.data.operation === 'add') {
|
||||
return setReaction(message, patch.data.reaction, patch.creator, patch.created)
|
||||
} else if (patch.data.operation === 'remove') {
|
||||
return removeReaction(message, patch.data.reaction, patch.creator)
|
||||
}
|
||||
return message
|
||||
}
|
||||
|
||||
function setReaction (message: OldMessage, reaction: string, creator: SocialID, created: Date): OldMessage {
|
||||
const isExist = message.reactions.some((it) => it.reaction === reaction && it.creator === creator)
|
||||
if (isExist) return message
|
||||
message.reactions.push({
|
||||
reaction,
|
||||
creator,
|
||||
created
|
||||
})
|
||||
return message
|
||||
}
|
||||
|
||||
function removeReaction (message: OldMessage, reaction: string, creator: SocialID): OldMessage {
|
||||
const reactions = message.reactions.filter((it) => it.reaction !== reaction || it.creator !== creator)
|
||||
if (reactions.length === message.reactions.length) return message
|
||||
|
||||
return {
|
||||
...message,
|
||||
reactions
|
||||
}
|
||||
}
|
||||
|
||||
function addAttachments (message: OldMessage, data: AttachmentData[], created: Date, creator: SocialID): OldMessage {
|
||||
const newAttachments: Attachment[] = []
|
||||
for (const attach of data) {
|
||||
const isExists = message.attachments.some((it) => it.id === attach.id)
|
||||
if (isExists === undefined) continue
|
||||
const attachment: Attachment = {
|
||||
...attach,
|
||||
created,
|
||||
creator
|
||||
} as any
|
||||
newAttachments.push(attachment)
|
||||
}
|
||||
|
||||
if (newAttachments.length === 0) return message
|
||||
return {
|
||||
...message,
|
||||
attachments: [...message.attachments, ...newAttachments]
|
||||
}
|
||||
}
|
||||
|
||||
function updateAttachments (message: OldMessage, updates: AttachmentUpdateData[], date: Date): OldMessage {
|
||||
if (updates.length === 0) return message
|
||||
const updatedAttachments: Attachment[] = []
|
||||
for (const attachment of message.attachments) {
|
||||
const update = updates.find((it) => it.id === attachment.id)
|
||||
if (update === undefined) {
|
||||
updatedAttachments.push(attachment)
|
||||
} else {
|
||||
updatedAttachments.push({
|
||||
...attachment,
|
||||
params: {
|
||||
...attachment.params,
|
||||
...update.params
|
||||
},
|
||||
modified: date.getTime() > (attachment.modified?.getTime() ?? 0) ? date : attachment.modified
|
||||
} as any)
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
...message,
|
||||
attachments: updatedAttachments
|
||||
}
|
||||
}
|
||||
|
||||
function removeAttachments (message: OldMessage, ids: AttachmentID[]): OldMessage {
|
||||
const attachments = message.attachments.filter((it) => !ids.includes(it.id))
|
||||
if (attachments.length === message.attachments.length) return message
|
||||
|
||||
return {
|
||||
...message,
|
||||
attachments
|
||||
}
|
||||
}
|
||||
|
||||
function setAttachments (message: OldMessage, data: AttachmentData[], created: Date, creator: SocialID): OldMessage {
|
||||
if (data.length === 0) return message
|
||||
return {
|
||||
...message,
|
||||
attachments: data.map(
|
||||
(it) =>
|
||||
({
|
||||
...it,
|
||||
created,
|
||||
creator
|
||||
}) as any
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
function patchThread (message: OldMessage, patch: ThreadPatch): OldMessage {
|
||||
if (patch.data.operation === 'attach') {
|
||||
return attachThread(message, patch.data.threadId, patch.data.threadType)
|
||||
} else if (patch.data.operation === 'update') {
|
||||
return updateThread(
|
||||
message,
|
||||
patch.data.threadId,
|
||||
patch.data.threadType,
|
||||
patch.data.repliesCountOp,
|
||||
patch.data.lastReply
|
||||
)
|
||||
}
|
||||
return message
|
||||
}
|
||||
|
||||
function attachThread (message: OldMessage, threadId: CardID, threadType: CardType): OldMessage {
|
||||
if (message.thread !== undefined) return message
|
||||
return {
|
||||
...message,
|
||||
thread: {
|
||||
cardId: message.cardId,
|
||||
messageId: message.id,
|
||||
threadId,
|
||||
threadType,
|
||||
repliesCount: 0,
|
||||
lastReply: new Date()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function updateThread (
|
||||
message: OldMessage,
|
||||
threadId: CardID,
|
||||
threadType?: CardType,
|
||||
repliesCountOp?: 'increment' | 'decrement',
|
||||
lastReply?: Date
|
||||
): OldMessage {
|
||||
if (repliesCountOp === undefined && lastReply === undefined) return message
|
||||
if (message.thread === undefined) return message
|
||||
if (message.thread.threadId !== threadId) return message
|
||||
|
||||
let count = message.thread.repliesCount
|
||||
if (repliesCountOp === 'increment') {
|
||||
count = count + 1
|
||||
}
|
||||
|
||||
if (repliesCountOp === 'decrement') {
|
||||
count = Math.max(count - 1, 0)
|
||||
}
|
||||
|
||||
return {
|
||||
...message,
|
||||
thread: {
|
||||
...message.thread,
|
||||
repliesCount: count,
|
||||
threadType: threadType ?? message.thread.threadType,
|
||||
lastReply: lastReply ?? message.thread.lastReply
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
interface OldMessage {
|
||||
id: MessageID
|
||||
cardId: CardID
|
||||
type: 'message' | 'activity'
|
||||
content: Markdown
|
||||
extra?: MessageExtra
|
||||
creator: SocialID
|
||||
created: Date
|
||||
|
||||
removed: boolean
|
||||
edited?: Date
|
||||
|
||||
reactions: OldReaction[]
|
||||
attachments: Attachment[]
|
||||
thread?: OldThread
|
||||
}
|
||||
|
||||
interface OldReaction {
|
||||
reaction: string
|
||||
creator: SocialID
|
||||
created: Date
|
||||
}
|
||||
|
||||
interface OldThread {
|
||||
cardId: CardID
|
||||
messageId: MessageID
|
||||
threadId: CardID
|
||||
threadType: CardType
|
||||
repliesCount: number
|
||||
lastReply: Date
|
||||
}
|
||||
|
||||
type Patch = UpdatePatch | RemovePatch | ReactionPatch | ThreadPatch | AttachmentPatch
|
||||
|
||||
enum PatchType {
|
||||
update = 'update',
|
||||
remove = 'remove',
|
||||
reaction = 'reaction',
|
||||
attachment = 'attachment',
|
||||
thread = 'thread'
|
||||
}
|
||||
|
||||
interface BasePatch {
|
||||
messageId: MessageID
|
||||
type: PatchType
|
||||
creator: SocialID
|
||||
created: Date
|
||||
|
||||
data: Record<string, any>
|
||||
}
|
||||
|
||||
interface UpdatePatch extends BasePatch {
|
||||
type: PatchType.update
|
||||
data: UpdatePatchData
|
||||
}
|
||||
|
||||
interface UpdatePatchData {
|
||||
content?: Markdown
|
||||
extra?: MessageExtra
|
||||
}
|
||||
|
||||
interface RemovePatch extends BasePatch {
|
||||
type: PatchType.remove
|
||||
data: RemovePatchData
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-empty-interface
|
||||
interface RemovePatchData {}
|
||||
|
||||
interface ReactionPatch extends BasePatch {
|
||||
type: PatchType.reaction
|
||||
data: AddReactionPatchData | RemoveReactionPatchData
|
||||
}
|
||||
|
||||
interface AddReactionPatchData {
|
||||
operation: 'add'
|
||||
reaction: string
|
||||
}
|
||||
|
||||
interface RemoveReactionPatchData {
|
||||
operation: 'remove'
|
||||
reaction: string
|
||||
}
|
||||
|
||||
interface AttachmentPatch extends BasePatch {
|
||||
type: PatchType.attachment
|
||||
data: AddAttachmentsPatchData | RemoveAttachmentsPatchData | SetAttachmentsPatchData | UpdateAttachmentsPatchData
|
||||
}
|
||||
|
||||
interface AddAttachmentsPatchData {
|
||||
operation: 'add'
|
||||
attachments: AttachmentData[]
|
||||
}
|
||||
|
||||
interface RemoveAttachmentsPatchData {
|
||||
operation: 'remove'
|
||||
ids: AttachmentID[]
|
||||
}
|
||||
|
||||
interface SetAttachmentsPatchData {
|
||||
operation: 'set'
|
||||
attachments: AttachmentData[]
|
||||
}
|
||||
|
||||
interface UpdateAttachmentsPatchData {
|
||||
operation: 'update'
|
||||
attachments: AttachmentUpdateData[]
|
||||
}
|
||||
|
||||
interface ThreadPatch extends BasePatch {
|
||||
type: PatchType.thread
|
||||
data: AttachThreadPatchData | UpdateThreadPatchData
|
||||
}
|
||||
|
||||
interface AttachThreadPatchData {
|
||||
operation: 'attach'
|
||||
threadId: CardID
|
||||
threadType: CardType
|
||||
}
|
||||
|
||||
interface UpdateThreadPatchData {
|
||||
operation: 'update'
|
||||
threadId: CardID
|
||||
threadType?: CardType
|
||||
repliesCountOp?: 'increment' | 'decrement'
|
||||
lastReply?: Date
|
||||
}
|
||||
+1
-1
@@ -1911,7 +1911,7 @@ export async function restoreTrustedV6Workspace (
|
||||
url
|
||||
}
|
||||
|
||||
const queue = getPlatformQueue('tool', workspace.region)
|
||||
const queue = getPlatformQueue('tool', region)
|
||||
const wsProducer = queue.getProducer<QueueWorkspaceMessage>(ctx, QueueTopic.Workspace)
|
||||
|
||||
await wsProducer.send(ctx, uuid, [workspaceEvents.restoring()])
|
||||
|
||||
+61
-7
@@ -56,6 +56,14 @@ import {
|
||||
type Account as OldAccount,
|
||||
type Workspace as OldWorkspace
|
||||
} from '@hcengineering/account-service'
|
||||
import { getClient as getHulylakeClient } from '@hcengineering/hulylake-client'
|
||||
import {
|
||||
getDBClient,
|
||||
createPostgreeDestroyAdapter,
|
||||
createPostgresAdapter,
|
||||
createPostgresTxAdapter,
|
||||
shutdownPostgres
|
||||
} from '@hcengineering/postgres'
|
||||
|
||||
import { faker } from '@faker-js/faker'
|
||||
import { getPlatformQueue } from '@hcengineering/kafka'
|
||||
@@ -69,6 +77,7 @@ import {
|
||||
isDeletingMode,
|
||||
MeasureMetricsContext,
|
||||
metricsToString,
|
||||
SocialId,
|
||||
SocialIdType,
|
||||
systemAccountEmail,
|
||||
systemAccountUuid,
|
||||
@@ -80,7 +89,8 @@ import {
|
||||
type Tx,
|
||||
type Version,
|
||||
type WorkspaceDataId,
|
||||
type WorkspaceUuid
|
||||
type WorkspaceUuid,
|
||||
type PersonUuid
|
||||
} from '@hcengineering/core'
|
||||
import { consoleModelLogger, type MigrateOperation } from '@hcengineering/model'
|
||||
import {
|
||||
@@ -92,12 +102,6 @@ import {
|
||||
} from '@hcengineering/mongo'
|
||||
|
||||
import { getModelVersion } from '@hcengineering/model-all'
|
||||
import {
|
||||
createPostgreeDestroyAdapter,
|
||||
createPostgresAdapter,
|
||||
createPostgresTxAdapter,
|
||||
shutdownPostgres
|
||||
} from '@hcengineering/postgres'
|
||||
import {
|
||||
QueueTopic,
|
||||
workspaceEvents,
|
||||
@@ -131,6 +135,8 @@ import { mkdir, writeFile } from 'fs/promises'
|
||||
import { dirname } from 'path'
|
||||
import { restoreMarkupRefs } from './markup'
|
||||
import { restoreGithubIntegrations } from './restoreGithub'
|
||||
import { migrateWorkspaceMessages } from './communication'
|
||||
import { type CardID } from '@hcengineering/communication-types'
|
||||
|
||||
const colorConstants = {
|
||||
colorRed: '\u001b[31m',
|
||||
@@ -2845,6 +2851,54 @@ export function devTool (
|
||||
}, dbUrl)
|
||||
})
|
||||
|
||||
program
|
||||
.command('migrate-communication-to-hulylake <workspace>')
|
||||
.description('Migrate communication messages to hulylake')
|
||||
.option('-c, --card <card>', 'Card to migrate')
|
||||
.action(async (workspace: WorkspaceUuid, cmd: { card?: CardID }) => {
|
||||
const { dbUrl } = prepareTools()
|
||||
const hulylakeUrl = process.env.HULYLAKE_URL ?? ''
|
||||
|
||||
console.log('Workspace', workspace)
|
||||
console.log('Card', cmd.card)
|
||||
if (hulylakeUrl === '') {
|
||||
throw new Error('HULYLAKE_URL should be specified')
|
||||
}
|
||||
|
||||
const token = generateToken(systemAccountUuid, undefined, {
|
||||
service: 'tool'
|
||||
})
|
||||
const db = getDBClient(dbUrl, undefined, 'tool')
|
||||
const dbClient = await db.getClient()
|
||||
const accountClient = getAccountClient(token)
|
||||
const personUuidBySocialId = new Map<PersonId, PersonUuid>()
|
||||
|
||||
await withAccountDatabase(async (accountDb) => {
|
||||
const ws = (await accountDb.workspace.find({ uuid: workspace }))[0]
|
||||
if (ws === undefined) {
|
||||
throw new Error(`Workspace ${workspace} not found`)
|
||||
}
|
||||
try {
|
||||
const hulylake = getHulylakeClient(hulylakeUrl, ws.uuid, token)
|
||||
console.log('start workspace migration', ws.name)
|
||||
await migrateWorkspaceMessages(
|
||||
toolCtx.newChild(ws.name, {}),
|
||||
ws,
|
||||
cmd.card,
|
||||
dbClient,
|
||||
hulylake,
|
||||
accountClient,
|
||||
personUuidBySocialId
|
||||
)
|
||||
console.log('done workspace migration', ws.name)
|
||||
} catch (err: any) {
|
||||
console.error('failed to migrate workspace', ws.name)
|
||||
console.error(err)
|
||||
}
|
||||
db.close()
|
||||
}, dbUrl)
|
||||
})
|
||||
|
||||
extendProgram?.(program)
|
||||
|
||||
process.on('unhandledRejection', (reason, promise) => {
|
||||
|
||||
@@ -474,7 +474,7 @@ export default function buildModel (): Builder {
|
||||
[
|
||||
chatModel,
|
||||
chatId,
|
||||
{ label: chat.string.Chat, hidden: true, enabled: false, beta: true, classFilter: defaultFilter }
|
||||
{ label: chat.string.Chat, hidden: false, enabled: true, beta: true, classFilter: defaultFilter }
|
||||
],
|
||||
[achievementModel, achievementId],
|
||||
[emojiModel, emojiId],
|
||||
|
||||
@@ -0,0 +1,218 @@
|
||||
// Copyright © 2025 Hardcore Engineering Inc.
|
||||
//
|
||||
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License. You may
|
||||
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
//
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { type Builder } from '@hcengineering/model'
|
||||
import presentation from '@hcengineering/model-presentation'
|
||||
import setting from '@hcengineering/model-setting'
|
||||
import view, { createAction } from '@hcengineering/model-view'
|
||||
import workbench from '@hcengineering/model-workbench'
|
||||
import card from './plugin'
|
||||
|
||||
export function createActions (builder: Builder): void {
|
||||
createAction(builder, {
|
||||
action: card.actionImpl.EditSpace,
|
||||
label: presentation.string.Edit,
|
||||
icon: view.icon.Edit,
|
||||
input: 'focus',
|
||||
category: view.category.General,
|
||||
target: card.class.CardSpace,
|
||||
visibilityTester: view.function.CanEditSpace,
|
||||
query: {},
|
||||
context: {
|
||||
mode: ['context', 'browser'],
|
||||
group: 'edit'
|
||||
}
|
||||
})
|
||||
|
||||
createAction(builder, {
|
||||
action: view.actionImpl.CopyTextToClipboard,
|
||||
actionProps: {
|
||||
textProvider: card.function.GetSpaceAccessPublicLink
|
||||
},
|
||||
label: card.string.GetIndividualPublicLink,
|
||||
icon: view.icon.CopyLink,
|
||||
input: 'any',
|
||||
category: view.category.General,
|
||||
target: card.class.CardSpace,
|
||||
query: {},
|
||||
visibilityTester: card.function.CanGetSpaceAccessPublicLink,
|
||||
context: {
|
||||
mode: ['context', 'browser']
|
||||
}
|
||||
})
|
||||
|
||||
createAction(
|
||||
builder,
|
||||
{
|
||||
action: view.actionImpl.ShowPopup,
|
||||
actionProps: {
|
||||
component: card.component.SetParentActionPopup,
|
||||
element: 'top',
|
||||
fillProps: {
|
||||
_objects: 'value'
|
||||
}
|
||||
},
|
||||
label: card.string.SetParent,
|
||||
icon: card.icon.MasterTag,
|
||||
input: 'none',
|
||||
category: card.category.Card,
|
||||
target: card.class.Card,
|
||||
context: {
|
||||
mode: ['context'],
|
||||
application: card.app.Card,
|
||||
group: 'associate'
|
||||
}
|
||||
},
|
||||
card.action.SetParent
|
||||
)
|
||||
|
||||
createAction(
|
||||
builder,
|
||||
{
|
||||
action: view.actionImpl.UpdateDocument,
|
||||
actionProps: {
|
||||
key: 'parent',
|
||||
value: null
|
||||
},
|
||||
query: {
|
||||
parent: { $ne: null, $exists: true }
|
||||
},
|
||||
label: card.string.UnsetParent,
|
||||
icon: card.icon.MasterTag,
|
||||
input: 'none',
|
||||
category: card.category.Card,
|
||||
target: card.class.Card,
|
||||
context: {
|
||||
mode: ['context'],
|
||||
application: card.app.Card,
|
||||
group: 'associate'
|
||||
}
|
||||
},
|
||||
card.action.UnsetParent
|
||||
)
|
||||
|
||||
createAction(builder, {
|
||||
action: view.actionImpl.ShowPopup,
|
||||
actionProps: {
|
||||
component: card.component.CreateTag,
|
||||
props: {
|
||||
_class: card.class.Tag
|
||||
},
|
||||
fillProps: {
|
||||
_object: 'parent'
|
||||
}
|
||||
},
|
||||
label: card.string.CreateTag,
|
||||
input: 'focus',
|
||||
icon: view.icon.Add,
|
||||
category: setting.category.Settings,
|
||||
target: card.class.MasterTag,
|
||||
context: {
|
||||
mode: ['context', 'browser'],
|
||||
group: 'edit'
|
||||
}
|
||||
})
|
||||
|
||||
createAction(builder, {
|
||||
action: view.actionImpl.ShowPopup,
|
||||
actionProps: {
|
||||
component: card.component.CreateTag,
|
||||
props: {
|
||||
_class: card.class.Tag
|
||||
},
|
||||
fillProps: {
|
||||
_object: 'parent'
|
||||
}
|
||||
},
|
||||
label: card.string.CreateTag,
|
||||
input: 'focus',
|
||||
icon: view.icon.Add,
|
||||
category: setting.category.Settings,
|
||||
target: card.class.Tag,
|
||||
context: {
|
||||
mode: ['context', 'browser'],
|
||||
group: 'edit'
|
||||
}
|
||||
})
|
||||
|
||||
createAction(
|
||||
builder,
|
||||
{
|
||||
action: card.actionImpl.DeleteMasterTag,
|
||||
label: workbench.string.Delete,
|
||||
icon: view.icon.Delete,
|
||||
input: 'any',
|
||||
category: view.category.General,
|
||||
target: card.class.MasterTag,
|
||||
context: {
|
||||
mode: ['context', 'browser'],
|
||||
group: 'remove'
|
||||
}
|
||||
},
|
||||
card.action.DeleteMasterTag
|
||||
)
|
||||
|
||||
createAction(builder, {
|
||||
action: card.actionImpl.DeleteMasterTag,
|
||||
label: workbench.string.Delete,
|
||||
icon: view.icon.Delete,
|
||||
input: 'any',
|
||||
category: view.category.General,
|
||||
target: card.class.Tag,
|
||||
context: {
|
||||
mode: ['context', 'browser'],
|
||||
group: 'remove'
|
||||
}
|
||||
})
|
||||
|
||||
createAction(builder, {
|
||||
action: view.actionImpl.ShowPopup,
|
||||
actionProps: {
|
||||
component: card.component.ChangeType,
|
||||
fillProps: {
|
||||
_object: 'value'
|
||||
}
|
||||
},
|
||||
label: card.string.ChangeType,
|
||||
input: 'focus',
|
||||
icon: card.icon.MasterTag,
|
||||
category: setting.category.Settings,
|
||||
target: card.class.Card,
|
||||
context: {
|
||||
mode: ['context', 'browser'],
|
||||
group: 'edit'
|
||||
}
|
||||
})
|
||||
|
||||
createAction(builder, {
|
||||
action: view.actionImpl.ShowPopup,
|
||||
actionProps: {
|
||||
component: card.component.CreateTag,
|
||||
props: {
|
||||
_class: card.class.MasterTag
|
||||
},
|
||||
fillProps: {
|
||||
_object: 'parent'
|
||||
}
|
||||
},
|
||||
label: card.string.CreateMasterTag,
|
||||
input: 'focus',
|
||||
icon: card.icon.MasterTag,
|
||||
category: setting.category.Settings,
|
||||
target: card.class.MasterTag,
|
||||
context: {
|
||||
mode: ['context', 'browser'],
|
||||
group: 'edit'
|
||||
}
|
||||
})
|
||||
}
|
||||
+23
-200
@@ -13,6 +13,7 @@
|
||||
|
||||
import activity from '@hcengineering/activity'
|
||||
import {
|
||||
type CanCreateCardResource,
|
||||
type Card,
|
||||
cardId,
|
||||
type CardNavigation,
|
||||
@@ -20,7 +21,6 @@ import {
|
||||
type CardSpace,
|
||||
type CardViewDefaults,
|
||||
type CreateCardExtension,
|
||||
type CanCreateCardResource,
|
||||
DOMAIN_CARD,
|
||||
type FavoriteCard,
|
||||
type MasterTag,
|
||||
@@ -60,17 +60,18 @@ import {
|
||||
import attachment from '@hcengineering/model-attachment'
|
||||
import { TAttachedDoc, TClass, TDoc, TMixin, TSpace } from '@hcengineering/model-core'
|
||||
import { createPublicLinkAction } from '@hcengineering/model-guest'
|
||||
import preference, { TPreference } from '@hcengineering/model-preference'
|
||||
import presentation from '@hcengineering/model-presentation'
|
||||
import setting from '@hcengineering/model-setting'
|
||||
import view, { createAction, type Viewlet } from '@hcengineering/model-view'
|
||||
import view, { type Viewlet } from '@hcengineering/model-view'
|
||||
import workbench, { WidgetType } from '@hcengineering/model-workbench'
|
||||
import { type Asset, getEmbeddedLabel, type IntlString, type Resource } from '@hcengineering/platform'
|
||||
import time, { type ToDo } from '@hcengineering/time'
|
||||
import { PaletteColorIndexes } from '@hcengineering/ui/src/colors'
|
||||
import { type AnyComponent } from '@hcengineering/ui/src/types'
|
||||
import { type BuildModelKey } from '@hcengineering/view'
|
||||
import preference, { TPreference } from '@hcengineering/model-preference'
|
||||
import card from './plugin'
|
||||
import { PaletteColorIndexes } from '@hcengineering/ui/src/colors'
|
||||
import { createActions } from './actions'
|
||||
|
||||
export { cardId } from '@hcengineering/card'
|
||||
|
||||
@@ -399,37 +400,7 @@ export function createModel (builder: Builder): void {
|
||||
}
|
||||
})
|
||||
|
||||
createAction(builder, {
|
||||
action: card.actionImpl.EditSpace,
|
||||
label: presentation.string.Edit,
|
||||
icon: view.icon.Edit,
|
||||
input: 'focus',
|
||||
category: view.category.General,
|
||||
target: card.class.CardSpace,
|
||||
visibilityTester: view.function.CanEditSpace,
|
||||
query: {},
|
||||
context: {
|
||||
mode: ['context', 'browser'],
|
||||
group: 'edit'
|
||||
}
|
||||
})
|
||||
|
||||
createAction(builder, {
|
||||
action: view.actionImpl.CopyTextToClipboard,
|
||||
actionProps: {
|
||||
textProvider: card.function.GetSpaceAccessPublicLink
|
||||
},
|
||||
label: card.string.GetIndividualPublicLink,
|
||||
icon: view.icon.CopyLink,
|
||||
input: 'any',
|
||||
category: view.category.General,
|
||||
target: card.class.CardSpace,
|
||||
query: {},
|
||||
visibilityTester: card.function.CanGetSpaceAccessPublicLink,
|
||||
context: {
|
||||
mode: ['context', 'browser']
|
||||
}
|
||||
})
|
||||
createActions(builder)
|
||||
|
||||
builder.mixin(card.class.CardSpace, core.class.Class, workbench.mixin.SpaceView, {
|
||||
view: {
|
||||
@@ -450,6 +421,19 @@ export function createModel (builder: Builder): void {
|
||||
locationDataResolver: card.resolver.LocationData,
|
||||
navigatorModel: {
|
||||
specials: [
|
||||
{
|
||||
id: 'all',
|
||||
label: card.string.AllCards,
|
||||
icon: card.icon.All,
|
||||
component: workbench.component.SpecialView,
|
||||
componentProps: {
|
||||
_class: card.class.Card,
|
||||
icon: card.icon.All,
|
||||
label: card.string.AllCards,
|
||||
defaultViewletDescriptor: card.viewlet.CardFeedDescriptor
|
||||
},
|
||||
position: 'top'
|
||||
},
|
||||
{
|
||||
id: 'browser',
|
||||
label: core.string.Spaces,
|
||||
@@ -484,31 +468,6 @@ export function createModel (builder: Builder): void {
|
||||
presenter: card.component.SpacePresenter
|
||||
})
|
||||
|
||||
createAction(
|
||||
builder,
|
||||
{
|
||||
action: view.actionImpl.ShowPopup,
|
||||
actionProps: {
|
||||
component: card.component.SetParentActionPopup,
|
||||
element: 'top',
|
||||
fillProps: {
|
||||
_objects: 'value'
|
||||
}
|
||||
},
|
||||
label: card.string.SetParent,
|
||||
icon: card.icon.MasterTag,
|
||||
input: 'none',
|
||||
category: card.category.Card,
|
||||
target: card.class.Card,
|
||||
context: {
|
||||
mode: ['context'],
|
||||
application: card.app.Card,
|
||||
group: 'associate'
|
||||
}
|
||||
},
|
||||
card.action.SetParent
|
||||
)
|
||||
|
||||
builder.mixin(card.class.Card, core.class.Class, view.mixin.AttributeEditor, {
|
||||
inlineEditor: card.component.CardEditor
|
||||
})
|
||||
@@ -517,31 +476,6 @@ export function createModel (builder: Builder): void {
|
||||
inlineEditor: card.component.CardArrayEditor
|
||||
})
|
||||
|
||||
createAction(
|
||||
builder,
|
||||
{
|
||||
action: view.actionImpl.UpdateDocument,
|
||||
actionProps: {
|
||||
key: 'parent',
|
||||
value: null
|
||||
},
|
||||
query: {
|
||||
parent: { $ne: null, $exists: true }
|
||||
},
|
||||
label: card.string.UnsetParent,
|
||||
icon: card.icon.MasterTag,
|
||||
input: 'none',
|
||||
category: card.category.Card,
|
||||
target: card.class.Card,
|
||||
context: {
|
||||
mode: ['context'],
|
||||
application: card.app.Card,
|
||||
group: 'associate'
|
||||
}
|
||||
},
|
||||
card.action.UnsetParent
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
view.class.Viewlet,
|
||||
core.space.Model,
|
||||
@@ -680,50 +614,6 @@ export function createModel (builder: Builder): void {
|
||||
encode: card.function.GetCardLink
|
||||
})
|
||||
|
||||
createAction(builder, {
|
||||
action: view.actionImpl.ShowPopup,
|
||||
actionProps: {
|
||||
component: card.component.CreateTag,
|
||||
props: {
|
||||
_class: card.class.Tag
|
||||
},
|
||||
fillProps: {
|
||||
_object: 'parent'
|
||||
}
|
||||
},
|
||||
label: card.string.CreateTag,
|
||||
input: 'focus',
|
||||
icon: view.icon.Add,
|
||||
category: setting.category.Settings,
|
||||
target: card.class.MasterTag,
|
||||
context: {
|
||||
mode: ['context', 'browser'],
|
||||
group: 'edit'
|
||||
}
|
||||
})
|
||||
|
||||
createAction(builder, {
|
||||
action: view.actionImpl.ShowPopup,
|
||||
actionProps: {
|
||||
component: card.component.CreateTag,
|
||||
props: {
|
||||
_class: card.class.Tag
|
||||
},
|
||||
fillProps: {
|
||||
_object: 'parent'
|
||||
}
|
||||
},
|
||||
label: card.string.CreateTag,
|
||||
input: 'focus',
|
||||
icon: view.icon.Add,
|
||||
category: setting.category.Settings,
|
||||
target: card.class.Tag,
|
||||
context: {
|
||||
mode: ['context', 'browser'],
|
||||
group: 'edit'
|
||||
}
|
||||
})
|
||||
|
||||
builder.mixin(card.class.MasterTag, core.class.Class, view.mixin.IgnoreActions, {
|
||||
actions: [setting.action.CreateMixin, view.action.OpenInNewTab, view.action.Delete, setting.action.DeleteMixin]
|
||||
})
|
||||
@@ -732,77 +622,6 @@ export function createModel (builder: Builder): void {
|
||||
actions: [setting.action.CreateMixin, view.action.OpenInNewTab, view.action.Delete, setting.action.DeleteMixin]
|
||||
})
|
||||
|
||||
createAction(
|
||||
builder,
|
||||
{
|
||||
action: card.actionImpl.DeleteMasterTag,
|
||||
label: workbench.string.Delete,
|
||||
icon: view.icon.Delete,
|
||||
input: 'any',
|
||||
category: view.category.General,
|
||||
target: card.class.MasterTag,
|
||||
context: {
|
||||
mode: ['context', 'browser'],
|
||||
group: 'remove'
|
||||
}
|
||||
},
|
||||
card.action.DeleteMasterTag
|
||||
)
|
||||
|
||||
createAction(builder, {
|
||||
action: card.actionImpl.DeleteMasterTag,
|
||||
label: workbench.string.Delete,
|
||||
icon: view.icon.Delete,
|
||||
input: 'any',
|
||||
category: view.category.General,
|
||||
target: card.class.Tag,
|
||||
context: {
|
||||
mode: ['context', 'browser'],
|
||||
group: 'remove'
|
||||
}
|
||||
})
|
||||
|
||||
createAction(builder, {
|
||||
action: view.actionImpl.ShowPopup,
|
||||
actionProps: {
|
||||
component: card.component.ChangeType,
|
||||
fillProps: {
|
||||
_object: 'value'
|
||||
}
|
||||
},
|
||||
label: card.string.ChangeType,
|
||||
input: 'focus',
|
||||
icon: card.icon.MasterTag,
|
||||
category: setting.category.Settings,
|
||||
target: card.class.Card,
|
||||
context: {
|
||||
mode: ['context', 'browser'],
|
||||
group: 'edit'
|
||||
}
|
||||
})
|
||||
|
||||
createAction(builder, {
|
||||
action: view.actionImpl.ShowPopup,
|
||||
actionProps: {
|
||||
component: card.component.CreateTag,
|
||||
props: {
|
||||
_class: card.class.MasterTag
|
||||
},
|
||||
fillProps: {
|
||||
_object: 'parent'
|
||||
}
|
||||
},
|
||||
label: card.string.CreateMasterTag,
|
||||
input: 'focus',
|
||||
icon: card.icon.MasterTag,
|
||||
category: setting.category.Settings,
|
||||
target: card.class.MasterTag,
|
||||
context: {
|
||||
mode: ['context', 'browser'],
|
||||
group: 'edit'
|
||||
}
|
||||
})
|
||||
|
||||
builder.createDoc(
|
||||
setting.class.WorkspaceSettingCategory,
|
||||
core.space.Model,
|
||||
@@ -849,6 +668,10 @@ export function createModel (builder: Builder): void {
|
||||
forceIndex: true
|
||||
})
|
||||
|
||||
builder.mixin(card.class.Card, core.class.Class, view.mixin.ObjectFactory, {
|
||||
create: card.function.CardFactory
|
||||
})
|
||||
|
||||
builder.createDoc(
|
||||
presentation.class.ObjectSearchCategory,
|
||||
core.space.Model,
|
||||
|
||||
@@ -36,7 +36,7 @@ export function buildMessageActions (builder: Builder): void {
|
||||
communication.class.MessageAction,
|
||||
core.space.Model,
|
||||
{
|
||||
label: communication.string.ReplyInThread,
|
||||
label: communication.string.Reply,
|
||||
icon: communication.icon.MessageMultiple,
|
||||
action: communication.messageActionImpl.ReplyInThread,
|
||||
visibilityTester: communication.function.CanReplyInThread,
|
||||
|
||||
@@ -921,6 +921,10 @@ export function createModel (builder: Builder): void {
|
||||
presenter: contact.component.PersonPresenter
|
||||
})
|
||||
|
||||
builder.mixin(contact.class.SocialIdentity, core.class.Class, view.mixin.ObjectPresenter, {
|
||||
presenter: contact.component.SocialIdentityPresenter
|
||||
})
|
||||
|
||||
builder.mixin(core.class.TypeAccountUuid, core.class.Class, view.mixin.ArrayEditor, {
|
||||
inlineEditor: contact.component.AccountArrayEditor
|
||||
})
|
||||
@@ -1141,6 +1145,7 @@ export function createModel (builder: Builder): void {
|
||||
label: contact.string.MergePersons,
|
||||
category: contact.category.Contact,
|
||||
target: contact.class.Person,
|
||||
visibilityTester: contact.function.CanMergePersons,
|
||||
input: 'focus',
|
||||
context: {
|
||||
mode: ['context'],
|
||||
|
||||
@@ -32,6 +32,7 @@ export default mergeIds(contactId, contact, {
|
||||
},
|
||||
component: {
|
||||
PersonPresenter: '' as AnyComponent,
|
||||
SocialIdentityPresenter: '' as AnyComponent,
|
||||
ContactRefPresenter: '' as AnyComponent,
|
||||
ContactPresenter: '' as AnyComponent,
|
||||
EditPerson: '' as AnyComponent,
|
||||
|
||||
@@ -12,6 +12,7 @@ export function definePermissions (builder: Builder): void {
|
||||
core.space.Model,
|
||||
{
|
||||
label: documents.string.ReviewDocumentPermission,
|
||||
scope: 'space',
|
||||
description: documents.string.ReviewDocumentDescription
|
||||
},
|
||||
documents.permission.ReviewDocument
|
||||
@@ -22,6 +23,7 @@ export function definePermissions (builder: Builder): void {
|
||||
core.space.Model,
|
||||
{
|
||||
label: documents.string.ApproveDocumentPermission,
|
||||
scope: 'space',
|
||||
description: documents.string.ApproveDocumentDescription
|
||||
},
|
||||
documents.permission.ApproveDocument
|
||||
@@ -32,6 +34,7 @@ export function definePermissions (builder: Builder): void {
|
||||
core.space.Model,
|
||||
{
|
||||
label: documents.string.ArchiveDocumentPermission,
|
||||
scope: 'space',
|
||||
description: documents.string.ArchiveDocumentDescription
|
||||
},
|
||||
documents.permission.ArchiveDocument
|
||||
@@ -42,6 +45,7 @@ export function definePermissions (builder: Builder): void {
|
||||
core.space.Model,
|
||||
{
|
||||
label: documents.string.CoAuthorDocumentPermission,
|
||||
scope: 'space',
|
||||
description: documents.string.CoAuthorDocumentDescription
|
||||
},
|
||||
documents.permission.CoAuthorDocument
|
||||
@@ -52,6 +56,7 @@ export function definePermissions (builder: Builder): void {
|
||||
core.space.Model,
|
||||
{
|
||||
label: documents.string.CreateDocumentPermission,
|
||||
scope: 'space',
|
||||
description: documents.string.CreateDocumentDescription
|
||||
},
|
||||
documents.permission.CreateDocument
|
||||
@@ -62,6 +67,7 @@ export function definePermissions (builder: Builder): void {
|
||||
core.space.Model,
|
||||
{
|
||||
label: documents.string.UpdateDocumentOwnerPermission,
|
||||
scope: 'space',
|
||||
description: documents.string.UpdateDocumentOwnerDescription
|
||||
},
|
||||
documents.permission.UpdateDocumentOwner
|
||||
@@ -72,6 +78,7 @@ export function definePermissions (builder: Builder): void {
|
||||
core.space.Model,
|
||||
{
|
||||
label: documents.string.CreateDocumentCategoryPermission,
|
||||
scope: 'space',
|
||||
description: documents.string.CreateDocumentCategoryDescription
|
||||
},
|
||||
documents.permission.CreateDocumentCategory
|
||||
@@ -82,6 +89,7 @@ export function definePermissions (builder: Builder): void {
|
||||
core.space.Model,
|
||||
{
|
||||
label: documents.string.UpdateDocumentCategoryPermission,
|
||||
scope: 'space',
|
||||
description: documents.string.UpdateDocumentCategoryDescription
|
||||
},
|
||||
documents.permission.UpdateDocumentCategory
|
||||
@@ -92,6 +100,7 @@ export function definePermissions (builder: Builder): void {
|
||||
core.space.Model,
|
||||
{
|
||||
label: documents.string.DeleteDocumentCategoryPermission,
|
||||
scope: 'space',
|
||||
description: documents.string.DeleteDocumentCategoryDescription
|
||||
},
|
||||
documents.permission.DeleteDocumentCategory
|
||||
|
||||
@@ -23,6 +23,7 @@ export function definePermissions (builder: Builder): void {
|
||||
core.space.Model,
|
||||
{
|
||||
label: core.string.CreateObject,
|
||||
scope: 'space',
|
||||
description: core.string.CreateObjectDescription
|
||||
},
|
||||
core.permission.CreateObject
|
||||
@@ -33,6 +34,7 @@ export function definePermissions (builder: Builder): void {
|
||||
core.space.Model,
|
||||
{
|
||||
label: core.string.UpdateObject,
|
||||
scope: 'space',
|
||||
description: core.string.UpdateObjectDescription
|
||||
},
|
||||
core.permission.UpdateObject
|
||||
@@ -43,6 +45,7 @@ export function definePermissions (builder: Builder): void {
|
||||
core.space.Model,
|
||||
{
|
||||
label: core.string.DeleteObject,
|
||||
scope: 'space',
|
||||
description: core.string.DeleteObjectDescription
|
||||
},
|
||||
core.permission.DeleteObject
|
||||
@@ -53,36 +56,20 @@ export function definePermissions (builder: Builder): void {
|
||||
core.space.Model,
|
||||
{
|
||||
label: core.string.ForbidDeleteObject,
|
||||
txClass: core.class.TxRemoveDoc,
|
||||
forbid: true,
|
||||
scope: 'space',
|
||||
description: core.string.ForbidDeleteObjectDescription
|
||||
},
|
||||
core.permission.ForbidDeleteObject
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
core.class.Permission,
|
||||
core.space.Model,
|
||||
{
|
||||
label: core.string.UpdateObject,
|
||||
description: core.string.UpdateObjectDescription
|
||||
},
|
||||
core.permission.UpdateObject
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
core.class.Permission,
|
||||
core.space.Model,
|
||||
{
|
||||
label: core.string.DeleteObject,
|
||||
description: core.string.DeleteObjectDescription
|
||||
},
|
||||
core.permission.DeleteObject
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
core.class.Permission,
|
||||
core.space.Model,
|
||||
{
|
||||
label: core.string.UpdateSpace,
|
||||
scope: 'space',
|
||||
description: core.string.UpdateSpaceDescription
|
||||
},
|
||||
core.permission.UpdateSpace
|
||||
@@ -93,6 +80,7 @@ export function definePermissions (builder: Builder): void {
|
||||
core.space.Model,
|
||||
{
|
||||
label: core.string.ArchiveSpace,
|
||||
scope: 'space',
|
||||
description: core.string.ArchiveSpaceDescription
|
||||
},
|
||||
core.permission.ArchiveSpace
|
||||
|
||||
@@ -29,7 +29,9 @@ import {
|
||||
type SpaceTypeDescriptor,
|
||||
type TypedSpace,
|
||||
type AccountUuid,
|
||||
type TxAccessLevel
|
||||
type TxAccessLevel,
|
||||
type Tx,
|
||||
type Doc
|
||||
} from '@hcengineering/core'
|
||||
import {
|
||||
ArrOf,
|
||||
@@ -155,6 +157,10 @@ export class TRole extends TAttachedDoc implements Role {
|
||||
@UX(core.string.Permission)
|
||||
export class TPermission extends TDoc implements Permission {
|
||||
label!: IntlString
|
||||
txClass?: Ref<Class<Tx>>
|
||||
forbid?: boolean
|
||||
objectClass?: Ref<Class<Doc<Space>>>
|
||||
scope?: 'space' | 'workspace'
|
||||
description?: IntlString
|
||||
icon?: Asset
|
||||
}
|
||||
|
||||
@@ -63,6 +63,7 @@ import { type Asset, getEmbeddedLabel } from '@hcengineering/platform'
|
||||
import tags from '@hcengineering/tags'
|
||||
import time, { type ToDo, type Todoable } from '@hcengineering/time'
|
||||
import document from './plugin'
|
||||
import { definePermissions } from './permissions'
|
||||
|
||||
export { documentId } from '@hcengineering/document'
|
||||
|
||||
@@ -550,6 +551,7 @@ export function createModel (builder: Builder): void {
|
||||
defineDocument(builder)
|
||||
|
||||
defineApplication(builder)
|
||||
definePermissions(builder)
|
||||
|
||||
builder.createDoc(core.class.DomainIndexConfiguration, core.space.Model, {
|
||||
domain: DOMAIN_DOCUMENT,
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
import type { Builder } from '@hcengineering/model'
|
||||
import core from '@hcengineering/core'
|
||||
import document from '@hcengineering/document'
|
||||
|
||||
export function definePermissions (builder: Builder): void {
|
||||
builder.createDoc(
|
||||
core.class.Permission,
|
||||
core.space.Model,
|
||||
{
|
||||
label: document.string.ForbidCreateTeamspacePermission,
|
||||
scope: 'workspace',
|
||||
txClass: core.class.TxCreateDoc,
|
||||
objectClass: document.class.Teamspace,
|
||||
forbid: true,
|
||||
description: document.string.ForbidCreateTeamspacePermissionDescription
|
||||
},
|
||||
document.permission.ForbidCreateTeamspace
|
||||
)
|
||||
}
|
||||
@@ -63,6 +63,7 @@ import workbench from '@hcengineering/model-workbench'
|
||||
import { getEmbeddedLabel } from '@hcengineering/platform'
|
||||
|
||||
import drive from './plugin'
|
||||
import { definePermissions } from './permissions'
|
||||
|
||||
export { driveId } from '@hcengineering/drive'
|
||||
export { driveOperation } from './migration'
|
||||
@@ -790,4 +791,5 @@ export function createModel (builder: Builder): void {
|
||||
defineFile(builder)
|
||||
defineFileVersion(builder)
|
||||
defineApplication(builder)
|
||||
definePermissions(builder)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
import type { Builder } from '@hcengineering/model'
|
||||
import core from '@hcengineering/core'
|
||||
import drive from '@hcengineering/drive'
|
||||
|
||||
export function definePermissions (builder: Builder): void {
|
||||
builder.createDoc(
|
||||
core.class.Permission,
|
||||
core.space.Model,
|
||||
{
|
||||
label: drive.string.ForbidCreateDrivePermission,
|
||||
scope: 'workspace',
|
||||
txClass: core.class.TxCreateDoc,
|
||||
objectClass: drive.class.Drive,
|
||||
forbid: true,
|
||||
description: drive.string.ForbidCreateDrivePermissionDescription
|
||||
},
|
||||
drive.permission.ForbidCreateDrive
|
||||
)
|
||||
}
|
||||
@@ -32,6 +32,7 @@ import { type ViewOptionsModel } from '@hcengineering/view'
|
||||
|
||||
import lead from './plugin'
|
||||
import { defineSpaceType } from './spaceType'
|
||||
import { definePermissions } from './permissions'
|
||||
import { TCustomer, TFunnel, TLead } from './types'
|
||||
|
||||
export { leadId } from '@hcengineering/lead'
|
||||
@@ -660,4 +661,5 @@ export function createModel (builder: Builder): void {
|
||||
})
|
||||
|
||||
defineSpaceType(builder)
|
||||
definePermissions(builder)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
import type { Builder } from '@hcengineering/model'
|
||||
import core from '@hcengineering/core'
|
||||
import lead from '@hcengineering/lead'
|
||||
|
||||
export function definePermissions (builder: Builder): void {
|
||||
builder.createDoc(
|
||||
core.class.Permission,
|
||||
core.space.Model,
|
||||
{
|
||||
label: lead.string.ForbidCreateFunnelPermission,
|
||||
scope: 'workspace',
|
||||
txClass: core.class.TxCreateDoc,
|
||||
objectClass: lead.class.Funnel,
|
||||
forbid: true,
|
||||
description: lead.string.ForbidCreateFunnelPermissionDescription
|
||||
},
|
||||
lead.permission.ForbidCreateFunnel
|
||||
)
|
||||
}
|
||||
@@ -719,6 +719,12 @@ export function createModel (builder: Builder): void {
|
||||
love.completion.MeetingMinutesCategory
|
||||
)
|
||||
|
||||
// Extensions
|
||||
builder.createDoc(presentation.class.ComponentPointExtension, core.space.Model, {
|
||||
extension: contact.extension.EmployeePopupActions,
|
||||
component: love.component.InviteEmployeeButton
|
||||
})
|
||||
|
||||
createAttributePresenter(
|
||||
builder,
|
||||
view.component.DateTimePresenter,
|
||||
|
||||
@@ -32,7 +32,8 @@ export default mergeIds(loveId, love, {
|
||||
WidgetSwitcher: '' as AnyComponent,
|
||||
RoomLanguageEditor: '' as AnyComponent,
|
||||
MediaPopupItemExt: '' as AnyComponent,
|
||||
SharingStateIndicator: '' as AnyComponent
|
||||
SharingStateIndicator: '' as AnyComponent,
|
||||
InviteEmployeeButton: '' as AnyComponent
|
||||
},
|
||||
app: {
|
||||
Love: '' as Ref<Doc>
|
||||
|
||||
@@ -45,6 +45,7 @@
|
||||
"@hcengineering/setting": "^0.6.17",
|
||||
"@hcengineering/model-time": "^0.6.0",
|
||||
"@hcengineering/rank": "^0.6.4",
|
||||
"@hcengineering/notification": "^0.6.23",
|
||||
"@hcengineering/process": "^0.6.0",
|
||||
"@hcengineering/process-resources": "^0.6.0",
|
||||
"@hcengineering/model-presentation": "^0.6.0",
|
||||
|
||||
@@ -0,0 +1,118 @@
|
||||
// Copyright © 2025 Hardcore Engineering Inc.
|
||||
//
|
||||
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License. You may
|
||||
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
//
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import card from '@hcengineering/card'
|
||||
import core from '@hcengineering/core'
|
||||
import { type Builder } from '@hcengineering/model'
|
||||
import process from './plugin'
|
||||
|
||||
export function defineMethods (builder: Builder): void {
|
||||
builder.createDoc(
|
||||
process.class.Method,
|
||||
core.space.Model,
|
||||
{
|
||||
label: process.string.RunProcess,
|
||||
objectClass: process.class.Process,
|
||||
editor: process.component.SubProcessEditor,
|
||||
presenter: process.component.SubProcessPresenter,
|
||||
createdContext: { _class: process.class.Execution },
|
||||
requiredParams: ['_id']
|
||||
},
|
||||
process.method.RunSubProcess
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
process.class.Method,
|
||||
core.space.Model,
|
||||
{
|
||||
label: process.string.CreateToDo,
|
||||
editor: process.component.ToDoEditor,
|
||||
objectClass: process.class.ProcessToDo,
|
||||
presenter: process.component.ToDoPresenter,
|
||||
createdContext: { _class: process.class.ProcessToDo, nameField: 'title' },
|
||||
requiredParams: ['title', 'user'],
|
||||
defaultParams: {
|
||||
withRollback: true
|
||||
}
|
||||
},
|
||||
process.method.CreateToDo
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
process.class.Trigger,
|
||||
core.space.Model,
|
||||
{
|
||||
label: process.string.OnExecutionStart,
|
||||
icon: process.icon.Process,
|
||||
init: true,
|
||||
requiredParams: []
|
||||
},
|
||||
process.trigger.OnExecutionStart
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
process.class.Method,
|
||||
core.space.Model,
|
||||
{
|
||||
label: process.string.UpdateCard,
|
||||
editor: process.component.UpdateCardEditor,
|
||||
objectClass: card.class.Card,
|
||||
createdContext: null,
|
||||
presenter: process.component.UpdateCardPresenter,
|
||||
requiredParams: []
|
||||
},
|
||||
process.method.UpdateCard
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
process.class.Method,
|
||||
core.space.Model,
|
||||
{
|
||||
label: process.string.CreateCard,
|
||||
objectClass: card.class.Card,
|
||||
editor: process.component.CreateCardEditor,
|
||||
presenter: process.component.CreateCardPresenter,
|
||||
createdContext: { _class: card.class.Card },
|
||||
requiredParams: ['title', '_class']
|
||||
},
|
||||
process.method.CreateCard
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
process.class.Method,
|
||||
core.space.Model,
|
||||
{
|
||||
label: core.string.AddRelation,
|
||||
objectClass: core.class.Relation,
|
||||
editor: process.component.AddRelationEditor,
|
||||
presenter: process.component.AddRelationPresenter,
|
||||
createdContext: { _class: core.class.Relation },
|
||||
requiredParams: ['association', 'direction', '_id']
|
||||
},
|
||||
process.method.AddRelation
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
process.class.Method,
|
||||
core.space.Model,
|
||||
{
|
||||
label: card.string.AddTag,
|
||||
objectClass: card.class.Tag,
|
||||
editor: process.component.AddTagEditor,
|
||||
presenter: process.component.AddTagPresenter,
|
||||
createdContext: { _class: card.class.Card },
|
||||
requiredParams: ['_id']
|
||||
},
|
||||
process.method.AddTag
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,520 @@
|
||||
// Copyright © 2025 Hardcore Engineering Inc.
|
||||
//
|
||||
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License. You may
|
||||
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
//
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import contact from '@hcengineering/contact'
|
||||
import core from '@hcengineering/core'
|
||||
import { type Builder } from '@hcengineering/model'
|
||||
import process from './plugin'
|
||||
|
||||
export function defineFunctions (builder: Builder): void {
|
||||
builder.createDoc(
|
||||
process.class.ProcessFunction,
|
||||
core.space.Model,
|
||||
{
|
||||
of: core.class.TypeString,
|
||||
category: 'attribute',
|
||||
label: process.string.UpperCase,
|
||||
type: 'transform'
|
||||
},
|
||||
process.function.UpperCase
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
process.class.ProcessFunction,
|
||||
core.space.Model,
|
||||
{
|
||||
of: core.class.TypeString,
|
||||
category: 'attribute',
|
||||
label: process.string.LowerCase,
|
||||
type: 'transform'
|
||||
},
|
||||
process.function.LowerCase
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
process.class.ProcessFunction,
|
||||
core.space.Model,
|
||||
{
|
||||
of: core.class.TypeString,
|
||||
category: 'attribute',
|
||||
label: process.string.Trim,
|
||||
type: 'transform'
|
||||
},
|
||||
process.function.Trim
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
process.class.ProcessFunction,
|
||||
core.space.Model,
|
||||
{
|
||||
of: core.class.TypeString,
|
||||
category: 'attribute',
|
||||
label: process.string.Prepend,
|
||||
allowMany: true,
|
||||
type: 'transform',
|
||||
editor: process.transformEditor.AppendEditor
|
||||
},
|
||||
process.function.Prepend
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
process.class.ProcessFunction,
|
||||
core.space.Model,
|
||||
{
|
||||
of: core.class.TypeString,
|
||||
category: 'attribute',
|
||||
label: process.string.Append,
|
||||
allowMany: true,
|
||||
type: 'transform',
|
||||
editor: process.transformEditor.AppendEditor
|
||||
},
|
||||
process.function.Append
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
process.class.ProcessFunction,
|
||||
core.space.Model,
|
||||
{
|
||||
of: core.class.TypeString,
|
||||
category: 'attribute',
|
||||
label: process.string.Replace,
|
||||
allowMany: true,
|
||||
type: 'transform',
|
||||
editor: process.transformEditor.ReplaceEditor
|
||||
},
|
||||
process.function.Replace
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
process.class.ProcessFunction,
|
||||
core.space.Model,
|
||||
{
|
||||
of: core.class.TypeString,
|
||||
category: 'attribute',
|
||||
label: process.string.ReplaceAll,
|
||||
allowMany: true,
|
||||
type: 'transform',
|
||||
editor: process.transformEditor.ReplaceEditor
|
||||
},
|
||||
process.function.ReplaceAll
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
process.class.ProcessFunction,
|
||||
core.space.Model,
|
||||
{
|
||||
of: core.class.TypeString,
|
||||
category: 'attribute',
|
||||
label: process.string.Split,
|
||||
allowMany: true,
|
||||
type: 'transform',
|
||||
editor: process.transformEditor.SplitEditor
|
||||
},
|
||||
process.function.Split
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
process.class.ProcessFunction,
|
||||
core.space.Model,
|
||||
{
|
||||
of: core.class.TypeString,
|
||||
category: 'attribute',
|
||||
label: process.string.Cut,
|
||||
allowMany: true,
|
||||
editor: process.transformEditor.CutEditor,
|
||||
type: 'transform'
|
||||
},
|
||||
process.function.Cut
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
process.class.ProcessFunction,
|
||||
core.space.Model,
|
||||
{
|
||||
of: core.class.ArrOf,
|
||||
category: undefined,
|
||||
label: process.string.FirstValue,
|
||||
type: 'reduce'
|
||||
},
|
||||
process.function.FirstValue
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
process.class.ProcessFunction,
|
||||
core.space.Model,
|
||||
{
|
||||
of: core.class.ArrOf,
|
||||
type: 'reduce',
|
||||
category: undefined,
|
||||
label: process.string.LastValue
|
||||
},
|
||||
process.function.LastValue
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
process.class.ProcessFunction,
|
||||
core.space.Model,
|
||||
{
|
||||
of: core.class.ArrOf,
|
||||
type: 'reduce',
|
||||
category: undefined,
|
||||
label: process.string.Random
|
||||
},
|
||||
process.function.Random
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
process.class.ProcessFunction,
|
||||
core.space.Model,
|
||||
{
|
||||
of: core.class.ArrOf,
|
||||
category: 'array',
|
||||
label: process.string.Each,
|
||||
type: 'reduce'
|
||||
},
|
||||
process.function.All
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
process.class.ProcessFunction,
|
||||
core.space.Model,
|
||||
{
|
||||
of: core.class.TypeNumber,
|
||||
type: 'transform',
|
||||
category: 'attribute',
|
||||
label: process.string.Add,
|
||||
presenter: process.transformPresenter.NumberPresenter,
|
||||
allowMany: true,
|
||||
editor: process.transformEditor.NumberEditor
|
||||
},
|
||||
process.function.Add
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
process.class.ProcessFunction,
|
||||
core.space.Model,
|
||||
{
|
||||
of: core.class.TypeNumber,
|
||||
category: 'attribute',
|
||||
label: process.string.Subtract,
|
||||
presenter: process.transformPresenter.NumberPresenter,
|
||||
allowMany: true,
|
||||
editor: process.transformEditor.NumberEditor,
|
||||
type: 'transform'
|
||||
},
|
||||
process.function.Subtract
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
process.class.ProcessFunction,
|
||||
core.space.Model,
|
||||
{
|
||||
of: core.class.TypeNumber,
|
||||
category: 'attribute',
|
||||
label: process.string.Multiply,
|
||||
presenter: process.transformPresenter.NumberPresenter,
|
||||
type: 'transform',
|
||||
allowMany: true,
|
||||
editor: process.transformEditor.NumberEditor
|
||||
},
|
||||
process.function.Multiply
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
process.class.ProcessFunction,
|
||||
core.space.Model,
|
||||
{
|
||||
of: core.class.TypeNumber,
|
||||
category: 'attribute',
|
||||
label: process.string.Divide,
|
||||
presenter: process.transformPresenter.NumberPresenter,
|
||||
type: 'transform',
|
||||
allowMany: true,
|
||||
editor: process.transformEditor.NumberEditor
|
||||
},
|
||||
process.function.Divide
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
process.class.ProcessFunction,
|
||||
core.space.Model,
|
||||
{
|
||||
of: core.class.TypeNumber,
|
||||
category: 'attribute',
|
||||
label: process.string.Modulo,
|
||||
presenter: process.transformPresenter.NumberPresenter,
|
||||
type: 'transform',
|
||||
allowMany: true,
|
||||
editor: process.transformEditor.NumberEditor
|
||||
},
|
||||
process.function.Modulo
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
process.class.ProcessFunction,
|
||||
core.space.Model,
|
||||
{
|
||||
of: core.class.TypeNumber,
|
||||
category: 'attribute',
|
||||
label: process.string.Power,
|
||||
presenter: process.transformPresenter.NumberPresenter,
|
||||
type: 'transform',
|
||||
allowMany: true,
|
||||
editor: process.transformEditor.NumberEditor
|
||||
},
|
||||
process.function.Power
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
process.class.ProcessFunction,
|
||||
core.space.Model,
|
||||
{
|
||||
of: core.class.TypeNumber,
|
||||
category: 'attribute',
|
||||
label: process.string.Sqrt,
|
||||
presenter: process.transformPresenter.NumberPresenter,
|
||||
type: 'transform',
|
||||
allowMany: true
|
||||
},
|
||||
process.function.Sqrt
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
process.class.ProcessFunction,
|
||||
core.space.Model,
|
||||
{
|
||||
of: core.class.TypeNumber,
|
||||
category: 'attribute',
|
||||
label: process.string.Round,
|
||||
type: 'transform',
|
||||
allowMany: true
|
||||
},
|
||||
process.function.Round
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
process.class.ProcessFunction,
|
||||
core.space.Model,
|
||||
{
|
||||
of: core.class.TypeNumber,
|
||||
category: 'attribute',
|
||||
label: process.string.Absolute,
|
||||
type: 'transform',
|
||||
allowMany: true
|
||||
},
|
||||
process.function.Absolute
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
process.class.ProcessFunction,
|
||||
core.space.Model,
|
||||
{
|
||||
of: core.class.TypeNumber,
|
||||
category: 'attribute',
|
||||
label: process.string.Ceil,
|
||||
type: 'transform',
|
||||
allowMany: true
|
||||
},
|
||||
process.function.Ceil
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
process.class.ProcessFunction,
|
||||
core.space.Model,
|
||||
{
|
||||
of: core.class.TypeNumber,
|
||||
category: 'attribute',
|
||||
label: process.string.Floor,
|
||||
type: 'transform',
|
||||
allowMany: true
|
||||
},
|
||||
process.function.Floor
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
process.class.ProcessFunction,
|
||||
core.space.Model,
|
||||
{
|
||||
of: core.class.TypeDate,
|
||||
category: 'attribute',
|
||||
label: process.string.Offset,
|
||||
editor: process.transformEditor.DateOffsetEditor,
|
||||
type: 'transform'
|
||||
},
|
||||
process.function.Offset
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
process.class.ProcessFunction,
|
||||
core.space.Model,
|
||||
{
|
||||
of: core.class.TypeDate,
|
||||
category: 'attribute',
|
||||
label: process.string.FirstWorkingDayAfter,
|
||||
type: 'transform'
|
||||
},
|
||||
process.function.FirstWorkingDayAfter
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
process.class.ProcessFunction,
|
||||
core.space.Model,
|
||||
{
|
||||
of: core.class.ArrOf,
|
||||
category: 'array',
|
||||
label: process.string.Insert,
|
||||
allowMany: true,
|
||||
editor: process.transformEditor.MultiArrayElementEditor,
|
||||
type: 'transform'
|
||||
},
|
||||
process.function.Insert
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
process.class.ProcessFunction,
|
||||
core.space.Model,
|
||||
{
|
||||
of: core.class.ArrOf,
|
||||
category: 'array',
|
||||
label: process.string.Remove,
|
||||
allowMany: true,
|
||||
editor: process.transformEditor.ArrayElementEditor,
|
||||
type: 'transform'
|
||||
},
|
||||
process.function.Remove
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
process.class.ProcessFunction,
|
||||
core.space.Model,
|
||||
{
|
||||
of: core.class.ArrOf,
|
||||
category: 'array',
|
||||
label: process.string.RemoveFirst,
|
||||
allowMany: true,
|
||||
type: 'transform'
|
||||
},
|
||||
process.function.RemoveFirst
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
process.class.ProcessFunction,
|
||||
core.space.Model,
|
||||
{
|
||||
of: core.class.ArrOf,
|
||||
category: 'array',
|
||||
label: process.string.RemoveLast,
|
||||
allowMany: true,
|
||||
type: 'transform'
|
||||
},
|
||||
process.function.RemoveLast
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
process.class.ProcessFunction,
|
||||
core.space.Model,
|
||||
{
|
||||
of: contact.mixin.Employee,
|
||||
editor: process.component.RoleEditor,
|
||||
presenter: process.transformPresenter.RolePresenter,
|
||||
category: 'array',
|
||||
label: core.string.Role,
|
||||
type: 'context'
|
||||
},
|
||||
process.function.RoleContext
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
process.class.ProcessFunction,
|
||||
core.space.Model,
|
||||
{
|
||||
of: core.class.ArrOf,
|
||||
category: 'array',
|
||||
label: process.string.EmptyArray,
|
||||
type: 'context'
|
||||
},
|
||||
process.function.EmptyArray
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
process.class.ProcessFunction,
|
||||
core.space.Model,
|
||||
{
|
||||
of: contact.mixin.Employee,
|
||||
category: 'attribute',
|
||||
label: process.string.ExecutionInitiator,
|
||||
type: 'context'
|
||||
},
|
||||
process.function.ExecutionEmployeeInitiator
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
process.class.ProcessFunction,
|
||||
core.space.Model,
|
||||
{
|
||||
of: contact.class.Person,
|
||||
category: 'attribute',
|
||||
label: process.string.ExecutionInitiator,
|
||||
type: 'context'
|
||||
},
|
||||
process.function.ExecutionInitiator
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
process.class.ProcessFunction,
|
||||
core.space.Model,
|
||||
{
|
||||
of: core.class.TypeDate,
|
||||
category: 'attribute',
|
||||
label: process.string.ExecutionStarted,
|
||||
type: 'context'
|
||||
},
|
||||
process.function.ExecutionStarted
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
process.class.ProcessFunction,
|
||||
core.space.Model,
|
||||
{
|
||||
of: contact.class.Person,
|
||||
category: 'attribute',
|
||||
label: process.string.CurrentUser,
|
||||
type: 'context'
|
||||
},
|
||||
process.function.CurrentUser
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
process.class.ProcessFunction,
|
||||
core.space.Model,
|
||||
{
|
||||
of: contact.mixin.Employee,
|
||||
category: 'attribute',
|
||||
label: process.string.CurrentUser,
|
||||
type: 'context'
|
||||
},
|
||||
process.function.CurrentEmployee
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
process.class.ProcessFunction,
|
||||
core.space.Model,
|
||||
{
|
||||
of: core.class.TypeDate,
|
||||
category: 'attribute',
|
||||
label: process.string.CurrentDate,
|
||||
type: 'context'
|
||||
},
|
||||
process.function.CurrentDate
|
||||
)
|
||||
}
|
||||
+82
-633
@@ -12,7 +12,6 @@
|
||||
// limitations under the License.
|
||||
|
||||
import card, { type Card, type MasterTag, type Tag } from '@hcengineering/card'
|
||||
import contact from '@hcengineering/contact'
|
||||
import core, {
|
||||
AccountRole,
|
||||
type Class,
|
||||
@@ -43,10 +42,12 @@ import presentation from '@hcengineering/model-presentation'
|
||||
import { TToDo } from '@hcengineering/model-time'
|
||||
import view, { createAction } from '@hcengineering/model-view'
|
||||
import workbench from '@hcengineering/model-workbench'
|
||||
import notification from '@hcengineering/notification'
|
||||
import { type Asset, type IntlString, type Resource } from '@hcengineering/platform'
|
||||
import {
|
||||
type CheckFunc,
|
||||
type ContextId,
|
||||
type CreatedContext,
|
||||
type Execution,
|
||||
type ExecutionContext,
|
||||
type ExecutionError,
|
||||
@@ -69,6 +70,9 @@ import {
|
||||
import time from '@hcengineering/time'
|
||||
import { type AnyComponent } from '@hcengineering/ui'
|
||||
import { type AttributeCategory } from '@hcengineering/view'
|
||||
import { defineMethods } from './actions'
|
||||
import { defineTriggers } from './triggers'
|
||||
import { defineFunctions } from './functions'
|
||||
import process from './plugin'
|
||||
|
||||
const DOMAIN_PROCESS = 'process' as Domain
|
||||
@@ -206,7 +210,7 @@ export class TMethod extends TDoc implements Method<Doc> {
|
||||
|
||||
editor!: AnyComponent
|
||||
|
||||
contextClass!: Ref<Class<Doc>> | null
|
||||
createdContext!: CreatedContext
|
||||
|
||||
presenter?: AnyComponent
|
||||
|
||||
@@ -247,6 +251,8 @@ export class TUpdateCriteriaComponent extends TDoc implements UpdateCriteriaComp
|
||||
editor!: AnyComponent
|
||||
|
||||
of!: Ref<Class<Doc<Space>>>
|
||||
|
||||
props!: Record<string, any>
|
||||
}
|
||||
|
||||
export * from './migration'
|
||||
@@ -265,6 +271,28 @@ export function createModel (builder: Builder): void {
|
||||
TUpdateCriteriaComponent
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
notification.class.NotificationType,
|
||||
core.space.Model,
|
||||
{
|
||||
hidden: false,
|
||||
generated: false,
|
||||
allowedForAuthor: true,
|
||||
label: process.string.NewProcessToDo,
|
||||
group: time.ids.TimeNotificationGroup,
|
||||
txClasses: [core.class.TxCreateDoc],
|
||||
objectClass: process.class.ProcessToDo,
|
||||
onlyOwn: true,
|
||||
defaultEnabled: true,
|
||||
templates: {
|
||||
textTemplate: '{body}',
|
||||
htmlTemplate: '<p>{body}</p>',
|
||||
subjectTemplate: '{title}'
|
||||
}
|
||||
},
|
||||
process.ids.ProcessToDoCreated
|
||||
)
|
||||
|
||||
createAction(builder, {
|
||||
action: view.actionImpl.Delete,
|
||||
label: view.string.Delete,
|
||||
@@ -339,444 +367,9 @@ export function createModel (builder: Builder): void {
|
||||
process.pipeline.ProcessMiddleware
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
process.class.ProcessFunction,
|
||||
core.space.Model,
|
||||
{
|
||||
of: core.class.TypeString,
|
||||
category: 'attribute',
|
||||
label: process.string.UpperCase,
|
||||
type: 'transform'
|
||||
},
|
||||
process.function.UpperCase
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
process.class.ProcessFunction,
|
||||
core.space.Model,
|
||||
{
|
||||
of: core.class.TypeString,
|
||||
category: 'attribute',
|
||||
label: process.string.LowerCase,
|
||||
type: 'transform'
|
||||
},
|
||||
process.function.LowerCase
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
process.class.ProcessFunction,
|
||||
core.space.Model,
|
||||
{
|
||||
of: core.class.TypeString,
|
||||
category: 'attribute',
|
||||
label: process.string.Trim,
|
||||
type: 'transform'
|
||||
},
|
||||
process.function.Trim
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
process.class.ProcessFunction,
|
||||
core.space.Model,
|
||||
{
|
||||
of: core.class.TypeString,
|
||||
category: 'attribute',
|
||||
label: process.string.Prepend,
|
||||
allowMany: true,
|
||||
type: 'transform',
|
||||
editor: process.transformEditor.AppendEditor
|
||||
},
|
||||
process.function.Prepend
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
process.class.ProcessFunction,
|
||||
core.space.Model,
|
||||
{
|
||||
of: core.class.TypeString,
|
||||
category: 'attribute',
|
||||
label: process.string.Append,
|
||||
allowMany: true,
|
||||
type: 'transform',
|
||||
editor: process.transformEditor.AppendEditor
|
||||
},
|
||||
process.function.Append
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
process.class.ProcessFunction,
|
||||
core.space.Model,
|
||||
{
|
||||
of: core.class.TypeString,
|
||||
category: 'attribute',
|
||||
label: process.string.Replace,
|
||||
allowMany: true,
|
||||
type: 'transform',
|
||||
editor: process.transformEditor.ReplaceEditor
|
||||
},
|
||||
process.function.Replace
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
process.class.ProcessFunction,
|
||||
core.space.Model,
|
||||
{
|
||||
of: core.class.TypeString,
|
||||
category: 'attribute',
|
||||
label: process.string.ReplaceAll,
|
||||
allowMany: true,
|
||||
type: 'transform',
|
||||
editor: process.transformEditor.ReplaceEditor
|
||||
},
|
||||
process.function.ReplaceAll
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
process.class.ProcessFunction,
|
||||
core.space.Model,
|
||||
{
|
||||
of: core.class.TypeString,
|
||||
category: 'attribute',
|
||||
label: process.string.Split,
|
||||
allowMany: true,
|
||||
type: 'transform',
|
||||
editor: process.transformEditor.SplitEditor
|
||||
},
|
||||
process.function.Split
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
process.class.ProcessFunction,
|
||||
core.space.Model,
|
||||
{
|
||||
of: core.class.TypeString,
|
||||
category: 'attribute',
|
||||
label: process.string.Cut,
|
||||
allowMany: true,
|
||||
editor: process.transformEditor.CutEditor,
|
||||
type: 'transform'
|
||||
},
|
||||
process.function.Cut
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
process.class.ProcessFunction,
|
||||
core.space.Model,
|
||||
{
|
||||
of: core.class.ArrOf,
|
||||
category: undefined,
|
||||
label: process.string.FirstValue,
|
||||
type: 'reduce'
|
||||
},
|
||||
process.function.FirstValue
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
process.class.ProcessFunction,
|
||||
core.space.Model,
|
||||
{
|
||||
of: core.class.ArrOf,
|
||||
type: 'reduce',
|
||||
category: undefined,
|
||||
label: process.string.LastValue
|
||||
},
|
||||
process.function.LastValue
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
process.class.ProcessFunction,
|
||||
core.space.Model,
|
||||
{
|
||||
of: core.class.ArrOf,
|
||||
type: 'reduce',
|
||||
category: undefined,
|
||||
label: process.string.Random
|
||||
},
|
||||
process.function.Random
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
process.class.ProcessFunction,
|
||||
core.space.Model,
|
||||
{
|
||||
of: core.class.ArrOf,
|
||||
category: 'array',
|
||||
label: process.string.Each,
|
||||
type: 'reduce'
|
||||
},
|
||||
process.function.All
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
process.class.ProcessFunction,
|
||||
core.space.Model,
|
||||
{
|
||||
of: core.class.TypeNumber,
|
||||
type: 'transform',
|
||||
category: 'attribute',
|
||||
label: process.string.Add,
|
||||
presenter: process.transformPresenter.NumberPresenter,
|
||||
allowMany: true,
|
||||
editor: process.transformEditor.NumberEditor
|
||||
},
|
||||
process.function.Add
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
process.class.ProcessFunction,
|
||||
core.space.Model,
|
||||
{
|
||||
of: core.class.TypeNumber,
|
||||
category: 'attribute',
|
||||
label: process.string.Subtract,
|
||||
presenter: process.transformPresenter.NumberPresenter,
|
||||
allowMany: true,
|
||||
editor: process.transformEditor.NumberEditor,
|
||||
type: 'transform'
|
||||
},
|
||||
process.function.Subtract
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
process.class.ProcessFunction,
|
||||
core.space.Model,
|
||||
{
|
||||
of: core.class.TypeNumber,
|
||||
category: 'attribute',
|
||||
label: process.string.Multiply,
|
||||
presenter: process.transformPresenter.NumberPresenter,
|
||||
type: 'transform',
|
||||
allowMany: true,
|
||||
editor: process.transformEditor.NumberEditor
|
||||
},
|
||||
process.function.Multiply
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
process.class.ProcessFunction,
|
||||
core.space.Model,
|
||||
{
|
||||
of: core.class.TypeNumber,
|
||||
category: 'attribute',
|
||||
label: process.string.Divide,
|
||||
presenter: process.transformPresenter.NumberPresenter,
|
||||
type: 'transform',
|
||||
allowMany: true,
|
||||
editor: process.transformEditor.NumberEditor
|
||||
},
|
||||
process.function.Divide
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
process.class.ProcessFunction,
|
||||
core.space.Model,
|
||||
{
|
||||
of: core.class.TypeNumber,
|
||||
category: 'attribute',
|
||||
label: process.string.Modulo,
|
||||
presenter: process.transformPresenter.NumberPresenter,
|
||||
type: 'transform',
|
||||
allowMany: true,
|
||||
editor: process.transformEditor.NumberEditor
|
||||
},
|
||||
process.function.Modulo
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
process.class.ProcessFunction,
|
||||
core.space.Model,
|
||||
{
|
||||
of: core.class.TypeNumber,
|
||||
category: 'attribute',
|
||||
label: process.string.Power,
|
||||
presenter: process.transformPresenter.NumberPresenter,
|
||||
type: 'transform',
|
||||
allowMany: true,
|
||||
editor: process.transformEditor.NumberEditor
|
||||
},
|
||||
process.function.Power
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
process.class.ProcessFunction,
|
||||
core.space.Model,
|
||||
{
|
||||
of: core.class.TypeNumber,
|
||||
category: 'attribute',
|
||||
label: process.string.Round,
|
||||
type: 'transform',
|
||||
allowMany: true
|
||||
},
|
||||
process.function.Round
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
process.class.ProcessFunction,
|
||||
core.space.Model,
|
||||
{
|
||||
of: core.class.TypeNumber,
|
||||
category: 'attribute',
|
||||
label: process.string.Absolute,
|
||||
type: 'transform',
|
||||
allowMany: true
|
||||
},
|
||||
process.function.Absolute
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
process.class.ProcessFunction,
|
||||
core.space.Model,
|
||||
{
|
||||
of: core.class.TypeNumber,
|
||||
category: 'attribute',
|
||||
label: process.string.Ceil,
|
||||
type: 'transform',
|
||||
allowMany: true
|
||||
},
|
||||
process.function.Ceil
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
process.class.ProcessFunction,
|
||||
core.space.Model,
|
||||
{
|
||||
of: core.class.TypeNumber,
|
||||
category: 'attribute',
|
||||
label: process.string.Floor,
|
||||
type: 'transform',
|
||||
allowMany: true
|
||||
},
|
||||
process.function.Floor
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
process.class.ProcessFunction,
|
||||
core.space.Model,
|
||||
{
|
||||
of: core.class.TypeDate,
|
||||
category: 'attribute',
|
||||
label: process.string.Offset,
|
||||
editor: process.transformEditor.DateOffsetEditor,
|
||||
type: 'transform'
|
||||
},
|
||||
process.function.Offset
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
process.class.ProcessFunction,
|
||||
core.space.Model,
|
||||
{
|
||||
of: core.class.TypeDate,
|
||||
category: 'attribute',
|
||||
label: process.string.FirstWorkingDayAfter,
|
||||
type: 'transform'
|
||||
},
|
||||
process.function.FirstWorkingDayAfter
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
process.class.ProcessFunction,
|
||||
core.space.Model,
|
||||
{
|
||||
of: core.class.ArrOf,
|
||||
category: 'array',
|
||||
label: process.string.Insert,
|
||||
allowMany: true,
|
||||
editor: process.transformEditor.MultiArrayElementEditor,
|
||||
type: 'transform'
|
||||
},
|
||||
process.function.Insert
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
process.class.ProcessFunction,
|
||||
core.space.Model,
|
||||
{
|
||||
of: core.class.ArrOf,
|
||||
category: 'array',
|
||||
label: process.string.Remove,
|
||||
allowMany: true,
|
||||
editor: process.transformEditor.ArrayElementEditor,
|
||||
type: 'transform'
|
||||
},
|
||||
process.function.Remove
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
process.class.ProcessFunction,
|
||||
core.space.Model,
|
||||
{
|
||||
of: core.class.ArrOf,
|
||||
category: 'array',
|
||||
label: process.string.RemoveFirst,
|
||||
allowMany: true,
|
||||
type: 'transform'
|
||||
},
|
||||
process.function.RemoveFirst
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
process.class.ProcessFunction,
|
||||
core.space.Model,
|
||||
{
|
||||
of: core.class.ArrOf,
|
||||
category: 'array',
|
||||
label: process.string.RemoveLast,
|
||||
allowMany: true,
|
||||
type: 'transform'
|
||||
},
|
||||
process.function.RemoveLast
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
process.class.ProcessFunction,
|
||||
core.space.Model,
|
||||
{
|
||||
of: contact.mixin.Employee,
|
||||
editor: process.component.RoleEditor,
|
||||
presenter: process.transformPresenter.RolePresenter,
|
||||
category: 'array',
|
||||
label: core.string.Role,
|
||||
type: 'context'
|
||||
},
|
||||
process.function.RoleContext
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
process.class.ProcessFunction,
|
||||
core.space.Model,
|
||||
{
|
||||
of: contact.class.Person,
|
||||
category: 'attribute',
|
||||
label: process.string.CurrentUser,
|
||||
type: 'context'
|
||||
},
|
||||
process.function.CurrentUser
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
process.class.ProcessFunction,
|
||||
core.space.Model,
|
||||
{
|
||||
of: contact.mixin.Employee,
|
||||
category: 'attribute',
|
||||
label: process.string.CurrentUser,
|
||||
type: 'context'
|
||||
},
|
||||
process.function.CurrentEmployee
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
process.class.ProcessFunction,
|
||||
core.space.Model,
|
||||
{
|
||||
of: core.class.TypeDate,
|
||||
category: 'attribute',
|
||||
label: process.string.CurrentDate,
|
||||
type: 'context'
|
||||
},
|
||||
process.function.CurrentDate
|
||||
)
|
||||
defineFunctions(builder)
|
||||
defineMethods(builder)
|
||||
defineTriggers(builder)
|
||||
|
||||
builder.mixin(process.class.Process, core.class.Class, view.mixin.AttributePresenter, {
|
||||
presenter: process.component.ProcessPresenter
|
||||
@@ -989,183 +582,6 @@ export function createModel (builder: Builder): void {
|
||||
process.section.CardProcesses
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
process.class.Method,
|
||||
core.space.Model,
|
||||
{
|
||||
label: process.string.RunProcess,
|
||||
objectClass: process.class.Process,
|
||||
editor: process.component.SubProcessEditor,
|
||||
presenter: process.component.SubProcessPresenter,
|
||||
contextClass: process.class.Execution,
|
||||
requiredParams: ['_id']
|
||||
},
|
||||
process.method.RunSubProcess
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
process.class.Method,
|
||||
core.space.Model,
|
||||
{
|
||||
label: process.string.CreateToDo,
|
||||
editor: process.component.ToDoEditor,
|
||||
objectClass: process.class.ProcessToDo,
|
||||
presenter: process.component.ToDoPresenter,
|
||||
contextClass: process.class.ProcessToDo,
|
||||
requiredParams: ['state', 'title', 'user'],
|
||||
defaultParams: {
|
||||
withRollback: true
|
||||
}
|
||||
},
|
||||
process.method.CreateToDo
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
process.class.Trigger,
|
||||
core.space.Model,
|
||||
{
|
||||
label: process.string.OnExecutionStart,
|
||||
icon: process.icon.Process,
|
||||
init: true,
|
||||
requiredParams: []
|
||||
},
|
||||
process.trigger.OnExecutionStart
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
process.class.Method,
|
||||
core.space.Model,
|
||||
{
|
||||
label: process.string.UpdateCard,
|
||||
editor: process.component.UpdateCardEditor,
|
||||
objectClass: card.class.Card,
|
||||
contextClass: null,
|
||||
presenter: process.component.UpdateCardPresenter,
|
||||
requiredParams: []
|
||||
},
|
||||
process.method.UpdateCard
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
process.class.Method,
|
||||
core.space.Model,
|
||||
{
|
||||
label: process.string.CreateCard,
|
||||
objectClass: card.class.Card,
|
||||
editor: process.component.CreateCardEditor,
|
||||
presenter: process.component.CreateCardPresenter,
|
||||
contextClass: card.class.Card,
|
||||
requiredParams: ['title', '_class']
|
||||
},
|
||||
process.method.CreateCard
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
process.class.Method,
|
||||
core.space.Model,
|
||||
{
|
||||
label: core.string.AddRelation,
|
||||
objectClass: core.class.Relation,
|
||||
editor: process.component.AddRelationEditor,
|
||||
presenter: process.component.AddRelationPresenter,
|
||||
contextClass: core.class.Relation,
|
||||
requiredParams: ['association', 'direction', '_id']
|
||||
},
|
||||
process.method.AddRelation
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
process.class.Method,
|
||||
core.space.Model,
|
||||
{
|
||||
label: card.string.AddTag,
|
||||
objectClass: card.class.Tag,
|
||||
editor: process.component.AddTagEditor,
|
||||
presenter: process.component.AddTagPresenter,
|
||||
contextClass: card.class.Card,
|
||||
requiredParams: ['_id']
|
||||
},
|
||||
process.method.AddTag
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
process.class.Trigger,
|
||||
core.space.Model,
|
||||
{
|
||||
label: process.string.OnToDoDone,
|
||||
icon: process.icon.ToDo,
|
||||
editor: process.component.ToDoCloseEditor,
|
||||
presenter: process.component.ToDoSettingPresenter,
|
||||
requiredParams: ['_id'],
|
||||
checkFunction: process.triggerCheck.ToDo,
|
||||
init: false,
|
||||
auto: true
|
||||
},
|
||||
process.trigger.OnToDoClose
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
process.class.Trigger,
|
||||
core.space.Model,
|
||||
{
|
||||
label: process.string.OnToDoCancelled,
|
||||
icon: process.icon.ToDoRemove,
|
||||
editor: process.component.ToDoRemoveEditor,
|
||||
presenter: process.component.ToDoSettingPresenter,
|
||||
requiredParams: ['_id'],
|
||||
checkFunction: process.triggerCheck.ToDo,
|
||||
init: false,
|
||||
auto: true
|
||||
},
|
||||
process.trigger.OnToDoRemove
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
process.class.Trigger,
|
||||
core.space.Model,
|
||||
{
|
||||
label: process.string.OnCardUpdate,
|
||||
icon: process.icon.OnCardUpdate,
|
||||
editor: process.component.CardUpdateEditor,
|
||||
presenter: process.component.CardUpdatePresenter,
|
||||
requiredParams: [],
|
||||
checkFunction: process.triggerCheck.UpdateCheck,
|
||||
init: false,
|
||||
auto: true
|
||||
},
|
||||
process.trigger.OnCardUpdate
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
process.class.Trigger,
|
||||
core.space.Model,
|
||||
{
|
||||
label: process.string.OnSubProcessesDone,
|
||||
icon: process.icon.WaitSubprocesses,
|
||||
checkFunction: process.triggerCheck.SubProcessesDoneCheck,
|
||||
requiredParams: [],
|
||||
init: false,
|
||||
auto: true
|
||||
},
|
||||
process.trigger.OnSubProcessesDone
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
process.class.Trigger,
|
||||
core.space.Model,
|
||||
{
|
||||
label: process.string.WaitUntil,
|
||||
icon: process.icon.Time,
|
||||
requiredParams: [],
|
||||
init: false,
|
||||
auto: true,
|
||||
editor: process.component.TimeEditor,
|
||||
presenter: process.component.TimePresenter,
|
||||
checkFunction: process.triggerCheck.Time
|
||||
},
|
||||
process.trigger.OnTime
|
||||
)
|
||||
|
||||
builder.createDoc(card.class.MasterTagEditorSection, core.space.Model, {
|
||||
id: 'processes',
|
||||
label: process.string.Processes,
|
||||
@@ -1179,50 +595,83 @@ export function createModel (builder: Builder): void {
|
||||
|
||||
builder.createDoc(process.class.UpdateCriteriaComponent, core.space.Model, {
|
||||
category: 'attribute',
|
||||
editor: process.criteriaEditor.StringCriteria,
|
||||
of: core.class.TypeString
|
||||
editor: process.criteriaEditor.BaseCriteria,
|
||||
of: core.class.TypeString,
|
||||
props: {
|
||||
modes: ['Equal', 'StringContains', 'Exists']
|
||||
}
|
||||
})
|
||||
|
||||
builder.createDoc(process.class.UpdateCriteriaComponent, core.space.Model, {
|
||||
category: 'attribute',
|
||||
editor: process.criteriaEditor.StringCriteria,
|
||||
of: core.class.TypeHyperlink
|
||||
editor: process.criteriaEditor.BaseCriteria,
|
||||
of: core.class.TypeHyperlink,
|
||||
props: {
|
||||
modes: ['Equal', 'StringContains', 'Exists']
|
||||
}
|
||||
})
|
||||
|
||||
builder.createDoc(process.class.UpdateCriteriaComponent, core.space.Model, {
|
||||
category: 'attribute',
|
||||
editor: process.criteriaEditor.NumberCriteria,
|
||||
of: core.class.TypeNumber
|
||||
editor: process.criteriaEditor.BaseCriteria,
|
||||
of: core.class.TypeNumber,
|
||||
props: {
|
||||
modes: ['Equal', 'GT', 'LT', 'Between', 'Exists']
|
||||
}
|
||||
})
|
||||
|
||||
builder.createDoc(process.class.UpdateCriteriaComponent, core.space.Model, {
|
||||
category: 'attribute',
|
||||
editor: process.criteriaEditor.DateCriteria,
|
||||
of: core.class.TypeDate
|
||||
editor: process.criteriaEditor.BaseCriteria,
|
||||
of: core.class.TypeDate,
|
||||
props: {
|
||||
modes: ['Equal', 'GT', 'LT', 'Between', 'Exists']
|
||||
}
|
||||
})
|
||||
|
||||
builder.createDoc(process.class.UpdateCriteriaComponent, core.space.Model, {
|
||||
category: 'attribute',
|
||||
editor: process.criteriaEditor.BooleanCriteria,
|
||||
of: core.class.TypeBoolean
|
||||
editor: process.criteriaEditor.BaseCriteria,
|
||||
of: core.class.TypeBoolean,
|
||||
props: {
|
||||
modes: ['Equal', 'NotEqual', 'Exists']
|
||||
}
|
||||
})
|
||||
|
||||
builder.createDoc(process.class.UpdateCriteriaComponent, core.space.Model, {
|
||||
category: 'array',
|
||||
editor: process.criteriaEditor.ArrayCriteria,
|
||||
of: core.class.ArrOf
|
||||
editor: process.criteriaEditor.BaseCriteria,
|
||||
of: core.class.ArrOf,
|
||||
props: {
|
||||
modes: [
|
||||
'ArrayAll',
|
||||
'ArrayAny',
|
||||
'ArrayNotIncludes',
|
||||
'ArraySizeEquals',
|
||||
'ArraySizeGt',
|
||||
'ArraySizeGte',
|
||||
'ArraySizeLt',
|
||||
'ArraySizeLte'
|
||||
]
|
||||
}
|
||||
})
|
||||
|
||||
builder.createDoc(process.class.UpdateCriteriaComponent, core.space.Model, {
|
||||
category: 'attribute',
|
||||
editor: process.criteriaEditor.EnumCriteria,
|
||||
of: core.class.EnumOf
|
||||
editor: process.criteriaEditor.BaseCriteria,
|
||||
of: core.class.EnumOf,
|
||||
props: {
|
||||
modes: ['Equal', 'NotEqual', 'Exists']
|
||||
}
|
||||
})
|
||||
|
||||
builder.createDoc(process.class.UpdateCriteriaComponent, core.space.Model, {
|
||||
category: 'object',
|
||||
editor: process.criteriaEditor.RefCriteria,
|
||||
of: core.class.RefTo
|
||||
editor: process.criteriaEditor.BaseCriteria,
|
||||
of: core.class.RefTo,
|
||||
props: {
|
||||
modes: ['Equal', 'NotEqual', 'Exists']
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -30,7 +30,8 @@ export default mergeIds(processId, process, {
|
||||
ProcessMiddleware: '' as Ref<PresentationMiddlewareFactory>
|
||||
},
|
||||
ids: {
|
||||
ProcessSettings: '' as Ref<Doc>
|
||||
ProcessSettings: '' as Ref<Doc>,
|
||||
ProcessToDoCreated: '' as Ref<Doc>
|
||||
},
|
||||
actionImpl: {
|
||||
ContinueExecution: '' as ViewAction
|
||||
@@ -40,6 +41,7 @@ export default mergeIds(processId, process, {
|
||||
ContinueExecution: '' as Ref<Action<Doc, any>>
|
||||
},
|
||||
string: {
|
||||
NewProcessToDo: '' as IntlString,
|
||||
ConfigLabel: '' as IntlString,
|
||||
ConfigDescription: '' as IntlString,
|
||||
LogAction: '' as IntlString
|
||||
|
||||
@@ -0,0 +1,112 @@
|
||||
// Copyright © 2025 Hardcore Engineering Inc.
|
||||
//
|
||||
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License. You may
|
||||
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
//
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import core from '@hcengineering/core'
|
||||
import { type Builder } from '@hcengineering/model'
|
||||
import process from './plugin'
|
||||
|
||||
export function defineTriggers (builder: Builder): void {
|
||||
builder.createDoc(
|
||||
process.class.Trigger,
|
||||
core.space.Model,
|
||||
{
|
||||
label: process.string.OnToDoDone,
|
||||
icon: process.icon.ToDo,
|
||||
editor: process.component.ToDoCloseEditor,
|
||||
presenter: process.component.ToDoSettingPresenter,
|
||||
requiredParams: ['_id'],
|
||||
checkFunction: process.triggerCheck.ToDo,
|
||||
init: false,
|
||||
auto: true
|
||||
},
|
||||
process.trigger.OnToDoClose
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
process.class.Trigger,
|
||||
core.space.Model,
|
||||
{
|
||||
label: process.string.OnToDoCancelled,
|
||||
icon: process.icon.ToDoRemove,
|
||||
editor: process.component.ToDoRemoveEditor,
|
||||
presenter: process.component.ToDoSettingPresenter,
|
||||
requiredParams: ['_id'],
|
||||
checkFunction: process.triggerCheck.ToDo,
|
||||
init: false,
|
||||
auto: true
|
||||
},
|
||||
process.trigger.OnToDoRemove
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
process.class.Trigger,
|
||||
core.space.Model,
|
||||
{
|
||||
label: process.string.WhenCardMatches,
|
||||
icon: process.icon.WhenCardMatches,
|
||||
editor: process.component.CardUpdateEditor,
|
||||
presenter: process.component.CardUpdatePresenter,
|
||||
requiredParams: [],
|
||||
checkFunction: process.triggerCheck.MatchCheck,
|
||||
init: false,
|
||||
auto: true
|
||||
},
|
||||
process.trigger.OnCardUpdate
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
process.class.Trigger,
|
||||
core.space.Model,
|
||||
{
|
||||
label: process.string.WhenFieldChanges,
|
||||
icon: process.icon.WhenFieldChanges,
|
||||
editor: process.component.FieldChangesEditor,
|
||||
presenter: process.component.CardUpdatePresenter,
|
||||
requiredParams: [],
|
||||
checkFunction: process.triggerCheck.FieldChangedCheck,
|
||||
init: false,
|
||||
auto: true
|
||||
},
|
||||
process.trigger.WhenFieldChanges
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
process.class.Trigger,
|
||||
core.space.Model,
|
||||
{
|
||||
label: process.string.OnSubProcessesDone,
|
||||
icon: process.icon.WaitSubprocesses,
|
||||
checkFunction: process.triggerCheck.SubProcessesDoneCheck,
|
||||
requiredParams: [],
|
||||
init: false,
|
||||
auto: true
|
||||
},
|
||||
process.trigger.OnSubProcessesDone
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
process.class.Trigger,
|
||||
core.space.Model,
|
||||
{
|
||||
label: process.string.WaitUntil,
|
||||
icon: process.icon.Time,
|
||||
requiredParams: [],
|
||||
init: false,
|
||||
auto: true,
|
||||
editor: process.component.TimeEditor,
|
||||
presenter: process.component.TimePresenter,
|
||||
checkFunction: process.triggerCheck.Time
|
||||
},
|
||||
process.trigger.OnTime
|
||||
)
|
||||
}
|
||||
+23
-17
@@ -37,6 +37,7 @@ import { type KeyBinding, type ViewOptionModel, type ViewOptionsModel } from '@h
|
||||
import recruit from './plugin'
|
||||
import { createReviewModel, reviewTableConfig, reviewTableOptions } from './review'
|
||||
import { defineSpaceType } from './spaceType'
|
||||
import { definePermissions } from './permissions'
|
||||
import { TApplicant, TApplicantMatch, TCandidate, TOpinion, TReview, TVacancy, TVacancyList } from './types'
|
||||
|
||||
export { recruitId } from '@hcengineering/recruit'
|
||||
@@ -1019,24 +1020,28 @@ export function createModel (builder: Builder): void {
|
||||
},
|
||||
override: [recruit.action.CreateGlobalApplication]
|
||||
})
|
||||
createAction(builder, {
|
||||
action: view.actionImpl.ShowPopup,
|
||||
actionProps: {
|
||||
component: recruit.component.CreateCandidate,
|
||||
element: 'top'
|
||||
createAction(
|
||||
builder,
|
||||
{
|
||||
action: view.actionImpl.ShowPopup,
|
||||
actionProps: {
|
||||
component: recruit.component.CreateCandidate,
|
||||
element: 'top'
|
||||
},
|
||||
label: recruit.string.CreateTalent,
|
||||
icon: recruit.icon.Create,
|
||||
keyBinding: ['keyC'],
|
||||
input: 'none',
|
||||
category: recruit.category.Recruit,
|
||||
target: core.class.Doc,
|
||||
context: {
|
||||
mode: ['workbench', 'browser'],
|
||||
application: recruit.app.Recruit,
|
||||
group: 'create'
|
||||
}
|
||||
},
|
||||
label: recruit.string.CreateTalent,
|
||||
icon: recruit.icon.Create,
|
||||
keyBinding: ['keyC'],
|
||||
input: 'none',
|
||||
category: recruit.category.Recruit,
|
||||
target: core.class.Doc,
|
||||
context: {
|
||||
mode: ['workbench', 'browser'],
|
||||
application: recruit.app.Recruit,
|
||||
group: 'create'
|
||||
}
|
||||
})
|
||||
recruit.action.CreateTalent
|
||||
)
|
||||
|
||||
createAction(builder, {
|
||||
action: view.actionImpl.ShowPopup,
|
||||
@@ -1596,4 +1601,5 @@ export function createModel (builder: Builder): void {
|
||||
)
|
||||
|
||||
defineSpaceType(builder)
|
||||
definePermissions(builder)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
import type { Builder } from '@hcengineering/model'
|
||||
import core from '@hcengineering/core'
|
||||
import recruit from '@hcengineering/recruit'
|
||||
|
||||
export function definePermissions (builder: Builder): void {
|
||||
builder.createDoc(
|
||||
core.class.Permission,
|
||||
core.space.Model,
|
||||
{
|
||||
label: recruit.string.ForbidCreateVacancyPermission,
|
||||
scope: 'workspace',
|
||||
txClass: core.class.TxCreateDoc,
|
||||
objectClass: recruit.class.Vacancy,
|
||||
forbid: true,
|
||||
description: recruit.string.ForbidCreateVacancyPermissionDescription
|
||||
},
|
||||
recruit.permission.ForbidCreateVacancy
|
||||
)
|
||||
}
|
||||
@@ -61,7 +61,11 @@ export function createModel (builder: Builder): void {
|
||||
})
|
||||
|
||||
builder.mixin(process.trigger.OnCardUpdate, process.class.Trigger, serverProcess.mixin.TriggerImpl, {
|
||||
serverCheckFunc: serverProcess.func.OnCardUpdateCheck
|
||||
serverCheckFunc: serverProcess.func.MatchCardCheck
|
||||
})
|
||||
|
||||
builder.mixin(process.trigger.WhenFieldChanges, process.class.Trigger, serverProcess.mixin.TriggerImpl, {
|
||||
serverCheckFunc: serverProcess.func.FieldChangedCheck
|
||||
})
|
||||
|
||||
builder.mixin(process.trigger.OnTime, process.class.Trigger, serverProcess.mixin.TriggerImpl, {
|
||||
@@ -173,6 +177,10 @@ export function createModel (builder: Builder): void {
|
||||
func: serverProcess.transform.Power
|
||||
})
|
||||
|
||||
builder.mixin(process.function.Sqrt, process.class.ProcessFunction, serverProcess.mixin.FuncImpl, {
|
||||
func: serverProcess.transform.Sqrt
|
||||
})
|
||||
|
||||
builder.mixin(process.function.Round, process.class.ProcessFunction, serverProcess.mixin.FuncImpl, {
|
||||
func: serverProcess.transform.Round
|
||||
})
|
||||
@@ -229,6 +237,27 @@ export function createModel (builder: Builder): void {
|
||||
func: serverProcess.transform.RemoveLast
|
||||
})
|
||||
|
||||
builder.mixin(process.function.EmptyArray, process.class.ProcessFunction, serverProcess.mixin.FuncImpl, {
|
||||
func: serverProcess.transform.EmptyArray
|
||||
})
|
||||
|
||||
builder.mixin(
|
||||
process.function.ExecutionEmployeeInitiator,
|
||||
process.class.ProcessFunction,
|
||||
serverProcess.mixin.FuncImpl,
|
||||
{
|
||||
func: serverProcess.transform.ExecutionInitiator
|
||||
}
|
||||
)
|
||||
|
||||
builder.mixin(process.function.ExecutionInitiator, process.class.ProcessFunction, serverProcess.mixin.FuncImpl, {
|
||||
func: serverProcess.transform.ExecutionInitiator
|
||||
})
|
||||
|
||||
builder.mixin(process.function.ExecutionStarted, process.class.ProcessFunction, serverProcess.mixin.FuncImpl, {
|
||||
func: serverProcess.transform.ExecutionStarted
|
||||
})
|
||||
|
||||
builder.createDoc(serverCore.class.Trigger, core.space.Model, {
|
||||
trigger: serverProcess.trigger.OnExecutionContinue,
|
||||
txMatch: {
|
||||
|
||||
@@ -52,7 +52,7 @@ import document from '@hcengineering/model-document'
|
||||
import tracker from '@hcengineering/model-tracker'
|
||||
import view, { createAction } from '@hcengineering/model-view'
|
||||
import workbench from '@hcengineering/model-workbench'
|
||||
import notification from '@hcengineering/notification'
|
||||
import notification, { type NotificationGroup } from '@hcengineering/notification'
|
||||
import recruit from '@hcengineering/recruit'
|
||||
import tags from '@hcengineering/tags'
|
||||
import {
|
||||
@@ -372,7 +372,7 @@ export function createModel (builder: Builder): void {
|
||||
generated: false,
|
||||
allowedForAuthor: true,
|
||||
label: time.string.NewToDo,
|
||||
group: time.ids.TimeNotificationGroup,
|
||||
group: time.ids.TimeNotificationGroup as Ref<NotificationGroup>,
|
||||
txClasses: [core.class.TxCreateDoc],
|
||||
objectClass: time.class.ProjectToDo,
|
||||
onlyOwn: true,
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
|
||||
import { type Client, type Doc, type Ref } from '@hcengineering/core'
|
||||
import { type Application } from '@hcengineering/model-workbench'
|
||||
import { type NotificationGroup, type NotificationType } from '@hcengineering/notification'
|
||||
import { type NotificationType } from '@hcengineering/notification'
|
||||
import { type IntlString, mergeIds, type Resource } from '@hcengineering/platform'
|
||||
import { timeId } from '@hcengineering/time'
|
||||
import time from '@hcengineering/time-resources/src/plugin'
|
||||
@@ -56,7 +56,6 @@ export default mergeIds(timeId, time, {
|
||||
Team: '' as Ref<Application>
|
||||
},
|
||||
ids: {
|
||||
TimeNotificationGroup: '' as Ref<NotificationGroup>,
|
||||
ToDoCreated: '' as Ref<NotificationType>
|
||||
},
|
||||
function: {
|
||||
|
||||
@@ -32,6 +32,7 @@ import { PaletteColorIndexes } from '@hcengineering/ui/src/colors'
|
||||
import { createActions as defineActions } from './actions'
|
||||
import tracker from './plugin'
|
||||
import { definePresenters } from './presenters'
|
||||
import { definePermissions } from './permissions'
|
||||
import {
|
||||
DOMAIN_TRACKER,
|
||||
TClassicProjectTypeData,
|
||||
@@ -709,6 +710,7 @@ export function createModel (builder: Builder): void {
|
||||
]
|
||||
})
|
||||
|
||||
definePermissions(builder)
|
||||
defineSpaceType(builder)
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
import type { Builder } from '@hcengineering/model'
|
||||
import core from '@hcengineering/core'
|
||||
import tracker from '@hcengineering/tracker'
|
||||
|
||||
export function definePermissions (builder: Builder): void {
|
||||
builder.createDoc(
|
||||
core.class.Permission,
|
||||
core.space.Model,
|
||||
{
|
||||
label: tracker.string.ForbidCreateProjectPermission,
|
||||
txClass: core.class.TxCreateDoc,
|
||||
objectClass: tracker.class.Project,
|
||||
forbid: true,
|
||||
scope: 'workspace',
|
||||
description: tracker.string.ForbidCreateProjectPermissionDescription
|
||||
},
|
||||
tracker.permission.ForbidCreateProject
|
||||
)
|
||||
}
|
||||
@@ -354,18 +354,22 @@ function defineTraining (builder: Builder): void {
|
||||
|
||||
definePermission(builder, training.permission.ChangeSomeoneElsesTrainingOwner, {
|
||||
label: training.string.Permission_ChangeSomeoneElsesTrainingOwner,
|
||||
scope: 'space',
|
||||
description: training.string.Permission_ChangeSomeoneElsesTrainingOwner_Description
|
||||
})
|
||||
definePermission(builder, training.permission.CreateTraining, {
|
||||
label: training.string.Permission_CreateTraining,
|
||||
scope: 'space',
|
||||
description: training.string.Permission_CreateTraining_Description
|
||||
})
|
||||
definePermission(builder, training.permission.ViewSomeoneElsesTrainingOverview, {
|
||||
label: training.string.Permission_ViewSomeoneElsesTrainingOverview,
|
||||
scope: 'space',
|
||||
description: training.string.Permission_ViewSomeoneElsesTrainingOverview_Description
|
||||
})
|
||||
definePermission(builder, training.permission.ViewSomeoneElsesTrainingQuestions, {
|
||||
label: training.string.Permission_ViewSomeoneElsesTrainingQuestions,
|
||||
scope: 'space',
|
||||
description: training.string.Permission_ViewSomeoneElsesTrainingQuestions_Description
|
||||
})
|
||||
}
|
||||
@@ -491,14 +495,17 @@ function defineTrainingRequest (builder: Builder): void {
|
||||
|
||||
definePermission(builder, training.permission.ChangeSomeoneElsesSentRequestOwner, {
|
||||
label: training.string.Permission_ChangeSomeoneElsesSentRequestOwner,
|
||||
scope: 'space',
|
||||
description: training.string.Permission_ChangeSomeoneElsesSentRequestOwner_Description
|
||||
})
|
||||
definePermission(builder, training.permission.CreateRequestOnSomeoneElsesTraining, {
|
||||
label: training.string.Permission_CreateRequestOnSomeoneElsesTraining,
|
||||
scope: 'space',
|
||||
description: training.string.Permission_CreateRequestOnSomeoneElsesTraining_Description
|
||||
})
|
||||
definePermission(builder, training.permission.ViewSomeoneElsesSentRequest, {
|
||||
label: training.string.Permission_ViewSomeoneElsesSentRequest,
|
||||
scope: 'space',
|
||||
description: training.string.Permission_ViewSomeoneElsesSentRequest_Description
|
||||
})
|
||||
})()
|
||||
@@ -753,6 +760,7 @@ function defineTrainingAttempt (builder: Builder): void {
|
||||
|
||||
definePermission(builder, training.permission.ViewSomeoneElsesTraineesResults, {
|
||||
label: training.string.Permission_ViewSomeoneElsesTraineesResults,
|
||||
scope: 'space',
|
||||
description: training.string.Permission_ViewSomeoneElsesTraineesResults_Description
|
||||
})
|
||||
}
|
||||
|
||||
@@ -244,7 +244,7 @@ export class TObjectValidator extends TClass implements ObjectValidator {
|
||||
@Mixin(view.mixin.ObjectFactory, core.class.Class)
|
||||
export class TObjectFactory extends TClass implements ObjectFactory {
|
||||
component?: AnyComponent
|
||||
create?: Resource<() => Promise<void>>
|
||||
create?: Resource<(props?: Record<string, any>) => Promise<Ref<Doc> | undefined>>
|
||||
}
|
||||
|
||||
@Mixin(view.mixin.ObjectTitle, core.class.Class)
|
||||
@@ -835,6 +835,25 @@ export function createModel (builder: Builder): void {
|
||||
view.action.Join
|
||||
)
|
||||
|
||||
createAction(builder, {
|
||||
action: view.actionImpl.ShowPopup,
|
||||
actionProps: {
|
||||
component: view.component.AddRelationPopup,
|
||||
fillProps: {
|
||||
_objects: 'value'
|
||||
}
|
||||
},
|
||||
label: core.string.AddRelation,
|
||||
input: 'any',
|
||||
icon: view.icon.CopyLink,
|
||||
category: view.category.Editor,
|
||||
target: core.class.Doc,
|
||||
context: {
|
||||
mode: ['context', 'browser'],
|
||||
group: 'associate'
|
||||
}
|
||||
})
|
||||
|
||||
createAction(
|
||||
builder,
|
||||
{
|
||||
|
||||
@@ -96,7 +96,8 @@ export default mergeIds(viewId, view, {
|
||||
BaseDocPresenter: '' as AnyComponent,
|
||||
MasterDetailView: '' as AnyComponent,
|
||||
AssociationPresenter: '' as AnyComponent,
|
||||
TreeView: '' as AnyComponent
|
||||
TreeView: '' as AnyComponent,
|
||||
AddRelationPopup: '' as AnyComponent
|
||||
},
|
||||
string: {
|
||||
Table: '' as IntlString,
|
||||
|
||||
@@ -192,6 +192,22 @@ export function createModel (builder: Builder): void {
|
||||
group: 'edit'
|
||||
}
|
||||
})
|
||||
|
||||
createAction(
|
||||
builder,
|
||||
{
|
||||
action: workbench.actionImpl.CloseCurrentTab,
|
||||
label: presentation.string.Close,
|
||||
icon: view.icon.Delete,
|
||||
keyBinding: ['Meta + keyW', 'Ctrl + keyW'],
|
||||
input: 'none',
|
||||
category: workbench.category.Workbench,
|
||||
allowedForEditableContent: 'always',
|
||||
target: core.class.Doc,
|
||||
context: { mode: ['workbench', 'browser', 'panel', 'editor', 'input'] }
|
||||
},
|
||||
workbench.action.CloseCurrentTab
|
||||
)
|
||||
}
|
||||
|
||||
export default workbench
|
||||
|
||||
@@ -40,6 +40,7 @@ export default mergeIds(workbenchId, workbench, {
|
||||
actionImpl: {
|
||||
PinTab: '' as Resource<(doc?: Doc | Doc[]) => Promise<void>>,
|
||||
UnpinTab: '' as Resource<(doc?: Doc | Doc[]) => Promise<void>>,
|
||||
CloseTab: '' as Resource<(doc?: Doc | Doc[]) => Promise<void>>
|
||||
CloseTab: '' as Resource<(doc?: Doc | Doc[]) => Promise<void>>,
|
||||
CloseCurrentTab: '' as Resource<(doc?: Doc | Doc[]) => Promise<void>>
|
||||
}
|
||||
})
|
||||
|
||||
@@ -220,6 +220,7 @@ export interface AccountClient {
|
||||
getIntegrationSecret: (integrationSecretKey: IntegrationSecretKey) => Promise<IntegrationSecret | null>
|
||||
listIntegrationsSecrets: (filter: Partial<IntegrationSecretKey>) => Promise<IntegrationSecret[]>
|
||||
getAccountInfo: (uuid: AccountUuid) => Promise<AccountInfo>
|
||||
canMergeSpecifiedPersons: (primaryPerson: PersonUuid, secondaryPerson: PersonUuid) => Promise<boolean>
|
||||
mergeSpecifiedPersons: (primaryPerson: PersonUuid, secondaryPerson: PersonUuid) => Promise<void>
|
||||
mergeSpecifiedAccounts: (primaryAccount: AccountUuid, secondaryAccount: AccountUuid) => Promise<void>
|
||||
addEmailSocialId: (email: string) => Promise<OtpInfo>
|
||||
@@ -1072,6 +1073,15 @@ class AccountClientImpl implements AccountClient {
|
||||
return await this.rpc(request)
|
||||
}
|
||||
|
||||
async canMergeSpecifiedPersons (primaryPerson: PersonUuid, secondaryPerson: PersonUuid): Promise<boolean> {
|
||||
const request = {
|
||||
method: 'canMergeSpecifiedPersons' as const,
|
||||
params: { primaryPerson, secondaryPerson }
|
||||
}
|
||||
|
||||
return await this.rpc(request)
|
||||
}
|
||||
|
||||
async mergeSpecifiedPersons (primaryPerson: PersonUuid, secondaryPerson: PersonUuid): Promise<void> {
|
||||
const request = {
|
||||
method: 'mergeSpecifiedPersons' as const,
|
||||
|
||||
@@ -40,6 +40,7 @@ export interface LoginInfoWorkspace {
|
||||
endpoint: EndpointInfo
|
||||
role: AccountRole | null
|
||||
progress?: number
|
||||
branding?: string
|
||||
}
|
||||
|
||||
export interface LoginInfoWithWorkspaces extends LoginInfo {
|
||||
|
||||
@@ -82,7 +82,7 @@ class RestTxClient implements Client {
|
||||
params: DomainParams,
|
||||
options?: DomainRequestOptions
|
||||
): Promise<DomainResult<T>> {
|
||||
throw new Error('Domain request operation not supported')
|
||||
return await this.client.domainRequest(domain, params, options)
|
||||
}
|
||||
|
||||
async findOne<T extends Doc>(
|
||||
|
||||
@@ -69,4 +69,4 @@
|
||||
"PersonId": "Osoba",
|
||||
"AccountId": "Účet"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,4 +69,4 @@
|
||||
"PersonId": "Id de personne",
|
||||
"AccountId": "Compte"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
{
|
||||
"string": {
|
||||
"Id": "Id",
|
||||
"Space": "Alan",
|
||||
"Spaces": "Alanlar",
|
||||
"SpacesDescription": "Tüm alanların alan tipini yönet",
|
||||
"TypedSpace": "Tipli alan",
|
||||
"SpaceType": "Alan tipi",
|
||||
"Modified": "Değiştirildi",
|
||||
"ModifiedDate": "Değiştirilme tarihi",
|
||||
"ModifiedBy": "Değiştiren",
|
||||
"Class": "Sınıf",
|
||||
"AttachedTo": "Eklenen",
|
||||
"AttachedToClass": "Eklenen sınıf",
|
||||
"Name": "İsim",
|
||||
"Description": "Açıklama",
|
||||
"ShortDescription": "Kısa açıklama",
|
||||
"Descriptor": "Tanımlayıcı",
|
||||
"TargetClass": "Hedef sınıf",
|
||||
"Role": "Rol",
|
||||
"Roles": "Roller",
|
||||
"Private": "Özel",
|
||||
"Archived": "Arşivlendi",
|
||||
"ClassLabel": "Tip",
|
||||
"ClassPropertyLabel": "Etiket",
|
||||
"String": "Metin",
|
||||
"Markup": "İşaretleme",
|
||||
"Number": "Sayı",
|
||||
"Boolean": "Onay kutusu",
|
||||
"Timestamp": "Zaman damgası",
|
||||
"Date": "Tarih",
|
||||
"IntlString": "IntlString",
|
||||
"Ref": "Referans",
|
||||
"Collection": "Koleksiyon",
|
||||
"Array": "Çoklu Seçim",
|
||||
"Enum": "Seçim",
|
||||
"Members": "Üyeler",
|
||||
"Hyperlink": "URL",
|
||||
"MarkupBlobRef": "İşbirlikçi",
|
||||
"Object": "Nesne",
|
||||
"System": "Sistem",
|
||||
"CreatedBy": "Oluşturan",
|
||||
"CreatedDate": "Oluşturulma tarihi",
|
||||
"Status": "Durum",
|
||||
"StatusCategory": "Durum kategorisi",
|
||||
"Account": "Hesap",
|
||||
"Rank": "Sıra",
|
||||
"Owners": "Sahipler",
|
||||
"Permission": "İzin",
|
||||
"CreateObject": "Nesne oluştur",
|
||||
"UpdateObject": "Nesneyi güncelle",
|
||||
"DeleteObject": "Nesneyi sil",
|
||||
"ForbidDeleteObject": "Nesne silmeyi yasakla",
|
||||
"UpdateSpace": "Alanı güncelle",
|
||||
"ArchiveSpace": "Alanı arşivle",
|
||||
"CreateObjectDescription": "Kullanıcılara alanda nesne oluşturma yetkisi verir",
|
||||
"UpdateObjectDescription": "Kullanıcılara alandaki nesneleri güncelleme yetkisi verir",
|
||||
"DeleteObjectDescription": "Kullanıcılara alandaki nesneleri silme yetkisi verir",
|
||||
"ForbidDeleteObjectDescription": "Kullanıcıların alandaki nesneleri silmesini yasaklar",
|
||||
"UpdateSpaceDescription": "Kullanıcılara alanı güncelleme yetkisi verir",
|
||||
"ArchiveSpaceDescription": "Kullanıcılara alanı arşivleme yetkisi verir",
|
||||
"AutoJoin": "Otomatik katıl",
|
||||
"AutoJoinDescr": "Yeni çalışanları bu alana otomatik olarak ekle",
|
||||
"BlobSize": "Boyut",
|
||||
"BlobContentType": "İçerik türü",
|
||||
"Relation": "İlişki",
|
||||
"Relations": "İlişkiler",
|
||||
"AddRelation": "İlişki ekle",
|
||||
"PersonId": "Kişi",
|
||||
"AccountId": "Hesap"
|
||||
}
|
||||
}
|
||||
@@ -17,6 +17,7 @@
|
||||
import type { Asset, IntlString, Plugin } from '@hcengineering/platform'
|
||||
import type { DocumentQuery } from './storage'
|
||||
import { type WorkspaceDataId, type WorkspaceUuid } from './utils'
|
||||
import { Tx } from '.'
|
||||
|
||||
/**
|
||||
* @public
|
||||
@@ -333,6 +334,15 @@ export interface TypeDate extends Type<Date> {
|
||||
withShift: boolean
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export interface TypeNumber extends Type<number> {
|
||||
min?: number
|
||||
max?: number
|
||||
digits?: number // Number of digits after comma
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
@@ -520,6 +530,10 @@ export type RolesAssignment = Record<Ref<Role>, AccountUuid[] | undefined>
|
||||
*/
|
||||
export interface Permission extends Doc {
|
||||
label: IntlString
|
||||
txClass?: Ref<Class<Tx>>
|
||||
forbid?: boolean
|
||||
objectClass?: Ref<Class<Doc>>
|
||||
scope?: 'space' | 'workspace'
|
||||
description?: IntlString
|
||||
icon?: Asset
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ class Client implements HulylakeClient {
|
||||
}
|
||||
|
||||
private objectUrl (key: string): string {
|
||||
return `${this.baseUrl}/api/${this.workspace}/${key}`
|
||||
return `${this.baseUrl}/api/${this.workspace}/${encodeURIComponent(key)}`
|
||||
}
|
||||
|
||||
private authHeaders (init?: HeadersInit): Headers {
|
||||
|
||||
@@ -26,10 +26,3 @@ export class HulylakeError extends Error {
|
||||
this.name = 'HulylakeError'
|
||||
}
|
||||
}
|
||||
|
||||
export class NotFoundError extends Error {
|
||||
constructor (message: string) {
|
||||
super(message)
|
||||
this.name = 'NotFoundError'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,6 +69,7 @@ export type JsonPatch =
|
||||
| { op: 'test', path: string, value: any }
|
||||
| { hop: 'add', path: string, value: any, safe?: boolean }
|
||||
| { hop: 'inc', path: string, value: number, safe?: boolean }
|
||||
| { hop: 'remove', path: string, safe?: boolean }
|
||||
|
||||
export interface HulyResponse<Body = ArrayBuffer | string | any> {
|
||||
ok: boolean
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
//
|
||||
|
||||
import { RetryOptions, withRetry } from '@hcengineering/retry'
|
||||
import { HulylakeError, NetworkError, NotFoundError } from './error'
|
||||
import { HulylakeError, NetworkError } from './error'
|
||||
|
||||
async function innerFetchSafe (url: string | URL, init?: RequestInit): Promise<Response> {
|
||||
let response
|
||||
@@ -31,7 +31,7 @@ async function innerFetchSafe (url: string | URL, init?: RequestInit): Promise<R
|
||||
|
||||
const text = await response.text()
|
||||
if (response.status === 404) {
|
||||
throw new NotFoundError(text)
|
||||
return response
|
||||
} else {
|
||||
throw new HulylakeError(text)
|
||||
}
|
||||
|
||||
@@ -50,6 +50,7 @@ import core, {
|
||||
type ArrOf as TypeArrOf,
|
||||
type Collection as TypeCollection,
|
||||
type TypeDate as TypeDateType,
|
||||
type TypeNumber as TypeNumberType,
|
||||
generateId,
|
||||
type AccountUuid
|
||||
} from '@hcengineering/core'
|
||||
@@ -413,8 +414,8 @@ export function TypeHyperlink (): Type<Hyperlink> {
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export function TypeNumber (): Type<number> {
|
||||
return { _class: core.class.TypeNumber, label: core.string.Number, icon: core.icon.TypeNumber }
|
||||
export function TypeNumber (min?: number, max?: number, digits?: number): TypeNumberType {
|
||||
return { _class: core.class.TypeNumber, label: core.string.Number, icon: core.icon.TypeNumber, min, max, digits }
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
{
|
||||
"status": {
|
||||
"LoadingPlugin": "{plugin} eklentisi yükleniyor...",
|
||||
"UnknownError": "Bilinmeyen hata: {message}",
|
||||
"InvalidId": "Geçersiz Id: {id}",
|
||||
"BadRequest": "Hatalı istek",
|
||||
"Forbidden": "Yasak",
|
||||
"Conflict": "Çakışma",
|
||||
"ExpiredLink": "Bu davet bağlantısının süresi dolmuş",
|
||||
"Unauthorized": "Yetkisiz",
|
||||
"UnknownMethod": "Bilinmeyen metod: {method}",
|
||||
"InternalServerError": "Dahili sunucu hatası",
|
||||
"MaintenanceWarning": "Bakım Planlandı:",
|
||||
"MaintenanceWarningTime": "{time, plural, =1 {bir dakikadan az} other {# dakika}}",
|
||||
"AccountNotFound": "Hesap bulunamadı veya girilen kimlik bilgileri yanlış",
|
||||
"AccountNotConfirmed": "Hesap doğrulanmadı",
|
||||
"WorkspaceNotFound": "Çalışma alanı bulunamadı",
|
||||
"AccountAlreadyExists": "Hesap zaten mevcut",
|
||||
"WorkspaceRateLimit": "Sunucu meşgul, Lütfen biraz bekleyin ve tekrar deneyin",
|
||||
"AccountAlreadyConfirmed": "Hesap zaten doğrulandı",
|
||||
"WorkspaceAlreadyExists": "Çalışma alanı zaten mevcut",
|
||||
"InvalidOtp": "Geçersiz kod",
|
||||
"InviteNotFound": "E-posta ile davet bulunamadı: {email}",
|
||||
"WorkspaceLimitReached": "Çalışma alanı limitine ulaştınız. Lütfen bizimle iletişime geçin...",
|
||||
"ReadOnlyAccount": "Anonim Misafir Demo",
|
||||
"SystemAccount": "Sistem hesabı",
|
||||
"SocialIdAlreadyExists": "Sosyal ID zaten mevcut"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
{
|
||||
"string": {
|
||||
"Create": "Oluştur",
|
||||
"Cancel": "İptal",
|
||||
"Ok": "Tamam",
|
||||
"Save": "Kaydet",
|
||||
"Saved": "Kaydedildi...",
|
||||
"Download": "İndir",
|
||||
"DownloadOriginal": "Orijinali indir",
|
||||
"Delete": "Sil",
|
||||
"Close": "Kapat",
|
||||
"NotSelected": "Seçilmedi",
|
||||
"Deselect": "Seçimi kaldır",
|
||||
"Archived": "(arşivlendi)",
|
||||
"AddSocialLinks": "Sosyal bağlantılar ekle",
|
||||
"EditSocialLinks": "Sosyal bağlantıları düzenle",
|
||||
"Change": "Değiştir",
|
||||
"Remove": "Kaldır",
|
||||
"Search": "Ara...",
|
||||
"Spaces": "Alanlar",
|
||||
"NumberSpaces": "{count, plural, =0 {İçinde} =1 {1 yerde} other {# yerde}}",
|
||||
"InThis": "Bu {space} içinde",
|
||||
"NoMatchesInThis": "Bu {space} içinde eşleşme yok",
|
||||
"NoMatchesFound": "Eşleşme bulunamadı",
|
||||
"NotInThis": "Bu {space} içinde değil",
|
||||
"Match": "Eşleşme",
|
||||
"Add": "Ekle",
|
||||
"Edit": "Düzenle",
|
||||
"DocumentPreview": "Önizleme",
|
||||
"MakePrivate": "Özel yap",
|
||||
"MakePrivateDescription": "Sadece üyeler görebilir",
|
||||
"Created": "Oluşturuldu",
|
||||
"Selected": "Seçildi",
|
||||
"NoResults": "Gösterilecek sonuç yok",
|
||||
"Next": "İleri",
|
||||
"FailedToPreview": "Önizleme başarısız",
|
||||
"ContentType": "İçerik türü",
|
||||
"ContentTypeNotSupported": "Bu içerik türü için önizleme mevcut değil",
|
||||
"StartDrawing": "Üzerine çiz",
|
||||
"DrawingHistory": "Çizim geçmişi",
|
||||
"ColorAdd": "Renk önayarı ekle",
|
||||
"ColorRemove": "Renk önayarını kaldır",
|
||||
"ColorReset": "Renk önayarlarını sıfırla",
|
||||
"Copy": "Kopyala",
|
||||
"DocumentUrlCopied": "Kopyalandı!",
|
||||
"CopyLink": "Bağlantıyı kopyala",
|
||||
"AccessDenied": "Nesne mevcut değil veya erişim izniniz yok.",
|
||||
"UnableToFollowMention": "Bahsedilen nesne açılamıyor",
|
||||
"Undo": "Geri al",
|
||||
"Redo": "Yinele",
|
||||
"ClearCanvas": "Tuvali temizle",
|
||||
"PenTool": "Kalem aracı",
|
||||
"EraserTool": "Silgi aracı",
|
||||
"PanTool": "Kaydırma aracı",
|
||||
"TextTool": "Metin aracı",
|
||||
"PaletteManagementMenu": "Renk önayarlarını yönet"
|
||||
},
|
||||
"status": {
|
||||
"FileTooLarge": "Dosya çok büyük"
|
||||
}
|
||||
}
|
||||
@@ -61,11 +61,12 @@
|
||||
"@hcengineering/emoji": "^0.6.0",
|
||||
"@hcengineering/theme": "^0.6.5",
|
||||
"@hcengineering/retry": "^0.6.0",
|
||||
"@hcengineering/hulylake-client": "^0.6.0",
|
||||
"fast-equals": "^5.2.2",
|
||||
"png-chunks-extract": "^1.0.0",
|
||||
"svelte": "^4.2.20",
|
||||
"uuid": "^8.3.2",
|
||||
"yjs": "^13.6.23"
|
||||
"yjs": "^13.6.27"
|
||||
},
|
||||
"repository": "https://github.com/hcengineering/platform",
|
||||
"publishConfig": {
|
||||
|
||||
@@ -33,26 +33,24 @@ import {
|
||||
type UpdateAttachmentsOperation,
|
||||
type UpdateNotificationContextEvent,
|
||||
type UpdateNotificationEvent,
|
||||
type UpdateNotificationQuery,
|
||||
type NotificationQuery,
|
||||
type UpdatePatchEvent
|
||||
} from '@hcengineering/communication-sdk-types'
|
||||
import {
|
||||
type AccountID,
|
||||
type AccountUuid,
|
||||
type CardID,
|
||||
type CardType,
|
||||
type Collaborator,
|
||||
type ContextID,
|
||||
type FindCollaboratorsParams,
|
||||
type FindLabelsParams,
|
||||
type FindMessagesGroupsParams,
|
||||
type FindMessagesParams,
|
||||
type FindNotificationContextParams,
|
||||
type FindNotificationsParams,
|
||||
type FindMessagesMetaParams,
|
||||
type Label,
|
||||
type Markdown,
|
||||
type Message,
|
||||
type MessageID,
|
||||
type MessagesGroup,
|
||||
MessageType,
|
||||
type Notification,
|
||||
type NotificationContext,
|
||||
@@ -61,7 +59,12 @@ import {
|
||||
type AttachmentData,
|
||||
type AttachmentParams,
|
||||
type AttachmentUpdateData,
|
||||
type WithTotal
|
||||
type WithTotal,
|
||||
type NotificationID,
|
||||
type Emoji,
|
||||
type MessageMeta,
|
||||
type FindMessagesGroupParams,
|
||||
type MessagesGroup
|
||||
} from '@hcengineering/communication-types'
|
||||
import core, {
|
||||
generateId,
|
||||
@@ -71,17 +74,19 @@ import core, {
|
||||
SocialIdType,
|
||||
type Tx,
|
||||
type TxDomainEvent,
|
||||
AccountRole
|
||||
AccountRole,
|
||||
generateUuid
|
||||
} from '@hcengineering/core'
|
||||
import { onDestroy } from 'svelte'
|
||||
import { addNotification, NotificationSeverity, languageStore } from '@hcengineering/ui'
|
||||
import { translate } from '@hcengineering/platform'
|
||||
import { getMetadata, translate } from '@hcengineering/platform'
|
||||
import view from '@hcengineering/view'
|
||||
import { v4 as uuid } from 'uuid'
|
||||
|
||||
import { getCurrentWorkspaceUuid, getFilesUrl } from './file'
|
||||
import { addTxListener, removeTxListener, type TxListener } from './utils'
|
||||
import { get } from 'svelte/store'
|
||||
import { getClient as getHulylakeClient } from '@hcengineering/hulylake-client'
|
||||
|
||||
import { getCurrentWorkspaceUuid } from './file'
|
||||
import { addTxListener, removeTxListener, type TxListener } from './utils'
|
||||
import presentation from './plugin'
|
||||
|
||||
export {
|
||||
createCollaboratorsQuery,
|
||||
@@ -107,7 +112,12 @@ export async function setCommunicationClient (platformClient: PlatformClient): P
|
||||
client.close()
|
||||
}
|
||||
const _client = new Client(platformClient)
|
||||
initLiveQueries(_client, getCurrentWorkspaceUuid(), getFilesUrl(), onDestroy)
|
||||
|
||||
const token = getMetadata(presentation.metadata.Token) ?? ''
|
||||
const hulylakeUrl = getMetadata(presentation.metadata.HulylakeUrl) ?? ''
|
||||
const hulylake = getHulylakeClient(hulylakeUrl, getCurrentWorkspaceUuid(), token)
|
||||
|
||||
initLiveQueries(_client, hulylake, onDestroy)
|
||||
client = _client
|
||||
onClientListeners.forEach((fn) => {
|
||||
fn()
|
||||
@@ -161,7 +171,7 @@ class Client {
|
||||
async createMessage (cardId: CardID, cardType: CardType, content: Markdown): Promise<CreateMessageResult> {
|
||||
const event: CreateMessageEvent = {
|
||||
type: MessageEventType.CreateMessage,
|
||||
messageType: MessageType.Message,
|
||||
messageType: MessageType.Text,
|
||||
cardId,
|
||||
cardType,
|
||||
content,
|
||||
@@ -198,28 +208,28 @@ class Client {
|
||||
await this.sendEvent(event)
|
||||
}
|
||||
|
||||
async addReaction (cardId: CardID, messageId: MessageID, reaction: string): Promise<void> {
|
||||
async addReaction (cardId: CardID, messageId: MessageID, emoji: Emoji): Promise<void> {
|
||||
const event: ReactionPatchEvent = {
|
||||
type: MessageEventType.ReactionPatch,
|
||||
cardId,
|
||||
messageId,
|
||||
operation: {
|
||||
opcode: 'add',
|
||||
reaction
|
||||
reaction: emoji
|
||||
},
|
||||
socialId: this.getSocialId()
|
||||
}
|
||||
await this.sendEvent(event)
|
||||
}
|
||||
|
||||
async removeReaction (cardId: CardID, messageId: MessageID, reaction: string): Promise<void> {
|
||||
async removeReaction (cardId: CardID, messageId: MessageID, emoji: Emoji): Promise<void> {
|
||||
const event: ReactionPatchEvent = {
|
||||
type: MessageEventType.ReactionPatch,
|
||||
cardId,
|
||||
messageId,
|
||||
operation: {
|
||||
opcode: 'remove',
|
||||
reaction
|
||||
reaction: emoji
|
||||
},
|
||||
socialId: this.getSocialId()
|
||||
}
|
||||
@@ -245,7 +255,7 @@ class Client {
|
||||
opcode: 'add',
|
||||
attachments: ops.add.map((it) => ({
|
||||
...it,
|
||||
id: it.id ?? (uuid() as AttachmentID)
|
||||
id: it.id ?? (generateUuid() as AttachmentID)
|
||||
}))
|
||||
})
|
||||
}
|
||||
@@ -262,7 +272,7 @@ class Client {
|
||||
opcode: 'set',
|
||||
attachments: ops.set.map((it) => ({
|
||||
...it,
|
||||
id: it.id ?? (uuid() as AttachmentID)
|
||||
id: it.id ?? (generateUuid() as AttachmentID)
|
||||
}))
|
||||
})
|
||||
}
|
||||
@@ -286,7 +296,7 @@ class Client {
|
||||
await this.sendEvent(event)
|
||||
}
|
||||
|
||||
async addCollaborators (cardId: CardID, cardType: CardType, collaborators: AccountID[]): Promise<void> {
|
||||
async addCollaborators (cardId: CardID, cardType: CardType, collaborators: AccountUuid[]): Promise<void> {
|
||||
const event: AddCollaboratorsEvent = {
|
||||
type: NotificationEventType.AddCollaborators,
|
||||
cardId,
|
||||
@@ -297,7 +307,7 @@ class Client {
|
||||
await this.sendEvent(event)
|
||||
}
|
||||
|
||||
async removeCollaborators (cardId: CardID, cardType: CardType, collaborators: AccountID[]): Promise<void> {
|
||||
async removeCollaborators (cardId: CardID, cardType: CardType, collaborators: AccountUuid[]): Promise<void> {
|
||||
const event: RemoveCollaboratorsEvent = {
|
||||
type: NotificationEventType.RemoveCollaborators,
|
||||
cardId,
|
||||
@@ -329,7 +339,11 @@ class Client {
|
||||
await this.sendEvent(event)
|
||||
}
|
||||
|
||||
async updateNotifications (contextId: ContextID, query: UpdateNotificationQuery, read: boolean): Promise<void> {
|
||||
async updateNotifications (
|
||||
contextId: ContextID,
|
||||
query: Pick<NotificationQuery, 'type' | 'untilDate'> & { id?: NotificationID },
|
||||
read: boolean
|
||||
): Promise<void> {
|
||||
const event: UpdateNotificationEvent = {
|
||||
type: NotificationEventType.UpdateNotification,
|
||||
contextId,
|
||||
@@ -342,15 +356,15 @@ class Client {
|
||||
await this.sendEvent(event)
|
||||
}
|
||||
|
||||
async findMessages (params: FindMessagesParams, queryId?: number): Promise<Message[]> {
|
||||
async findMessagesMeta (params: FindMessagesMetaParams): Promise<MessageMeta[]> {
|
||||
return (
|
||||
await this.connection.domainRequest<Message[]>(COMMUNICATION, {
|
||||
findMessages: { params, queryId }
|
||||
await this.connection.domainRequest<MessageMeta[]>(COMMUNICATION, {
|
||||
findMessagesMeta: { params }
|
||||
})
|
||||
).value
|
||||
}
|
||||
|
||||
async findMessagesGroups (params: FindMessagesGroupsParams): Promise<MessagesGroup[]> {
|
||||
async findMessagesGroups (params: FindMessagesGroupParams): Promise<MessagesGroup[]> {
|
||||
return (
|
||||
await this.connection.domainRequest<MessagesGroup[]>(COMMUNICATION, {
|
||||
findMessagesGroups: { params }
|
||||
@@ -360,19 +374,22 @@ class Client {
|
||||
|
||||
async findNotificationContexts (
|
||||
params: FindNotificationContextParams,
|
||||
queryId?: number
|
||||
subscription?: number | string
|
||||
): Promise<NotificationContext[]> {
|
||||
return (
|
||||
await this.connection.domainRequest<NotificationContext[]>(COMMUNICATION, {
|
||||
findNotificationContexts: { params, queryId }
|
||||
findNotificationContexts: { params, subscription }
|
||||
})
|
||||
).value
|
||||
}
|
||||
|
||||
async findNotifications (params: FindNotificationsParams, queryId?: number): Promise<WithTotal<Notification>> {
|
||||
async findNotifications (
|
||||
params: FindNotificationsParams,
|
||||
subscription?: number | string
|
||||
): Promise<WithTotal<Notification>> {
|
||||
return (
|
||||
await this.connection.domainRequest<WithTotal<Notification>>(COMMUNICATION, {
|
||||
findNotifications: { params, queryId }
|
||||
findNotifications: { params, subscription }
|
||||
})
|
||||
).value
|
||||
}
|
||||
@@ -393,9 +410,15 @@ class Client {
|
||||
).value
|
||||
}
|
||||
|
||||
async unsubscribeQuery (id: number): Promise<void> {
|
||||
async subscribeCard (cardId: CardID, subscription: string | number): Promise<void> {
|
||||
await this.connection.domainRequest<Message[]>(COMMUNICATION, {
|
||||
unsubscribeQuery: id
|
||||
subscribeCard: { cardId, subscription }
|
||||
})
|
||||
}
|
||||
|
||||
async unsubscribeCard (cardId: CardID, subscription: string | number): Promise<void> {
|
||||
await this.connection.domainRequest<Message[]>(COMMUNICATION, {
|
||||
unsubscribeCard: { cardId, subscription }
|
||||
})
|
||||
}
|
||||
|
||||
@@ -438,7 +461,7 @@ class Client {
|
||||
return id
|
||||
}
|
||||
|
||||
private getAccount (): AccountID {
|
||||
private getAccount (): AccountUuid {
|
||||
return getCurrentAccount().uuid
|
||||
}
|
||||
}
|
||||
|
||||
@@ -79,14 +79,11 @@
|
||||
findObjectPresenter(_class)
|
||||
}
|
||||
|
||||
const checkSelected = (item?: Doc): void => {
|
||||
if (item === undefined) {
|
||||
return
|
||||
}
|
||||
if (selectedElements.has(item._id)) {
|
||||
selectedElements.delete(item._id)
|
||||
const checkSelected = (_id: Ref<Doc>): void => {
|
||||
if (selectedElements.has(_id)) {
|
||||
selectedElements.delete(_id)
|
||||
} else {
|
||||
selectedElements.add(item._id)
|
||||
selectedElements.add(_id)
|
||||
}
|
||||
|
||||
selectedObjects = Array.from(selectedElements)
|
||||
@@ -97,12 +94,16 @@
|
||||
let selection = 0
|
||||
let list: ListView
|
||||
|
||||
async function handleSelection (evt: Event | undefined, objects: Doc[], selection: number): Promise<void> {
|
||||
function handleSelection (evt: Event | undefined, objects: Doc[], selection: number): void {
|
||||
const item = objects[selection]
|
||||
if (item === undefined) {
|
||||
return
|
||||
}
|
||||
|
||||
select(item)
|
||||
}
|
||||
|
||||
function select (item: Doc): void {
|
||||
if (!multiSelect) {
|
||||
if (allowDeselect) {
|
||||
selected = item._id === selected ? undefined : item._id
|
||||
@@ -111,7 +112,7 @@
|
||||
}
|
||||
dispatch(closeAfterSelect ? 'close' : 'update', selected !== undefined ? item : undefined)
|
||||
} else {
|
||||
checkSelected(item)
|
||||
checkSelected(item._id)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -129,27 +130,35 @@
|
||||
if (key.code === 'Enter') {
|
||||
key.preventDefault()
|
||||
key.stopPropagation()
|
||||
void handleSelection(key, objects, selection)
|
||||
handleSelection(key, objects, selection)
|
||||
}
|
||||
}
|
||||
const manager = createFocusManager()
|
||||
|
||||
function onCreate (): void {
|
||||
async function onCreate (): Promise<void> {
|
||||
if (create === undefined) {
|
||||
return
|
||||
}
|
||||
const c = create
|
||||
showPopup(c.component, c.props ?? {}, 'top', async (res) => {
|
||||
const handler = async (res: Ref<Doc> | undefined) => {
|
||||
if (res != null) {
|
||||
// We expect reference to new object.
|
||||
const newPerson = await getClient().findOne(_class, { _id: res })
|
||||
if (newPerson !== undefined) {
|
||||
search = c.update?.(newPerson) ?? ''
|
||||
dispatch('created', newPerson)
|
||||
dispatch('search', search)
|
||||
const newObject = await getClient().findOne(_class, { _id: res })
|
||||
if (newObject !== undefined) {
|
||||
dispatch('created', newObject)
|
||||
select(newObject)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
const c = create
|
||||
if (c.component !== undefined) {
|
||||
showPopup(c.component, c.props ?? {}, 'top', handler)
|
||||
} else if (c.func !== undefined) {
|
||||
const impl = await getResource(c.func)
|
||||
if (impl !== undefined) {
|
||||
const newObjectId = await impl(c.props)
|
||||
await handler(newObjectId)
|
||||
}
|
||||
}
|
||||
}
|
||||
function toAny (obj: any): any {
|
||||
return obj
|
||||
@@ -248,7 +257,7 @@
|
||||
class="menu-item withList w-full flex-row-center"
|
||||
disabled={readonly || isDeselectDisabled || loading}
|
||||
on:click={() => {
|
||||
void handleSelection(undefined, objects, item)
|
||||
handleSelection(undefined, objects, item)
|
||||
}}
|
||||
>
|
||||
{#if type === 'text'}
|
||||
|
||||
@@ -180,7 +180,8 @@ export default plugin(presentationId, {
|
||||
StatsUrl: '' as Metadata<string>,
|
||||
MailUrl: '' as Metadata<string>,
|
||||
PreviewUrl: '' as Metadata<string>,
|
||||
PulseUrl: '' as Metadata<string>
|
||||
PulseUrl: '' as Metadata<string>,
|
||||
HulylakeUrl: '' as Metadata<string>
|
||||
},
|
||||
status: {
|
||||
FileTooLarge: '' as StatusCode
|
||||
|
||||
@@ -35,10 +35,11 @@ export interface ObjectSearchResult {
|
||||
* @public
|
||||
*/
|
||||
export interface ObjectCreate {
|
||||
component: AnyComponent
|
||||
component: AnyComponent | undefined
|
||||
props?: Record<string, any>
|
||||
label: IntlString
|
||||
update?: (doc: Doc) => string
|
||||
func?: Resource<(props?: Record<string, any>) => Promise<Ref<Doc> | undefined>>
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -40,7 +40,8 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@hcengineering/core": "^0.6.32",
|
||||
"fast-equals": "^5.2.2"
|
||||
"fast-equals": "^5.2.2",
|
||||
"hash-it": "^6.0.0"
|
||||
},
|
||||
"repository": "https://github.com/hcengineering/platform",
|
||||
"publishConfig": {
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
import { hashAttrs, stripHash } from '../utils'
|
||||
|
||||
describe('hashAttrs', () => {
|
||||
it('should return a hash of length 8', () => {
|
||||
const attrs = { a: 1 }
|
||||
const hash = hashAttrs(attrs)
|
||||
expect(hash.length).toEqual(8)
|
||||
})
|
||||
it('should return the same hash for the same attrs', () => {
|
||||
const attrs = { a: 1, b: 2 }
|
||||
const hash1 = hashAttrs(attrs)
|
||||
const hash2 = hashAttrs(attrs)
|
||||
expect(hash1).toEqual(hash2)
|
||||
})
|
||||
|
||||
it('should return different hashes for different attrs', () => {
|
||||
const attrs1 = { a: 1, b: 2 }
|
||||
const attrs2 = { a: 1, b: 3 }
|
||||
const hash1 = hashAttrs(attrs1)
|
||||
const hash2 = hashAttrs(attrs2)
|
||||
expect(hash1).not.toEqual(hash2)
|
||||
})
|
||||
})
|
||||
|
||||
describe('stripHash', () => {
|
||||
it('should return the name without the hash', () => {
|
||||
const name = 'bold--c0decafe'
|
||||
const result = stripHash(name)
|
||||
expect(result).toEqual('bold')
|
||||
})
|
||||
|
||||
it('should return the original name if no hash is present', () => {
|
||||
const name = 'bold'
|
||||
const result = stripHash(name)
|
||||
expect(result).toEqual(name)
|
||||
})
|
||||
|
||||
it('should return the original name if the hash is not 8 characters long', () => {
|
||||
const name = 'bold--1234567'
|
||||
const result = stripHash(name)
|
||||
expect(result).toEqual(name)
|
||||
})
|
||||
|
||||
it('should return the original name if the hash is not a valid base64 string', () => {
|
||||
const name = 'bold--invalid!'
|
||||
const result = stripHash(name)
|
||||
expect(result).toEqual(name)
|
||||
})
|
||||
})
|
||||
@@ -16,9 +16,11 @@
|
||||
import { Markup } from '@hcengineering/core'
|
||||
|
||||
import { deepEqual } from 'fast-equals'
|
||||
import hashIt from 'hash-it'
|
||||
|
||||
import { nodeDoc, nodeParagraph, nodeText } from './dsl'
|
||||
import { MarkupMark, MarkupNode, MarkupNodeType, emptyMarkupNode } from './model'
|
||||
import { traverseNode } from './traverse'
|
||||
import { MarkupMark, MarkupMarkType, MarkupNode, MarkupNodeType, emptyMarkupNode } from './model'
|
||||
import { traverseAllMarks, traverseNode } from './traverse'
|
||||
|
||||
/** @public */
|
||||
export const EmptyMarkup: Markup = jsonToMarkup(emptyMarkupNode())
|
||||
@@ -124,7 +126,11 @@ export function markupToJSON (markup: Markup): MarkupNode {
|
||||
// But there seem to be some cases when it contains HTML or plain text
|
||||
// So we need to handle those cases and produce valid MarkupNode
|
||||
if (markup.startsWith('{')) {
|
||||
return JSON.parse(markup) as MarkupNode
|
||||
const json = JSON.parse(markup) as MarkupNode
|
||||
traverseAllMarks(json, (node, mark) => {
|
||||
mark.type = stripHash(mark.type) as MarkupMarkType
|
||||
})
|
||||
return json
|
||||
} else {
|
||||
return nodeDoc(nodeParagraph(nodeText(markup)))
|
||||
}
|
||||
@@ -207,3 +213,17 @@ export function markupToText (markup: Markup): string {
|
||||
|
||||
return fragments.join('').trim()
|
||||
}
|
||||
|
||||
// see https://github.com/yjs/y-prosemirror/blob/master/src/lib.js#L402
|
||||
const hashedMarkNameRegex = /(.*)(--[a-zA-Z0-9+/=]{8})$/
|
||||
|
||||
/** @public */
|
||||
export function stripHash (attrName: string): string {
|
||||
return hashedMarkNameRegex.exec(attrName)?.[1] ?? attrName
|
||||
}
|
||||
|
||||
/** @public */
|
||||
export function hashAttrs (attrs: any): string {
|
||||
const hash = hashIt(attrs)
|
||||
return (hash >>> 0).toString(16).padStart(8, '0')
|
||||
}
|
||||
|
||||
@@ -37,12 +37,13 @@
|
||||
"@types/jest": "^29.5.5",
|
||||
"jest-environment-jsdom": "^29.7.0",
|
||||
"fast-equals": "^5.2.2",
|
||||
"y-prosemirror": "^1.3.6"
|
||||
"y-prosemirror": "^1.3.7"
|
||||
},
|
||||
"dependencies": {
|
||||
"@hcengineering/core": "^0.6.32",
|
||||
"@hcengineering/text": "^0.6.5",
|
||||
"yjs": "^13.6.23"
|
||||
"@hcengineering/text-core": "^0.6.0",
|
||||
"yjs": "^13.6.27"
|
||||
},
|
||||
"repository": "https://github.com/hcengineering/platform",
|
||||
"publishConfig": {
|
||||
|
||||
@@ -25,6 +25,7 @@ declare global {
|
||||
namespace jest {
|
||||
interface Matchers<R> {
|
||||
toEqualMarkup: (expected: string) => R
|
||||
toEqualYdoc: (expected: YDoc) => R
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -35,8 +36,22 @@ expect.extend({
|
||||
return {
|
||||
message: () =>
|
||||
pass
|
||||
? `Expected markup strings NOT to be equal:\n Received: ${received}\n Expected: ${expected}`
|
||||
: `Expected markup strings to be equal:\n Received: ${received}\n Expected: ${expected}`,
|
||||
? `Expected markup strings NOT to be equal:\n Expected: ${expected}\n Received: ${received}`
|
||||
: `Expected markup strings to be equal:\n Expected: ${expected}\n Received: ${received}`,
|
||||
pass
|
||||
}
|
||||
},
|
||||
|
||||
toEqualYdoc (received: YDoc, expected: YDoc) {
|
||||
const expectedJSON = expected.toJSON()
|
||||
const receivedJSON = received.toJSON()
|
||||
|
||||
const pass = deepEqual(expectedJSON, receivedJSON)
|
||||
return {
|
||||
message: () =>
|
||||
pass
|
||||
? `Expected yjs documents NOT to be equal:\n Expected: ${JSON.stringify(expectedJSON)}\n Received: ${JSON.stringify(receivedJSON)}`
|
||||
: `Expected yjs documents to be equal:\n Expected: ${JSON.stringify(expectedJSON)}\n Received: ${JSON.stringify(receivedJSON)}`,
|
||||
pass
|
||||
}
|
||||
}
|
||||
@@ -54,52 +69,157 @@ function referenceYDocToMarkup (ydoc: YDoc, field: string): Markup {
|
||||
return jsonToMarkup(json as MarkupNode)
|
||||
}
|
||||
|
||||
const markups: Array<[string, Markup]> = [
|
||||
['text', '{"type":"doc","content":[{"type":"paragraph","content":[{"type":"text","text":"hello world"}]}]}'],
|
||||
[
|
||||
'text with bold mark',
|
||||
'{"type":"doc","content":[{"type":"paragraph","content":[{"type":"text","marks":[{"type":"bold","attrs":{}}],"text":"hello world"}]}]}'
|
||||
],
|
||||
[
|
||||
'separate paragraphs with bold mark',
|
||||
'{"type":"doc","content":[{"type":"paragraph","content":[{"type":"text","marks":[{"type":"bold","attrs":{}}],"text":"hello"}]},{"type":"paragraph","content":[{"type":"text","marks":[{"type":"bold","attrs":{}}],"text":"world"}]}]}'
|
||||
],
|
||||
[
|
||||
'mixed text and text with bold mark',
|
||||
'{"type":"doc","content":[{"type":"paragraph","content":[{"type":"text","text":"hello "},{"type":"text","marks":[{"type":"bold","attrs":{}}],"text":"world"}]}]}'
|
||||
],
|
||||
[
|
||||
'mixed text with italic and text with bold and italic marks',
|
||||
'{"type":"doc","content":[{"type":"paragraph","content":[{"type":"text","marks":[{"type":"italic","attrs":{}}],"text":"hello "},{"type":"text","marks":[{"type":"bold","attrs":{}},{"type":"italic","attrs":{}}],"text":"world"}]}]}'
|
||||
],
|
||||
[
|
||||
'text with link and italic marks',
|
||||
'{"type":"doc","content":[{"type":"paragraph","content":[{"type":"text","text":"hello "},{"type":"text","text":"hello world","marks":[{"type":"link","attrs":{"href":"http://example.com","target":"_blank","rel":"noopener noreferrer","class":"cursor-pointer"}},{"type":"italic","attrs":{}}]}]}]}'
|
||||
],
|
||||
[
|
||||
'image',
|
||||
'{"type":"doc","content":[{"type":"paragraph","content":[{"type":"image","attrs":{"src":"http://example.com/image.jpg","alt":"image"}}]}]}'
|
||||
],
|
||||
[
|
||||
'table with formatting inside',
|
||||
'{"type":"doc","content":[{"type":"table","content":[{"type":"tableRow","content":[{"type":"tableCell","attrs":{"colspan":1,"rowspan":1},"content":[{"type":"paragraph","content":[{"type":"text","marks":[{"type":"bold","attrs":{}}],"text":"1"}]}]},{"type":"tableCell","attrs":{"colspan":1,"rowspan":1},"content":[{"type":"paragraph","content":[{"type":"text","marks":[{"type":"italic","attrs":{}}],"text":"2"}]}]}]},{"type":"tableRow","content":[{"type":"tableCell","attrs":{"colspan":1,"rowspan":1},"content":[{"type":"codeBlock","content":[{"type":"text","text":"3"}]}]},{"type":"tableCell","attrs":{"colspan":1,"rowspan":1},"content":[{"type":"paragraph","content":[{"type":"text","text":"4"}]}]}]}]}]}'
|
||||
const markups: Array<{ name: string, markup: Markup, skipYdocCompare?: boolean }> = [
|
||||
{
|
||||
name: 'text',
|
||||
markup: '{"type":"doc","content":[{"type":"paragraph","content":[{"type":"text","text":"hello world"}]}]}'
|
||||
},
|
||||
{
|
||||
name: 'text with bold mark',
|
||||
markup:
|
||||
'{"type":"doc","content":[{"type":"paragraph","content":[{"type":"text","marks":[{"type":"bold","attrs":{}}],"text":"hello world"}]}]}'
|
||||
},
|
||||
{
|
||||
name: 'separate paragraphs with bold mark',
|
||||
markup:
|
||||
'{"type":"doc","content":[{"type":"paragraph","content":[{"type":"text","marks":[{"type":"bold","attrs":{}}],"text":"hello"}]},{"type":"paragraph","content":[{"type":"text","marks":[{"type":"bold","attrs":{}}],"text":"world"}]}]}'
|
||||
},
|
||||
{
|
||||
name: 'mixed text and text with bold mark',
|
||||
markup:
|
||||
'{"type":"doc","content":[{"type":"paragraph","content":[{"type":"text","text":"hello "},{"type":"text","marks":[{"type":"bold","attrs":{}}],"text":"world"}]}]}'
|
||||
},
|
||||
{
|
||||
name: 'mixed text with italic and text with bold and italic marks',
|
||||
markup:
|
||||
'{"type":"doc","content":[{"type":"paragraph","content":[{"type":"text","marks":[{"type":"italic","attrs":{}}],"text":"hello "},{"type":"text","marks":[{"type":"bold","attrs":{}},{"type":"italic","attrs":{}}],"text":"world"}]}]}'
|
||||
},
|
||||
{
|
||||
name: 'text with link and italic marks',
|
||||
markup:
|
||||
'{"type":"doc","content":[{"type":"paragraph","content":[{"type":"text","text":"hello "},{"type":"text","text":"hello world","marks":[{"type":"link","attrs":{"href":"http://example.com","target":"_blank","rel":"noopener noreferrer","class":"cursor-pointer"}},{"type":"italic","attrs":{}}]}]}]}'
|
||||
},
|
||||
{
|
||||
name: 'image',
|
||||
markup:
|
||||
'{"type":"doc","content":[{"type":"paragraph","content":[{"type":"image","attrs":{"src":"http://example.com/image.jpg","alt":"image"}}]}]}'
|
||||
},
|
||||
{
|
||||
name: 'table with formatting inside',
|
||||
markup:
|
||||
'{"type":"doc","content":[{"type":"table","content":[{"type":"tableRow","content":[{"type":"tableCell","attrs":{"colspan":1,"rowspan":1},"content":[{"type":"paragraph","content":[{"type":"text","marks":[{"type":"bold","attrs":{}}],"text":"1"}]}]},{"type":"tableCell","attrs":{"colspan":1,"rowspan":1},"content":[{"type":"paragraph","content":[{"type":"text","marks":[{"type":"italic","attrs":{}}],"text":"2"}]}]}]},{"type":"tableRow","content":[{"type":"tableCell","attrs":{"colspan":1,"rowspan":1},"content":[{"type":"codeBlock","content":[{"type":"text","text":"3"}]}]},{"type":"tableCell","attrs":{"colspan":1,"rowspan":1},"content":[{"type":"paragraph","content":[{"type":"text","text":"4"}]}]}]}]}]}'
|
||||
},
|
||||
{
|
||||
name: 'non-overlapping marks',
|
||||
markup: `{
|
||||
"type": "doc",
|
||||
"content": [
|
||||
{
|
||||
"type": "paragraph",
|
||||
"content": [
|
||||
{
|
||||
"type": "text",
|
||||
"text": "xy",
|
||||
"marks": [
|
||||
{
|
||||
"type": "inline-comment",
|
||||
"attrs": {
|
||||
"thread": "1"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"text": "z",
|
||||
"marks": [
|
||||
{
|
||||
"type": "inline-comment",
|
||||
"attrs": {
|
||||
"thread": "2"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}`,
|
||||
skipYdocCompare: true
|
||||
},
|
||||
{
|
||||
name: 'overlapping marks',
|
||||
markup: `{
|
||||
"type": "doc",
|
||||
"content": [
|
||||
{
|
||||
"type": "paragraph",
|
||||
"content": [
|
||||
{
|
||||
"type": "text",
|
||||
"text": "x",
|
||||
"marks": [
|
||||
{
|
||||
"type": "inline-comment",
|
||||
"attrs": {
|
||||
"thread": "1"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"text": "y",
|
||||
"marks": [
|
||||
{
|
||||
"type": "inline-comment",
|
||||
"attrs": {
|
||||
"thread": "1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "inline-comment",
|
||||
"attrs": {
|
||||
"thread": "2"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"text": "z",
|
||||
"marks": [
|
||||
{
|
||||
"type": "inline-comment",
|
||||
"attrs": {
|
||||
"thread": "2"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}`,
|
||||
skipYdocCompare: true
|
||||
}
|
||||
]
|
||||
|
||||
describe('markupToYDoc', () => {
|
||||
describe.each(markups)('compare with reference', (name, markup) => {
|
||||
it(name, () => {
|
||||
const expected = referenceMarkupToYDoc(markup, 'test')
|
||||
const actual = markupToYDoc(markup, 'test')
|
||||
expect(actual.toJSON()).toEqual(expected.toJSON())
|
||||
})
|
||||
describe.each(markups)('compare with reference', ({ name, markup, skipYdocCompare }) => {
|
||||
if (skipYdocCompare !== true) {
|
||||
it(name, () => {
|
||||
const expected = referenceMarkupToYDoc(markup, 'test')
|
||||
const actual = markupToYDoc(markup, 'test')
|
||||
expect(actual).toEqualYdoc(expected)
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
describe.each(markups)('converts markup to ydoc and back', (name, markup) => {
|
||||
describe.each(markups)('converts markup to ydoc and back', ({ name, markup }) => {
|
||||
it(name, () => {
|
||||
console.log(markup, name)
|
||||
const ydoc = markupToYDoc(markup, 'test')
|
||||
const actual = referenceYDocToMarkup(ydoc, 'test')
|
||||
const actual = yDocToMarkup(ydoc, 'test')
|
||||
|
||||
expect(actual).toEqualMarkup(markup)
|
||||
})
|
||||
@@ -107,7 +227,7 @@ describe('markupToYDoc', () => {
|
||||
})
|
||||
|
||||
describe('yDocToMarkup', () => {
|
||||
describe.each(markups)('compare with original', (name, markup) => {
|
||||
describe.each(markups)('compare with original', ({ name, markup }) => {
|
||||
it(name, () => {
|
||||
const ydoc = referenceMarkupToYDoc(markup, 'test')
|
||||
const actual = yDocToMarkup(ydoc, 'test')
|
||||
@@ -116,7 +236,7 @@ describe('yDocToMarkup', () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe.each(markups)('compare with reference', (name, markup) => {
|
||||
describe.each(markups)('compare with reference', ({ name, markup }) => {
|
||||
it(name, () => {
|
||||
const ydoc = referenceMarkupToYDoc(markup, 'test')
|
||||
const expected = referenceYDocToMarkup(ydoc, 'test')
|
||||
@@ -126,12 +246,14 @@ describe('yDocToMarkup', () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe.each(markups)('converts ydoc to markup and back', (name, markup) => {
|
||||
it(name, () => {
|
||||
const expected = referenceMarkupToYDoc(markup, 'test')
|
||||
const actual = referenceMarkupToYDoc(yDocToMarkup(expected, 'test'), 'test')
|
||||
describe.each(markups)('converts ydoc to markup and back', ({ name, markup, skipYdocCompare }) => {
|
||||
if (skipYdocCompare !== true) {
|
||||
it(name, () => {
|
||||
const expected = referenceMarkupToYDoc(markup, 'test')
|
||||
const actual = referenceMarkupToYDoc(yDocToMarkup(expected, 'test'), 'test')
|
||||
|
||||
expect(actual.toJSON()).toEqual(expected.toJSON())
|
||||
})
|
||||
expect(actual).toEqualYdoc(expected)
|
||||
})
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
|
||||
import { generateId, Markup } from '@hcengineering/core'
|
||||
import { jsonToMarkup, MarkupMarkType, MarkupNodeType, markupToJSON, type MarkupNode } from '@hcengineering/text'
|
||||
import { hashAttrs, stripHash } from '@hcengineering/text-core'
|
||||
import { Doc as YDoc, XmlElement as YXmlElement, XmlFragment as YXmlFragment, XmlText as YXmlText } from 'yjs'
|
||||
|
||||
/**
|
||||
@@ -49,14 +50,22 @@ function nodeToXmlElement (parent: YXmlFragment, node: MarkupNode): YXmlElement
|
||||
if (node.content !== undefined && node.content.length > 0) {
|
||||
node.content.map((p) => nodeToXmlElement(elem, p))
|
||||
}
|
||||
} else {
|
||||
} else if (elem instanceof YXmlText) {
|
||||
// https://github.com/yjs/y-prosemirror/blob/master/src/plugins/sync-plugin.js#L777
|
||||
const attributes: Record<string, any> = {}
|
||||
if (node.marks !== undefined) {
|
||||
const attrCount = new Map<string, number>()
|
||||
node.marks.forEach((mark) => {
|
||||
attributes[mark.type] = mark.attrs ?? {}
|
||||
attrCount.set(mark.type, (attrCount.get(mark.type) ?? 0) + 1)
|
||||
})
|
||||
|
||||
node.marks.forEach((mark) => {
|
||||
const count = attrCount.get(mark.type) ?? 0
|
||||
const type = count > 1 ? `${mark.type}--${hashAttrs(mark.attrs)}` : mark.type
|
||||
attributes[type] = mark.attrs ?? {}
|
||||
})
|
||||
}
|
||||
|
||||
elem.applyDelta([
|
||||
{
|
||||
insert: node.text ?? '',
|
||||
@@ -119,7 +128,7 @@ function xmlFragmentToNode (fragment: YXmlFragment): MarkupNode[] {
|
||||
// Convert attributes to marks
|
||||
if (op.attributes != null) {
|
||||
textNode.marks = Object.entries(op.attributes).map(([type, attrs]) => ({
|
||||
type: type as MarkupMarkType,
|
||||
type: stripHash(type) as MarkupMarkType,
|
||||
attrs: attrs as Record<string, any>
|
||||
}))
|
||||
}
|
||||
|
||||
@@ -561,6 +561,7 @@ input.search {
|
||||
.mx-2 { margin: 0 .5rem; }
|
||||
.mx-3 { margin: 0 .75rem; }
|
||||
.mx-4 { margin: 0 1rem; }
|
||||
.mx-8 { margin: 0 2rem; }
|
||||
.mx-10 { margin: 0 2.5rem; }
|
||||
.mx-auto { margin: 0 auto; }
|
||||
.my-1 { margin: .25rem 0; }
|
||||
|
||||
@@ -303,6 +303,7 @@
|
||||
.text-editor-image-container {
|
||||
img {
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -57,6 +57,7 @@
|
||||
"Czech": "Čeština",
|
||||
"German": "Deutsch",
|
||||
"Japanese": "日本語",
|
||||
"Turkish": "Türkçe",
|
||||
"MinutesBefore": "{minutes, plural, =1 {1 minuta před} other {# minut před}}",
|
||||
"HoursBefore": "{hours, plural, =1 {1 hodina před} other {# hodin před}}",
|
||||
"DaysBefore": "{days, plural, =1 {1 den před} other {# dní před}}",
|
||||
|
||||
@@ -57,6 +57,7 @@
|
||||
"Czech": "Tschechisch",
|
||||
"German": "Deutsch",
|
||||
"Japanese": "Japanisch",
|
||||
"Turkish": "Türkçe",
|
||||
"MinutesBefore": "{minutes, plural, =1 {eine Minute vorher} other {# Minuten vorher}}",
|
||||
"HoursBefore": "{hours, plural, =1 {eine Stunde vorher} other {# Stunden vorher}}",
|
||||
"DaysBefore": "{days, plural, =1 {einen Tag vorher} other {# Tage vorher}}",
|
||||
|
||||
@@ -57,6 +57,7 @@
|
||||
"Czech": "Čeština",
|
||||
"German": "Deutsch",
|
||||
"Japanese": "日本語",
|
||||
"Turkish": "Türkçe",
|
||||
"MinutesBefore": "{minutes, plural, =1 {a minute before} other {# minutes before}}",
|
||||
"HoursBefore": "{hours, plural, =1 {an hour before} other {# hours before}}",
|
||||
"DaysBefore": "{days, plural, =1 {a day before} other {# days before}}",
|
||||
|
||||
@@ -57,6 +57,7 @@
|
||||
"Czech": "Čeština",
|
||||
"German": "Deutsch",
|
||||
"Japanese": "日本語",
|
||||
"Turkish": "Türkçe",
|
||||
"MinutesBefore": "{minutes, plural, =1 {un minuto antes} other {# minutos antes}}",
|
||||
"HoursBefore": "{hours, plural, =1 {una hora antes} other {# horas antes}}",
|
||||
"DaysBefore": "{days, plural, =1 {un día antes} other {# días antes}}",
|
||||
|
||||
@@ -57,6 +57,7 @@
|
||||
"Czech": "Čeština",
|
||||
"German": "Deutsch",
|
||||
"Japanese": "日本語",
|
||||
"Turkish": "Türkçe",
|
||||
"MinutesBefore": "{minutes, plural, =1 {une minute avant} other {# minutes avant}}",
|
||||
"HoursBefore": "{hours, plural, =1 {une heure avant} other {# heures avant}}",
|
||||
"DaysBefore": "{days, plural, =1 {un jour avant} other {# jours avant}}",
|
||||
|
||||
@@ -57,6 +57,7 @@
|
||||
"Czech": "Čeština",
|
||||
"German": "Deutsch",
|
||||
"Japanese": "日本語",
|
||||
"Turkish": "Türkçe",
|
||||
"MinutesBefore": "{minutes, plural, =1 {un minuto prima} other {# minuti prima}}",
|
||||
"HoursBefore": "{hours, plural, =1 {un'ora prima} other {# ore prima}}",
|
||||
"DaysBefore": "{days, plural, =1 {un giorno prima} other {# giorni prima}}",
|
||||
|
||||
@@ -57,6 +57,7 @@
|
||||
"Czech": "Čeština",
|
||||
"German": "Deutsch",
|
||||
"Japanese": "日本語",
|
||||
"Turkish": "Türkçe",
|
||||
"MinutesBefore": "{minutes, plural, =1 {1分前} other {#分前}}",
|
||||
"HoursBefore": "{hours, plural, =1 {1時間前} other {#時間前}}",
|
||||
"DaysBefore": "{days, plural, =1 {1日前} other {#日前}}",
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user