Felix Zieger commited on
Commit
34f302f
·
1 Parent(s): fe9a0c4

input validations

Browse files
src/components/HighScoreBoard.tsx CHANGED
@@ -78,10 +78,11 @@ export const HighScoreBoard = ({
78
  });
79
 
80
  const handleSubmitScore = async () => {
81
- if (!playerName.trim()) {
 
82
  toast({
83
  title: "Error",
84
- description: "Please enter your name",
85
  variant: "destructive",
86
  });
87
  return;
@@ -221,11 +222,16 @@ export const HighScoreBoard = ({
221
  {!hasSubmitted && currentScore > 0 && (
222
  <div className="flex gap-4 mb-6">
223
  <Input
224
- placeholder="Enter your name"
225
  value={playerName}
226
- onChange={(e) => setPlayerName(e.target.value)}
 
 
 
 
227
  onKeyDown={handleKeyDown}
228
  className="flex-1"
 
229
  />
230
  <Button
231
  onClick={handleSubmitScore}
 
78
  });
79
 
80
  const handleSubmitScore = async () => {
81
+ // Validate player name (only alphanumeric characters allowed)
82
+ if (!playerName.trim() || !/^[a-zA-Z0-9]+$/.test(playerName.trim())) {
83
  toast({
84
  title: "Error",
85
+ description: "Please enter a valid name (only letters and numbers allowed)",
86
  variant: "destructive",
87
  });
88
  return;
 
222
  {!hasSubmitted && currentScore > 0 && (
223
  <div className="flex gap-4 mb-6">
224
  <Input
225
+ placeholder="Enter your name (letters and numbers only)"
226
  value={playerName}
227
+ onChange={(e) => {
228
+ // Only allow alphanumeric input
229
+ const value = e.target.value.replace(/[^a-zA-Z0-9]/g, '');
230
+ setPlayerName(value);
231
+ }}
232
  onKeyDown={handleKeyDown}
233
  className="flex-1"
234
+ maxLength={20}
235
  />
236
  <Button
237
  onClick={handleSubmitScore}
src/components/game/SentenceBuilder.tsx CHANGED
@@ -2,6 +2,7 @@ import { Button } from "@/components/ui/button";
2
  import { Input } from "@/components/ui/input";
3
  import { motion } from "framer-motion";
4
  import { KeyboardEvent, useRef, useEffect, useState } from "react";
 
5
 
6
  interface SentenceBuilderProps {
7
  currentWord: string;
@@ -27,6 +28,7 @@ export const SentenceBuilder = ({
27
  const inputRef = useRef<HTMLInputElement>(null);
28
  const [imageLoaded, setImageLoaded] = useState(false);
29
  const imagePath = `/think_in_sync_assets/${currentWord.toLowerCase()}.jpg`;
 
30
 
31
  useEffect(() => {
32
  const img = new Image();
@@ -55,17 +57,40 @@ export const SentenceBuilder = ({
55
  if (e.shiftKey && e.key === 'Enter') {
56
  e.preventDefault();
57
  if (playerInput.trim()) {
58
- // Create a synthetic form event to add the current word
59
- const syntheticEvent = {
60
- preventDefault: () => {},
61
- } as React.FormEvent;
62
- onSubmitWord(syntheticEvent);
63
  }
64
  // Make the guess immediately without waiting for AI response
65
  onMakeGuess();
66
  }
67
  };
68
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
69
  return (
70
  <motion.div
71
  initial={{ opacity: 0 }}
@@ -95,14 +120,18 @@ export const SentenceBuilder = ({
95
  {sentence.length > 0 ? sentence.join(" ") : "Start your sentence..."}
96
  </p>
97
  </div>
98
- <form onSubmit={onSubmitWord} className="mb-4">
99
  <Input
100
  ref={inputRef}
101
  type="text"
102
  value={playerInput}
103
- onChange={(e) => onInputChange(e.target.value.replace(/\s/g, ''))}
 
 
 
 
104
  onKeyDown={handleKeyDown}
105
- placeholder="Enter your word..."
106
  className="mb-4"
107
  disabled={isAiThinking}
108
  />
 
2
  import { Input } from "@/components/ui/input";
3
  import { motion } from "framer-motion";
4
  import { KeyboardEvent, useRef, useEffect, useState } from "react";
5
+ import { useToast } from "@/hooks/use-toast";
6
 
7
  interface SentenceBuilderProps {
8
  currentWord: string;
 
28
  const inputRef = useRef<HTMLInputElement>(null);
29
  const [imageLoaded, setImageLoaded] = useState(false);
30
  const imagePath = `/think_in_sync_assets/${currentWord.toLowerCase()}.jpg`;
31
+ const { toast } = useToast();
32
 
33
  useEffect(() => {
34
  const img = new Image();
 
57
  if (e.shiftKey && e.key === 'Enter') {
58
  e.preventDefault();
59
  if (playerInput.trim()) {
60
+ handleSubmit(e as any);
 
 
 
 
61
  }
62
  // Make the guess immediately without waiting for AI response
63
  onMakeGuess();
64
  }
65
  };
66
 
67
+ const handleSubmit = (e: React.FormEvent) => {
68
+ e.preventDefault();
69
+ const input = playerInput.trim().toLowerCase();
70
+ const target = currentWord.toLowerCase();
71
+
72
+ // Check if the input contains only letters
73
+ if (!/^[a-zA-Z]+$/.test(input)) {
74
+ toast({
75
+ title: "Invalid Word",
76
+ description: "Please use only letters (no numbers or special characters)",
77
+ variant: "destructive",
78
+ });
79
+ return;
80
+ }
81
+
82
+ if (input.includes(target)) {
83
+ toast({
84
+ title: "Invalid Word",
85
+ description: `You cannot use words that contain "${currentWord}"`,
86
+ variant: "destructive",
87
+ });
88
+ return;
89
+ }
90
+
91
+ onSubmitWord(e);
92
+ };
93
+
94
  return (
95
  <motion.div
96
  initial={{ opacity: 0 }}
 
120
  {sentence.length > 0 ? sentence.join(" ") : "Start your sentence..."}
121
  </p>
122
  </div>
123
+ <form onSubmit={handleSubmit} className="mb-4">
124
  <Input
125
  ref={inputRef}
126
  type="text"
127
  value={playerInput}
128
+ onChange={(e) => {
129
+ // Only allow letters in the input
130
+ const value = e.target.value.replace(/[^a-zA-Z]/g, '');
131
+ onInputChange(value);
132
+ }}
133
  onKeyDown={handleKeyDown}
134
+ placeholder="Enter your word (letters only)..."
135
  className="mb-4"
136
  disabled={isAiThinking}
137
  />
src/components/game/WordDisplay.tsx CHANGED
@@ -1,6 +1,7 @@
1
  import { Button } from "@/components/ui/button";
2
  import { motion } from "framer-motion";
3
  import { useEffect, useState } from "react";
 
4
 
5
  interface WordDisplayProps {
6
  currentWord: string;
@@ -11,13 +12,16 @@ interface WordDisplayProps {
11
  export const WordDisplay = ({ currentWord, successfulRounds, onContinue }: WordDisplayProps) => {
12
  const [imageLoaded, setImageLoaded] = useState(false);
13
  const imagePath = `/think_in_sync_assets/${currentWord.toLowerCase()}.jpg`;
 
14
 
15
  useEffect(() => {
16
- const img = new Image();
17
- img.onload = () => setImageLoaded(true);
18
- img.src = imagePath;
19
- console.log("Attempting to load image:", imagePath);
20
- }, [imagePath]);
 
 
21
 
22
  return (
23
  <motion.div
@@ -27,7 +31,7 @@ export const WordDisplay = ({ currentWord, successfulRounds, onContinue }: WordD
27
  >
28
  <h2 className="mb-4 text-2xl font-semibold text-gray-900">Your Word</h2>
29
  <div className="mb-4 overflow-hidden rounded-lg bg-secondary/10">
30
- {imageLoaded && (
31
  <img
32
  src={imagePath}
33
  alt={currentWord}
 
1
  import { Button } from "@/components/ui/button";
2
  import { motion } from "framer-motion";
3
  import { useEffect, useState } from "react";
4
+ import { useIsMobile } from "@/hooks/use-mobile";
5
 
6
  interface WordDisplayProps {
7
  currentWord: string;
 
12
  export const WordDisplay = ({ currentWord, successfulRounds, onContinue }: WordDisplayProps) => {
13
  const [imageLoaded, setImageLoaded] = useState(false);
14
  const imagePath = `/think_in_sync_assets/${currentWord.toLowerCase()}.jpg`;
15
+ const isMobile = useIsMobile();
16
 
17
  useEffect(() => {
18
+ if (!isMobile) {
19
+ const img = new Image();
20
+ img.onload = () => setImageLoaded(true);
21
+ img.src = imagePath;
22
+ console.log("Attempting to load image:", imagePath);
23
+ }
24
+ }, [imagePath, isMobile]);
25
 
26
  return (
27
  <motion.div
 
31
  >
32
  <h2 className="mb-4 text-2xl font-semibold text-gray-900">Your Word</h2>
33
  <div className="mb-4 overflow-hidden rounded-lg bg-secondary/10">
34
+ {!isMobile && imageLoaded && (
35
  <img
36
  src={imagePath}
37
  alt={currentWord}