mirror of
https://github.com/hcengineering/platform.git
synced 2025-06-02 13:52:40 +00:00
Merge remote-tracking branch 'origin/develop' into staging
Signed-off-by: Andrey Sobolev <haiodo@gmail.com>
This commit is contained in:
commit
d6d4ba1633
@ -15,30 +15,30 @@
|
|||||||
|
|
||||||
import activity, { type ActivityMessageControl } from '@hcengineering/activity'
|
import activity, { type ActivityMessageControl } from '@hcengineering/activity'
|
||||||
import { chunterId, type ChunterSpace } from '@hcengineering/chunter'
|
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 { type Builder } from '@hcengineering/model'
|
||||||
import core from '@hcengineering/model-core'
|
import core from '@hcengineering/model-core'
|
||||||
|
import presentation from '@hcengineering/model-presentation'
|
||||||
import view from '@hcengineering/model-view'
|
import view from '@hcengineering/model-view'
|
||||||
import workbench from '@hcengineering/model-workbench'
|
import workbench from '@hcengineering/model-workbench'
|
||||||
import { WidgetType } from '@hcengineering/workbench'
|
import { WidgetType } from '@hcengineering/workbench'
|
||||||
import contact from '@hcengineering/contact'
|
|
||||||
|
|
||||||
import chunter from './plugin'
|
|
||||||
import { defineActions } from './actions'
|
import { defineActions } from './actions'
|
||||||
import { defineNotifications } from './notifications'
|
import { defineNotifications } from './notifications'
|
||||||
|
import chunter from './plugin'
|
||||||
import {
|
import {
|
||||||
DOMAIN_CHUNTER,
|
DOMAIN_CHUNTER,
|
||||||
TChannel,
|
TChannel,
|
||||||
TChatMessage,
|
TChatMessage,
|
||||||
TChatMessageViewlet,
|
TChatMessageViewlet,
|
||||||
TChatSyncInfo,
|
TChatSyncInfo,
|
||||||
|
TChunterExtension,
|
||||||
TChunterSpace,
|
TChunterSpace,
|
||||||
TDirectMessage,
|
TDirectMessage,
|
||||||
TInlineButton,
|
TInlineButton,
|
||||||
TObjectChatPanel,
|
TObjectChatPanel,
|
||||||
TThreadMessage,
|
TThreadMessage,
|
||||||
TTypingInfo,
|
TTypingInfo
|
||||||
TChunterExtension
|
|
||||||
} from './types'
|
} from './types'
|
||||||
|
|
||||||
export { chunterId } from '@hcengineering/chunter'
|
export { chunterId } from '@hcengineering/chunter'
|
||||||
@ -161,6 +161,10 @@ export function createModel (builder: Builder): void {
|
|||||||
presenter: chunter.component.ThreadMessagePresenter
|
presenter: chunter.component.ThreadMessagePresenter
|
||||||
})
|
})
|
||||||
|
|
||||||
|
builder.mixin(chunter.class.TypingInfo, core.class.Class, core.mixin.TransientConfiguration, {
|
||||||
|
broadcastOnly: true
|
||||||
|
})
|
||||||
|
|
||||||
builder.createDoc(
|
builder.createDoc(
|
||||||
view.class.Viewlet,
|
view.class.Viewlet,
|
||||||
core.space.Model,
|
core.space.Model,
|
||||||
|
@ -14,8 +14,6 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
import {
|
import {
|
||||||
type Card,
|
|
||||||
type CollaborativeDoc,
|
|
||||||
DOMAIN_BLOB,
|
DOMAIN_BLOB,
|
||||||
DOMAIN_CONFIGURATION,
|
DOMAIN_CONFIGURATION,
|
||||||
DOMAIN_DOC_INDEX_STATE,
|
DOMAIN_DOC_INDEX_STATE,
|
||||||
@ -27,8 +25,10 @@ import {
|
|||||||
type ArrOf,
|
type ArrOf,
|
||||||
type AttachedDoc,
|
type AttachedDoc,
|
||||||
type Blob,
|
type Blob,
|
||||||
|
type Card,
|
||||||
type Class,
|
type Class,
|
||||||
type ClassifierKind,
|
type ClassifierKind,
|
||||||
|
type CollaborativeDoc,
|
||||||
type Collection,
|
type Collection,
|
||||||
type Configuration,
|
type Configuration,
|
||||||
type ConfigurationElement,
|
type ConfigurationElement,
|
||||||
@ -50,6 +50,7 @@ import {
|
|||||||
type RefTo,
|
type RefTo,
|
||||||
type Space,
|
type Space,
|
||||||
type Timestamp,
|
type Timestamp,
|
||||||
|
type TransientConfiguration,
|
||||||
type Type,
|
type Type,
|
||||||
type TypeAny,
|
type TypeAny,
|
||||||
type Version
|
type Version
|
||||||
@ -403,3 +404,9 @@ export class TTypeCollaborativeDocVersion extends TType {}
|
|||||||
@UX(core.string.Rank)
|
@UX(core.string.Rank)
|
||||||
@Model(core.class.TypeRank, core.class.Type)
|
@Model(core.class.TypeRank, core.class.Type)
|
||||||
export class TTypeRank extends TType {}
|
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
|
||||||
|
}
|
||||||
|
@ -39,12 +39,12 @@ import {
|
|||||||
TAttachedDoc,
|
TAttachedDoc,
|
||||||
TAttribute,
|
TAttribute,
|
||||||
TBlob,
|
TBlob,
|
||||||
|
TCard,
|
||||||
TClass,
|
TClass,
|
||||||
TCollection,
|
TCollection,
|
||||||
TConfiguration,
|
TConfiguration,
|
||||||
TConfigurationElement,
|
TConfigurationElement,
|
||||||
TDoc,
|
TDoc,
|
||||||
TCard,
|
|
||||||
TDocIndexState,
|
TDocIndexState,
|
||||||
TDomainIndexConfiguration,
|
TDomainIndexConfiguration,
|
||||||
TEnum,
|
TEnum,
|
||||||
@ -57,6 +57,7 @@ import {
|
|||||||
TObj,
|
TObj,
|
||||||
TPluginConfiguration,
|
TPluginConfiguration,
|
||||||
TRefTo,
|
TRefTo,
|
||||||
|
TTransientConfiguration,
|
||||||
TType,
|
TType,
|
||||||
TTypeAny,
|
TTypeAny,
|
||||||
TTypeBlob,
|
TTypeBlob,
|
||||||
@ -173,7 +174,8 @@ export function createModel (builder: Builder): void {
|
|||||||
TMigrationState,
|
TMigrationState,
|
||||||
TBlob,
|
TBlob,
|
||||||
TDomainIndexConfiguration,
|
TDomainIndexConfiguration,
|
||||||
TBenchmarkDoc
|
TBenchmarkDoc,
|
||||||
|
TTransientConfiguration
|
||||||
)
|
)
|
||||||
|
|
||||||
builder.createDoc(
|
builder.createDoc(
|
||||||
|
@ -15,8 +15,8 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
import type { Asset, IntlString, Plugin } from '@hcengineering/platform'
|
import type { Asset, IntlString, Plugin } from '@hcengineering/platform'
|
||||||
import type { DocumentQuery } from './storage'
|
|
||||||
import { CollaborativeDoc } from './collaboration'
|
import { CollaborativeDoc } from './collaboration'
|
||||||
|
import type { DocumentQuery } from './storage'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @public
|
* @public
|
||||||
@ -346,6 +346,14 @@ export const DOMAIN_MIGRATION = '_migrations' as Domain
|
|||||||
*/
|
*/
|
||||||
export const DOMAIN_TRANSIENT = 'transient' as Domain
|
export const DOMAIN_TRANSIENT = 'transient' as Domain
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
export interface TransientConfiguration extends Class<Doc> {
|
||||||
|
// If set will not store transient objects into memdb
|
||||||
|
broadcastOnly: boolean
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Special domain to access s3 blob data.
|
* Special domain to access s3 blob data.
|
||||||
* @public
|
* @public
|
||||||
|
@ -48,6 +48,7 @@ import type {
|
|||||||
SpaceTypeDescriptor,
|
SpaceTypeDescriptor,
|
||||||
SystemSpace,
|
SystemSpace,
|
||||||
Timestamp,
|
Timestamp,
|
||||||
|
TransientConfiguration,
|
||||||
Type,
|
Type,
|
||||||
TypeAny,
|
TypeAny,
|
||||||
TypedSpace,
|
TypedSpace,
|
||||||
@ -148,7 +149,8 @@ export default plugin(coreId, {
|
|||||||
mixin: {
|
mixin: {
|
||||||
ConfigurationElement: '' as Ref<Mixin<ConfigurationElement>>,
|
ConfigurationElement: '' as Ref<Mixin<ConfigurationElement>>,
|
||||||
IndexConfiguration: '' as Ref<Mixin<IndexingConfiguration<Doc>>>,
|
IndexConfiguration: '' as Ref<Mixin<IndexingConfiguration<Doc>>>,
|
||||||
SpacesTypeData: '' as Ref<Mixin<Space>>
|
SpacesTypeData: '' as Ref<Mixin<Space>>,
|
||||||
|
TransientConfiguration: '' as Ref<Mixin<TransientConfiguration>>
|
||||||
},
|
},
|
||||||
space: {
|
space: {
|
||||||
Tx: '' as Ref<Space>,
|
Tx: '' as Ref<Space>,
|
||||||
|
@ -13,7 +13,7 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
-->
|
-->
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { getResource, getResourceP } from '@hcengineering/platform'
|
import { getResourceP } from '@hcengineering/platform'
|
||||||
import { deepEqual } from 'fast-equals'
|
import { deepEqual } from 'fast-equals'
|
||||||
import { SvelteComponent } from 'svelte'
|
import { SvelteComponent } from 'svelte'
|
||||||
import type { AnyComponent, AnySvelteComponent } from '../types'
|
import type { AnyComponent, AnySvelteComponent } from '../types'
|
||||||
@ -70,6 +70,7 @@
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
|
loading = false
|
||||||
Ctor = component
|
Ctor = component
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -14,9 +14,9 @@
|
|||||||
-->
|
-->
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { DirectMessage } from '@hcengineering/chunter'
|
import { DirectMessage } from '@hcengineering/chunter'
|
||||||
import contact, { Person, PersonAccount } from '@hcengineering/contact'
|
import contact, { Person } from '@hcengineering/contact'
|
||||||
import { Avatar, CombineAvatars, personAccountByIdStore, personByIdStore } from '@hcengineering/contact-resources'
|
import { Avatar, CombineAvatars, personByIdStore } from '@hcengineering/contact-resources'
|
||||||
import { Account, IdMap, Ref } from '@hcengineering/core'
|
import { Ref } from '@hcengineering/core'
|
||||||
import { getClient } from '@hcengineering/presentation'
|
import { getClient } from '@hcengineering/presentation'
|
||||||
import { Icon, IconSize } from '@hcengineering/ui'
|
import { Icon, IconSize } from '@hcengineering/ui'
|
||||||
import { classIcon } from '@hcengineering/view-resources'
|
import { classIcon } from '@hcengineering/view-resources'
|
||||||
@ -54,10 +54,6 @@
|
|||||||
$: if (size === 'small') {
|
$: if (size === 'small') {
|
||||||
avatarSize = 'x-small'
|
avatarSize = 'x-small'
|
||||||
}
|
}
|
||||||
|
|
||||||
function getAccountByPerson (accountById: IdMap<PersonAccount>, person: Person): Account | undefined {
|
|
||||||
return Array.from(accountById.values()).find((account) => account.person === person._id)
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if persons.length === 0}
|
{#if persons.length === 0}
|
||||||
@ -65,13 +61,7 @@
|
|||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
{#if persons.length === 1}
|
{#if persons.length === 1}
|
||||||
<Avatar
|
<Avatar person={persons[0]} size={avatarSize} name={persons[0].name} {showStatus} />
|
||||||
person={persons[0]}
|
|
||||||
size={avatarSize}
|
|
||||||
name={persons[0].name}
|
|
||||||
{showStatus}
|
|
||||||
account={getAccountByPerson($personAccountByIdStore, persons[0])?._id}
|
|
||||||
/>
|
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
{#if persons.length > 1 && size === 'medium'}
|
{#if persons.length > 1 && size === 'medium'}
|
||||||
|
@ -31,8 +31,7 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { getAvatarProviderId, getFirstName, getLastName } from '@hcengineering/contact'
|
import { Employee, getAvatarProviderId, getFirstName, getLastName, Person } from '@hcengineering/contact'
|
||||||
import { Account } from '@hcengineering/core'
|
|
||||||
import { Asset, getMetadata, getResource } from '@hcengineering/platform'
|
import { Asset, getMetadata, getResource } from '@hcengineering/platform'
|
||||||
import { getBlobURL, reduceCalls } from '@hcengineering/presentation'
|
import { getBlobURL, reduceCalls } from '@hcengineering/presentation'
|
||||||
import {
|
import {
|
||||||
@ -45,10 +44,11 @@
|
|||||||
themeStore
|
themeStore
|
||||||
} from '@hcengineering/ui'
|
} from '@hcengineering/ui'
|
||||||
import { onMount } from 'svelte'
|
import { onMount } from 'svelte'
|
||||||
import { loadUsersStatus, statusByUserStore } from '../utils'
|
|
||||||
|
import { loadUsersStatus, employeeByIdStore, personAccountByPersonId, statusByUserStore } from '../utils'
|
||||||
import AvatarInstance from './AvatarInstance.svelte'
|
import AvatarInstance from './AvatarInstance.svelte'
|
||||||
|
|
||||||
export let person: Data<WithLookup<AvatarInfo>> | undefined = undefined
|
export let person: (Data<WithLookup<AvatarInfo>> & { _id?: Ref<Person> }) | undefined = undefined
|
||||||
export let name: string | null | undefined = undefined
|
export let name: string | null | undefined = undefined
|
||||||
export let direct: Blob | undefined = undefined
|
export let direct: Blob | undefined = undefined
|
||||||
export let size: IconSize
|
export let size: IconSize
|
||||||
@ -56,7 +56,6 @@
|
|||||||
export let variant: 'circle' | 'roundedRect' | 'none' = 'roundedRect'
|
export let variant: 'circle' | 'roundedRect' | 'none' = 'roundedRect'
|
||||||
export let borderColor: number | undefined = undefined
|
export let borderColor: number | undefined = undefined
|
||||||
export let showStatus: boolean = true
|
export let showStatus: boolean = true
|
||||||
export let account: Ref<Account> | undefined = undefined
|
|
||||||
|
|
||||||
export function pulse (): void {
|
export function pulse (): void {
|
||||||
avatarInst.pulse()
|
avatarInst.pulse()
|
||||||
@ -126,10 +125,14 @@
|
|||||||
loadUsersStatus()
|
loadUsersStatus()
|
||||||
})
|
})
|
||||||
|
|
||||||
$: userStatus = account !== undefined ? $statusByUserStore.get(account) : undefined
|
let employee: Employee | undefined = undefined
|
||||||
|
|
||||||
|
$: employee = person?._id && showStatus ? $employeeByIdStore.get(person._id as Ref<Employee>) : undefined
|
||||||
|
$: accounts = employee?.active ? $personAccountByPersonId.get(employee._id) ?? [] : []
|
||||||
|
$: isOnline = accounts.some((account) => $statusByUserStore.get(account._id)?.online === true)
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if showStatus && account}
|
{#if showStatus && accounts.length > 0}
|
||||||
<div class="relative">
|
<div class="relative">
|
||||||
<AvatarInstance
|
<AvatarInstance
|
||||||
bind:this={avatarInst}
|
bind:this={avatarInst}
|
||||||
@ -144,7 +147,7 @@
|
|||||||
bind:element
|
bind:element
|
||||||
withStatus
|
withStatus
|
||||||
/>
|
/>
|
||||||
<div class="hulyAvatar-statusMarker {size}" class:online={userStatus?.online} class:offline={!userStatus?.online} />
|
<div class="hulyAvatar-statusMarker {size}" class:online={isOnline} class:offline={!isOnline} />
|
||||||
</div>
|
</div>
|
||||||
{:else}
|
{:else}
|
||||||
<AvatarInstance
|
<AvatarInstance
|
||||||
|
@ -15,10 +15,11 @@
|
|||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import contact, { type Contact, type Employee } from '@hcengineering/contact'
|
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 { Asset } from '@hcengineering/platform'
|
||||||
import { getClient } from '@hcengineering/presentation'
|
import { getClient } from '@hcengineering/presentation'
|
||||||
import { AnySvelteComponent, IconSize } from '@hcengineering/ui'
|
import { AnySvelteComponent, IconSize } from '@hcengineering/ui'
|
||||||
|
|
||||||
import { employeeByIdStore, personByIdStore } from '../utils'
|
import { employeeByIdStore, personByIdStore } from '../utils'
|
||||||
import Avatar from './Avatar.svelte'
|
import Avatar from './Avatar.svelte'
|
||||||
|
|
||||||
@ -30,7 +31,6 @@
|
|||||||
export let variant: 'circle' | 'roundedRect' | 'none' = 'roundedRect'
|
export let variant: 'circle' | 'roundedRect' | 'none' = 'roundedRect'
|
||||||
export let borderColor: number | undefined = undefined
|
export let borderColor: number | undefined = undefined
|
||||||
export let showStatus: boolean = true
|
export let showStatus: boolean = true
|
||||||
export let account: Ref<Account> | undefined = undefined
|
|
||||||
|
|
||||||
$: empValue = $employeeByIdStore.get(_id as Ref<Employee>) ?? $personByIdStore.get(_id)
|
$: empValue = $employeeByIdStore.get(_id as Ref<Employee>) ?? $personByIdStore.get(_id)
|
||||||
|
|
||||||
@ -47,4 +47,4 @@
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Avatar person={_contact} {name} {size} {icon} {variant} {borderColor} {showStatus} {account} />
|
<Avatar person={_contact} {name} {size} {icon} {variant} {borderColor} {showStatus} />
|
||||||
|
@ -13,14 +13,12 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
-->
|
-->
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import contact, { Employee, Person } from '@hcengineering/contact'
|
import { Employee, Person } from '@hcengineering/contact'
|
||||||
import { IconSize, LabelAndProps, tooltip } from '@hcengineering/ui'
|
import { IconSize, LabelAndProps, tooltip } from '@hcengineering/ui'
|
||||||
import { DocNavLink, ObjectMention } from '@hcengineering/view-resources'
|
import { DocNavLink, ObjectMention } from '@hcengineering/view-resources'
|
||||||
import { ObjectPresenterType } from '@hcengineering/view'
|
import { ObjectPresenterType } from '@hcengineering/view'
|
||||||
|
|
||||||
import Avatar from './Avatar.svelte'
|
import Avatar from './Avatar.svelte'
|
||||||
import { personAccountByIdStore } from '../utils'
|
|
||||||
import { getClient } from '@hcengineering/presentation'
|
|
||||||
|
|
||||||
export let value: Person | Employee | undefined | null
|
export let value: Person | Employee | undefined | null
|
||||||
export let name: string
|
export let name: string
|
||||||
@ -39,12 +37,6 @@
|
|||||||
export let type: ObjectPresenterType = 'link'
|
export let type: ObjectPresenterType = 'link'
|
||||||
export let showStatus = true
|
export let showStatus = true
|
||||||
export let overflowLabel = true
|
export let overflowLabel = true
|
||||||
|
|
||||||
const client = getClient()
|
|
||||||
const hierarchy = client.getHierarchy()
|
|
||||||
|
|
||||||
$: showStatus = showStatus && !!value && hierarchy.hasMixin(value, contact.mixin.Employee)
|
|
||||||
$: account = value && Array.from($personAccountByIdStore.values()).find((account) => account.person === value?._id)
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if value}
|
{#if value}
|
||||||
@ -64,7 +56,7 @@
|
|||||||
class:mr-2={shouldShowName && !enlargedText}
|
class:mr-2={shouldShowName && !enlargedText}
|
||||||
class:mr-3={shouldShowName && enlargedText}
|
class:mr-3={shouldShowName && enlargedText}
|
||||||
>
|
>
|
||||||
<Avatar size={avatarSize} person={value} name={value.name} {showStatus} account={account?._id} />
|
<Avatar size={avatarSize} person={value} name={value.name} {showStatus} />
|
||||||
</span>
|
</span>
|
||||||
{/if}
|
{/if}
|
||||||
{#if shouldShowName}
|
{#if shouldShowName}
|
||||||
|
@ -15,37 +15,23 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { getClient } from '@hcengineering/presentation'
|
import { getClient } from '@hcengineering/presentation'
|
||||||
import { IconSize } from '@hcengineering/ui'
|
import { IconSize } from '@hcengineering/ui'
|
||||||
import { Person, getName, PersonAccount } from '@hcengineering/contact'
|
import { Person, getName } from '@hcengineering/contact'
|
||||||
import { Account, IdMap } from '@hcengineering/core'
|
|
||||||
|
|
||||||
import Avatar from './Avatar.svelte'
|
import Avatar from './Avatar.svelte'
|
||||||
import { personAccountByIdStore } from '../utils'
|
|
||||||
|
|
||||||
export let person: Person
|
export let person: Person
|
||||||
export let avatarSize: IconSize = 'x-small'
|
export let avatarSize: IconSize = 'x-small'
|
||||||
export let showStatus = true
|
export let showStatus = true
|
||||||
|
|
||||||
const client = getClient()
|
const client = getClient()
|
||||||
const hierarchy = client.getHierarchy()
|
|
||||||
|
|
||||||
function getAccountByPerson (accountById: IdMap<PersonAccount>, person: Person): Account | undefined {
|
|
||||||
return Array.from(accountById.values()).find((account) => account.person === person._id)
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||||
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
||||||
<div class="flex-row-center" on:click>
|
<div class="flex-row-center" on:click>
|
||||||
<Avatar
|
<Avatar {person} size={avatarSize} name={person.name} on:accent-color {showStatus} />
|
||||||
{person}
|
|
||||||
size={avatarSize}
|
|
||||||
name={person.name}
|
|
||||||
on:accent-color
|
|
||||||
{showStatus}
|
|
||||||
account={getAccountByPerson($personAccountByIdStore, person)?._id}
|
|
||||||
/>
|
|
||||||
<div class="flex-col min-w-0 {avatarSize === 'tiny' || avatarSize === 'inline' ? 'ml-1' : 'ml-3'}">
|
<div class="flex-col min-w-0 {avatarSize === 'tiny' || avatarSize === 'inline' ? 'ml-1' : 'ml-3'}">
|
||||||
<div class="label overflow-label text-left">{getName(hierarchy, person)}</div>
|
<div class="label overflow-label text-left">{getName(client.getHierarchy(), person)}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -15,11 +15,10 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import Avatar from './Avatar.svelte'
|
import Avatar from './Avatar.svelte'
|
||||||
|
|
||||||
import contact, { getName, Person } from '@hcengineering/contact'
|
import { getName, Person } from '@hcengineering/contact'
|
||||||
import { Asset } from '@hcengineering/platform'
|
import { Asset } from '@hcengineering/platform'
|
||||||
import { getClient } from '@hcengineering/presentation'
|
import { getClient } from '@hcengineering/presentation'
|
||||||
import { AnySvelteComponent, IconSize } from '@hcengineering/ui'
|
import { AnySvelteComponent, IconSize } from '@hcengineering/ui'
|
||||||
import { personAccountByIdStore } from '../utils'
|
|
||||||
|
|
||||||
export let value: Person
|
export let value: Person
|
||||||
export let subtitle: string | undefined = undefined
|
export let subtitle: string | undefined = undefined
|
||||||
@ -29,16 +28,12 @@
|
|||||||
export let showStatus = true
|
export let showStatus = true
|
||||||
|
|
||||||
const client = getClient()
|
const client = getClient()
|
||||||
const hierarchy = client.getHierarchy()
|
|
||||||
|
|
||||||
$: showStatus = showStatus && !!value && hierarchy.hasMixin(value, contact.mixin.Employee)
|
|
||||||
$: account = value && Array.from($personAccountByIdStore.values()).find((account) => account.person === value?._id)
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||||
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
||||||
<div class="flex-row-center" on:click>
|
<div class="flex-row-center" on:click>
|
||||||
<Avatar person={value} {size} {icon} name={value.name} on:accent-color {showStatus} account={account?._id} />
|
<Avatar person={value} {size} {icon} name={value.name} on:accent-color {showStatus} />
|
||||||
<div class="flex-col min-w-0 {size === 'tiny' || size === 'inline' ? 'ml-1' : 'ml-2'}" class:max-w-20={short}>
|
<div class="flex-col min-w-0 {size === 'tiny' || size === 'inline' ? 'ml-1' : 'ml-2'}" class:max-w-20={short}>
|
||||||
{#if subtitle}<div class="content-dark-color text-sm">{subtitle}</div>{/if}
|
{#if subtitle}<div class="content-dark-color text-sm">{subtitle}</div>{/if}
|
||||||
<div class="label text-left overflow-label">{getName(client.getHierarchy(), value)}</div>
|
<div class="label text-left overflow-label">{getName(client.getHierarchy(), value)}</div>
|
||||||
|
@ -13,6 +13,7 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
//
|
//
|
||||||
|
|
||||||
|
import { Analytics } from '@hcengineering/analytics'
|
||||||
import core, {
|
import core, {
|
||||||
BaseWorkspaceInfo,
|
BaseWorkspaceInfo,
|
||||||
DOMAIN_TX,
|
DOMAIN_TX,
|
||||||
@ -81,7 +82,7 @@ class BackupWorker {
|
|||||||
`****************************************
|
`****************************************
|
||||||
backup statistics:`,
|
backup statistics:`,
|
||||||
{
|
{
|
||||||
backuped: stats.processed,
|
processed: stats.processed,
|
||||||
notChanges: stats.skipped,
|
notChanges: stats.skipped,
|
||||||
failed: stats.failedWorkspaces.length
|
failed: stats.failedWorkspaces.length
|
||||||
}
|
}
|
||||||
@ -91,15 +92,23 @@ class BackupWorker {
|
|||||||
async schedule (ctx: MeasureContext): Promise<void> {
|
async schedule (ctx: MeasureContext): Promise<void> {
|
||||||
console.log('schedule backup with interval', this.config.Interval, 'seconds')
|
console.log('schedule backup with interval', this.config.Interval, 'seconds')
|
||||||
while (!this.canceled) {
|
while (!this.canceled) {
|
||||||
const res = await this.backup(ctx)
|
try {
|
||||||
this.printStats(ctx, res)
|
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<void>((resolve) => setTimeout(resolve, (this.config.CoolDown / 5) * 1000))
|
||||||
|
continue
|
||||||
|
}
|
||||||
console.log('cool down', this.config.CoolDown, 'seconds')
|
console.log('cool down', this.config.CoolDown, 'seconds')
|
||||||
await new Promise<void>((resolve) => setTimeout(resolve, this.config.CoolDown * 1000))
|
await new Promise<void>((resolve) => setTimeout(resolve, this.config.CoolDown * 1000))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async backup (
|
async backup (
|
||||||
ctx: MeasureContext
|
ctx: MeasureContext,
|
||||||
|
recheckTimeout: number
|
||||||
): Promise<{ failedWorkspaces: BaseWorkspaceInfo[], processed: number, skipped: number }> {
|
): Promise<{ failedWorkspaces: BaseWorkspaceInfo[], processed: number, skipped: number }> {
|
||||||
const workspacesIgnore = new Set(this.config.SkipWorkspaces.split(';'))
|
const workspacesIgnore = new Set(this.config.SkipWorkspaces.split(';'))
|
||||||
ctx.info('skipped workspaces', { workspacesIgnore })
|
ctx.info('skipped workspaces', { workspacesIgnore })
|
||||||
@ -135,19 +144,21 @@ class BackupWorker {
|
|||||||
workspaces: workspaces.map((it) => it.workspace)
|
workspaces: workspaces.map((it) => it.workspace)
|
||||||
})
|
})
|
||||||
|
|
||||||
return await this.doBackup(ctx, workspaces)
|
return await this.doBackup(ctx, workspaces, recheckTimeout)
|
||||||
}
|
}
|
||||||
|
|
||||||
async doBackup (
|
async doBackup (
|
||||||
rootCtx: MeasureContext,
|
rootCtx: MeasureContext,
|
||||||
workspaces: BaseWorkspaceInfo[]
|
workspaces: BaseWorkspaceInfo[],
|
||||||
|
recheckTimeout: number
|
||||||
): Promise<{ failedWorkspaces: BaseWorkspaceInfo[], processed: number, skipped: number }> {
|
): Promise<{ failedWorkspaces: BaseWorkspaceInfo[], processed: number, skipped: number }> {
|
||||||
let index = 0
|
let index = 0
|
||||||
|
|
||||||
const failedWorkspaces: BaseWorkspaceInfo[] = []
|
const failedWorkspaces: BaseWorkspaceInfo[] = []
|
||||||
let processed = 0
|
let processed = 0
|
||||||
|
const startTime = Date.now()
|
||||||
for (const ws of workspaces) {
|
for (const ws of workspaces) {
|
||||||
if (this.canceled) {
|
if (this.canceled || Date.now() - startTime > recheckTimeout) {
|
||||||
return { failedWorkspaces, processed, skipped: workspaces.length - processed }
|
return { failedWorkspaces, processed, skipped: workspaces.length - processed }
|
||||||
}
|
}
|
||||||
index++
|
index++
|
||||||
|
@ -30,10 +30,12 @@ import core, {
|
|||||||
type StorageIterator,
|
type StorageIterator,
|
||||||
toFindResult,
|
toFindResult,
|
||||||
type Tx,
|
type Tx,
|
||||||
|
type TxCUD,
|
||||||
|
TxProcessor,
|
||||||
type TxResult,
|
type TxResult,
|
||||||
type WorkspaceId
|
type WorkspaceId
|
||||||
} from '@hcengineering/core'
|
} from '@hcengineering/core'
|
||||||
import { type DbAdapterHandler, type DbAdapter, type DomainHelperOperations } from './adapter'
|
import { type DbAdapter, type DbAdapterHandler, type DomainHelperOperations } from './adapter'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @public
|
* @public
|
||||||
@ -119,7 +121,7 @@ export class DummyDbAdapter implements DbAdapter {
|
|||||||
class InMemoryAdapter extends DummyDbAdapter implements DbAdapter {
|
class InMemoryAdapter extends DummyDbAdapter implements DbAdapter {
|
||||||
private readonly modeldb: ModelDb
|
private readonly modeldb: ModelDb
|
||||||
|
|
||||||
constructor (hierarchy: Hierarchy) {
|
constructor (readonly hierarchy: Hierarchy) {
|
||||||
super()
|
super()
|
||||||
this.modeldb = new ModelDb(hierarchy)
|
this.modeldb = new ModelDb(hierarchy)
|
||||||
}
|
}
|
||||||
@ -138,7 +140,23 @@ class InMemoryAdapter extends DummyDbAdapter implements DbAdapter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async tx (ctx: MeasureContext, ...tx: Tx[]): Promise<TxResult[]> {
|
async tx (ctx: MeasureContext, ...tx: Tx[]): Promise<TxResult[]> {
|
||||||
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<Doc>
|
||||||
|
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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user