Felix Zieger
commited on
Commit
路
444233f
1
Parent(s):
5835ecd
session
Browse files
src/components/GameContainer.tsx
CHANGED
@@ -11,6 +11,7 @@ import { GuessDisplay } from "./game/GuessDisplay";
|
|
11 |
import { GameOver } from "./game/GameOver";
|
12 |
import { useTranslation } from "@/hooks/useTranslation";
|
13 |
import { LanguageContext } from "@/contexts/LanguageContext";
|
|
|
14 |
|
15 |
type GameState = "welcome" | "theme-selection" | "building-sentence" | "showing-guess" | "game-over";
|
16 |
|
@@ -25,10 +26,18 @@ export const GameContainer = () => {
|
|
25 |
const [successfulRounds, setSuccessfulRounds] = useState<number>(0);
|
26 |
const [totalWords, setTotalWords] = useState<number>(0);
|
27 |
const [usedWords, setUsedWords] = useState<string[]>([]);
|
|
|
28 |
const { toast } = useToast();
|
29 |
const t = useTranslation();
|
30 |
const { language } = useContext(LanguageContext);
|
31 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
32 |
useEffect(() => {
|
33 |
const handleKeyPress = (e: KeyboardEvent) => {
|
34 |
if (e.key === 'Enter') {
|
@@ -62,6 +71,7 @@ export const GameContainer = () => {
|
|
62 |
setSuccessfulRounds(0);
|
63 |
setTotalWords(0);
|
64 |
setUsedWords([]);
|
|
|
65 |
};
|
66 |
|
67 |
const handleThemeSelect = async (theme: string) => {
|
@@ -130,6 +140,32 @@ export const GameContainer = () => {
|
|
130 |
const sentenceString = finalSentence.join(' ');
|
131 |
const guess = await guessWord(sentenceString, language);
|
132 |
setAiGuess(guess);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
133 |
setGameState("showing-guess");
|
134 |
} catch (error) {
|
135 |
console.error('Error getting AI guess:', error);
|
@@ -231,6 +267,7 @@ export const GameContainer = () => {
|
|
231 |
onPlayAgain={handlePlayAgain}
|
232 |
currentScore={successfulRounds}
|
233 |
avgWordsPerRound={getAverageWordsPerRound()}
|
|
|
234 |
/>
|
235 |
) : gameState === "game-over" ? (
|
236 |
<GameOver
|
|
|
11 |
import { GameOver } from "./game/GameOver";
|
12 |
import { useTranslation } from "@/hooks/useTranslation";
|
13 |
import { LanguageContext } from "@/contexts/LanguageContext";
|
14 |
+
import { supabase } from "@/integrations/supabase/client";
|
15 |
|
16 |
type GameState = "welcome" | "theme-selection" | "building-sentence" | "showing-guess" | "game-over";
|
17 |
|
|
|
26 |
const [successfulRounds, setSuccessfulRounds] = useState<number>(0);
|
27 |
const [totalWords, setTotalWords] = useState<number>(0);
|
28 |
const [usedWords, setUsedWords] = useState<string[]>([]);
|
29 |
+
const [sessionId, setSessionId] = useState<string>("");
|
30 |
const { toast } = useToast();
|
31 |
const t = useTranslation();
|
32 |
const { language } = useContext(LanguageContext);
|
33 |
|
34 |
+
useEffect(() => {
|
35 |
+
// Generate a new session ID when starting a new game
|
36 |
+
if (gameState === "theme-selection") {
|
37 |
+
setSessionId(crypto.randomUUID());
|
38 |
+
}
|
39 |
+
}, [gameState]);
|
40 |
+
|
41 |
useEffect(() => {
|
42 |
const handleKeyPress = (e: KeyboardEvent) => {
|
43 |
if (e.key === 'Enter') {
|
|
|
71 |
setSuccessfulRounds(0);
|
72 |
setTotalWords(0);
|
73 |
setUsedWords([]);
|
74 |
+
setSessionId("");
|
75 |
};
|
76 |
|
77 |
const handleThemeSelect = async (theme: string) => {
|
|
|
140 |
const sentenceString = finalSentence.join(' ');
|
141 |
const guess = await guessWord(sentenceString, language);
|
142 |
setAiGuess(guess);
|
143 |
+
|
144 |
+
// Save game result with session ID
|
145 |
+
try {
|
146 |
+
const { error } = await supabase
|
147 |
+
.from('game_results')
|
148 |
+
.insert({
|
149 |
+
target_word: currentWord,
|
150 |
+
description: sentenceString,
|
151 |
+
ai_guess: guess,
|
152 |
+
is_correct: guess.toLowerCase() === currentWord.toLowerCase(),
|
153 |
+
session_id: sessionId
|
154 |
+
});
|
155 |
+
|
156 |
+
if (error) {
|
157 |
+
console.error('Error saving game result:', error);
|
158 |
+
throw error;
|
159 |
+
}
|
160 |
+
} catch (error) {
|
161 |
+
console.error('Error in database operation:', error);
|
162 |
+
toast({
|
163 |
+
title: "Error",
|
164 |
+
description: "Failed to save game result. Please try again.",
|
165 |
+
variant: "destructive",
|
166 |
+
});
|
167 |
+
}
|
168 |
+
|
169 |
setGameState("showing-guess");
|
170 |
} catch (error) {
|
171 |
console.error('Error getting AI guess:', error);
|
|
|
267 |
onPlayAgain={handlePlayAgain}
|
268 |
currentScore={successfulRounds}
|
269 |
avgWordsPerRound={getAverageWordsPerRound()}
|
270 |
+
sessionId={sessionId}
|
271 |
/>
|
272 |
) : gameState === "game-over" ? (
|
273 |
<GameOver
|
src/components/HighScoreBoard.tsx
CHANGED
@@ -28,6 +28,7 @@ interface HighScore {
|
|
28 |
score: number;
|
29 |
avg_words_per_round: number;
|
30 |
created_at: string;
|
|
|
31 |
}
|
32 |
|
33 |
interface HighScoreBoardProps {
|
@@ -35,6 +36,7 @@ interface HighScoreBoardProps {
|
|
35 |
avgWordsPerRound: number;
|
36 |
onClose: () => void;
|
37 |
onPlayAgain: () => void;
|
|
|
38 |
}
|
39 |
|
40 |
const ITEMS_PER_PAGE = 5;
|
@@ -57,6 +59,7 @@ export const HighScoreBoard = ({
|
|
57 |
currentScore,
|
58 |
avgWordsPerRound,
|
59 |
onClose,
|
|
|
60 |
}: HighScoreBoardProps) => {
|
61 |
const [playerName, setPlayerName] = useState("");
|
62 |
const [isSubmitting, setIsSubmitting] = useState(false);
|
@@ -86,7 +89,6 @@ export const HighScoreBoard = ({
|
|
86 |
});
|
87 |
|
88 |
const handleSubmitScore = async () => {
|
89 |
-
// Updated regex to allow letters with diacritics and special characters
|
90 |
if (!playerName.trim() || !/^[\p{L}0-9]+$/u.test(playerName.trim())) {
|
91 |
toast({
|
92 |
title: t.leaderboard.error.invalidName,
|
@@ -138,6 +140,7 @@ export const HighScoreBoard = ({
|
|
138 |
.update({
|
139 |
score: currentScore,
|
140 |
avg_words_per_round: avgWordsPerRound,
|
|
|
141 |
})
|
142 |
.eq("id", existingScore.id);
|
143 |
|
@@ -172,6 +175,7 @@ export const HighScoreBoard = ({
|
|
172 |
player_name: playerName.trim(),
|
173 |
score: currentScore,
|
174 |
avg_words_per_round: avgWordsPerRound,
|
|
|
175 |
});
|
176 |
|
177 |
if (insertError) {
|
@@ -250,7 +254,6 @@ export const HighScoreBoard = ({
|
|
250 |
placeholder={t.leaderboard.enterName}
|
251 |
value={playerName}
|
252 |
onChange={(e) => {
|
253 |
-
// Allow letters with diacritics and numbers
|
254 |
const value = e.target.value.replace(/[^a-zA-Z脌-每0-9]/g, "");
|
255 |
setPlayerName(value);
|
256 |
}}
|
|
|
28 |
score: number;
|
29 |
avg_words_per_round: number;
|
30 |
created_at: string;
|
31 |
+
session_id: string;
|
32 |
}
|
33 |
|
34 |
interface HighScoreBoardProps {
|
|
|
36 |
avgWordsPerRound: number;
|
37 |
onClose: () => void;
|
38 |
onPlayAgain: () => void;
|
39 |
+
sessionId: string;
|
40 |
}
|
41 |
|
42 |
const ITEMS_PER_PAGE = 5;
|
|
|
59 |
currentScore,
|
60 |
avgWordsPerRound,
|
61 |
onClose,
|
62 |
+
sessionId,
|
63 |
}: HighScoreBoardProps) => {
|
64 |
const [playerName, setPlayerName] = useState("");
|
65 |
const [isSubmitting, setIsSubmitting] = useState(false);
|
|
|
89 |
});
|
90 |
|
91 |
const handleSubmitScore = async () => {
|
|
|
92 |
if (!playerName.trim() || !/^[\p{L}0-9]+$/u.test(playerName.trim())) {
|
93 |
toast({
|
94 |
title: t.leaderboard.error.invalidName,
|
|
|
140 |
.update({
|
141 |
score: currentScore,
|
142 |
avg_words_per_round: avgWordsPerRound,
|
143 |
+
session_id: sessionId
|
144 |
})
|
145 |
.eq("id", existingScore.id);
|
146 |
|
|
|
175 |
player_name: playerName.trim(),
|
176 |
score: currentScore,
|
177 |
avg_words_per_round: avgWordsPerRound,
|
178 |
+
session_id: sessionId
|
179 |
});
|
180 |
|
181 |
if (insertError) {
|
|
|
254 |
placeholder={t.leaderboard.enterName}
|
255 |
value={playerName}
|
256 |
onChange={(e) => {
|
|
|
257 |
const value = e.target.value.replace(/[^a-zA-Z脌-每0-9]/g, "");
|
258 |
setPlayerName(value);
|
259 |
}}
|
src/components/game/GuessDisplay.tsx
CHANGED
@@ -20,6 +20,7 @@ interface GuessDisplayProps {
|
|
20 |
onBack?: () => void;
|
21 |
currentScore: number;
|
22 |
avgWordsPerRound: number;
|
|
|
23 |
}
|
24 |
|
25 |
export const GuessDisplay = ({
|
@@ -31,6 +32,7 @@ export const GuessDisplay = ({
|
|
31 |
onBack,
|
32 |
currentScore,
|
33 |
avgWordsPerRound,
|
|
|
34 |
}: GuessDisplayProps) => {
|
35 |
const isGuessCorrect = () => aiGuess.toLowerCase() === currentWord.toLowerCase();
|
36 |
const isCheating = () => aiGuess === 'CHEATING';
|
@@ -145,6 +147,7 @@ export const GuessDisplay = ({
|
|
145 |
setIsDialogOpen(false);
|
146 |
onPlayAgain();
|
147 |
}}
|
|
|
148 |
/>
|
149 |
</DialogContent>
|
150 |
</Dialog>
|
|
|
20 |
onBack?: () => void;
|
21 |
currentScore: number;
|
22 |
avgWordsPerRound: number;
|
23 |
+
sessionId: string;
|
24 |
}
|
25 |
|
26 |
export const GuessDisplay = ({
|
|
|
32 |
onBack,
|
33 |
currentScore,
|
34 |
avgWordsPerRound,
|
35 |
+
sessionId,
|
36 |
}: GuessDisplayProps) => {
|
37 |
const isGuessCorrect = () => aiGuess.toLowerCase() === currentWord.toLowerCase();
|
38 |
const isCheating = () => aiGuess === 'CHEATING';
|
|
|
147 |
setIsDialogOpen(false);
|
148 |
onPlayAgain();
|
149 |
}}
|
150 |
+
sessionId={sessionId}
|
151 |
/>
|
152 |
</DialogContent>
|
153 |
</Dialog>
|
src/integrations/supabase/types.ts
CHANGED
@@ -16,6 +16,7 @@ export type Database = {
|
|
16 |
description: string
|
17 |
id: string
|
18 |
is_correct: boolean
|
|
|
19 |
target_word: string
|
20 |
}
|
21 |
Insert: {
|
@@ -24,6 +25,7 @@ export type Database = {
|
|
24 |
description: string
|
25 |
id?: string
|
26 |
is_correct: boolean
|
|
|
27 |
target_word: string
|
28 |
}
|
29 |
Update: {
|
@@ -32,6 +34,7 @@ export type Database = {
|
|
32 |
description?: string
|
33 |
id?: string
|
34 |
is_correct?: boolean
|
|
|
35 |
target_word?: string
|
36 |
}
|
37 |
Relationships: []
|
@@ -43,6 +46,7 @@ export type Database = {
|
|
43 |
id: string
|
44 |
player_name: string
|
45 |
score: number
|
|
|
46 |
}
|
47 |
Insert: {
|
48 |
avg_words_per_round: number
|
@@ -50,6 +54,7 @@ export type Database = {
|
|
50 |
id?: string
|
51 |
player_name: string
|
52 |
score: number
|
|
|
53 |
}
|
54 |
Update: {
|
55 |
avg_words_per_round?: number
|
@@ -57,6 +62,7 @@ export type Database = {
|
|
57 |
id?: string
|
58 |
player_name?: string
|
59 |
score?: number
|
|
|
60 |
}
|
61 |
Relationships: []
|
62 |
}
|
|
|
16 |
description: string
|
17 |
id: string
|
18 |
is_correct: boolean
|
19 |
+
session_id: string
|
20 |
target_word: string
|
21 |
}
|
22 |
Insert: {
|
|
|
25 |
description: string
|
26 |
id?: string
|
27 |
is_correct: boolean
|
28 |
+
session_id?: string
|
29 |
target_word: string
|
30 |
}
|
31 |
Update: {
|
|
|
34 |
description?: string
|
35 |
id?: string
|
36 |
is_correct?: boolean
|
37 |
+
session_id?: string
|
38 |
target_word?: string
|
39 |
}
|
40 |
Relationships: []
|
|
|
46 |
id: string
|
47 |
player_name: string
|
48 |
score: number
|
49 |
+
session_id: string
|
50 |
}
|
51 |
Insert: {
|
52 |
avg_words_per_round: number
|
|
|
54 |
id?: string
|
55 |
player_name: string
|
56 |
score: number
|
57 |
+
session_id: string
|
58 |
}
|
59 |
Update: {
|
60 |
avg_words_per_round?: number
|
|
|
62 |
id?: string
|
63 |
player_name?: string
|
64 |
score?: number
|
65 |
+
session_id?: string
|
66 |
}
|
67 |
Relationships: []
|
68 |
}
|