Skip to content
This repository was archived by the owner on Nov 10, 2025. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
77 changes: 74 additions & 3 deletions App.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,81 @@
import React, { useState, useEffect } from 'react';
import { StatusBar } from 'expo-status-bar';
import { StyleSheet, Text, View } from 'react-native';
import { StyleSheet, View } from 'react-native';
import { StopWatch } from './src/StopWatch';
import { StopWatchButton } from './src/StopWatchButton';
import { LapList } from './src/LapList';

const CURRENT_TIME = 0;

export default function App() {

// States that keep track of the time
const [timeCount, setTimeCount] = useState<number>(CURRENT_TIME);
const [timeInterval, setTimeInterval] = useState<NodeJS.Timeout | null>(null);
const [isStopWatchRunning, setIsStopWatchRunning] = useState<boolean>(false);
const [laps, setLaps] = useState<number[]>([]);


// Start Time Function
const startTime = () => {
setIsStopWatchRunning(true);

// Calculate the initial elapsed time when starting
const startTimeStamp = performance.now() - timeCount;

const id = setInterval(() => {

const currentTimeStamp = performance.now();
const elapsedTime = currentTimeStamp - startTimeStamp;
setTimeCount(Math.floor(elapsedTime));

}, 10);
setTimeInterval(id);

};


// Stop Time Function
const stopTime = () => {
if (timeInterval !== null) {
clearInterval(timeInterval);
setTimeInterval(null);
}
setIsStopWatchRunning(false);
};

// Reset Time Function
const resetTime = () => {
setTimeCount(0);
setLaps([]);
};

// Add Lap Function
const addLap = () => {
setLaps((prevLaps) => [...prevLaps, timeCount]);
};

useEffect(() => {
return () => {
if (timeInterval !== null) {
clearInterval(timeInterval);
setTimeInterval(null);
}
};
}, [timeInterval]);

return (
<View style={styles.container}>
<Text>Open up App.tsx to start working on your app!</Text>
<StatusBar style="auto" />
<StopWatch stopWatchCount={timeCount} />
<StopWatchButton
isStopWatchRunning={isStopWatchRunning}
startTime={startTime}
stopTime={stopTime}
resetTime={resetTime}
addLap={addLap}
/>
<LapList laps={laps} />
</View>
);
}
Expand All @@ -17,4 +87,5 @@ const styles = StyleSheet.create({
alignItems: 'center',
justifyContent: 'center',
},
});

});
78 changes: 78 additions & 0 deletions src/LapList.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import React from 'react';
import { FlatList, StyleSheet, Text, View } from 'react-native';

// Declares props that will be used by the StopWatch Buttons
type Props = {

// List that keeps track of the laps
laps: number[];
};

export const LapList: React.FC<Props> = ({ laps }) => {
const reversedLaps = [...laps].reverse();

const renderItem = ({ item, index }: { item: number; index: number }) => (
<View style={styles.lapItem}>
<Text style={styles.lapItemText}>{`Lap ${reversedLaps.length - index}:`}</Text>
<Text style={styles.lapItemText}>{formatTime(item)}</Text>
</View>
);

// Format the time of the laps in HH:MM:SS format
const formatTime = (time: number): string => {
const hours = Math.floor(time / (1000 * 60 * 60)).toString().padStart(2, '0');
const minutes = Math.floor(time / (1000 * 60)).toString().padStart(2, '0');
const seconds = Math.floor((time % (1000 * 60)) / 1000).toString().padStart(2, '0');
const milliseconds = (time % 1000).toString().padStart(3, '0').slice(0, 2);
return `${hours}:${minutes}:${seconds}.${milliseconds}`;
};

return (


// List of the Laps using FlatList
<View style={styles.lapListContainer}>
<FlatList
data={reversedLaps}
renderItem={renderItem}
keyExtractor={(item, index) => index.toString()}
style={styles.lapList}
/>
</View>
);

};

// LapList Styles
const styles = StyleSheet.create({
lapListContainer: {
marginTop: 10,
flex: 1,
width: '100%',

},

lapList: {
width: '100%',
},

lapItem: {
padding: 10,
flexDirection: 'row', // Use row layout to align lap number and time horizontally
justifyContent: 'space-between',
borderBottomWidth: 1,
borderBottomColor: '#ccc',

},

lapItemText: {
fontWeight: '300',
paddingLeft: 20,
paddingRight: 20,
color: '#383838',
fontSize: 20,


},

});
72 changes: 68 additions & 4 deletions src/StopWatch.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,72 @@
import { View } from 'react-native';
import React from 'react';
import { Text, View, StyleSheet } from 'react-native';

// Declares props that will be used by the StopWatch
type Props = {
// Variable that keeps track of the current stopwatch count
stopWatchCount: number;
};

export const StopWatch: React.FC<Props> = ({ stopWatchCount }) => {
// Hours Count
const hours = Math.floor(stopWatchCount / (60 * 60 * 1000)).toString().padStart(2, '0');

// Minutes Count
const minutes = Math.floor((stopWatchCount % (60 * 60 * 1000)) / (60 * 1000)).toString().padStart(2, '0');

// Seconds Count
const seconds = Math.floor((stopWatchCount % (60 * 1000)) / 1000).toString().padStart(2, '0');

// Milliseconds Count
const milliseconds = (stopWatchCount % 1000).toString().padStart(3, '0').slice(0, 2);

export default function StopWatch() {
return (
<View >
// Display the current stopwatch time in HH:MM:SS format
<View style={styles.stopWatchCountContainer}>
<Text style={styles.timeText}>{hours}</Text>
<Text style={styles.separator}>:</Text>
<Text style={styles.timeText}>{minutes}</Text>
<Text style={styles.separator}>:</Text>
<Text style={styles.timeText}>{seconds}</Text>
<Text style={styles.separator}>.</Text>
<Text style={styles.timeTextMS}>{milliseconds}</Text>
</View>
);
}
};

// StopWatch Styles
const styles = StyleSheet.create({
// StopWatch Container
stopWatchCountContainer: {
paddingTop: 100,
flexDirection: 'row',
alignItems: 'center',
},

// Time Text (Hours, Mins, Secs)
timeText: {
fontSize: 50,
fontWeight: '700',
width: 70, // Adjusted width for better spacing
textAlign: 'center',
color: '#383838',
},

// Separator
separator: {
fontSize: 50,
fontWeight: '700',
color: '#383838',
},

// Miliseconds Text
timeTextMS: {
fontSize: 20,
fontWeight: '700',
width: 30, // Adjusted width for better spacing
textAlign: 'center',
color: '#383838',
marginTop: 10
},

});
106 changes: 98 additions & 8 deletions src/StopWatchButton.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,98 @@
import { View } from 'react-native';

export default function StopWatchButton() {
return (
<View >
</View>
);
}
import React from 'react';
import { Pressable, View, StyleSheet, Text } from 'react-native';

// Declares props that will be used by the StopWatch Buttons
type Props = {

// Keeps track of whether stopwatch is running or not
isStopWatchRunning: boolean;

// Function that stops time
stopTime: () => void;

// Function that starts time
startTime: () => void;

// Function that resets the time
resetTime: () => void;

// Functions that add a lap to lap-list
addLap: () => void;

};

export const StopWatchButton: React.FC<Props> = ({
isStopWatchRunning,
stopTime,
startTime,
resetTime,
addLap,
}) => {

// Start/Stop button color that changes depending on state
const startStopButtonColor = isStopWatchRunning ? '#e37878' : '#57ba8d';

return (
<View style={styles.buttonsContainer}>

{/* Add lap / Reset Time Button */}
<Pressable
style={({ pressed }) => [
{
backgroundColor: pressed ? 'lightgray' : '#383838',
marginRight: 50,
},
styles.pressableButton,
]}
onPress={isStopWatchRunning ? addLap : resetTime}
>
<Text style={styles.buttonText}>{isStopWatchRunning ? 'Lap' : 'Reset'}</Text>
</Pressable>

{/* Start/Pause Button */}
<Pressable
style={({ pressed }) => [
{
backgroundColor: pressed ? 'lightgray' : startStopButtonColor,
},
styles.pressableButton,
]}
onPress={isStopWatchRunning ? stopTime : startTime}
>
<Text style={styles.buttonText}>{isStopWatchRunning ? 'Stop' : 'Start'}</Text>
</Pressable>
</View>

);
};


// StopWatch Buttons Styles
const styles = StyleSheet.create({

// Displays Buttons in a row
buttonsContainer: {
flexDirection: 'row',
alignItems: 'center',
marginTop: 10,
},

// Style for all buttons
pressableButton: {
elevation: 5,
borderRadius: 50,
width: 75,
height: 75,
alignItems: 'center',
justifyContent: 'center',
},

// Text style for buttons
buttonText: {
color: 'white',
fontSize: 20,
fontWeight: '400',
},


});
6 changes: 5 additions & 1 deletion tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
{
"extends": "@tsconfig/react-native/tsconfig.json",
"compilerOptions": {
"strict": true
"strict": true,
"outDir": "../build/app",
"module": "esnext",
"baseUrl": "",
"types": ["node"]
}
}