diff --git a/models/chunter/src/index.ts b/models/chunter/src/index.ts index b8bb7da2ca..a59cb8b959 100644 --- a/models/chunter/src/index.ts +++ b/models/chunter/src/index.ts @@ -15,30 +15,30 @@ import activity, { type ActivityMessageControl } from '@hcengineering/activity' import { chunterId, type ChunterSpace } from '@hcengineering/chunter' -import presentation from '@hcengineering/model-presentation' +import contact from '@hcengineering/contact' import { type Builder } from '@hcengineering/model' import core from '@hcengineering/model-core' +import presentation from '@hcengineering/model-presentation' import view from '@hcengineering/model-view' import workbench from '@hcengineering/model-workbench' import { WidgetType } from '@hcengineering/workbench' -import contact from '@hcengineering/contact' -import chunter from './plugin' import { defineActions } from './actions' import { defineNotifications } from './notifications' +import chunter from './plugin' import { DOMAIN_CHUNTER, TChannel, TChatMessage, TChatMessageViewlet, TChatSyncInfo, + TChunterExtension, TChunterSpace, TDirectMessage, TInlineButton, TObjectChatPanel, TThreadMessage, - TTypingInfo, - TChunterExtension + TTypingInfo } from './types' export { chunterId } from '@hcengineering/chunter' @@ -161,6 +161,10 @@ export function createModel (builder: Builder): void { presenter: chunter.component.ThreadMessagePresenter }) + builder.mixin(chunter.class.TypingInfo, core.class.Class, core.mixin.TransientConfiguration, { + broadcastOnly: true + }) + builder.createDoc( view.class.Viewlet, core.space.Model, diff --git a/models/core/src/core.ts b/models/core/src/core.ts index 3942868da8..2cff517172 100644 --- a/models/core/src/core.ts +++ b/models/core/src/core.ts @@ -14,8 +14,6 @@ // import { - type Card, - type CollaborativeDoc, DOMAIN_BLOB, DOMAIN_CONFIGURATION, DOMAIN_DOC_INDEX_STATE, @@ -27,8 +25,10 @@ import { type ArrOf, type AttachedDoc, type Blob, + type Card, type Class, type ClassifierKind, + type CollaborativeDoc, type Collection, type Configuration, type ConfigurationElement, @@ -50,6 +50,7 @@ import { type RefTo, type Space, type Timestamp, + type TransientConfiguration, type Type, type TypeAny, type Version @@ -403,3 +404,9 @@ export class TTypeCollaborativeDocVersion extends TType {} @UX(core.string.Rank) @Model(core.class.TypeRank, core.class.Type) export class TTypeRank extends TType {} + +@MMixin(core.mixin.TransientConfiguration, core.class.Class) +export class TTransientConfiguration extends TClass implements TransientConfiguration { + @Prop(TypeBoolean(), core.string.Private) + broadcastOnly!: boolean +} diff --git a/models/core/src/index.ts b/models/core/src/index.ts index 0ef71c7ae9..e7dfc70eca 100644 --- a/models/core/src/index.ts +++ b/models/core/src/index.ts @@ -39,12 +39,12 @@ import { TAttachedDoc, TAttribute, TBlob, + TCard, TClass, TCollection, TConfiguration, TConfigurationElement, TDoc, - TCard, TDocIndexState, TDomainIndexConfiguration, TEnum, @@ -57,6 +57,7 @@ import { TObj, TPluginConfiguration, TRefTo, + TTransientConfiguration, TType, TTypeAny, TTypeBlob, @@ -173,7 +174,8 @@ export function createModel (builder: Builder): void { TMigrationState, TBlob, TDomainIndexConfiguration, - TBenchmarkDoc + TBenchmarkDoc, + TTransientConfiguration ) builder.createDoc( diff --git a/packages/core/src/classes.ts b/packages/core/src/classes.ts index c64aa82119..3c37f52773 100644 --- a/packages/core/src/classes.ts +++ b/packages/core/src/classes.ts @@ -15,8 +15,8 @@ // import type { Asset, IntlString, Plugin } from '@hcengineering/platform' -import type { DocumentQuery } from './storage' import { CollaborativeDoc } from './collaboration' +import type { DocumentQuery } from './storage' /** * @public @@ -346,6 +346,14 @@ export const DOMAIN_MIGRATION = '_migrations' as Domain */ export const DOMAIN_TRANSIENT = 'transient' as Domain +/** + * @public + */ +export interface TransientConfiguration extends Class { + // If set will not store transient objects into memdb + broadcastOnly: boolean +} + /** * Special domain to access s3 blob data. * @public diff --git a/packages/core/src/component.ts b/packages/core/src/component.ts index 5349a3a8c2..baed6b65de 100644 --- a/packages/core/src/component.ts +++ b/packages/core/src/component.ts @@ -48,6 +48,7 @@ import type { SpaceTypeDescriptor, SystemSpace, Timestamp, + TransientConfiguration, Type, TypeAny, TypedSpace, @@ -148,7 +149,8 @@ export default plugin(coreId, { mixin: { ConfigurationElement: '' as Ref>, IndexConfiguration: '' as Ref>>, - SpacesTypeData: '' as Ref> + SpacesTypeData: '' as Ref>, + TransientConfiguration: '' as Ref> }, space: { Tx: '' as Ref, diff --git a/packages/ui/src/components/Component.svelte b/packages/ui/src/components/Component.svelte index eeb4909f8d..597233cce3 100644 --- a/packages/ui/src/components/Component.svelte +++ b/packages/ui/src/components/Component.svelte @@ -13,7 +13,7 @@ // limitations under the License. --> {#if persons.length === 0} @@ -65,13 +61,7 @@ {/if} {#if persons.length === 1} - + {/if} {#if persons.length > 1 && size === 'medium'} diff --git a/plugins/contact-resources/src/components/Avatar.svelte b/plugins/contact-resources/src/components/Avatar.svelte index 50331af79a..6160eccf3e 100644 --- a/plugins/contact-resources/src/components/Avatar.svelte +++ b/plugins/contact-resources/src/components/Avatar.svelte @@ -31,8 +31,7 @@ -{#if showStatus && account} +{#if showStatus && accounts.length > 0}
-
+
{:else} import contact, { type Contact, type Employee } from '@hcengineering/contact' - import core, { Account, type Ref, type WithLookup } from '@hcengineering/core' + import { type Ref, type WithLookup } from '@hcengineering/core' import { Asset } from '@hcengineering/platform' import { getClient } from '@hcengineering/presentation' import { AnySvelteComponent, IconSize } from '@hcengineering/ui' + import { employeeByIdStore, personByIdStore } from '../utils' import Avatar from './Avatar.svelte' @@ -30,7 +31,6 @@ export let variant: 'circle' | 'roundedRect' | 'none' = 'roundedRect' export let borderColor: number | undefined = undefined export let showStatus: boolean = true - export let account: Ref | undefined = undefined $: empValue = $employeeByIdStore.get(_id as Ref) ?? $personByIdStore.get(_id) @@ -47,4 +47,4 @@ } - + diff --git a/plugins/contact-resources/src/components/PersonElement.svelte b/plugins/contact-resources/src/components/PersonElement.svelte index 8fb3eb2f8a..b33f817d0a 100644 --- a/plugins/contact-resources/src/components/PersonElement.svelte +++ b/plugins/contact-resources/src/components/PersonElement.svelte @@ -13,14 +13,12 @@ // limitations under the License. --> {#if value} @@ -64,7 +56,7 @@ class:mr-2={shouldShowName && !enlargedText} class:mr-3={shouldShowName && enlargedText} > - + {/if} {#if shouldShowName} diff --git a/plugins/contact-resources/src/components/UserDetails.svelte b/plugins/contact-resources/src/components/UserDetails.svelte index 8f9cdd2bde..f42879c34e 100644 --- a/plugins/contact-resources/src/components/UserDetails.svelte +++ b/plugins/contact-resources/src/components/UserDetails.svelte @@ -15,37 +15,23 @@
- +
-
{getName(hierarchy, person)}
+
{getName(client.getHierarchy(), person)}
diff --git a/plugins/contact-resources/src/components/UserInfo.svelte b/plugins/contact-resources/src/components/UserInfo.svelte index 5c11f5fbb5..a3cb0c555c 100644 --- a/plugins/contact-resources/src/components/UserInfo.svelte +++ b/plugins/contact-resources/src/components/UserInfo.svelte @@ -15,11 +15,10 @@
- +
{#if subtitle}
{subtitle}
{/if}
{getName(client.getHierarchy(), value)}
diff --git a/server/backup/src/service.ts b/server/backup/src/service.ts index 55ef156a8c..144bbc7022 100644 --- a/server/backup/src/service.ts +++ b/server/backup/src/service.ts @@ -13,6 +13,7 @@ // limitations under the License. // +import { Analytics } from '@hcengineering/analytics' import core, { BaseWorkspaceInfo, DOMAIN_TX, @@ -81,7 +82,7 @@ class BackupWorker { `**************************************** backup statistics:`, { - backuped: stats.processed, + processed: stats.processed, notChanges: stats.skipped, failed: stats.failedWorkspaces.length } @@ -91,15 +92,23 @@ class BackupWorker { async schedule (ctx: MeasureContext): Promise { console.log('schedule backup with interval', this.config.Interval, 'seconds') while (!this.canceled) { - const res = await this.backup(ctx) - this.printStats(ctx, res) + try { + const res = await this.backup(ctx, this.config.CoolDown * 1000) + this.printStats(ctx, res) + } catch (err: any) { + Analytics.handleError(err) + ctx.error('error retry in cool down/5', { cooldown: this.config.CoolDown, error: err }) + await new Promise((resolve) => setTimeout(resolve, (this.config.CoolDown / 5) * 1000)) + continue + } console.log('cool down', this.config.CoolDown, 'seconds') await new Promise((resolve) => setTimeout(resolve, this.config.CoolDown * 1000)) } } async backup ( - ctx: MeasureContext + ctx: MeasureContext, + recheckTimeout: number ): Promise<{ failedWorkspaces: BaseWorkspaceInfo[], processed: number, skipped: number }> { const workspacesIgnore = new Set(this.config.SkipWorkspaces.split(';')) ctx.info('skipped workspaces', { workspacesIgnore }) @@ -135,19 +144,21 @@ class BackupWorker { workspaces: workspaces.map((it) => it.workspace) }) - return await this.doBackup(ctx, workspaces) + return await this.doBackup(ctx, workspaces, recheckTimeout) } async doBackup ( rootCtx: MeasureContext, - workspaces: BaseWorkspaceInfo[] + workspaces: BaseWorkspaceInfo[], + recheckTimeout: number ): Promise<{ failedWorkspaces: BaseWorkspaceInfo[], processed: number, skipped: number }> { let index = 0 const failedWorkspaces: BaseWorkspaceInfo[] = [] let processed = 0 + const startTime = Date.now() for (const ws of workspaces) { - if (this.canceled) { + if (this.canceled || Date.now() - startTime > recheckTimeout) { return { failedWorkspaces, processed, skipped: workspaces.length - processed } } index++ diff --git a/server/core/src/mem.ts b/server/core/src/mem.ts index 8b615044d6..5f113d8e42 100644 --- a/server/core/src/mem.ts +++ b/server/core/src/mem.ts @@ -30,10 +30,12 @@ import core, { type StorageIterator, toFindResult, type Tx, + type TxCUD, + TxProcessor, type TxResult, type WorkspaceId } from '@hcengineering/core' -import { type DbAdapterHandler, type DbAdapter, type DomainHelperOperations } from './adapter' +import { type DbAdapter, type DbAdapterHandler, type DomainHelperOperations } from './adapter' /** * @public @@ -119,7 +121,7 @@ export class DummyDbAdapter implements DbAdapter { class InMemoryAdapter extends DummyDbAdapter implements DbAdapter { private readonly modeldb: ModelDb - constructor (hierarchy: Hierarchy) { + constructor (readonly hierarchy: Hierarchy) { super() this.modeldb = new ModelDb(hierarchy) } @@ -138,7 +140,23 @@ class InMemoryAdapter extends DummyDbAdapter implements DbAdapter { } async tx (ctx: MeasureContext, ...tx: Tx[]): Promise { - return await this.modeldb.tx(...tx) + // Filter transactions with broadcast only flags + const ftx = tx.filter((it) => { + if (TxProcessor.isExtendsCUD(it._class)) { + const cud = it as TxCUD + const objClass = this.hierarchy.getClass(cud.objectClass) + const mix = this.hierarchy.hasMixin(objClass, core.mixin.TransientConfiguration) + if (mix && this.hierarchy.as(objClass, core.mixin.TransientConfiguration).broadcastOnly) { + // We do not need to store a broadcast only transactions into model. + return false + } + } + return true + }) + if (ftx.length === 0) { + return [] + } + return await this.modeldb.tx(...ftx) } }