From 3081adc945038949ccb118c51ff74b29235e08eb Mon Sep 17 00:00:00 2001 From: = Date: Tue, 1 Jul 2025 15:52:30 +0100 Subject: [PATCH] feat: notifications link to relevant pages when available. --- .../components/self/UserManualModal.svelte | 3 +- website/src/lib/server/db/schema.ts | 1 + website/src/lib/server/job.ts | 2 + website/src/lib/server/notification.ts | 5 ++- website/src/lib/stores/notifications.ts | 1 + website/src/lib/stores/websocket.ts | 1 + .../api/coin/[coinSymbol]/trade/+server.ts | 1 + .../src/routes/api/notifications/+server.ts | 1 + website/src/routes/api/prestige/+server.ts | 2 +- website/src/routes/api/transfer/+server.ts | 2 + website/src/routes/notifications/+page.svelte | 12 ++---- .../notifications/NotificationItem.svelte | 40 +++++++++++++++++++ 12 files changed, 59 insertions(+), 12 deletions(-) create mode 100644 website/src/routes/notifications/NotificationItem.svelte diff --git a/website/src/lib/components/self/UserManualModal.svelte b/website/src/lib/components/self/UserManualModal.svelte index 58fb01e..275966f 100644 --- a/website/src/lib/components/self/UserManualModal.svelte +++ b/website/src/lib/components/self/UserManualModal.svelte @@ -205,11 +205,12 @@
{#each tips as _, index} {/each}
diff --git a/website/src/lib/server/db/schema.ts b/website/src/lib/server/db/schema.ts index 40abe7e..30fcc12 100644 --- a/website/src/lib/server/db/schema.ts +++ b/website/src/lib/server/db/schema.ts @@ -265,6 +265,7 @@ export const notifications = pgTable("notification", { type: notificationTypeEnum("type").notNull(), title: varchar("title", { length: 200 }).notNull(), message: text("message").notNull(), + link: text("link"), isRead: boolean("is_read").notNull().default(false), createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(), }, (table) => { diff --git a/website/src/lib/server/job.ts b/website/src/lib/server/job.ts index 9f3a61a..04ba42a 100644 --- a/website/src/lib/server/job.ts +++ b/website/src/lib/server/job.ts @@ -120,6 +120,7 @@ export async function resolveExpiredQuestions() { 'HOPIUM', title, message, + `/hopium/${question.id}` ); } }); @@ -219,6 +220,7 @@ export async function resolveExpiredQuestions() { 'HOPIUM', title, message, + `/hopium/${question.id}` ); } }); diff --git a/website/src/lib/server/notification.ts b/website/src/lib/server/notification.ts index a5dd010..abf7428 100644 --- a/website/src/lib/server/notification.ts +++ b/website/src/lib/server/notification.ts @@ -9,12 +9,14 @@ export async function createNotification( type: NotificationType, title: string, message: string, + link?: string, ): Promise { await db.insert(notifications).values({ userId: parseInt(userId), type, title, - message + message, + link }); try { @@ -27,6 +29,7 @@ export async function createNotification( notificationType: type, title, message, + link }; await redis.publish(channel, JSON.stringify(payload)); diff --git a/website/src/lib/stores/notifications.ts b/website/src/lib/stores/notifications.ts index a5a00b9..ecfbf3c 100644 --- a/website/src/lib/stores/notifications.ts +++ b/website/src/lib/stores/notifications.ts @@ -5,6 +5,7 @@ export interface Notification { type: string; title: string; message: string; + link?: string; data: any; isRead: boolean; createdAt: string; diff --git a/website/src/lib/stores/websocket.ts b/website/src/lib/stores/websocket.ts index 54d38b2..7e26a26 100644 --- a/website/src/lib/stores/websocket.ts +++ b/website/src/lib/stores/websocket.ts @@ -197,6 +197,7 @@ function handleWebSocketMessage(event: MessageEvent): void { type: message.notificationType, title: message.title, message: message.message, + link: message.link, isRead: false, createdAt: message.timestamp, data: message.amount ? { amount: message.amount } : null diff --git a/website/src/routes/api/coin/[coinSymbol]/trade/+server.ts b/website/src/routes/api/coin/[coinSymbol]/trade/+server.ts index 821e93e..ac2a531 100644 --- a/website/src/routes/api/coin/[coinSymbol]/trade/+server.ts +++ b/website/src/routes/api/coin/[coinSymbol]/trade/+server.ts @@ -354,6 +354,7 @@ export async function POST({ params, request }) { 'RUG_PULL', 'Coin rugpulled!', `A coin you owned, ${coinData.name} (*${normalizedSymbol}), crashed ${Math.abs(priceImpact).toFixed(1)}%!`, + `/coin/${normalizedSymbol}` ); } } diff --git a/website/src/routes/api/notifications/+server.ts b/website/src/routes/api/notifications/+server.ts index 1438ef1..8f9500e 100644 --- a/website/src/routes/api/notifications/+server.ts +++ b/website/src/routes/api/notifications/+server.ts @@ -25,6 +25,7 @@ export const GET: RequestHandler = async ({ url, request }) => { type: notifications.type, title: notifications.title, message: notifications.message, + link: notifications.link, isRead: notifications.isRead, createdAt: notifications.createdAt, }) diff --git a/website/src/routes/api/prestige/+server.ts b/website/src/routes/api/prestige/+server.ts index 4564278..81dbe5d 100644 --- a/website/src/routes/api/prestige/+server.ts +++ b/website/src/routes/api/prestige/+server.ts @@ -93,7 +93,7 @@ export const POST: RequestHandler = async ({ request, locals }) => { userId: userId, type: 'SYSTEM', title: `${prestigeName} Achieved!`, - message: `Congratulations! You have successfully reached ${prestigeName}. Your portfolio has been reset, daily reward cooldown has been cleared, and you can now start fresh with your new prestige badge and enhanced daily rewards.`, + message: `Congratulations! You have successfully reached ${prestigeName}. Your portfolio has been reset, daily reward cooldown has been cleared, and you can now start fresh with your new prestige badge and enhanced daily rewards.` }); return json({ diff --git a/website/src/routes/api/transfer/+server.ts b/website/src/routes/api/transfer/+server.ts index 726d345..7c1f225 100644 --- a/website/src/routes/api/transfer/+server.ts +++ b/website/src/routes/api/transfer/+server.ts @@ -131,6 +131,7 @@ export const POST: RequestHandler = async ({ request }) => { 'TRANSFER', 'Money received!', `You received ${formatValue(amount)} from @${senderData.username}`, + `/user/${senderData.id}` ); })(); @@ -264,6 +265,7 @@ export const POST: RequestHandler = async ({ request }) => { 'TRANSFER', 'Coins received!', `You received ${amount.toFixed(6)} *${coinData.symbol} from @${senderData.username}`, + `/coin/${normalizedSymbol}` ); })(); diff --git a/website/src/routes/notifications/+page.svelte b/website/src/routes/notifications/+page.svelte index 7a8ba6a..8b3c761 100644 --- a/website/src/routes/notifications/+page.svelte +++ b/website/src/routes/notifications/+page.svelte @@ -16,6 +16,7 @@ import { formatTimeAgo, formatValue } from '$lib/utils'; import { goto } from '$app/navigation'; import { toast } from 'svelte-sonner'; + import NotificationItem from './NotificationItem.svelte'; let loading = $state(true); let newNotificationIds = $state([]); @@ -132,13 +133,7 @@ {#each $NOTIFICATIONS as notification, index (notification.id)} {@const IconComponent = getNotificationIcon(notification.type)} {@const isNewNotification = newNotificationIds.includes(notification.id)} - + {#if index < $NOTIFICATIONS.length - 1} diff --git a/website/src/routes/notifications/NotificationItem.svelte b/website/src/routes/notifications/NotificationItem.svelte new file mode 100644 index 0000000..7424b6e --- /dev/null +++ b/website/src/routes/notifications/NotificationItem.svelte @@ -0,0 +1,40 @@ + + +{#if notification.link} + + + +{:else} +
+ +
+{/if} \ No newline at end of file