diff --git a/App.tsx b/App.tsx index 0329d0c..4c662ea 100644 --- a/App.tsx +++ b/App.tsx @@ -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(CURRENT_TIME); + const [timeInterval, setTimeInterval] = useState(null); + const [isStopWatchRunning, setIsStopWatchRunning] = useState(false); + const [laps, setLaps] = useState([]); + + + // 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 ( - Open up App.tsx to start working on your app! + + + ); } @@ -17,4 +87,5 @@ const styles = StyleSheet.create({ alignItems: 'center', justifyContent: 'center', }, -}); + +}); \ No newline at end of file diff --git a/src/LapList.tsx b/src/LapList.tsx new file mode 100644 index 0000000..aec1f2b --- /dev/null +++ b/src/LapList.tsx @@ -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 = ({ laps }) => { + const reversedLaps = [...laps].reverse(); + + const renderItem = ({ item, index }: { item: number; index: number }) => ( + + {`Lap ${reversedLaps.length - index}:`} + {formatTime(item)} + + ); + + // 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 + + index.toString()} + style={styles.lapList} + /> + + ); + + }; + + // 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, + + + }, + +}); \ No newline at end of file diff --git a/src/StopWatch.tsx b/src/StopWatch.tsx index 5c7eb74..3de8b34 100644 --- a/src/StopWatch.tsx +++ b/src/StopWatch.tsx @@ -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 = ({ 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 ( - + // Display the current stopwatch time in HH:MM:SS format + + {hours} + : + {minutes} + : + {seconds} + . + {milliseconds} ); -} \ No newline at end of file +}; + +// 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 + }, + +}); \ No newline at end of file diff --git a/src/StopWatchButton.tsx b/src/StopWatchButton.tsx index 8768555..74e0313 100644 --- a/src/StopWatchButton.tsx +++ b/src/StopWatchButton.tsx @@ -1,8 +1,98 @@ -import { View } from 'react-native'; - -export default function StopWatchButton() { - return ( - - - ); -} \ No newline at end of file +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 = ({ + isStopWatchRunning, + stopTime, + startTime, + resetTime, + addLap, + }) => { + + // Start/Stop button color that changes depending on state + const startStopButtonColor = isStopWatchRunning ? '#e37878' : '#57ba8d'; + + return ( + + + {/* Add lap / Reset Time Button */} + [ + { + backgroundColor: pressed ? 'lightgray' : '#383838', + marginRight: 50, + }, + styles.pressableButton, + ]} + onPress={isStopWatchRunning ? addLap : resetTime} + > + {isStopWatchRunning ? 'Lap' : 'Reset'} + + + {/* Start/Pause Button */} + [ + { + backgroundColor: pressed ? 'lightgray' : startStopButtonColor, + }, + styles.pressableButton, + ]} + onPress={isStopWatchRunning ? stopTime : startTime} + > + {isStopWatchRunning ? 'Stop' : 'Start'} + + + + ); + }; + + + // 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', + }, + + + }); \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json index a7b94da..8508b3d 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,6 +1,10 @@ { "extends": "@tsconfig/react-native/tsconfig.json", "compilerOptions": { - "strict": true + "strict": true, + "outDir": "../build/app", + "module": "esnext", + "baseUrl": "", + "types": ["node"] } }