diff --git a/App.tsx b/App.tsx
index 0329d0c..a076846 100644
--- a/App.tsx
+++ b/App.tsx
@@ -1,11 +1,11 @@
import { StatusBar } from 'expo-status-bar';
import { StyleSheet, Text, View } from 'react-native';
+import StopWatch from './src/components/StopWatch';
export default function App() {
return (
- Open up App.tsx to start working on your app!
-
+
);
}
@@ -13,8 +13,8 @@ export default function App() {
const styles = StyleSheet.create({
container: {
flex: 1,
- backgroundColor: '#fff',
- alignItems: 'center',
- justifyContent: 'center',
+ backgroundColor: "#0D0916",
+ alignItems: "center",
+ justifyContent: "center",
},
});
diff --git a/src/StopWatch.tsx b/src/StopWatch.tsx
deleted file mode 100644
index 5c7eb74..0000000
--- a/src/StopWatch.tsx
+++ /dev/null
@@ -1,8 +0,0 @@
-import { View } from 'react-native';
-
-export default function StopWatch() {
- return (
-
-
- );
-}
\ No newline at end of file
diff --git a/src/StopWatchButton.tsx b/src/StopWatchButton.tsx
deleted file mode 100644
index 8768555..0000000
--- a/src/StopWatchButton.tsx
+++ /dev/null
@@ -1,8 +0,0 @@
-import { View } from 'react-native';
-
-export default function StopWatchButton() {
- return (
-
-
- );
-}
\ No newline at end of file
diff --git a/src/components/StopWatch.tsx b/src/components/StopWatch.tsx
new file mode 100644
index 0000000..faa28cf
--- /dev/null
+++ b/src/components/StopWatch.tsx
@@ -0,0 +1,194 @@
+import { FlatList, StyleSheet, Text, View } from 'react-native';
+import StopWatchButton from './StopWatchButton';
+import { useEffect, useState } from 'react';
+import { formatTime } from '../utils/formatTime';
+import { LapItem } from '../types/LapItem';
+
+//renderLap function is used to render each lapItem into a row with the formatted time
+const renderLap = ({ item }: { item: LapItem }) => {
+ return (
+
+
+
+ {item.index.toString()}
+
+
+
+
+ {formatTime(item.lapTime)}
+
+
+
+
+ {formatTime(item.generalTime)}
+
+
+
+
+ );
+};
+
+export default function StopWatch() {
+ //States:
+ //startTime, the startTime of the stopwatch, necessary for precise milliseconds display
+ //time, the time in milliseconds passed from the beginning
+ //isTimerOn, a boolean state needed to set the timer on/off
+ //lapItems, a state array needed to add lapItems and display the laps
+ const [startTime, setStartTime] = useState(null);
+ const [time, setTime] = useState(0);
+ const [isTimerOn, setIsTimerOn] = useState(false);
+ const [lapItems, setLapItems] = useState([]);
+
+ //useEffect is needed here to update the time once the user presses Start or Stop
+ useEffect(() => {
+ let interval: number | null = null;
+
+ //Checks if the timer is on:
+ //If it is on: keep incrementing the time
+ if(isTimerOn){
+ if (startTime === null) {
+ setStartTime(Date.now() - time);
+ }
+
+ interval = setInterval(() => {
+ setTime(Date.now() - startTime!);
+ }, 1);
+ } else {
+ //If the timer is off: stop incrementing the time
+ setStartTime(null);
+ }
+
+ //Clear the interval on return
+ return () => clearInterval(interval!);
+ })
+
+ //reset the timer by resetting all time states and laps
+ function resetTimer(){
+ setTime(0);
+ setIsTimerOn(false);
+ setLapItems([]);
+ }
+
+ //function to add a lap item
+ function addLap(){
+ //Gets the latest lap item to get the time and the index, if there's no previous lap item, start at 0
+ const newestTime = lapItems.length > 0 ? lapItems[lapItems.length-1].generalTime : 0;
+ const newestIndex = lapItems.length > 0 ? lapItems[lapItems.length-1].index : 0;
+
+ //Create a new lap item using the latest lap item
+ //Increment the index and calculate the lap time using the time now - the time at which the previous lap was at
+ const newLapItem = {
+ index: newestIndex+1,
+ lapTime: time-newestTime,
+ generalTime: time
+ }
+
+ //Create a new laps array and add the new object
+ const newLaps = [
+ ...lapItems.slice(0, lapItems.length),
+ newLapItem,
+ ...lapItems.slice(lapItems.length)
+ ];
+
+ //Set the lap items to the new array
+ setLapItems(newLaps);
+ }
+
+ return (
+
+
+ {formatTime(time)}
+
+
+ {lapItems.length > 0 ?
+
+
+ Lap
+
+
+ Lap Time
+
+
+ Overall Time
+
+ :
+ null}
+
+
+
+
+ setIsTimerOn(true)} isDisabled={isTimerOn} buttonColor='#62a84f' buttonBorderColor='#326345'/>
+ setIsTimerOn(false)} isDisabled={!isTimerOn}>
+
+
+
+
+
+
+
+ );
+}
+
+const styles = StyleSheet.create({
+ container: {
+ justifyContent: "center",
+ alignItems: "center",
+ width: "100%",
+ height: "100%",
+ },
+ stopwatchContainer: {
+ justifyContent: "flex-end",
+ alignItems: "center",
+ height: "30%",
+ width: "90%",
+ },
+ lapsHeader: {
+ flexDirection: "row",
+ width: "100%",
+ marginBottom: 4,
+ },
+ lapsHeaderLeftSection: {
+ width: "20%",
+ },
+ lapsHeaderMiddleSection: {
+ width: "40%",
+ alignItems: "center",
+ },
+ lapsHeaderRightSection: {
+ width: "40%",
+ alignItems: "flex-end",
+ },
+ lapsContainer: {
+ height: "40%",
+ width: "80%",
+ },
+ lapItemContainer:{
+ flexDirection: "row",
+ alignItems: "center",
+ },
+ buttonsContainer:{
+ justifyContent: "flex-end",
+ alignItems: "center",
+ height: "30%",
+ width: "90%",
+ },
+ buttonsRowContainer: {
+ flexDirection: "row",
+ justifyContent: "space-between",
+ width: "80%",
+ marginBottom: 32,
+ },
+ stopwatchText: {
+ color: "#FAFAFA",
+ fontSize: 48,
+ },
+ text: {
+ color: "#FAFAFA",
+ fontSize: 16,
+ },
+})
\ No newline at end of file
diff --git a/src/components/StopWatchButton.tsx b/src/components/StopWatchButton.tsx
new file mode 100644
index 0000000..aa82837
--- /dev/null
+++ b/src/components/StopWatchButton.tsx
@@ -0,0 +1,66 @@
+import { StyleSheet, Text, TouchableOpacity, View } from 'react-native';
+import { StopWatchButtonProps } from '../types/ButtonProps';
+
+export default function StopWatchButton(
+ {
+ action,
+ onPress,
+ height = 40,
+ width = 80,
+ buttonColor = "#6BBDF3",
+ buttonBorderColor = "#4796D0",
+ borderRadius = 8,
+ isDisabled = false,
+ hitslop = { top: 20, bottom: 20, left: 20, right: 20 },
+ ...props
+ }
+ : StopWatchButtonProps) {
+
+ //Button title is just the action name capitalized
+ const buttonTitle = action.substring(0, 1).toUpperCase() + action.substring(1);
+
+ return (
+
+
+ null}
+ >
+ {buttonTitle}
+
+
+
+
+ );
+}
+
+const styles = StyleSheet.create({
+ container: {
+ width: "100%",
+ height: "100%",
+ },
+ button: {
+ width: "100%",
+ height: "100%",
+ justifyContent: "center",
+ alignItems: "center"
+ },
+ text: {
+ color: "#FAFAFA",
+ },
+});
diff --git a/src/types/ButtonProps.ts b/src/types/ButtonProps.ts
new file mode 100644
index 0000000..a8d8a57
--- /dev/null
+++ b/src/types/ButtonProps.ts
@@ -0,0 +1,28 @@
+import { DimensionValue, Insets } from "react-native";
+import { StopwatchButtonActionTypes } from "./StopwatchButtonActionTypes";
+
+//The stopwatch button takes in as input the following:
+//action: the type of the button, what it does
+//onPress: the function which is executes on press
+//height: adjustable height if needed
+//width: adjustable width if needed
+//buttonColor: adjustable button color if needed, default is light blue
+//buttonBorderColor: adjustable border color if needed, default is shade of light blue
+//isDisabled: boolean to disable the button if needed
+//hitslop: adjustable hitslop, default is 20
+export interface StopWatchButtonProps {
+ action: StopwatchButtonActionTypes
+ onPress(): void;
+
+ height?: DimensionValue;
+ width?: DimensionValue;
+
+ buttonColor?: string;
+ buttonBorderColor?: string;
+
+ borderRadius?: number;
+ bottomBorderWidth?: number;
+
+ isDisabled?: boolean;
+ hitslop?: Insets;
+}
\ No newline at end of file
diff --git a/src/types/LapItem.ts b/src/types/LapItem.ts
new file mode 100644
index 0000000..cc71e86
--- /dev/null
+++ b/src/types/LapItem.ts
@@ -0,0 +1,6 @@
+//LapItem contains the field needed to display the laps
+export type LapItem = {
+ index: number;
+ lapTime: number;
+ generalTime: number;
+}
\ No newline at end of file
diff --git a/src/types/StopwatchButtonActionTypes.ts b/src/types/StopwatchButtonActionTypes.ts
new file mode 100644
index 0000000..ef55fcc
--- /dev/null
+++ b/src/types/StopwatchButtonActionTypes.ts
@@ -0,0 +1,6 @@
+//The different types of buttons
+export type StopwatchButtonActionTypes =
+ | "start"
+ | "stop"
+ | "reset"
+ | "lap";
\ No newline at end of file
diff --git a/src/utils/formatTime.tsx b/src/utils/formatTime.tsx
new file mode 100644
index 0000000..4ff576f
--- /dev/null
+++ b/src/utils/formatTime.tsx
@@ -0,0 +1,9 @@
+//Function which formats the time from milliseconds to HH:MM:SS.(mls)
+export function formatTime(millis: number): string {
+ const hours = Math.floor((millis/1000)/3600);
+ const minutes = Math.floor(((millis/1000) - (hours*3600))/60);
+ const seconds = Math.floor((millis/1000)%60);
+ const millisFormatted = millis%1000;
+
+ return `${hours.toString().padStart(2, "0")}:${minutes.toString().padStart(2, "0")}:${seconds.toString().padStart(2, "0")}.${millisFormatted.toString().padStart(3, "0")}`;
+}
\ No newline at end of file
diff --git a/tests/Stopwatch.test.js b/tests/Stopwatch.test.js
index d5e9f1f..584f38a 100644
--- a/tests/Stopwatch.test.js
+++ b/tests/Stopwatch.test.js
@@ -1,6 +1,6 @@
import React from 'react';
import { render, fireEvent } from '@testing-library/react-native';
-import Stopwatch from '../src/Stopwatch';
+import Stopwatch from '../src/components/StopWatch';
describe('Stopwatch', () => {
test('renders initial state correctly', () => {