refactor(backend): new chat message notification email (#8357)

* new chat message notification email

- new mail features name in subject and text

* fix english version

* typo

* fix typos

* adjust tests
This commit is contained in:
Ulf Gebhardt 2025-04-10 18:38:51 +02:00 committed by GitHub
parent fcc99ab58e
commit ab6fe37c3e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 49 additions and 37 deletions

View File

@ -39,7 +39,12 @@ const resetPasswordTemplateData = () => ({
const chatMessageTemplateData = {
email: 'test@example.org',
variables: {
name: 'Mr Example',
senderUser: {
name: 'Sender',
},
recipientUser: {
name: 'Recipient',
},
},
}
const wrongAccountTemplateData = () => ({
@ -174,10 +179,10 @@ describe('templateBuilder', () => {
describe('chatMessageTemplate', () => {
describe('multi language', () => {
it('e-mail is build with all data', () => {
const subject = 'Neue Chatnachricht | New chat message'
const subject = `Neue Chat-Nachricht | New chat message - ${chatMessageTemplateData.variables.senderUser.name}`
const actionUrl = new URL('/chat', CONFIG.CLIENT_URI).toString()
const enContent = 'You have received a new chat message.'
const deContent = 'Du hast eine neue Chatnachricht erhalten.'
const enContent = `You have received a new chat message from <b>${chatMessageTemplateData.variables.senderUser.name}</b>.`
const deContent = `Du hast eine neue Chat-Nachricht von <b>${chatMessageTemplateData.variables.senderUser.name}</b> erhalten.`
testEmailData(null, chatMessageTemplate, chatMessageTemplateData, [
...textsStandard,
{
@ -187,7 +192,8 @@ describe('templateBuilder', () => {
},
englishHint,
actionUrl,
chatMessageTemplateData.variables.name,
chatMessageTemplateData.variables.senderUser,
chatMessageTemplateData.variables.recipientUser,
enContent,
deContent,
supportUrl,

View File

@ -1,9 +1,9 @@
/* eslint-disable import/no-namespace */
import mustache from 'mustache'
import logosWebapp from '@config//logos'
import metadata from '@config//metadata'
import CONFIG from '@config/index'
import logosWebapp from '@config/logos'
import metadata from '@config/metadata'
import * as templates from './templates'
import * as templatesDE from './templates/de'
@ -73,10 +73,17 @@ export const resetPasswordTemplate = ({ email, variables: { nonce, name } }) =>
}
}
export const chatMessageTemplate = ({ email, variables: { name } }) => {
const subject = 'Neue Chatnachricht | New chat message'
export const chatMessageTemplate = ({ email, variables: { senderUser, recipientUser } }) => {
const subject = `Neue Chat-Nachricht | New chat message - ${senderUser.name}`
const actionUrl = new URL('/chat', CONFIG.CLIENT_URI)
const renderParams = { ...defaultParams, englishHint, actionUrl, name, subject }
const renderParams = {
...defaultParams,
subject,
englishHint,
actionUrl,
senderUser,
recipientUser,
}
return {
from,

View File

@ -23,8 +23,8 @@
style="padding: 20px; padding-top: 0; font-family: Lato, sans-serif; font-size: 16px; line-height: 22px; color: #555555;">
<h1
style="margin: 0 0 10px 0; font-family: Lato, sans-serif; font-size: 25px; line-height: 30px; color: #333333; font-weight: normal;">
Hallo {{ name }}!</h1>
<p style="margin: 0;">Du hast eine neue Chatnachricht erhalten.</p>
Hallo {{ recipientUser.name }}!</h1>
<p style="margin: 0;">Du hast eine neue Chat-Nachricht von <b>{{ senderUser.name }}</b> erhalten.</p>
</td>
</tr>
<tr>
@ -78,8 +78,8 @@
style="padding: 20px; padding-top: 0; font-family: Lato, sans-serif; font-size: 16px; line-height: 22px; color: #555555;">
<h1
style="margin: 0 0 10px 0; font-family: Lato, sans-serif; font-size: 25px; line-height: 30px; color: #333333; font-weight: normal;">
Hello {{ name }}!</h1>
<p style="margin: 0;">You have received a new chat message.</p>
Hello {{ recipientUser.name }}!</h1>
<p style="margin: 0;">You have received a new chat message from <b>{{ senderUser.name }}</b>.</p>
</td>
</tr>
<tr>

View File

@ -5,35 +5,33 @@ let user
describe('isUserOnline', () => {
beforeEach(() => {
user = {
properties: {
lastActiveAt: null,
awaySince: null,
lastOnlineStatus: null,
},
lastActiveAt: null,
awaySince: null,
lastOnlineStatus: null,
}
})
describe('user has lastOnlineStatus `online`', () => {
it('returns true if he was active within the last 90 seconds', () => {
user.properties.lastOnlineStatus = 'online'
user.properties.lastActiveAt = new Date()
user.lastOnlineStatus = 'online'
user.lastActiveAt = new Date()
expect(isUserOnline(user)).toBe(true)
})
it('returns false if he was not active within the last 90 seconds', () => {
user.properties.lastOnlineStatus = 'online'
user.properties.lastActiveAt = new Date().getTime() - 90001
user.lastOnlineStatus = 'online'
user.lastActiveAt = new Date().getTime() - 90001
expect(isUserOnline(user)).toBe(false)
})
})
describe('user has lastOnlineStatus `away`', () => {
it('returns true if he went away less then 180 seconds ago', () => {
user.properties.lastOnlineStatus = 'away'
user.properties.awaySince = new Date()
user.lastOnlineStatus = 'away'
user.awaySince = new Date()
expect(isUserOnline(user)).toBe(true)
})
it('returns false if he went away more then 180 seconds ago', () => {
user.properties.lastOnlineStatus = 'away'
user.properties.awaySince = new Date().getTime() - 180001
user.lastOnlineStatus = 'away'
user.awaySince = new Date().getTime() - 180001
expect(isUserOnline(user)).toBe(false)
})
})

View File

@ -1,9 +1,9 @@
export const isUserOnline = (user) => {
// Is Recipient considered online
const lastActive = new Date(user.properties.lastActiveAt).getTime()
const awaySince = new Date(user.properties.awaySince).getTime()
const lastActive = new Date(user.lastActiveAt).getTime()
const awaySince = new Date(user.awaySince).getTime()
const now = new Date().getTime()
const status = user.properties.lastOnlineStatus
const status = user.lastOnlineStatus
if (
// online & last active less than 1.5min -> online
(status === 'online' && now - lastActive < 90000) ||

View File

@ -355,12 +355,12 @@ const handleCreateMessage = async (resolve, root, args, context, resolveInfo) =>
const session = context.driver.session()
const messageRecipient = session.readTransaction(async (transaction) => {
const messageRecipientCypher = `
MATCH (currentUser:User { id: $currentUserId })-[:CHATS_IN]->(room:Room { id: $roomId })
MATCH (senderUser:User { id: $currentUserId })-[:CHATS_IN]->(room:Room { id: $roomId })
MATCH (room)<-[:CHATS_IN]-(recipientUser:User)-[:PRIMARY_EMAIL]->(emailAddress:EmailAddress)
WHERE NOT recipientUser.id = $currentUserId
AND NOT (recipientUser)-[:BLOCKED]-(currentUser)
AND NOT (recipientUser)-[:BLOCKED]-(senderUser)
AND NOT recipientUser.emailNotificationsChatMessage = false
RETURN recipientUser, emailAddress {.email}
RETURN senderUser {.*}, recipientUser {.*}, emailAddress {.email}
`
const txResponse = await transaction.run(messageRecipientCypher, {
currentUserId,
@ -368,18 +368,19 @@ const handleCreateMessage = async (resolve, root, args, context, resolveInfo) =>
})
return {
user: await txResponse.records.map((record) => record.get('recipientUser'))[0],
senderUser: await txResponse.records.map((record) => record.get('senderUser'))[0],
recipientUser: await txResponse.records.map((record) => record.get('recipientUser'))[0],
email: await txResponse.records.map((record) => record.get('emailAddress'))[0]?.email,
}
})
try {
// Execute Query
const { user, email } = await messageRecipient
const { senderUser, recipientUser, email } = await messageRecipient
// Send EMail if we found a user(not blocked) and he is not considered online
if (user && !isUserOnline(user)) {
void sendMail(chatMessageTemplate({ email, variables: { name: user.properties.name } }))
if (recipientUser && !isUserOnline(recipientUser)) {
void sendMail(chatMessageTemplate({ email, variables: { senderUser, recipientUser } }))
}
// Return resolver result to client