236 lines
6.9 KiB
TypeScript
236 lines
6.9 KiB
TypeScript
import type {
|
|
ClientNotificationFilters,
|
|
ClientPaginationState,
|
|
Notifications,
|
|
} from "@pkg/logic/domains/notifications/data";
|
|
import {
|
|
archiveSC,
|
|
deleteNotificationsSC,
|
|
getNotificationsSQ,
|
|
getUnreadCountSQ,
|
|
markAllReadSC,
|
|
markReadSC,
|
|
markUnreadSC,
|
|
unarchiveSC,
|
|
} from "./notifications.remote";
|
|
import { user } from "$lib/global.stores";
|
|
import { toast } from "svelte-sonner";
|
|
import { get } from "svelte/store";
|
|
|
|
class NotificationViewModel {
|
|
notifications = $state([] as Notifications);
|
|
loading = $state(false);
|
|
selectedIds = $state(new Set<number>());
|
|
|
|
pagination = $state<ClientPaginationState>({
|
|
page: 1,
|
|
pageSize: 20,
|
|
total: 0,
|
|
totalPages: 0,
|
|
sortBy: "createdAt",
|
|
sortOrder: "desc",
|
|
});
|
|
|
|
filters = $state<ClientNotificationFilters>({
|
|
userId: get(user)?.id!,
|
|
isArchived: false,
|
|
});
|
|
|
|
unreadCount = $state(0);
|
|
|
|
private getFetchQueryInput() {
|
|
return {
|
|
filters: {
|
|
userId: this.filters.userId,
|
|
isRead: this.filters.isRead,
|
|
isArchived: this.filters.isArchived,
|
|
type: this.filters.type,
|
|
category: this.filters.category,
|
|
priority: this.filters.priority,
|
|
search: this.filters.search,
|
|
},
|
|
pagination: {
|
|
page: this.pagination.page,
|
|
pageSize: this.pagination.pageSize,
|
|
sortBy: this.pagination.sortBy,
|
|
sortOrder: this.pagination.sortOrder,
|
|
},
|
|
};
|
|
}
|
|
|
|
private async runCommand(
|
|
fn: (payload: { notificationIds: number[] }) => Promise<any>,
|
|
notificationIds: number[],
|
|
errorMessage: string,
|
|
after: Array<() => Promise<void>>,
|
|
) {
|
|
try {
|
|
const result = await fn({ notificationIds });
|
|
if (result?.error) {
|
|
toast.error(result.error.message || errorMessage, {
|
|
description: result.error.description || "Please try again later",
|
|
});
|
|
return;
|
|
}
|
|
|
|
for (const action of after) {
|
|
await action();
|
|
}
|
|
} catch (error) {
|
|
toast.error(errorMessage, {
|
|
description:
|
|
error instanceof Error ? error.message : "Please try again later",
|
|
});
|
|
}
|
|
}
|
|
|
|
async fetchNotifications() {
|
|
this.loading = true;
|
|
try {
|
|
const result = await getNotificationsSQ(this.getFetchQueryInput());
|
|
if (result?.error || !result?.data) {
|
|
toast.error(
|
|
result?.error?.message || "Failed to fetch notifications",
|
|
{
|
|
description:
|
|
result?.error?.description || "Please try again later",
|
|
},
|
|
);
|
|
return;
|
|
}
|
|
|
|
this.notifications = result.data.data as Notifications;
|
|
this.pagination.total = result.data.total;
|
|
this.pagination.totalPages = result.data.totalPages;
|
|
} catch (error) {
|
|
toast.error("Failed to fetch notifications", {
|
|
description:
|
|
error instanceof Error ? error.message : "Please try again later",
|
|
});
|
|
} finally {
|
|
this.loading = false;
|
|
}
|
|
}
|
|
|
|
async markAsRead(notificationIds: number[]) {
|
|
await this.runCommand(markReadSC, notificationIds, "Failed to mark as read", [
|
|
() => this.fetchNotifications(),
|
|
() => this.fetchUnreadCount(),
|
|
]);
|
|
}
|
|
|
|
async markAsUnread(notificationIds: number[]) {
|
|
await this.runCommand(
|
|
markUnreadSC,
|
|
notificationIds,
|
|
"Failed to mark as unread",
|
|
[() => this.fetchNotifications(), () => this.fetchUnreadCount()],
|
|
);
|
|
}
|
|
|
|
async archive(notificationIds: number[]) {
|
|
await this.runCommand(archiveSC, notificationIds, "Failed to archive", [
|
|
() => this.fetchNotifications(),
|
|
]);
|
|
}
|
|
|
|
async unarchive(notificationIds: number[]) {
|
|
await this.runCommand(unarchiveSC, notificationIds, "Failed to unarchive", [
|
|
() => this.fetchNotifications(),
|
|
]);
|
|
}
|
|
|
|
async deleteNotifications(notificationIds: number[]) {
|
|
await this.runCommand(
|
|
deleteNotificationsSC,
|
|
notificationIds,
|
|
"Failed to delete",
|
|
[() => this.fetchNotifications(), () => this.fetchUnreadCount()],
|
|
);
|
|
}
|
|
|
|
async markAllAsRead() {
|
|
try {
|
|
const result = await markAllReadSC({});
|
|
if (result?.error) {
|
|
toast.error(result.error.message || "Failed to mark all as read", {
|
|
description: result.error.description || "Please try again later",
|
|
});
|
|
return;
|
|
}
|
|
await this.fetchNotifications();
|
|
await this.fetchUnreadCount();
|
|
} catch (error) {
|
|
toast.error("Failed to mark all as read", {
|
|
description:
|
|
error instanceof Error ? error.message : "Please try again later",
|
|
});
|
|
}
|
|
}
|
|
|
|
async fetchUnreadCount() {
|
|
try {
|
|
const result = await getUnreadCountSQ();
|
|
if (result?.error) {
|
|
return;
|
|
}
|
|
if (result?.data !== undefined && result?.data !== null) {
|
|
this.unreadCount = result.data as number;
|
|
}
|
|
} catch {
|
|
// Intentionally silent - unread count is non-critical UI data.
|
|
}
|
|
}
|
|
|
|
toggleSelection(id: number) {
|
|
if (this.selectedIds.has(id)) {
|
|
this.selectedIds.delete(id);
|
|
} else {
|
|
this.selectedIds.add(id);
|
|
}
|
|
}
|
|
|
|
selectAll() {
|
|
this.notifications.forEach((n) => this.selectedIds.add(n.id));
|
|
}
|
|
|
|
clearSelection() {
|
|
this.selectedIds.clear();
|
|
}
|
|
|
|
goToPage(page: number) {
|
|
this.pagination.page = page;
|
|
this.fetchNotifications();
|
|
}
|
|
|
|
setPageSize(pageSize: number) {
|
|
this.pagination.pageSize = pageSize;
|
|
this.pagination.page = 1;
|
|
this.fetchNotifications();
|
|
}
|
|
|
|
setSorting(
|
|
sortBy: ClientPaginationState["sortBy"],
|
|
sortOrder: ClientPaginationState["sortOrder"],
|
|
) {
|
|
this.pagination.sortBy = sortBy;
|
|
this.pagination.sortOrder = sortOrder;
|
|
this.pagination.page = 1;
|
|
this.fetchNotifications();
|
|
}
|
|
|
|
setFilters(newFilters: Partial<ClientNotificationFilters>) {
|
|
this.filters = { ...this.filters, ...newFilters };
|
|
this.pagination.page = 1;
|
|
this.fetchNotifications();
|
|
}
|
|
|
|
clearFilters() {
|
|
this.filters = { userId: get(user)?.id!, isArchived: false };
|
|
this.pagination.page = 1;
|
|
this.fetchNotifications();
|
|
}
|
|
}
|
|
|
|
export const notificationViewModel = new NotificationViewModel();
|