From 24c547ab3927e7674441817077fb6b28e7b8c3f7 Mon Sep 17 00:00:00 2001
From: Denis Bunakalya <denis.bunakalya@xored.com>
Date: Tue, 12 Apr 2022 18:45:06 +0300
Subject: [PATCH] Chunter: Delete message (#1336)

Signed-off-by: Denis Bunakalya <denis.bunakalya@xored.com>
---
 plugins/chunter-assets/lang/en.json           |  3 +-
 plugins/chunter-assets/lang/ru.json           |  3 +-
 .../src/components/Message.svelte             | 24 ++++++++--
 .../src/components/ThreadComment.svelte       | 10 +++-
 plugins/chunter-resources/src/plugin.ts       |  3 +-
 server-plugins/chunter-resources/src/index.ts | 47 +++++++++++++++++--
 6 files changed, 77 insertions(+), 13 deletions(-)

diff --git a/plugins/chunter-assets/lang/en.json b/plugins/chunter-assets/lang/en.json
index c1f6497a98..725ebbbc8b 100644
--- a/plugins/chunter-assets/lang/en.json
+++ b/plugins/chunter-assets/lang/en.json
@@ -30,6 +30,7 @@
     "New": "New",
     "MarkUnread": "Mark unread",
     "GetNewReplies": "Get notified about new replies",
-    "TurnOffReplies": "Turn off notifications for replies"
+    "TurnOffReplies": "Turn off notifications for replies",
+    "DeleteMessage": "Delete message"
   }
 }
\ No newline at end of file
diff --git a/plugins/chunter-assets/lang/ru.json b/plugins/chunter-assets/lang/ru.json
index 130b6eb1b8..eccff39861 100644
--- a/plugins/chunter-assets/lang/ru.json
+++ b/plugins/chunter-assets/lang/ru.json
@@ -29,6 +29,7 @@
     "New": "Новое",
     "MarkUnread": "Отметить как непрочитанное",
     "GetNewReplies": "Получать уведомления о новых ответах",
-    "TurnOffReplies": "Выключить уведомления об ответах"
+    "TurnOffReplies": "Выключить уведомления об ответах",
+    "DeleteMessage": "Удалить сообщение"
   }
 }
\ No newline at end of file
diff --git a/plugins/chunter-resources/src/components/Message.svelte b/plugins/chunter-resources/src/components/Message.svelte
index 3bd4ef242a..9f13f9871f 100644
--- a/plugins/chunter-resources/src/components/Message.svelte
+++ b/plugins/chunter-resources/src/components/Message.svelte
@@ -17,11 +17,11 @@
   import { AttachmentList } from '@anticrm/attachment-resources'
   import type { Message } from '@anticrm/chunter'
   import { Employee, EmployeeAccount, formatName } from '@anticrm/contact'
-  import { Ref, WithLookup } from '@anticrm/core'
+  import { Ref, WithLookup, getCurrentAccount } from '@anticrm/core'
   import { NotificationClientImpl } from '@anticrm/notification-resources'
   import { getResource } from '@anticrm/platform'
   import { Avatar, getClient, MessageViewer } from '@anticrm/presentation'
-  import { ActionIcon, IconMoreH, Menu, showPopup } from '@anticrm/ui'
+  import { ActionIcon, IconMoreH, Menu, showPopup, getCurrentLocation, navigate } from '@anticrm/ui'
   import { Action } from '@anticrm/view'
   import { getActions } from '@anticrm/view-resources'
   import { createEventDispatcher } from 'svelte'
@@ -59,6 +59,21 @@
         action: chunter.actionImpl.SubscribeMessage
       } as Action)
 
+  async function deleteMessage () {
+      await client.remove(message)
+      const loc = getCurrentLocation()
+
+      if (loc.path[3] === message._id) {
+        loc.path.length = 3
+        navigate(loc)
+      }  
+    }
+
+  const deleteAction = {
+    label: chunter.string.DeleteMessage,
+    action: deleteMessage
+  }
+
   const showMenu = async (ev: Event): Promise<void> => {
     const actions = await getActions(client, message, chunter.class.Message)
     actions.push(subscribeAction)
@@ -73,7 +88,8 @@
               const impl = await getResource(a.action)
               await impl(message)
             }
-          }))
+          })),
+          ...(getCurrentAccount()._id === message.createBy ? [deleteAction] : [])
         ]
       },
       ev.target as HTMLElement
@@ -108,7 +124,7 @@
         </div>
         {#if !thread}
           <div>
-            {#if message.replies}<Replies
+            {#if message.replies?.length}<Replies
                 replies={message.replies}
                 lastReply={message.lastReply}
                 on:click={openThread}
diff --git a/plugins/chunter-resources/src/components/ThreadComment.svelte b/plugins/chunter-resources/src/components/ThreadComment.svelte
index 22affdaeba..4003b7bbf7 100644
--- a/plugins/chunter-resources/src/components/ThreadComment.svelte
+++ b/plugins/chunter-resources/src/components/ThreadComment.svelte
@@ -17,7 +17,7 @@
   import { AttachmentList } from '@anticrm/attachment-resources'
   import type { ThreadMessage } from '@anticrm/chunter'
   import { Employee, EmployeeAccount, formatName } from '@anticrm/contact'
-  import { Ref, WithLookup } from '@anticrm/core'
+  import { Ref, WithLookup, getCurrentAccount } from '@anticrm/core'
   import { NotificationClientImpl } from '@anticrm/notification-resources'
   import { getResource } from '@anticrm/platform'
   import { Avatar, getClient, MessageViewer } from '@anticrm/presentation'
@@ -53,6 +53,11 @@
         action: chunter.actionImpl.SubscribeComment
       } as Action)
 
+  const deleteAction = {
+    label: chunter.string.DeleteMessage,
+    action: async () => await client.removeDoc(message._class, message.space, message._id)
+  }
+
   const showMenu = async (ev: Event): Promise<void> => {
     const actions = await getActions(client, message, chunter.class.ThreadMessage)
     actions.push(subscribeAction)
@@ -67,7 +72,8 @@
               const impl = await getResource(a.action)
               await impl(message)
             }
-          }))
+          })),
+          ...(getCurrentAccount()._id === message.createBy ? [deleteAction] : [])
         ]
       },
       ev.target as HTMLElement
diff --git a/plugins/chunter-resources/src/plugin.ts b/plugins/chunter-resources/src/plugin.ts
index e08dbcf25e..cba353810e 100644
--- a/plugins/chunter-resources/src/plugin.ts
+++ b/plugins/chunter-resources/src/plugin.ts
@@ -50,6 +50,7 @@ export default mergeIds(chunterId, chunter, {
     LastReply: '' as IntlString,
     New: '' as IntlString,
     GetNewReplies: '' as IntlString,
-    TurnOffReplies: '' as IntlString
+    TurnOffReplies: '' as IntlString,
+    DeleteMessage: '' as IntlString
   }
 })
diff --git a/server-plugins/chunter-resources/src/index.ts b/server-plugins/chunter-resources/src/index.ts
index 1784ccf069..fbc79374e6 100644
--- a/server-plugins/chunter-resources/src/index.ts
+++ b/server-plugins/chunter-resources/src/index.ts
@@ -13,9 +13,9 @@
 // limitations under the License.
 //
 
-import chunter, { Channel, Comment, Message } from '@anticrm/chunter'
+import chunter, { Channel, Comment, Message, ThreadMessage } from '@anticrm/chunter'
 import { EmployeeAccount } from '@anticrm/contact'
-import core, { Class, Doc, DocumentQuery, FindOptions, FindResult, Hierarchy, Ref, Tx, TxCreateDoc, TxProcessor, TxUpdateDoc } from '@anticrm/core'
+import core, { Class, Doc, DocumentQuery, FindOptions, FindResult, Hierarchy, Ref, Tx, TxCreateDoc, TxProcessor, TxUpdateDoc, TxRemoveDoc } from '@anticrm/core'
 import login from '@anticrm/login'
 import { getMetadata } from '@anticrm/platform'
 import { TriggerControl } from '@anticrm/server-core'
@@ -58,7 +58,7 @@ export async function CommentCreate (tx: Tx, control: TriggerControl): Promise<T
   const hierarchy = control.hierarchy
   if (tx._class !== core.class.TxCreateDoc) return []
   const doc = TxProcessor.createDoc2Doc(tx as TxCreateDoc<Doc>)
-  if (!hierarchy.isDerived(doc._class, chunter.class.Comment)) {
+  if (!hierarchy.isDerived(doc._class, chunter.class.ThreadMessage)) {
     return []
   }
 
@@ -80,6 +80,44 @@ export async function CommentCreate (tx: Tx, control: TriggerControl): Promise<T
   return result
 }
 
+/**
+ * @public
+ */
+export async function CommentDelete (tx: Tx, control: TriggerControl): Promise<Tx[]> {
+  const hierarchy = control.hierarchy
+  if (tx._class !== core.class.TxRemoveDoc) return []
+
+  const rmTx = tx as TxRemoveDoc<ThreadMessage>
+  if (!hierarchy.isDerived(rmTx.objectClass, chunter.class.ThreadMessage)) {
+    return []
+  }
+  const createTx = (await control.findAll(core.class.TxCreateDoc, {
+    objectId: rmTx.objectId
+  }, { limit: 1 }))[0]
+
+  const comment = TxProcessor.createDoc2Doc(createTx as TxCreateDoc<ThreadMessage>)
+
+  const comments = await control.findAll(chunter.class.ThreadMessage, {
+    attachedTo: comment.attachedTo
+  })
+  const updateTx = control.txFactory.createTxUpdateDoc<Message>(
+    chunter.class.Message,
+    comment.space,
+    comment.attachedTo,
+    {
+      replies:
+        comments
+          .map(comm => (control.modelDb.getObject(comm.createBy) as EmployeeAccount).employee),
+      lastReply:
+        comments.length > 0
+          ? Math.max(...comments.map(comm => comm.createOn))
+          : undefined
+    }
+  )
+
+  return [updateTx]
+}
+
 /**
  * @public
  */
@@ -112,7 +150,8 @@ export async function MessageCreate (tx: Tx, control: TriggerControl): Promise<T
 export async function ChunterTrigger (tx: Tx, control: TriggerControl): Promise<Tx[]> {
   const promises = [
     MessageCreate(tx, control),
-    CommentCreate(tx, control)
+    CommentCreate(tx, control),
+    CommentDelete(tx, control)
   ]
   const res = await Promise.all(promises)
   return res.flat()