feat: merge duplicate notifications (#2056)

Use the `ignoreDuplicates` prop from vue3-notification to ignore duplicate notifications, show a count (×N) instead when notifications are merged.

Superseeds and closes https://github.com/go-vikunja/vikunja/pull/971

---------

Co-authored-by: Dominik Pschenitschni <6173598+dpschen@users.noreply.github.com>
This commit is contained in:
kolaente 2026-01-06 18:36:29 +01:00 committed by GitHub
parent 29b5f7b95e
commit 0ebecb99ba
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 54 additions and 8 deletions

View File

@ -2,6 +2,7 @@
<Notifications
position="bottom left"
:max="2"
:ignore-duplicates="true"
class="global-notification"
>
<template #body="{ item, close }">
@ -20,12 +21,23 @@
{{ item.title }}
</div>
<div class="notification-content">
<template
v-for="(t, k) in item.text"
:key="k"
>
{{ t }}<br>
<template v-if="Array.isArray(item.text)">
<template
v-for="(t, k) in item.text"
:key="k"
>
{{ t }}<br>
</template>
</template>
<template v-else>
{{ item.text }}
</template>
<span
v-if="item.duplicates > 0"
class="tw-text-xs tw-font-bold tw-ml-1"
>
×{{ item.duplicates + 1 }}
</span>
</div>
<div
v-if="item.data?.actions?.length > 0"

View File

@ -36,8 +36,11 @@ export function error(e, actions: Action[] = []) {
notify({
type: 'error',
title: i18n.global.t('error.error'),
text: [getErrorText(e)],
actions: actions,
text: getErrorText(e),
ignoreDuplicates: true,
data: {
actions: actions,
},
})
}
@ -45,7 +48,8 @@ export function success(e, actions: Action[] = []) {
notify({
type: 'success',
title: i18n.global.t('error.success'),
text: [getErrorText(e)],
text: getErrorText(e),
ignoreDuplicates: true,
data: {
actions: actions,
},

View File

@ -0,0 +1,30 @@
import {test, expect} from '../../support/fixtures'
test.describe('Duplicate Notifications', () => {
test('Merges duplicate notifications and shows count', async ({authenticatedPage: page}) => {
await page.goto('/')
await page.waitForLoadState('networkidle')
// Trigger the same notification twice via the Vue app using $notify directly
await page.evaluate(() => {
const app = document.getElementById('app')
const vueApp = (app as any).__vue_app__
vueApp.config.globalProperties.$notify({
type: 'success',
title: 'Test',
text: 'Duplicate Test',
})
vueApp.config.globalProperties.$notify({
type: 'success',
title: 'Test',
text: 'Duplicate Test',
})
})
// Should only show one notification with a count of ×2
const notification = page.locator('.global-notification .vue-notification.success')
await expect(notification).toHaveCount(1)
await expect(notification.locator('.notification-content')).toContainText('Duplicate Test')
await expect(notification.locator('.notification-content')).toContainText('×2')
})
})