| name | notifications |
| description | Reference for the notification system. Use when adding new notification types or modifying notification handling. |
| user-invocable | false |
Notification System
Overview
Notifications are stored in the database and hydrated with related data before being rendered. The system supports multiple notification types (comments, subscriptions, etc.) that are processed in parallel.
Key Files
src/notifications.ts - Core notification types and hydration logic
app/(home-pages)/notifications/NotificationList.tsx - Renders all notification types
app/(home-pages)/notifications/Notification.tsx - Base notification component
- Individual notification components (e.g.,
CommentNotification.tsx, FollowNotification.tsx)
Adding a New Notification Type
1. Update Notification Data Types (src/notifications.ts)
Add your type to the NotificationData union:
export type NotificationData =
| { type: "comment"; comment_uri: string; parent_uri?: string }
| { type: "subscribe"; subscription_uri: string }
| { type: "your_type"; your_field: string };
Add to the HydratedNotification union:
export type HydratedNotification =
| HydratedCommentNotification
| HydratedSubscribeNotification
| HydratedYourNotification;
2. Create Hydration Function (src/notifications.ts)
export type HydratedYourNotification = Awaited<
ReturnType<typeof hydrateYourNotifications>
>[0];
async function hydrateYourNotifications(notifications: NotificationRow[]) {
const yourNotifications = notifications.filter(
(n): n is NotificationRow & { data: ExtractNotificationType<"your_type"> } =>
(n.data as NotificationData)?.type === "your_type",
);
if (yourNotifications.length === 0) return [];
const { data } = await supabaseServerClient
.from("your_table")
.select("*, related_table(*)")
.in("uri", yourNotifications.map((n) => n.data.your_field));
return yourNotifications.map((notification) => ({
id: notification.id,
recipient: notification.recipient,
created_at: notification.created_at,
type: "your_type" as const,
your_field: notification.data.your_field,
yourData: data?.find((d) => d.uri === notification.data.your_field)!,
}));
}
Add to hydrateNotifications parallel array:
const [commentNotifications, subscribeNotifications, yourNotifications] = await Promise.all([
hydrateCommentNotifications(notifications),
hydrateSubscribeNotifications(notifications),
hydrateYourNotifications(notifications),
]);
const allHydrated = [...commentNotifications, ...subscribeNotifications, ...yourNotifications];
3. Trigger the Notification (in your action file)
import { Notification, pingIdentityToUpdateNotification } from "src/notifications";
import { v7 } from "uuid";
const recipient = ;
if (recipient !== currentUser) {
const notification: Notification = {
id: v7(),
recipient,
data: {
type: "your_type",
your_field: "value",
},
};
await supabaseServerClient.from("notifications").insert(notification);
await pingIdentityToUpdateNotification(recipient);
}
4. Create Notification Component
Create a new component (e.g., YourNotification.tsx):
import { HydratedYourNotification } from "src/notifications";
import { Notification } from "./Notification";
export const YourNotification = (props: HydratedYourNotification) => {
return (
<Notification
timestamp={props.created_at}
href={/* link to relevant page */}
icon={/* icon or avatar */}
actionText={<>Message to display</>}
content={}
/>
);
};
5. Update NotificationList (NotificationList.tsx)
Import and render your notification type:
import { YourNotification } from "./YourNotification";
if (n.type === "your_type") {
return <YourNotification key={n.id} {...n} />;
}
Example: Subscribe Notifications
See the implementation in:
src/notifications.ts:88-125 - Hydration logic
app/lish/subscribeToPublication.ts:55-68 - Trigger
app/(home-pages)/notifications/FollowNotification.tsx - Component
app/(home-pages)/notifications/NotificationList.tsx:40-42 - Rendering