[UBER-781] Adding chats to inbox after sending messages (#3621)

Signed-off-by: Oleg Solodkov <oleg.solodkov@xored.com>
This commit is contained in:
Oleg Solodkov 2023-08-23 18:23:58 +07:00 committed by GitHub
parent 77f6b3dfd6
commit 04274d1167
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 116 additions and 55 deletions

View File

@ -46,10 +46,11 @@ export function createModel (builder: Builder): void {
})
builder.createDoc(serverCore.class.Trigger, core.space.Model, {
trigger: serverChunter.trigger.OnDmCreate,
trigger: serverChunter.trigger.OnMessageSent,
txMatch: {
objectClass: chunter.class.DirectMessage,
_class: core.class.TxCreateDoc
_class: core.class.TxCollectionCUD,
collection: 'messages'
}
})

View File

@ -73,6 +73,7 @@
"ConfigDescription": "Extension to perform text communications",
"LastMessage": "Last message",
"You": "You",
"YouHaveStartedAConversation": "You have started a conversation"
"YouHaveJoinedTheConversation": "You have joined the conversation",
"NoMessages": "There are no messages yet"
}
}

View File

@ -73,6 +73,7 @@
"ConfigDescription": "Расширение для текстовых переписок",
"LastMessage": "Последнее сообщение",
"You": "Вы",
"YouHaveStartedAConversation": "Вы начали диалог"
"YouHaveJoinedTheConversation": "Вы присоединились к диалогу",
"NoMessages": "Сообщений пока нет"
}
}

View File

@ -55,7 +55,7 @@
<MessagePreview value={message} />
{/each}
{:else}
<Label label={chunterResources.string.YouHaveStartedAConversation} />
<Label label={chunterResources.string.NoMessages} />
{/if}
</div>

View File

@ -89,6 +89,7 @@ export default mergeIds(chunterId, chunter, {
NoResults: '' as IntlString,
CopyLink: '' as IntlString,
You: '' as IntlString,
YouHaveStartedAConversation: '' as IntlString
YouHaveJoinedTheConversation: '' as IntlString,
NoMessages: '' as IntlString
}
})

View File

@ -190,8 +190,7 @@ export default plugin(chunterId, {
DMNotification: '' as Ref<NotificationType>,
MentionNotification: '' as Ref<NotificationType>,
ThreadNotification: '' as Ref<NotificationType>,
ChannelNotification: '' as Ref<NotificationType>,
DMCreationNotification: '' as Ref<NotificationType>
ChannelNotification: '' as Ref<NotificationType>
},
app: {
Chunter: '' as Ref<Doc>

View File

@ -19,7 +19,7 @@
"Change": "Change",
"AddedRemoved": "Added/removed",
"YouAddedCollaborators": "You have been added to collaborators",
"YouHaveStartedAConversation": "You have started a conversation",
"YouHaveJoinedTheConversation": "You have joined the conversation",
"ChangeCollaborators": "changed collaborators",
"Activity": "Activity",
"People": "People",

View File

@ -19,7 +19,7 @@
"Change": "Изменено",
"AddedRemoved": "Добавлено/удалено",
"YouAddedCollaborators": "Вы были добавлены как участник",
"YouHaveStartedAConversation": "Вы начали диалог",
"YouHaveJoinedTheConversation": "Вы присоединились к диалогу",
"ChangeCollaborators": "изменил(а) участники",
"Activity": "Активность",
"People": "Люди",

View File

@ -28,6 +28,7 @@
import EmployeeInbox from './EmployeeInbox.svelte'
import Filter from './Filter.svelte'
import People from './People.svelte'
import { subscribe } from '../utils'
export let visibileNav: boolean
let filter: 'all' | 'read' | 'unread' = 'all'
@ -100,6 +101,10 @@
const personAccount = await client.findOne(contact.class.PersonAccount, { person: employee._id })
if (personAccount !== undefined) {
const channel = await getDirectChannel(client, me._id as Ref<PersonAccount>, personAccount._id)
// re-subscribing in case DM was removed from notifications
await subscribe(chunter.class.DirectMessage, channel)
openDM(channel)
}
}

View File

@ -23,4 +23,4 @@
export let value: DirectMessage
</script>
<Label label={notification.string.YouHaveStartedAConversation} />
<Label label={notification.string.YouHaveJoinedTheConversation} />

View File

@ -30,7 +30,7 @@ export default mergeIds(notificationId, notification, {
Change: '' as IntlString,
AddedRemoved: '' as IntlString,
YouAddedCollaborators: '' as IntlString,
YouHaveStartedAConversation: '' as IntlString,
YouHaveJoinedTheConversation: '' as IntlString,
ChangeCollaborators: '' as IntlString,
Activity: '' as IntlString,
People: '' as IntlString,

View File

@ -14,7 +14,7 @@
// limitations under the License.
//
import { Account, Class, Doc, getCurrentAccount, Ref } from '@hcengineering/core'
import { Account, Class, Doc, DocumentUpdate, getCurrentAccount, Ref, TxOperations } from '@hcengineering/core'
import notification, { Collaborators, DocUpdates, NotificationClient } from '@hcengineering/notification'
import { createQuery, getClient } from '@hcengineering/presentation'
import { writable } from 'svelte/store'
@ -119,29 +119,69 @@ export async function hasntNotifications (object: DocUpdates): Promise<boolean>
return !object.txes.some((p) => p.isNew)
}
enum OpWithMe {
Add = 'add',
Remove = 'remove'
}
async function updateMeInCollaborators (
client: TxOperations,
docClass: Ref<Class<Doc>>,
docId: Ref<Doc>,
op: OpWithMe
): Promise<void> {
const me = getCurrentAccount()._id
const hierarchy = client.getHierarchy()
const target = await client.findOne(docClass, { _id: docId })
if (target !== undefined) {
if (hierarchy.hasMixin(target, notification.mixin.Collaborators)) {
const collab = hierarchy.as(target, notification.mixin.Collaborators)
let collabUpdate: DocumentUpdate<Collaborators> | undefined
if (collab.collaborators.includes(me) && op === OpWithMe.Remove) {
collabUpdate = {
$pull: {
collaborators: me
}
}
} else if (!collab.collaborators.includes(me) && op === OpWithMe.Add) {
collabUpdate = {
$push: {
collaborators: me
}
}
}
if (collabUpdate !== undefined) {
await client.updateMixin(
collab._id,
collab._class,
collab.space,
notification.mixin.Collaborators,
collabUpdate
)
}
}
}
}
/**
* @public
*/
export async function unsubscribe (object: DocUpdates): Promise<void> {
const me = getCurrentAccount()._id
const client = getClient()
const hierarchy = client.getHierarchy()
const target = await client.findOne(object.attachedToClass, { _id: object.attachedTo })
if (target !== undefined) {
if (hierarchy.hasMixin(target, notification.mixin.Collaborators)) {
const collab = hierarchy.as(target, notification.mixin.Collaborators)
if (collab.collaborators.includes(me)) {
await client.updateMixin(collab._id, collab._class, collab.space, notification.mixin.Collaborators, {
$pull: {
collaborators: me
}
})
}
}
}
await updateMeInCollaborators(client, object.attachedToClass, object.attachedTo, OpWithMe.Remove)
await client.remove(object)
}
/**
* @public
*/
export async function subscribe (docClass: Ref<Class<Doc>>, docId: Ref<Doc>): Promise<void> {
const client = getClient()
await updateMeInCollaborators(client, docClass, docId, OpWithMe.Add)
}
/**
* @public
*/

View File

@ -13,15 +13,7 @@
// limitations under the License.
//
import chunter, {
Backlink,
chunterId,
ChunterSpace,
Comment,
DirectMessage,
Message,
ThreadMessage
} from '@hcengineering/chunter'
import chunter, { Backlink, chunterId, ChunterSpace, Comment, Message, ThreadMessage } from '@hcengineering/chunter'
import contact, { Employee, PersonAccount } from '@hcengineering/contact'
import core, {
Account,
@ -44,7 +36,7 @@ import core, {
import notification, { Collaborators, NotificationType } from '@hcengineering/notification'
import { getMetadata } from '@hcengineering/platform'
import serverCore, { TriggerControl } from '@hcengineering/server-core'
import { pushNotification, getDocCollaborators, getMixinTx } from '@hcengineering/server-notification-resources'
import { getDocCollaborators, getMixinTx, pushNotification } from '@hcengineering/server-notification-resources'
import { workbenchId } from '@hcengineering/workbench'
/**
@ -216,29 +208,50 @@ export async function ChunterTrigger (tx: Tx, control: TriggerControl): Promise<
/**
* @public
* Sends notification to the message sender in case when DM
* notifications are deleted or hidden. This is required for
* the DM to re-appear in the sender's inbox.
*/
export async function OnDmCreate (tx: Tx, control: TriggerControl): Promise<Tx[]> {
const ptx = tx as TxCreateDoc<DirectMessage>
export async function OnMessageSent (tx: Tx, control: TriggerControl): Promise<Tx[]> {
const ptx = TxProcessor.extractTx(tx) as TxCreateDoc<Message>
if (ptx._class !== core.class.TxCreateDoc) return []
const message = TxProcessor.createDoc2Doc(ptx)
if (message.createdBy === undefined) return []
if (!control.hierarchy.isDerived(message.attachedToClass, chunter.class.DirectMessage)) return []
const channel = (await control.findAll(chunter.class.DirectMessage, { _id: message.attachedTo })).shift()
if (channel === undefined || channel.members.length !== 2 || !channel.private) return []
const res: Tx[] = []
if (tx.createdBy == null) return []
const docUpdates = await control.findAll(notification.class.DocUpdates, { attachedTo: channel._id })
const dm = TxProcessor.createDoc2Doc(ptx)
// binding notification to the DM creation tx to properly display it in inbox
const dmCreationTx = (
await control.findAll(core.class.TxCreateDoc, { objectClass: channel._class, objectId: channel._id })
).shift()
if (dmCreationTx === undefined) return []
if (dm.members.length > 2) return []
let dmWithPerson: Ref<Account> | undefined
for (const person of dm.members) {
if (person !== tx.createdBy) {
dmWithPerson = person
break
const sender = message.createdBy
const docUpdate = docUpdates.find((du) => du.user === sender)
if (docUpdate === undefined) {
let anotherPerson: Ref<Account> | undefined
for (const person of channel.members) {
if (person !== sender) {
anotherPerson = person
break
}
}
if (anotherPerson == null) return []
pushNotification(control, res, sender, channel, dmCreationTx, docUpdates, anotherPerson)
} else if (docUpdate.hidden) {
res.push(control.txFactory.createTxUpdateDoc(docUpdate._class, docUpdate.space, docUpdate._id, { hidden: false }))
}
if (dmWithPerson == null) return []
pushNotification(control, res, tx.createdBy, dm, ptx, [], dmWithPerson)
return res
}
@ -309,7 +322,7 @@ export async function IsChannelMessage (
export default async () => ({
trigger: {
ChunterTrigger,
OnDmCreate
OnMessageSent
},
function: {
CommentRemove,

View File

@ -30,7 +30,7 @@ export const serverChunterId = 'server-chunter' as Plugin
export default plugin(serverChunterId, {
trigger: {
ChunterTrigger: '' as Resource<TriggerFunc>,
OnDmCreate: '' as Resource<TriggerFunc>
OnMessageSent: '' as Resource<TriggerFunc>
},
function: {
CommentRemove: '' as Resource<