From ba49e4d66ec8cfab81fc37b54c963d0454fdad52 Mon Sep 17 00:00:00 2001
From: Kristina <kristin.fefelova@gmail.com>
Date: Thu, 21 Dec 2023 19:31:32 +0400
Subject: [PATCH] UBERF-4707: fix activity messages updating (#4238)

Signed-off-by: Kristina Fefelova <kristin.fefelova@gmail.com>
---
 models/notification/src/index.ts              |  1 -
 models/telegram/src/index.ts                  |  2 +-
 models/telegram/src/plugin.ts                 |  3 +-
 .../src/activityMessagesUtils.ts              | 57 +---------------
 .../DocUpdateMessageContent.svelte            |  9 ++-
 .../DocUpdateMessageObjectValue.svelte        | 29 ++++++--
 .../DocUpdateMessagePresenter.svelte          | 57 ++++++++++------
 .../reactions/ReactionAddedMessage.svelte     | 14 +++-
 .../activity/AttachmentsUpdatedMessage.svelte | 14 +++-
 .../src/components/BacklinkContent.svelte     | 24 +++++--
 .../src/components/inbox/InboxAside.svelte    | 24 +++++--
 plugins/telegram-resources/package.json       | 29 ++++----
 .../TelegramMessageCreated.svelte}            | 26 +++++++-
 plugins/telegram-resources/src/index.ts       |  8 +--
 plugins/telegram/src/index.ts                 |  3 -
 plugins/view-resources/src/utils.ts           | 66 ++++++++++++++++++-
 16 files changed, 237 insertions(+), 129 deletions(-)
 rename plugins/telegram-resources/src/components/{notification/NotificationMessageCreated.svelte => activity/TelegramMessageCreated.svelte} (52%)

diff --git a/models/notification/src/index.ts b/models/notification/src/index.ts
index 30303d05de..11c4f4edfa 100644
--- a/models/notification/src/index.ts
+++ b/models/notification/src/index.ts
@@ -468,7 +468,6 @@ export function createModel (builder: Builder): void {
       objectClass: notification.mixin.Collaborators,
       action: 'update',
       icon: notification.icon.Notifications,
-      component: notification.activity.TxCollaboratorsChange,
       label: notification.string.ChangeCollaborators
     },
     notification.ids.NotificationCollaboratorsChanged
diff --git a/models/telegram/src/index.ts b/models/telegram/src/index.ts
index 0271ba1d8f..866e83260b 100644
--- a/models/telegram/src/index.ts
+++ b/models/telegram/src/index.ts
@@ -140,7 +140,7 @@ export function createModel (builder: Builder): void {
       objectClass: telegram.class.Message,
       action: 'create',
       icon: contact.icon.Telegram,
-      component: telegram.notification.NotificationMessageCreated,
+      component: telegram.activity.TelegramMessageCreated,
       label: telegram.string.SharedMessages
     },
     telegram.ids.TelegramMessageCreatedActivityViewlet
diff --git a/models/telegram/src/plugin.ts b/models/telegram/src/plugin.ts
index 9d6229c3ac..d0e905c9b9 100644
--- a/models/telegram/src/plugin.ts
+++ b/models/telegram/src/plugin.ts
@@ -52,6 +52,7 @@ export default mergeIds(telegramId, telegram, {
   },
   activity: {
     TxMessage: '' as AnyComponent,
-    TxSharedCreate: '' as AnyComponent
+    TxSharedCreate: '' as AnyComponent,
+    TelegramMessageCreated: '' as AnyComponent
   }
 })
diff --git a/plugins/activity-resources/src/activityMessagesUtils.ts b/plugins/activity-resources/src/activityMessagesUtils.ts
index 42bdb8fb8b..1f5d5a52ff 100644
--- a/plugins/activity-resources/src/activityMessagesUtils.ts
+++ b/plugins/activity-resources/src/activityMessagesUtils.ts
@@ -22,17 +22,11 @@ import core, {
   groupByArray,
   type Hierarchy,
   type Ref,
-  SortingOrder,
-  type TxCollectionCUD,
-  type TxCreateDoc,
-  type TxCUD,
-  type TxMixin,
-  TxProcessor,
-  type TxUpdateDoc
+  SortingOrder
 } from '@hcengineering/core'
 import view, { type AttributeModel } from '@hcengineering/view'
 import { getClient, getFiltredKeys } from '@hcengineering/presentation'
-import { getAttributePresenter, getDocLinkTitle } from '@hcengineering/view-resources'
+import { buildRemovedDoc, getAttributePresenter, getDocLinkTitle } from '@hcengineering/view-resources'
 import { type Person } from '@hcengineering/contact'
 import { type IntlString } from '@hcengineering/platform'
 import { type AnyComponent } from '@hcengineering/ui'
@@ -60,36 +54,6 @@ const valueTypes: ReadonlyArray<Ref<Class<Doc>>> = [
   core.class.TypeHyperlink
 ]
 
-async function buildRemovedDoc (client: Client, objectId: Ref<Doc>, _class: Ref<Class<Doc>>): Promise<Doc | undefined> {
-  const isAttached = client.getHierarchy().isDerived(_class, core.class.AttachedDoc)
-  const txes = await client.findAll<TxCUD<Doc>>(
-    isAttached ? core.class.TxCollectionCUD : core.class.TxCUD,
-    isAttached
-      ? { 'tx.objectId': objectId as Ref<AttachedDoc> }
-      : {
-          objectId
-        },
-    { sort: { modifiedOn: 1 } }
-  )
-  const createTx = isAttached
-    ? txes.map((tx) => (tx as TxCollectionCUD<Doc, AttachedDoc>).tx).find((tx) => tx._class === core.class.TxCreateDoc)
-    : txes.find((tx) => tx._class === core.class.TxCreateDoc)
-
-  if (createTx === undefined) return
-  let doc = TxProcessor.createDoc2Doc(createTx as TxCreateDoc<Doc>)
-
-  for (let tx of txes) {
-    tx = TxProcessor.extractTx(tx) as TxCUD<Doc>
-    if (tx._class === core.class.TxUpdateDoc) {
-      doc = TxProcessor.updateDoc2Doc(doc, tx as TxUpdateDoc<Doc>)
-    } else if (tx._class === core.class.TxMixin) {
-      const mixinTx = tx as TxMixin<Doc, Doc>
-      doc = TxProcessor.updateMixin4Doc(doc, mixinTx)
-    }
-  }
-  return doc
-}
-
 export async function getAttributeValues (client: Client, values: any[], attrClass: Ref<Class<Doc>>): Promise<any[]> {
   if (values.some((value) => typeof value !== 'string')) {
     return values
@@ -127,23 +91,6 @@ export function getCollectionAttribute (
   return undefined
 }
 
-export async function getActivityObject (
-  client: Client,
-  objectId: Ref<Doc>,
-  objectClass: Ref<Class<Doc>>
-): Promise<{ isRemoved: boolean, object?: Doc }> {
-  const object = await client.findOne(objectClass, { _id: objectId })
-
-  if (object !== undefined) {
-    return { isRemoved: false, object }
-  }
-
-  return {
-    isRemoved: true,
-    object: await buildRemovedDoc(client, objectId, objectClass)
-  }
-}
-
 export async function getAttributeModel (
   client: Client,
   attributeUpdates: DocAttributeUpdates | undefined,
diff --git a/plugins/activity-resources/src/components/doc-update-message/DocUpdateMessageContent.svelte b/plugins/activity-resources/src/components/doc-update-message/DocUpdateMessageContent.svelte
index 613f702df4..cc17e09bca 100644
--- a/plugins/activity-resources/src/components/doc-update-message/DocUpdateMessageContent.svelte
+++ b/plugins/activity-resources/src/components/doc-update-message/DocUpdateMessageContent.svelte
@@ -20,7 +20,7 @@
   import view from '@hcengineering/view'
   import activity, { DisplayDocUpdateMessage, DocUpdateMessage, DocUpdateMessageViewlet } from '@hcengineering/activity'
 
-  import NotificationObjectValue from './DocUpdateMessageObjectValue.svelte'
+  import DocUpdateMessageObjectValue from './DocUpdateMessageObjectValue.svelte'
 
   export let message: DisplayDocUpdateMessage
   export let viewlet: DocUpdateMessageViewlet | undefined
@@ -42,7 +42,6 @@
 
   $: valueMessages = message.previousMessages?.length ? [...message.previousMessages, message] : [message]
   $: hasDifferentActions = message.previousMessages?.some(({ action }) => action !== message.action)
-  // TODO: use AcrivityIcon
   $: icon = viewlet?.icon ?? collectionAttribute?.icon ?? clazz.icon ?? activity.icon.Activity
 </script>
 
@@ -70,7 +69,7 @@
     {@const createMessages = valueMessages.filter(({ action }) => action === 'create')}
 
     {#each createMessages as valueMessage, index}
-      <NotificationObjectValue
+      <DocUpdateMessageObjectValue
         message={valueMessage}
         {objectPresenter}
         {objectPanel}
@@ -80,7 +79,7 @@
       />
     {/each}
     {#each removeMessages as valueMessage, index}
-      <NotificationObjectValue
+      <DocUpdateMessageObjectValue
         message={valueMessage}
         {objectPresenter}
         {objectPanel}
@@ -91,7 +90,7 @@
     {/each}
   {:else}
     {#each valueMessages as valueMessage, index}
-      <NotificationObjectValue
+      <DocUpdateMessageObjectValue
         message={valueMessage}
         {objectPresenter}
         {objectPanel}
diff --git a/plugins/activity-resources/src/components/doc-update-message/DocUpdateMessageObjectValue.svelte b/plugins/activity-resources/src/components/doc-update-message/DocUpdateMessageObjectValue.svelte
index 92d26c1963..5104c91b98 100644
--- a/plugins/activity-resources/src/components/doc-update-message/DocUpdateMessageObjectValue.svelte
+++ b/plugins/activity-resources/src/components/doc-update-message/DocUpdateMessageObjectValue.svelte
@@ -13,15 +13,13 @@
 // limitations under the License.
 -->
 <script lang="ts">
-  import { DocNavLink, getDocLinkTitle } from '@hcengineering/view-resources'
+  import { buildRemovedDoc, checkIsObjectRemoved, DocNavLink, getDocLinkTitle } from '@hcengineering/view-resources'
   import { Component, Icon, IconAdd, IconDelete } from '@hcengineering/ui'
-  import { getClient } from '@hcengineering/presentation'
+  import { createQuery, getClient } from '@hcengineering/presentation'
   import view, { ObjectPanel, ObjectPresenter } from '@hcengineering/view'
-  import { Doc } from '@hcengineering/core'
+  import { Class, Doc, Ref } from '@hcengineering/core'
   import { DisplayDocUpdateMessage, DocUpdateMessageViewlet } from '@hcengineering/activity'
 
-  import { getActivityObject } from '../../activityMessagesUtils'
-
   export let message: DisplayDocUpdateMessage
   export let viewlet: DocUpdateMessageViewlet | undefined
   export let objectPanel: ObjectPanel | undefined
@@ -30,6 +28,9 @@
   export let hasSeparator: boolean = false
 
   const client = getClient()
+  const objectQuery = createQuery()
+
+  let object: Doc | undefined = undefined
 
   async function getValue (object: Doc | undefined): Promise<string | undefined> {
     if (object === undefined) {
@@ -42,9 +43,23 @@
 
     return await getDocLinkTitle(client, object._id, object._class, object)
   }
+
+  async function loadObject (_id: Ref<Doc>, _class: Ref<Class<Doc>>) {
+    const isRemoved = await checkIsObjectRemoved(client, _id, _class)
+
+    if (isRemoved) {
+      object = await buildRemovedDoc(client, _id, _class)
+    } else {
+      objectQuery.query(_class, { _id }, (res) => {
+        object = res[0]
+      })
+    }
+  }
+
+  $: loadObject(message.objectId, message.objectClass)
 </script>
 
-{#await getActivityObject(client, message.objectId, message.objectClass) then { object }}
+{#if object}
   {#await getValue(object) then value}
     {#if withIcon && message.action === 'create'}
       <Icon icon={IconAdd} size="x-small" />
@@ -71,7 +86,7 @@
       <Component is={objectPresenter.presenter} props={{ value: object, accent: true, shouldShowAvatar: false }} />
     {/if}
   {/await}
-{/await}
+{/if}
 
 <style lang="scss">
   .valueLink {
diff --git a/plugins/activity-resources/src/components/doc-update-message/DocUpdateMessagePresenter.svelte b/plugins/activity-resources/src/components/doc-update-message/DocUpdateMessagePresenter.svelte
index 3b9814aedd..eaf5da7787 100644
--- a/plugins/activity-resources/src/components/doc-update-message/DocUpdateMessagePresenter.svelte
+++ b/plugins/activity-resources/src/components/doc-update-message/DocUpdateMessagePresenter.svelte
@@ -35,7 +35,8 @@
   import DocUpdateMessageContent from './DocUpdateMessageContent.svelte'
   import DocUpdateMessageAttributes from './DocUpdateMessageAttributes.svelte'
 
-  import { getAttributeModel, getCollectionAttribute, getActivityObject } from '../../activityMessagesUtils'
+  import { getAttributeModel, getCollectionAttribute } from '../../activityMessagesUtils'
+  import { buildRemovedDoc, checkIsObjectRemoved } from '@hcengineering/view-resources'
 
   export let value: DisplayDocUpdateMessage
   export let showNotify: boolean = false
@@ -51,6 +52,8 @@
 
   const viewletQuery = createQuery()
   const userQuery = createQuery()
+  const objectQuery = createQuery()
+  const parentObjectQuery = createQuery()
 
   const collectionAttribute = getCollectionAttribute(hierarchy, value.attachedToClass, value.updateCollection)
   const clazz = hierarchy.getClass(value.objectClass)
@@ -111,26 +114,40 @@
 
   $: person = user?.person && $personByIdStore.get(user.person)
 
-  $: getActivityObject(client, value.objectId, value.objectClass).then((result) => {
-    isObjectLoading = false
-    object = result.object
-    isObjectRemoved = result.isRemoved
-  })
+  $: loadObject(value.objectId, value.objectClass)
+  $: loadParentObject(value, parentMessage)
 
-  $: getParentObject(value, parentMessage).then((result) => {
-    parentObject = result?.object
-  })
+  async function loadObject (_id: Ref<Doc>, _class: Ref<Class<Doc>>) {
+    isObjectRemoved = await checkIsObjectRemoved(client, _id, _class)
 
-  async function getParentObject (message: DocUpdateMessage, parentMessage?: ActivityMessage) {
-    if (parentMessage) {
-      return await getActivityObject(client, parentMessage.attachedTo, parentMessage.attachedToClass)
+    if (isObjectRemoved) {
+      object = await buildRemovedDoc(client, _id, _class)
+      isObjectLoading = false
+    } else {
+      objectQuery.query(_class, { _id }, (res) => {
+        isObjectLoading = false
+        object = res[0]
+      })
     }
+  }
 
-    if (message.objectId === message.attachedTo) {
+  async function loadParentObject (message: DocUpdateMessage, parentMessage?: ActivityMessage) {
+    if (!parentMessage && message.objectId === message.attachedTo) {
       return
     }
 
-    return await getActivityObject(client, message.attachedTo, message.attachedToClass)
+    const _id = parentMessage ? parentMessage.attachedTo : message.attachedTo
+    const _class = parentMessage ? parentMessage.attachedToClass : message.attachedToClass
+    const isRemoved = await checkIsObjectRemoved(client, _id, _class)
+
+    if (isRemoved) {
+      parentObject = await buildRemovedDoc(client, _id, _class)
+      return
+    }
+
+    parentObjectQuery.query(_class, { _id }, (res) => {
+      parentObject = res[0]
+    })
   }
 
   $: if (object && value.objectClass !== object._class) {
@@ -172,13 +189,13 @@
       {#if viewlet?.component}
         <ShowMore>
           <div class="customContent">
-            {#each [...(value?.previousMessages ?? []), value] as msg}
-              {#await getActivityObject(client, msg.objectId, msg.objectClass) then { object }}
-                {#if object}
-                  <Component is={viewlet.component} props={{ message: value, value: object }} />
-                {/if}
-              {/await}
+            {#each value?.previousMessages ?? [] as msg}
+              <Component is={viewlet.component} props={{ message: msg, _id: msg.objectId, _class: msg.objectClass }} />
             {/each}
+            <Component
+              is={viewlet.component}
+              props={{ message: value, _id: value.objectId, _class: value.objectClass, value: object }}
+            />
           </div>
         </ShowMore>
       {:else if value.action === 'create' || value.action === 'remove'}
diff --git a/plugins/activity-resources/src/components/reactions/ReactionAddedMessage.svelte b/plugins/activity-resources/src/components/reactions/ReactionAddedMessage.svelte
index eaf4be1c11..3ffdab1030 100644
--- a/plugins/activity-resources/src/components/reactions/ReactionAddedMessage.svelte
+++ b/plugins/activity-resources/src/components/reactions/ReactionAddedMessage.svelte
@@ -14,8 +14,20 @@
 -->
 <script lang="ts">
   import { Reaction } from '@hcengineering/activity'
+  import { Ref } from '@hcengineering/core'
+  import { createQuery } from '@hcengineering/presentation'
 
-  export let value: Reaction | undefined
+  import activity from '../../plugin'
+
+  export let _id: Ref<Reaction>
+  export let value: Reaction | undefined = undefined
+
+  const query = createQuery()
+
+  $: value === undefined &&
+    query.query(activity.class.Reaction, { _id }, (res) => {
+      value = res[0]
+    })
 </script>
 
 <span class="labels-row gap-1">
diff --git a/plugins/attachment-resources/src/components/activity/AttachmentsUpdatedMessage.svelte b/plugins/attachment-resources/src/components/activity/AttachmentsUpdatedMessage.svelte
index 8e58458839..815b459dff 100644
--- a/plugins/attachment-resources/src/components/activity/AttachmentsUpdatedMessage.svelte
+++ b/plugins/attachment-resources/src/components/activity/AttachmentsUpdatedMessage.svelte
@@ -14,13 +14,25 @@
 -->
 <script lang="ts">
   import { DocUpdateMessage } from '@hcengineering/activity'
+  import { Ref } from '@hcengineering/core'
+  import { getClient } from '@hcengineering/presentation'
   import { Attachment } from '@hcengineering/attachment'
+  import { getOrBuildObject } from '@hcengineering/view-resources'
 
+  import attachment from '../../plugin'
   import AttachmentPresenter from '../AttachmentPresenter.svelte'
   import RemovedAttachmentPresenter from '../RemovedAttachmentPresenter.svelte'
 
   export let message: DocUpdateMessage
-  export let value: Attachment | undefined
+  export let _id: Ref<Attachment>
+  export let value: Attachment | undefined = undefined
+
+  const client = getClient()
+
+  $: value === undefined &&
+    getOrBuildObject<Attachment>(client, _id, attachment.class.Attachment).then((res) => {
+      value = res
+    })
 </script>
 
 {#if value}
diff --git a/plugins/chunter-resources/src/components/BacklinkContent.svelte b/plugins/chunter-resources/src/components/BacklinkContent.svelte
index a715b368ff..70027765b1 100644
--- a/plugins/chunter-resources/src/components/BacklinkContent.svelte
+++ b/plugins/chunter-resources/src/components/BacklinkContent.svelte
@@ -14,14 +14,28 @@
 -->
 <script lang="ts">
   import type { Backlink } from '@hcengineering/chunter'
-  import { MessageViewer } from '@hcengineering/presentation'
+  import { createQuery, MessageViewer } from '@hcengineering/presentation'
+  import { Ref } from '@hcengineering/core'
 
-  export let value: Backlink
+  import chunter from '../plugin'
+
+  export let _id: Ref<Backlink> | undefined = undefined
+  export let value: Backlink | undefined = undefined
+
+  const query = createQuery()
+
+  $: value === undefined &&
+    _id &&
+    query.query(chunter.class.Backlink, { _id }, (res) => {
+      value = res[0]
+    })
 </script>
 
-<div class="root">
-  <MessageViewer message={value?.message} />
-</div>
+{#if value}
+  <div class="root">
+    <MessageViewer message={value.message} />
+  </div>
+{/if}
 
 <style lang="scss">
   .root {
diff --git a/plugins/notification-resources/src/components/inbox/InboxAside.svelte b/plugins/notification-resources/src/components/inbox/InboxAside.svelte
index 98bb1bcc8b..fa40c84b03 100644
--- a/plugins/notification-resources/src/components/inbox/InboxAside.svelte
+++ b/plugins/notification-resources/src/components/inbox/InboxAside.svelte
@@ -14,7 +14,7 @@
 -->
 <script lang="ts">
   import { createEventDispatcher } from 'svelte'
-  import { Doc, Ref, SortingOrder } from '@hcengineering/core'
+  import { Class, Doc, Ref, SortingOrder } from '@hcengineering/core'
   import { createQuery, getClient } from '@hcengineering/presentation'
   import { Component, IconClose, Spinner } from '@hcengineering/ui'
   import view from '@hcengineering/view'
@@ -22,9 +22,9 @@
   import {
     ActivityExtension,
     ActivityMessagePresenter,
-    combineActivityMessages,
-    getActivityObject
+    combineActivityMessages
   } from '@hcengineering/activity-resources'
+  import { buildRemovedDoc, checkIsObjectRemoved } from '@hcengineering/view-resources'
 
   export let _id: Ref<ActivityMessage>
 
@@ -33,6 +33,7 @@
   const dispatch = createEventDispatcher()
   const selectedMessageQuery = createQuery()
   const messagesQuery = createQuery()
+  const objectQuery = createQuery()
 
   let messages: DisplayActivityMessage[] = []
   let selectedMessage: ActivityMessage | undefined = undefined
@@ -52,10 +53,19 @@
     }
   )
 
-  $: selectedMessage &&
-    getActivityObject(client, selectedMessage.attachedTo, selectedMessage.attachedToClass).then((res) => {
-      object = res.object
-    })
+  async function loadObject (_id: Ref<Doc>, _class: Ref<Class<Doc>>) {
+    const isRemoved = await checkIsObjectRemoved(client, _id, _class)
+
+    if (isRemoved) {
+      object = await buildRemovedDoc(client, _id, _class)
+    } else {
+      objectQuery.query(_class, { _id }, (res) => {
+        object = res[0]
+      })
+    }
+  }
+
+  $: selectedMessage && loadObject(selectedMessage.attachedTo, selectedMessage.attachedToClass)
   $: objectPresenter =
     selectedMessage && hierarchy.classHierarchyMixin(selectedMessage.attachedToClass, view.mixin.ObjectPresenter)
 
diff --git a/plugins/telegram-resources/package.json b/plugins/telegram-resources/package.json
index 5c5f21bd7f..877d34f2f7 100644
--- a/plugins/telegram-resources/package.json
+++ b/plugins/telegram-resources/package.json
@@ -35,22 +35,23 @@
     "svelte-eslint-parser": "^0.33.1"
   },
   "dependencies": {
-    "@hcengineering/platform": "^0.6.9",
-    "svelte": "^4.2.5",
-    "@hcengineering/telegram": "^0.6.14",
-    "@hcengineering/ui": "^0.6.11",
-    "@hcengineering/presentation": "^0.6.2",
-    "@hcengineering/text-editor": "^0.6.0",
-    "@hcengineering/contact": "^0.6.20",
-    "@hcengineering/setting": "^0.6.11",
-    "@hcengineering/chunter": "^0.6.12",
-    "@hcengineering/login": "^0.6.8",
-    "@hcengineering/core": "^0.6.28",
-    "@hcengineering/contact-resources": "^0.6.0",
-    "@hcengineering/notification-resources": "^0.6.0",
     "@hcengineering/attachment": "^0.6.9",
     "@hcengineering/attachment-resources": "^0.6.0",
+    "@hcengineering/chunter": "^0.6.12",
+    "@hcengineering/contact": "^0.6.20",
+    "@hcengineering/contact-resources": "^0.6.0",
+    "@hcengineering/core": "^0.6.28",
+    "@hcengineering/login": "^0.6.8",
+    "@hcengineering/notification-resources": "^0.6.0",
     "@hcengineering/panel": "^0.6.15",
-    "@hcengineering/templates": "^0.6.7"
+    "@hcengineering/platform": "^0.6.9",
+    "@hcengineering/presentation": "^0.6.2",
+    "@hcengineering/setting": "^0.6.11",
+    "@hcengineering/telegram": "^0.6.14",
+    "@hcengineering/templates": "^0.6.7",
+    "@hcengineering/text-editor": "^0.6.0",
+    "@hcengineering/ui": "^0.6.11",
+    "@hcengineering/view-resources": "^0.6.0",
+    "svelte": "^4.2.5"
   }
 }
diff --git a/plugins/telegram-resources/src/components/notification/NotificationMessageCreated.svelte b/plugins/telegram-resources/src/components/activity/TelegramMessageCreated.svelte
similarity index 52%
rename from plugins/telegram-resources/src/components/notification/NotificationMessageCreated.svelte
rename to plugins/telegram-resources/src/components/activity/TelegramMessageCreated.svelte
index 5856cc0d1d..c0fcf49e26 100644
--- a/plugins/telegram-resources/src/components/notification/NotificationMessageCreated.svelte
+++ b/plugins/telegram-resources/src/components/activity/TelegramMessageCreated.svelte
@@ -13,10 +13,32 @@
 // limitations under the License.
 -->
 <script lang="ts">
-  import { MessageViewer } from '@hcengineering/presentation'
+  import { createQuery, getClient, MessageViewer } from '@hcengineering/presentation'
   import { TelegramMessage } from '@hcengineering/telegram'
+  import { Ref } from '@hcengineering/core'
+  import { buildRemovedDoc, checkIsObjectRemoved } from '@hcengineering/view-resources'
 
-  export let value: TelegramMessage | undefined
+  import telegram from '../../plugin'
+
+  export let _id: Ref<TelegramMessage> | undefined = undefined
+  export let value: TelegramMessage | undefined = undefined
+
+  const query = createQuery()
+  const client = getClient()
+
+  $: value === undefined && _id && loadObject(_id)
+
+  async function loadObject (_id: Ref<TelegramMessage>) {
+    const isRemoved = await checkIsObjectRemoved(client, _id, telegram.class.Message)
+
+    if (isRemoved) {
+      value = await buildRemovedDoc(client, _id, telegram.class.Message)
+    } else {
+      query.query(telegram.class.Message, { _id }, (res) => {
+        value = res[0]
+      })
+    }
+  }
 </script>
 
 {#if value}
diff --git a/plugins/telegram-resources/src/index.ts b/plugins/telegram-resources/src/index.ts
index d32cac681f..234b3ca59c 100644
--- a/plugins/telegram-resources/src/index.ts
+++ b/plugins/telegram-resources/src/index.ts
@@ -24,7 +24,7 @@ import Reconnect from './components/Reconnect.svelte'
 import TxMessage from './components/activity/TxMessage.svelte'
 import IconTelegram from './components/icons/TelegramColor.svelte'
 import TxSharedCreate from './components/activity/TxSharedCreate.svelte'
-import NotificationMessageCreated from './components/notification/NotificationMessageCreated.svelte'
+import TelegramMessageCreated from './components/activity/TelegramMessageCreated.svelte'
 
 import telegram from './plugin'
 import { getCurrentEmployeeTG, getIntegrationOwnerTG } from './utils'
@@ -38,12 +38,10 @@ export default async (): Promise<Resources> => ({
     IconTelegram,
     SharedMessages
   },
-  notification: {
-    NotificationMessageCreated
-  },
   activity: {
     TxSharedCreate,
-    TxMessage
+    TxMessage,
+    TelegramMessageCreated
   },
   function: {
     GetCurrentEmployeeTG: getCurrentEmployeeTG,
diff --git a/plugins/telegram/src/index.ts b/plugins/telegram/src/index.ts
index ac7df39dc0..85e81fd3be 100644
--- a/plugins/telegram/src/index.ts
+++ b/plugins/telegram/src/index.ts
@@ -71,9 +71,6 @@ export default plugin(telegramId, {
     IconTelegram: '' as AnyComponent,
     SharedMessages: '' as AnyComponent
   },
-  notification: {
-    NotificationMessageCreated: '' as AnyComponent
-  },
   integrationType: {
     Telegram: '' as Ref<IntegrationType>
   },
diff --git a/plugins/view-resources/src/utils.ts b/plugins/view-resources/src/utils.ts
index b0ba81ff71..1d64fb87e1 100644
--- a/plugins/view-resources/src/utils.ts
+++ b/plugins/view-resources/src/utils.ts
@@ -37,7 +37,13 @@ import core, {
   type ReverseLookups,
   type Space,
   type TxOperations,
-  type Mixin
+  type Mixin,
+  type TxCUD,
+  type TxCollectionCUD,
+  TxProcessor,
+  type TxCreateDoc,
+  type TxUpdateDoc,
+  type TxMixin
 } from '@hcengineering/core'
 import type { IntlString } from '@hcengineering/platform'
 import { getResource, translate } from '@hcengineering/platform'
@@ -1075,3 +1081,61 @@ export function getCategoryQueryNoLookupOptions<T extends Doc> (options: FindOpt
   const { lookup, ...resultOptions } = options
   return resultOptions
 }
+
+export async function buildRemovedDoc<T extends Doc> (
+  client: Client,
+  objectId: Ref<T>,
+  _class: Ref<Class<T>>
+): Promise<T | undefined> {
+  const isAttached = client.getHierarchy().isDerived(_class, core.class.AttachedDoc)
+  const txes = await client.findAll<TxCUD<Doc>>(
+    isAttached ? core.class.TxCollectionCUD : core.class.TxCUD,
+    isAttached
+      ? { 'tx.objectId': objectId }
+      : {
+          objectId
+        },
+    { sort: { modifiedOn: 1 } }
+  )
+  const createTx = isAttached
+    ? txes.map((tx) => (tx as TxCollectionCUD<Doc, AttachedDoc>).tx).find((tx) => tx._class === core.class.TxCreateDoc)
+    : txes.find((tx) => tx._class === core.class.TxCreateDoc)
+
+  if (createTx === undefined) return
+  let doc = TxProcessor.createDoc2Doc(createTx as TxCreateDoc<Doc>)
+
+  for (let tx of txes) {
+    tx = TxProcessor.extractTx(tx) as TxCUD<Doc>
+    if (tx._class === core.class.TxUpdateDoc) {
+      doc = TxProcessor.updateDoc2Doc(doc, tx as TxUpdateDoc<Doc>)
+    } else if (tx._class === core.class.TxMixin) {
+      const mixinTx = tx as TxMixin<Doc, Doc>
+      doc = TxProcessor.updateMixin4Doc(doc, mixinTx)
+    }
+  }
+  return doc as T
+}
+
+export async function getOrBuildObject<T extends Doc> (
+  client: Client,
+  objectId: Ref<T>,
+  objectClass: Ref<Class<T>>
+): Promise<T | undefined> {
+  const object = await client.findOne<Doc>(objectClass, { _id: objectId })
+
+  if (object !== undefined) {
+    return object as T
+  }
+
+  return await buildRemovedDoc(client, objectId, objectClass)
+}
+
+export async function checkIsObjectRemoved (
+  client: Client,
+  objectId: Ref<Doc>,
+  objectClass: Ref<Class<Doc>>
+): Promise<boolean> {
+  const object = await client.findOne(objectClass, { _id: objectId })
+
+  return object === undefined
+}