feat(frontend): notifications

This commit is contained in:
sam 2025-03-05 01:18:21 +01:00
parent f99d10ecf0
commit dd9d35249c
Signed by: sam
GPG key ID: B4EF20DDE721CAA1
9 changed files with 129 additions and 6 deletions

View file

@ -2,16 +2,24 @@ import { clearToken, TOKEN_COOKIE_NAME } from "$lib";
import { apiRequest } from "$api";
import ApiError, { ErrorCode } from "$api/error";
import type { Meta, MeUser } from "$api/models";
import type { Notification } from "$api/models/moderation";
import log from "$lib/log";
import type { LayoutServerLoad } from "./$types";
export const load = (async ({ fetch, cookies }) => {
let token: string | null = null;
let meUser: MeUser | null = null;
let unreadNotifications: boolean = false;
if (cookies.get(TOKEN_COOKIE_NAME)) {
try {
meUser = await apiRequest<MeUser>("GET", "/users/@me", { fetch, cookies });
token = cookies.get(TOKEN_COOKIE_NAME) || null;
const notifications = await apiRequest<Notification[]>("GET", "/notifications", {
fetch,
cookies,
});
unreadNotifications = notifications.filter((n) => !n.acknowledged).length > 0;
} catch (e) {
if (e instanceof ApiError && e.code === ErrorCode.AuthenticationRequired) clearToken(cookies);
else log.error("Could not fetch /users/@me and token has not expired:", e);
@ -19,5 +27,5 @@ export const load = (async ({ fetch, cookies }) => {
}
const meta = await apiRequest<Meta>("GET", "/meta", { fetch, cookies });
return { meta, meUser, token };
return { meta, meUser, token, unreadNotifications };
}) satisfies LayoutServerLoad;

View file

@ -11,7 +11,7 @@
<div class="d-flex flex-column min-vh-100">
<div class="flex-grow-1">
<Navbar user={data.meUser} meta={data.meta} />
<Navbar user={data.meUser} meta={data.meta} unreadNotifications={data.unreadNotifications} />
{@render children?.()}
</div>
<Footer meta={data.meta} />

View file

@ -0,0 +1,11 @@
import { apiRequest } from "$api";
import type { Notification } from "$api/models/moderation";
import { alertKey } from "$lib";
export const load = async ({ url, fetch, cookies }) => {
const notifications = await apiRequest<Notification[]>("GET", "/notifications", {
fetch,
cookies,
});
return { notifications, alertKey: alertKey(url) };
};

View file

@ -0,0 +1,16 @@
<script lang="ts">
import type { PageData } from "./$types";
import Notification from "$components/settings/Notification.svelte";
import UrlAlert from "$components/URLAlert.svelte";
type Props = { data: PageData };
let { data }: Props = $props();
</script>
<UrlAlert {data} />
{#each data.notifications as notification (notification.id)}
<Notification {notification} />
{:else}
You have no notifications.
{/each}

View file

@ -0,0 +1,14 @@
import { redirect } from "@sveltejs/kit";
import type { RequestHandler } from "./$types";
import { fastRequest } from "$api";
import log from "$lib/log";
export const GET: RequestHandler = async ({ params, fetch, cookies }) => {
try {
await fastRequest("PUT", `/notifications/${params.id}/ack`, { fetch, cookies });
} catch (e) {
log.error("error acking notification %s:", params.id, e);
redirect(303, "/settings/notifications?alert=notif-ack-fail");
}
redirect(303, "/settings/notifications?alert=notif-ack-successful");
};