diff --git a/App.js b/App.js index e4635ef3..39b00748 100644 --- a/App.js +++ b/App.js @@ -11,6 +11,7 @@ import { Touchable, Pressable, } from "react-native"; +import { GestureHandlerRootView } from "react-native-gesture-handler"; import { NavigationContainer } from "@react-navigation/native"; import { createNativeStackNavigator } from "@react-navigation/native-stack"; import Calculator from "./src/screens/Calculator/calculator"; @@ -22,7 +23,7 @@ import BookFinder from "./src/screens/BookFinder/BookFinder"; import ToDoList from "./src/screens/ToDoList/toDoList"; import AddTaskScreen from "./src/screens/ToDoList/addTask"; import QRScanner from "./src/screens/QRScanner/qrScanner"; -import MovieFinder from './src/screens/MovieFinder/MovieFinder'; +import MovieFinder from "./src/screens/MovieFinder/MovieFinder"; import HexColorGeneratorScreen from "./src/screens/HexColorGenerator/HexColorGeneratorScreen"; import QRCodeGenerator from "./src/screens/QRGenerator/QRGenerator"; import RecipeFinder from "./src/screens/RecipeFinder/RecipeFinder"; @@ -33,159 +34,166 @@ import Pokedex from "./src/screens/Pokedex/Pokedex"; import AddWorkout from "./src/screens/FitnessTracker/AddWorkout"; import WorkoutOverview from "./src/screens/FitnessTracker/WorkoutOverview"; import CodingQuiz from "./src/screens/CodingQuiz/CodingQuiz"; - +import TimerHome from "./src/screens/Timer/TimerHome"; export default function App() { const Stack = createNativeStackNavigator(); return ( - - - - - - - - + + + + + + + + + - + - - + + - + - + - - - - - - - + + + + + + + - + + headerTintColor: "#00ff99", + headerTitleStyle: { + fontWeight: "bold", + }, + cardStyle: { backgroundColor: "#121212" }, + animationEnabled: true, + animationTypeForReplace: "push", + transitionSpec: { + open: { + animation: "timing", + config: { duration: 300 }, + }, + close: { + animation: "timing", + config: { duration: 300 }, + }, + }, + }} + /> - - + + + ); -} \ No newline at end of file +} diff --git a/README.md b/README.md index 27decc0b..9223219b 100644 --- a/README.md +++ b/README.md @@ -34,40 +34,38 @@ QR Scanner QR Generator Tic Tac Toe + Timer App + -

🧐 Features 🧐

Here're some of the project's best features: -* Beginner friendly projects -* Easy to understand code -* Implementation of all the fundamental concepts of React Native +- Beginner friendly projects +- Easy to understand code +- Implementation of all the fundamental concepts of React Native -

💻 Built with 💻

Technologies used in the project: -* React Native Expo -* Tailwind CSS - +- React Native Expo +- Tailwind CSS
-

🛠️ Installation Steps: 🛠️

1. Clone the repository

@@ -91,7 +89,7 @@ code .

4. Install required packages

``` -npm install +npm install ```

5. Start the deployment server

@@ -117,11 +115,13 @@ npx expo start ``` npx expo install --fix ``` +

3. If there is an issue to use commands containing 'expo' then, the following command would help in fixing the issue.

``` npm install -g react-native-cli ``` +

4. If there is an error while using the QR Scanner or QR Generator try to use the below given commands.

``` diff --git a/output/Timer App (1).jpg b/output/Timer App (1).jpg new file mode 100644 index 00000000..399c36d8 Binary files /dev/null and b/output/Timer App (1).jpg differ diff --git a/output/Timer App (2).jpg b/output/Timer App (2).jpg new file mode 100644 index 00000000..b45d860c Binary files /dev/null and b/output/Timer App (2).jpg differ diff --git a/output/Timer App (3).jpg b/output/Timer App (3).jpg new file mode 100644 index 00000000..3fc3f176 Binary files /dev/null and b/output/Timer App (3).jpg differ diff --git a/output/Timer App (4).jpg b/output/Timer App (4).jpg new file mode 100644 index 00000000..078fb20d Binary files /dev/null and b/output/Timer App (4).jpg differ diff --git a/output/Timer App (5).jpg b/output/Timer App (5).jpg new file mode 100644 index 00000000..1343803b Binary files /dev/null and b/output/Timer App (5).jpg differ diff --git a/output/Timer App (6).jpg b/output/Timer App (6).jpg new file mode 100644 index 00000000..2f8fa558 Binary files /dev/null and b/output/Timer App (6).jpg differ diff --git a/output/Timer App (7).jpg b/output/Timer App (7).jpg new file mode 100644 index 00000000..e94b34af Binary files /dev/null and b/output/Timer App (7).jpg differ diff --git a/output/Timer App (8).jpg b/output/Timer App (8).jpg new file mode 100644 index 00000000..41356553 Binary files /dev/null and b/output/Timer App (8).jpg differ diff --git a/package-lock.json b/package-lock.json index d431dc6a..4bd12628 100644 --- a/package-lock.json +++ b/package-lock.json @@ -30,13 +30,13 @@ "react-native-keyboard-aware-scroll-view": "^0.9.5", "react-native-paper": "^5.12.3", "react-native-qrcode-svg": "^6.3.1", - "react-native-reanimated": "~3.10.1", - "react-native-safe-area-context": "4.10.1", - "react-native-screens": "3.31.1", - "react-native-svg": "15.2.0", + "react-native-reanimated": "~3.3.0", + "react-native-safe-area-context": "4.6.3", + "react-native-screens": "~3.22.0", + "react-native-svg": "13.9.0", "react-native-toast-message": "^2.2.0", "react-native-vector-icons": "^10.1.0", - "react-native-view-shot": "^3.8.0" + "react-native-view-shot": "3.7.0" }, "devDependencies": { "@babel/core": "^7.20.0", @@ -4814,9 +4814,16 @@ "react-refresh": "^0.14.0" }, "engines": { - "node": ">=18" - }, + "node": ">= 8" + } + }, + "node_modules/@react-native-picker/picker": { + "version": "2.4.10", + "resolved": "https://registry.npmjs.org/@react-native-picker/picker/-/picker-2.4.10.tgz", + "integrity": "sha512-EvAlHmPEPOwvbP6Pjg/gtDV3XJzIjIxr10fXFNlX5r9HeHw582G1Zt2o8FLyB718nOttgj8HYUTGxvhu4N65sQ==", "peerDependencies": { + "react": ">=16", + "react-native": ">=0.57", "@babel/core": "*" } }, @@ -12625,7 +12632,6 @@ "version": "6.3.1", "resolved": "https://registry.npmjs.org/react-native-qrcode-svg/-/react-native-qrcode-svg-6.3.1.tgz", "integrity": "sha512-A6NoYnxzG7CXAzwPXfqCkwJpTIlmhpV+woflNBBXrxUdX4NeB83iKMs5F1kdx5IOIwsrQUbCrpnJWarCqg1X2Q==", - "license": "MIT", "dependencies": { "prop-types": "^15.8.0", "qrcode": "^1.5.1" @@ -12766,9 +12772,9 @@ } }, "node_modules/react-native-view-shot": { - "version": "3.8.0", - "resolved": "https://registry.npmjs.org/react-native-view-shot/-/react-native-view-shot-3.8.0.tgz", - "integrity": "sha512-4cU8SOhMn3YQIrskh+5Q8VvVRxQOu8/s1M9NAL4z5BY1Rm0HXMWkQJ4N0XsZ42+Yca+y86ISF3LC5qdLPvPuiA==", + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/react-native-view-shot/-/react-native-view-shot-3.7.0.tgz", + "integrity": "sha512-tQruLNjs7Ee/p6xUgJqF6glnatHaq/UqaIQ6KdYIFG0+XpUZdhqmEM4WMLsYfayfFEhdlF86G1S3eXMOfDNzFg==", "dependencies": { "html2canvas": "^1.4.1" }, diff --git a/package.json b/package.json index dbec95a7..ba73c83f 100644 --- a/package.json +++ b/package.json @@ -31,13 +31,13 @@ "react-native-keyboard-aware-scroll-view": "^0.9.5", "react-native-paper": "^5.12.3", "react-native-qrcode-svg": "^6.3.1", - "react-native-reanimated": "~3.10.1", - "react-native-safe-area-context": "4.10.1", - "react-native-screens": "3.31.1", - "react-native-svg": "15.2.0", + "react-native-reanimated": "~3.3.0", + "react-native-safe-area-context": "4.6.3", + "react-native-screens": "~3.22.0", + "react-native-svg": "13.9.0", "react-native-toast-message": "^2.2.0", "react-native-vector-icons": "^10.1.0", - "react-native-view-shot": "^3.8.0" + "react-native-view-shot": "3.7.0" }, "devDependencies": { "@babel/core": "^7.20.0", diff --git a/src/screens/Home/home.js b/src/screens/Home/home.js index cb91624f..9e827319 100644 --- a/src/screens/Home/home.js +++ b/src/screens/Home/home.js @@ -81,21 +81,35 @@ export default function Home() { Calculator + { + console.log("Button to Calculator is Pressed!"); + navigation.navigate("Calculator"); + }} + > + Calculator + - { - console.log("Button to Calculator is Pressed!") - navigation.navigate("Calculator") - })}>Calculator - - { - console.log("Button to Movie Finder is Pressed!") - navigation.navigate("Movie Finder") - })}>Movie Finder + { + console.log("Button to Movie Finder is Pressed!"); + navigation.navigate("Movie Finder"); + }} + > + Movie Finder + - { - console.log("Button to Color Generate is Pressed!") - navigation.navigate("Hex Color") - })}>Color Generator + { + console.log("Button to Color Generate is Pressed!"); + navigation.navigate("Hex Color"); + }} + > + Color Generator + Add Workout + { @@ -186,6 +201,15 @@ export default function Home() { > Quiz Session + { + console.log("Button to Clock App is Pressed!"); + navigation.navigate("Timer App"); + }} + > + Clock + @@ -240,4 +264,4 @@ const styles = StyleSheet.create({ margin: 20, width: "100%", }, -}); \ No newline at end of file +}); diff --git a/src/screens/Timer/AnalogClock.js b/src/screens/Timer/AnalogClock.js new file mode 100644 index 00000000..6d0968a6 --- /dev/null +++ b/src/screens/Timer/AnalogClock.js @@ -0,0 +1,126 @@ +import React, { useState, useEffect } from "react"; +import { View, StyleSheet, Dimensions, Text } from "react-native"; +import Svg, { Circle, Line } from "react-native-svg"; + +const AnalogClock = () => { + // State to hold current date and time + const [date, setDate] = useState(new Date()); + + useEffect(() => { + const interval = setInterval(() => { + setDate(new Date()); + }, 1000); + return () => clearInterval(interval); + }, []); + + // Dimensions and center coordinates of the clock + const radius = Dimensions.get("window").width / 2 - 20; // Radius of the clock + const center = { + x: Dimensions.get("window").width / 2, + y: Dimensions.get("window").height / 2, + }; + + // Calculate angles for second, minute, and hour hands + const secondAngle = (date.getSeconds() / 60) * 360; + const minuteAngle = + (date.getMinutes() / 60) * 360 + (date.getSeconds() / 60) * 6; + const hourAngle = + ((date.getHours() % 12) / 12) * 360 + (date.getMinutes() / 60) * 30; + + const toRadians = (degrees) => degrees * (Math.PI / 180); + + const secondHand = { + x1: center.x, + y1: center.y, + x2: center.x + radius * 0.9 * Math.cos(toRadians(secondAngle - 90)), + y2: center.y + radius * 0.9 * Math.sin(toRadians(secondAngle - 90)), + }; + + const minuteHand = { + x1: center.x, + y1: center.y, + x2: center.x + radius * 0.75 * Math.cos(toRadians(minuteAngle - 90)), + y2: center.y + radius * 0.75 * Math.sin(toRadians(minuteAngle - 90)), + }; + + const hourHand = { + x1: center.x, + y1: center.y, + x2: center.x + radius * 0.5 * Math.cos(toRadians(hourAngle - 90)), + y2: center.y + radius * 0.5 * Math.sin(toRadians(hourAngle - 90)), + }; + + // Format time to HH:mm:ss + const formattedTime = `${date.getHours().toString().padStart(2, "0")}:${date + .getMinutes() + .toString() + .padStart(2, "0")}:${date.getSeconds().toString().padStart(2, "0")}`; + + return ( + + + {/* Draw clock face */} + + + {/* Second hand */} + + + {/* Minute hand */} + + + {/* Hour hand */} + + + {formattedTime} + + ); +}; + +const styles = StyleSheet.create({ + container: { + flex: 1, + justifyContent: "center", + alignItems: "center", + }, + digitalTime: { + fontSize: 45, + margin: 40, + fontWeight: "bold", + position: "absolute", + color: "whitesmoke", + top: 20, + }, +}); + +export default AnalogClock; diff --git a/src/screens/Timer/CountDown.js b/src/screens/Timer/CountDown.js new file mode 100644 index 00000000..eb3f2d75 --- /dev/null +++ b/src/screens/Timer/CountDown.js @@ -0,0 +1,169 @@ +import React, { useState, useEffect, useRef } from "react"; +import { View, Text, TextInput, Button, StyleSheet, Alert } from "react-native"; + +const Countdown = () => { + const [hours, setHours] = useState(""); + const [minutes, setMinutes] = useState(""); + const [seconds, setSeconds] = useState(""); + const [time, setTime] = useState(0); + const [isRunning, setIsRunning] = useState(false); + const intervalRef = useRef(null); + const endTimeRef = useRef(null); + + const handleStart = () => { + const hoursInMs = parseInt(hours, 10) * 3600 * 1000 || 0; + const minutesInMs = parseInt(minutes, 10) * 60 * 1000 || 0; + const secondsInMs = parseInt(seconds, 10) * 1000 || 0; + const totalTime = hoursInMs + minutesInMs + secondsInMs; + + if (totalTime <= 0) { + Alert.alert("Please set a valid time!"); + handleReset(); + return; + } + + setTime(totalTime); + endTimeRef.current = Date.now() + totalTime; + setIsRunning(true); + }; + + useEffect(() => { + if (isRunning) { + // Start interval to update countdown every 100 milliseconds + intervalRef.current = setInterval(() => { + const remainingTime = endTimeRef.current - Date.now(); + if (remainingTime <= 0) { + // Countdown finished + clearInterval(intervalRef.current); + setIsRunning(false); + Alert.alert("Time is up!"); + setTime(0); + handleReset(); + } else { + // Update remaining time + setTime(remainingTime); + } + }, 100); + } else { + clearInterval(intervalRef.current); + } + return () => clearInterval(intervalRef.current); + }, [isRunning]); + + const handleReset = () => { + setIsRunning(false); + setTime(0); + setHours(""); + setMinutes(""); + setSeconds(""); + }; + + const formatTime = (milliseconds) => { + const totalSeconds = Math.floor(milliseconds / 1000); + const getSeconds = `0${totalSeconds % 60}`.slice(-2); + const totalMinutes = Math.floor(totalSeconds / 60); + const getMinutes = `0${totalMinutes % 60}`.slice(-2); + const getHours = `0${Math.floor(totalMinutes / 60)}`.slice(-2); + + return `${getHours}:${getMinutes}:${getSeconds}`; + }; + + const handleMinutesChange = (value) => { + const intValue = parseInt(value, 10); + if (intValue >= 60) { + Alert.alert("Minutes cannot be 60 or more!"); + setMinutes(""); + } else { + setMinutes(value); + } + }; + + const handleSecondsChange = (value) => { + const intValue = parseInt(value, 10); + if (intValue >= 60) { + Alert.alert("Seconds cannot be 60 or more!"); + setSeconds(""); + } else { + setSeconds(value); + } + }; + + return ( + + {formatTime(time)} + + + : + + : + + + +