mirror of
https://github.com/hcengineering/platform.git
synced 2024-12-23 03:22:19 +03:00
UBERF-4716: Activity info message (#4241)
Signed-off-by: Andrey Sobolev <haiodo@gmail.com>
This commit is contained in:
parent
02e5513dda
commit
b5bb679998
@ -15,6 +15,7 @@
|
||||
|
||||
import {
|
||||
type ActivityAttributeUpdatesPresenter,
|
||||
type ActivityInfoMessage,
|
||||
type ActivityDoc,
|
||||
type ActivityExtension,
|
||||
type ActivityExtensionKind,
|
||||
@ -28,7 +29,8 @@ import {
|
||||
type DocUpdateMessageViewlet,
|
||||
type DocUpdateMessageViewletAttributesConfig,
|
||||
type Reaction,
|
||||
type TxViewlet
|
||||
type TxViewlet,
|
||||
type ActivityMessageControl
|
||||
} from '@hcengineering/activity'
|
||||
import core, {
|
||||
DOMAIN_MODEL,
|
||||
@ -51,7 +53,8 @@ import {
|
||||
TypeString,
|
||||
Mixin,
|
||||
Collection,
|
||||
TypeBoolean
|
||||
TypeBoolean,
|
||||
TypeIntlString
|
||||
} from '@hcengineering/model'
|
||||
import { TAttachedDoc, TClass, TDoc } from '@hcengineering/model-core'
|
||||
import type { Asset, IntlString, Resource } from '@hcengineering/platform'
|
||||
@ -119,6 +122,24 @@ export class TDocUpdateMessage extends TActivityMessage implements DocUpdateMess
|
||||
attributeUpdates?: DocAttributeUpdates
|
||||
}
|
||||
|
||||
@Model(activity.class.ActivityInfoMessage, activity.class.ActivityMessage, DOMAIN_ACTIVITY)
|
||||
export class TActivityInfoMessage extends TActivityMessage implements ActivityInfoMessage {
|
||||
@Prop(TypeIntlString(), activity.string.Update)
|
||||
message!: IntlString
|
||||
|
||||
props!: Record<string, any>
|
||||
icon!: Asset
|
||||
iconProps!: Record<string, any>
|
||||
}
|
||||
|
||||
@Model(activity.class.ActivityMessageControl, core.class.Doc, DOMAIN_MODEL)
|
||||
export class TActivityMessageControl extends TDoc implements ActivityMessageControl {
|
||||
objectClass!: Ref<Class<Doc>>
|
||||
|
||||
// A set of rules to be skipped from generate doc update activity messages
|
||||
skip!: DocumentQuery<Tx>[]
|
||||
}
|
||||
|
||||
@Model(activity.class.DocUpdateMessageViewlet, core.class.Doc, DOMAIN_MODEL)
|
||||
export class TDocUpdateMessageViewlet extends TDoc implements DocUpdateMessageViewlet {
|
||||
@Prop(TypeRef(core.class.Doc), core.string.Class)
|
||||
@ -184,7 +205,9 @@ export function createModel (builder: Builder): void {
|
||||
TDocUpdateMessageViewlet,
|
||||
TActivityExtension,
|
||||
TReaction,
|
||||
TActivityAttributeUpdatesPresenter
|
||||
TActivityAttributeUpdatesPresenter,
|
||||
TActivityInfoMessage,
|
||||
TActivityMessageControl
|
||||
)
|
||||
|
||||
builder.mixin(activity.class.DocUpdateMessage, core.class.Class, activity.mixin.ActivityDoc, {})
|
||||
@ -193,6 +216,10 @@ export function createModel (builder: Builder): void {
|
||||
presenter: activity.component.DocUpdateMessagePresenter
|
||||
})
|
||||
|
||||
builder.mixin(activity.class.ActivityInfoMessage, core.class.Class, view.mixin.ObjectPresenter, {
|
||||
presenter: activity.component.ActivityInfoMessagePresenter
|
||||
})
|
||||
|
||||
builder.createDoc(activity.class.ActivityMessagesFilter, core.space.Model, {
|
||||
label: activity.string.Attributes,
|
||||
filter: activity.filter.AttributesFilter
|
||||
|
@ -127,7 +127,7 @@ export async function translate<P extends Record<string, any>> (
|
||||
cache.set(message, translation)
|
||||
return message
|
||||
}
|
||||
const compiled = new IntlMessageFormat(translation, locale)
|
||||
const compiled = new IntlMessageFormat(translation, locale, undefined, { ignoreTag: true })
|
||||
cache.set(message, compiled)
|
||||
return compiled.format(params)
|
||||
} catch (err) {
|
||||
|
@ -39,7 +39,7 @@
|
||||
|
||||
let isNewestFirst = JSON.parse(localStorage.getItem('activity-newest-first') ?? 'false')
|
||||
|
||||
$: client.findAll(activity.class.ActivityExtension, { ofClass: object._class }).then((res) => {
|
||||
$: void client.findAll(activity.class.ActivityExtension, { ofClass: object._class }).then((res) => {
|
||||
extensions = res
|
||||
})
|
||||
|
||||
@ -64,7 +64,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
$: updateActivityMessages(object._id, isNewestFirst ? SortingOrder.Descending : SortingOrder.Ascending)
|
||||
$: void updateActivityMessages(object._id, isNewestFirst ? SortingOrder.Descending : SortingOrder.Ascending)
|
||||
</script>
|
||||
|
||||
<div class="antiSection-header high mt-9" class:invisible={transparent}>
|
||||
|
@ -0,0 +1,107 @@
|
||||
<!--
|
||||
// Copyright © 2023 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 { ActivityInfoMessage } from '@hcengineering/activity'
|
||||
import { Employee, PersonAccount } from '@hcengineering/contact'
|
||||
import {
|
||||
Avatar,
|
||||
SystemAvatar,
|
||||
employeeByIdStore,
|
||||
personAccountByIdStore,
|
||||
personByIdStore
|
||||
} from '@hcengineering/contact-resources'
|
||||
|
||||
import ActivityMessageTemplate from './ActivityMessageTemplate.svelte'
|
||||
|
||||
import { Ref } from '@hcengineering/core'
|
||||
import { translate } from '@hcengineering/platform'
|
||||
import { MessageViewer } from '@hcengineering/presentation'
|
||||
import ActivityMessageHeader from './ActivityMessageHeader.svelte'
|
||||
|
||||
export let value: ActivityInfoMessage
|
||||
export let showNotify: boolean = false
|
||||
export let isHighlighted: boolean = false
|
||||
export let isSelected: boolean = false
|
||||
export let shouldScroll: boolean = false
|
||||
export let embedded: boolean = false
|
||||
export let hasActionsMenu: boolean = true
|
||||
export let onClick: (() => void) | undefined = undefined
|
||||
|
||||
$: personAccount = $personAccountByIdStore.get((value.createdBy ?? value.modifiedBy) as Ref<PersonAccount>)
|
||||
$: person =
|
||||
personAccount?.person !== undefined
|
||||
? $employeeByIdStore.get(personAccount.person as Ref<Employee>) ?? $personByIdStore.get(personAccount.person)
|
||||
: undefined
|
||||
|
||||
let content = ''
|
||||
|
||||
$: void translate(value.message, value.props)
|
||||
.then((message) => {
|
||||
content = message
|
||||
})
|
||||
.catch((err) => {
|
||||
content = JSON.stringify(err, null, 2)
|
||||
})
|
||||
</script>
|
||||
|
||||
<ActivityMessageTemplate
|
||||
message={value}
|
||||
parentMessage={undefined}
|
||||
{person}
|
||||
{showNotify}
|
||||
{isHighlighted}
|
||||
{isSelected}
|
||||
{shouldScroll}
|
||||
{embedded}
|
||||
{hasActionsMenu}
|
||||
viewlet={undefined}
|
||||
{onClick}
|
||||
>
|
||||
<svelte:fragment slot="icon">
|
||||
{#if value.icon}
|
||||
<SystemAvatar size="medium" icon={value.icon} iconProps={value.iconProps} />
|
||||
{:else if person}
|
||||
<Avatar size="medium" avatar={person.avatar} name={person.name} />
|
||||
{:else}
|
||||
<SystemAvatar size="medium" />
|
||||
{/if}
|
||||
</svelte:fragment>
|
||||
<svelte:fragment slot="header">
|
||||
<ActivityMessageHeader
|
||||
message={value}
|
||||
{person}
|
||||
object={undefined}
|
||||
parentObject={undefined}
|
||||
isEdited={false}
|
||||
label={value.title}
|
||||
/>
|
||||
</svelte:fragment>
|
||||
<svelte:fragment slot="content">
|
||||
<div class="flex-row-center">
|
||||
<div class="customContent">
|
||||
<MessageViewer message={content} />
|
||||
</div>
|
||||
</div>
|
||||
</svelte:fragment>
|
||||
</ActivityMessageTemplate>
|
||||
|
||||
<style lang="scss">
|
||||
.customContent {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
column-gap: 0.625rem;
|
||||
row-gap: 0.625rem;
|
||||
}
|
||||
</style>
|
@ -0,0 +1,63 @@
|
||||
<!--
|
||||
// Copyright © 2023 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 { ActivityMessage } from '@hcengineering/activity'
|
||||
import { Person } from '@hcengineering/contact'
|
||||
import { Doc } from '@hcengineering/core'
|
||||
import notification from '../../plugin'
|
||||
import { Label } from '@hcengineering/ui'
|
||||
import { DocNavLink } from '@hcengineering/view-resources'
|
||||
import { LinkData, getLinkData } from '../../activityMessagesUtils'
|
||||
import { IntlString } from '@hcengineering/platform'
|
||||
|
||||
export let message: ActivityMessage
|
||||
export let person: Person | undefined
|
||||
export let object: Doc | undefined
|
||||
export let parentObject: Doc | undefined
|
||||
export let label: IntlString | undefined = undefined
|
||||
export let isEdited: boolean = false
|
||||
|
||||
let linkData: LinkData | undefined = undefined
|
||||
|
||||
$: void getLinkData(message, object, parentObject, person).then((data) => {
|
||||
linkData = data
|
||||
})
|
||||
</script>
|
||||
|
||||
<span class="text-sm lower">
|
||||
{#if label}
|
||||
<Label {label} />
|
||||
{/if}
|
||||
</span>
|
||||
|
||||
{#if linkData}
|
||||
<span class="text-sm lower"><Label label={linkData.preposition} /></span>
|
||||
<span class="text-sm">
|
||||
<DocNavLink {object} component={linkData.panelComponent} shrink={0}>
|
||||
<span class="overflow-label select-text">{linkData.title}</span>
|
||||
</DocNavLink>
|
||||
</span>
|
||||
{#if isEdited}
|
||||
<span class="text-sm lower"><Label label={notification.string.Edited} /></span>
|
||||
{/if}
|
||||
{/if}
|
||||
|
||||
<style lang="scss">
|
||||
span {
|
||||
margin-left: 0.25rem;
|
||||
font-weight: 400;
|
||||
line-height: 1.25rem;
|
||||
}
|
||||
</style>
|
@ -13,23 +13,22 @@
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import activity, {
|
||||
ActivityMessageExtension,
|
||||
ActivityMessageViewlet,
|
||||
DisplayActivityMessage
|
||||
} from '@hcengineering/activity'
|
||||
import { Person } from '@hcengineering/contact'
|
||||
import { Avatar, EmployeePresenter, SystemAvatar } from '@hcengineering/contact-resources'
|
||||
import core, { getDisplayTime } from '@hcengineering/core'
|
||||
import { getClient } from '@hcengineering/presentation'
|
||||
import core from '@hcengineering/core/lib/component'
|
||||
import activity, {
|
||||
DisplayActivityMessage,
|
||||
ActivityMessageExtension,
|
||||
ActivityMessageViewlet
|
||||
} from '@hcengineering/activity'
|
||||
import { Action, ActionIcon, IconMoreH, Label, showPopup } from '@hcengineering/ui'
|
||||
import { getActions, Menu } from '@hcengineering/view-resources'
|
||||
import { getDisplayTime } from '@hcengineering/core'
|
||||
import { Menu, getActions } from '@hcengineering/view-resources'
|
||||
|
||||
import ActivityMessageExtensionComponent from './ActivityMessageExtension.svelte'
|
||||
import ActivityMessagePresenter from './ActivityMessagePresenter.svelte'
|
||||
import AddReactionAction from '../reactions/AddReactionAction.svelte'
|
||||
import ReactionsPresenter from '../reactions/ReactionsPresenter.svelte'
|
||||
import ActivityMessageExtensionComponent from './ActivityMessageExtension.svelte'
|
||||
import ActivityMessagePresenter from './ActivityMessagePresenter.svelte'
|
||||
import PinMessageAction from './PinMessageAction.svelte'
|
||||
|
||||
export let message: DisplayActivityMessage
|
||||
@ -54,38 +53,38 @@
|
||||
let extensions: ActivityMessageExtension[] = []
|
||||
let isActionMenuOpened = false
|
||||
|
||||
$: getActions(client, message, activity.class.ActivityMessage).then((res) => {
|
||||
$: void getActions(client, message, activity.class.ActivityMessage).then((res) => {
|
||||
allActionIds = res.map(({ _id }) => _id)
|
||||
})
|
||||
|
||||
function scrollToMessage () {
|
||||
if (element && shouldScroll) {
|
||||
function scrollToMessage (): void {
|
||||
if (element != null && shouldScroll) {
|
||||
element.scrollIntoView({ behavior: 'auto', block: 'end' })
|
||||
shouldScroll = false
|
||||
}
|
||||
}
|
||||
|
||||
$: if (element && shouldScroll) {
|
||||
$: if (element != null && shouldScroll) {
|
||||
setTimeout(scrollToMessage, 100)
|
||||
}
|
||||
|
||||
client
|
||||
void client
|
||||
.findAll(activity.class.ActivityMessageExtension, { ofMessage: message._class })
|
||||
.then((res: ActivityMessageExtension[]) => {
|
||||
extensions = res
|
||||
})
|
||||
|
||||
function handleActionMenuOpened () {
|
||||
function handleActionMenuOpened (): void {
|
||||
isActionMenuOpened = true
|
||||
}
|
||||
|
||||
function handleActionMenuClosed () {
|
||||
function handleActionMenuClosed (): void {
|
||||
isActionMenuOpened = false
|
||||
}
|
||||
|
||||
$: key = parentMessage ? `${message._id}_${parentMessage._id}` : message._id
|
||||
$: key = parentMessage != null ? `${message._id}_${parentMessage._id}` : message._id
|
||||
|
||||
function showMenu (ev: MouseEvent) {
|
||||
function showMenu (ev: MouseEvent): void {
|
||||
showPopup(
|
||||
Menu,
|
||||
{
|
||||
@ -124,7 +123,9 @@
|
||||
{/if}
|
||||
{#if !embedded}
|
||||
<div class="min-w-6 mt-1">
|
||||
{#if person}
|
||||
{#if $$slots.icon}
|
||||
<slot name="icon" />
|
||||
{:else if person}
|
||||
<Avatar size="medium" avatar={person.avatar} name={person.name} />
|
||||
{:else}
|
||||
<SystemAvatar size="medium" />
|
||||
@ -138,9 +139,9 @@
|
||||
{#if person}
|
||||
<EmployeePresenter value={person} shouldShowAvatar={false} />
|
||||
{:else}
|
||||
<span class="strong">
|
||||
<div class="strong">
|
||||
<Label label={core.string.System} />
|
||||
</span>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<slot name="header" />
|
||||
|
@ -36,7 +36,7 @@
|
||||
|
||||
let linkData: LinkData | undefined = undefined
|
||||
|
||||
$: getLinkData(message, object, parentObject, person).then((data) => {
|
||||
$: void getLinkData(message, object, parentObject, person).then((data) => {
|
||||
linkData = data
|
||||
})
|
||||
|
||||
|
@ -13,27 +13,25 @@
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { Person, PersonAccount } from '@hcengineering/contact'
|
||||
import { personByIdStore } from '@hcengineering/contact-resources'
|
||||
import { Account, AttachedDoc, Class, Collection, Doc, Ref } from '@hcengineering/core'
|
||||
import { createQuery, getClient } from '@hcengineering/presentation'
|
||||
import core from '@hcengineering/core/lib/component'
|
||||
import { AttributeModel } from '@hcengineering/view'
|
||||
import { IntlString } from '@hcengineering/platform'
|
||||
import { Component, ShowMore } from '@hcengineering/ui'
|
||||
import activity, {
|
||||
ActivityMessage,
|
||||
DisplayActivityMessage,
|
||||
DisplayDocUpdateMessage,
|
||||
DocUpdateAction,
|
||||
DocUpdateMessage,
|
||||
DocUpdateMessageViewlet
|
||||
} from '@hcengineering/activity'
|
||||
import { Person, PersonAccount } from '@hcengineering/contact'
|
||||
import { personByIdStore } from '@hcengineering/contact-resources'
|
||||
import core, { Account, AttachedDoc, Class, Collection, Doc, Ref } from '@hcengineering/core'
|
||||
import { IntlString } from '@hcengineering/platform'
|
||||
import { createQuery, getClient } from '@hcengineering/presentation'
|
||||
import { Component, ShowMore } from '@hcengineering/ui'
|
||||
import { AttributeModel } from '@hcengineering/view'
|
||||
|
||||
import ActivityMessageTemplate from '../activity-message/ActivityMessageTemplate.svelte'
|
||||
import DocUpdateMessageHeader from './DocUpdateMessageHeader.svelte'
|
||||
import DocUpdateMessageContent from './DocUpdateMessageContent.svelte'
|
||||
import DocUpdateMessageAttributes from './DocUpdateMessageAttributes.svelte'
|
||||
import DocUpdateMessageContent from './DocUpdateMessageContent.svelte'
|
||||
import DocUpdateMessageHeader from './DocUpdateMessageHeader.svelte'
|
||||
|
||||
import { getAttributeModel, getCollectionAttribute } from '../../activityMessagesUtils'
|
||||
import { buildRemovedDoc, checkIsObjectRemoved } from '@hcengineering/view-resources'
|
||||
@ -50,7 +48,6 @@
|
||||
const client = getClient()
|
||||
const hierarchy = client.getHierarchy()
|
||||
|
||||
const viewletQuery = createQuery()
|
||||
const userQuery = createQuery()
|
||||
const objectQuery = createQuery()
|
||||
const parentObjectQuery = createQuery()
|
||||
@ -70,31 +67,15 @@
|
||||
let object: Doc | undefined
|
||||
let isObjectRemoved: boolean = false
|
||||
|
||||
let isViewletLoading = true
|
||||
let isObjectLoading = true
|
||||
|
||||
$: isLoading = isViewletLoading || isObjectLoading
|
||||
$: isLoading = isObjectLoading
|
||||
|
||||
$: loadViewlet(value.action, value.objectClass)
|
||||
$: [viewlet] = client
|
||||
.getModel()
|
||||
.findAllSync(activity.class.DocUpdateMessageViewlet, { action: value.action, objectClass: value.objectClass })
|
||||
|
||||
async function loadViewlet (action: DocUpdateAction, objectClass: Ref<Class<Doc>>) {
|
||||
isViewletLoading = true
|
||||
|
||||
const res = viewletQuery.query(
|
||||
activity.class.DocUpdateMessageViewlet,
|
||||
{ action, objectClass },
|
||||
(result: DocUpdateMessageViewlet[]) => {
|
||||
viewlet = result[0]
|
||||
isViewletLoading = false
|
||||
}
|
||||
)
|
||||
|
||||
if (!res) {
|
||||
isViewletLoading = false
|
||||
}
|
||||
}
|
||||
|
||||
$: getAttributeModel(client, value.attributeUpdates, value.attachedToClass).then((model) => {
|
||||
$: void getAttributeModel(client, value.attributeUpdates, value.attachedToClass).then((model) => {
|
||||
attributeModel = model
|
||||
})
|
||||
|
||||
@ -104,7 +85,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
$: getParentMessage(value.attachedToClass, value.attachedTo).then((res) => {
|
||||
$: void getParentMessage(value.attachedToClass, value.attachedTo).then((res) => {
|
||||
parentMessage = res as DisplayActivityMessage
|
||||
})
|
||||
|
||||
@ -112,12 +93,12 @@
|
||||
user = res[0] as PersonAccount
|
||||
})
|
||||
|
||||
$: person = user?.person && $personByIdStore.get(user.person)
|
||||
$: person = user?.person != null ? $personByIdStore.get(user.person) : undefined
|
||||
|
||||
$: loadObject(value.objectId, value.objectClass)
|
||||
$: loadParentObject(value, parentMessage)
|
||||
$: void loadObject(value.objectId, value.objectClass)
|
||||
$: void loadParentObject(value, parentMessage)
|
||||
|
||||
async function loadObject (_id: Ref<Doc>, _class: Ref<Class<Doc>>) {
|
||||
async function loadObject (_id: Ref<Doc>, _class: Ref<Class<Doc>>): Promise<void> {
|
||||
isObjectRemoved = await checkIsObjectRemoved(client, _id, _class)
|
||||
|
||||
if (isObjectRemoved) {
|
||||
@ -131,7 +112,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
async function loadParentObject (message: DocUpdateMessage, parentMessage?: ActivityMessage) {
|
||||
async function loadParentObject (message: DocUpdateMessage, parentMessage?: ActivityMessage): Promise<void> {
|
||||
if (!parentMessage && message.objectId === message.attachedTo) {
|
||||
return
|
||||
}
|
||||
@ -150,12 +131,12 @@
|
||||
})
|
||||
}
|
||||
|
||||
$: if (object && value.objectClass !== object._class) {
|
||||
$: if (object != null && value.objectClass !== object._class) {
|
||||
object = undefined
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if !isLoading && (!viewlet?.hideIfRemoved || !isObjectRemoved) && (value.action !== 'update' || attributeModel !== undefined)}
|
||||
{#if !isLoading && (!(viewlet?.hideIfRemoved ?? false) || !isObjectRemoved) && (value.action !== 'update' || attributeModel !== undefined)}
|
||||
<ActivityMessageTemplate
|
||||
message={value}
|
||||
{parentMessage}
|
||||
|
@ -18,6 +18,7 @@ import { type Resources } from '@hcengineering/platform'
|
||||
import Activity from './components/Activity.svelte'
|
||||
import ActivityMessagePresenter from './components/activity-message/ActivityMessagePresenter.svelte'
|
||||
import DocUpdateMessagePresenter from './components/doc-update-message/DocUpdateMessagePresenter.svelte'
|
||||
import ActivityInfoMessagePresenter from './components/activity-message/ActivityInfoMessagePresenter.svelte'
|
||||
import ReactionAddedMessage from './components/reactions/ReactionAddedMessage.svelte'
|
||||
|
||||
import { attributesFilter, pinnedFilter } from './activityMessagesUtils'
|
||||
@ -36,7 +37,8 @@ export default async (): Promise<Resources> => ({
|
||||
Activity,
|
||||
ActivityMessagePresenter,
|
||||
DocUpdateMessagePresenter,
|
||||
ReactionAddedMessage
|
||||
ReactionAddedMessage,
|
||||
ActivityInfoMessagePresenter
|
||||
},
|
||||
filter: {
|
||||
AttributesFilter: attributesFilter,
|
||||
|
@ -113,13 +113,37 @@ export interface ActivityMessage extends AttachedDoc {
|
||||
reactions?: number
|
||||
}
|
||||
|
||||
export type DisplayActivityMessage = DisplayDocUpdateMessage | ActivityMessage
|
||||
export type DisplayActivityMessage = DisplayDocUpdateMessage | ActivityMessage | ActivityInfoMessage
|
||||
|
||||
export interface DisplayDocUpdateMessage extends DocUpdateMessage {
|
||||
previousMessages?: DocUpdateMessage[]
|
||||
combinedMessagesIds?: Ref<DocUpdateMessage>[]
|
||||
}
|
||||
|
||||
/**
|
||||
* Designed to control and filter some of changes from being to be propagated into activity.
|
||||
* @public
|
||||
*/
|
||||
export interface ActivityMessageControl extends Doc {
|
||||
objectClass: Ref<Class<Doc>>
|
||||
|
||||
// A set of rules to be skipped from generate doc update activity messages
|
||||
skip: DocumentQuery<Tx>[]
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* General information activity message.
|
||||
* @public
|
||||
*/
|
||||
export interface ActivityInfoMessage extends ActivityMessage {
|
||||
title?: IntlString
|
||||
message: IntlString
|
||||
props?: Record<string, any>
|
||||
icon?: Asset
|
||||
iconProps?: Record<string, any>
|
||||
}
|
||||
|
||||
export type ActivityMessageExtensionKind = 'action' | 'footer'
|
||||
|
||||
/**
|
||||
@ -252,6 +276,8 @@ export default plugin(activityId, {
|
||||
TxViewlet: '' as Ref<Class<TxViewlet>>,
|
||||
DocUpdateMessage: '' as Ref<Class<DocUpdateMessage>>,
|
||||
ActivityMessage: '' as Ref<Class<ActivityMessage>>,
|
||||
ActivityInfoMessage: '' as Ref<Class<ActivityInfoMessage>>,
|
||||
ActivityMessageControl: '' as Ref<Class<ActivityMessageControl>>,
|
||||
DocUpdateMessageViewlet: '' as Ref<Class<DocUpdateMessageViewlet>>,
|
||||
ActivityMessageExtension: '' as Ref<Class<ActivityMessageExtension>>,
|
||||
ActivityMessagesFilter: '' as Ref<Class<ActivityMessagesFilter>>,
|
||||
@ -288,6 +314,7 @@ export default plugin(activityId, {
|
||||
Activity: '' as AnyComponent,
|
||||
ActivityMessagePresenter: '' as AnyComponent,
|
||||
DocUpdateMessagePresenter: '' as AnyComponent,
|
||||
ActivityInfoMessagePresenter: '' as AnyComponent,
|
||||
ReactionAddedMessage: '' as AnyComponent
|
||||
}
|
||||
})
|
||||
|
@ -13,17 +13,24 @@
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { IconSize, resolvedLocationStore } from '@hcengineering/ui'
|
||||
import { Icon, IconSize, resolvedLocationStore } from '@hcengineering/ui'
|
||||
import { Asset } from '@hcengineering/platform'
|
||||
|
||||
export let size: IconSize
|
||||
export let variant: 'circle' | 'roundedRect' = 'circle'
|
||||
export let icon: Asset | undefined = undefined
|
||||
export let iconProps: Record<string, any> | undefined = undefined
|
||||
|
||||
$: workspace = $resolvedLocationStore.path[1]
|
||||
</script>
|
||||
|
||||
<div class="avatar {size} {variant}">
|
||||
<div class="text">
|
||||
{#if icon}
|
||||
<Icon {icon} {size} {iconProps} />
|
||||
{:else}
|
||||
{workspace?.toUpperCase()?.[0]}
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -32,11 +39,12 @@
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
color: var(--white-color);
|
||||
background-color: rgb(246, 105, 77);
|
||||
color: var(--avatar-border-color);
|
||||
background-color: var(--avatar-bg-color);
|
||||
|
||||
.text {
|
||||
font-weight: 500;
|
||||
color: var(--theme-accent-color);
|
||||
}
|
||||
|
||||
&.circle {
|
||||
|
@ -13,11 +13,13 @@
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import activity, { DocUpdateMessage } from '@hcengineering/activity'
|
||||
import {
|
||||
Account,
|
||||
AttachedDoc,
|
||||
Data,
|
||||
Doc,
|
||||
matchQuery,
|
||||
Ref,
|
||||
Tx,
|
||||
TxCollectionCUD,
|
||||
@ -25,10 +27,9 @@ import {
|
||||
TxCUD,
|
||||
TxProcessor
|
||||
} from '@hcengineering/core'
|
||||
import type { TriggerControl } from '@hcengineering/server-core'
|
||||
import activity, { DocUpdateMessage } from '@hcengineering/activity'
|
||||
import core from '@hcengineering/core/lib/component'
|
||||
import { ActivityControl, DocObjectCache } from '@hcengineering/server-activity'
|
||||
import type { TriggerControl } from '@hcengineering/server-core'
|
||||
import { getDocUpdateAction, getTxAttributesUpdates } from './utils'
|
||||
|
||||
// export async function OnReactionChanged (originTx: Tx, control: TriggerControl): Promise<Tx[]> {
|
||||
@ -222,6 +223,29 @@ export async function generateDocUpdateMessages (
|
||||
if (control.hierarchy.isDerived(tx.objectClass, activity.class.ActivityMessage)) {
|
||||
return res
|
||||
}
|
||||
const etx = TxProcessor.extractTx(tx)
|
||||
if (
|
||||
control.hierarchy.isDerived(etx._class, core.class.TxCUD) &&
|
||||
control.hierarchy.isDerived((etx as TxCUD<Doc>).objectClass, activity.class.ActivityMessage)
|
||||
) {
|
||||
return res
|
||||
}
|
||||
|
||||
// Check if we have override control over transaction => activity mappings
|
||||
const controlRules = control.modelDb.findAllSync(activity.class.ActivityMessageControl, {
|
||||
objectClass: { $in: control.hierarchy.getDescendants(tx.objectClass) }
|
||||
})
|
||||
if (controlRules.length > 0) {
|
||||
for (const r of controlRules) {
|
||||
for (const s of r.skip) {
|
||||
const otx = originTx ?? TxProcessor.extractTx(tx)
|
||||
if (matchQuery(otx !== undefined ? [tx, otx] : [tx], s, r.objectClass, control.hierarchy).length > 0) {
|
||||
// Match found, we need to skip
|
||||
return res
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
switch (tx._class) {
|
||||
case core.class.TxCreateDoc: {
|
||||
|
Loading…
Reference in New Issue
Block a user