From 3d2e2a762a09ab853dd240151fe227ab448e7c6d Mon Sep 17 00:00:00 2001
From: Oleg Solodkov <94829167+sol-0@users.noreply.github.com>
Date: Tue, 5 Sep 2023 09:29:46 +0400
Subject: [PATCH] [UBER-824] Mention notification fix (#3658)

Signed-off-by: Oleg Solodkov <oleg.solodkov@xored.com>
---
 models/chunter/src/index.ts                   |  4 +
 .../src/components/CommentPanel.svelte        | 95 +++++++++++++++++++
 plugins/chunter-resources/src/index.ts        |  4 +-
 plugins/chunter-resources/src/plugin.ts       |  3 +-
 4 files changed, 104 insertions(+), 2 deletions(-)
 create mode 100644 plugins/chunter-resources/src/components/CommentPanel.svelte

diff --git a/models/chunter/src/index.ts b/models/chunter/src/index.ts
index cf1cd313d3..a87f956c39 100644
--- a/models/chunter/src/index.ts
+++ b/models/chunter/src/index.ts
@@ -467,6 +467,10 @@ export function createModel (builder: Builder, options = { addApplication: true
     presenter: chunter.component.CommentsPresenter
   })
 
+  builder.mixin(chunter.class.Comment, core.class.Class, view.mixin.ObjectPanel, {
+    component: chunter.component.CommentPanel
+  })
+
   builder.createDoc(
     activity.class.TxViewlet,
     core.space.Model,
diff --git a/plugins/chunter-resources/src/components/CommentPanel.svelte b/plugins/chunter-resources/src/components/CommentPanel.svelte
new file mode 100644
index 0000000000..a0dbdf562f
--- /dev/null
+++ b/plugins/chunter-resources/src/components/CommentPanel.svelte
@@ -0,0 +1,95 @@
+<!--
+// Copyright © 2023 Anticrm Platform Contributors.
+// 
+// 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 contact, { PersonAccount } from '@hcengineering/contact'
+  import chunter, { Backlink, Comment } from '@hcengineering/chunter'
+  import view, { ObjectPanel } from '@hcengineering/view'
+  import { createQuery, getClient } from '@hcengineering/presentation'
+  import { AnyComponent, Component, Loading } from '@hcengineering/ui'
+  import { Class, Doc, Ref, getCurrentAccount } from '@hcengineering/core'
+
+  export let _id: Ref<Comment> | undefined
+  export let embedded: boolean = true
+
+  const client = getClient()
+  const hierarchy = client.getHierarchy()
+
+  let comment: Comment | undefined
+  const commentQuery = createQuery()
+  let loadingComment = true
+
+  $: if (_id !== undefined) {
+    commentQuery.query(chunter.class.Comment, { _id }, (res) => {
+      ;[comment] = res
+      loadingComment = false
+    })
+  } else {
+    commentQuery.unsubscribe()
+    loadingComment = false
+  }
+
+  let backlinks: Backlink[] = []
+  const backlinksQuery = createQuery()
+  let loadingBacklinks = true
+
+  const mePerson = (getCurrentAccount() as PersonAccount).person
+  let isMeMentioned = false
+
+  $: if (_id !== undefined) {
+    backlinksQuery.query(chunter.class.Backlink, { attachedDocId: _id }, (res) => {
+      backlinks = res
+      for (const backlink of backlinks) {
+        if (hierarchy.isDerived(backlink.attachedToClass, contact.class.Person) && mePerson === backlink.attachedTo) {
+          isMeMentioned = true
+          break
+        }
+      }
+      loadingBacklinks = false
+    })
+  } else {
+    backlinksQuery.unsubscribe()
+    loadingBacklinks = false
+  }
+
+  let attachedDocId: Ref<Doc> | undefined
+  let attachedDocClass: Ref<Class<Doc>> | undefined
+
+  $: loading = loadingComment || loadingBacklinks
+
+  let component: AnyComponent
+  $: getComponent(loading, comment)
+
+  async function getComponent (loading: boolean, comment?: Comment): Promise<void> {
+    if (comment == null || loading) {
+      return
+    }
+
+    let panelComponent: ObjectPanel | undefined
+    if (isMeMentioned) {
+      panelComponent = hierarchy.classHierarchyMixin(comment.attachedToClass, view.mixin.ObjectPanel)
+    }
+
+    component = panelComponent?.component ?? view.component.EditDoc
+
+    attachedDocId = comment.attachedTo
+    attachedDocClass = comment.attachedToClass
+  }
+</script>
+
+{#if loading}
+  <Loading />
+{:else if component && attachedDocId && attachedDocClass}
+  <Component is={component} props={{ _id: attachedDocId, _class: attachedDocClass, embedded }} on:close />
+{/if}
diff --git a/plugins/chunter-resources/src/index.ts b/plugins/chunter-resources/src/index.ts
index 6ed65be296..528647dfe6 100644
--- a/plugins/chunter-resources/src/index.ts
+++ b/plugins/chunter-resources/src/index.ts
@@ -38,6 +38,7 @@ import CommentInput from './components/CommentInput.svelte'
 import CommentPopup from './components/CommentPopup.svelte'
 import CommentPresenter from './components/CommentPresenter.svelte'
 import CommentsPresenter from './components/CommentsPresenter.svelte'
+import CommentPanel from './components/CommentPanel.svelte'
 import ConvertDmToPrivateChannelModal from './components/ConvertDmToPrivateChannel.svelte'
 import CreateChannel from './components/CreateChannel.svelte'
 import CreateDirectMessage from './components/CreateDirectMessage.svelte'
@@ -292,7 +293,8 @@ export default async (): Promise<Resources> => ({
     EditChannel,
     Threads,
     ThreadView,
-    SavedMessages
+    SavedMessages,
+    CommentPanel
   },
   function: {
     GetDmName: getDmName,
diff --git a/plugins/chunter-resources/src/plugin.ts b/plugins/chunter-resources/src/plugin.ts
index cdd51acb9e..c52d1f932b 100644
--- a/plugins/chunter-resources/src/plugin.ts
+++ b/plugins/chunter-resources/src/plugin.ts
@@ -31,7 +31,8 @@ export default mergeIds(chunterId, chunter, {
     EditChannel: '' as AnyComponent,
     ChannelPreview: '' as AnyComponent,
     MessagePreview: '' as AnyComponent,
-    DirectMessageInput: '' as AnyComponent
+    DirectMessageInput: '' as AnyComponent,
+    CommentPanel: '' as AnyComponent
   },
   function: {
     GetDmName: '' as Resource<(client: Client, space: Space) => Promise<string>>