From c002c5553133bf8c4858b54653ae315024edd204 Mon Sep 17 00:00:00 2001 From: Nishidh Date: Thu, 29 May 2025 20:29:00 +0530 Subject: [PATCH 1/2] Added infinite mode feature --- src/components/InfiniteTypingBoard.jsx | 347 ++++++++++++++++++++++--- 1 file changed, 309 insertions(+), 38 deletions(-) diff --git a/src/components/InfiniteTypingBoard.jsx b/src/components/InfiniteTypingBoard.jsx index 43c94f1..51367ad 100644 --- a/src/components/InfiniteTypingBoard.jsx +++ b/src/components/InfiniteTypingBoard.jsx @@ -3,13 +3,27 @@ import Timer from './Timer'; import { motion, time } from "motion/react" import TypingCompleteScreen from './TypingCompleteScreen'; - - //feature const InfiniteTypingBoard = (props) => { const [words, setWords] = useState([]) - const [quote, setQuote] = useState(props.quotes) + + // Sample quotes for infinite mode + const infiniteQuotes = [ + "The sun is shining today, and many people are outside enjoying the warm weather. Some are walking their dogs, while others are sitting on benches reading books.", + "Technology has revolutionized the way we communicate, work, and learn. From smartphones to artificial intelligence, innovation continues to shape our daily lives.", + "Reading books opens doors to new worlds and perspectives. Literature has the power to inspire, educate, and transport us to different times and places.", + "Exercise and proper nutrition are essential for maintaining good health. Regular physical activity strengthens both body and mind while improving overall well-being.", + "Music is a universal language that transcends cultural boundaries. It has the ability to evoke emotions, create memories, and bring people together across the globe.", + "Learning new skills throughout life keeps the mind sharp and opens up opportunities. Whether it's coding, cooking, or crafting, continuous growth enriches our experience.", + "Nature provides countless benefits for mental and physical health. Spending time outdoors can reduce stress, improve mood, and connect us with the natural world.", + "Friendship is one of life's greatest treasures. Good friends provide support, laughter, and companionship through both joyful and challenging times in our lives." + ]; + + // const [quote, setQuote] = useState(props.quotes); + const [quote, setQuote] = useState(props.quotes); + const [isInfiniteMode, setIsInfiniteMode] = useState(false); + const [currentQuoteIndex, setCurrentQuoteIndex] = useState(0); const [currentIdx, setCurrentIdx] = useState(0); const [typedChars, setTypedChars] = useState(""); @@ -21,21 +35,123 @@ const InfiniteTypingBoard = (props) => { const [finalTime, setFinalTime] = useState(0); const [WPM, setWPM] = useState(0) const [accuracy, setAccuracy] = useState(0) - + + // Timer settings for infinite mode + const [timerDuration, setTimerDuration] = useState(30); // Default 30 seconds + const [timeLeft, setTimeLeft] = useState(30); + const [showTimerSettings, setShowTimerSettings] = useState(false); + const [infiniteStartTime, setInfiniteStartTime] = useState(null); // Track start time for infinite mode + const [timerActive, setTimerActive] = useState(false); // Track if timer is running + + // Check if user typed the trigger phrase for infinite mode useEffect(() => { + const cleanTypedChars = typedChars.toLowerCase().replace(/\s+/g, ''); + if (cleanTypedChars.includes("infinitemodeoftyping")) { + setIsInfiniteMode(true); + setShowTimerSettings(true); // Show timer settings when entering infinite mode + // Reset stats for infinite mode + setCurrentIdx(0); + setTypedChars(""); + setWordsPressed(0); + setRightWords(0); + setTypos(0); + setTimeLeft(timerDuration); + // Set first quote for infinite mode + setQuote(infiniteQuotes[0]); + setCurrentQuoteIndex(0); + } + }, [typedChars, timerDuration]); + + // Function to start infinite mode with selected timer + const startInfiniteMode = (selectedDuration) => { + setTimerDuration(selectedDuration); + setTimeLeft(selectedDuration); + setShowTimerSettings(false); + // Initialize typing area first + setCurrentIdx(0); + setTypedChars(""); + setWordsPressed(0); + setRightWords(0); + setTypos(0); + setIsCompeleted(false); // Ensure completion is false + setFinalTime(0); + setWPM(0); + setAccuracy(0); + setQuote(infiniteQuotes[0]); + setCurrentQuoteIndex(0); + + // Start timer after a brief delay to ensure all states are reset + setTimeout(() => { + setTimerActive(true); + setInfiniteStartTime(Date.now()); + }, 100); + }; + + + // Infinite mode timer - stops when time runs out and shows results + useEffect(() => { + let timer; + if (isInfiniteMode && !isCompeleted && !showTimerSettings && timerActive && timeLeft > 0) { + timer = setInterval(() => { + setTimeLeft(prevTime => { + if (prevTime <= 1) { + // Time's up, stop the test and show results + setTimerActive(false); + const totalTime = timerDuration; // Use the full timer duration + setFinalTime(totalTime); + + // Calculate final WPM and accuracy for infinite mode + // Fixed WPM calculation: (correct chars / 5) / (time in minutes) + const timeInMinutes = totalTime / 60; + const calculatedWPM = timeInMinutes > 0 ? Math.round((rightWords / 5) / timeInMinutes) : 0; + setWPM(calculatedWPM); + + const calculatedAccuracy = wordsPressed > 0 ? parseFloat(((rightWords / wordsPressed) * 100).toFixed(2)) : 0; + setAccuracy(calculatedAccuracy); + + // Set completion after calculations are done + setTimeout(() => { + setIsCompeleted(true); + }, 100); + + return 0; + } + return prevTime - 1; + }); + }, 1000); + } + + return () => { + if (timer) clearInterval(timer); + }; + }, [isInfiniteMode, isCompeleted, showTimerSettings, timerActive, timeLeft]); + useEffect(() => { const handleKeyDown = (e) => { + // Don't process keys if timer settings are shown or test is completed + if (showTimerSettings || isCompeleted) return; + const pressedKey = e.key; - if (/^[a-zA-Z0-9\s]$/.test(pressedKey)) { + if (/^[a-zA-Z0-9\s.,!?;:'"()-]$/.test(pressedKey)) { setWordsPressed(prev => prev + 1) if (pressedKey === quote[currentIdx]) { const nextIdx = currentIdx + 1; if (nextIdx === quote.length) { - setIsCompeleted(true); + if (isInfiniteMode && timerActive) { + // In infinite mode, immediately load next quote if timer is still active + const nextIndex = (currentQuoteIndex + 1) % infiniteQuotes.length; + setCurrentQuoteIndex(nextIndex); + setQuote(infiniteQuotes[nextIndex]); + setCurrentIdx(0); + } else if (!isInfiniteMode) { + // For regular mode, calculate stats before completion + setIsCompeleted(true); + } + } else { + setCurrentIdx(nextIdx); } - setCurrentIdx(nextIdx); setTypedChars(prev => prev + pressedKey); setRightWords(prev => prev + 1) setWarning(prev => prev = true) @@ -49,22 +165,62 @@ const InfiniteTypingBoard = (props) => { document.addEventListener('keydown', handleKeyDown); return () => document.removeEventListener('keydown', handleKeyDown); - }, [currentIdx, quote, words, wordsPressed]); + }, [currentIdx, quote, words, wordsPressed, isInfiniteMode, currentQuoteIndex, showTimerSettings, timerDuration, timerActive, isCompeleted]); - - // Calculate WPM when typing is completed + + // Calculate WPM when typing is completed (for regular mode only) useEffect(() => { - if (isCompeleted && finalTime > 0) { - - const timeInMinutes = finalTime / 8;//number 8 is assuring that this salculation will show word per minute and not aplbet ot keysTyped per minute. i am assumned that there is 6-7 words are commmon in words. - const calculatedWPM = Math.round(rightWords / timeInMinutes); + if (isCompeleted && finalTime > 0 && !isInfiniteMode) { + // Fixed WPM calculation for regular mode + const timeInMinutes = finalTime / 60; // Convert seconds to minutes + const calculatedWPM = timeInMinutes > 0 ? Math.round((rightWords / 5) / timeInMinutes) : 0; setWPM(calculatedWPM); - const calculatedAccuracy = ((rightWords / wordsPressed) * 100).toFixed(2); + const calculatedAccuracy = wordsPressed > 0 ? parseFloat(((rightWords / wordsPressed) * 100).toFixed(2)) : 0; setAccuracy(calculatedAccuracy); } - }, [isCompeleted, finalTime, rightWords]); - + }, [isCompeleted, finalTime, rightWords, isInfiniteMode, wordsPressed]); + + // Function to exit infinite mode + const exitInfiniteMode = () => { + setIsInfiniteMode(false); + setShowTimerSettings(false); + setTimerActive(false); + setQuote(props.quotes); + setCurrentIdx(0); + setTypedChars(""); + setWordsPressed(0); + setRightWords(0); + setTypos(0); + setTimeLeft(30); + setTimerDuration(30); + setIsCompeleted(false); + setInfiniteStartTime(null); + setFinalTime(0); + setWPM(0); + setAccuracy(0); + }; + + // Function to restart the typing test + const restartTest = () => { + if (isInfiniteMode) { + setShowTimerSettings(true); + setIsCompeleted(false); + setTimerActive(false); + setTimeLeft(timerDuration); + } else { + setIsCompeleted(false); + } + setCurrentIdx(0); + setTypedChars(""); + setWordsPressed(0); + setRightWords(0); + setTypos(0); + setFinalTime(0); + setWPM(0); + setAccuracy(0); + setInfiniteStartTime(null); + }; return ( <> @@ -72,21 +228,144 @@ const InfiniteTypingBoard = (props) => {
+ {/* Show infinite mode indicator with countdown */} + {isInfiniteMode && !showTimerSettings && timerActive && ( +
+ INFINITE MODE - {Math.floor(timeLeft / 60)}:{(timeLeft % 60).toString().padStart(2, '0')} +
+ )} + + {/* Show current quote number in infinite mode */} + {isInfiniteMode && !showTimerSettings && timerActive && ( +
+ Quote {currentQuoteIndex + 1} of {infiniteQuotes.length} +
+ )} + + {/* Show "Time's Up!" when infinite mode timer expires */} + {isInfiniteMode && isCompeleted && ( +
+ TIME'S UP! +
+ )} + + {/* Exit infinite mode button */} + {isInfiniteMode && ( + + )} + + {/* Restart button when test is completed */} + {isCompeleted && ( + + )} + {/* total typos while typing*/}
total Typos : {typos}
- {/* timer */} - - + {/* Show cumulative stats in infinite mode */} + {isInfiniteMode && !showTimerSettings && ( + <> +
+ Total Chars: {rightWords} +
+
+ Accuracy: {wordsPressed > 0 ? ((rightWords / wordsPressed) * 100).toFixed(1) : 0}% +
+ + )} + + {/* timer - only show in regular mode */} + {!isInfiniteMode && }
-
+ {/* Timer Settings Modal for Infinite Mode */} + {showTimerSettings && ( +
+
+

+ Choose Timer Duration +

+

+ Select how long you want the typing test to last +

+
+ + + + +
+ +
+
+ )} +
+ {/* Show instruction for infinite mode */} + {!isInfiniteMode && ( +
+ + Type "infinite mode of typing" to enter infinite mode + +
+ )} + + {/* Show timer selection message */} + {showTimerSettings && ( +
+ + Please select a timer duration above to continue + +
+ )} + + {/* Show completion message when test ends */} + {isCompeleted && isInfiniteMode && ( +
+ + Test Complete! Check your results below. + +
+ )}
- {quote.split(' ').map((word, wordIndex) => { + {!showTimerSettings && !isCompeleted && quote.split(' ').map((word, wordIndex) => { const startIndex = quote.split(' ').slice(0, wordIndex).join(' ').length + (wordIndex > 0 ? 1 : 0); // +1 for space return ( @@ -120,28 +399,20 @@ const InfiniteTypingBoard = (props) => { })}
- -
- {/* */} - {isCompeleted ? - :
} - -
+ {/* Show completion screen when test is completed (only show if there's actual typing data) */} + {isCompeleted && (finalTime > 0 || (isInfiniteMode && (rightWords > 0 || wordsPressed > 0))) ? + + :
+ } + +
); }; -export default InfiniteTypingBoard; - +export default InfiniteTypingBoard; \ No newline at end of file From da780157d660f019fc62d83d7b6d3f8e00dcd26b Mon Sep 17 00:00:00 2001 From: Nishidh Date: Thu, 29 May 2025 20:36:44 +0530 Subject: [PATCH 2/2] Added infinite mode feature --- src/components/GithubLogo.jsx | 20 ++++++++++++++++++++ src/components/TwitterLogo.jsx | 14 ++++++++++++++ 2 files changed, 34 insertions(+) create mode 100644 src/components/GithubLogo.jsx create mode 100644 src/components/TwitterLogo.jsx diff --git a/src/components/GithubLogo.jsx b/src/components/GithubLogo.jsx new file mode 100644 index 0000000..61085db --- /dev/null +++ b/src/components/GithubLogo.jsx @@ -0,0 +1,20 @@ +import React from "react"; + +function GithubLogo() { + return ( + + + + ); +} + +export default GithubLogo; \ No newline at end of file diff --git a/src/components/TwitterLogo.jsx b/src/components/TwitterLogo.jsx new file mode 100644 index 0000000..f3a44b8 --- /dev/null +++ b/src/components/TwitterLogo.jsx @@ -0,0 +1,14 @@ +import React from 'react' + +function TwitterLogo() { + return ( + + + + + ) +} + +export default TwitterLogo \ No newline at end of file