Fix remote likes, notification reads, and profile tabs
This commit is contained in:
@@ -1,9 +1,21 @@
|
||||
import { NextResponse } from 'next/server';
|
||||
import { db, likes, posts, users } from '@/db';
|
||||
import { db, likes, posts, users, userSwarmLikes } from '@/db';
|
||||
import { eq, desc, and, inArray } from 'drizzle-orm';
|
||||
|
||||
type RouteContext = { params: Promise<{ handle: string }> };
|
||||
|
||||
const parseMediaJson = (mediaJson: string | null) => {
|
||||
if (!mediaJson) {
|
||||
return [];
|
||||
}
|
||||
|
||||
try {
|
||||
return JSON.parse(mediaJson);
|
||||
} catch {
|
||||
return [];
|
||||
}
|
||||
};
|
||||
|
||||
export async function GET(request: Request, context: RouteContext) {
|
||||
try {
|
||||
const { handle } = await context.params;
|
||||
@@ -41,11 +53,51 @@ export async function GET(request: Request, context: RouteContext) {
|
||||
limit,
|
||||
});
|
||||
|
||||
// Filter out any likes where the post was removed and format response
|
||||
let likedPosts = userLikes
|
||||
const localLikedPosts = userLikes
|
||||
.filter(like => like.post && !like.post.isRemoved)
|
||||
.map(like => like.post);
|
||||
|
||||
const swarmLikedRows = await db.query.userSwarmLikes.findMany({
|
||||
where: eq(userSwarmLikes.userId, user.id),
|
||||
orderBy: [desc(userSwarmLikes.likedAt)],
|
||||
limit,
|
||||
});
|
||||
|
||||
const swarmLikedPosts = swarmLikedRows.map((like) => ({
|
||||
id: `swarm:${like.nodeDomain}:${like.originalPostId}`,
|
||||
originalPostId: like.originalPostId,
|
||||
content: like.content,
|
||||
createdAt: like.postCreatedAt.toISOString(),
|
||||
likesCount: like.likesCount,
|
||||
repostsCount: like.repostsCount,
|
||||
repliesCount: like.repliesCount,
|
||||
author: {
|
||||
id: `swarm:${like.nodeDomain}:${like.authorHandle}`,
|
||||
handle: `${like.authorHandle}@${like.nodeDomain}`,
|
||||
displayName: like.authorDisplayName || like.authorHandle,
|
||||
avatarUrl: like.authorAvatarUrl,
|
||||
},
|
||||
media: parseMediaJson(like.mediaJson),
|
||||
linkPreviewUrl: like.linkPreviewUrl,
|
||||
linkPreviewTitle: like.linkPreviewTitle,
|
||||
linkPreviewDescription: like.linkPreviewDescription,
|
||||
linkPreviewImage: like.linkPreviewImage,
|
||||
isSwarm: true,
|
||||
nodeDomain: like.nodeDomain,
|
||||
likedAt: like.likedAt.toISOString(),
|
||||
isLiked: false,
|
||||
}));
|
||||
|
||||
let likedPosts: any[] = [
|
||||
...localLikedPosts.map((post) => ({
|
||||
...post,
|
||||
likedAt: userLikes.find((like) => like.post?.id === post.id)?.createdAt?.toISOString() || post.createdAt.toISOString(),
|
||||
})),
|
||||
...swarmLikedPosts,
|
||||
]
|
||||
.sort((a, b) => new Date(b.likedAt).getTime() - new Date(a.likedAt).getTime())
|
||||
.slice(0, limit);
|
||||
|
||||
// Populate isLiked and isReposted for authenticated users
|
||||
try {
|
||||
const { getSession } = await import('@/lib/auth');
|
||||
@@ -53,13 +105,17 @@ export async function GET(request: Request, context: RouteContext) {
|
||||
|
||||
if (session?.user && likedPosts.length > 0) {
|
||||
const viewer = session.user;
|
||||
const postIds = likedPosts.map(p => p!.id).filter(Boolean);
|
||||
const isOwnLikesView = viewer.id === user.id;
|
||||
const localPostIds = likedPosts
|
||||
.filter((post: any) => !post.isSwarm)
|
||||
.map((post: any) => post.id)
|
||||
.filter(Boolean);
|
||||
|
||||
if (postIds.length > 0) {
|
||||
if (localPostIds.length > 0) {
|
||||
const viewerLikes = await db.query.likes.findMany({
|
||||
where: and(
|
||||
eq(likes.userId, viewer.id),
|
||||
inArray(likes.postId, postIds)
|
||||
inArray(likes.postId, localPostIds)
|
||||
),
|
||||
});
|
||||
const likedPostIds = new Set(viewerLikes.map(l => l.postId));
|
||||
@@ -67,7 +123,7 @@ export async function GET(request: Request, context: RouteContext) {
|
||||
const viewerReposts = await db.query.posts.findMany({
|
||||
where: and(
|
||||
eq(posts.userId, viewer.id),
|
||||
inArray(posts.repostOfId, postIds),
|
||||
inArray(posts.repostOfId, localPostIds),
|
||||
eq(posts.isRemoved, false)
|
||||
),
|
||||
});
|
||||
@@ -75,9 +131,14 @@ export async function GET(request: Request, context: RouteContext) {
|
||||
|
||||
likedPosts = likedPosts.map(p => ({
|
||||
...p!,
|
||||
isLiked: likedPostIds.has(p!.id),
|
||||
isLiked: p!.isSwarm ? isOwnLikesView : likedPostIds.has(p!.id),
|
||||
isReposted: repostedPostIds.has(p!.id),
|
||||
})) as any;
|
||||
} else {
|
||||
likedPosts = likedPosts.map(p => ({
|
||||
...p!,
|
||||
isLiked: p!.isSwarm ? isOwnLikesView : p!.isLiked,
|
||||
})) as any;
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import { NextResponse } from 'next/server';
|
||||
import { db, posts, users, likes } from '@/db';
|
||||
import { eq, desc, and, inArray, lt, sql } from 'drizzle-orm';
|
||||
import { eq, desc, and, inArray, lt, sql, isNull } from 'drizzle-orm';
|
||||
import { fetchSwarmUserProfile, isSwarmNode } from '@/lib/swarm/interactions';
|
||||
import { discoverNode } from '@/lib/swarm/discovery';
|
||||
import { getViewerSwarmLikedPostIds } from '@/lib/swarm/likes';
|
||||
|
||||
type RouteContext = { params: Promise<{ handle: string }> };
|
||||
|
||||
@@ -15,6 +16,43 @@ const parseRemoteHandle = (handle: string) => {
|
||||
return null;
|
||||
};
|
||||
|
||||
async function populateViewerLikeState(
|
||||
remotePosts: any[],
|
||||
domain: string
|
||||
) {
|
||||
if (!remotePosts.length) {
|
||||
return remotePosts;
|
||||
}
|
||||
|
||||
try {
|
||||
const { getSession } = await import('@/lib/auth');
|
||||
const session = await getSession();
|
||||
const viewer = session?.user;
|
||||
const nodeDomain = process.env.NEXT_PUBLIC_NODE_DOMAIN || 'localhost:3000';
|
||||
|
||||
if (!viewer) {
|
||||
return remotePosts;
|
||||
}
|
||||
|
||||
const likedIds = await getViewerSwarmLikedPostIds(
|
||||
remotePosts.map((post) => ({
|
||||
id: `swarm:${domain}:${post.originalPostId}`,
|
||||
nodeDomain: domain,
|
||||
originalPostId: post.originalPostId,
|
||||
})),
|
||||
viewer.handle,
|
||||
nodeDomain
|
||||
);
|
||||
|
||||
return remotePosts.map((post) => ({
|
||||
...post,
|
||||
isLiked: likedIds.has(`swarm:${domain}:${post.originalPostId}`),
|
||||
}));
|
||||
} catch {
|
||||
return remotePosts;
|
||||
}
|
||||
}
|
||||
|
||||
export async function GET(request: Request, context: RouteContext) {
|
||||
try {
|
||||
const { handle } = await context.params;
|
||||
@@ -54,6 +92,7 @@ export async function GET(request: Request, context: RouteContext) {
|
||||
|
||||
const remotePosts = profileData.posts.map((post: any) => ({
|
||||
id: post.id,
|
||||
originalPostId: post.id,
|
||||
content: post.content,
|
||||
createdAt: post.createdAt,
|
||||
likesCount: post.likesCount || 0,
|
||||
@@ -67,10 +106,9 @@ export async function GET(request: Request, context: RouteContext) {
|
||||
linkPreviewImage: post.linkPreviewImage || null,
|
||||
isSwarm: true,
|
||||
nodeDomain: remote.domain,
|
||||
originalPostId: post.id,
|
||||
}));
|
||||
|
||||
return NextResponse.json({ posts: remotePosts, nextCursor: null });
|
||||
return NextResponse.json({ posts: await populateViewerLikeState(remotePosts, remote.domain), nextCursor: null });
|
||||
}
|
||||
|
||||
return NextResponse.json({ posts: [] });
|
||||
@@ -132,6 +170,7 @@ export async function GET(request: Request, context: RouteContext) {
|
||||
|
||||
const remotePosts = profileData.posts.map((post: any) => ({
|
||||
id: post.id,
|
||||
originalPostId: post.id,
|
||||
content: post.content,
|
||||
createdAt: post.createdAt,
|
||||
likesCount: post.likesCount || 0,
|
||||
@@ -145,10 +184,9 @@ export async function GET(request: Request, context: RouteContext) {
|
||||
linkPreviewImage: post.linkPreviewImage || null,
|
||||
isSwarm: true,
|
||||
nodeDomain: remote.domain,
|
||||
originalPostId: post.id,
|
||||
}));
|
||||
|
||||
return NextResponse.json({ posts: remotePosts, nextCursor: null });
|
||||
return NextResponse.json({ posts: await populateViewerLikeState(remotePosts, remote.domain), nextCursor: null });
|
||||
}
|
||||
|
||||
return NextResponse.json({ posts: [] });
|
||||
@@ -159,7 +197,12 @@ export async function GET(request: Request, context: RouteContext) {
|
||||
}
|
||||
|
||||
// Get user's posts with cursor-based pagination
|
||||
let whereConditions = and(eq(posts.userId, user.id), eq(posts.isRemoved, false));
|
||||
let whereConditions = and(
|
||||
eq(posts.userId, user.id),
|
||||
eq(posts.isRemoved, false),
|
||||
isNull(posts.replyToId),
|
||||
isNull(posts.swarmReplyToId)
|
||||
);
|
||||
|
||||
// If cursor provided, get posts older than the cursor
|
||||
if (cursor) {
|
||||
@@ -170,6 +213,8 @@ export async function GET(request: Request, context: RouteContext) {
|
||||
whereConditions = and(
|
||||
eq(posts.userId, user.id),
|
||||
eq(posts.isRemoved, false),
|
||||
isNull(posts.replyToId),
|
||||
isNull(posts.swarmReplyToId),
|
||||
lt(posts.createdAt, cursorPost.createdAt)
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user