mirror of
https://github.com/bookorbit/bookorbit.git
synced 2026-11-03 00:11:37 -06:00
fix(notifications): keep notifications visible when marking all as read
Emit a notification:all-read event instead of notification:cleared when marking all notifications as read, and handle this event on the client by updating the read status rather than emptying the notification list. Closes #587
This commit is contained in:
@@ -363,6 +363,31 @@ describe('useNotifications', () => {
|
||||
expect(notifications.value.every((n) => n.read)).toBe(true)
|
||||
expect(unreadCount.value).toBe(0)
|
||||
})
|
||||
|
||||
it('keeps notifications visible when all-read socket event arrives', async () => {
|
||||
const useNotifications = await loadModule()
|
||||
const { fetchNotifications, notifications, subscribe, total, unreadCount } = useNotifications()
|
||||
|
||||
mockApi.mockReturnValueOnce(
|
||||
mockOk({
|
||||
items: [makeNotification({ id: 1, read: false }), makeNotification({ id: 2, read: false })],
|
||||
total: 2,
|
||||
}),
|
||||
)
|
||||
await fetchNotifications(true)
|
||||
unreadCount.value = 2
|
||||
|
||||
subscribe()
|
||||
const allReadHandler = mockSocket.on.mock.calls.find(([event]: unknown[]) => event === 'notification:all-read')?.[1] as (() => void) | undefined
|
||||
|
||||
expect(allReadHandler).toBeDefined()
|
||||
allReadHandler!()
|
||||
|
||||
expect(notifications.value).toHaveLength(2)
|
||||
expect(notifications.value.every((n) => n.read)).toBe(true)
|
||||
expect(total.value).toBe(2)
|
||||
expect(unreadCount.value).toBe(0)
|
||||
})
|
||||
})
|
||||
|
||||
describe('dismiss', () => {
|
||||
|
||||
@@ -114,6 +114,11 @@ function getSocket(): Socket {
|
||||
total.value = Math.max(0, total.value - 1)
|
||||
})
|
||||
|
||||
socket.on('notification:all-read', () => {
|
||||
notifications.value.forEach((n) => (n.read = true))
|
||||
unreadCount.value = 0
|
||||
})
|
||||
|
||||
socket.on('notification:cleared', () => {
|
||||
notifications.value = []
|
||||
total.value = 0
|
||||
|
||||
@@ -81,6 +81,10 @@ export class NotificationGateway implements OnGatewayInit, OnGatewayConnection,
|
||||
this.server?.to(`user:${userId}`).emit('notification:dismissed', { id: notificationId });
|
||||
}
|
||||
|
||||
emitAllRead(userId: number): void {
|
||||
this.server?.to(`user:${userId}`).emit('notification:all-read', {});
|
||||
}
|
||||
|
||||
emitCleared(userId: number): void {
|
||||
this.server?.to(`user:${userId}`).emit('notification:cleared', {});
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ describe('NotificationService', () => {
|
||||
emitCountUpdate: ReturnType<typeof vi.fn>;
|
||||
emitRead: ReturnType<typeof vi.fn>;
|
||||
emitDismissed: ReturnType<typeof vi.fn>;
|
||||
emitAllRead: ReturnType<typeof vi.fn>;
|
||||
emitCleared: ReturnType<typeof vi.fn>;
|
||||
};
|
||||
|
||||
@@ -47,6 +48,7 @@ describe('NotificationService', () => {
|
||||
emitCountUpdate: vi.fn(),
|
||||
emitRead: vi.fn(),
|
||||
emitDismissed: vi.fn(),
|
||||
emitAllRead: vi.fn(),
|
||||
emitCleared: vi.fn(),
|
||||
};
|
||||
service = new NotificationService(repo as never, gateway as never);
|
||||
@@ -272,7 +274,7 @@ describe('NotificationService', () => {
|
||||
// ---------- markAllAsRead() ----------
|
||||
|
||||
describe('markAllAsRead()', () => {
|
||||
it('marks all as read and emits count update + cleared', async () => {
|
||||
it('marks all as read and emits count update + all-read', async () => {
|
||||
repo.setAllRead.mockResolvedValue(undefined);
|
||||
repo.countUnread.mockResolvedValue(0);
|
||||
|
||||
@@ -281,7 +283,8 @@ describe('NotificationService', () => {
|
||||
expect(repo.setAllRead).toHaveBeenCalledWith(42);
|
||||
expect(repo.countUnread).toHaveBeenCalledWith(42);
|
||||
expect(gateway.emitCountUpdate).toHaveBeenCalledWith(42, 0);
|
||||
expect(gateway.emitCleared).toHaveBeenCalledWith(42);
|
||||
expect(gateway.emitAllRead).toHaveBeenCalledWith(42);
|
||||
expect(gateway.emitCleared).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -104,7 +104,7 @@ export class NotificationService {
|
||||
await this.repo.setAllRead(userId);
|
||||
const count = await this.repo.countUnread(userId);
|
||||
this.gateway.emitCountUpdate(userId, count);
|
||||
this.gateway.emitCleared(userId);
|
||||
this.gateway.emitAllRead(userId);
|
||||
}
|
||||
|
||||
async dismiss(userId: number, id: number): Promise<boolean> {
|
||||
|
||||
Reference in New Issue
Block a user