a tool for shared writing and social publishing
1# Notification System 2 3## Overview 4 5Notifications 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. 6 7## Key Files 8 9- **`src/notifications.ts`** - Core notification types and hydration logic 10- **`app/(home-pages)/notifications/NotificationList.tsx`** - Renders all notification types 11- **`app/(home-pages)/notifications/Notification.tsx`** - Base notification component 12- Individual notification components (e.g., `CommentNotification.tsx`, `FollowNotification.tsx`) 13 14## Adding a New Notification Type 15 16### 1. Update Notification Data Types (`src/notifications.ts`) 17 18Add your type to the `NotificationData` union: 19 20```typescript 21export type NotificationData = 22 | { type: "comment"; comment_uri: string; parent_uri?: string } 23 | { type: "subscribe"; subscription_uri: string } 24 | { type: "your_type"; your_field: string }; // Add here 25``` 26 27Add to the `HydratedNotification` union: 28 29```typescript 30export type HydratedNotification = 31 | HydratedCommentNotification 32 | HydratedSubscribeNotification 33 | HydratedYourNotification; // Add here 34``` 35 36### 2. Create Hydration Function (`src/notifications.ts`) 37 38```typescript 39export type HydratedYourNotification = Awaited< 40 ReturnType<typeof hydrateYourNotifications> 41>[0]; 42 43async function hydrateYourNotifications(notifications: NotificationRow[]) { 44 const yourNotifications = notifications.filter( 45 (n): n is NotificationRow & { data: ExtractNotificationType<"your_type"> } => 46 (n.data as NotificationData)?.type === "your_type", 47 ); 48 49 if (yourNotifications.length === 0) return []; 50 51 // Fetch related data with joins 52 const { data } = await supabaseServerClient 53 .from("your_table") 54 .select("*, related_table(*)") 55 .in("uri", yourNotifications.map((n) => n.data.your_field)); 56 57 return yourNotifications.map((notification) => ({ 58 id: notification.id, 59 recipient: notification.recipient, 60 created_at: notification.created_at, 61 type: "your_type" as const, 62 your_field: notification.data.your_field, 63 yourData: data?.find((d) => d.uri === notification.data.your_field)!, 64 })); 65} 66``` 67 68Add to `hydrateNotifications` parallel array: 69 70```typescript 71const [commentNotifications, subscribeNotifications, yourNotifications] = await Promise.all([ 72 hydrateCommentNotifications(notifications), 73 hydrateSubscribeNotifications(notifications), 74 hydrateYourNotifications(notifications), // Add here 75]); 76 77const allHydrated = [...commentNotifications, ...subscribeNotifications, ...yourNotifications]; 78``` 79 80### 3. Trigger the Notification (in your action file) 81 82```typescript 83import { Notification, pingIdentityToUpdateNotification } from "src/notifications"; 84import { v7 } from "uuid"; 85 86// When the event occurs: 87const recipient = /* determine who should receive it */; 88if (recipient !== currentUser) { 89 const notification: Notification = { 90 id: v7(), 91 recipient, 92 data: { 93 type: "your_type", 94 your_field: "value", 95 }, 96 }; 97 await supabaseServerClient.from("notifications").insert(notification); 98 await pingIdentityToUpdateNotification(recipient); 99} 100``` 101 102### 4. Create Notification Component 103 104Create a new component (e.g., `YourNotification.tsx`): 105 106```typescript 107import { HydratedYourNotification } from "src/notifications"; 108import { Notification } from "./Notification"; 109 110export const YourNotification = (props: HydratedYourNotification) => { 111 // Extract data from props.yourData 112 113 return ( 114 <Notification 115 timestamp={props.created_at} 116 href={/* link to relevant page */} 117 icon={/* icon or avatar */} 118 actionText={<>Message to display</>} 119 content={/* optional additional content */} 120 /> 121 ); 122}; 123``` 124 125### 5. Update NotificationList (`NotificationList.tsx`) 126 127Import and render your notification type: 128 129```typescript 130import { YourNotification } from "./YourNotification"; 131 132// In the map function: 133if (n.type === "your_type") { 134 return <YourNotification key={n.id} {...n} />; 135} 136``` 137 138## Example: Subscribe Notifications 139 140See the implementation in: 141- `src/notifications.ts:88-125` - Hydration logic 142- `app/lish/subscribeToPublication.ts:55-68` - Trigger 143- `app/(home-pages)/notifications/FollowNotification.tsx` - Component 144- `app/(home-pages)/notifications/NotificationList.tsx:40-42` - Rendering