Skip to content

Commit

Permalink
UBERF-4716: Activity info message
Browse files Browse the repository at this point in the history
Signed-off-by: Andrey Sobolev <haiodo@gmail.com>
  • Loading branch information
haiodo committed Dec 21, 2023
1 parent 02e5513 commit c3396e9
Show file tree
Hide file tree
Showing 12 changed files with 318 additions and 78 deletions.
33 changes: 30 additions & 3 deletions models/activity/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

import {
type ActivityAttributeUpdatesPresenter,
type ActivityInfoMessage,
type ActivityDoc,
type ActivityExtension,
type ActivityExtensionKind,
Expand All @@ -28,7 +29,8 @@ import {
type DocUpdateMessageViewlet,
type DocUpdateMessageViewletAttributesConfig,
type Reaction,
type TxViewlet
type TxViewlet,
type ActivityMessageControl
} from '@hcengineering/activity'
import core, {
DOMAIN_MODEL,
Expand All @@ -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'
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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, {})
Expand All @@ -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
Expand Down
2 changes: 1 addition & 1 deletion packages/platform/src/i18n.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
4 changes: 2 additions & 2 deletions plugins/activity-resources/src/components/Activity.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -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
})
Expand All @@ -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}>
Expand Down
Original file line number Diff line number Diff line change
@@ -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>
Original file line number Diff line number Diff line change
@@ -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>
Original file line number Diff line number Diff line change
Expand Up @@ -13,23 +13,22 @@
// limitations under the License.
-->
<script lang="ts">
import { Person } from '@hcengineering/contact'
import { Avatar, EmployeePresenter, SystemAvatar } from '@hcengineering/contact-resources'
import { getClient } from '@hcengineering/presentation'
import core from '@hcengineering/core/lib/component'
import activity, {
DisplayActivityMessage,
ActivityMessageExtension,
ActivityMessageViewlet
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 { 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
Expand All @@ -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,
{
Expand Down Expand Up @@ -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" />
Expand All @@ -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" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
})
Expand Down
Loading

0 comments on commit c3396e9

Please sign in to comment.