fix: center trick pile; add error boundary (surface post-buy crash)
- Trick area: smaller offsets (±50/52) + retuned scale so the played pile sits centered in the felt instead of flung out to the side seats. - ErrorBoundary around screens + overlays: a render error now shows a recoverable in-app message with the cause (and logs componentStack) instead of the browser's blank "page couldn't load" — helps pinpoint the post-purchase crash. Verified: tsc + next build clean; web rebuilt on :1500. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
+10
-7
@@ -21,6 +21,7 @@ import { NotificationToaster } from "@/components/online/NotificationToaster";
|
||||
import { ResumeGameBar } from "@/components/online/ResumeGameBar";
|
||||
import { CelebrationOverlay } from "@/components/online/CelebrationOverlay";
|
||||
import { MusicToggle } from "@/components/online/MusicToggle";
|
||||
import { ErrorBoundary } from "@/components/ErrorBoundary";
|
||||
import { PublicProfileModal } from "@/components/online/PublicProfileModal";
|
||||
import { CapacitorBack } from "@/components/CapacitorBack";
|
||||
import { useSessionStore } from "@/lib/session-store";
|
||||
@@ -193,13 +194,15 @@ export default function Page() {
|
||||
// reducedMotion="user" makes every Framer Motion animation honor the OS
|
||||
// "reduce motion" accessibility setting (coin rain, confetti, count-ups…).
|
||||
<MotionConfig reducedMotion="user">
|
||||
{renderScreen(screen)}
|
||||
<DailyRewardModal />
|
||||
<NotificationToaster />
|
||||
<ResumeGameBar />
|
||||
<CelebrationOverlay />
|
||||
<MusicToggle />
|
||||
<PublicProfileModal />
|
||||
<ErrorBoundary>
|
||||
{renderScreen(screen)}
|
||||
<DailyRewardModal />
|
||||
<NotificationToaster />
|
||||
<ResumeGameBar />
|
||||
<CelebrationOverlay />
|
||||
<MusicToggle />
|
||||
<PublicProfileModal />
|
||||
</ErrorBoundary>
|
||||
<CapacitorBack />
|
||||
{loading && null}
|
||||
</MotionConfig>
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
"use client";
|
||||
|
||||
import React from "react";
|
||||
|
||||
/**
|
||||
* Catches React render errors so a crash shows a recoverable in-app message
|
||||
* (with the cause) instead of the browser's blank "page couldn't load" screen.
|
||||
*/
|
||||
export class ErrorBoundary extends React.Component<
|
||||
{ children: React.ReactNode },
|
||||
{ error: Error | null }
|
||||
> {
|
||||
state: { error: Error | null } = { error: null };
|
||||
|
||||
static getDerivedStateFromError(error: Error) {
|
||||
return { error };
|
||||
}
|
||||
|
||||
componentDidCatch(error: Error, info: React.ErrorInfo) {
|
||||
// Surface the real cause in the console for diagnosis.
|
||||
console.error("[Hokm] render error:", error, info?.componentStack);
|
||||
}
|
||||
|
||||
render() {
|
||||
if (!this.state.error) return this.props.children;
|
||||
return (
|
||||
<div className="fixed inset-0 z-[100] grid place-items-center bg-navy-950 p-6 text-center">
|
||||
<div className="glass rounded-3xl p-6 w-full max-w-sm">
|
||||
<div className="text-5xl mb-2">⚠️</div>
|
||||
<h2 className="gold-text text-xl font-black">مشکلی پیش آمد</h2>
|
||||
<p className="text-cream/60 text-sm mt-1">برنامه به مشکل خورد؛ دوباره بارگذاری کنید.</p>
|
||||
<pre className="mt-3 max-h-32 overflow-auto rounded-lg bg-navy-900/70 p-2 text-start text-[10px] leading-relaxed text-rose-300/80 whitespace-pre-wrap break-words">
|
||||
{String(this.state.error?.message || this.state.error)}
|
||||
</pre>
|
||||
<button
|
||||
onClick={() => window.location.reload()}
|
||||
className="btn-gold rounded-xl px-5 py-2.5 mt-4 font-bold"
|
||||
>
|
||||
بارگذاری مجدد
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -65,7 +65,7 @@ export function GameTable({
|
||||
const exit = onExit ?? reset;
|
||||
const vw = useViewportWidth();
|
||||
// Pull the played-card pile inward on narrow screens so it clears the side stacks.
|
||||
const trickScale = vw < 360 ? 0.5 : vw < 460 ? 0.6 : 1;
|
||||
const trickScale = vw < 400 ? 0.82 : 1;
|
||||
// Smaller played cards on phones so the center pile stays clear of the side seats.
|
||||
const trickCardSize: "sm" | "md" = vw < 480 ? "sm" : "md";
|
||||
const { phase, players, hakem, trump, turn, currentTrick } = game;
|
||||
@@ -410,11 +410,13 @@ function OpponentHand({
|
||||
|
||||
/* ----------------------------- Trick area ----------------------------- */
|
||||
|
||||
// Compact, centered cross — small magnitudes keep the played pile in the middle of
|
||||
// the felt (clear of the side seats/stacks). Each card still nudges toward its player.
|
||||
const TRICK_OFFSET: Record<Seat, { x: number; y: number }> = {
|
||||
0: { x: 0, y: 70 },
|
||||
1: { x: 96, y: 0 },
|
||||
2: { x: 0, y: -70 },
|
||||
3: { x: -96, y: 0 },
|
||||
0: { x: 0, y: 52 },
|
||||
1: { x: 50, y: 0 },
|
||||
2: { x: 0, y: -52 },
|
||||
3: { x: -50, y: 0 },
|
||||
};
|
||||
const TRICK_ENTER: Record<Seat, { x: number; y: number }> = {
|
||||
0: { x: 0, y: 260 },
|
||||
|
||||
Reference in New Issue
Block a user