diff --git a/packages/frontend/src/components/Settings/Notifications.tsx b/packages/frontend/src/components/Settings/Notifications.tsx
index ca520326f1..47197a5d77 100644
--- a/packages/frontend/src/components/Settings/Notifications.tsx
+++ b/packages/frontend/src/components/Settings/Notifications.tsx
@@ -22,6 +22,12 @@ export default function Notifications({ desktopSettings }: Props) {
<>
{tx('pref_current_account')}
+
{tx('pref_all_accounts')}
{
- incomingMessageHandler(accountId, chatId, msgId, false)
+ log.debug('IncomingMsg', { accountId, msgId, chatId })
+ incomingMessageHandler(accountId, chatId, msgId, NOTIFICATION_TYPE.MESSAGE)
})
+
BackendRemote.on(
'IncomingWebxdcNotify',
(accountId, { msgId, text, chatId }) => {
- incomingMessageHandler(accountId, chatId, msgId, true, text)
+ incomingMessageHandler(
+ accountId,
+ chatId,
+ msgId,
+ NOTIFICATION_TYPE.WEBXDC_INFO,
+ text
+ )
}
)
+
+ BackendRemote.on(
+ 'IncomingReaction',
+ (accountId, { contactId, chatId, msgId, reaction }) => {
+ log.debug('IncomingReaction', { contactId, chatId, msgId, reaction })
+ incomingMessageHandler(
+ accountId,
+ chatId,
+ msgId,
+ NOTIFICATION_TYPE.REACTION,
+ reaction,
+ contactId
+ )
+ }
+ )
+
BackendRemote.on('IncomingMsgBunch', accountId => {
flushNotifications(accountId)
})
@@ -39,23 +64,25 @@ function isMuted(accountId: number, chatId: number) {
return BackendRemote.rpc.isChatMuted(accountId, chatId)
}
-type queuedNotification = {
+type QueuedNotification = {
chatId: number
messageId: number
- isWebxdcInfo: boolean
- eventText?: string
+ notificationType: NOTIFICATION_TYPE
+ eventText: string // for webxdc-info notifications or reactions
+ contactId?: number // for reactions
}
let queuedNotifications: {
- [accountId: number]: queuedNotification[]
+ [accountId: number]: QueuedNotification[]
} = {}
function incomingMessageHandler(
accountId: number,
chatId: number,
messageId: number,
- isWebxdcInfo: boolean,
- eventText?: string
+ notificationType: NOTIFICATION_TYPE,
+ eventText = '',
+ contactId?: number
) {
log.debug('incomingMessageHandler: ', { chatId, messageId })
@@ -90,8 +117,9 @@ function incomingMessageHandler(
queuedNotifications[accountId].push({
chatId,
messageId,
- isWebxdcInfo,
+ notificationType,
eventText,
+ contactId,
})
}
@@ -99,8 +127,9 @@ async function showNotification(
accountId: number,
chatId: number,
messageId: number,
- isWebxdcInfo: boolean,
- eventText?: string
+ notificationType: NOTIFICATION_TYPE,
+ eventText: string,
+ contactId?: number
) {
const tx = window.static_translate
@@ -112,16 +141,17 @@ async function showNotification(
chatId,
messageId,
accountId,
+ notificationType,
})
} else {
try {
const notificationInfo =
await BackendRemote.rpc.getMessageNotificationInfo(accountId, messageId)
- let summaryPrefix = notificationInfo.summaryPrefix
- const summaryText = eventText ?? notificationInfo.summaryText
+ let summaryPrefix = notificationInfo.summaryPrefix ?? ''
+ let summaryText = notificationInfo.summaryText ?? ''
const chatName = notificationInfo.chatName
let icon = getNotificationIcon(notificationInfo)
- if (isWebxdcInfo) {
+ if (notificationType === NOTIFICATION_TYPE.WEBXDC_INFO) {
/**
* messageId may refer to a webxdc message OR a wexdc-info-message!
*
@@ -141,6 +171,7 @@ async function showNotification(
)
}
if (message.webxdcInfo) {
+ summaryText = eventText
summaryPrefix = `${message.webxdcInfo.name}`
if (message.webxdcInfo.icon) {
const iconName = message.webxdcInfo.icon
@@ -156,6 +187,19 @@ async function showNotification(
} else {
throw new Error(`no webxdcInfo in message with id ${message.id}`)
}
+ } else if (notificationType === NOTIFICATION_TYPE.REACTION) {
+ if (contactId) {
+ const reactionSender = await BackendRemote.rpc.getContact(
+ accountId,
+ contactId
+ )
+ summaryText = `${tx('reaction_by_other', [
+ reactionSender.displayName,
+ eventText,
+ summaryText,
+ ])}`
+ summaryPrefix = '' // not needed, sender name is included in summaryText
+ }
}
runtime.showNotification({
title: chatName,
@@ -164,6 +208,7 @@ async function showNotification(
chatId,
messageId,
accountId,
+ notificationType,
})
} catch (error) {
log.error('failed to create notification for message: ', messageId, error)
@@ -173,7 +218,7 @@ async function showNotification(
async function showGroupedNotification(
accountId: number,
- notifications: queuedNotification[]
+ notifications: QueuedNotification[]
) {
const tx = window.static_translate
@@ -185,6 +230,7 @@ async function showGroupedNotification(
chatId: 0,
messageId: 0,
accountId,
+ notificationType: NOTIFICATION_TYPE.MESSAGE,
})
} else {
const chatIds = [...new Set(notifications.map(({ chatId }) => chatId))]
@@ -211,6 +257,7 @@ async function showGroupedNotification(
chatId: chatIds[0],
messageId: 0, // just select chat on click, no specific message
accountId,
+ notificationType: NOTIFICATION_TYPE.MESSAGE,
})
} else {
// messages from diffent chats
@@ -227,6 +274,7 @@ async function showGroupedNotification(
chatId: 0,
messageId: 0,
accountId,
+ notificationType: NOTIFICATION_TYPE.MESSAGE,
})
}
} catch (error) {
@@ -246,7 +294,7 @@ async function flushNotifications(accountId: number) {
// make it work even if there is nothing
queuedNotifications[accountId] = []
}
- let notifications = [...queuedNotifications[accountId]]
+ const notifications = [...queuedNotifications[accountId]]
queuedNotifications = []
// filter out muted chats:
@@ -264,37 +312,83 @@ async function flushNotifications(accountId: number) {
// some chats are muted
log.debug(`ignoring notifications of ${mutedChats.length} muted chats`)
}
- notifications = notifications.filter(notification => {
- if (mutedChats.includes(notification.chatId)) {
- // muted chat
- log.debug('notification ignored: chat muted', notification)
- return false
- } else {
- return true
- }
- })
+ const filteredNotifications = (
+ await Promise.all(
+ notifications.map(async notification => {
+ if (mutedChats.includes(notification.chatId)) {
+ // muted chat - only show if it's a mention and mentions are enabled
+ const isMention = await notificationIsMention(accountId, notification)
+ return isMention ? notification : null
+ } else {
+ log.debug('notification on not muted chat:', notification)
+ return notification
+ }
+ })
+ )
+ ).filter(notification => notification !== null)
- if (notifications.length > notificationLimit) {
+ if (filteredNotifications.length > notificationLimit) {
showGroupedNotification(accountId, notifications)
} else {
for (const {
chatId,
messageId,
- isWebxdcInfo,
+ notificationType,
eventText,
- } of notifications) {
+ contactId,
+ } of filteredNotifications) {
await showNotification(
accountId,
chatId,
messageId,
- isWebxdcInfo,
- eventText
+ notificationType,
+ eventText,
+ contactId
)
}
}
notificationLimit = NORMAL_LIMIT
}
+/**
+ * if isMentionsEnabled returns true
+ * if the notification is a mention
+ */
+async function notificationIsMention(
+ accountId: number,
+ notification: QueuedNotification
+) {
+ if (!SettingsStoreInstance.state?.desktopSettings.isMentionsEnabled) {
+ log.info('allowIfMention: mentions are disabled')
+ return false
+ } else if (notification.notificationType === NOTIFICATION_TYPE.WEBXDC_INFO) {
+ log.info('mention detected: webxdc-info notification')
+ return true
+ } else {
+ const message = await BackendRemote.rpc.getMessage(
+ accountId,
+ notification.messageId
+ )
+ if (notification.notificationType === NOTIFICATION_TYPE.REACTION) {
+ if (message.sender.id === C.DC_CONTACT_ID_SELF) {
+ log.info('mention detected: reaction to own message')
+ return true
+ }
+ }
+ if (message.quote && message.quote.kind === 'WithMessage') {
+ const quote = await BackendRemote.rpc.getMessage(
+ accountId,
+ message.quote.messageId
+ )
+ if (quote.sender.id === C.DC_CONTACT_ID_SELF) {
+ log.info('mention detected: answer to own message')
+ return true
+ }
+ }
+ return false
+ }
+}
+
export function clearNotificationsForChat(accountId: number, chatId: number) {
log.debug('clearNotificationsForChat', accountId, chatId)
// ask runtime to delete the notifications
diff --git a/packages/shared/constants.ts b/packages/shared/constants.ts
index 6afb9c5fd3..af7e1c96d2 100644
--- a/packages/shared/constants.ts
+++ b/packages/shared/constants.ts
@@ -33,3 +33,9 @@ export const IMAGE_EXTENSIONS = ['jpg', 'jpeg', 'png', 'apng', 'gif', 'webp']
// Videochat Server URLs
export const VIDEO_CHAT_INSTANCE_SYSTEMLI = 'https://meet.systemli.org/$ROOM'
export const VIDEO_CHAT_INSTANCE_AUTISTICI = 'https://vc.autistici.org/$ROOM'
+
+export const enum NOTIFICATION_TYPE {
+ MESSAGE,
+ REACTION,
+ WEBXDC_INFO,
+}
diff --git a/packages/shared/shared-types.d.ts b/packages/shared/shared-types.d.ts
index eeab6c814c..b4922994d5 100644
--- a/packages/shared/shared-types.d.ts
+++ b/packages/shared/shared-types.d.ts
@@ -25,6 +25,7 @@ export interface DesktopSettingsType {
locale: string | null
notifications: boolean
showNotificationContent: boolean
+ isMentionsEnabled: boolean
/** @deprecated isn't used anymore since the move to jsonrpc */
lastChats: { [accountId: number]: number }
zoomFactor: number
@@ -69,6 +70,7 @@ export interface RC_Config {
}
import type { T } from '@deltachat/jsonrpc-client'
+import { NOTIFICATION_TYPE } from './constants.ts'
export type Theme = {
name: string
@@ -117,6 +119,7 @@ export interface DcNotification {
messageId: number
// for future
accountId: number
+ notificationType: NOTIFICATION_TYPE
}
export interface DcOpenWebxdcParameters {