// Import required React and React Native components and hooks
import { useState, useEffect } from 'react';
import {
View,
Text,
TextInput,
TouchableOpacity,
StyleSheet,
ScrollView,
} from 'react-native';
// Main functional component
const TypingSpeedMonitorApp = () => {
// Static sample text for typing test
const sampleText = `GeeksforGeeks is a computer science portal that provides a wide variety of resources for programmers and computer science enthusiasts. It was created with the goal of providing high-quality educational content to help individuals learn and improve their programming and computer science skills.`;
// State to hold user-typed text
const [text, setText] = useState('');
// State to store start time of typing
const [startTime, setStartTime] = useState(0);
// State to store end time of typing
const [endTime, setEndTime] = useState(0);
// State to store calculated typing speed
const [typingSpeed, setTypingSpeed] = useState(0);
// Boolean flag to track if typing is in progress
const [isTyping, setIsTyping] = useState(false);
// Countdown timer (starts at 60 seconds)
const [timer, setTimer] = useState(60);
// Number of errors between typed text and sample
const [errors, setErrors] = useState(0);
// useEffect to handle countdown timer during typing
useEffect(() => {
let interval;
if (isTyping) {
interval = setInterval(() => {
setTimer(prev => {
if (prev === 1) {
clearInterval(interval); // Stop the timer
handleEndTyping(); // Automatically end typing
return 60; // Reset timer for next session
}
return prev - 1; // Decrease timer by 1 each second
});
}, 1000); // Runs every second
}
// Clear interval when component unmounts or isTyping changes
return () => clearInterval(interval);
}, [isTyping]);
// useEffect to calculate WPM (Words Per Minute) once typing ends
useEffect(() => {
if (startTime && endTime && text.trim().length > 0) {
const seconds = (endTime - startTime) / 1000; // Calculate time in seconds
const wpm = (text.trim().split(/\s+/).length / seconds) * 60; // WPM formula
setTypingSpeed(wpm.toFixed(2)); // Save speed rounded to 2 decimal places
}
}, [endTime]);
// Function to start typing session
const handleStartTyping = () => {
setText(''); // Clear previous text
setStartTime(new Date().getTime()); // Capture current time as start time
setIsTyping(true); // Enable typing mode
setErrors(0); // Reset errors
};
// Function to manually or auto end typing session
const handleEndTyping = () => {
setEndTime(new Date().getTime()); // Capture end time
setIsTyping(false); // Disable typing mode
calculateErrors(); // Check number of errors
};
// Compare typed words with sample words to count mistakes
const calculateErrors = () => {
const sampleWords = sampleText.split(/\s+/); // Break sample into words
const enteredWords = text.split(/\s+/); // Break user input into words
let count = 0;
for (let i = 0; i < sampleWords.length; i++) {
if (sampleWords[i] !== enteredWords[i]) count++; // Count mismatched words
}
setErrors(count); // Set error count
};
// Function to reset the entire typing session
const handleReset = () => {
setText('');
setStartTime(0);
setEndTime(0);
setTypingSpeed(0);
setIsTyping(false);
setTimer(60);
setErrors(0);
};
// JSX layout for the UI
return (
<ScrollView contentContainerStyle={styles.container}>
{/* Title-Text */}
<Text style={styles.title}>🧠 Typing Speed Monitor</Text>
{/* Timer Display */}
<Text style={styles.timer}>⏱️ Time Left: {timer}s</Text>
{/* Box displaying the sample text */}
<View style={styles.sampleBox}>
<Text style={styles.sampleText}>{sampleText}</Text>
</View>
{/* Text input for user to type in */}
<TextInput
style={styles.input}
multiline
placeholder="Start typing here..."
value={text}
onChangeText={setText}
editable={isTyping} // Only editable during typing
/>
{/* Row of control buttons */}
<View style={styles.buttonRow}>
{/* Start or End Typing Button */}
<TouchableOpacity
style={isTyping ? styles.endButton : styles.startButton}
onPress={isTyping ? handleEndTyping : handleStartTyping}
>
<Text style={styles.buttonText}>
{isTyping ? 'End Typing' : 'Start Typing'}
</Text>
</TouchableOpacity>
{/* Reset Button */}
<TouchableOpacity style={styles.resetButton} onPress={handleReset}>
<Text style={styles.buttonText}>Reset</Text>
</TouchableOpacity>
</View>
{/* Display result only if typing has ended */}
{endTime > 0 && (
<View style={styles.resultBox}>
<Text style={styles.resultText}>
🚀 Typing Speed: <Text style={styles.bold}>{typingSpeed}</Text> WPM
</Text>
<Text style={styles.resultText}>
❌ Error Count: <Text style={styles.bold}>{errors}</Text>
</Text>
</View>
)}
</ScrollView>
);
};
// StyleSheet for UI styling
const styles = StyleSheet.create({
container: {
flexGrow: 1,
padding: 20,
backgroundColor: '#f0f4f8',
},
title: {
fontSize: 28,
fontWeight: 'bold',
marginVertical: 20,
textAlign: 'center',
color: '#2c3e50',
},
timer: {
fontSize: 20,
backgroundColor: '#ffeaa7',
padding: 8,
borderRadius: 8,
alignSelf: 'center',
marginBottom: 15,
color: '#2d3436',
fontWeight: '600',
},
sampleBox: {
backgroundColor: '#dfe6e9',
borderRadius: 10,
padding: 15,
marginBottom: 15,
},
sampleText: {
fontSize: 16,
lineHeight: 22,
color: '#34495e',
},
input: {
height: 120,
borderColor: '#74b9ff',
borderWidth: 1.5,
borderRadius: 10,
backgroundColor: '#fff',
padding: 12,
fontSize: 16,
marginBottom: 16,
},
buttonRow: {
flexDirection: 'row',
justifyContent: 'space-between',
},
startButton: {
flex: 1,
backgroundColor: '#00b894',
padding: 14,
borderRadius: 10,
alignItems: 'center',
marginRight: 10,
},
endButton: {
flex: 1,
backgroundColor: '#d63031',
padding: 14,
borderRadius: 10,
alignItems: 'center',
marginRight: 10,
},
resetButton: {
flex: 1,
backgroundColor: '#0984e3',
padding: 14,
borderRadius: 10,
alignItems: 'center',
},
buttonText: {
color: '#fff',
fontWeight: '700',
fontSize: 16,
},
resultBox: {
backgroundColor: '#fff',
borderRadius: 10,
marginTop: 20,
padding: 16,
elevation: 2,
},
resultText: {
fontSize: 18,
color: '#2c3e50',
marginBottom: 8,
},
bold: {
fontWeight: 'bold',
color: '#0984e3',
},
});
// Export the component as default
export default TypingSpeedMonitorApp;