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
14 changes: 7 additions & 7 deletions App.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
import { StatusBar } from 'expo-status-bar';
import { StyleSheet, Text, View } from 'react-native';
import { ScrollView, StyleSheet, View } from 'react-native';
import StopWatch from './src/StopWatch';

export default function App() {

return (
<View style={styles.container}>
<Text>Open up App.tsx to start working on your app!</Text>
<StatusBar style="auto" />
<StopWatch />
</View>
);
}

const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
backgroundColor: '#000',
alignItems: 'center',
justifyContent: 'center',
justifyContent: 'flex-start',
},
});
})
95 changes: 11 additions & 84 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,84 +1,11 @@
# Technical Instructions
1. Fork this repo to your local Github account.
2. Create a new branch to complete all your work in.
3. Test your work using the provided tests
4. Create a Pull Request against the Shopify Main branch when you're done and all tests are passing

# Project Overview
The goal of this project is to implement a stopwatch application using React Native and TypeScript. The stopwatch should have the following functionality:

- Start the stopwatch to begin counting time.
- Stop the stopwatch to pause the timer.
- Displays Laps when a button is pressed.
- Reset the stopwatch to zero.

You will be provided with a basic project structure that includes the necessary files and dependencies. Your task is to write the code to implement the stopwatch functionality and ensure that it works correctly.

## Project Setup
To get started with the project, follow these steps:

1. Clone the project repository to your local development environment.

2. Install the required dependencies by running npm install in the project directory.

3. Familiarize yourself with the project structure. The main files you will be working with are:
- /App.tsx: The main component that renders the stopwatch and handles its functionality.
- src/Stopwatch.tsx: A separate component that represents the stopwatch display.
- src/StopwatchButton.tsx: A separate component that represents the start, stop, and reset buttons.

4. Review the existing code in the above files to understand the initial structure and component hierarchy.

## Project Goals
Your specific goals for this project are as follows:

1. Implement the stopwatch functionality:
- The stopwatch should start counting when the user clicks the start button.
- The stopwatch should stop counting when the user clicks the stop button.
- The stopwatch should reset to zero when the user clicks the reset button.
- The stopwatch should record and display laps when user clicks the lap button.

2. Ensure code quality:
- Write clean, well-structured, and maintainable code.
- Follow best practices and adhere to the React and TypeScript coding conventions.
- Pay attention to code readability, modularity, and performance.

3. Test your code:
- Run the application and test the stopwatch functionality to ensure it works correctly.
- Verify that the stopwatch starts, stops, resets, and records laps as expected.

4. Code documentation:
- Document your code by adding comments and explanatory notes where necessary.
- Provide clear explanations of the implemented functionality and any important details.

5. Version control:
- Use Git for version control. Commit your changes regularly and push them to a branch in your forked repository.

6. Create a Pull Request:
- Once you have completed the project goals, create a pull request to merge your changes into the main repository.
- Provide a clear description of the changes made and any relevant information for the code review.

## Getting Started
To start working on the project, follow these steps:

1. Clone the repository to your local development environment.

2. Install the required dependencies by running npm install in the project directory.

3. Open the project in your preferred code editor.

4. Review the existing code in the src directory to understand the initial structure and component hierarchy.

5. Implement the stopwatch functionality by modifying the necessary components (App.tsx, Stopwatch.tsx, StopwatchButton.tsx).

6. Run the application using npm start and test the stopwatch functionality.

7. Commit your changes regularly and push them to a branch in your forked repository.

8. Once you have completed the project goals, create a pull request to merge your changes into the main repository.

## Resources
Here are some resources that may be helpful during your work on this project:

- [TypeScript Documentation](https://www.typescriptlang.org/docs/) - Official documentation for TypeScript, offering guidance on TypeScript features and usage.

- [React Testing Library](https://testing-library.com/docs/react-testing-library/intro/) - Explore React Testing Library, a popular testing library for React applications.
## Implementation
- Start, Stop, Resume, Lap, Reset features all working
- Color coding for fastest and slowest laps implemented
- Laps accurate to millisecond, whereas only centiseconds displayed on stopwatch

## Testing
- Test functions modified to exclude centiseconds
- This was due to the fact that the difference in width of digits i.e. 1 and 9 would cause a bouncing effect on screen when the stopwatch was run
- This was solved by putting the centisecond digits into separate Text components of fixed width
- An alternate solution would have been to use a monospace font but there were no visually appealing monospace fonts available without importing
- Other than that, tests worked as expected with minor adjustments
189 changes: 186 additions & 3 deletions src/StopWatch.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,191 @@
import { View } from 'react-native';
import React, { useState, useEffect } from 'react';
import { View, Text, StyleSheet, StatusBar, ScrollView, FlatList, ListRenderItem, SafeAreaView, Image } from 'react-native';
import StopwatchButton from './StopWatchButton';
import { LapData } from './types';
import { formatLapTime, formatTime } from './utils/formatTime';
import StopwatchDigit from './StopWatchDigit';

export default function StopWatch() {

// State for tracking whether the stopwatch is running
const [isRunning, setIsRunning] = useState(false);

// State for storing the start time
const [startTime, setStartTime] = useState(Date.now());

// State for storing the elapsed time
const [elapsedTime, setElapsedTime] = useState(0);

// State for storing the interval ID
const [intervalId, setIntervalId] = useState(0);

// State for storing lap, lap start time, lap number
const [laps, setLaps] = useState<LapData[]>([]);
const [lapStartTime, setLapStartTime] = useState(Date.now())
const [lapNumber, setLapNumber] = useState(1)

// Effect hook for handling the stopwatch functionality
useEffect(() => {
if (isRunning) {
// Set start time if not already set
setStartTime(prevStartTime => prevStartTime || new Date().getTime());

// Create an interval that updates the elapsed time
const id = setInterval(() => {
setElapsedTime(new Date().getTime() - startTime);
}, 1);
setIntervalId(id);
} else {
// Clear the interval when stopwatch stops
clearInterval(intervalId);
setIntervalId(0);
setStartTime(0);
}
return () => clearInterval(intervalId);
}, [isRunning, startTime]);

// Event handler for reset button
const handleReset = () => {
setIsRunning(false);
setElapsedTime(0);
setLaps([]);
setLapNumber(1)
if (intervalId) {
clearInterval(intervalId);
setIntervalId(0);
}
};

// Event handler for start/stop button
const handleStartStop = () => {
setIsRunning(!isRunning);
if (!isRunning) {
setStartTime(new Date().getTime() - elapsedTime);
}
};

// Event handler for lap button
const handleLap = () => {
// Save the lap time difference
const lapTime = new Date().getTime() - (laps.length == 0 ? startTime : lapStartTime);
setLapNumber(lapNumber+1)
setLaps(prevLaps => {
const updatedLaps = [...prevLaps, { time: lapTime, number: lapNumber }];

// Check if there are more than 10 laps, remove the first lap
if (updatedLaps.length > 10) {
updatedLaps.shift();
}

return updatedLaps;
});

// Update the startTime for the next lap
setLapStartTime(new Date().getTime());
};

// Find fastest and slowest laps
const fastestLap = laps.length > 0 ? Math.min(...laps.map(lap => lap.time)) : 0;
const slowestLap = laps.length > 0 ? Math.max(...laps.map(lap => lap.time)) : 0;

// Destructure formatted time into its components
const { minutes_and_seconds, centisecondsArray } = formatTime(elapsedTime);

return (
<View >
<View>
<View style={styles.container}>
<StatusBar light-content />
<View style={styles.display}>
<Text style={styles.punct}>{minutes_and_seconds}</Text>
{centisecondsArray.map((digit, index) => (
<StopwatchDigit key={`centisecond-${index}`} value={digit} adjust={false} />
))}
</View>
<View style={styles.buttons}>
<StopwatchButton title={ isRunning ? 'Pause' : (elapsedTime == 0 ? 'Start' : 'Resume') } onPress={handleStartStop} />
<StopwatchButton title={isRunning ? 'Lap' : 'Reset'} onPress={isRunning ? handleLap : handleReset} />
</View>
</View>
<View testID='lap-list' style={styles.lapContainer}>
{laps.map((lap, index) => (
<View style={[styles.lapView, (laps[index+1] ? null : styles.lapBorder)]} key={index}>
<Text style={styles.lap}>Lap {lap.number}:</Text>
<Text style={[styles.lap, index == laps.map(lap => lap.time).indexOf(fastestLap) && styles.fastestLap,
index == laps.map(lap => lap.time).indexOf(slowestLap) && styles.slowestLap]}>{formatLapTime(lap.time)}</Text>
</View>
))}
</View>
</View>
);
}
}

// StyleSheet for the component
const styles = StyleSheet.create({
container: {
flex: 1,
flexDirection: 'column',
backgroundColor: '#000',
alignItems: 'center',
justifyContent: 'center',
},

display: {
flexDirection: 'row',
backgroundColor: '#000',
alignItems: 'center',
justifyContent: 'space-between',
marginTop: 100,
},

buttons: {
flexDirection: 'row',
backgroundColor: '#000',
alignItems: 'center',
justifyContent: 'space-around',
marginTop: 50,
gap: 150,
},

punct: {
fontSize: 90,
fontWeight: '100',
color: '#fff',
},

lapContainer: {
flex: 1,
flexDirection: 'column-reverse',
backgroundColor: '#000',
alignItems: 'center',
justifyContent: 'flex-end',
},

lapView: {
flexDirection: 'row',
backgroundColor: '#000',
alignItems: 'center',
justifyContent: 'space-between',
width: 350,
height: 40,
borderBottomWidth: 1,
borderBottomColor: 'rgba(255, 255, 255, 0.2)',
},

lapBorder: {
borderTopWidth: 1,
borderTopColor: 'rgba(255, 255, 255, 0.2)',
},

lap: {
color: '#fff',
fontSize: 16,
},

fastestLap: {
color: 'green',
},
slowestLap: {
color: 'red',
},

});
37 changes: 32 additions & 5 deletions src/StopWatchButton.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,35 @@
import { View } from 'react-native';
import React from 'react';
import { TouchableOpacity, Text, StyleSheet } from 'react-native';

export default function StopWatchButton() {
type StopwatchButtonProps = {
title: string,
onPress: () => void,
}

const StopwatchButton = ({ title, onPress }: StopwatchButtonProps) => {
return (
<View >
</View>
<TouchableOpacity onPress={onPress} style={[styles.button]}>
<Text style={[styles.text]}>{title}</Text>
</TouchableOpacity>
);
}
};

const styles = StyleSheet.create({
button: {
flex: 0,
alignItems: 'center',
justifyContent: 'center',
width: 100,
height: 100,
borderRadius: 50,
borderColor: 'white',
borderWidth: 1,
},
text: {
// Default text styles
color: 'white',
fontSize: 20,
},
});

export default StopwatchButton;
21 changes: 21 additions & 0 deletions src/StopWatchDigit.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import React from 'react';
import { Text, StyleSheet } from 'react-native';
import { StopwatchDigitProps } from './types';

const StopwatchDigit = ({ value, adjust }: StopwatchDigitProps) => (
<Text style={[styles.digit, value == '1' && adjust ? styles.one : null]}>{value}</Text>
);

const styles = StyleSheet.create({
digit: {
width: 50,
fontSize: 90,
fontWeight: '100',
color: '#fff',
},
one: {
width: 30,
},
});

export default StopwatchDigit;
9 changes: 9 additions & 0 deletions src/types/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export type LapData = {
time: number,
number: number,
}

export type StopwatchDigitProps = {
value: string,
adjust: boolean,
};
Loading