-
Notifications
You must be signed in to change notification settings - Fork 3
Infinite mode #4
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
|
@its-nihit-0407 is attempting to deploy a commit to the deepak's projects Team on Vercel. A member of the Team first needs to authorize it. |
Reviewer's GuideThis PR integrates an infinite typing mode into the typing board by detecting a trigger phrase, prompting the user to select a timer duration, cycling through a predefined list of quotes until time expires, and then displaying the aggregated results; it also introduces two new social logo components. File-Level Changes
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hey @its-nihit-0407 - I've reviewed your changes - here's some feedback:
- The InfiniteTypingBoard component has become very large and could benefit from extracting the timer and infinite‐mode logic into separate hooks or child components for better readability and maintainability.
- Move static data like the infiniteQuotes array and timer‐option values out of the component (e.g., into a constants file or props) to avoid reinitializing them on every render.
- In GithubLogo.jsx, use React‐friendly SVG props (e.g., className instead of class, strokeWidth instead of stroke-width) to ensure attributes bind correctly.
Here's what I looked at during the review
- 🟡 General issues: 5 issues found
- 🟢 Security: all looks good
- 🟢 Testing: all looks good
- 🟡 Complexity: 1 issue found
- 🟢 Documentation: all looks good
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
| const pressedKey = e.key; | ||
|
|
||
| if (/^[a-zA-Z0-9\s]$/.test(pressedKey)) { | ||
| if (/^[a-zA-Z0-9\s.,!?;:'"()-]$/.test(pressedKey)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
question: Keydown handler does not handle non-printable or special keys.
Currently, keys like Backspace, Tab, and arrow keys are ignored. If users need to edit or navigate, consider handling these keys as well.
| setIsInfiniteMode(true); | ||
| setShowTimerSettings(true); // Show timer settings when entering infinite mode | ||
| // Reset stats for infinite mode | ||
| setCurrentIdx(0); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
suggestion: Quote cycling in infinite mode may repeat quotes in a predictable order.
Since the next quote is always selected in sequence, users may quickly see repeats, especially with a small quote list. Shuffling or tracking used quotes could improve variety.
Suggested implementation:
if (isInfiniteMode && timerActive) {
// In infinite mode, immediately load next quote if timer is still active
// Shuffle and track used quotes for better variety
let nextUsedQuotes = [...usedQuotes, currentQuoteIndex];
let nextIndex;
if (nextUsedQuotes.length === infiniteQuotes.length) {
// All quotes used, reshuffle and start over
const shuffled = [...infiniteQuotes].sort(() => Math.random() - 0.5);
setInfiniteQuotes(shuffled);
nextUsedQuotes = [];
nextIndex = 0;
} else {
// Pick a random unused quote
const unusedIndexes = infiniteQuotes
.map((_, idx) => idx)
.filter(idx => !nextUsedQuotes.includes(idx));
nextIndex = unusedIndexes[Math.floor(Math.random() * unusedIndexes.length)];
}
setCurrentQuoteIndex(nextIndex);
setQuote(infiniteQuotes[nextIndex]);
setCurrentIdx(0);
setUsedQuotes(nextUsedQuotes);You will need to:
- Add
usedQuotes,setUsedQuotes,infiniteQuotes, andsetInfiniteQuotesto your component's state usinguseState. - Initialize
usedQuotesas an empty array andinfiniteQuotesas a shuffled version of your quotes array when infinite mode starts. - Reset
usedQuotesand reshuffleinfiniteQuoteswhen all quotes have been used.
Example state initialization at the top of your component:
const [usedQuotes, setUsedQuotes] = useState([]);
const [infiniteQuotes, setInfiniteQuotes] = useState(() => shuffleArray(initialQuotes));Where shuffleArray is a helper function:
function shuffleArray(array) {
return [...array].sort(() => Math.random() - 0.5);
}Make sure to update these states when infinite mode is toggled or when the quote list changes.
| // 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); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
suggestion: WPM calculation for infinite mode and regular mode uses different logic.
Extract the WPM calculation into a shared function to maintain consistency between modes.
Suggested implementation:
// Shared WPM calculation function
const calculateWPM = (wordsCount, timeInSeconds) => {
const timeInMinutes = timeInSeconds / 60;
return timeInMinutes > 0 ? Math.round((wordsCount / 5) / timeInMinutes) : 0;
};
// Calculate WPM when typing is completed (for regular mode only)
useEffect(() => {
if (isCompeleted && finalTime > 0 && !isInfiniteMode) {
setWPM(calculateWPM(rightWords, finalTime));If infinite mode calculates WPM elsewhere in this file, update that logic to use the new calculateWPM function as well for consistency.
| <svg | ||
| stroke="currentColor" | ||
| fill="currentColor" | ||
| stroke-width="0" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
issue: Use camelCase for SVG attributes in JSX.
Update attributes like stroke-width to strokeWidth and class to className to comply with JSX conventions and prevent React warnings.
| fill="currentColor" | ||
| stroke-width="0" | ||
| viewBox="0 0 496 512" | ||
| class="text-xl" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
issue (bug_risk): Use className instead of class in JSX.
React uses 'className' for CSS classes; using 'class' may cause rendering problems.
|
|
||
| //feature | ||
|
|
||
| const InfiniteTypingBoard = (props) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
issue (complexity): Consider extracting key-handling, infinite mode logic, and the timer settings modal into separate hooks and components to simplify the main component.
Here are three small, focused extractions that will dramatically reduce the size and complexity of your main component without changing any behavior:
- Extract your key‐handling into a
useKeyPresshook - Extract infinite-mode state/timer logic into a
useInfiniteModehook - Pull your timer-settings modal into its own
TimerSettingsModalcomponent
1) useKeyPress hook
// hooks/useKeyPress.js
import { useEffect } from 'react';
export default function useKeyPress(onKeyPress, deps = []) {
useEffect(() => {
function handle(e) {
onKeyPress(e);
}
document.addEventListener('keydown', handle);
return () => document.removeEventListener('keydown', handle);
}, deps);
}Usage in your component:
import useKeyPress from './hooks/useKeyPress';
function InfiniteTypingBoard(props) {
// ... all your state ...
const handleKeyDown = (e) => {
if (showTimerSettings || isCompleted) return;
// (rest of your logic here)
};
useKeyPress(handleKeyDown, [
quote, currentIdx, isInfiniteMode, timerActive,
showTimerSettings, wordsPressed, /* etc */
]);
// ...
}2) useInfiniteMode hook
// hooks/useInfiniteMode.js
import { useState, useEffect } from 'react';
export default function useInfiniteMode(infiniteQuotes, timerDuration) {
const [isInfiniteMode, setInfinite] = useState(false);
const [currentQuoteIndex, setQuoteIndex] = useState(0);
const [timeLeft, setTimeLeft] = useState(timerDuration);
const [timerActive, setTimerActive] = useState(false);
const [showTimerSettings, setShowTimerSettings] = useState(false);
// start & exit logic
const start = (duration) => {
setTimerActive(true);
setTimeLeft(duration);
setShowTimerSettings(false);
setQuoteIndex(0);
};
const exit = () => {
setInfinite(false);
setTimerActive(false);
setShowTimerSettings(false);
setTimeLeft(timerDuration);
};
// timer countdown
useEffect(() => {
if (!isInfiniteMode || !timerActive || timeLeft <= 0) return;
const iv = setInterval(() => {
setTimeLeft(t => Math.max(0, t - 1));
}, 1000);
return () => clearInterval(iv);
}, [isInfiniteMode, timerActive, timeLeft]);
// detect trigger phrase
useEffect(() => {
// assume typedChars lives outside and is passed in via a setter
// when trigger detected => setInfinite(true); setShowTimerSettings(true)
}, [/* typedChars */]);
return {
isInfiniteMode, currentQuoteIndex, timeLeft,
timerActive, showTimerSettings,
setShowTimerSettings, setInfinite,
setQuoteIndex, start, exit
};
}In your component you then do:
const {
isInfiniteMode, currentQuoteIndex, timeLeft,
timerActive, showTimerSettings,
setShowTimerSettings, start, exit
} = useInfiniteMode(infiniteQuotes, timerDuration);3) TimerSettingsModal component
// components/TimerSettingsModal.jsx
export default function TimerSettingsModal({onSelect, onCancel}) {
const opts = [30, 60, 120, 300];
return (
<div className="fixed inset-0 bg-black/50 flex items-center justify-center">
<div className="bg-white p-6 rounded-lg">
<h2>Select duration</h2>
<div className="flex gap-2">
{opts.map(sec => (
<button key={sec} onClick={() => onSelect(sec)}>
{sec / 60 >= 1 ? sec/60 + 'm' : sec + 's'}
</button>
))}
</div>
<button onClick={onCancel}>Cancel</button>
</div>
</div>
);
}And in your main render:
{showTimerSettings && (
<TimerSettingsModal
onSelect={start}
onCancel={() => setShowTimerSettings(false)}
/>
)}By moving those three chunks out you’ll shrink the main component by several hundred lines, eliminate nested conditionals in effects, and make each piece easy to reason about in isolation.
| } | ||
|
|
||
| return () => { | ||
| if (timer) clearInterval(timer); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
suggestion (code-quality): Use block braces for ifs, whiles, etc. (use-braces)
| if (timer) clearInterval(timer); | |
| if (timer) { |
Explanation
It is recommended to always use braces and create explicit statement blocks.Using the allowed syntax to just write a single statement can lead to very confusing
situations, especially where subsequently a developer might add another statement
while forgetting to add the braces (meaning that this wouldn't be included in the condition).
| useEffect(() => { | ||
| const handleKeyDown = (e) => { | ||
| // Don't process keys if timer settings are shown or test is completed | ||
| if (showTimerSettings || isCompeleted) return; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
suggestion (code-quality): Use block braces for ifs, whiles, etc. (use-braces)
| if (showTimerSettings || isCompeleted) return; | |
| if (showTimerSettings || isCompeleted) { |
Explanation
It is recommended to always use braces and create explicit statement blocks.Using the allowed syntax to just write a single statement can lead to very confusing
situations, especially where subsequently a developer might add another statement
while forgetting to add the braces (meaning that this wouldn't be included in the condition).
ekkp6tju9w6kj4i5gbph.mp4
rs5is1c93vbn0gfrjlfu.mp4
Summary by Sourcery
Implement an Infinite Mode in the typing board that is activated by the phrase "infinite mode of typing" and lets users choose from four timer durations before running continuous typing sessions with multiple quotes, automated countdown stop, and result display. Also add simple GithubLogo and TwitterLogo components.
New Features:
Enhancements:
Chores: