refactor: notification queries (#32653)
(cherry picked from commit 8f3ba2c808e02e2a606b6acc65ce22f8ee3f9b34)
This commit is contained in:
@@ -12,11 +12,11 @@
|
||||
import { Route } from '$lib/route';
|
||||
import { getGlobalActions } from '$lib/services/app.service';
|
||||
import { mediaQueryManager } from '$lib/stores/media-query-manager.svelte';
|
||||
import { notificationManager } from '$lib/stores/notification-manager.svelte';
|
||||
import { sidebarStore } from '$lib/stores/sidebar.svelte';
|
||||
import { getNotificationsQuery } from '$lib/queries/notification.query';
|
||||
import { ActionButton, Button, IconButton, Logo } from '@immich/ui';
|
||||
import { createQuery } from '@tanstack/svelte-query';
|
||||
import { mdiBellBadge, mdiBellOutline, mdiMagnify, mdiMenu, mdiTrayArrowUp } from '@mdi/js';
|
||||
import { onMount } from 'svelte';
|
||||
import { t } from 'svelte-i18n';
|
||||
import ThemeButton from '../ThemeButton.svelte';
|
||||
import UserAvatar from '../UserAvatar.svelte';
|
||||
@@ -33,15 +33,9 @@
|
||||
let shouldShowAccountInfoPanel = $state(false);
|
||||
let shouldShowNotificationPanel = $state(false);
|
||||
let innerWidth: number = $state(0);
|
||||
const hasUnreadNotifications = $derived(notificationManager.notifications.length > 0);
|
||||
|
||||
onMount(async () => {
|
||||
try {
|
||||
await notificationManager.refresh();
|
||||
} catch (error) {
|
||||
console.error('Failed to load notifications on mount', error);
|
||||
}
|
||||
});
|
||||
const query = createQuery(getNotificationsQuery);
|
||||
const unreadCount = $derived(query.data?.length ?? 0);
|
||||
const hasUnreadNotifications = $derived(unreadCount > 0);
|
||||
|
||||
const { Cast } = $derived(getGlobalActions($t));
|
||||
</script>
|
||||
@@ -143,7 +137,7 @@
|
||||
<div
|
||||
class="pointer-events-none absolute top-0 right-1 flex size-5 items-center justify-center rounded-full border bg-primary text-[10px] font-bold text-light"
|
||||
>
|
||||
{notificationManager.notifications.length}
|
||||
{unreadCount}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
@@ -4,33 +4,28 @@
|
||||
import NotificationItem from '$lib/components/shared-components/navigation-bar/NotificationItem.svelte';
|
||||
import { OpenQueryParam } from '$lib/constants';
|
||||
import { Route } from '$lib/route';
|
||||
import { notificationManager } from '$lib/stores/notification-manager.svelte';
|
||||
import { handleError } from '$lib/utils/handle-error';
|
||||
import { executeMutation } from '$lib/queries/client';
|
||||
import {
|
||||
getNotificationsQuery,
|
||||
updateNotificationMutation,
|
||||
updateNotificationsMutation,
|
||||
} from '$lib/queries/notification.query';
|
||||
import { NotificationType, type NotificationDto } from '@immich/sdk';
|
||||
import { Button, Icon, Scrollable, Stack, Text, toastManager } from '@immich/ui';
|
||||
import { Button, Icon, Scrollable, Stack, Text } from '@immich/ui';
|
||||
import { mdiBellOutline, mdiCheckAll } from '@mdi/js';
|
||||
import { createQuery } from '@tanstack/svelte-query';
|
||||
import { t } from 'svelte-i18n';
|
||||
import { flip } from 'svelte/animate';
|
||||
import { fade } from 'svelte/transition';
|
||||
|
||||
const noUnreadNotifications = $derived(notificationManager.notifications.length === 0);
|
||||
const query = createQuery(getNotificationsQuery);
|
||||
const notifications = $derived(query.data ?? []);
|
||||
const noUnreadNotifications = $derived(notifications.length === 0);
|
||||
|
||||
const markAsRead = async (id: string) => {
|
||||
try {
|
||||
await notificationManager.markAsRead(id);
|
||||
} catch (error) {
|
||||
handleError(error, $t('errors.failed_to_update_notification_status'));
|
||||
}
|
||||
};
|
||||
|
||||
const markAllAsRead = async () => {
|
||||
try {
|
||||
await notificationManager.markAllAsRead();
|
||||
toastManager.info($t('marked_all_as_read'));
|
||||
} catch (error) {
|
||||
handleError(error, $t('errors.failed_to_update_notification_status'));
|
||||
}
|
||||
};
|
||||
const markAllAsRead = () =>
|
||||
executeMutation(updateNotificationsMutation, [
|
||||
{ notificationUpdateAllDto: { ids: notifications.map(({ id }) => id), readAt: new Date().toISOString() } },
|
||||
]);
|
||||
|
||||
const handleNotificationAction = async (notification: NotificationDto) => {
|
||||
switch (notification.type) {
|
||||
@@ -63,10 +58,12 @@
|
||||
}
|
||||
};
|
||||
|
||||
const onclick = async (notification: NotificationDto) => {
|
||||
await markAsRead(notification.id);
|
||||
await handleNotificationAction(notification);
|
||||
};
|
||||
const onclick = (notification: NotificationDto) =>
|
||||
executeMutation(
|
||||
updateNotificationMutation,
|
||||
[{ id: notification.id, notificationUpdateDto: { readAt: new Date().toISOString() } }],
|
||||
{ onSuccess: () => void handleNotificationAction(notification) },
|
||||
);
|
||||
</script>
|
||||
|
||||
<div
|
||||
@@ -104,7 +101,7 @@
|
||||
{:else}
|
||||
<Scrollable class="pb-6">
|
||||
<Stack gap={0}>
|
||||
{#each notificationManager.notifications as notification (notification.id)}
|
||||
{#each notifications as notification (notification.id)}
|
||||
<div animate:flip={{ duration: 400 }}>
|
||||
<NotificationItem {notification} {onclick} />
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
import { getNotifications, updateNotification, updateNotifications } from '@immich/sdk';
|
||||
import { queryOptions } from '@tanstack/svelte-query';
|
||||
import { asMutation, queryClient } from '$lib/queries/client';
|
||||
|
||||
export const getNotificationsQuery = () =>
|
||||
queryOptions({
|
||||
queryKey: ['notifications'],
|
||||
queryFn: ({ signal }) => getNotifications({ unread: true }, { signal }),
|
||||
});
|
||||
|
||||
export const updateNotificationMutation = () =>
|
||||
asMutation(updateNotification, {
|
||||
onSuccess: (notification) =>
|
||||
queryClient.setQueryData(getNotificationsQuery().queryKey, (notifications) =>
|
||||
notifications?.filter((item) => item.id !== notification.id || !notification.readAt),
|
||||
),
|
||||
meta: { error: { toast: 'errors.failed_to_update_notification_status' } },
|
||||
});
|
||||
|
||||
export const updateNotificationsMutation = () =>
|
||||
asMutation(updateNotifications, {
|
||||
onSuccess: (_, [{ notificationUpdateAllDto }]) =>
|
||||
queryClient.setQueryData(getNotificationsQuery().queryKey, (notifications) =>
|
||||
notifications?.filter(
|
||||
(item) => !notificationUpdateAllDto.ids.includes(item.id) || !notificationUpdateAllDto.readAt,
|
||||
),
|
||||
),
|
||||
meta: {
|
||||
success: { toast: { color: 'info', description: 'marked_all_as_read' } },
|
||||
error: { toast: 'errors.failed_to_update_notification_status' },
|
||||
},
|
||||
});
|
||||
@@ -1,42 +0,0 @@
|
||||
import { getNotifications, updateNotification, updateNotifications, type NotificationDto } from '@immich/sdk';
|
||||
import { t } from 'svelte-i18n';
|
||||
import { get } from 'svelte/store';
|
||||
import { eventManager } from '$lib/managers/event-manager.svelte';
|
||||
import { handleError } from '$lib/utils/handle-error';
|
||||
|
||||
class NotificationStore {
|
||||
notifications = $state<NotificationDto[]>([]);
|
||||
|
||||
constructor() {
|
||||
eventManager.on({
|
||||
AuthLogin: () => this.refresh(),
|
||||
AuthLogout: () => this.clear(),
|
||||
});
|
||||
}
|
||||
|
||||
async refresh() {
|
||||
try {
|
||||
this.notifications = await getNotifications({ unread: true });
|
||||
} catch (error) {
|
||||
const translate = get(t);
|
||||
handleError(error, translate('errors.failed_to_load_notifications'));
|
||||
}
|
||||
}
|
||||
|
||||
markAsRead = async (id: string) => {
|
||||
this.notifications = this.notifications.filter((notification) => notification.id !== id);
|
||||
await updateNotification({ id, notificationUpdateDto: { readAt: new Date().toISOString() } });
|
||||
};
|
||||
|
||||
markAllAsRead = async () => {
|
||||
const ids = this.notifications.map(({ id }) => id);
|
||||
this.notifications = [];
|
||||
await updateNotifications({ notificationUpdateAllDto: { ids, readAt: new Date().toISOString() } });
|
||||
};
|
||||
|
||||
clear = () => {
|
||||
this.notifications = [];
|
||||
};
|
||||
}
|
||||
|
||||
export const notificationManager = new NotificationStore();
|
||||
@@ -15,11 +15,11 @@ import { page } from '$app/state';
|
||||
import { authManager } from '$lib/managers/auth-manager.svelte';
|
||||
import { eventManager } from '$lib/managers/event-manager.svelte';
|
||||
import { queryClient } from '$lib/queries/client';
|
||||
import { getNotificationsQuery } from '$lib/queries/notification.query';
|
||||
import { getServerConfigQuery } from '$lib/queries/server.query';
|
||||
import { searchUsersAdminQuery } from '$lib/queries/user-admin.query';
|
||||
import { Route } from '$lib/route';
|
||||
import { maintenanceStore } from '$lib/stores/maintenance.store';
|
||||
import { notificationManager } from '$lib/stores/notification-manager.svelte';
|
||||
import { createEventEmitter } from '$lib/utils/eventemitter';
|
||||
|
||||
interface AppRestartEvent {
|
||||
@@ -103,7 +103,7 @@ websocket
|
||||
.on('on_asset_trash', (assets) => eventManager.emit('AssetsDelete', assets))
|
||||
.on('on_asset_update', (asset) => eventManager.emit('AssetUpdate', asset))
|
||||
.on('on_person_thumbnail', (id) => eventManager.emit('PersonThumbnailReady', { id }))
|
||||
.on('on_notification', () => notificationManager.refresh())
|
||||
.on('on_notification', () => void queryClient.invalidateQueries({ queryKey: getNotificationsQuery().queryKey }))
|
||||
.on('connect_error', (e) => console.log('Websocket Connect Error', e));
|
||||
|
||||
export const openWebsocketConnection = () => {
|
||||
|
||||
Reference in New Issue
Block a user