feat: user blocking and content reporting/flagging
- user_blocks table (composite PK), Block/Unblock button on profiles. Blocking severs any existing follow relationship both ways and prevents the blocked party from following, commenting on the blocker's recipes, or the blocker from seeing their comments. - reports table (recipe/comment/user targets, pending/reviewed/ dismissed status). ReportButton on comments, admin review queue at /admin/reports with dismiss/mark-reviewed actions, audit-logged. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -7,6 +7,7 @@ import {
|
||||
users,
|
||||
recipes,
|
||||
userFollows,
|
||||
userBlocks,
|
||||
eq,
|
||||
and,
|
||||
desc,
|
||||
@@ -15,6 +16,7 @@ import {
|
||||
import { Avatar, AvatarImage, AvatarFallback } from "@/components/ui/avatar";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { FollowButton } from "@/components/social/follow-button";
|
||||
import { BlockButton } from "@/components/social/block-button";
|
||||
import { getPublicUrl } from "@/lib/storage";
|
||||
|
||||
type Params = { params: Promise<{ username: string }> };
|
||||
@@ -61,15 +63,25 @@ export default async function UserProfilePage({ params }: Params) {
|
||||
const recipeCount = recipeCountRow[0]?.count ?? 0;
|
||||
|
||||
let isFollowing = false;
|
||||
let isBlocked = false;
|
||||
const isOwnProfile = session?.user.id === user.id;
|
||||
if (session && !isOwnProfile) {
|
||||
const followRow = await db.query.userFollows.findFirst({
|
||||
where: and(
|
||||
eq(userFollows.followerId, session.user.id),
|
||||
eq(userFollows.followingId, user.id),
|
||||
),
|
||||
});
|
||||
const [followRow, blockRow] = await Promise.all([
|
||||
db.query.userFollows.findFirst({
|
||||
where: and(
|
||||
eq(userFollows.followerId, session.user.id),
|
||||
eq(userFollows.followingId, user.id),
|
||||
),
|
||||
}),
|
||||
db.query.userBlocks.findFirst({
|
||||
where: and(
|
||||
eq(userBlocks.blockerId, session.user.id),
|
||||
eq(userBlocks.blockedId, user.id),
|
||||
),
|
||||
}),
|
||||
]);
|
||||
isFollowing = !!followRow;
|
||||
isBlocked = !!blockRow;
|
||||
}
|
||||
|
||||
const initials = user.name
|
||||
@@ -95,11 +107,14 @@ export default async function UserProfilePage({ params }: Params) {
|
||||
<p className="text-muted-foreground text-sm">@{user.username}</p>
|
||||
</div>
|
||||
{!isOwnProfile && session && (
|
||||
<FollowButton
|
||||
targetUserId={user.id}
|
||||
targetUsername={user.username!}
|
||||
initialFollowing={isFollowing}
|
||||
/>
|
||||
<div className="flex items-center gap-2">
|
||||
<FollowButton
|
||||
targetUserId={user.id}
|
||||
targetUsername={user.username!}
|
||||
initialFollowing={isFollowing}
|
||||
/>
|
||||
<BlockButton targetUsername={user.username!} initialBlocked={isBlocked} />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user