Show the liked post in notifications with text or media preview

Hop-State: A_06FPB5RGJ37K5NPB79MT66G
Hop-Proposal: R_06FPB5QJB3W279N6JSGB8R0
Hop-Task: T_06FPB4PW583MAWM13VA1VT8
Hop-Attempt: AT_06FPB4PW5A561YFQGJBWW1G
This commit is contained in:
2026-07-15 04:43:46 -07:00
committed by Hop
parent 93be1cffd8
commit c1f3afa103
4 changed files with 144 additions and 9 deletions
+17 -3
View File
@@ -1,7 +1,7 @@
import { NextResponse } from 'next/server';
import { db, notifications } from '@/db';
import { requireAuth } from '@/lib/auth';
import { and, desc, eq, inArray, isNull } from 'drizzle-orm';
import { and, eq, inArray } from 'drizzle-orm';
import { z } from 'zod';
const markSchema = z.object({
@@ -52,6 +52,14 @@ export async function GET(request: Request) {
},
orderBy: (notifications, { desc }) => [desc(notifications.createdAt)],
limit,
with: {
post: {
with: {
author: true,
media: true,
},
},
},
});
// For remote actors missing avatar, fetch fresh data
@@ -108,8 +116,14 @@ export async function GET(request: Request) {
} : null,
post: row.postId ? {
id: row.postId,
content: row.postContent,
authorHandle: row.actorHandle, // The actor is the post author for likes/reposts
content: row.post?.content || row.postContent,
authorHandle: row.post?.author?.handle || null,
media: row.post?.media.map((item) => ({
url: item.url,
mimeType: item.mimeType,
altText: item.altText,
})) || [],
linkPreviewImage: row.post?.linkPreviewImage || null,
} : null,
};
});
+39 -6
View File
@@ -6,6 +6,7 @@ import Link from 'next/link';
import { useAuth } from '@/lib/contexts/AuthContext';
import { getProfilePath } from '@/lib/utils/handle';
import { AvatarImage } from '@/components/AvatarImage';
import { getNotificationPostPreview } from '@/lib/notifications/post-preview';
interface NotificationActor {
id: string;
@@ -24,7 +25,14 @@ interface NotificationTarget {
interface NotificationPost {
id: string;
content: string;
content: string | null;
authorHandle: string | null;
media: Array<{
url: string;
mimeType: string | null;
altText: string | null;
}>;
linkPreviewImage: string | null;
}
interface Notification {
@@ -206,6 +214,9 @@ function NotificationItem({
const isUnread = !notification.readAt;
const actor = notification.actor;
const actorProfilePath = actor ? getProfilePath(actor.handle) : '#';
const postPreview = notification.post
? getNotificationPostPreview(notification.post)
: null;
return (
<div
@@ -244,20 +255,42 @@ function NotificationItem({
<Link
href={`/posts/${notification.post.id}`}
style={{
display: 'block',
display: 'flex',
alignItems: 'center',
gap: '10px',
marginTop: '8px',
padding: '8px 12px',
padding: '8px',
background: 'var(--background-secondary)',
borderRadius: '8px',
color: 'var(--foreground-secondary)',
fontSize: '14px',
textDecoration: 'none',
overflow: 'hidden',
}}
aria-label={`View liked post: ${postPreview?.label || 'View post'}`}
>
{postPreview?.imageUrl && (
// eslint-disable-next-line @next/next/no-img-element
<img
src={postPreview.imageUrl}
alt={postPreview.imageAlt}
style={{
width: 52,
height: 52,
borderRadius: '6px',
objectFit: 'cover',
flexShrink: 0,
}}
/>
)}
<span style={{
minWidth: 0,
overflow: 'hidden',
textOverflow: 'ellipsis',
whiteSpace: 'nowrap',
}}
>
{notification.post.content}
}}>
{postPreview?.label || 'View post'}
</span>
</Link>
)}
</div>