feat(community): Post.gameName + GameShareBanner-rendering chain

Adds optional `gameName` column to community_posts so game-share posts
can render with the game-banner above the post-content (Snake/Tetris/
Memory/TTT visual indicator).

- prisma/schema.prisma: CommunityPost.gameName String? @map("game_name")
- migration: ALTER TABLE rebreak.community_posts ADD COLUMN game_name
- db/community.ts: createPost() accepts gameName param
- api/community/post.post.ts: extracts gameName from body
- api/community/posts.get.ts: returns gameName, prefers DB over content-parse

Frontend (already in flight on upgrade/sdk-54): PostCard.tsx renders
GameShareBanner when post.category === 'game_share' && post.gameName.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
chahinebrini 2026-05-09 22:28:07 +02:00
parent fd737f8658
commit a81ba2e54a
5 changed files with 11 additions and 4 deletions

View File

@ -0,0 +1,3 @@
-- Migration: add game_name column to community_posts
-- Generated: 2026-05-09
ALTER TABLE "rebreak"."community_posts" ADD COLUMN "game_name" TEXT;

View File

@ -160,6 +160,7 @@ model CommunityPost {
deletedAt DateTime? @map("deleted_at") deletedAt DateTime? @map("deleted_at")
/// Wann der Post zum ersten Mal gemeldet wurde (queue-Sortierung). /// Wann der Post zum ersten Mal gemeldet wurde (queue-Sortierung).
reportedAt DateTime? @map("reported_at") reportedAt DateTime? @map("reported_at")
gameName String? @map("game_name")
repostOfId String? @map("repost_of_id") @db.Uuid repostOfId String? @map("repost_of_id") @db.Uuid
challengeId String? @map("challenge_id") @db.Uuid challengeId String? @map("challenge_id") @db.Uuid
createdAt DateTime @default(now()) @map("created_at") createdAt DateTime @default(now()) @map("created_at")

View File

@ -14,10 +14,11 @@ export default defineEventHandler(async (event) => {
const user = await requireUser(event); const user = await requireUser(event);
const body = await readBody(event); const body = await readBody(event);
const { category, content, imageUrl } = body as { const { category, content, imageUrl, gameName } = body as {
category: string; category: string;
content: string; content: string;
imageUrl?: string; imageUrl?: string;
gameName?: string | null;
}; };
if (!content?.trim() || !category) { if (!content?.trim() || !category) {
@ -72,7 +73,7 @@ export default defineEventHandler(async (event) => {
} }
} }
const data = await createPost(user.id, category, content.trim(), imageUrl); const data = await createPost(user.id, category, content.trim(), imageUrl, gameName ?? null);
// Punkte vergeben // Punkte vergeben
await awardPoints(user.id, "post_created", { post_id: data.id }); await awardPoints(user.id, "post_created", { post_id: data.id });

View File

@ -64,9 +64,9 @@ export default defineEventHandler(async (event) => {
: null, : null,
gameName: (p as any).challengeId gameName: (p as any).challengeId
? challengeStatuses[(p as any).challengeId]?.gameType ?? null ? challengeStatuses[(p as any).challengeId]?.gameType ?? null
: p.category === "game_share" : (p as any).gameName ?? (p.category === "game_share"
? p.content.split("\n")[0] ?? null ? p.content.split("\n")[0] ?? null
: null, : null),
opponentName: (p as any).challengeId opponentName: (p as any).challengeId
? challengeStatuses[(p as any).challengeId]?.opponentName ?? null ? challengeStatuses[(p as any).challengeId]?.opponentName ?? null
: null, : null,

View File

@ -256,6 +256,7 @@ export async function createPost(
category: string, category: string,
content: string, content: string,
imageUrl?: string, imageUrl?: string,
gameName?: string | null,
) { ) {
const db = usePrisma(); const db = usePrisma();
return db.communityPost.create({ return db.communityPost.create({
@ -264,6 +265,7 @@ export async function createPost(
category, category,
content, content,
imageUrl: imageUrl || null, imageUrl: imageUrl || null,
gameName: gameName ?? null,
isAnonymous: false, isAnonymous: false,
isModerated: false, isModerated: false,
}, },