diff --git a/apps/web/app/(org)/dashboard/_components/Notifications/NotificationItem.tsx b/apps/web/app/(org)/dashboard/_components/Notifications/NotificationItem.tsx index 2eddf25dea..90ad061e96 100644 --- a/apps/web/app/(org)/dashboard/_components/Notifications/NotificationItem.tsx +++ b/apps/web/app/(org)/dashboard/_components/Notifications/NotificationItem.tsx @@ -1,10 +1,12 @@ import type { Notification as APINotification } from "@cap/web-api-contract"; +import type { ImageUpload } from "@cap/web-domain"; import { faComment, faEye, faReply } from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import clsx from "clsx"; import moment from "moment"; import Link from "next/link"; import { markAsRead } from "@/actions/notifications/mark-as-read"; +import { SignedImageUrl } from "@/components/SignedImageUrl"; import type { NotificationType } from "@/lib/Notification"; type NotificationItemProps = { @@ -45,17 +47,12 @@ export const NotificationItem = ({ > {/* Avatar */}
- {notification.author.avatar ? ( - {notification.author.name} - ) : ( -
- {notification.author.name.charAt(0)} -
- )} + {notification.readAt === null && (
)} diff --git a/apps/web/app/api/notifications/route.ts b/apps/web/app/api/notifications/route.ts index 0ea8748bee..f27ee584d0 100644 --- a/apps/web/app/api/notifications/route.ts +++ b/apps/web/app/api/notifications/route.ts @@ -2,12 +2,13 @@ import { db } from "@cap/database"; import { getCurrentUser } from "@cap/database/auth/session"; import { notifications, users } from "@cap/database/schema"; import { Notification as APINotification } from "@cap/web-api-contract"; -import { and, ColumnBaseConfig, desc, eq, isNull, sql } from "drizzle-orm"; -import { MySqlColumn } from "drizzle-orm/mysql-core"; +import { ImageUploads } from "@cap/web-backend"; +import { and, desc, eq, isNull, sql } from "drizzle-orm"; +import { Effect } from "effect"; import { NextResponse } from "next/server"; -import { AvcProfileInfo } from "node_modules/@remotion/media-parser/dist/containers/avc/parse-avc"; import { z } from "zod"; import type { NotificationType } from "@/lib/Notification"; +import { runPromise } from "@/lib/server"; import { jsonExtractString } from "@/utils/sql"; const notificationDataSchema = z.object({ @@ -90,32 +91,46 @@ export async function GET() { formattedCountResults[type] = Number(count); }); - const formattedNotifications = notificationsWithAuthors - .map(({ notification, author }) => { - try { - // all notifications currently require an author - if (!author) return; + const formattedNotifications = await Effect.gen(function* () { + const imageUploads = yield* ImageUploads; - return APINotification.parse({ - id: notification.id, - type: notification.type, - readAt: notification.readAt, - videoId: notification.data.videoId, - createdAt: notification.createdAt, - data: notification.data, - comment: notification.data.comment, - author: { - id: author.id, - name: author.name ?? "Unknown", - avatar: author.avatar, - }, - }); - } catch (error) { - console.error("Invalid notification data:", error); - return null; - } - }) - .filter(Boolean); + return yield* Effect.all( + notificationsWithAuthors.map(({ notification, author }) => + Effect.gen(function* () { + // all notifications currently require an author + if (!author) return null; + + const resolvedAvatar = author.avatar + ? yield* imageUploads + .resolveImageUrl(author.avatar) + .pipe(Effect.catchAll(() => Effect.succeed(null))) + : null; + + return APINotification.parse({ + id: notification.id, + type: notification.type, + readAt: notification.readAt, + videoId: notification.data.videoId, + createdAt: notification.createdAt, + data: notification.data, + comment: notification.data.comment, + author: { + id: author.id, + name: author.name ?? "Unknown", + avatar: resolvedAvatar, + }, + }); + }).pipe( + Effect.catchAll((error) => { + console.error("Invalid notification data:", error); + return Effect.succeed(null); + }), + ), + ), + ); + }) + .pipe(runPromise) + .then((results) => results.filter(Boolean)); return NextResponse.json({ notifications: formattedNotifications,