mirror of
https://github.com/hcengineering/platform.git
synced 2026-09-11 12:17:44 +02:00
Merge remote-tracking branch 'origin/develop' into staging
Signed-off-by: Andrey Sobolev <haiodo@gmail.com>
This commit is contained in:
+92
-6
@@ -29,6 +29,14 @@ services:
|
||||
ports:
|
||||
- 5432:5432
|
||||
restart: unless-stopped
|
||||
cockroach:
|
||||
image: cockroachdb/cockroach:latest-v24.2
|
||||
ports:
|
||||
- "26257:26257"
|
||||
- "8089:8080"
|
||||
command: start-single-node --insecure
|
||||
volumes:
|
||||
- cockroach_db:/cockroach/cockroach-data"
|
||||
minio:
|
||||
image: 'minio/minio'
|
||||
command: server /data --address ":9000" --console-address ":9001"
|
||||
@@ -78,8 +86,8 @@ services:
|
||||
# - DB_URL=postgresql://postgres:example@postgres:5432
|
||||
- DB_URL=${MONGO_URL}
|
||||
# - DB_NS=account-2
|
||||
- REGION_INFO=|Mongo;pg|Postgres
|
||||
- TRANSACTOR_URL=ws://host.docker.internal:3333,ws://host.docker.internal:3331;;pg
|
||||
- REGION_INFO=|Mongo;pg|Postgres;cockroach|CockroachDB
|
||||
- TRANSACTOR_URL=ws://host.docker.internal:3333,ws://host.docker.internal:3331;;pg,ws://host.docker.internal:3332;;cockroach,
|
||||
- SES_URL=
|
||||
- STORAGE_CONFIG=${STORAGE_CONFIG}
|
||||
- FRONT_URL=http://host.docker.internal:8087
|
||||
@@ -124,7 +132,6 @@ services:
|
||||
# - DB_URL=postgresql://postgres:example@postgres:5432
|
||||
- SES_URL=
|
||||
- STORAGE_CONFIG=${STORAGE_CONFIG}
|
||||
- FRONT_URL=http://host.docker.internal:8087
|
||||
- RESERVED_DB_NAMES=telegram,gmail,github
|
||||
- MODEL_ENABLED=*
|
||||
- ACCOUNTS_URL=http://host.docker.internal:3000
|
||||
@@ -160,6 +167,32 @@ services:
|
||||
- INIT_SCRIPT_URL=https://raw.githubusercontent.com/hcengineering/init/main/script.yaml
|
||||
# - INIT_WORKSPACE=onboarding
|
||||
restart: unless-stopped
|
||||
workspace_cockroach:
|
||||
image: hardcoreeng/workspace
|
||||
extra_hosts:
|
||||
- "host.docker.internal:host-gateway"
|
||||
links:
|
||||
- cockroach
|
||||
- minio
|
||||
- stats
|
||||
volumes:
|
||||
- ./branding.json:/var/cfg/branding.json
|
||||
environment:
|
||||
# - WS_OPERATION=create
|
||||
- SERVER_SECRET=secret
|
||||
- DB_URL=postgresql://root@host.docker.internal:26257/defaultdb?sslmode=disable
|
||||
- STATS_URL=http://host.docker.internal:4900
|
||||
- SES_URL=
|
||||
- REGION=cockroach
|
||||
- STORAGE_CONFIG=${STORAGE_CONFIG}
|
||||
- RESERVED_DB_NAMES=telegram,gmail,github
|
||||
- MODEL_ENABLED=*
|
||||
- ACCOUNTS_URL=http://host.docker.internal:3000
|
||||
- BRANDING_PATH=/var/cfg/branding.json
|
||||
# - PARALLEL=2
|
||||
- INIT_SCRIPT_URL=https://raw.githubusercontent.com/hcengineering/init/main/script.yaml
|
||||
# - INIT_WORKSPACE=onboarding
|
||||
restart: unless-stopped
|
||||
collaborator:
|
||||
image: hardcoreeng/collaborator
|
||||
extra_hosts:
|
||||
@@ -270,15 +303,47 @@ services:
|
||||
environment:
|
||||
# - SERVER_PROVIDER=uweb
|
||||
# - UWS_HTTP_MAX_HEADERS_SIZE="32768"
|
||||
- UV_THREADPOOL_SIZE=10
|
||||
# - UV_THREADPOOL_SIZE=10
|
||||
- SERVER_PORT=3331
|
||||
- SERVER_SECRET=secret
|
||||
- ENABLE_COMPRESSION=true
|
||||
- FULLTEXT_URL=http://host.docker.internal:4701
|
||||
- STATS_URL=http://host.docker.internal:4900
|
||||
- DB_URL=postgresql://postgres:example@postgres:5432
|
||||
- MONGO_URL=${MONGO_URL}
|
||||
- 'MONGO_OPTIONS={"appName": "transactor-pg", "maxPoolSize": 1}'
|
||||
- METRICS_CONSOLE=false
|
||||
- METRICS_FILE=metrics.txt
|
||||
- STORAGE_CONFIG=${STORAGE_CONFIG}
|
||||
- FRONT_URL=http://host.docker.internal:8087
|
||||
# - APM_SERVER_URL=http://apm-server:8200
|
||||
- SES_URL=''
|
||||
- ACCOUNTS_URL=http://host.docker.internal:3000
|
||||
- LAST_NAME_FIRST=true
|
||||
- BRANDING_PATH=/var/cfg/branding.json
|
||||
restart: unless-stopped
|
||||
transactor_cockroach:
|
||||
image: hardcoreeng/transactor
|
||||
extra_hosts:
|
||||
- "host.docker.internal:host-gateway"
|
||||
links:
|
||||
- cockroach
|
||||
- minio
|
||||
- account
|
||||
- stats
|
||||
# - apm-server
|
||||
ports:
|
||||
- 3332:3332
|
||||
volumes:
|
||||
- ./branding.json:/var/cfg/branding.json
|
||||
environment:
|
||||
# - SERVER_PROVIDER=uweb
|
||||
# - UWS_HTTP_MAX_HEADERS_SIZE="32768"
|
||||
# - UV_THREADPOOL_SIZE=10
|
||||
- SERVER_PORT=3332
|
||||
- SERVER_SECRET=secret
|
||||
- ENABLE_COMPRESSION=true
|
||||
- FULLTEXT_URL=http://host.docker.internal:4702
|
||||
- STATS_URL=http://host.docker.internal:4900
|
||||
- DB_URL=postgresql://root@host.docker.internal:26257/defaultdb?sslmode=disable
|
||||
- METRICS_CONSOLE=false
|
||||
- METRICS_FILE=metrics.txt
|
||||
- STORAGE_CONFIG=${STORAGE_CONFIG}
|
||||
@@ -333,6 +398,26 @@ services:
|
||||
- STATS_URL=http://host.docker.internal:4900
|
||||
- REKONI_URL=http://host.docker.internal:4004
|
||||
- ACCOUNTS_URL=http://host.docker.internal:3000
|
||||
fulltext_cockroach:
|
||||
image: hardcoreeng/fulltext
|
||||
extra_hosts:
|
||||
- "host.docker.internal:host-gateway"
|
||||
restart: unless-stopped
|
||||
links:
|
||||
- elastic
|
||||
- postgres
|
||||
ports:
|
||||
- 4702:4702
|
||||
environment:
|
||||
- PORT=4702
|
||||
- SERVER_SECRET=secret
|
||||
- DB_URL=postgresql://root@host.docker.internal:26257/defaultdb?sslmode=disable
|
||||
- FULLTEXT_DB_URL=http://host.docker.internal:9200
|
||||
- ELASTIC_INDEX_NAME=local_storage_index
|
||||
- STORAGE_CONFIG=${STORAGE_CONFIG}
|
||||
- STATS_URL=http://host.docker.internal:4900
|
||||
- REKONI_URL=http://host.docker.internal:4004
|
||||
- ACCOUNTS_URL=http://host.docker.internal:3000
|
||||
print:
|
||||
image: hardcoreeng/print
|
||||
extra_hosts:
|
||||
@@ -420,3 +505,4 @@ volumes:
|
||||
dbpg:
|
||||
files:
|
||||
elastic:
|
||||
cockroach_db:
|
||||
|
||||
+12
-31
@@ -20,7 +20,6 @@ import { loadCollaborativeDoc, saveCollaborativeDoc, yDocToBuffer } from '@hceng
|
||||
import contact from '@hcengineering/contact'
|
||||
import core, {
|
||||
type ArrOf,
|
||||
type AttachedDoc,
|
||||
type BackupClient,
|
||||
type Class,
|
||||
ClassifierKind,
|
||||
@@ -47,7 +46,6 @@ import core, {
|
||||
type StatusCategory,
|
||||
type Tx,
|
||||
type TxCUD,
|
||||
type TxCollectionCUD,
|
||||
type TxCreateDoc,
|
||||
type TxMixin,
|
||||
TxOperations,
|
||||
@@ -158,16 +156,6 @@ export async function cleanWorkspace (
|
||||
await db.collection(DOMAIN_TX).deleteMany({ objectId: (tx as TxRemoveDoc<Doc>).objectId })
|
||||
processed++
|
||||
}
|
||||
if (
|
||||
tx._class === core.class.TxCollectionCUD &&
|
||||
(tx as TxCollectionCUD<Doc, AttachedDoc>).tx._class === core.class.TxRemoveDoc
|
||||
) {
|
||||
// We need to remove all update and create operations for document
|
||||
await db.collection(DOMAIN_TX).deleteMany({
|
||||
'tx.objectId': ((tx as TxCollectionCUD<Doc, AttachedDoc>).tx as TxRemoveDoc<Doc>).objectId
|
||||
})
|
||||
processed++
|
||||
}
|
||||
}
|
||||
if (processed % 1000 === 0) {
|
||||
console.log('processed', processed)
|
||||
@@ -219,18 +207,13 @@ export async function cleanRemovedTransactions (workspaceId: WorkspaceId, transa
|
||||
try {
|
||||
let count = 0
|
||||
while (true) {
|
||||
const removedDocs = await connection.findAll(
|
||||
core.class.TxCollectionCUD,
|
||||
{ 'tx._class': core.class.TxRemoveDoc },
|
||||
{ limit: 1000 }
|
||||
)
|
||||
const removedDocs = await connection.findAll(core.class.TxRemoveDoc, {}, { limit: 1000 })
|
||||
if (removedDocs.length === 0) {
|
||||
break
|
||||
}
|
||||
|
||||
const toRemove = await connection.findAll(core.class.TxCollectionCUD, {
|
||||
'tx._class': { $in: [core.class.TxCreateDoc, core.class.TxRemoveDoc, core.class.TxUpdateDoc] },
|
||||
'tx.objectId': { $in: removedDocs.map((it) => it.tx.objectId) }
|
||||
const toRemove = await connection.findAll(core.class.TxCUD, {
|
||||
objectId: { $in: removedDocs.map((it) => it.objectId) }
|
||||
})
|
||||
await connection.clean(
|
||||
DOMAIN_TX,
|
||||
@@ -368,20 +351,18 @@ export async function fixCommentDoubleIdCreate (workspaceId: WorkspaceId, transa
|
||||
mode: 'backup'
|
||||
})) as unknown as CoreClient & BackupClient
|
||||
try {
|
||||
const commentTxes = await connection.findAll(core.class.TxCollectionCUD, {
|
||||
'tx._class': core.class.TxCreateDoc,
|
||||
'tx.objectClass': chunter.class.ChatMessage
|
||||
const commentTxes = await connection.findAll(core.class.TxCreateDoc, {
|
||||
objectClass: chunter.class.ChatMessage
|
||||
})
|
||||
const commentTxesRemoved = await connection.findAll(core.class.TxCollectionCUD, {
|
||||
'tx._class': core.class.TxRemoveDoc,
|
||||
'tx.objectClass': chunter.class.ChatMessage
|
||||
const commentTxesRemoved = await connection.findAll(core.class.TxRemoveDoc, {
|
||||
objectClass: chunter.class.ChatMessage
|
||||
})
|
||||
const removed = new Map(commentTxesRemoved.map((it) => [it.tx.objectId, it]))
|
||||
const removed = new Map(commentTxesRemoved.map((it) => [it.objectId, it]))
|
||||
// Do not checked removed
|
||||
const objSet = new Set<Ref<Doc>>()
|
||||
const oldValue = new Map<Ref<Doc>, string>()
|
||||
for (const c of commentTxes) {
|
||||
const cid = c.tx.objectId
|
||||
const cid = c.objectId
|
||||
if (removed.has(cid)) {
|
||||
continue
|
||||
}
|
||||
@@ -389,7 +370,7 @@ export async function fixCommentDoubleIdCreate (workspaceId: WorkspaceId, transa
|
||||
objSet.add(cid)
|
||||
if (has) {
|
||||
// We have found duplicate one, let's rename it.
|
||||
const doc = TxProcessor.createDoc2Doc<ChatMessage>(c.tx as unknown as TxCreateDoc<ChatMessage>)
|
||||
const doc = TxProcessor.createDoc2Doc<ChatMessage>(c as unknown as TxCreateDoc<ChatMessage>)
|
||||
if (doc.message !== '' && doc.message.trim() !== '<p></p>') {
|
||||
await connection.clean(DOMAIN_TX, [c._id])
|
||||
if (oldValue.get(cid) === doc.message.trim()) {
|
||||
@@ -398,8 +379,8 @@ export async function fixCommentDoubleIdCreate (workspaceId: WorkspaceId, transa
|
||||
oldValue.set(doc._id, doc.message)
|
||||
console.log('renaming', cid, doc.message)
|
||||
// Remove previous transaction.
|
||||
c.tx.objectId = generateId()
|
||||
doc._id = c.tx.objectId as Ref<ChatMessage>
|
||||
c.objectId = generateId()
|
||||
doc._id = c.objectId as Ref<ChatMessage>
|
||||
await connection.upload(DOMAIN_TX, [c])
|
||||
// Also we need to create snapsot
|
||||
await connection.upload(DOMAIN_ACTIVITY, [doc])
|
||||
|
||||
+1
-11
@@ -215,17 +215,7 @@ async function getMixinWithForeignProperties (
|
||||
{ limit: 1, sort: { modifiedOn: SortingOrder.Descending } }
|
||||
)
|
||||
|
||||
const collectionTx = await connection.findAll(
|
||||
core.class.TxCollectionCUD,
|
||||
{
|
||||
'tx._class': core.class.TxUpdateDoc,
|
||||
'tx.objectId': doc._id,
|
||||
[`tx.operations.${property}`]: { $exists: true }
|
||||
},
|
||||
{ limit: 1, sort: { modifiedOn: SortingOrder.Descending } }
|
||||
)
|
||||
|
||||
const cModifiedOn = Math.max(updateDocTx[0]?.modifiedOn ?? 0, collectionTx[0]?.modifiedOn ?? 0)
|
||||
const cModifiedOn = updateDocTx[0]?.modifiedOn ?? 0
|
||||
const mModifiedOn = mixinTx[0]?.modifiedOn ?? 0
|
||||
|
||||
properties.push({ name: property, cValue, mValue, cModifiedOn, mModifiedOn })
|
||||
|
||||
@@ -201,11 +201,8 @@ export class TDocUpdateMessageViewlet extends TDoc implements DocUpdateMessageVi
|
||||
|
||||
@Model(activity.class.ActivityExtension, core.class.Doc, DOMAIN_MODEL)
|
||||
export class TActivityExtension extends TDoc implements ActivityExtension {
|
||||
@Prop(TypeRef(core.class.Class), core.string.Class)
|
||||
@Index(IndexKind.Indexed)
|
||||
ofClass!: Ref<Class<Doc>>
|
||||
|
||||
components!: Record<ActivityExtensionKind, AnyComponent>
|
||||
ofClass!: Ref<Class<Doc>>
|
||||
components!: Record<ActivityExtensionKind, { component: AnyComponent, props?: Record<string, any> }>
|
||||
}
|
||||
|
||||
@Model(activity.class.ActivityMessagesFilter, core.class.Doc, DOMAIN_MODEL)
|
||||
|
||||
@@ -85,7 +85,7 @@ export function createModel (builder: Builder): void {
|
||||
|
||||
builder.createDoc(activity.class.ActivityExtension, core.space.Model, {
|
||||
ofClass: analyticsCollector.class.OnboardingChannel,
|
||||
components: { input: chunter.component.ChatMessageInput }
|
||||
components: { input: { component: chunter.component.ChatMessageInput } }
|
||||
})
|
||||
|
||||
builder.createDoc<ActivityMessageControl<OnboardingChannel>>(
|
||||
|
||||
@@ -273,27 +273,27 @@ export function createModel (builder: Builder): void {
|
||||
|
||||
builder.createDoc(activity.class.ActivityExtension, core.space.Model, {
|
||||
ofClass: chunter.class.Channel,
|
||||
components: { input: chunter.component.ChatMessageInput }
|
||||
components: { input: { component: chunter.component.ChatMessageInput } }
|
||||
})
|
||||
|
||||
builder.createDoc(activity.class.ActivityExtension, core.space.Model, {
|
||||
ofClass: chunter.class.DirectMessage,
|
||||
components: { input: chunter.component.ChatMessageInput }
|
||||
components: { input: { component: chunter.component.ChatMessageInput } }
|
||||
})
|
||||
|
||||
builder.createDoc(activity.class.ActivityExtension, core.space.Model, {
|
||||
ofClass: activity.class.DocUpdateMessage,
|
||||
components: { input: chunter.component.ChatMessageInput }
|
||||
components: { input: { component: chunter.component.ChatMessageInput } }
|
||||
})
|
||||
|
||||
builder.createDoc(activity.class.ActivityExtension, core.space.Model, {
|
||||
ofClass: chunter.class.ChatMessage,
|
||||
components: { input: chunter.component.ChatMessageInput }
|
||||
components: { input: { component: chunter.component.ChatMessageInput } }
|
||||
})
|
||||
|
||||
builder.createDoc(activity.class.ActivityExtension, core.space.Model, {
|
||||
ofClass: activity.class.ActivityReference,
|
||||
components: { input: chunter.component.ChatMessageInput }
|
||||
components: { input: { component: chunter.component.ChatMessageInput } }
|
||||
})
|
||||
|
||||
// Indexing
|
||||
|
||||
@@ -263,22 +263,22 @@ export function createModel (builder: Builder): void {
|
||||
|
||||
builder.createDoc(activity.class.ActivityExtension, core.space.Model, {
|
||||
ofClass: contact.class.Contact,
|
||||
components: { input: chunter.component.ChatMessageInput }
|
||||
components: { input: { component: chunter.component.ChatMessageInput } }
|
||||
})
|
||||
|
||||
builder.createDoc(activity.class.ActivityExtension, core.space.Model, {
|
||||
ofClass: contact.class.Person,
|
||||
components: { input: chunter.component.ChatMessageInput }
|
||||
components: { input: { component: chunter.component.ChatMessageInput } }
|
||||
})
|
||||
|
||||
builder.createDoc(activity.class.ActivityExtension, core.space.Model, {
|
||||
ofClass: contact.class.Organization,
|
||||
components: { input: chunter.component.ChatMessageInput }
|
||||
components: { input: { component: chunter.component.ChatMessageInput } }
|
||||
})
|
||||
|
||||
builder.createDoc(activity.class.ActivityExtension, core.space.Model, {
|
||||
ofClass: contact.class.Member,
|
||||
components: { input: chunter.component.ChatMessageInput }
|
||||
components: { input: { component: chunter.component.ChatMessageInput } }
|
||||
})
|
||||
|
||||
builder.mixin(contact.mixin.Employee, core.class.Class, view.mixin.ObjectFactory, {
|
||||
|
||||
@@ -514,7 +514,7 @@ export function createModel (builder: Builder): void {
|
||||
|
||||
builder.createDoc(activity.class.ActivityExtension, core.space.Model, {
|
||||
ofClass: documents.class.DocumentCategory,
|
||||
components: { input: chunter.component.ChatMessageInput }
|
||||
components: { input: { component: chunter.component.ChatMessageInput } }
|
||||
})
|
||||
|
||||
builder.mixin(documents.class.DocumentCategory, core.class.Class, view.mixin.ObjectPresenter, {
|
||||
@@ -768,7 +768,7 @@ export function defineNotifications (builder: Builder): void {
|
||||
|
||||
builder.createDoc(activity.class.ActivityExtension, core.space.Model, {
|
||||
ofClass: documents.class.DocumentComment,
|
||||
components: { input: chunter.component.ChatMessageInput }
|
||||
components: { input: { component: chunter.component.ChatMessageInput } }
|
||||
})
|
||||
|
||||
builder.mixin(documents.class.ControlledDocument, core.class.Class, notification.mixin.ClassCollaborators, {
|
||||
|
||||
@@ -24,12 +24,7 @@ import {
|
||||
DOMAIN_STATUS,
|
||||
DOMAIN_TRANSIENT,
|
||||
DOMAIN_TX,
|
||||
systemAccountEmail,
|
||||
type AttachedDoc,
|
||||
type Class,
|
||||
type Doc,
|
||||
type IndexingConfiguration,
|
||||
type TxCollectionCUD
|
||||
systemAccountEmail
|
||||
} from '@hcengineering/core'
|
||||
import { type Builder } from '@hcengineering/model'
|
||||
import { TBenchmarkDoc } from './benchmark'
|
||||
@@ -91,17 +86,7 @@ import {
|
||||
import { defineSpaceType } from './spaceType'
|
||||
import { TDomainStatusPlaceholder, TStatus, TStatusCategory } from './status'
|
||||
import { TUserStatus } from './transient'
|
||||
import {
|
||||
TTx,
|
||||
TTxApplyIf,
|
||||
TTxCollectionCUD,
|
||||
TTxCreateDoc,
|
||||
TTxCUD,
|
||||
TTxMixin,
|
||||
TTxRemoveDoc,
|
||||
TTxUpdateDoc,
|
||||
TTxWorkspaceEvent
|
||||
} from './tx'
|
||||
import { TTx, TTxApplyIf, TTxCreateDoc, TTxCUD, TTxMixin, TTxRemoveDoc, TTxUpdateDoc, TTxWorkspaceEvent } from './tx'
|
||||
|
||||
export { coreId, DOMAIN_SPACE } from '@hcengineering/core'
|
||||
export * from './core'
|
||||
@@ -122,7 +107,6 @@ export function createModel (builder: Builder): void {
|
||||
TTxCUD,
|
||||
TTxCreateDoc,
|
||||
TAttachedDoc,
|
||||
TTxCollectionCUD,
|
||||
TTxMixin,
|
||||
TTxUpdateDoc,
|
||||
TTxRemoveDoc,
|
||||
@@ -188,14 +172,6 @@ export function createModel (builder: Builder): void {
|
||||
core.account.System
|
||||
)
|
||||
|
||||
builder.mixin<Class<TxCollectionCUD<Doc, AttachedDoc>>, IndexingConfiguration<TxCollectionCUD<Doc, AttachedDoc>>>(
|
||||
core.class.TxCollectionCUD,
|
||||
core.class.Class,
|
||||
core.mixin.IndexConfiguration,
|
||||
{
|
||||
indexes: ['tx.objectId', 'tx.operations.attachedTo']
|
||||
}
|
||||
)
|
||||
builder.createDoc(core.class.DomainIndexConfiguration, core.space.Model, {
|
||||
domain: DOMAIN_TX,
|
||||
disabled: [
|
||||
|
||||
@@ -15,22 +15,26 @@
|
||||
|
||||
import { saveCollaborativeDoc } from '@hcengineering/collaboration'
|
||||
import core, {
|
||||
collaborativeDocParse,
|
||||
coreId,
|
||||
DOMAIN_MODEL_TX,
|
||||
DOMAIN_SPACE,
|
||||
DOMAIN_STATUS,
|
||||
DOMAIN_TX,
|
||||
MeasureMetricsContext,
|
||||
RateLimiter,
|
||||
collaborativeDocParse,
|
||||
coreId,
|
||||
generateId,
|
||||
makeCollaborativeDoc,
|
||||
MeasureMetricsContext,
|
||||
RateLimiter,
|
||||
type AnyAttribute,
|
||||
type Class,
|
||||
type Doc,
|
||||
type Domain,
|
||||
type MeasureContext,
|
||||
type Ref,
|
||||
type Space,
|
||||
type Status,
|
||||
type TxCreateDoc
|
||||
type TxCreateDoc,
|
||||
type TxCUD
|
||||
} from '@hcengineering/core'
|
||||
import {
|
||||
createDefaultSpace,
|
||||
@@ -285,6 +289,67 @@ export const coreOperation: MigrateOperation = {
|
||||
await client.update(DOMAIN_TX, { '%hash%': { $exists: true } }, { $set: { '%hash%': null } })
|
||||
await client.update(DOMAIN_SPACE, { '%hash%': { $exists: true } }, { $set: { '%hash%': null } })
|
||||
}
|
||||
},
|
||||
{
|
||||
state: 'remove-github-patches',
|
||||
func: async (client) => {
|
||||
await client.update(
|
||||
DOMAIN_TX,
|
||||
{
|
||||
objectClass: 'tracker:class:Issue',
|
||||
collection: 'pullRequests',
|
||||
'tx.attributes.patch': { $exists: true }
|
||||
},
|
||||
{ $unset: { 'tx.attributes.patch': 1 } }
|
||||
)
|
||||
}
|
||||
},
|
||||
{
|
||||
state: 'remove-collection-txes',
|
||||
func: async (client) => {
|
||||
let processed = 0
|
||||
const iterator = await client.traverse<TxCUD<Doc>>(DOMAIN_TX, {
|
||||
_class: 'core:class:TxCollectionCUD' as Ref<Class<Doc>>
|
||||
})
|
||||
while (true) {
|
||||
const txes = await iterator.next(1000)
|
||||
if (txes === null || txes.length === 0) break
|
||||
processed += txes.length
|
||||
try {
|
||||
await client.deleteMany(DOMAIN_TX, {
|
||||
_id: { $in: txes.map((it) => it._id) }
|
||||
})
|
||||
await client.create(
|
||||
DOMAIN_TX,
|
||||
txes.map((tx) => {
|
||||
const { collection, objectId, objectClass } = tx
|
||||
return {
|
||||
collection,
|
||||
attachedTo: objectId,
|
||||
attachedToClass: objectClass,
|
||||
...(tx as any).tx
|
||||
}
|
||||
})
|
||||
)
|
||||
} catch (err: any) {
|
||||
console.error(err)
|
||||
}
|
||||
console.log('processed', processed)
|
||||
}
|
||||
await iterator.close()
|
||||
}
|
||||
},
|
||||
{
|
||||
state: 'move-model-txes',
|
||||
func: async (client) => {
|
||||
await client.move(
|
||||
DOMAIN_TX,
|
||||
{
|
||||
objectSpace: core.space.Model
|
||||
},
|
||||
DOMAIN_MODEL_TX
|
||||
)
|
||||
}
|
||||
}
|
||||
])
|
||||
},
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
//
|
||||
|
||||
import {
|
||||
type AttachedDoc,
|
||||
type Class,
|
||||
type Data,
|
||||
type Doc,
|
||||
@@ -28,7 +27,6 @@ import {
|
||||
type Space,
|
||||
type Tx,
|
||||
type TxApplyIf,
|
||||
type TxCollectionCUD,
|
||||
type TxCreateDoc,
|
||||
type TxCUD,
|
||||
type TxMixin,
|
||||
@@ -72,12 +70,6 @@ export class TTxCreateDoc<T extends Doc> extends TTxCUD<T> implements TxCreateDo
|
||||
attributes!: Data<T>
|
||||
}
|
||||
|
||||
@Model(core.class.TxCollectionCUD, core.class.TxCUD)
|
||||
export class TTxCollectionCUD<T extends Doc, P extends AttachedDoc> extends TTxCUD<T> implements TxCollectionCUD<T, P> {
|
||||
collection!: string
|
||||
tx!: TxCUD<P>
|
||||
}
|
||||
|
||||
@Model(core.class.TxMixin, core.class.TxCUD)
|
||||
export class TTxMixin<D extends Doc, M extends D> extends TTxCUD<D> implements TxMixin<D, M> {
|
||||
mixin!: Ref<Mixin<M>>
|
||||
|
||||
@@ -492,7 +492,7 @@ function defineDocument (builder: Builder): void {
|
||||
|
||||
builder.createDoc(activity.class.ActivityExtension, core.space.Model, {
|
||||
ofClass: document.class.Document,
|
||||
components: { input: chunter.component.ChatMessageInput }
|
||||
components: { input: { component: chunter.component.ChatMessageInput } }
|
||||
})
|
||||
|
||||
// Search
|
||||
|
||||
@@ -13,11 +13,11 @@
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import { type CollaborativeDoc, DOMAIN_TX, MeasureMetricsContext, SortingOrder } from '@hcengineering/core'
|
||||
import { type DocumentSnapshot, type Document, type Teamspace } from '@hcengineering/document'
|
||||
import { DOMAIN_MODEL_TX, MeasureMetricsContext, SortingOrder, type CollaborativeDoc } from '@hcengineering/core'
|
||||
import { type Document, type DocumentSnapshot, type Teamspace } from '@hcengineering/document'
|
||||
import {
|
||||
tryMigrate,
|
||||
migrateSpaceRanks,
|
||||
tryMigrate,
|
||||
type MigrateOperation,
|
||||
type MigrateUpdate,
|
||||
type MigrationClient,
|
||||
@@ -75,7 +75,7 @@ async function migrateTeamspacesMixins (client: MigrationClient): Promise<void>
|
||||
const newSpaceTypeMixin = document.mixin.DefaultTeamspaceTypeData
|
||||
|
||||
await client.update(
|
||||
DOMAIN_TX,
|
||||
DOMAIN_MODEL_TX,
|
||||
{
|
||||
objectClass: core.class.Attribute,
|
||||
'attributes.attributeOf': oldSpaceTypeMixin
|
||||
|
||||
@@ -595,7 +595,7 @@ function defineFile (builder: Builder): void {
|
||||
|
||||
builder.createDoc(activity.class.ActivityExtension, core.space.Model, {
|
||||
ofClass: drive.class.File,
|
||||
components: { input: chunter.component.ChatMessageInput }
|
||||
components: { input: { component: chunter.component.ChatMessageInput } }
|
||||
})
|
||||
|
||||
// Search
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import {
|
||||
AccountRole,
|
||||
DOMAIN_TX,
|
||||
DOMAIN_MODEL_TX,
|
||||
type Account,
|
||||
type Ref,
|
||||
type Space,
|
||||
type Account,
|
||||
type TxCreateDoc,
|
||||
type TxUpdateDoc
|
||||
} from '@hcengineering/core'
|
||||
@@ -30,13 +30,13 @@ export const guestOperation: MigrateOperation = {
|
||||
1: AccountRole.Maintainer,
|
||||
2: AccountRole.Owner
|
||||
}
|
||||
const createTxes = await client.find<TxCreateDoc<Account>>(DOMAIN_TX, {
|
||||
const createTxes = await client.find<TxCreateDoc<Account>>(DOMAIN_MODEL_TX, {
|
||||
_class: core.class.TxCreateDoc,
|
||||
'attributes.role': { $in: [0, 1, 2] }
|
||||
})
|
||||
for (const tx of createTxes) {
|
||||
await client.update(
|
||||
DOMAIN_TX,
|
||||
DOMAIN_MODEL_TX,
|
||||
{
|
||||
_id: tx._id
|
||||
},
|
||||
@@ -47,13 +47,13 @@ export const guestOperation: MigrateOperation = {
|
||||
}
|
||||
)
|
||||
}
|
||||
const updateTxes = await client.find<TxUpdateDoc<Account>>(DOMAIN_TX, {
|
||||
const updateTxes = await client.find<TxUpdateDoc<Account>>(DOMAIN_MODEL_TX, {
|
||||
_class: core.class.TxUpdateDoc,
|
||||
'operations.role': { $in: [0, 1, 2] }
|
||||
})
|
||||
for (const tx of updateTxes) {
|
||||
await client.update(
|
||||
DOMAIN_TX,
|
||||
DOMAIN_MODEL_TX,
|
||||
{
|
||||
_id: tx._id
|
||||
},
|
||||
|
||||
@@ -85,17 +85,17 @@ export function createModel (builder: Builder): void {
|
||||
|
||||
builder.createDoc(activity.class.ActivityExtension, core.space.Model, {
|
||||
ofClass: inventory.class.Product,
|
||||
components: { input: chunter.component.ChatMessageInput }
|
||||
components: { input: { component: chunter.component.ChatMessageInput } }
|
||||
})
|
||||
|
||||
builder.createDoc(activity.class.ActivityExtension, core.space.Model, {
|
||||
ofClass: inventory.class.Category,
|
||||
components: { input: chunter.component.ChatMessageInput }
|
||||
components: { input: { component: chunter.component.ChatMessageInput } }
|
||||
})
|
||||
|
||||
builder.createDoc(activity.class.ActivityExtension, core.space.Model, {
|
||||
ofClass: inventory.class.Variant,
|
||||
components: { input: chunter.component.ChatMessageInput }
|
||||
components: { input: { component: chunter.component.ChatMessageInput } }
|
||||
})
|
||||
|
||||
builder.mixin(inventory.class.Category, core.class.Class, view.mixin.ObjectPresenter, {
|
||||
|
||||
@@ -51,12 +51,12 @@ export function createModel (builder: Builder): void {
|
||||
|
||||
builder.createDoc(activity.class.ActivityExtension, core.space.Model, {
|
||||
ofClass: lead.class.Lead,
|
||||
components: { input: chunter.component.ChatMessageInput }
|
||||
components: { input: { component: chunter.component.ChatMessageInput } }
|
||||
})
|
||||
|
||||
builder.createDoc(activity.class.ActivityExtension, core.space.Model, {
|
||||
ofClass: lead.class.Funnel,
|
||||
components: { input: chunter.component.ChatMessageInput }
|
||||
components: { input: { component: chunter.component.ChatMessageInput } }
|
||||
})
|
||||
|
||||
builder.mixin(lead.class.Funnel, core.class.Class, workbench.mixin.SpaceView, {
|
||||
|
||||
@@ -13,7 +13,14 @@
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import { AccountRole, DOMAIN_TX, makeCollaborativeDoc, TxOperations, type Ref, type Status } from '@hcengineering/core'
|
||||
import {
|
||||
AccountRole,
|
||||
DOMAIN_MODEL_TX,
|
||||
makeCollaborativeDoc,
|
||||
TxOperations,
|
||||
type Ref,
|
||||
type Status
|
||||
} from '@hcengineering/core'
|
||||
import { leadId, type Lead } from '@hcengineering/lead'
|
||||
import {
|
||||
tryMigrate,
|
||||
@@ -26,7 +33,7 @@ import {
|
||||
import core, { DOMAIN_SPACE } from '@hcengineering/model-core'
|
||||
|
||||
import contact, { DOMAIN_CONTACT } from '@hcengineering/model-contact'
|
||||
import task, { DOMAIN_TASK, createSequence, migrateDefaultStatusesBase } from '@hcengineering/model-task'
|
||||
import task, { createSequence, DOMAIN_TASK, migrateDefaultStatusesBase } from '@hcengineering/model-task'
|
||||
|
||||
import lead from './plugin'
|
||||
import { defaultLeadStatuses } from './spaceType'
|
||||
@@ -110,7 +117,7 @@ async function migrateDefaultTypeMixins (client: MigrationClient): Promise<void>
|
||||
const newTaskTypeMixin = lead.mixin.LeadTypeData
|
||||
|
||||
await client.update(
|
||||
DOMAIN_TX,
|
||||
DOMAIN_MODEL_TX,
|
||||
{
|
||||
objectClass: core.class.Attribute,
|
||||
'attributes.attributeOf': oldSpaceTypeMixin
|
||||
|
||||
@@ -28,8 +28,8 @@
|
||||
"typescript": "^5.3.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"@hcengineering/attachment": "^0.6.14",
|
||||
"@hcengineering/activity": "^0.6.0",
|
||||
"@hcengineering/attachment": "^0.6.14",
|
||||
"@hcengineering/chunter": "^0.6.20",
|
||||
"@hcengineering/contact": "^0.6.24",
|
||||
"@hcengineering/core": "^0.6.32",
|
||||
@@ -46,6 +46,7 @@
|
||||
"@hcengineering/notification": "^0.6.23",
|
||||
"@hcengineering/platform": "^0.6.11",
|
||||
"@hcengineering/setting": "^0.6.17",
|
||||
"@hcengineering/time": "^0.6.0",
|
||||
"@hcengineering/ui": "^0.6.15",
|
||||
"@hcengineering/view": "^0.6.13",
|
||||
"@hcengineering/workbench": "^0.6.16"
|
||||
|
||||
+73
-11
@@ -20,7 +20,10 @@ import {
|
||||
DOMAIN_TRANSIENT,
|
||||
IndexKind,
|
||||
type Ref,
|
||||
type CollaborativeDoc
|
||||
type CollaborativeDoc,
|
||||
type Doc,
|
||||
type Timestamp,
|
||||
type CollectionSize
|
||||
} from '@hcengineering/core'
|
||||
import {
|
||||
type DevicesPreference,
|
||||
@@ -37,7 +40,8 @@ import {
|
||||
type RoomInfo,
|
||||
type RoomType,
|
||||
type RoomLanguage,
|
||||
type MeetingMinutes
|
||||
type MeetingMinutes,
|
||||
type MeetingStatus
|
||||
} from '@hcengineering/love'
|
||||
import {
|
||||
type Builder,
|
||||
@@ -52,7 +56,9 @@ import {
|
||||
TypeCollaborativeDoc,
|
||||
TypeRef,
|
||||
TypeString,
|
||||
UX
|
||||
TypeTimestamp,
|
||||
UX,
|
||||
TypeAny
|
||||
} from '@hcengineering/model'
|
||||
import calendar, { TEvent } from '@hcengineering/model-calendar'
|
||||
import core, { TAttachedDoc, TDoc } from '@hcengineering/model-core'
|
||||
@@ -66,6 +72,7 @@ import workbench, { WidgetType } from '@hcengineering/workbench'
|
||||
import activity from '@hcengineering/activity'
|
||||
import chunter from '@hcengineering/chunter'
|
||||
import attachment from '@hcengineering/attachment'
|
||||
import time, { type ToDo, type Todoable } from '@hcengineering/time'
|
||||
|
||||
import love from './plugin'
|
||||
|
||||
@@ -107,6 +114,9 @@ export class TRoom extends TDoc implements Room {
|
||||
|
||||
@Prop(PropCollection(love.class.MeetingMinutes), love.string.MeetingMinutes)
|
||||
meetings?: number
|
||||
|
||||
@Prop(PropCollection(chunter.class.ChatMessage), activity.string.Messages)
|
||||
messages?: number
|
||||
}
|
||||
|
||||
@Model(love.class.Office, love.class.Room)
|
||||
@@ -184,8 +194,13 @@ export class TMeeting extends TEvent implements Meeting {
|
||||
}
|
||||
|
||||
@Model(love.class.MeetingMinutes, core.class.Doc, DOMAIN_MEETING_MINUTES)
|
||||
@UX(love.string.MeetingMinutes, love.icon.Cam)
|
||||
export class TMeetingMinutes extends TAttachedDoc implements MeetingMinutes {
|
||||
@UX(love.string.MeetingMinutes, love.icon.Cam, undefined, 'createdOn', undefined, love.string.MeetingsMinutes)
|
||||
export class TMeetingMinutes extends TAttachedDoc implements MeetingMinutes, Todoable {
|
||||
@Prop(TypeRef(core.class.Doc), love.string.Room, { editor: love.component.MeetingMinutesDocEditor })
|
||||
@Index(IndexKind.Indexed)
|
||||
@ReadOnly()
|
||||
declare attachedTo: Ref<Doc>
|
||||
|
||||
@Hidden()
|
||||
sid!: string
|
||||
|
||||
@@ -197,6 +212,12 @@ export class TMeetingMinutes extends TAttachedDoc implements MeetingMinutes {
|
||||
@Index(IndexKind.FullText)
|
||||
description!: CollaborativeDoc
|
||||
|
||||
@Prop(TypeAny(love.component.MeetingMinutesStatusPresenter, love.string.Status), love.string.Status, {
|
||||
editor: love.component.MeetingMinutesStatusPresenter
|
||||
})
|
||||
@ReadOnly()
|
||||
status!: MeetingStatus
|
||||
|
||||
@Prop(Collection(attachment.class.Attachment), attachment.string.Attachments, { shortLabel: attachment.string.Files })
|
||||
attachments?: number
|
||||
|
||||
@@ -205,6 +226,18 @@ export class TMeetingMinutes extends TAttachedDoc implements MeetingMinutes {
|
||||
|
||||
@Prop(PropCollection(chunter.class.ChatMessage), activity.string.Messages)
|
||||
messages?: number
|
||||
|
||||
@Prop(TypeTimestamp(), love.string.MeetingStart, { editor: view.component.TimestampPresenter })
|
||||
@ReadOnly()
|
||||
@Index(IndexKind.IndexedDsc)
|
||||
declare createdOn: Timestamp
|
||||
|
||||
@Prop(TypeTimestamp(), love.string.MeetingEnd)
|
||||
@ReadOnly()
|
||||
meetingEnd?: Timestamp
|
||||
|
||||
@Prop(Collection(time.class.ToDo), getEmbeddedLabel('Action Items'))
|
||||
todos?: CollectionSize<ToDo>
|
||||
}
|
||||
|
||||
export default love
|
||||
@@ -418,17 +451,17 @@ export function createModel (builder: Builder): void {
|
||||
|
||||
builder.createDoc(activity.class.ActivityExtension, core.space.Model, {
|
||||
ofClass: love.class.Room,
|
||||
components: { input: chunter.component.ChatMessageInput }
|
||||
components: { input: { component: chunter.component.ChatMessageInput, props: { collection: 'messages' } } }
|
||||
})
|
||||
|
||||
builder.createDoc(activity.class.ActivityExtension, core.space.Model, {
|
||||
ofClass: love.class.Office,
|
||||
components: { input: chunter.component.ChatMessageInput }
|
||||
components: { input: { component: chunter.component.ChatMessageInput, props: { collection: 'messages' } } }
|
||||
})
|
||||
|
||||
builder.createDoc(activity.class.ActivityExtension, core.space.Model, {
|
||||
ofClass: love.class.MeetingMinutes,
|
||||
components: { input: chunter.component.ChatMessageInput }
|
||||
components: { input: { component: chunter.component.ChatMessageInput, props: { collection: 'messages' } } }
|
||||
})
|
||||
|
||||
builder.mixin(love.class.MeetingMinutes, core.class.Class, activity.mixin.ActivityDoc, {})
|
||||
@@ -439,6 +472,10 @@ export function createModel (builder: Builder): void {
|
||||
presenter: love.component.MeetingMinutesPresenter
|
||||
})
|
||||
|
||||
builder.mixin(love.class.Room, core.class.Class, view.mixin.ObjectPresenter, {
|
||||
presenter: love.component.RoomPresenter
|
||||
})
|
||||
|
||||
builder.mixin(love.class.MeetingMinutes, core.class.Class, view.mixin.CollectionEditor, {
|
||||
editor: love.component.MeetingMinutesSection
|
||||
})
|
||||
@@ -467,10 +504,11 @@ export function createModel (builder: Builder): void {
|
||||
descriptor: view.viewlet.Table,
|
||||
config: [
|
||||
'',
|
||||
{ key: 'status', presenter: love.component.MeetingMinutesStatusPresenter, label: love.string.Status },
|
||||
'createdOn',
|
||||
'meetingEnd',
|
||||
{ key: 'messages', displayProps: { key: 'messages', suffix: true } },
|
||||
{ key: 'transcription', displayProps: { key: 'transcription', suffix: true } },
|
||||
'modifiedOn',
|
||||
'modifiedBy'
|
||||
{ key: 'transcription', displayProps: { key: 'transcription', suffix: true } }
|
||||
],
|
||||
configOptions: {
|
||||
hiddenKeys: ['description'],
|
||||
@@ -481,6 +519,26 @@ export function createModel (builder: Builder): void {
|
||||
love.viewlet.TableMeetingMinutes
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
view.class.Viewlet,
|
||||
core.space.Model,
|
||||
{
|
||||
attachTo: love.class.MeetingMinutes,
|
||||
descriptor: view.viewlet.Table,
|
||||
config: [
|
||||
'',
|
||||
{ key: 'status', presenter: love.component.MeetingMinutesStatusPresenter, label: love.string.Status },
|
||||
'createdOn',
|
||||
'meetingEnd'
|
||||
],
|
||||
configOptions: {
|
||||
sortable: true
|
||||
},
|
||||
variant: 'embedded'
|
||||
},
|
||||
love.viewlet.TableMeetingMinutesEmbedded
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
view.class.ViewletDescriptor,
|
||||
core.space.Model,
|
||||
@@ -574,4 +632,8 @@ export function createModel (builder: Builder): void {
|
||||
indexes: [],
|
||||
searchDisabled: true
|
||||
})
|
||||
|
||||
builder.mixin(love.class.MeetingMinutes, core.class.Class, view.mixin.ObjectPanelFooter, {
|
||||
editor: love.component.PanelControlBar
|
||||
})
|
||||
}
|
||||
|
||||
@@ -16,7 +16,16 @@
|
||||
import contact from '@hcengineering/contact'
|
||||
import { type Space, TxOperations, type Ref, makeCollaborativeDoc } from '@hcengineering/core'
|
||||
import drive from '@hcengineering/drive'
|
||||
import { RoomAccess, RoomType, createDefaultRooms, isOffice, loveId, type Floor, type Room } from '@hcengineering/love'
|
||||
import {
|
||||
MeetingStatus,
|
||||
RoomAccess,
|
||||
RoomType,
|
||||
createDefaultRooms,
|
||||
isOffice,
|
||||
loveId,
|
||||
type Floor,
|
||||
type Room
|
||||
} from '@hcengineering/love'
|
||||
import {
|
||||
createDefaultSpace,
|
||||
migrateSpace,
|
||||
@@ -142,6 +151,16 @@ export const loveOperation: MigrateOperation = {
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
state: 'default-meeting-minutes-status',
|
||||
func: async (client) => {
|
||||
await client.update(
|
||||
DOMAIN_MEETING_MINUTES,
|
||||
{ status: { $exists: false } },
|
||||
{ status: MeetingStatus.Finished }
|
||||
)
|
||||
}
|
||||
}
|
||||
])
|
||||
},
|
||||
|
||||
@@ -13,6 +13,8 @@
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import chunter from '@hcengineering/chunter'
|
||||
import contact, { type PersonSpace } from '@hcengineering/contact'
|
||||
import core, { DOMAIN_TX, type Class, type Doc, type DocumentQuery, type Ref, type Space } from '@hcengineering/core'
|
||||
import {
|
||||
migrateSpace,
|
||||
@@ -28,11 +30,9 @@ import notification, {
|
||||
type InboxNotification
|
||||
} from '@hcengineering/notification'
|
||||
import { DOMAIN_PREFERENCE } from '@hcengineering/preference'
|
||||
import contact, { type PersonSpace } from '@hcengineering/contact'
|
||||
import chunter from '@hcengineering/chunter'
|
||||
|
||||
import { DOMAIN_DOC_NOTIFY, DOMAIN_NOTIFICATION, DOMAIN_USER_NOTIFY } from './index'
|
||||
import { DOMAIN_SPACE } from '@hcengineering/model-core'
|
||||
import { DOMAIN_DOC_NOTIFY, DOMAIN_NOTIFICATION, DOMAIN_USER_NOTIFY } from './index'
|
||||
|
||||
export async function removeNotifications (
|
||||
client: MigrationClient,
|
||||
@@ -429,6 +429,12 @@ export const notificationOperation: MigrateOperation = {
|
||||
objectClass: notification.class.BrowserNotification
|
||||
})
|
||||
}
|
||||
},
|
||||
{
|
||||
state: 'migrate-dnc-space',
|
||||
func: async (client) => {
|
||||
await client.update(DOMAIN_DOC_NOTIFY, { space: core.space.Space }, { space: core.space.Workspace })
|
||||
}
|
||||
}
|
||||
])
|
||||
},
|
||||
|
||||
@@ -159,7 +159,7 @@ function defineProduct (builder: Builder): void {
|
||||
|
||||
builder.createDoc(activity.class.ActivityExtension, core.space.Model, {
|
||||
ofClass: products.class.Product,
|
||||
components: { input: chunter.component.ChatMessageInput }
|
||||
components: { input: { component: chunter.component.ChatMessageInput } }
|
||||
})
|
||||
|
||||
builder.mixin(products.class.Product, core.class.Class, view.mixin.ObjectEditor, {
|
||||
@@ -299,7 +299,7 @@ function defineProductVersion (builder: Builder): void {
|
||||
|
||||
builder.createDoc(activity.class.ActivityExtension, core.space.Model, {
|
||||
ofClass: products.class.ProductVersion,
|
||||
components: { input: chunter.component.ChatMessageInput }
|
||||
components: { input: { component: chunter.component.ChatMessageInput } }
|
||||
})
|
||||
|
||||
builder.mixin(products.class.ProductVersion, core.class.Class, view.mixin.ObjectEditor, {
|
||||
|
||||
@@ -56,17 +56,17 @@ export function createModel (builder: Builder): void {
|
||||
|
||||
builder.createDoc(activity.class.ActivityExtension, core.space.Model, {
|
||||
ofClass: recruit.class.Vacancy,
|
||||
components: { input: chunter.component.ChatMessageInput }
|
||||
components: { input: { component: chunter.component.ChatMessageInput } }
|
||||
})
|
||||
|
||||
builder.createDoc(activity.class.ActivityExtension, core.space.Model, {
|
||||
ofClass: recruit.class.Applicant,
|
||||
components: { input: chunter.component.ChatMessageInput }
|
||||
components: { input: { component: chunter.component.ChatMessageInput } }
|
||||
})
|
||||
|
||||
builder.createDoc(activity.class.ActivityExtension, core.space.Model, {
|
||||
ofClass: recruit.class.Review,
|
||||
components: { input: chunter.component.ChatMessageInput }
|
||||
components: { input: { component: chunter.component.ChatMessageInput } }
|
||||
})
|
||||
|
||||
builder.mixin(recruit.class.Vacancy, core.class.Class, workbench.mixin.SpaceView, {
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
|
||||
import { getCategories } from '@anticrm/skillset'
|
||||
import core, {
|
||||
DOMAIN_TX,
|
||||
DOMAIN_MODEL_TX,
|
||||
toIdMap,
|
||||
TxOperations,
|
||||
type Doc,
|
||||
@@ -149,7 +149,7 @@ async function migrateDefaultTypeMixins (client: MigrationClient): Promise<void>
|
||||
const newTaskTypeMixin = recruit.mixin.ApplicantTypeData
|
||||
|
||||
await client.update(
|
||||
DOMAIN_TX,
|
||||
DOMAIN_MODEL_TX,
|
||||
{
|
||||
objectClass: core.class.Attribute,
|
||||
'attributes.attributeOf': oldSpaceTypeMixin
|
||||
|
||||
+24
-14
@@ -18,7 +18,7 @@ import {
|
||||
Account,
|
||||
IndexKind,
|
||||
type CollaborativeDoc,
|
||||
type Collection as Array,
|
||||
type Collection,
|
||||
type Domain,
|
||||
type Markup,
|
||||
type Ref,
|
||||
@@ -28,7 +28,7 @@ import {
|
||||
type Timestamp
|
||||
} from '@hcengineering/core'
|
||||
import {
|
||||
Collection,
|
||||
Collection as TypeCollection,
|
||||
Hidden,
|
||||
Index,
|
||||
Mixin,
|
||||
@@ -70,7 +70,9 @@ export class TVacancy extends TProject implements Vacancy {
|
||||
@Index(IndexKind.FullText)
|
||||
fullDescription!: CollaborativeDoc
|
||||
|
||||
@Prop(Collection(attachment.class.Attachment), attachment.string.Attachments, { shortLabel: attachment.string.Files })
|
||||
@Prop(TypeCollection(attachment.class.Attachment), attachment.string.Attachments, {
|
||||
shortLabel: attachment.string.Files
|
||||
})
|
||||
attachments?: number
|
||||
|
||||
@Prop(TypeDate(), recruit.string.Due, recruit.icon.Calendar)
|
||||
@@ -83,13 +85,16 @@ export class TVacancy extends TProject implements Vacancy {
|
||||
@Prop(TypeRef(contact.class.Organization), recruit.string.Company, { icon: contact.icon.Company })
|
||||
company?: Ref<Organization>
|
||||
|
||||
@Prop(Collection(chunter.class.ChatMessage), chunter.string.Comments)
|
||||
@Prop(TypeCollection(chunter.class.ChatMessage), chunter.string.Comments)
|
||||
comments?: number
|
||||
|
||||
@Prop(TypeString(), recruit.string.Vacancy)
|
||||
@Index(IndexKind.FullText)
|
||||
@Hidden()
|
||||
number!: number
|
||||
|
||||
@Prop(TypeCollection(survey.class.Poll), survey.string.Polls)
|
||||
polls?: Collection<Poll>
|
||||
}
|
||||
|
||||
@Mixin(recruit.mixin.Candidate, contact.class.Person)
|
||||
@@ -99,7 +104,7 @@ export class TCandidate extends TPerson implements Candidate {
|
||||
@Index(IndexKind.FullText)
|
||||
title?: string
|
||||
|
||||
@Prop(Collection(recruit.class.Applicant), recruit.string.Applications, {
|
||||
@Prop(TypeCollection(recruit.class.Applicant), recruit.string.Applications, {
|
||||
shortLabel: recruit.string.ApplicationsShort
|
||||
})
|
||||
applications?: number
|
||||
@@ -114,26 +119,29 @@ export class TCandidate extends TPerson implements Candidate {
|
||||
@Index(IndexKind.FullText)
|
||||
source?: string
|
||||
|
||||
@Prop(Collection(tags.class.TagReference, recruit.string.SkillLabel), recruit.string.SkillsLabel, {
|
||||
@Prop(TypeCollection(tags.class.TagReference, recruit.string.SkillLabel), recruit.string.SkillsLabel, {
|
||||
icon: recruit.icon.Skills,
|
||||
schema: '3'
|
||||
})
|
||||
skills?: number
|
||||
|
||||
@Prop(Collection(recruit.class.Review, recruit.string.Review), recruit.string.Reviews)
|
||||
@Prop(TypeCollection(recruit.class.Review, recruit.string.Review), recruit.string.Reviews)
|
||||
reviews?: number
|
||||
|
||||
@Prop(
|
||||
Collection(recruit.class.ApplicantMatch, getEmbeddedLabel('Vacancy match')),
|
||||
TypeCollection(recruit.class.ApplicantMatch, getEmbeddedLabel('Vacancy match')),
|
||||
getEmbeddedLabel('Vacancy Matches')
|
||||
)
|
||||
vacancyMatch?: number
|
||||
|
||||
@Prop(TypeCollection(survey.class.Poll), survey.string.Polls)
|
||||
polls?: Collection<Poll>
|
||||
}
|
||||
|
||||
@Mixin(recruit.mixin.VacancyList, contact.class.Organization)
|
||||
@UX(recruit.string.VacancyList, recruit.icon.RecruitApplication, 'CM', 'name')
|
||||
export class TVacancyList extends TOrganization implements VacancyList {
|
||||
@Prop(Collection(recruit.class.Vacancy), recruit.string.Vacancies)
|
||||
@Prop(TypeCollection(recruit.class.Vacancy), recruit.string.Vacancies)
|
||||
vacancies!: number
|
||||
}
|
||||
|
||||
@@ -162,8 +170,8 @@ export class TApplicant extends TTask implements Applicant {
|
||||
@Index(IndexKind.Indexed)
|
||||
declare status: Ref<Status>
|
||||
|
||||
@Prop(Collection(survey.class.Poll), survey.string.Polls)
|
||||
polls?: Array<Poll>
|
||||
@Prop(TypeCollection(survey.class.Poll), survey.string.Polls)
|
||||
polls?: Collection<Poll>
|
||||
}
|
||||
|
||||
@Model(recruit.class.ApplicantMatch, core.class.AttachedDoc, DOMAIN_TASK)
|
||||
@@ -211,7 +219,7 @@ export class TReview extends TEvent implements Review {
|
||||
@Prop(TypeRef(contact.class.Organization), recruit.string.Company, { icon: contact.icon.Company })
|
||||
company?: Ref<Organization>
|
||||
|
||||
@Prop(Collection(recruit.class.Opinion), recruit.string.Opinions)
|
||||
@Prop(TypeCollection(recruit.class.Opinion), recruit.string.Opinions)
|
||||
opinions?: number
|
||||
}
|
||||
|
||||
@@ -225,10 +233,12 @@ export class TOpinion extends TAttachedDoc implements Opinion {
|
||||
@Prop(TypeRef(recruit.class.Review), recruit.string.Review)
|
||||
declare attachedTo: Ref<Review>
|
||||
|
||||
@Prop(Collection(attachment.class.Attachment), attachment.string.Attachments, { shortLabel: attachment.string.Files })
|
||||
@Prop(TypeCollection(attachment.class.Attachment), attachment.string.Attachments, {
|
||||
shortLabel: attachment.string.Files
|
||||
})
|
||||
attachments?: number
|
||||
|
||||
@Prop(Collection(chunter.class.ChatMessage), chunter.string.Comments)
|
||||
@Prop(TypeCollection(chunter.class.ChatMessage), chunter.string.Comments)
|
||||
comments?: number
|
||||
|
||||
@Prop(TypeMarkup(), recruit.string.Description)
|
||||
|
||||
@@ -12,18 +12,18 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
import core, { DOMAIN_TX, type Ref, type TxCreateDoc } from '@hcengineering/core'
|
||||
import request, { requestId, type Request } from '@hcengineering/request'
|
||||
import contact, { type Person, type PersonAccount } from '@hcengineering/contact'
|
||||
import core, { DOMAIN_MODEL_TX, type Ref, type TxCreateDoc } from '@hcengineering/core'
|
||||
import {
|
||||
type MigrateUpdate,
|
||||
type MigrationDocumentQuery,
|
||||
tryMigrate,
|
||||
type MigrateOperation,
|
||||
type MigrateUpdate,
|
||||
type MigrationClient,
|
||||
type MigrationDocumentQuery,
|
||||
type MigrationUpgradeClient,
|
||||
type ModelLogger
|
||||
} from '@hcengineering/model'
|
||||
import contact, { type Person, type PersonAccount } from '@hcengineering/contact'
|
||||
import request, { requestId, type Request } from '@hcengineering/request'
|
||||
|
||||
import { DOMAIN_REQUEST } from '.'
|
||||
|
||||
@@ -32,7 +32,7 @@ async function migrateRequestPersonAccounts (client: MigrationClient): Promise<v
|
||||
const requests = await client.find<Request>(DOMAIN_REQUEST, {
|
||||
_class: { $in: descendants }
|
||||
})
|
||||
const personAccountsCreateTxes = await client.find(DOMAIN_TX, {
|
||||
const personAccountsCreateTxes = await client.find(DOMAIN_MODEL_TX, {
|
||||
_class: core.class.TxCreateDoc,
|
||||
objectClass: contact.class.PersonAccount
|
||||
})
|
||||
|
||||
@@ -36,14 +36,16 @@ export function createModel (builder: Builder): void {
|
||||
builder.createDoc(serverCore.class.Trigger, core.space.Model, {
|
||||
trigger: serverActivity.trigger.OnReactionChanged,
|
||||
txMatch: {
|
||||
collection: 'reactions',
|
||||
_class: core.class.TxCollectionCUD
|
||||
collection: 'reactions'
|
||||
},
|
||||
isAsync: true
|
||||
})
|
||||
|
||||
builder.createDoc(serverCore.class.Trigger, core.space.Model, {
|
||||
trigger: serverActivity.trigger.ActivityMessagesHandler,
|
||||
txMatch: {
|
||||
objectClass: { $nin: [activity.class.ActivityMessage, notification.class.DocNotifyContext] }
|
||||
},
|
||||
isAsync: true
|
||||
})
|
||||
|
||||
@@ -54,8 +56,8 @@ export function createModel (builder: Builder): void {
|
||||
builder.createDoc(serverCore.class.Trigger, core.space.Model, {
|
||||
trigger: serverActivity.trigger.ReferenceTrigger,
|
||||
txMatch: {
|
||||
'tx.objectClass': { $ne: activity.class.ActivityReference },
|
||||
objectClass: {
|
||||
objectClass: { $ne: activity.class.ActivityReference },
|
||||
attachedToClass: {
|
||||
$nin: [
|
||||
notification.class.InboxNotification,
|
||||
notification.class.DocNotifyContext,
|
||||
|
||||
@@ -18,16 +18,13 @@ import core, {
|
||||
MeasureMetricsContext,
|
||||
SortingOrder,
|
||||
TxFactory,
|
||||
TxProcessor,
|
||||
toFindResult,
|
||||
toIdMap,
|
||||
type AttachedDoc,
|
||||
type Class,
|
||||
type Doc,
|
||||
type Ref,
|
||||
type Tx,
|
||||
type TxCUD,
|
||||
type TxCollectionCUD,
|
||||
type TxCreateDoc
|
||||
} from '@hcengineering/core'
|
||||
import {
|
||||
@@ -85,12 +82,11 @@ async function generateDocUpdateMessageByTx (
|
||||
tx,
|
||||
control,
|
||||
undefined,
|
||||
undefined,
|
||||
objectCache
|
||||
)
|
||||
|
||||
for (const collectionTx of createCollectionCUDTxes) {
|
||||
const createTx = collectionTx.tx as TxCreateDoc<DocUpdateMessage>
|
||||
const createTx = collectionTx as TxCreateDoc<DocUpdateMessage>
|
||||
const domain = client.hierarchy.getDomain(createTx.objectClass)
|
||||
|
||||
await client.create<DocUpdateMessage>(domain, {
|
||||
@@ -152,15 +148,14 @@ async function createDocUpdateMessages (client: MigrationClient): Promise<void>
|
||||
continue
|
||||
}
|
||||
|
||||
if (v._class === core.class.TxCollectionCUD) {
|
||||
if (v.attachedToClass !== undefined && v.attachedTo !== undefined) {
|
||||
try {
|
||||
const vcol = v as TxCollectionCUD<Doc, AttachedDoc>
|
||||
const _cl = client.hierarchy.getBaseClass(vcol.tx.objectClass)
|
||||
const _cl = client.hierarchy.getBaseClass(v.attachedToClass)
|
||||
const s = byClass.get(_cl) ?? new Set()
|
||||
s.add(vcol.tx.objectId)
|
||||
s.add(v.attachedTo)
|
||||
byClass.set(_cl, s)
|
||||
} catch {
|
||||
const objClass = (v as TxCollectionCUD<Doc, AttachedDoc>).tx.objectClass
|
||||
const objClass = v.attachedToClass
|
||||
const has = classNotFound.has(objClass)
|
||||
if (!has) {
|
||||
classNotFound.add(objClass)
|
||||
@@ -198,8 +193,7 @@ async function createDocUpdateMessages (client: MigrationClient): Promise<void>
|
||||
}
|
||||
const transactions = allTransactions.get(d._id) ?? []
|
||||
for (const tx of transactions) {
|
||||
const innerTx = TxProcessor.extractTx(tx) as TxCUD<Doc>
|
||||
txIds.add(innerTx._id)
|
||||
txIds.add(tx._id)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -220,10 +214,8 @@ async function createDocUpdateMessages (client: MigrationClient): Promise<void>
|
||||
}
|
||||
const transactions = allTransactions.get(d._id) ?? []
|
||||
for (const tx of transactions) {
|
||||
const innerTx = TxProcessor.extractTx(tx) as TxCUD<Doc>
|
||||
|
||||
if (!client.hierarchy.hasClass(innerTx.objectClass)) {
|
||||
const objClass = innerTx.objectClass
|
||||
if (!client.hierarchy.hasClass(tx.objectClass)) {
|
||||
const objClass = tx.objectClass
|
||||
const has = classNotFound.has(objClass)
|
||||
if (!has) {
|
||||
classNotFound.add(objClass)
|
||||
|
||||
@@ -27,9 +27,8 @@ export function createModel (builder: Builder): void {
|
||||
builder.createDoc(serverCore.class.Trigger, core.space.Model, {
|
||||
trigger: serverAttachment.trigger.OnAttachmentDelete,
|
||||
txMatch: {
|
||||
_class: core.class.TxCollectionCUD,
|
||||
'tx.objectClass': attachment.class.Attachment,
|
||||
'tx._class': core.class.TxRemoveDoc
|
||||
_class: core.class.TxRemoveDoc,
|
||||
objectClass: attachment.class.Attachment
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -44,7 +44,7 @@ export function createModel (builder: Builder): void {
|
||||
builder.createDoc(serverCore.class.Trigger, core.space.Model, {
|
||||
trigger: serverCalendar.trigger.OnEvent,
|
||||
txMatch: {
|
||||
'tx.objectClass': calendar.class.Event
|
||||
objectClass: calendar.class.Event
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
@@ -78,18 +78,16 @@ export function createModel (builder: Builder): void {
|
||||
builder.createDoc(serverCore.class.Trigger, core.space.Model, {
|
||||
trigger: serverChunter.trigger.OnChatMessageRemoved,
|
||||
txMatch: {
|
||||
_class: core.class.TxCollectionCUD,
|
||||
'tx._class': core.class.TxRemoveDoc,
|
||||
'tx.objectClass': chunter.class.ChatMessage
|
||||
_class: core.class.TxRemoveDoc,
|
||||
objectClass: chunter.class.ChatMessage
|
||||
}
|
||||
})
|
||||
|
||||
builder.createDoc(serverCore.class.Trigger, core.space.Model, {
|
||||
trigger: serverChunter.trigger.ChatNotificationsHandler,
|
||||
txMatch: {
|
||||
_class: core.class.TxCollectionCUD,
|
||||
'tx._class': core.class.TxCreateDoc,
|
||||
'tx.objectClass': chunter.class.ChatMessage
|
||||
_class: core.class.TxCreateDoc,
|
||||
objectClass: chunter.class.ChatMessage
|
||||
},
|
||||
isAsync: true
|
||||
})
|
||||
|
||||
@@ -23,6 +23,9 @@ export { serverCollaborationId } from '@hcengineering/server-collaboration'
|
||||
|
||||
export function createModel (builder: Builder): void {
|
||||
builder.createDoc(serverCore.class.Trigger, core.space.Model, {
|
||||
trigger: serverCollaboration.trigger.OnDelete
|
||||
trigger: serverCollaboration.trigger.OnDelete,
|
||||
txMatch: {
|
||||
_class: core.class.TxRemoveDoc
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -33,11 +33,10 @@ export function createModel (builder: Builder): void {
|
||||
builder.createDoc(serverCore.class.Trigger, core.space.Model, {
|
||||
trigger: serverDocuments.trigger.OnDocApprovalRequestApproved,
|
||||
txMatch: {
|
||||
_class: core.class.TxCollectionCUD,
|
||||
objectClass: documents.class.ControlledDocument,
|
||||
'tx._class': core.class.TxUpdateDoc,
|
||||
'tx.objectClass': documents.class.DocumentApprovalRequest,
|
||||
'tx.operations.status': RequestStatus.Completed
|
||||
_class: core.class.TxUpdateDoc,
|
||||
attachedToClass: documents.class.ControlledDocument,
|
||||
objectClass: documents.class.DocumentApprovalRequest,
|
||||
'operations.status': RequestStatus.Completed
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
@@ -42,9 +42,8 @@ export function createModel (builder: Builder): void {
|
||||
builder.createDoc(serverCore.class.Trigger, core.space.Model, {
|
||||
trigger: serverGmail.trigger.OnMessageCreate,
|
||||
txMatch: {
|
||||
_class: core.class.TxCollectionCUD,
|
||||
'tx.objectClass': gmail.class.Message,
|
||||
'tx._class': core.class.TxCreateDoc
|
||||
_class: core.class.TxCreateDoc,
|
||||
objectClass: gmail.class.Message
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
@@ -44,27 +44,24 @@ export function createModel (builder: Builder): void {
|
||||
builder.createDoc(serverCore.class.Trigger, core.space.Model, {
|
||||
trigger: serverHr.trigger.OnRequestCreate,
|
||||
txMatch: {
|
||||
_class: core.class.TxCollectionCUD,
|
||||
'tx.objectClass': hr.class.Request,
|
||||
'tx._class': core.class.TxCreateDoc
|
||||
_class: core.class.TxCreateDoc,
|
||||
objectClass: hr.class.Request
|
||||
}
|
||||
})
|
||||
|
||||
builder.createDoc(serverCore.class.Trigger, core.space.Model, {
|
||||
trigger: serverHr.trigger.OnRequestUpdate,
|
||||
txMatch: {
|
||||
_class: core.class.TxCollectionCUD,
|
||||
'tx.objectClass': hr.class.Request,
|
||||
'tx._class': core.class.TxUpdateDoc
|
||||
_class: core.class.TxUpdateDoc,
|
||||
objectClass: hr.class.Request
|
||||
}
|
||||
})
|
||||
|
||||
builder.createDoc(serverCore.class.Trigger, core.space.Model, {
|
||||
trigger: serverHr.trigger.OnRequestRemove,
|
||||
txMatch: {
|
||||
_class: core.class.TxCollectionCUD,
|
||||
'tx.objectClass': hr.class.Request,
|
||||
'tx._class': core.class.TxRemoveDoc
|
||||
_class: core.class.TxRemoveDoc,
|
||||
objectClass: hr.class.Request
|
||||
}
|
||||
})
|
||||
|
||||
@@ -88,9 +85,8 @@ export function createModel (builder: Builder): void {
|
||||
builder.createDoc(serverCore.class.Trigger, core.space.Model, {
|
||||
trigger: serverHr.trigger.OnPublicHolidayCreate,
|
||||
txMatch: {
|
||||
_class: core.class.TxCollectionCUD,
|
||||
'tx.objectClass': hr.class.PublicHoliday,
|
||||
'tx._class': core.class.TxCreateDoc
|
||||
_class: core.class.TxCreateDoc,
|
||||
objectClass: hr.class.PublicHoliday
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
@@ -89,7 +89,10 @@ export function createModel (builder: Builder): void {
|
||||
})
|
||||
|
||||
builder.createDoc(serverCore.class.Trigger, core.space.Model, {
|
||||
trigger: serverNotification.trigger.OnDocRemove
|
||||
trigger: serverNotification.trigger.OnDocRemove,
|
||||
txMatch: {
|
||||
_class: core.class.TxRemoveDoc
|
||||
}
|
||||
})
|
||||
|
||||
builder.createDoc(serverCore.class.Trigger, core.space.Model, {
|
||||
|
||||
@@ -25,7 +25,10 @@ export { serverRequestId } from '@hcengineering/server-request'
|
||||
|
||||
export function createModel (builder: Builder): void {
|
||||
builder.createDoc(serverCore.class.Trigger, core.space.Model, {
|
||||
trigger: serverRequest.trigger.OnRequest
|
||||
trigger: serverRequest.trigger.OnRequest,
|
||||
txMatch: {
|
||||
objectClass: request.class.Request
|
||||
}
|
||||
})
|
||||
|
||||
builder.mixin(request.class.Request, core.class.Class, serverNotification.mixin.TextPresenter, {
|
||||
|
||||
@@ -68,11 +68,8 @@ export function createModel (builder: Builder): void {
|
||||
builder.createDoc(serverCore.class.Trigger, core.space.Model, {
|
||||
trigger: serverSetting.trigger.OnRoleNameUpdate,
|
||||
txMatch: {
|
||||
_class: core.class.TxCollectionCUD,
|
||||
objectSpace: core.space.Model,
|
||||
collection: 'roles',
|
||||
'tx._class': core.class.TxUpdateDoc,
|
||||
'tx.objectClass': core.class.Role
|
||||
_class: core.class.TxUpdateDoc,
|
||||
objectClass: core.class.Role
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -23,7 +23,11 @@ export { serverTagsId } from '@hcengineering/server-tags'
|
||||
|
||||
export function createModel (builder: Builder): void {
|
||||
builder.createDoc(serverCore.class.Trigger, core.space.Model, {
|
||||
trigger: serverTags.trigger.onTagReference
|
||||
trigger: serverTags.trigger.onTagReference,
|
||||
txMatch: {
|
||||
_class: { $in: [core.class.TxCreateDoc, core.class.TxRemoveDoc] },
|
||||
objectClass: tags.class.TagReference
|
||||
}
|
||||
})
|
||||
|
||||
builder.mixin<Class<Doc>, ObjectDDParticipant>(
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
|
||||
import core from '@hcengineering/core/src/component'
|
||||
import { type Builder } from '@hcengineering/model'
|
||||
import task from '@hcengineering/task'
|
||||
import serverCore from '@hcengineering/server-core'
|
||||
import serverTask from '@hcengineering/server-task'
|
||||
|
||||
@@ -22,6 +23,9 @@ export { serverTaskId } from '@hcengineering/server-task'
|
||||
|
||||
export function createModel (builder: Builder): void {
|
||||
builder.createDoc(serverCore.class.Trigger, core.space.Model, {
|
||||
trigger: serverTask.trigger.OnStateUpdate
|
||||
trigger: serverTask.trigger.OnStateUpdate,
|
||||
txMatch: {
|
||||
objectClass: task.class.Task
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -45,9 +45,8 @@ export function createModel (builder: Builder): void {
|
||||
builder.createDoc(serverCore.class.Trigger, core.space.Model, {
|
||||
trigger: serverTelegram.trigger.OnMessageCreate,
|
||||
txMatch: {
|
||||
_class: core.class.TxCollectionCUD,
|
||||
'tx.objectClass': telegram.class.Message,
|
||||
'tx._class': core.class.TxCreateDoc
|
||||
_class: core.class.TxCreateDoc,
|
||||
objectClass: telegram.class.Message
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
@@ -44,27 +44,24 @@ export function createModel (builder: Builder): void {
|
||||
builder.createDoc(serverCore.class.Trigger, core.space.Model, {
|
||||
trigger: serverTime.trigger.OnToDoUpdate,
|
||||
txMatch: {
|
||||
_class: core.class.TxCollectionCUD,
|
||||
'tx.objectClass': time.class.ToDo,
|
||||
'tx._class': core.class.TxUpdateDoc
|
||||
_class: core.class.TxUpdateDoc,
|
||||
objectClass: time.class.ToDo
|
||||
}
|
||||
})
|
||||
|
||||
builder.createDoc(serverCore.class.Trigger, core.space.Model, {
|
||||
trigger: serverTime.trigger.OnToDoRemove,
|
||||
txMatch: {
|
||||
_class: core.class.TxCollectionCUD,
|
||||
'tx.objectClass': time.class.ToDo,
|
||||
'tx._class': core.class.TxRemoveDoc
|
||||
_class: core.class.TxRemoveDoc,
|
||||
objectClass: time.class.ToDo
|
||||
}
|
||||
})
|
||||
|
||||
builder.createDoc(serverCore.class.Trigger, core.space.Model, {
|
||||
trigger: serverTime.trigger.OnToDoCreate,
|
||||
txMatch: {
|
||||
_class: core.class.TxCollectionCUD,
|
||||
'tx.objectClass': time.class.ToDo,
|
||||
'tx._class': core.class.TxCreateDoc
|
||||
_class: core.class.TxCreateDoc,
|
||||
objectClass: time.class.ToDo
|
||||
},
|
||||
isAsync: true
|
||||
})
|
||||
@@ -72,18 +69,16 @@ export function createModel (builder: Builder): void {
|
||||
builder.createDoc(serverCore.class.Trigger, core.space.Model, {
|
||||
trigger: serverTime.trigger.OnWorkSlotCreate,
|
||||
txMatch: {
|
||||
_class: core.class.TxCollectionCUD,
|
||||
'tx.objectClass': time.class.WorkSlot,
|
||||
'tx._class': core.class.TxCreateDoc
|
||||
_class: core.class.TxCreateDoc,
|
||||
objectClass: time.class.WorkSlot
|
||||
}
|
||||
})
|
||||
|
||||
builder.createDoc(serverCore.class.Trigger, core.space.Model, {
|
||||
trigger: serverTime.trigger.OnWorkSlotUpdate,
|
||||
txMatch: {
|
||||
_class: core.class.TxCollectionCUD,
|
||||
'tx.objectClass': time.class.WorkSlot,
|
||||
'tx._class': core.class.TxUpdateDoc
|
||||
_class: core.class.TxUpdateDoc,
|
||||
objectClass: time.class.WorkSlot
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
@@ -38,6 +38,7 @@
|
||||
"@hcengineering/platform": "^0.6.11",
|
||||
"@hcengineering/survey": "^0.6.0",
|
||||
"@hcengineering/survey-resources": "^0.6.0",
|
||||
"@hcengineering/ui": "^0.6.15"
|
||||
"@hcengineering/ui": "^0.6.15",
|
||||
"@hcengineering/view": "^0.6.13"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ import { AccountRole } from '@hcengineering/core'
|
||||
import { type Builder } from '@hcengineering/model'
|
||||
import core from '@hcengineering/model-core'
|
||||
import chunter from '@hcengineering/model-chunter'
|
||||
import view, { type Viewlet } from '@hcengineering/model-view'
|
||||
import view, { createAction, type Viewlet } from '@hcengineering/model-view'
|
||||
import workbench from '@hcengineering/model-workbench'
|
||||
import { surveyId } from '@hcengineering/survey'
|
||||
import { TPoll, TSurvey } from './types'
|
||||
@@ -79,6 +79,14 @@ export function createModel (builder: Builder): void {
|
||||
survey.viewlet.TableSurvey
|
||||
)
|
||||
|
||||
builder.mixin(survey.class.Survey, core.class.Class, view.mixin.ObjectTitle, {
|
||||
titleProvider: survey.function.SurveyTitleProvider
|
||||
})
|
||||
|
||||
builder.mixin(survey.class.Survey, core.class.Class, view.mixin.LinkProvider, {
|
||||
encode: survey.function.GetSurveyLink
|
||||
})
|
||||
|
||||
builder.mixin(survey.class.Survey, core.class.Class, view.mixin.ObjectPanel, {
|
||||
component: survey.component.EditSurveyPanel
|
||||
})
|
||||
@@ -91,7 +99,7 @@ export function createModel (builder: Builder): void {
|
||||
|
||||
builder.createDoc(activity.class.ActivityExtension, core.space.Model, {
|
||||
ofClass: survey.class.Survey,
|
||||
components: { input: chunter.component.ChatMessageInput }
|
||||
components: { input: { component: chunter.component.ChatMessageInput } }
|
||||
})
|
||||
|
||||
builder.createDoc<Viewlet>(
|
||||
@@ -100,20 +108,58 @@ export function createModel (builder: Builder): void {
|
||||
{
|
||||
attachTo: survey.class.Poll,
|
||||
descriptor: view.viewlet.Table,
|
||||
config: ['', 'modifiedOn'],
|
||||
config: ['', 'isCompleted', 'modifiedOn'],
|
||||
configOptions: {
|
||||
hiddenKeys: ['name', 'survey', 'results'],
|
||||
hiddenKeys: ['name', 'survey', 'questions'],
|
||||
sortable: true
|
||||
}
|
||||
},
|
||||
survey.viewlet.TablePoll
|
||||
)
|
||||
|
||||
builder.mixin(survey.class.Poll, core.class.Class, view.mixin.ObjectTitle, {
|
||||
titleProvider: survey.function.PollTitleProvider
|
||||
})
|
||||
|
||||
builder.mixin(survey.class.Poll, core.class.Class, view.mixin.LinkProvider, {
|
||||
encode: survey.function.GetPollLink
|
||||
})
|
||||
|
||||
builder.mixin(survey.class.Poll, core.class.Class, view.mixin.ObjectPanel, {
|
||||
component: survey.component.EditPollPanel
|
||||
})
|
||||
|
||||
builder.mixin(survey.class.Poll, core.class.Class, view.mixin.ObjectPresenter, {
|
||||
presenter: survey.component.PollPresenter
|
||||
})
|
||||
|
||||
builder.mixin(survey.class.Poll, core.class.Class, activity.mixin.ActivityDoc, {})
|
||||
|
||||
builder.createDoc(activity.class.ActivityExtension, core.space.Model, {
|
||||
ofClass: survey.class.Poll,
|
||||
components: { input: { component: chunter.component.ChatMessageInput } }
|
||||
})
|
||||
|
||||
builder.mixin(survey.class.Poll, core.class.Class, view.mixin.CollectionEditor, {
|
||||
editor: survey.component.PollCollection
|
||||
})
|
||||
|
||||
createAction(
|
||||
builder,
|
||||
{
|
||||
action: survey.actionImpl.DeletePoll,
|
||||
label: workbench.string.Delete,
|
||||
icon: view.icon.Delete,
|
||||
input: 'any',
|
||||
category: survey.category.Survey,
|
||||
target: survey.class.Poll,
|
||||
context: {
|
||||
mode: ['context', 'browser'],
|
||||
group: 'remove'
|
||||
},
|
||||
visibilityTester: view.function.CanDeleteObject,
|
||||
override: [view.action.Delete]
|
||||
},
|
||||
survey.action.DeletePoll
|
||||
)
|
||||
}
|
||||
|
||||
@@ -13,8 +13,6 @@
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import { mergeIds } from '@hcengineering/platform'
|
||||
import { surveyId } from '@hcengineering/survey'
|
||||
import survey from '@hcengineering/survey-resources/src/plugin'
|
||||
|
||||
export default mergeIds(surveyId, survey, {})
|
||||
export default survey
|
||||
|
||||
@@ -14,7 +14,18 @@
|
||||
//
|
||||
|
||||
import { IndexKind, type Domain, type Ref } from '@hcengineering/core'
|
||||
import { ArrOf, Hidden, Index, Model, Prop, TypeRecord, TypeRef, TypeString, UX } from '@hcengineering/model'
|
||||
import {
|
||||
ArrOf,
|
||||
Hidden,
|
||||
Index,
|
||||
Model,
|
||||
Prop,
|
||||
TypeBoolean,
|
||||
TypeRecord,
|
||||
TypeRef,
|
||||
TypeString,
|
||||
UX
|
||||
} from '@hcengineering/model'
|
||||
import core, { TAttachedDoc, TDoc } from '@hcengineering/model-core'
|
||||
import { getEmbeddedLabel } from '@hcengineering/platform'
|
||||
import { type Poll, type Question, type Survey } from '@hcengineering/survey'
|
||||
@@ -48,7 +59,10 @@ export class TPoll extends TAttachedDoc implements Poll {
|
||||
@Prop(TypeString(), survey.string.Prompt)
|
||||
prompt!: string
|
||||
|
||||
@Prop(ArrOf(TypeRecord()), getEmbeddedLabel('Answers'))
|
||||
@Prop(ArrOf(TypeRecord()), getEmbeddedLabel('Questions'))
|
||||
@Hidden()
|
||||
results?: { question: string, answer: string[] }[]
|
||||
questions?: Question[]
|
||||
|
||||
@Prop(TypeBoolean(), survey.string.Completed)
|
||||
isCompleted?: boolean
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
|
||||
import activity, { type DocUpdateMessage } from '@hcengineering/activity'
|
||||
import {
|
||||
DOMAIN_MODEL_TX,
|
||||
DOMAIN_STATUS,
|
||||
DOMAIN_TX,
|
||||
TxOperations,
|
||||
@@ -25,7 +26,6 @@ import {
|
||||
type Ref,
|
||||
type Space,
|
||||
type Status,
|
||||
type TxCollectionCUD,
|
||||
type TxCreateDoc,
|
||||
type TxUpdateDoc
|
||||
} from '@hcengineering/core'
|
||||
@@ -95,7 +95,7 @@ export async function migrateDefaultStatusesBase<T extends Task> (
|
||||
// 3. More than one type (one system and one custom) - the tool is running after the WS upgrade.
|
||||
// Not supported for now. Alternatively - Proceed with (2) scenario for the custom one. Delete it in the end.
|
||||
|
||||
const defaultTypes = await client.find<TxCreateDoc<ProjectType>>(DOMAIN_TX, {
|
||||
const defaultTypes = await client.find<TxCreateDoc<ProjectType>>(DOMAIN_MODEL_TX, {
|
||||
_class: core.class.TxCreateDoc,
|
||||
objectId: defaultTypeId,
|
||||
objectSpace: core.space.Model,
|
||||
@@ -121,7 +121,7 @@ export async function migrateDefaultStatusesBase<T extends Task> (
|
||||
// and not modified by user
|
||||
if (defaultType.attributes.tasks.length === 1 && defaultType.attributes.tasks[0] === defaultTaskTypeId) {
|
||||
const defaultTaskType = (
|
||||
await client.find<TxCreateDoc<TaskType>>(DOMAIN_TX, {
|
||||
await client.find<TxCreateDoc<TaskType>>(DOMAIN_MODEL_TX, {
|
||||
_class: core.class.TxCreateDoc,
|
||||
objectId: defaultTaskTypeId,
|
||||
objectSpace: core.space.Model,
|
||||
@@ -133,7 +133,7 @@ export async function migrateDefaultStatusesBase<T extends Task> (
|
||||
logger.log('Moving the existing default type created by ConfigUser to a system one', '')
|
||||
logger.log('Moving the existing default task type created by ConfigUser to a system one', '')
|
||||
await client.update(
|
||||
DOMAIN_TX,
|
||||
DOMAIN_MODEL_TX,
|
||||
{ _id: defaultTaskType._id },
|
||||
{
|
||||
$set: {
|
||||
@@ -143,7 +143,7 @@ export async function migrateDefaultStatusesBase<T extends Task> (
|
||||
)
|
||||
|
||||
await client.update(
|
||||
DOMAIN_TX,
|
||||
DOMAIN_MODEL_TX,
|
||||
{ _id: defaultType._id },
|
||||
{
|
||||
$set: {
|
||||
@@ -173,7 +173,7 @@ export async function migrateDefaultStatusesBase<T extends Task> (
|
||||
logger.log('Moving the existing default type to a custom one', '')
|
||||
const newId = defaultType.objectId + '-custom'
|
||||
await client.update(
|
||||
DOMAIN_TX,
|
||||
DOMAIN_MODEL_TX,
|
||||
{ _id: defaultType._id },
|
||||
{
|
||||
$set: {
|
||||
@@ -183,7 +183,7 @@ export async function migrateDefaultStatusesBase<T extends Task> (
|
||||
}
|
||||
)
|
||||
await client.update(
|
||||
DOMAIN_TX,
|
||||
DOMAIN_MODEL_TX,
|
||||
{
|
||||
objectId: defaultType.objectId,
|
||||
objectSpace: core.space.Model
|
||||
@@ -195,7 +195,7 @@ export async function migrateDefaultStatusesBase<T extends Task> (
|
||||
}
|
||||
)
|
||||
await client.update(
|
||||
DOMAIN_TX,
|
||||
DOMAIN_MODEL_TX,
|
||||
{
|
||||
objectId: { $in: defaultType.attributes.tasks },
|
||||
objectSpace: core.space.Model,
|
||||
@@ -295,7 +295,7 @@ export async function migrateDefaultStatusesBase<T extends Task> (
|
||||
// 1. Update all update TXes with statuses
|
||||
// 2. Update all push TXes with statuses
|
||||
|
||||
const projectTypeStatusesCreates = await client.find<TxCreateDoc<ProjectType>>(DOMAIN_TX, {
|
||||
const projectTypeStatusesCreates = await client.find<TxCreateDoc<ProjectType>>(DOMAIN_MODEL_TX, {
|
||||
_class: core.class.TxCreateDoc,
|
||||
objectClass: task.class.ProjectType,
|
||||
objectSpace: core.space.Model,
|
||||
@@ -313,11 +313,11 @@ export async function migrateDefaultStatusesBase<T extends Task> (
|
||||
}
|
||||
|
||||
counter++
|
||||
await client.update(DOMAIN_TX, { _id: ptsCreate._id }, { $set: { 'attributes.statuses': newUpdateStatuses } })
|
||||
await client.update(DOMAIN_MODEL_TX, { _id: ptsCreate._id }, { $set: { 'attributes.statuses': newUpdateStatuses } })
|
||||
}
|
||||
logger.log('projectTypeStatusesCreates updated: ', counter)
|
||||
|
||||
const projectTypeStatusesUpdates = await client.find<TxUpdateDoc<ProjectType>>(DOMAIN_TX, {
|
||||
const projectTypeStatusesUpdates = await client.find<TxUpdateDoc<ProjectType>>(DOMAIN_MODEL_TX, {
|
||||
_class: core.class.TxUpdateDoc,
|
||||
objectId: { $in: projectTypeStatusesCreates.map((sc) => sc.objectId) },
|
||||
objectClass: task.class.ProjectType,
|
||||
@@ -335,11 +335,11 @@ export async function migrateDefaultStatusesBase<T extends Task> (
|
||||
}
|
||||
|
||||
counter++
|
||||
await client.update(DOMAIN_TX, { _id: ptsUpdate._id }, { $set: { 'operations.statuses': newUpdateStatuses } })
|
||||
await client.update(DOMAIN_MODEL_TX, { _id: ptsUpdate._id }, { $set: { 'operations.statuses': newUpdateStatuses } })
|
||||
}
|
||||
logger.log('projectTypeStatusesUpdates updated: ', counter)
|
||||
|
||||
const projectTypeStatusesPushes = await client.find<TxUpdateDoc<ProjectType>>(DOMAIN_TX, {
|
||||
const projectTypeStatusesPushes = await client.find<TxUpdateDoc<ProjectType>>(DOMAIN_MODEL_TX, {
|
||||
_class: core.class.TxUpdateDoc,
|
||||
objectId: { $in: projectTypeStatusesCreates.map((sc) => sc.objectId) },
|
||||
objectClass: task.class.ProjectType,
|
||||
@@ -363,7 +363,11 @@ export async function migrateDefaultStatusesBase<T extends Task> (
|
||||
}
|
||||
|
||||
counter++
|
||||
await client.update(DOMAIN_TX, { _id: ptsUpdate._id }, { $set: { 'operations.$push.statuses': newPushStatus } })
|
||||
await client.update(
|
||||
DOMAIN_MODEL_TX,
|
||||
{ _id: ptsUpdate._id },
|
||||
{ $set: { 'operations.$push.statuses': newPushStatus } }
|
||||
)
|
||||
}
|
||||
logger.log('projectTypeStatusesPushes updated: ', counter)
|
||||
|
||||
@@ -371,7 +375,7 @@ export async function migrateDefaultStatusesBase<T extends Task> (
|
||||
// 1. Update create TX
|
||||
// 2. Update all update TXes with statuses
|
||||
|
||||
const allTaskTypes = await client.find<TxCreateDoc<TaskType>>(DOMAIN_TX, {
|
||||
const allTaskTypes = await client.find<TxCreateDoc<TaskType>>(DOMAIN_MODEL_TX, {
|
||||
_class: core.class.TxCreateDoc,
|
||||
objectClass: taskTypeClass,
|
||||
'attributes.ofClass': { $in: baseTaskClasses }
|
||||
@@ -388,11 +392,15 @@ export async function migrateDefaultStatusesBase<T extends Task> (
|
||||
}
|
||||
|
||||
counter++
|
||||
await client.update(DOMAIN_TX, { _id: taskType._id }, { $set: { 'attributes.statuses': newTaskTypeStatuses } })
|
||||
await client.update(
|
||||
DOMAIN_MODEL_TX,
|
||||
{ _id: taskType._id },
|
||||
{ $set: { 'attributes.statuses': newTaskTypeStatuses } }
|
||||
)
|
||||
}
|
||||
logger.log('allTaskTypes updated: ', counter)
|
||||
|
||||
const allTaskTypeStatusesUpdates = await client.find<TxUpdateDoc<TaskType>>(DOMAIN_TX, {
|
||||
const allTaskTypeStatusesUpdates = await client.find<TxUpdateDoc<TaskType>>(DOMAIN_MODEL_TX, {
|
||||
_class: core.class.TxUpdateDoc,
|
||||
objectClass: taskTypeClass,
|
||||
objectId: { $in: allTaskTypes.map((tt) => tt.objectId) },
|
||||
@@ -414,7 +422,7 @@ export async function migrateDefaultStatusesBase<T extends Task> (
|
||||
|
||||
counter++
|
||||
await client.update(
|
||||
DOMAIN_TX,
|
||||
DOMAIN_MODEL_TX,
|
||||
{ _id: ttsUpdate._id },
|
||||
{ $set: { 'operations.statuses': newTaskTypeUpdateStatuses } }
|
||||
)
|
||||
@@ -447,48 +455,6 @@ export async function migrateDefaultStatusesBase<T extends Task> (
|
||||
}
|
||||
logger.log('affectedBaseTasks updated: ', counter)
|
||||
|
||||
const baseTaskCreateTxes = await client.find<TxCollectionCUD<T, T>>(DOMAIN_TX, {
|
||||
_class: core.class.TxCollectionCUD,
|
||||
'tx._class': core.class.TxCreateDoc,
|
||||
'tx.objectClass': { $in: baseTaskClasses },
|
||||
'tx.attributes.status': { $in: statusIdsBeingMigrated }
|
||||
})
|
||||
|
||||
logger.log('Base task create TXes: ', baseTaskCreateTxes.length)
|
||||
|
||||
counter = 0
|
||||
for (const baseTaskCreateTx of baseTaskCreateTxes) {
|
||||
const tx = baseTaskCreateTx.tx as TxCreateDoc<T>
|
||||
const newStatus = getNewStatus(tx.attributes.status)
|
||||
|
||||
if (newStatus !== tx.attributes.status) {
|
||||
counter++
|
||||
await client.update(DOMAIN_TX, { _id: baseTaskCreateTx._id }, { $set: { 'tx.attributes.status': newStatus } })
|
||||
}
|
||||
}
|
||||
logger.log('Base task create TXes updated: ', counter)
|
||||
|
||||
const baseTaskUpdateTxes = await client.find<TxCollectionCUD<T, T>>(DOMAIN_TX, {
|
||||
_class: core.class.TxCollectionCUD,
|
||||
'tx._class': core.class.TxUpdateDoc,
|
||||
'tx.objectClass': { $in: baseTaskClasses },
|
||||
'tx.operations.status': { $in: statusIdsBeingMigrated }
|
||||
})
|
||||
|
||||
logger.log('Base task update TXes: ', baseTaskUpdateTxes.length)
|
||||
|
||||
counter = 0
|
||||
for (const baseTaskUpdateTx of baseTaskUpdateTxes) {
|
||||
const tx = baseTaskUpdateTx.tx as TxUpdateDoc<T>
|
||||
const newStatus = tx.operations.status !== undefined ? getNewStatus(tx.operations.status) : undefined
|
||||
|
||||
if (newStatus !== tx.operations.status) {
|
||||
counter++
|
||||
await client.update(DOMAIN_TX, { _id: baseTaskUpdateTx._id }, { $set: { 'tx.operations.status': newStatus } })
|
||||
}
|
||||
}
|
||||
logger.log('Base task update TXes updated: ', counter)
|
||||
|
||||
const baseTaskUpdateMessages = await client.find<DocUpdateMessage>(DOMAIN_ACTIVITY, {
|
||||
_class: activity.class.DocUpdateMessage,
|
||||
action: 'update',
|
||||
@@ -588,6 +554,12 @@ export const taskOperation: MigrateOperation = {
|
||||
{ objectId: { $in: missing }, objectSpace: 'task:space:Statuses' },
|
||||
{ $set: { objectSpace: core.space.Model } }
|
||||
)
|
||||
await client.update(
|
||||
DOMAIN_MODEL_TX,
|
||||
{ objectId: { $in: missing }, objectSpace: 'task:space:Statuses' },
|
||||
{ $set: { objectSpace: core.space.Model } }
|
||||
)
|
||||
await client.move(DOMAIN_TX, { objectId: { $in: missing }, objectSpace: core.space.Model }, DOMAIN_MODEL_TX)
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -119,7 +119,7 @@ function defineApplication (builder: Builder): void {
|
||||
}
|
||||
]
|
||||
},
|
||||
navHeaderComponent: testManagement.component.NewTestCaseHeader
|
||||
navHeaderComponent: testManagement.component.TestManagementSpaceHeader
|
||||
},
|
||||
testManagement.app.TestManagement
|
||||
)
|
||||
@@ -143,7 +143,7 @@ export function createModel (builder: Builder): void {
|
||||
|
||||
builder.createDoc(activity.class.ActivityExtension, core.space.Model, {
|
||||
ofClass: testManagement.class.TestProject,
|
||||
components: { input: chunter.component.ChatMessageInput }
|
||||
components: { input: { component: chunter.component.ChatMessageInput } }
|
||||
})
|
||||
|
||||
defineTestSuite(builder)
|
||||
@@ -218,7 +218,7 @@ function defineTestSuite (builder: Builder): void {
|
||||
|
||||
builder.createDoc(activity.class.ActivityExtension, core.space.Model, {
|
||||
ofClass: testManagement.class.TestSuite,
|
||||
components: { input: chunter.component.ChatMessageInput }
|
||||
components: { input: { component: chunter.component.ChatMessageInput } }
|
||||
})
|
||||
|
||||
builder.mixin(testManagement.class.TestSuite, core.class.Class, view.mixin.ObjectEditor, {
|
||||
@@ -283,7 +283,7 @@ function defineTestCase (builder: Builder): void {
|
||||
|
||||
builder.createDoc(activity.class.ActivityExtension, core.space.Model, {
|
||||
ofClass: testManagement.class.TestCase,
|
||||
components: { input: chunter.component.ChatMessageInput }
|
||||
components: { input: { component: chunter.component.ChatMessageInput } }
|
||||
})
|
||||
|
||||
builder.mixin(testManagement.class.TestCase, core.class.Class, view.mixin.ObjectEditor, {
|
||||
@@ -388,7 +388,7 @@ function defineTestRun (builder: Builder): void {
|
||||
|
||||
builder.createDoc(activity.class.ActivityExtension, core.space.Model, {
|
||||
ofClass: testManagement.class.TestRun,
|
||||
components: { input: chunter.component.ChatMessageInput }
|
||||
components: { input: { component: chunter.component.ChatMessageInput } }
|
||||
})
|
||||
|
||||
builder.mixin(testManagement.class.TestRun, core.class.Class, view.mixin.ObjectEditor, {
|
||||
|
||||
@@ -461,22 +461,22 @@ export function createModel (builder: Builder): void {
|
||||
|
||||
builder.createDoc(activity.class.ActivityExtension, core.space.Model, {
|
||||
ofClass: tracker.class.Issue,
|
||||
components: { input: chunter.component.ChatMessageInput }
|
||||
components: { input: { component: chunter.component.ChatMessageInput } }
|
||||
})
|
||||
|
||||
builder.createDoc(activity.class.ActivityExtension, core.space.Model, {
|
||||
ofClass: tracker.class.Milestone,
|
||||
components: { input: chunter.component.ChatMessageInput }
|
||||
components: { input: { component: chunter.component.ChatMessageInput } }
|
||||
})
|
||||
|
||||
builder.createDoc(activity.class.ActivityExtension, core.space.Model, {
|
||||
ofClass: tracker.class.Component,
|
||||
components: { input: chunter.component.ChatMessageInput }
|
||||
components: { input: { component: chunter.component.ChatMessageInput } }
|
||||
})
|
||||
|
||||
builder.createDoc(activity.class.ActivityExtension, core.space.Model, {
|
||||
ofClass: tracker.class.IssueTemplate,
|
||||
components: { input: chunter.component.ChatMessageInput }
|
||||
components: { input: { component: chunter.component.ChatMessageInput } }
|
||||
})
|
||||
|
||||
defineViewlets(builder)
|
||||
|
||||
@@ -13,37 +13,37 @@
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import activity, { type DocUpdateMessage } from '@hcengineering/activity'
|
||||
import core, {
|
||||
AccountRole,
|
||||
DOMAIN_MODEL_TX,
|
||||
DOMAIN_STATUS,
|
||||
type Ref,
|
||||
type Status,
|
||||
type TxCreateDoc,
|
||||
TxOperations,
|
||||
generateId,
|
||||
toIdMap,
|
||||
DOMAIN_TX,
|
||||
type Status,
|
||||
type Ref,
|
||||
AccountRole
|
||||
toIdMap
|
||||
} from '@hcengineering/core'
|
||||
import {
|
||||
type MigrateOperation,
|
||||
type MigrationClient,
|
||||
type MigrationUpgradeClient,
|
||||
type ModelLogger,
|
||||
createOrUpdate,
|
||||
tryMigrate,
|
||||
tryUpgrade,
|
||||
type MigrateOperation,
|
||||
type MigrationClient,
|
||||
type MigrationUpgradeClient
|
||||
tryUpgrade
|
||||
} from '@hcengineering/model'
|
||||
import { DOMAIN_SPACE } from '@hcengineering/model-core'
|
||||
import activity, { type DocUpdateMessage } from '@hcengineering/activity'
|
||||
import { DOMAIN_ACTIVITY } from '@hcengineering/model-activity'
|
||||
import { DOMAIN_SPACE } from '@hcengineering/model-core'
|
||||
import { DOMAIN_TASK, migrateDefaultStatusesBase } from '@hcengineering/model-task'
|
||||
import tags from '@hcengineering/tags'
|
||||
import task from '@hcengineering/task'
|
||||
import { type IssueStatus, TimeReportDayType, trackerId, type Issue, type Project } from '@hcengineering/tracker'
|
||||
import { type Issue, type IssueStatus, type Project, TimeReportDayType, trackerId } from '@hcengineering/tracker'
|
||||
|
||||
import tracker from './plugin'
|
||||
import contact from '@hcengineering/model-contact'
|
||||
import { classicIssueTaskStatuses } from '.'
|
||||
import tracker from './plugin'
|
||||
|
||||
async function createDefaultProject (tx: TxOperations): Promise<void> {
|
||||
const current = await tx.findOne(tracker.class.Project, {
|
||||
@@ -280,7 +280,7 @@ async function migrateStatusesToModel (client: MigrationClient): Promise<void> {
|
||||
modifiedBy
|
||||
}
|
||||
|
||||
await client.create(DOMAIN_TX, tx)
|
||||
await client.create(DOMAIN_MODEL_TX, tx)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -291,7 +291,7 @@ async function migrateDefaultTypeMixins (client: MigrationClient): Promise<void>
|
||||
const newTaskTypeMixin = tracker.mixin.IssueTypeData
|
||||
|
||||
await client.update(
|
||||
DOMAIN_TX,
|
||||
DOMAIN_MODEL_TX,
|
||||
{
|
||||
objectClass: core.class.Attribute,
|
||||
'attributes.attributeOf': oldSpaceTypeMixin
|
||||
@@ -350,7 +350,7 @@ async function migrateDefaultProjectOwners (client: MigrationClient): Promise<vo
|
||||
|
||||
async function migrateIssueStatuses (client: MigrationClient): Promise<void> {
|
||||
await client.update(
|
||||
DOMAIN_TX,
|
||||
DOMAIN_MODEL_TX,
|
||||
{
|
||||
objectClass: task.class.TaskType,
|
||||
'attributes.ofClass': tracker.class.Issue,
|
||||
@@ -363,7 +363,7 @@ async function migrateIssueStatuses (client: MigrationClient): Promise<void> {
|
||||
}
|
||||
)
|
||||
await client.update(
|
||||
DOMAIN_TX,
|
||||
DOMAIN_MODEL_TX,
|
||||
{
|
||||
objectClass: core.class.Status,
|
||||
'attributes.ofAttribute': tracker.attribute.IssueStatus
|
||||
|
||||
@@ -67,6 +67,7 @@ import {
|
||||
type ListItemPresenter,
|
||||
type ObjectEditor,
|
||||
type ObjectEditorFooter,
|
||||
type ObjectPanelFooter,
|
||||
type ObjectEditorHeader,
|
||||
type ObjectFactory,
|
||||
type ObjectPanel,
|
||||
@@ -216,6 +217,11 @@ export class TObjectEditorFooter extends TClass implements ObjectEditorFooter {
|
||||
editor!: AnyComponent
|
||||
}
|
||||
|
||||
@Mixin(view.mixin.ObjectPanelFooter, core.class.Class)
|
||||
export class TObjectPanelFooter extends TClass implements ObjectPanelFooter {
|
||||
editor!: AnyComponent
|
||||
}
|
||||
|
||||
@Mixin(view.mixin.SpaceHeader, core.class.Class)
|
||||
export class TSpaceHeader extends TClass implements SpaceHeader {
|
||||
header!: AnyComponent
|
||||
@@ -445,6 +451,7 @@ export function createModel (builder: Builder): void {
|
||||
TObjectTitle,
|
||||
TObjectEditorHeader,
|
||||
TObjectEditorFooter,
|
||||
TObjectPanelFooter,
|
||||
TSpaceHeader,
|
||||
TSpaceName,
|
||||
TSpacePresenter,
|
||||
|
||||
@@ -7,8 +7,6 @@ import { basename, dirname, join } from 'path'
|
||||
import winston from 'winston'
|
||||
import DailyRotateFile from 'winston-daily-rotate-file'
|
||||
|
||||
const PLATFORM_OPERATION_LOGGING = process.env.PLATFORM_OPERATION_LOGGING === 'true'
|
||||
|
||||
export class SplitLogger implements MeasureLogger {
|
||||
logger: winston.Logger
|
||||
|
||||
@@ -110,9 +108,7 @@ export class SplitLogger implements MeasureLogger {
|
||||
}
|
||||
|
||||
logOperation (operation: string, time: number, params: ParamsType): void {
|
||||
if (PLATFORM_OPERATION_LOGGING) {
|
||||
this.logger.info({ operation, time, ...params })
|
||||
}
|
||||
this.logger.info(operation, { time, ...params })
|
||||
}
|
||||
|
||||
childLogger (name: string, params: Record<string, string>): MeasureLogger {
|
||||
|
||||
@@ -189,13 +189,6 @@ export function genMinModel (): TxCUD<Doc>[] {
|
||||
kind: ClassifierKind.CLASS
|
||||
})
|
||||
)
|
||||
txes.push(
|
||||
createClass(core.class.TxCollectionCUD, {
|
||||
label: 'TxCollectionCUD' as IntlString,
|
||||
extends: core.class.TxCUD,
|
||||
kind: ClassifierKind.CLASS
|
||||
})
|
||||
)
|
||||
|
||||
txes.push(
|
||||
createClass(core.class.Blob, {
|
||||
|
||||
@@ -326,6 +326,11 @@ export interface TypeAny<AnyComponent = any> extends Type<any> {
|
||||
*/
|
||||
export const DOMAIN_MODEL = 'model' as Domain
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export const DOMAIN_MODEL_TX = 'model_tx' as Domain
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
|
||||
@@ -15,14 +15,14 @@
|
||||
|
||||
import { Analytics } from '@hcengineering/analytics'
|
||||
import { BackupClient, DocChunk } from './backup'
|
||||
import { Account, AttachedDoc, Class, DOMAIN_MODEL, Doc, Domain, Ref, Timestamp } from './classes'
|
||||
import { Account, Class, DOMAIN_MODEL, Doc, Domain, Ref, Timestamp } from './classes'
|
||||
import core from './component'
|
||||
import { Hierarchy } from './hierarchy'
|
||||
import { MeasureContext, MeasureMetricsContext } from './measurements'
|
||||
import { ModelDb } from './memdb'
|
||||
import type { DocumentQuery, FindOptions, FindResult, FulltextStorage, Storage, TxResult, WithLookup } from './storage'
|
||||
import { SearchOptions, SearchQuery, SearchResult, SortingOrder } from './storage'
|
||||
import { Tx, TxCUD, TxCollectionCUD } from './tx'
|
||||
import { Tx, TxCUD } from './tx'
|
||||
import { toFindResult } from './utils'
|
||||
|
||||
const transactionThreshold = 500
|
||||
@@ -307,9 +307,8 @@ export async function createClient (
|
||||
// if we have attachment document create/delete we need to full refresh, since some derived data could be missing
|
||||
for (const tx of atxes) {
|
||||
if (
|
||||
tx._class === core.class.TxCollectionCUD &&
|
||||
((tx as TxCollectionCUD<Doc, AttachedDoc>).tx._class === core.class.TxCreateDoc ||
|
||||
(tx as TxCollectionCUD<Doc, AttachedDoc>).tx._class === core.class.TxRemoveDoc)
|
||||
(tx as TxCUD<Doc>).attachedTo !== undefined &&
|
||||
(tx._class === core.class.TxCreateDoc || tx._class === core.class.TxRemoveDoc)
|
||||
) {
|
||||
needFullRefresh = true
|
||||
break
|
||||
|
||||
@@ -60,7 +60,6 @@ import type {
|
||||
Tx,
|
||||
TxApplyIf,
|
||||
TxCUD,
|
||||
TxCollectionCUD,
|
||||
TxCreateDoc,
|
||||
TxMixin,
|
||||
TxModelUpgrade,
|
||||
@@ -97,7 +96,6 @@ export default plugin(coreId, {
|
||||
TxApplyIf: '' as Ref<Class<TxApplyIf>>,
|
||||
TxCUD: '' as Ref<Class<TxCUD<Doc>>>,
|
||||
TxCreateDoc: '' as Ref<Class<TxCreateDoc<Doc>>>,
|
||||
TxCollectionCUD: '' as Ref<Class<TxCollectionCUD<Doc, AttachedDoc>>>,
|
||||
TxMixin: '' as Ref<Class<TxMixin<Doc, Doc>>>,
|
||||
TxUpdateDoc: '' as Ref<Class<TxUpdateDoc<Doc>>>,
|
||||
TxRemoveDoc: '' as Ref<Class<TxRemoveDoc<Doc>>>,
|
||||
|
||||
@@ -64,22 +64,7 @@ export class MeasureMetricsContext implements MeasureContext {
|
||||
st = Date.now()
|
||||
contextData: object = {}
|
||||
private done (value?: number, override?: boolean): void {
|
||||
updateMeasure(
|
||||
this.metrics,
|
||||
this.st,
|
||||
this.params,
|
||||
this.fullParams,
|
||||
(spend) => {
|
||||
this.logger.logOperation(this.name, spend, {
|
||||
...this.params,
|
||||
...(typeof this.fullParams === 'function' ? this.fullParams() : this.fullParams),
|
||||
...this.fullParams,
|
||||
...(this.logParams ?? {})
|
||||
})
|
||||
},
|
||||
value,
|
||||
override
|
||||
)
|
||||
updateMeasure(this.metrics, this.st, this.params, this.fullParams, (spend) => {}, value, override)
|
||||
}
|
||||
|
||||
constructor (
|
||||
@@ -144,7 +129,7 @@ export class MeasureMetricsContext implements MeasureContext {
|
||||
let needFinally = true
|
||||
try {
|
||||
const value = op(c)
|
||||
if (value != null && value instanceof Promise) {
|
||||
if (value instanceof Promise) {
|
||||
needFinally = false
|
||||
return value.finally(() => {
|
||||
c.end()
|
||||
|
||||
@@ -15,12 +15,12 @@
|
||||
|
||||
import { PlatformError, Severity, Status } from '@hcengineering/platform'
|
||||
import { Lookup, MeasureContext, ReverseLookups, getObjectValue } from '.'
|
||||
import type { Account, AttachedDoc, Class, Doc, Ref } from './classes'
|
||||
import type { Account, Class, Doc, Ref } from './classes'
|
||||
import core from './component'
|
||||
import { Hierarchy } from './hierarchy'
|
||||
import { checkMixinKey, matchQuery, resultSort } from './query'
|
||||
import type { DocumentQuery, FindOptions, FindResult, LookupData, Storage, TxResult, WithLookup } from './storage'
|
||||
import type { Tx, TxCollectionCUD, TxCreateDoc, TxMixin, TxRemoveDoc, TxUpdateDoc } from './tx'
|
||||
import type { Tx, TxCreateDoc, TxMixin, TxRemoveDoc, TxUpdateDoc } from './tx'
|
||||
import { TxProcessor } from './tx'
|
||||
import { toFindResult } from './utils'
|
||||
|
||||
@@ -363,25 +363,6 @@ export class ModelDb extends MemDb {
|
||||
case core.class.TxCreateDoc:
|
||||
this.addDoc(TxProcessor.createDoc2Doc(tx as TxCreateDoc<Doc>, clone))
|
||||
break
|
||||
case core.class.TxCollectionCUD: {
|
||||
// We need update only create transactions to contain attached, attachedToClass.
|
||||
const cud = tx as TxCollectionCUD<Doc, AttachedDoc<Doc>>
|
||||
if (cud.tx._class === core.class.TxCreateDoc) {
|
||||
const createTx = cud.tx as TxCreateDoc<AttachedDoc>
|
||||
const d: TxCreateDoc<AttachedDoc> = {
|
||||
...createTx,
|
||||
attributes: {
|
||||
...createTx.attributes,
|
||||
attachedTo: cud.objectId,
|
||||
attachedToClass: cud.objectClass,
|
||||
collection: cud.collection
|
||||
}
|
||||
}
|
||||
this.addDoc(TxProcessor.createDoc2Doc(d as TxCreateDoc<Doc>, clone))
|
||||
}
|
||||
this.addTxes(ctx, [cud.tx], clone)
|
||||
break
|
||||
}
|
||||
case core.class.TxUpdateDoc: {
|
||||
const cud = tx as TxUpdateDoc<Doc>
|
||||
const doc = this.findObject(cud.objectId)
|
||||
|
||||
@@ -123,7 +123,7 @@ export class TxOperations implements Omit<Client, 'notify'> {
|
||||
modifiedBy
|
||||
)
|
||||
await this.tx(tx)
|
||||
return tx.tx.objectId as unknown as Ref<P>
|
||||
return tx.objectId as unknown as Ref<P>
|
||||
}
|
||||
|
||||
async updateCollection<T extends Doc, P extends AttachedDoc>(
|
||||
@@ -148,7 +148,7 @@ export class TxOperations implements Omit<Client, 'notify'> {
|
||||
modifiedBy
|
||||
)
|
||||
await this.tx(tx)
|
||||
return tx.objectId
|
||||
return attachedTo
|
||||
}
|
||||
|
||||
async removeCollection<T extends Doc, P extends AttachedDoc>(
|
||||
@@ -171,7 +171,7 @@ export class TxOperations implements Omit<Client, 'notify'> {
|
||||
modifiedBy
|
||||
)
|
||||
await this.tx(tx)
|
||||
return tx.objectId
|
||||
return attachedTo
|
||||
}
|
||||
|
||||
updateDoc<T extends Doc>(
|
||||
|
||||
+21
-73
@@ -88,6 +88,9 @@ export interface TxModelUpgrade extends Tx {}
|
||||
export interface TxCUD<T extends Doc> extends Tx {
|
||||
objectId: Ref<T>
|
||||
objectClass: Ref<Class<T>>
|
||||
attachedTo?: Ref<Doc>
|
||||
attachedToClass?: Ref<Class<Doc>>
|
||||
collection?: string
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -97,17 +100,6 @@ export interface TxCreateDoc<T extends Doc> extends TxCUD<T> {
|
||||
attributes: Data<T>
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Will perform create/update/delete of attached documents.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export interface TxCollectionCUD<T extends Doc, P extends AttachedDoc> extends TxCUD<T> {
|
||||
collection: string
|
||||
tx: TxCUD<P>
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
@@ -327,9 +319,6 @@ export abstract class TxProcessor implements WithTx {
|
||||
case core.class.TxCreateDoc:
|
||||
result.push(await this.txCreateDoc(tx as TxCreateDoc<Doc>))
|
||||
break
|
||||
case core.class.TxCollectionCUD:
|
||||
result.push(await this.txCollectionCUD(tx as TxCollectionCUD<Doc, AttachedDoc>))
|
||||
break
|
||||
case core.class.TxUpdateDoc:
|
||||
result.push(await this.txUpdateDoc(tx as TxUpdateDoc<Doc>))
|
||||
break
|
||||
@@ -348,9 +337,19 @@ export abstract class TxProcessor implements WithTx {
|
||||
}
|
||||
|
||||
static createDoc2Doc<T extends Doc>(tx: TxCreateDoc<T>, doClone = true): T {
|
||||
const attached =
|
||||
tx.attachedTo !== undefined
|
||||
? {
|
||||
attachedTo: tx.attachedTo,
|
||||
attachedToClass: tx.attachedToClass,
|
||||
collection: tx.collection
|
||||
}
|
||||
: {}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
||||
return {
|
||||
...(doClone ? clone(tx.attributes) : tx.attributes),
|
||||
...attached,
|
||||
_id: tx.objectId,
|
||||
_class: tx.objectClass,
|
||||
space: tx.objectSpace,
|
||||
@@ -400,22 +399,10 @@ export abstract class TxProcessor implements WithTx {
|
||||
|
||||
static buildDoc2Doc<D extends Doc>(txes: Tx[]): D | undefined {
|
||||
let doc: Doc
|
||||
let createTx = txes.find((tx) => tx._class === core.class.TxCreateDoc)
|
||||
if (createTx === undefined) {
|
||||
const collectionTxes = txes.filter((tx) => tx._class === core.class.TxCollectionCUD) as Array<
|
||||
TxCollectionCUD<Doc, AttachedDoc>
|
||||
>
|
||||
const collectionCreateTx = collectionTxes.find((p) => p.tx._class === core.class.TxCreateDoc)
|
||||
if (collectionCreateTx === undefined) return
|
||||
createTx = TxProcessor.extractTx(collectionCreateTx)
|
||||
}
|
||||
const createTx = txes.find((tx) => tx._class === core.class.TxCreateDoc)
|
||||
if (createTx === undefined) return
|
||||
const objectId = (createTx as TxCreateDoc<D>).objectId
|
||||
doc = TxProcessor.createDoc2Doc(createTx as TxCreateDoc<Doc>)
|
||||
for (let tx of txes) {
|
||||
if ((tx as TxCUD<D>).objectId !== objectId && tx._class === core.class.TxCollectionCUD) {
|
||||
tx = TxProcessor.extractTx(tx)
|
||||
}
|
||||
for (const tx of txes) {
|
||||
if (tx._class === core.class.TxUpdateDoc) {
|
||||
doc = TxProcessor.updateDoc2Doc(doc, tx as TxUpdateDoc<Doc>)
|
||||
} else if (tx._class === core.class.TxMixin) {
|
||||
@@ -431,27 +418,10 @@ export abstract class TxProcessor implements WithTx {
|
||||
_class === core.class.TxCreateDoc ||
|
||||
_class === core.class.TxUpdateDoc ||
|
||||
_class === core.class.TxRemoveDoc ||
|
||||
_class === core.class.TxCollectionCUD ||
|
||||
_class === core.class.TxMixin
|
||||
)
|
||||
}
|
||||
|
||||
static extractTx (tx: Tx): Tx {
|
||||
if (tx._class === core.class.TxCollectionCUD) {
|
||||
const ctx = tx as TxCollectionCUD<Doc, AttachedDoc>
|
||||
if (ctx.tx._class === core.class.TxCreateDoc) {
|
||||
const create = ctx.tx as TxCreateDoc<AttachedDoc>
|
||||
create.attributes.attachedTo = ctx.objectId
|
||||
create.attributes.attachedToClass = ctx.objectClass
|
||||
create.attributes.collection = ctx.collection
|
||||
return create
|
||||
}
|
||||
return ctx.tx
|
||||
}
|
||||
|
||||
return tx
|
||||
}
|
||||
|
||||
static txHasUpdate<T extends Doc>(tx: TxUpdateDoc<T>, attribute: string): boolean {
|
||||
const ops = tx.operations
|
||||
if ((ops as any)[attribute] !== undefined) return true
|
||||
@@ -472,24 +442,6 @@ export abstract class TxProcessor implements WithTx {
|
||||
protected abstract txUpdateDoc (tx: TxUpdateDoc<Doc>): Promise<TxResult>
|
||||
protected abstract txRemoveDoc (tx: TxRemoveDoc<Doc>): Promise<TxResult>
|
||||
protected abstract txMixin (tx: TxMixin<Doc, Doc>): Promise<TxResult>
|
||||
|
||||
protected txCollectionCUD (tx: TxCollectionCUD<Doc, AttachedDoc>): Promise<TxResult> {
|
||||
// We need update only create transactions to contain attached, attachedToClass.
|
||||
if (tx.tx._class === core.class.TxCreateDoc) {
|
||||
const createTx = tx.tx as TxCreateDoc<AttachedDoc>
|
||||
const d: TxCreateDoc<AttachedDoc> = {
|
||||
...createTx,
|
||||
attributes: {
|
||||
...createTx.attributes,
|
||||
attachedTo: tx.objectId,
|
||||
attachedToClass: tx.objectClass,
|
||||
collection: tx.collection
|
||||
}
|
||||
}
|
||||
return this.txCreateDoc(d)
|
||||
}
|
||||
return this.tx(tx.tx)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -534,18 +486,14 @@ export class TxFactory {
|
||||
tx: TxCUD<P>,
|
||||
modifiedOn?: Timestamp,
|
||||
modifiedBy?: Ref<Account>
|
||||
): TxCollectionCUD<T, P> {
|
||||
): TxCUD<P> {
|
||||
return {
|
||||
_id: generateId(),
|
||||
_class: core.class.TxCollectionCUD,
|
||||
space: this.txSpace,
|
||||
objectId,
|
||||
objectClass: _class,
|
||||
objectSpace: space,
|
||||
modifiedOn: modifiedOn ?? Date.now(),
|
||||
modifiedBy: modifiedBy ?? this.account,
|
||||
...tx,
|
||||
collection,
|
||||
tx
|
||||
attachedTo: objectId,
|
||||
attachedToClass: _class,
|
||||
modifiedOn: modifiedOn ?? Date.now(),
|
||||
modifiedBy: modifiedBy ?? this.account
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -158,6 +158,11 @@
|
||||
<svelte:fragment slot="pre-utils">
|
||||
<slot name="pre-utils" />
|
||||
</svelte:fragment>
|
||||
|
||||
<svelte:fragment slot="panel-footer">
|
||||
<slot name="panel-footer" />
|
||||
</svelte:fragment>
|
||||
|
||||
<svelte:fragment slot="utils">
|
||||
{#if isUtils && $$slots.utils}
|
||||
<slot name="utils" />
|
||||
|
||||
@@ -93,6 +93,11 @@
|
||||
user-select: none;
|
||||
box-shadow: var(--theme-popup-shadow);
|
||||
|
||||
@media screen and (max-width: 480px) {
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.message {
|
||||
margin-bottom: 1.75rem;
|
||||
color: var(--theme-content-color);
|
||||
|
||||
@@ -119,7 +119,7 @@ class UIClient extends TxOperations implements Client, OptimisticTxes {
|
||||
this.pendingTxes.delete(t._id)
|
||||
|
||||
// Only CUD tx can be pending now
|
||||
const innerTx = TxProcessor.extractTx(t) as TxCUD<Doc>
|
||||
const innerTx = t as TxCUD<Doc>
|
||||
|
||||
if (innerTx._class === core.class.TxCreateDoc) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
|
||||
@@ -206,7 +206,7 @@ class UIClient extends TxOperations implements Client, OptimisticTxes {
|
||||
return
|
||||
}
|
||||
|
||||
const innerTx = TxProcessor.extractTx(tx) as TxCUD<Doc>
|
||||
const innerTx = tx as TxCUD<Doc>
|
||||
// Can pre-build some configuration later from the model if this will be too slow.
|
||||
const instantTxes = this.getHierarchy().classHierarchyMixin(innerTx.objectClass, plugin.mixin.InstantTransactions)
|
||||
if (instantTxes?.txClasses.includes(innerTx._class) !== true) {
|
||||
@@ -626,7 +626,7 @@ export async function getAttributeEditor (
|
||||
|
||||
function filterKeys (hierarchy: Hierarchy, keys: KeyedAttribute[], ignoreKeys: string[]): KeyedAttribute[] {
|
||||
const docKeys: Set<string> = new Set<string>(hierarchy.getAllAttributes(core.class.AttachedDoc).keys())
|
||||
keys = keys.filter((k) => !docKeys.has(k.key))
|
||||
keys = keys.filter((k) => !docKeys.has(k.key) || k.attr.editor !== undefined)
|
||||
keys = keys.filter((k) => !ignoreKeys.includes(k.key))
|
||||
return keys
|
||||
}
|
||||
|
||||
@@ -188,13 +188,6 @@ export function genMinModel (): TxCUD<Doc>[] {
|
||||
kind: ClassifierKind.CLASS
|
||||
})
|
||||
)
|
||||
txes.push(
|
||||
createClass(core.class.TxCollectionCUD, {
|
||||
label: 'TxCollectionCUD' as IntlString,
|
||||
extends: core.class.TxCUD,
|
||||
kind: ClassifierKind.CLASS
|
||||
})
|
||||
)
|
||||
txes.push(
|
||||
createClass(core.class.TxMixin, {
|
||||
label: 'TxMixin' as IntlString,
|
||||
|
||||
@@ -677,24 +677,33 @@ describe('query', () => {
|
||||
)
|
||||
)
|
||||
}
|
||||
const pp = new Promise((resolve) => {
|
||||
liveQuery.query<AttachedComment>(
|
||||
test.class.TestComment,
|
||||
{ _id: parentComment },
|
||||
(result) => {
|
||||
attempt++
|
||||
const comment = result[0]
|
||||
if (comment !== undefined) {
|
||||
expect((comment.$lookup as any)?.comments).toHaveLength(childLength - attempt)
|
||||
}
|
||||
if (attempt === childLength) {
|
||||
resolve(null)
|
||||
}
|
||||
},
|
||||
{ lookup: { _id: { comments: test.class.TestComment } } }
|
||||
)
|
||||
|
||||
let secondPromise: Promise<void> | undefined
|
||||
const firstCallback = new Promise<void>((resolve) => {
|
||||
secondPromise = new Promise<void>((_resolve) => {
|
||||
liveQuery.query<AttachedComment>(
|
||||
test.class.TestComment,
|
||||
{ _id: parentComment },
|
||||
(result) => {
|
||||
attempt++
|
||||
if (attempt === 0) {
|
||||
resolve()
|
||||
}
|
||||
const comment = result[0]
|
||||
if (comment !== undefined) {
|
||||
expect((comment.$lookup as any)?.comments).toHaveLength(childLength - attempt)
|
||||
}
|
||||
if (attempt === childLength) {
|
||||
_resolve()
|
||||
}
|
||||
},
|
||||
{ lookup: { _id: { comments: test.class.TestComment } } }
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
await firstCallback
|
||||
|
||||
for (const child of childs) {
|
||||
await factory.removeCollection(
|
||||
test.class.TestComment,
|
||||
@@ -705,7 +714,7 @@ describe('query', () => {
|
||||
'comments'
|
||||
)
|
||||
}
|
||||
await pp
|
||||
await secondPromise
|
||||
})
|
||||
|
||||
it('lookup query update doc', async () => {
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
|
||||
import { Analytics } from '@hcengineering/analytics'
|
||||
import core, {
|
||||
AttachedDoc,
|
||||
BulkUpdateEvent,
|
||||
Class,
|
||||
Client,
|
||||
@@ -38,7 +37,6 @@ import core, {
|
||||
Space,
|
||||
Timestamp,
|
||||
Tx,
|
||||
TxCollectionCUD,
|
||||
TxCreateDoc,
|
||||
TxMixin,
|
||||
TxProcessor,
|
||||
@@ -672,41 +670,6 @@ export class LiveQuery implements WithTx, Client {
|
||||
return {}
|
||||
}
|
||||
|
||||
protected async txCollectionCUD (
|
||||
tx: TxCollectionCUD<Doc, AttachedDoc>,
|
||||
docCache: Map<string, Doc>
|
||||
): Promise<TxResult> {
|
||||
for (const queries of this.queries) {
|
||||
const isTx = this.client.getHierarchy().isDerived(queries[0], core.class.Tx)
|
||||
for (const q of queries[1]) {
|
||||
if (isTx) {
|
||||
// handle add since Txes are immutable
|
||||
await this.handleDocAdd(q, tx, true, docCache)
|
||||
continue
|
||||
}
|
||||
|
||||
if (tx.tx._class === core.class.TxCreateDoc) {
|
||||
const createTx = tx.tx as TxCreateDoc<AttachedDoc>
|
||||
const d: TxCreateDoc<AttachedDoc> = {
|
||||
...createTx,
|
||||
attributes: {
|
||||
...createTx.attributes,
|
||||
attachedTo: tx.objectId,
|
||||
attachedToClass: tx.objectClass,
|
||||
collection: tx.collection
|
||||
}
|
||||
}
|
||||
await this.handleDocAdd(q, TxProcessor.createDoc2Doc(d), true, docCache)
|
||||
} else if (tx.tx._class === core.class.TxUpdateDoc) {
|
||||
await this.handleDocUpdate(q, tx.tx as unknown as TxUpdateDoc<Doc>, docCache)
|
||||
} else if (tx.tx._class === core.class.TxRemoveDoc) {
|
||||
await this.handleDocRemove(q, tx.tx as unknown as TxRemoveDoc<Doc>)
|
||||
}
|
||||
}
|
||||
}
|
||||
return {}
|
||||
}
|
||||
|
||||
async txUpdateDoc (tx: TxUpdateDoc<Doc>, docCache: Map<string, Doc>): Promise<TxResult> {
|
||||
for (const queries of this.queries) {
|
||||
const isTx = this.client.getHierarchy().isDerived(queries[0], core.class.Tx)
|
||||
@@ -1278,8 +1241,6 @@ export class LiveQuery implements WithTx, Client {
|
||||
switch (tx._class) {
|
||||
case core.class.TxCreateDoc:
|
||||
return await this.txCreateDoc(tx as TxCreateDoc<Doc>, docCache)
|
||||
case core.class.TxCollectionCUD:
|
||||
return await this.txCollectionCUD(tx as TxCollectionCUD<Doc, AttachedDoc>, docCache)
|
||||
case core.class.TxUpdateDoc:
|
||||
return await this.txUpdateDoc(tx as TxUpdateDoc<Doc>, docCache)
|
||||
case core.class.TxRemoveDoc:
|
||||
|
||||
@@ -900,6 +900,7 @@ a.no-line {
|
||||
.leading-3 { line-height: .75rem; }
|
||||
.tracking--05px { letter-spacing: -.5px; }
|
||||
.tracking-1px { letter-spacing: 1px; }
|
||||
.text-balance { text-wrap: balance; }
|
||||
|
||||
.over-underline {
|
||||
cursor: pointer;
|
||||
|
||||
@@ -31,6 +31,8 @@
|
||||
|
||||
--tag-on-accent-PorpoiseText: #FFFFFF;
|
||||
--tag-accent-SunshineBackground: #FFBD2E;
|
||||
|
||||
--border-color-global-error-border-color: #fb6863;
|
||||
}
|
||||
|
||||
/* Dark Theme */
|
||||
|
||||
@@ -444,4 +444,12 @@
|
||||
&.delete {
|
||||
background: var(--text-editor-highlighted-node-delete-background-color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Hiding the selection of an empty line
|
||||
.select-text p > br.ProseMirror-trailingBreak::selection {
|
||||
background: transparent;
|
||||
}
|
||||
.select-text .text-editor-image-container {
|
||||
user-select: all;
|
||||
}
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
--spacing-4: 2rem;
|
||||
--spacing-4_5: 2.25rem;
|
||||
--spacing-5: 2.5rem;
|
||||
--spacing-5_5: 2.75rem;
|
||||
--spacing-6: 3rem;
|
||||
--spacing-6_5: 3.5rem;
|
||||
--spacing-7: 4rem;
|
||||
|
||||
@@ -543,6 +543,11 @@
|
||||
.hulyModal-footer {
|
||||
padding: var(--spacing-1_5);
|
||||
}
|
||||
@media screen and (max-width: 480px) {
|
||||
width: 100vw;
|
||||
height: var(--app-height);
|
||||
min-width: 20rem;
|
||||
}
|
||||
}
|
||||
&.type-aside,
|
||||
&.type-popup {
|
||||
|
||||
@@ -50,6 +50,7 @@
|
||||
}
|
||||
|
||||
&__title-wrap {
|
||||
overflow-x: auto;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
min-width: 0;
|
||||
@@ -277,6 +278,12 @@
|
||||
max-width: 90vw;
|
||||
max-height: 90vh;
|
||||
|
||||
@media screen and (max-width: 480px) {
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
max-height: 100%;
|
||||
}
|
||||
|
||||
.antiCard-header {
|
||||
padding: .75rem .75rem .375rem;
|
||||
|
||||
|
||||
@@ -0,0 +1,123 @@
|
||||
<script lang="ts">
|
||||
//
|
||||
// © 2023 Hardcore Engineering, Inc. All Rights Reserved.
|
||||
// Licensed under the Eclipse Public License v2.0 (SPDX: EPL-2.0).
|
||||
//
|
||||
import type { IntlString } from '@hcengineering/platform'
|
||||
import { generateId } from '@hcengineering/core'
|
||||
import { Label } from '../..'
|
||||
|
||||
export let id: string = generateId()
|
||||
export let label: string | undefined = undefined
|
||||
export let labelIntl: IntlString | undefined = undefined
|
||||
export let labelParams: Record<string, any> | undefined = undefined
|
||||
export let checked: boolean = false
|
||||
export let indeterminate: boolean = false
|
||||
export let disabled: boolean = false
|
||||
export let error: boolean = false
|
||||
export let required: boolean = false
|
||||
</script>
|
||||
|
||||
<label class="checkbox-container" class:disabled>
|
||||
<input type="checkbox" class="checkbox" {id} bind:checked {disabled} {indeterminate} {required} on:change />
|
||||
<div class="checkbox-element" class:disabled class:error />
|
||||
{#if label !== undefined || labelIntl !== undefined || $$slots.default !== undefined}
|
||||
<div class="checkbox-label">
|
||||
{#if labelIntl}<Label label={labelIntl} params={labelParams} />{:else}{label}{/if}
|
||||
<slot />
|
||||
</div>
|
||||
{/if}
|
||||
<slot name="after" />
|
||||
</label>
|
||||
|
||||
<style lang="scss">
|
||||
.checkbox-element {
|
||||
position: relative;
|
||||
flex-shrink: 0;
|
||||
width: var(--spacing-2);
|
||||
height: var(--spacing-2);
|
||||
background-color: var(--selector-BackgroundColor);
|
||||
border: 1px solid var(--selector-BorderColor);
|
||||
border-radius: var(--extra-small-BorderRadius);
|
||||
}
|
||||
.checkbox-label {
|
||||
color: var(--global-primary-TextColor);
|
||||
user-select: none;
|
||||
}
|
||||
.checkbox {
|
||||
overflow: hidden;
|
||||
position: absolute;
|
||||
margin: -1px;
|
||||
padding: 0;
|
||||
width: 1px;
|
||||
height: 1px;
|
||||
border: 0;
|
||||
clip: rect(0 0 0 0);
|
||||
|
||||
&:checked + .checkbox-element,
|
||||
&:indeterminate + .checkbox-element {
|
||||
background-color: var(--selector-active-BackgroundColor);
|
||||
border-color: var(--selector-active-BackgroundColor);
|
||||
|
||||
&::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
width: 0.625rem;
|
||||
height: 0.5rem;
|
||||
background-color: var(--selector-IconColor);
|
||||
transform: translate(-50%, -50%);
|
||||
}
|
||||
}
|
||||
&:checked + .checkbox-element::after {
|
||||
clip-path: path(
|
||||
'M9.7,0.5c0.4,0.4,0.4,1,0,1.4L4.1,7.8L0.3,4.2c-0.4-0.4-0.4-1,0-1.4c0.4-0.4,1-0.4,1.4,0L4,5l4.3-4.5C8.6,0.1,9.3,0.1,9.7,0.5z'
|
||||
);
|
||||
}
|
||||
&:indeterminate + .checkbox-element::after {
|
||||
clip-path: path('M0,4c0-0.6,0.4-1,1-1h8c0.6,0,1,0.4,1,1c0,0.6-0.4,1-1,1H1C0.4,5,0,4.6,0,4z');
|
||||
}
|
||||
&:disabled + .checkbox-element {
|
||||
box-shadow: none;
|
||||
background-color: var(--selector-disabled-BackgroundColor);
|
||||
border-color: var(--selector-disabled-BorderColor);
|
||||
|
||||
& + .checkbox-label {
|
||||
color: var(--global-disabled-TextColor);
|
||||
}
|
||||
}
|
||||
& + .checkbox-element.error {
|
||||
border-color: var(--border-color-global-error-border-color);
|
||||
}
|
||||
&:focus + .checkbox-element {
|
||||
outline: 2px solid var(--global-focus-BorderColor);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
&:required + .checkbox-element + .checkbox-label::after {
|
||||
content: '*';
|
||||
position: relative;
|
||||
top: -0.125rem;
|
||||
left: 0.125rem;
|
||||
color: var(--global-error-TextColor);
|
||||
}
|
||||
}
|
||||
.checkbox-container {
|
||||
display: inline-flex;
|
||||
gap: var(--spacing-1_5);
|
||||
min-width: var(--spacing-2);
|
||||
min-height: var(--spacing-2);
|
||||
|
||||
&:not(.disabled) {
|
||||
cursor: pointer;
|
||||
|
||||
&:active .checkbox-element {
|
||||
outline: 2px solid var(--global-focus-BorderColor);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
&:hover .checkbox-element {
|
||||
box-shadow: 0 0 0 4px var(--selector-hover-overlay-BackgroundColor);
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,111 @@
|
||||
<script lang="ts">
|
||||
//
|
||||
// © 2023 Hardcore Engineering, Inc. All Rights Reserved.
|
||||
// Licensed under the Eclipse Public License v2.0 (SPDX: EPL-2.0).
|
||||
//
|
||||
import type { IntlString } from '@hcengineering/platform'
|
||||
import { generateId } from '@hcengineering/core'
|
||||
import { Label } from '../..'
|
||||
|
||||
export let id: string = generateId()
|
||||
export let group: any
|
||||
export let value: any
|
||||
export let label: string | undefined = undefined
|
||||
export let labelIntl: IntlString | undefined = undefined
|
||||
export let labelParams: Record<string, any> | undefined = undefined
|
||||
export let checked: boolean = false
|
||||
export let disabled: boolean = false
|
||||
export let error: boolean = false
|
||||
</script>
|
||||
|
||||
<label class="radioButton-container" class:disabled>
|
||||
<input type="radio" class="radioButton" {id} bind:group {value} {checked} {disabled} on:change />
|
||||
<div class="radioButton-element" class:disabled class:error />
|
||||
{#if label !== undefined || labelIntl !== undefined || $$slots.default !== undefined}
|
||||
<div class="radioButton-label">
|
||||
{#if labelIntl}<Label label={labelIntl} params={labelParams} />{:else}{label}{/if}
|
||||
<slot />
|
||||
</div>
|
||||
{/if}
|
||||
<slot name="after" />
|
||||
</label>
|
||||
|
||||
<style lang="scss">
|
||||
.radioButton-element {
|
||||
position: relative;
|
||||
flex-shrink: 0;
|
||||
width: var(--spacing-2);
|
||||
height: var(--spacing-2);
|
||||
background-color: var(--selector-BackgroundColor);
|
||||
border: 1px solid var(--selector-BorderColor);
|
||||
border-radius: var(--large-BorderRadius);
|
||||
}
|
||||
.radioButton-label {
|
||||
color: var(--global-primary-TextColor);
|
||||
user-select: none;
|
||||
}
|
||||
.radioButton {
|
||||
overflow: hidden;
|
||||
position: absolute;
|
||||
margin: -1px;
|
||||
padding: 0;
|
||||
width: 1px;
|
||||
height: 1px;
|
||||
border: 0;
|
||||
clip: rect(0 0 0 0);
|
||||
|
||||
&:checked + .radioButton-element {
|
||||
background-color: var(--selector-active-BackgroundColor);
|
||||
border-color: var(--selector-active-BackgroundColor);
|
||||
|
||||
&::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
width: var(--spacing-0_75);
|
||||
height: var(--spacing-0_75);
|
||||
background-color: var(--selector-IconColor);
|
||||
border-radius: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
}
|
||||
}
|
||||
&:disabled + .radioButton-element {
|
||||
box-shadow: none;
|
||||
background-color: var(--selector-disabled-BackgroundColor);
|
||||
border-color: var(--selector-disabled-BorderColor);
|
||||
|
||||
&::after {
|
||||
background-color: var(--selector-disabled-IconColor);
|
||||
}
|
||||
& + .radioButton-label {
|
||||
color: var(--global-disabled-TextColor);
|
||||
}
|
||||
}
|
||||
& + .radioButton-element.error {
|
||||
border-color: var(--border-color-global-error-border-color);
|
||||
}
|
||||
&:focus + .radioButton-element {
|
||||
outline: 2px solid var(--global-focus-BorderColor);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
}
|
||||
.radioButton-container {
|
||||
display: inline-flex;
|
||||
gap: var(--spacing-2);
|
||||
min-width: var(--spacing-2);
|
||||
min-height: var(--spacing-2);
|
||||
|
||||
&:not(.disabled) {
|
||||
cursor: pointer;
|
||||
|
||||
&:active .radioButton-element {
|
||||
outline: 2px solid var(--global-focus-BorderColor);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
&:hover .radioButton-element {
|
||||
box-shadow: 0 0 0 4px var(--selector-hover-overlay-BackgroundColor);
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -309,6 +309,11 @@
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
{#if $$slots['panel-footer']}
|
||||
<div class="popupPanel-footer">
|
||||
<slot name="panel-footer" />
|
||||
</div>
|
||||
{/if}
|
||||
<div class="popupPanel-pageHeader only-print" id="page-header">
|
||||
<slot name="page-header" />
|
||||
</div>
|
||||
|
||||
@@ -76,6 +76,12 @@
|
||||
|
||||
$: document.body.style.cursor = drag ? 'all-scroll' : 'default'
|
||||
$: docSize = checkAdaptiveMatching($deviceInfo.size, 'md')
|
||||
$: isFullMobile =
|
||||
$deviceInfo.isMobile &&
|
||||
$deviceInfo.isPortrait &&
|
||||
['right', 'top', 'float', 'full', 'content', 'middle', 'centered', 'center', 'full-centered'].some(
|
||||
(el) => element === el
|
||||
)
|
||||
|
||||
function _update (result: any): void {
|
||||
if (onUpdate !== undefined) onUpdate(result)
|
||||
@@ -100,7 +106,7 @@
|
||||
contentPanel: HTMLElement | undefined
|
||||
): void => {
|
||||
const device: DeviceOptions = $deviceInfo
|
||||
if ((fullSize || docSize) && (element === 'float' || element === 'centered')) {
|
||||
if (((fullSize || docSize) && (element === 'float' || element === 'centered')) || isFullMobile) {
|
||||
options = fitPopupElement(modalHTML, device, 'full', contentPanel, clientWidth, clientHeight)
|
||||
options.props.maxHeight = '100vh'
|
||||
if (!modalHTML.classList.contains('fullsize')) modalHTML.classList.add('fullsize')
|
||||
|
||||
@@ -48,6 +48,8 @@
|
||||
export let disablePointerEventsOnScroll = false
|
||||
export let onScroll: ((params: ScrollParams) => void) | undefined = undefined
|
||||
export let onResize: (() => void) | undefined = undefined
|
||||
export let containerName: string | undefined = undefined
|
||||
export let containerType: 'size' | 'inline-size' | undefined = containerName !== undefined ? 'inline-size' : undefined
|
||||
|
||||
export function scroll (top: number, left?: number, behavior: 'auto' | 'smooth' = 'auto') {
|
||||
if (divScroll) {
|
||||
@@ -598,6 +600,8 @@
|
||||
: 'row'}
|
||||
style:height={contentDirection === 'vertical-reverse' ? 'max-content' : noStretch ? 'auto' : '100%'}
|
||||
style:align-items={align}
|
||||
style:container-name={containerName}
|
||||
style:container-type={containerType}
|
||||
class:disableEvents={isScrolling && disablePointerEventsOnScroll}
|
||||
use:resizeObserver={() => {
|
||||
checkAutoScroll()
|
||||
|
||||
@@ -158,6 +158,8 @@ export { default as HotkeyGroup } from './components/HotkeyGroup.svelte'
|
||||
export { default as ModernWizardDialog } from './components/wizard/ModernWizardDialog.svelte'
|
||||
export { default as ModernWizardBar } from './components/wizard/ModernWizardBar.svelte'
|
||||
export { default as ModernTab } from './components/ModernTab.svelte'
|
||||
export { default as ModernCheckbox } from './components/ModernCheckbox.svelte'
|
||||
export { default as ModernRadioButton } from './components/ModernRadioButton.svelte'
|
||||
|
||||
export { default as IconAdd } from './components/icons/Add.svelte'
|
||||
export { default as IconCircleAdd } from './components/icons/CircleAdd.svelte'
|
||||
|
||||
@@ -372,7 +372,8 @@ export function fitPopupElement (
|
||||
newProps.right = '1px'
|
||||
show = true
|
||||
} else if (element === 'full-centered') {
|
||||
newProps.top = '20px'
|
||||
const rect = contentPanel !== undefined ? contentPanel.getBoundingClientRect() : { top: 0 }
|
||||
newProps.top = `${Math.max(20, rect.top + 1)}px`
|
||||
newProps.bottom = '20px'
|
||||
newProps.left = '20px'
|
||||
newProps.right = '20px'
|
||||
|
||||
@@ -24,5 +24,11 @@
|
||||
</script>
|
||||
|
||||
{#if extension}
|
||||
<Component is={extension.components[kind]} {props} on:close on:open on:submit />
|
||||
<Component
|
||||
is={extension.components[kind].component}
|
||||
props={{ ...extension.components[kind].props, ...props }}
|
||||
on:close
|
||||
on:open
|
||||
on:submit
|
||||
/>
|
||||
{/if}
|
||||
|
||||
+3
-2
@@ -30,7 +30,7 @@
|
||||
|
||||
$: attrViewletConfig = viewlet?.config?.[attributeModel.key]
|
||||
$: attributeIcon = attrViewletConfig?.icon ?? attributeModel.icon ?? IconEdit
|
||||
$: isUnset = values.length > 0 && !values.some((value) => value !== null && value !== '')
|
||||
$: isUnset = values.length > 0 && !values.some((value) => value != null && value !== '')
|
||||
|
||||
$: isTextType = getIsTextType(attributeModel)
|
||||
|
||||
@@ -42,7 +42,7 @@
|
||||
</script>
|
||||
|
||||
{#if isUnset}
|
||||
<div class="row overflow-label">
|
||||
<div class="unset row overflow-label">
|
||||
<span class="mr-1"><Icon icon={attributeIcon} size="small" /></span>
|
||||
<Label label={activity.string.Unset} />
|
||||
<span class="lower"><Label label={attributeModel.label} /></span>
|
||||
@@ -89,6 +89,7 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.25rem;
|
||||
color: var(--global-primary-TextColor);
|
||||
}
|
||||
|
||||
.showMore {
|
||||
|
||||
@@ -210,7 +210,7 @@ export type ActivityExtensionKind = 'input'
|
||||
*/
|
||||
export interface ActivityExtension extends Doc {
|
||||
ofClass: Ref<Class<Doc>>
|
||||
components: Record<ActivityExtensionKind, AnyComponent>
|
||||
components: Record<ActivityExtensionKind, { component: AnyComponent, props?: Record<string, any> }>
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -39,7 +39,7 @@
|
||||
}
|
||||
} else {
|
||||
let targetState: Status | undefined = undefined
|
||||
const txes = await client.findAll(core.class.Tx, { 'tx.objectId': value._id })
|
||||
const txes = await client.findAll(core.class.Tx, { objectId: value._id })
|
||||
while (txes.length) {
|
||||
const doc = TxProcessor.buildDoc2Doc<Card>(txes)
|
||||
if (doc === undefined) {
|
||||
|
||||
@@ -72,9 +72,6 @@
|
||||
|
||||
async function updateDataProvider (attachedTo: Ref<Doc>, selectedMessageId?: Ref<ActivityMessage>): Promise<void> {
|
||||
if (dataProvider === undefined) {
|
||||
// For now loading all messages for documents with activity. Need to correct handle aggregation with pagination.
|
||||
// Perhaps we should load all activity messages once, and keep loading in chunks only for ChatMessages then merge them correctly with activity messages
|
||||
const loadAll = isDocChannel
|
||||
const ctx =
|
||||
context ??
|
||||
(await client.findOne(notification.class.DocNotifyContext, {
|
||||
@@ -90,7 +87,7 @@
|
||||
attachedTo,
|
||||
activity.class.ActivityMessage,
|
||||
selectedMessageId,
|
||||
loadAll,
|
||||
false,
|
||||
hasRefs,
|
||||
collection
|
||||
)
|
||||
@@ -112,7 +109,7 @@
|
||||
provider={dataProvider}
|
||||
{freeze}
|
||||
{autofocus}
|
||||
loadMoreAllowed={!isDocChannel}
|
||||
loadMoreAllowed
|
||||
{withInput}
|
||||
{readonly}
|
||||
{onReply}
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
Space,
|
||||
Timestamp,
|
||||
Tx,
|
||||
TxCollectionCUD,
|
||||
TxCUD,
|
||||
TxProcessor
|
||||
} from '@hcengineering/core'
|
||||
import activity, { ActivityMessage } from '@hcengineering/activity'
|
||||
@@ -546,11 +546,9 @@
|
||||
}
|
||||
|
||||
const newMessageTxListener = (tx: Tx): void => {
|
||||
if (tx._class !== core.class.TxCollectionCUD) return
|
||||
const ctx = tx as TxCollectionCUD<Doc, ActivityMessage>
|
||||
if (ctx.objectId !== doc._id) return
|
||||
const etx = TxProcessor.extractTx(tx)
|
||||
if (etx._class !== core.class.TxCreateDoc) return
|
||||
const ctx = tx as TxCUD<ActivityMessage>
|
||||
if (ctx.attachedTo !== doc._id) return
|
||||
if (ctx._class !== core.class.TxCreateDoc) return
|
||||
if (shouldScrollToNew) {
|
||||
void wait().then(scrollToNewMessages)
|
||||
}
|
||||
|
||||
@@ -70,6 +70,9 @@
|
||||
$: locked = doc?.lockedBy != null
|
||||
$: readonly = $restrictionStore.readonly || locked
|
||||
|
||||
let useMaxWidth = getUseMaxWidth()
|
||||
$: saveUseMaxWidth(useMaxWidth)
|
||||
|
||||
export function canClose (): boolean {
|
||||
return false
|
||||
}
|
||||
@@ -179,6 +182,15 @@
|
||||
}
|
||||
}
|
||||
|
||||
function getUseMaxWidth (): boolean {
|
||||
const useMaxWidth = localStorage.getItem('document.useMaxWidth')
|
||||
return useMaxWidth === 'true'
|
||||
}
|
||||
|
||||
function saveUseMaxWidth (useMaxWidth: boolean): void {
|
||||
localStorage.setItem('document.useMaxWidth', useMaxWidth.toString())
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
dispatch('open', { ignoreKeys: ['comments', 'name'] })
|
||||
})
|
||||
@@ -245,7 +257,7 @@
|
||||
isHeader={false}
|
||||
isCustomAttr={false}
|
||||
isSub={false}
|
||||
useMaxWidth={false}
|
||||
bind:useMaxWidth
|
||||
printHeader={false}
|
||||
{embedded}
|
||||
adaptive={'default'}
|
||||
|
||||
@@ -68,9 +68,15 @@
|
||||
"Transcription": "Transcription",
|
||||
"StartWithTranscription": "Start with transcription",
|
||||
"MeetingMinutes": "Meeting minutes",
|
||||
"MeetingsMinutes": "Meetings minutes",
|
||||
"StartMeeting": "Start meeting",
|
||||
"Video": "Video",
|
||||
"NoMeetingMinutes": "No meeting minutes",
|
||||
"JoinMeeting": "Join meeting"
|
||||
"JoinMeeting": "Join meeting",
|
||||
"MeetingStart": "Meeting start",
|
||||
"MeetingEnd": "Meeting end",
|
||||
"Status": "Status",
|
||||
"Active": "Active",
|
||||
"Finished": "Finished"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,9 +68,15 @@
|
||||
"Transcription": "Transcripción",
|
||||
"StartWithTranscription": "Iniciar con transcripción",
|
||||
"MeetingMinutes": "Minutos de la reunión",
|
||||
"MeetingsMinutes": "Minutos de las reuniones",
|
||||
"StartMeeting": "Iniciar reunión",
|
||||
"Video": "Video",
|
||||
"NoMeetingMinutes": "Sin minutos de reunión",
|
||||
"JoinMeeting": "Unirse a la reunión"
|
||||
"JoinMeeting": "Unirse a la reunión",
|
||||
"MeetingStart": "Inicio de la reunión",
|
||||
"MeetingEnd": "Fin de la reunión",
|
||||
"Status": "Estado",
|
||||
"Active": "Activo",
|
||||
"Finished": "Terminado"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,9 +68,15 @@
|
||||
"Transcription": "Transcription",
|
||||
"StartWithTranscription": "Démarrer avec la transcription",
|
||||
"MeetingMinutes": "Minutes de la réunion",
|
||||
"MeetingsMinutes": "Minutes des réunions",
|
||||
"StartMeeting": "Démarrer la réunion",
|
||||
"Video": "Vidéo",
|
||||
"NoMeetingMinutes": "Pas de minutes de réunion",
|
||||
"JoinMeeting": "Rejoindre la réunion"
|
||||
"JoinMeeting": "Rejoindre la réunion",
|
||||
"MeetingStart": "Début de la réunion",
|
||||
"MeetingEnd": "Fin de la réunion",
|
||||
"Status": "Statut",
|
||||
"Active": "Actif",
|
||||
"Finished": "Terminé"
|
||||
}
|
||||
}
|
||||
@@ -68,9 +68,15 @@
|
||||
"Transcription": "Trascrizione",
|
||||
"StartWithTranscription": "Inizia con la trascrizione",
|
||||
"MeetingMinutes": "Verbale della riunione",
|
||||
"MeetingsMinutes": "Verbali delle riunioni",
|
||||
"StartMeeting": "Inizia riunione",
|
||||
"Video": "Video",
|
||||
"NoMeetingMinutes": "Nessun verbale della riunione",
|
||||
"JoinMeeting": "Unisciti alla riunione"
|
||||
"JoinMeeting": "Unisciti alla riunione",
|
||||
"MeetingStart": "Inizio riunione",
|
||||
"MeetingEnd": "Fine riunione",
|
||||
"Status": "Stato",
|
||||
"Active": "Attivo",
|
||||
"Finished": "Finito"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,9 +68,15 @@
|
||||
"Transcription": "Transcrição",
|
||||
"StartWithTranscription": "Começar com transcrição",
|
||||
"MeetingMinutes": "Minutos da reunião",
|
||||
"MeetingsMinutes": "Minutos das reuniões",
|
||||
"StartMeeting": "Iniciar reunião",
|
||||
"Video": "Vídeo",
|
||||
"NoMeetingMinutes": "Sem minutos de reunião",
|
||||
"JoinMeeting": "Participar na reunião"
|
||||
"JoinMeeting": "Participar na reunião",
|
||||
"MeetingStart": "Início da reunião",
|
||||
"MeetingEnd": "Fim da reunião",
|
||||
"Status": "Estado",
|
||||
"Active": "Ativo",
|
||||
"Finished": "Finalizado"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,10 +67,16 @@
|
||||
"Meeting": "Встреча",
|
||||
"Transcription": "Транскрипция",
|
||||
"StartWithTranscription": "Начинать с транскрипцией",
|
||||
"MeetingMinutes": "Результаты встреч",
|
||||
"MeetingMinutes": "Протокол встречи",
|
||||
"MeetingsMinutes": "Протоколы встреч",
|
||||
"StartMeeting": "Начать встречу",
|
||||
"Video": "Видео",
|
||||
"NoMeetingMinutes": "Нет результатов встреч",
|
||||
"JoinMeeting": "Присоединиться к встрече"
|
||||
"JoinMeeting": "Присоединиться к встрече",
|
||||
"MeetingStart": "Начало встречи",
|
||||
"MeetingEnd": "Конец встречи",
|
||||
"Status": "Статус",
|
||||
"Active": "Активно",
|
||||
"Finished": "Завершено"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,9 +68,15 @@
|
||||
"Transcription": "转录",
|
||||
"StartWithTranscription": "开始转录",
|
||||
"MeetingMinutes": "会议记录",
|
||||
"MeetingsMinutes": "会议记录",
|
||||
"StartMeeting": "开始会议",
|
||||
"Video": "视频",
|
||||
"NoMeetingMinutes": "无会议记录",
|
||||
"JoinMeeting": "加入会议"
|
||||
"JoinMeeting": "加入会议",
|
||||
"MeetingStart": "会议开始",
|
||||
"MeetingEnd": "会议结束",
|
||||
"Status": "状态",
|
||||
"Active": "活动",
|
||||
"Finished": "已完成"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,90 +0,0 @@
|
||||
<!--
|
||||
// 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.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { personByIdStore } from '@hcengineering/contact-resources'
|
||||
import { Room as TypeRoom } from '@hcengineering/love'
|
||||
import { getMetadata } from '@hcengineering/platform'
|
||||
import { Label, Loading, deviceOptionsStore as deviceInfo } from '@hcengineering/ui'
|
||||
import { onDestroy, onMount } from 'svelte'
|
||||
import presentation, { getClient } from '@hcengineering/presentation'
|
||||
import { EditDoc } from '@hcengineering/view-resources'
|
||||
import { subscribeDoc } from '@hcengineering/notification-resources'
|
||||
|
||||
import love from '../plugin'
|
||||
import { storePromise, currentRoom, infos, invites, myInfo, myRequests, meetingMinutesStore } from '../stores'
|
||||
import { awaitConnect, isConnected, isCurrentInstanceConnected, isFullScreen, tryConnect } from '../utils'
|
||||
import ControlBar from './ControlBar.svelte'
|
||||
|
||||
export let room: TypeRoom
|
||||
|
||||
let loading: boolean = false
|
||||
let configured: boolean = false
|
||||
|
||||
onMount(async () => {
|
||||
loading = true
|
||||
|
||||
const wsURL = getMetadata(love.metadata.WebSocketURL)
|
||||
|
||||
if (wsURL === undefined) {
|
||||
return
|
||||
}
|
||||
|
||||
configured = true
|
||||
|
||||
await $storePromise
|
||||
|
||||
if (
|
||||
!$isConnected &&
|
||||
!$isCurrentInstanceConnected &&
|
||||
$myInfo?.sessionId === getMetadata(presentation.metadata.SessionId)
|
||||
) {
|
||||
const info = $infos.filter((p) => p.room === room._id)
|
||||
await tryConnect($personByIdStore, $myInfo, room, info, $myRequests, $invites)
|
||||
}
|
||||
|
||||
await awaitConnect()
|
||||
loading = false
|
||||
})
|
||||
|
||||
let replacedPanel: HTMLElement
|
||||
$: $deviceInfo.replacedPanel = replacedPanel
|
||||
onDestroy(() => ($deviceInfo.replacedPanel = undefined))
|
||||
|
||||
$: if ($meetingMinutesStore) {
|
||||
void subscribeDoc(getClient(), $meetingMinutesStore._class, $meetingMinutesStore._id, 'add', $meetingMinutesStore)
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="antiPanel-component filledNav" bind:this={replacedPanel}>
|
||||
<div class="hulyComponent">
|
||||
{#if $isConnected && !$isCurrentInstanceConnected}
|
||||
<div class="flex-center justify-center error h-full w-full clear-mins">
|
||||
<Label label={love.string.AnotherWindowError} />
|
||||
</div>
|
||||
{:else if !configured}
|
||||
<div class="flex-center justify-center error h-full w-full clear-mins">
|
||||
<Label label={love.string.ServiceNotConfigured} />
|
||||
</div>
|
||||
{:else if loading || !$currentRoom || !$meetingMinutesStore}
|
||||
<Loading />
|
||||
{:else}
|
||||
<EditDoc _id={$meetingMinutesStore._id} _class={$meetingMinutesStore._class} embedded selectedAside={false} />
|
||||
{/if}
|
||||
{#if $currentRoom}
|
||||
<div class="flex-grow flex-shrink" />
|
||||
<ControlBar room={$currentRoom} fullScreen={$isFullScreen} />
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
@@ -29,7 +29,8 @@
|
||||
type CompAndProps,
|
||||
IconMoreV,
|
||||
ButtonMenu,
|
||||
DropdownIntlItem
|
||||
DropdownIntlItem,
|
||||
IconMaximize
|
||||
} from '@hcengineering/ui'
|
||||
import view, { Action } from '@hcengineering/view'
|
||||
import { getActions } from '@hcengineering/view-resources'
|
||||
@@ -60,8 +61,10 @@
|
||||
import RoomAccessPopup from './RoomAccessPopup.svelte'
|
||||
import RoomLanguageSelector from './RoomLanguageSelector.svelte'
|
||||
import ControlBarContainer from './ControlBarContainer.svelte'
|
||||
import RoomModal from './RoomModal.svelte'
|
||||
|
||||
export let room: Room
|
||||
export let canMaximize: boolean = true
|
||||
export let fullScreen: boolean = false
|
||||
export let onFullScreen: (() => void) | undefined = undefined
|
||||
|
||||
@@ -159,6 +162,10 @@
|
||||
await fn(room)
|
||||
}
|
||||
$: withVideo = $screenSharing || room.type === RoomType.Video
|
||||
|
||||
function maximize (): void {
|
||||
showPopup(RoomModal, { room }, 'full-centered')
|
||||
}
|
||||
</script>
|
||||
|
||||
<ControlBarContainer bind:noLabel>
|
||||
@@ -253,6 +260,20 @@
|
||||
}}
|
||||
/>
|
||||
{/if}
|
||||
|
||||
{#if ($screenSharing || room.type === RoomType.Video) && $isConnected && canMaximize}
|
||||
<ModernButton
|
||||
icon={IconMaximize}
|
||||
tooltip={{
|
||||
label: love.string.FullscreenMode,
|
||||
direction: 'top'
|
||||
}}
|
||||
kind={'secondary'}
|
||||
iconSize="medium"
|
||||
size={'large'}
|
||||
on:click={maximize}
|
||||
/>
|
||||
{/if}
|
||||
{#if $isConnected && moreItems.length > 0}
|
||||
<ButtonMenu
|
||||
items={moreItems}
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
import { getClient } from '@hcengineering/presentation'
|
||||
import { EditBox } from '@hcengineering/ui'
|
||||
import { MeetingMinutes } from '@hcengineering/love'
|
||||
import { createEventDispatcher, onMount } from 'svelte'
|
||||
|
||||
import love from '../plugin'
|
||||
|
||||
@@ -23,10 +24,15 @@
|
||||
export let readonly: boolean = false
|
||||
|
||||
const client = getClient()
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
async function changeTitle (): Promise<void> {
|
||||
await client.diffUpdate(object, { title: object.title })
|
||||
await client.update(object, { title: object.title })
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
dispatch('open', { ignoreKeys: ['title'] })
|
||||
})
|
||||
</script>
|
||||
|
||||
<div class="flex-row-stretch">
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { getClient } from '@hcengineering/presentation'
|
||||
import { EditBox, ModernButton, closePanel } from '@hcengineering/ui'
|
||||
import { EditBox, ModernButton } from '@hcengineering/ui'
|
||||
import { Room, isOffice } from '@hcengineering/love'
|
||||
import { createEventDispatcher, onMount } from 'svelte'
|
||||
import { personByIdStore } from '@hcengineering/contact-resources'
|
||||
@@ -22,7 +22,7 @@
|
||||
|
||||
import love from '../plugin'
|
||||
import { getRoomName, tryConnect } from '../utils'
|
||||
import { infos, invites, myInfo, myRequests, selectedRoomPlace } from '../stores'
|
||||
import { infos, invites, myInfo, myRequests, selectedRoomPlace, myOffice, currentRoom } from '../stores'
|
||||
|
||||
export let object: Room
|
||||
export let readonly: boolean = false
|
||||
@@ -59,8 +59,6 @@
|
||||
)
|
||||
connecting = false
|
||||
selectedRoomPlace.set(undefined)
|
||||
closePanel()
|
||||
dispatch('close')
|
||||
}
|
||||
|
||||
let connectLabel: IntlString = love.string.StartMeeting
|
||||
@@ -83,7 +81,9 @@
|
||||
focusIndex={1}
|
||||
/>
|
||||
</div>
|
||||
<ModernButton label={connectLabel} size="large" kind={'primary'} on:click={connect} loading={connecting} />
|
||||
{#if object._id !== $myOffice?._id && ($currentRoom?._id !== object._id || connecting)}
|
||||
<ModernButton label={connectLabel} size="large" kind={'primary'} on:click={connect} loading={connecting} />
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -14,12 +14,15 @@
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { deviceOptionsStore as deviceInfo } from '@hcengineering/ui'
|
||||
import { onDestroy } from 'svelte'
|
||||
import { onDestroy, onMount } from 'svelte'
|
||||
import presentation from '@hcengineering/presentation'
|
||||
import { personByIdStore } from '@hcengineering/contact-resources'
|
||||
|
||||
import Hall from './Hall.svelte'
|
||||
import { currentRoom } from '../stores'
|
||||
import { isConnected } from '../utils'
|
||||
import ActiveMeeting from './ActiveMeeting.svelte'
|
||||
import { getMetadata } from '@hcengineering/platform'
|
||||
import love from '../plugin'
|
||||
import { tryConnect, isConnected, isCurrentInstanceConnected } from '../utils'
|
||||
import { infos, invites, myInfo, myRequests, storePromise, currentRoom } from '../stores'
|
||||
|
||||
const localNav: boolean = $deviceInfo.navigator.visible
|
||||
const savedNav = localStorage.getItem('love-visibleNav')
|
||||
@@ -29,12 +32,30 @@
|
||||
onDestroy(() => {
|
||||
$deviceInfo.navigator.visible = localNav
|
||||
})
|
||||
|
||||
onMount(async () => {
|
||||
const wsURL = getMetadata(love.metadata.WebSocketURL)
|
||||
|
||||
if (wsURL === undefined) {
|
||||
return
|
||||
}
|
||||
|
||||
await $storePromise
|
||||
const room = $currentRoom
|
||||
|
||||
if (room === undefined) return
|
||||
|
||||
if (
|
||||
!$isConnected &&
|
||||
!$isCurrentInstanceConnected &&
|
||||
$myInfo?.sessionId === getMetadata(presentation.metadata.SessionId)
|
||||
) {
|
||||
const info = $infos.filter((p) => p.room === room._id)
|
||||
await tryConnect($personByIdStore, $myInfo, room, info, $myRequests, $invites)
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<div class="hulyPanels-container">
|
||||
{#if $currentRoom && $isConnected}
|
||||
<ActiveMeeting room={$currentRoom} />
|
||||
{:else}
|
||||
<Hall />
|
||||
{/if}
|
||||
<Hall />
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
<!--
|
||||
// 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.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { MeetingMinutes } from '@hcengineering/love'
|
||||
import { ObjectPresenter, openDoc } from '@hcengineering/view-resources'
|
||||
import view from '@hcengineering/view'
|
||||
import { ActionIcon } from '@hcengineering/ui'
|
||||
import { createQuery, getClient } from '@hcengineering/presentation'
|
||||
import type { Doc } from '@hcengineering/core'
|
||||
|
||||
export let object: MeetingMinutes
|
||||
|
||||
const client = getClient()
|
||||
const docQuery = createQuery()
|
||||
|
||||
let doc: Doc | undefined
|
||||
|
||||
$: docQuery.query(object.attachedToClass, { _id: object.attachedTo }, (r) => {
|
||||
doc = r.shift()
|
||||
})
|
||||
</script>
|
||||
|
||||
{#if doc}
|
||||
<span class="label flex-row-center flex-gap-4 ml-3 no-word-wrap">
|
||||
<ObjectPresenter
|
||||
objectId={doc._id}
|
||||
_class={doc._class}
|
||||
value={doc}
|
||||
shouldShowAvatar={false}
|
||||
disabled
|
||||
props={{ type: 'text' }}
|
||||
/>
|
||||
<ActionIcon
|
||||
icon={view.icon.Open}
|
||||
size={'small'}
|
||||
action={async () => {
|
||||
if (doc === undefined) return
|
||||
await openDoc(client.getHierarchy(), doc)
|
||||
}}
|
||||
/>
|
||||
</span>
|
||||
{/if}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user