feat: photo upload at level 3 + report a player (nudity avatar / chat insult)
Photo upload:
- Lower the custom profile-photo gate from level 25 to level 3 (client const +
i18n hint + server gate in ProfileService.Update). The level-25 "Expert" title
is unrelated and unchanged.
Report a player:
- New ReportReason type + service.reportUser(targetId, reason, details?).
- Report entry points: a "گزارش تخلف" button + reason picker (nudity / insult /
other) in the public-profile modal, and a flag button in the chat header
(reports the peer for an insulting chat) with a confirmation toast.
- Mock records reports to localStorage; SignalR POSTs /api/report.
- Server: POST /api/report → ProfileService.ReportUser stores the report in the
write-only ledger (kind="report", ref="{targetId}|{reason}|{details}") so no
schema change is needed (server uses EnsureCreated, not migrations).
- i18n: report.* keys (fa + en).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -1,12 +1,14 @@
|
||||
"use client";
|
||||
|
||||
import { ChevronLeft, ChevronRight, MessageCircle, Send, Smile } from "lucide-react";
|
||||
import { ChevronLeft, ChevronRight, Flag, MessageCircle, Send, Smile } from "lucide-react";
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import { useOnlineStore } from "@/lib/online-store";
|
||||
import { useSessionStore } from "@/lib/session-store";
|
||||
import { useUIStore } from "@/lib/ui-store";
|
||||
import { useI18n } from "@/lib/i18n";
|
||||
import { sound } from "@/lib/sound";
|
||||
import { getService } from "@/lib/online/service";
|
||||
import { pushNotification } from "@/lib/notification-store";
|
||||
import { ownedReactions } from "@/lib/online/gamification";
|
||||
import { avatarEmoji } from "@/lib/online/types";
|
||||
import { cn } from "@/lib/cn";
|
||||
@@ -23,6 +25,7 @@ export function ChatScreen() {
|
||||
const viewProfile = useUIStore((s) => s.viewProfile);
|
||||
const [text, setText] = useState("");
|
||||
const [showEmoji, setShowEmoji] = useState(false);
|
||||
const [reported, setReported] = useState(false);
|
||||
const emojis = profile ? ownedReactions(profile) : [];
|
||||
const endRef = useRef<HTMLDivElement>(null);
|
||||
const prevLen = useRef(0);
|
||||
@@ -58,6 +61,20 @@ export function ChatScreen() {
|
||||
await sendChat(e);
|
||||
};
|
||||
|
||||
const reportFriend = async () => {
|
||||
if (reported || !friend) return;
|
||||
setReported(true);
|
||||
await getService().reportUser(friend.id, "insult");
|
||||
pushNotification({
|
||||
kind: "system",
|
||||
titleFa: "گزارش ثبت شد",
|
||||
titleEn: "Report submitted",
|
||||
bodyFa: "از کمک شما برای حفظ محیط سالم ممنونیم.",
|
||||
bodyEn: "Thanks for helping keep the game friendly.",
|
||||
icon: "🚩",
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<main className="persian-pattern relative h-dvh w-full flex justify-center">
|
||||
<div className="w-full max-w-3xl flex flex-col h-full">
|
||||
@@ -86,6 +103,20 @@ export function ChatScreen() {
|
||||
</div>
|
||||
</div>
|
||||
</button>
|
||||
|
||||
{/* report this player for insulting chat */}
|
||||
<button
|
||||
onClick={reportFriend}
|
||||
disabled={reported}
|
||||
title={t("report.insult")}
|
||||
aria-label={t("report.button")}
|
||||
className={cn(
|
||||
"tap grid place-items-center rounded-full shrink-0 transition",
|
||||
reported ? "text-teal-300" : "text-cream/40 hover:text-rose-300 hover:bg-navy-800/80"
|
||||
)}
|
||||
>
|
||||
<Flag className="size-4" />
|
||||
</button>
|
||||
</header>
|
||||
|
||||
{/* messages */}
|
||||
|
||||
@@ -28,7 +28,7 @@ import { pushNotification } from "@/lib/notification-store";
|
||||
import { sound } from "@/lib/sound";
|
||||
|
||||
/** Level required before a player can upload a custom profile photo. */
|
||||
const PHOTO_UPLOAD_MIN_LEVEL = 25;
|
||||
const PHOTO_UPLOAD_MIN_LEVEL = 3;
|
||||
import { AVATARS, Gender, SocialVisibility } from "@/lib/online/types";
|
||||
import { GENDER_META, SOCIAL_PLATFORMS } from "@/lib/social";
|
||||
import { backVisualFromDef, cardBackMotif } from "@/lib/cardBack";
|
||||
|
||||
Reference in New Issue
Block a user