Newer
Older
PixelPaintWar / apps / client / src / scenes / result / ResultScene.tsx
/**
 * ResultScene
 * リザルト画面の表示状態を管理するコンテナコンポーネント
 * 背景演出と順位表の切り替え表示を制御する
 */
import type { GameResultPayload } from "@repo/shared";
import { useEffect, useState } from "react";
import { config } from "../../config";
import { ResultActionBar } from "./components/ResultActionBar";
import { ResultBackground } from "./components/ResultBackground";
import { ResultPlayerRankingTable } from "./components/ResultPlayerRankingTable";
import { ResultRankingTable } from "./components/ResultRankingTable";
import { ResultTabBar } from "./components/ResultTabBar";
import {
  RESULT_BACKGROUND_DARK_OVERLAY_STYLE,
  RESULT_CONTENT_FADE_STYLE,
  RESULT_CONTENT_STYLE,
  RESULT_KEYFRAMES_CSS,
  RESULT_ROOT_STYLE,
  RESULT_TAP_GUIDE_STYLE,
  RESULT_TITLE_STYLE,
  getResultTitleTextStyle,
} from "./styles/resultStyles";
import type { ResultTabMode } from "./types/resultTabMode";
import type { ResultViewMode } from "./types/resultViewMode";

type Props = {
  result: GameResultPayload | null;
  onBackToTitle: () => void;
};

const formatPaintRate = (value: number): string => `${value.toFixed(1)}%`;

/** 最終結果データを受け取り,順位一覧を表示する */
export const ResultScene = ({ result, onBackToTitle }: Props) => {
  const [viewMode, setViewMode] = useState<ResultViewMode>("mapPreview");
  const [activeTab, setActiveTab] = useState<ResultTabMode>("teamRanking");
  const isRankingVisible = viewMode === "ranking";

  useEffect(() => {
    setViewMode("mapPreview");
    setActiveTab("teamRanking");
  }, [result]);

  if (!result) {
    return (
      <div style={{ color: "white", padding: 40 }}>結果を読み込み中...</div>
    );
  }

  const winnerTeamId =
    result.rankings.find((row) => row.rank === 1)?.teamId ??
    result.rankings[0]?.teamId;
  const winnerColor =
    config.GAME_CONFIG.TEAM_COLORS[winnerTeamId ?? -1] ?? "#888888";
  const gridCols = config.GAME_CONFIG.GRID_COLS;
  const gridRows = config.GAME_CONFIG.GRID_ROWS;
  const totalCells = gridCols * gridRows;
  const finalGridColors = Array.from({ length: totalCells }, (_, index) => {
    const teamId = result.finalGridColors?.[index];
    return typeof teamId === "number" ? teamId : -1;
  });

  return (
    <div
      style={{
        ...RESULT_ROOT_STYLE,
        cursor: isRankingVisible ? "default" : "pointer",
      }}
      onClick={() => {
        if (isRankingVisible) {
          return;
        }

        setViewMode("ranking");
      }}
    >
      <style>{RESULT_KEYFRAMES_CSS}</style>
      <ResultBackground
        gridCols={gridCols}
        gridRows={gridRows}
        finalGridColors={finalGridColors}
        winnerColor={winnerColor}
      />
      <div style={RESULT_BACKGROUND_DARK_OVERLAY_STYLE} />

      <div style={RESULT_CONTENT_STYLE}>
        {isRankingVisible && (
          <ResultActionBar
            onBackToTitle={onBackToTitle}
            onShowMapPreview={() => setViewMode("mapPreview")}
          />
        )}

        <h2 style={RESULT_TITLE_STYLE}>
          <span style={getResultTitleTextStyle(winnerColor)}>結果発表</span>
        </h2>

        {!isRankingVisible && (
          <div style={RESULT_TAP_GUIDE_STYLE}>Tap To Result</div>
        )}

        {isRankingVisible && (
          <ResultTabBar activeTab={activeTab} onTabChange={setActiveTab} />
        )}

        {isRankingVisible && activeTab === "teamRanking" && (
          <div style={RESULT_CONTENT_FADE_STYLE}>
            <ResultRankingTable
              rankings={result.rankings}
              formatPaintRate={formatPaintRate}
            />
          </div>
        )}

        {isRankingVisible &&
          activeTab === "paintCount" &&
          result.playerStats &&
          result.playerStats.length > 0 && (
            <div style={RESULT_CONTENT_FADE_STYLE}>
              <ResultPlayerRankingTable
                playerStats={result.playerStats}
                sortKey="paintCount"
                valueLabel="塗り回数"
              />
            </div>
          )}

        {isRankingVisible &&
          activeTab === "bombHits" &&
          result.playerStats &&
          result.playerStats.length > 0 && (
            <div style={RESULT_CONTENT_FADE_STYLE}>
              <ResultPlayerRankingTable
                playerStats={result.playerStats}
                sortKey="bombHitCount"
                valueLabel="ヒット数"
              />
            </div>
          )}
      </div>
    </div>
  );
};