90 lines
5.3 KiB
TypeScript
90 lines
5.3 KiB
TypeScript
import { fail, redirect } from '@sveltejs/kit';
|
|
import { getSessionUser } from '$lib/server/auth/session';
|
|
import { getDatabase } from '$lib/server/database';
|
|
import { getPagination } from '$lib/server/pagination';
|
|
import { NOTIFICATION_CATEGORIES } from '$lib/server/notifications';
|
|
import type { Actions, PageServerLoad } from './$types';
|
|
|
|
export const load: PageServerLoad = async ({ cookies, url }) => {
|
|
const user = await getSessionUser(cookies);
|
|
if (!user) redirect(303, '/sign-in');
|
|
const database = await getDatabase();
|
|
const filtering = url.searchParams.get('filtering') === '1';
|
|
const categories = filtering
|
|
? url.searchParams.getAll('channel').filter((value) => NOTIFICATION_CATEGORIES.includes(value as any))
|
|
: [...NOTIFICATION_CATEGORIES];
|
|
const states = filtering
|
|
? url.searchParams.getAll('state').filter((value) => value === 'unread' || value === 'read')
|
|
: ['unread', 'read'];
|
|
const conditions = ['n.user_id=?', '(n.mandatory=1 OR COALESCE(p.in_app_enabled,1)=1)'];
|
|
const args: unknown[] = [user.id];
|
|
if (categories.length === 0 || states.length === 0) {
|
|
conditions.push('1=0');
|
|
} else {
|
|
if (categories.length < NOTIFICATION_CATEGORIES.length) {
|
|
conditions.push(`n.category IN (${categories.map(() => '?').join(',')})`);
|
|
args.push(...categories);
|
|
}
|
|
if (states.length === 1) conditions.push(states[0] === 'unread' ? 'n.read_at IS NULL' : 'n.read_at IS NOT NULL');
|
|
}
|
|
const where = `WHERE ${conditions.join(' AND ')}`;
|
|
const total = Number((await (await database.prepare(`SELECT COUNT(*) AS total FROM notifications n LEFT JOIN notification_preferences p ON p.user_id=n.user_id AND p.category=n.category ${where}`)).get(...args))?.total ?? 0);
|
|
const pagination = getPagination(url, total);
|
|
const notifications = await (await database.prepare(`SELECT n.* FROM notifications n
|
|
LEFT JOIN notification_preferences p ON p.user_id=n.user_id AND p.category=n.category
|
|
${where} ORDER BY n.created_at DESC LIMIT ? OFFSET ?`)).all(...args, pagination.perPage, pagination.offset);
|
|
const preferenceRows = await (await database.prepare('SELECT * FROM notification_preferences WHERE user_id=?')).all(user.id);
|
|
const preferences = NOTIFICATION_CATEGORIES.map((name) => {
|
|
const stored = preferenceRows.find((row) => row.category === name);
|
|
return {
|
|
category: name,
|
|
inApp: name === 'isac' ? true : stored?.in_app_enabled === undefined ? true : Boolean(stored.in_app_enabled),
|
|
browser: Boolean(stored?.browser_enabled),
|
|
emailFrequency: String(stored?.email_frequency ?? 'none')
|
|
};
|
|
});
|
|
const unread = Number((await (await database.prepare(`SELECT COUNT(*) AS count FROM notifications n
|
|
LEFT JOIN notification_preferences p ON p.user_id=n.user_id AND p.category=n.category
|
|
WHERE n.user_id=? AND n.read_at IS NULL AND (n.mandatory=1 OR COALESCE(p.in_app_enabled,1)=1)`)).get(user.id))?.count ?? 0);
|
|
return { notifications, preferences, pagination, filters: { categories, states }, unread, hasVerifiedEmail: Boolean(await (await database.prepare('SELECT 1 FROM user_emails WHERE user_id=?')).get(user.id)) };
|
|
};
|
|
|
|
export const actions: Actions = {
|
|
markRead: async ({ cookies, request }) => {
|
|
const user = await getSessionUser(cookies);
|
|
if (!user) redirect(303, '/sign-in');
|
|
const id = String((await request.formData()).get('id') ?? '');
|
|
await (await (await getDatabase()).prepare('UPDATE notifications SET read_at=COALESCE(read_at,?) WHERE id=? AND user_id=?')).run(new Date().toISOString(), id, user.id);
|
|
return { notificationUpdated: true };
|
|
},
|
|
markAllRead: async ({ cookies }) => {
|
|
const user = await getSessionUser(cookies);
|
|
if (!user) redirect(303, '/sign-in');
|
|
await (await (await getDatabase()).prepare('UPDATE notifications SET read_at=? WHERE user_id=? AND read_at IS NULL')).run(new Date().toISOString(), user.id);
|
|
return { notificationsCleared: true };
|
|
},
|
|
savePreferences: async ({ cookies, request }) => {
|
|
const user = await getSessionUser(cookies);
|
|
if (!user) redirect(303, '/sign-in');
|
|
const form = await request.formData();
|
|
const database = await getDatabase();
|
|
const now = new Date().toISOString();
|
|
const hasEmail = Boolean(await (await database.prepare('SELECT 1 FROM user_emails WHERE user_id=?')).get(user.id));
|
|
for (const category of NOTIFICATION_CATEGORIES) {
|
|
const inApp = category === 'isac' || form.get(`inApp:${category}`) === 'on';
|
|
const browser = form.get(`browser:${category}`) === 'on';
|
|
const emailFrequency = String(form.get(`email:${category}`) ?? 'none');
|
|
if (!['none', 'immediate', 'daily'].includes(emailFrequency)) return fail(400, { notificationPreferenceError: 'Choose a valid email frequency.' });
|
|
if (emailFrequency !== 'none' && !hasEmail) return fail(400, { notificationPreferenceError: 'Add and verify a magic-link email before enabling email notifications.' });
|
|
await (await database.prepare(`INSERT INTO notification_preferences(user_id,category,in_app_enabled,browser_enabled,email_frequency,updated_at)
|
|
VALUES(?,?,?,?,?,?) ON CONFLICT(user_id,category) DO UPDATE SET
|
|
in_app_enabled=excluded.in_app_enabled,browser_enabled=excluded.browser_enabled,email_frequency=excluded.email_frequency,updated_at=excluded.updated_at`))
|
|
.run(user.id, category, inApp ? 1 : 0, browser ? 1 : 0, emailFrequency, now);
|
|
}
|
|
return {
|
|
notificationPreferencesSaved: true,
|
|
notificationPreferenceSaveToken: crypto.randomUUID()
|
|
};
|
|
}
|
|
};
|