Skip to content

Commit

Permalink
Handle webxdc notifications
Browse files Browse the repository at this point in the history
resolves #4366

- add new param isWebxdcInfo to DcNotification
- openWebxdc on click
  • Loading branch information
nicodh committed Dec 10, 2024
1 parent 2b3cc19 commit b12aa1c
Show file tree
Hide file tree
Showing 7 changed files with 130 additions and 17 deletions.
13 changes: 12 additions & 1 deletion packages/frontend/src/components/RuntimeAdapter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import useDialog from '../hooks/dialog/useDialog'
import WebxdcSaveToChatDialog from './dialogs/WebxdcSendToChat'
import { saveLastChatId } from '../backend/chat'
import useChat from '../hooks/chat/useChat'
import { internalOpenWebxdc } from '../system-integration/webxdc'
import { BackendRemote } from '../backend-com'

type Props = {
accountId?: number
Expand Down Expand Up @@ -42,7 +44,12 @@ export default function RuntimeAdapter({
}

runtime.setNotificationCallback(
async ({ accountId: notificationAccountId, msgId, chatId }) => {
async ({
accountId: notificationAccountId,
msgId,
chatId,
isWebxdcInfo,
}) => {
if (accountId !== notificationAccountId) {
closeAllDialogs()
// selectAccount always loads the last used chat
Expand All @@ -52,6 +59,10 @@ export default function RuntimeAdapter({
await window.__selectAccount(notificationAccountId)
} else if (chatId !== 0) {
await selectChat(accountId, chatId)
if (isWebxdcInfo) {
const message = await BackendRemote.rpc.getMessage(accountId, msgId)
internalOpenWebxdc(accountId, message)
}
clearNotificationsForChat(notificationAccountId, chatId)
}
if (msgId) {
Expand Down
84 changes: 75 additions & 9 deletions packages/frontend/src/system-integration/notifications.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ function isMuted(accountId: number, chatId: number) {
type queuedNotification = {
chatId: number
messageId: number
isWebxdcInfo?: boolean
}

let queuedNotifications: {
Expand All @@ -26,7 +27,8 @@ let queuedNotifications: {
function incomingMessageHandler(
accountId: number,
chatId: number,
messageId: number
messageId: number,
isWebxdcInfo: boolean = false
) {
log.debug('incomingMessageHandler: ', { chatId, messageId })

Expand Down Expand Up @@ -58,13 +60,23 @@ function incomingMessageHandler(
if (typeof queuedNotifications[accountId] === 'undefined') {
queuedNotifications[accountId] = []
}
queuedNotifications[accountId].push({ chatId, messageId })
queuedNotifications[accountId].push({ chatId, messageId, isWebxdcInfo })
}

async function incomingWebxdcEventHandler(
accountId: number,
messageId: number
) {
const message = await BackendRemote.rpc.getMessage(accountId, messageId)
const chatId = message.chatId
incomingMessageHandler(accountId, chatId, messageId, true)
}

async function showNotification(
accountId: number,
chatId: number,
messageId: number
messageId: number,
isWebxdcInfo: boolean
) {
const tx = window.static_translate

Expand All @@ -76,19 +88,62 @@ async function showNotification(
chatId,
messageId,
accountId,
isWebxdcInfo,
})
} else {
try {
const notificationInfo =
await BackendRemote.rpc.getMessageNotificationInfo(accountId, messageId)
const { chatName, summaryPrefix, summaryText } = notificationInfo
let chatName = ''
let summaryPrefix = ''
let summaryText = ''
let notificationInfo: T.MessageNotificationInfo | undefined
let icon: string | null = null
if (isWebxdcInfo) {
const relatedMessage = await BackendRemote.rpc.getMessage(
accountId,
messageId
)
if (
relatedMessage.systemMessageType === 'WebxdcInfoMessage' &&
relatedMessage.parentId
) {
summaryText = relatedMessage.text
const webxdcMessage = await BackendRemote.rpc.getMessage(
accountId,
relatedMessage.parentId
)
if (webxdcMessage.webxdcInfo) {
summaryPrefix = `${webxdcMessage.webxdcInfo.name}`
if (webxdcMessage.webxdcInfo.icon) {
const iconName = webxdcMessage.webxdcInfo.icon
const iconBlob = await BackendRemote.rpc.getWebxdcBlob(
accountId,
webxdcMessage.id,
iconName
)
// needed for valid dataUrl
const imageExtension = iconName.split('.').pop()
icon = `data:image/${imageExtension};base64, ${iconBlob}`
}
}
}
} else {
notificationInfo = await BackendRemote.rpc.getMessageNotificationInfo(
accountId,
messageId
)
chatName = notificationInfo.chatName
summaryPrefix = notificationInfo.summaryPrefix ?? ''
summaryText = notificationInfo.summaryText ?? ''
icon = getNotificationIcon(notificationInfo)
}
runtime.showNotification({
title: chatName,
body: summaryPrefix ? `${summaryPrefix}: ${summaryText}` : summaryText,
icon: getNotificationIcon(notificationInfo),
icon,
chatId,
messageId,
accountId,
isWebxdcInfo,
})
} catch (error) {
log.error('failed to create notification for message: ', messageId, error)
Expand All @@ -110,6 +165,7 @@ async function showGroupedNotification(
chatId: 0,
messageId: 0,
accountId,
isWebxdcInfo: false,
})
} else {
const chatIds = [...new Set(notifications.map(({ chatId }) => chatId))]
Expand All @@ -136,6 +192,7 @@ async function showGroupedNotification(
chatId: chatIds[0],
messageId: 0, // just select chat on click, no specific message
accountId,
isWebxdcInfo: false, // no way handle webxdcInfo in grouped notifications
})
} else {
// messages from diffent chats
Expand All @@ -152,6 +209,7 @@ async function showGroupedNotification(
chatId: 0,
messageId: 0,
accountId,
isWebxdcInfo: false,
})
}
} catch (error) {
Expand Down Expand Up @@ -202,8 +260,13 @@ async function flushNotifications(accountId: number) {
if (notifications.length > notificationLimit) {
showGroupedNotification(accountId, notifications)
} else {
for (const { chatId, messageId } of notifications) {
await showNotification(accountId, chatId, messageId)
for (const { chatId, messageId, isWebxdcInfo } of notifications) {
await showNotification(
accountId,
chatId,
messageId,
isWebxdcInfo ?? false
)
}
}
notificationLimit = NORMAL_LIMIT
Expand Down Expand Up @@ -236,6 +299,9 @@ export function initNotifications() {
BackendRemote.on('IncomingMsg', (accountId, { chatId, msgId }) => {
incomingMessageHandler(accountId, chatId, msgId)
})
BackendRemote.on('IncomingWebxdcNotify', (accountId, { msgId }) => {
incomingWebxdcEventHandler(accountId, msgId)
})
BackendRemote.on('IncomingMsgBunch', accountId => {
flushNotifications(accountId)
})
Expand Down
11 changes: 9 additions & 2 deletions packages/runtime/runtime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ import type { getLogger as getLoggerFunction } from '@deltachat-desktop/shared/l
import type { setLogHandler as setLogHandlerFunction } from '@deltachat-desktop/shared/logger.js'

/**
* Offers an abstraction Layer to make it easier to make browser client in the future
* Offers an abstraction Layer to make it easier to capsulate
* context specific functions (like electron, browser, tauri, etc)
*/
export interface Runtime {
emitUIFullyReady(): void
Expand Down Expand Up @@ -112,8 +113,14 @@ export interface Runtime {
showNotification(data: DcNotification): void
clearAllNotifications(): void
clearNotifications(chatId: number): void
// enables to set a callback (used in frontend RuntimeAdapter)
setNotificationCallback(
cb: (data: { accountId: number; chatId: number; msgId: number }) => void
cb: (data: {
accountId: number
chatId: number
msgId: number
isWebxdcInfo: boolean
}) => void
): void
/** @param name optional name needed for browser */
writeClipboardToTempFile(name?: string): Promise<string>
Expand Down
1 change: 1 addition & 0 deletions packages/shared/shared-types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ export interface DcNotification {
messageId: number
// for future
accountId: number
isWebxdcInfo: boolean
}

export interface DcOpenWebxdcParameters {
Expand Down
10 changes: 9 additions & 1 deletion packages/target-browser/runtime-browser/runtime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -378,12 +378,18 @@ class BrowserRuntime implements Runtime {
accountId: number
chatId: number
msgId: number
isWebxdcInfo: boolean
}) => void = () => {
this.log.critical('notification click handler not initialized yet')
}

setNotificationCallback(
cb: (data: { accountId: number; chatId: number; msgId: number }) => void
cb: (data: {
accountId: number
chatId: number
msgId: number
isWebxdcInfo: boolean
}) => void
): void {
this.notificationCB = cb
}
Expand All @@ -401,6 +407,7 @@ class BrowserRuntime implements Runtime {
title,
icon: notificationIcon,
messageId,
isWebxdcInfo,
} = data
this.log.debug('showNotification', { accountId, chatId, messageId })

Expand Down Expand Up @@ -447,6 +454,7 @@ class BrowserRuntime implements Runtime {
accountId,
chatId,
msgId: messageId,
isWebxdcInfo,
})

if (this.activeNotifications[chatId]) {
Expand Down
8 changes: 7 additions & 1 deletion packages/target-electron/runtime-electron/runtime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -191,9 +191,15 @@ class ElectronRuntime implements Runtime {
accountId: number
chatId: number
msgId: number
isWebxdcInfo: boolean
}) => void = () => {}
setNotificationCallback(
cb: (data: { accountId: number; chatId: number; msgId: number }) => void
cb: (data: {
accountId: number
chatId: number
msgId: number
isWebxdcInfo: boolean
}) => void
): void {
this.notificationCallback = cb
}
Expand Down
20 changes: 17 additions & 3 deletions packages/target-electron/src/notifications.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@ const isMac = platform() === 'darwin'

function createNotification(data: DcNotification): Notification {
let icon: NativeImage | undefined = data.icon
? nativeImage.createFromPath(data.icon)
? data.icon.indexOf('base64') > -1
? nativeImage.createFromDataURL(data.icon)
: nativeImage.createFromPath(data.icon)
: undefined

if (!icon || icon.isEmpty()) {
Expand Down Expand Up @@ -51,9 +53,15 @@ function onClickNotification(
accountId: number,
chatId: number,
msgId: number,
isWebxdcInfo: boolean,
_ev: Electron.Event
) {
mainWindow.send('ClickOnNotification', { accountId, chatId, msgId })
mainWindow.send('ClickOnNotification', {
accountId,
chatId,
msgId,
isWebxdcInfo,
})
mainWindow.show()
app.focus()
mainWindow.window?.focus()
Expand All @@ -73,7 +81,13 @@ function showNotification(_event: IpcMainInvokeEvent, data: DcNotification) {
const notify = createNotification(data)

notify.on('click', Event => {
onClickNotification(data.accountId, chatId, data.messageId, Event)
onClickNotification(
data.accountId,
chatId,
data.messageId,
data.isWebxdcInfo,
Event
)
notifications[chatId] =
notifications[chatId]?.filter(n => n !== notify) || []
notify.close()
Expand Down

0 comments on commit b12aa1c

Please sign in to comment.