diff --git a/.eslintrc.js b/.eslintrc.js index 32f1383..d01e437 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -3,6 +3,9 @@ module.exports = { extends: ['expo', 'prettier', 'eslint:recommended'], plugins: ['prettier', '@typescript-eslint'], parser: '@typescript-eslint/parser', + env: { + node: true, + }, rules: { // add project-specific linting rules here 'prettier/prettier': 'error', diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 5b386aa..59b7f4e 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -31,5 +31,4 @@ [//]: # "Add related PRs you're waiting on/ PRs that will conflict, etc; if this is a refactor, feel free to add PRs that previously modified this code" - CC: @christophertorres1 diff --git a/App.tsx b/App.tsx index a1acf3d..2dd82df 100644 --- a/App.tsx +++ b/App.tsx @@ -1,22 +1,35 @@ -import { StyleSheet, Text, View } from 'react-native'; -import { StatusBar } from 'expo-status-bar'; -import Logo from '@/components/Logo'; +import * as React from 'react'; +import { NavigationContainer } from '@react-navigation/native'; +import { createNativeStackNavigator } from '@react-navigation/native-stack'; +import QRCodeScanner from '@/components/QRCodeScanner/QRCodeScanner'; +import HomeScreen from '@/screens/Home/Home'; +import TreeInfoPage from '@/screens/TreeInfo/TreeInfo'; +import { RootStackParamList } from '@/types/navigation'; -export default function App() { +const Stack = createNativeStackNavigator(); + +function App() { return ( - - - Open up App.tsx to start working on your app! - - + + + + + + + ); } -const styles = StyleSheet.create({ - container: { - flex: 1, - backgroundColor: '#fff', - alignItems: 'center', - justifyContent: 'center', - }, -}); +export default App; diff --git a/app.json b/app.json index 045662d..7f3e770 100644 --- a/app.json +++ b/app.json @@ -7,6 +7,14 @@ "orientation": "portrait", "icon": "./assets/bp-icon.png", "userInterfaceStyle": "light", + "plugins": [ + [ + "expo-camera", + { + "cameraPermission": "Allow Our City Forest to access your camera to scan QR codes." + } + ] + ], "splash": { "image": "./assets/bp-splash.png", "resizeMode": "contain", diff --git a/package-lock.json b/package-lock.json index 0e64512..029d93f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,30 +1,30 @@ { - "name": "mobile-app-template", + "name": "our-city-forest", "version": "1.0.0", "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "mobile-app-template", + "name": "our-city-forest", "version": "1.0.0", "dependencies": { "@react-native-async-storage/async-storage": "^2.0.0", "@react-navigation/native": "^6.1.18", "@react-navigation/native-stack": "^6.11.0", "@supabase/supabase-js": "^2.45.4", - "expo": "~51.0.21", + "expo": "~51.0.36", "expo-auth-session": "~5.5.2", - "expo-barcode-scanner": "~13.0.1", "expo-camera": "~15.0.16", "expo-constants": "~16.0.2", "expo-device": "~6.0.2", - "expo-linking": "~6.3.1", "expo-status-bar": "~1.12.1", "react": "18.2.0", "react-native": "^0.74.5", "react-native-dotenv": "^3.4.11", "react-native-gesture-handler": "^2.20.0", "react-native-reanimated": "^3.15.4", + "react-native-safe-area-context": "4.10.5", + "react-native-screens": "3.31.1", "react-native-svg": "^15.7.1", "react-native-toast-message": "^2.2.1", "react-native-url-polyfill": "^2.0.0", @@ -2285,9 +2285,9 @@ } }, "node_modules/@expo/cli": { - "version": "0.18.29", - "resolved": "https://registry.npmjs.org/@expo/cli/-/cli-0.18.29.tgz", - "integrity": "sha512-X810C48Ss+67RdZU39YEO1khNYo1RmjouRV+vVe0QhMoTe8R6OA3t+XYEdwaNbJ5p/DJN7szfHfNmX2glpC7xg==", + "version": "0.18.30", + "resolved": "https://registry.npmjs.org/@expo/cli/-/cli-0.18.30.tgz", + "integrity": "sha512-V90TUJh9Ly8stYo8nwqIqNWCsYjE28GlVFWEhAFCUOp99foiQr8HSTpiiX5GIrprcPoWmlGoY+J5fQA29R4lFg==", "license": "MIT", "dependencies": { "@babel/runtime": "^7.20.0", @@ -2302,7 +2302,7 @@ "@expo/osascript": "^2.0.31", "@expo/package-manager": "^1.5.0", "@expo/plist": "^0.1.0", - "@expo/prebuild-config": "7.0.8", + "@expo/prebuild-config": "7.0.9", "@expo/rudder-sdk-node": "1.1.1", "@expo/spawn-async": "^1.7.2", "@expo/xcpretty": "^4.3.0", @@ -2465,13 +2465,14 @@ } }, "node_modules/@expo/config": { - "version": "9.0.3", - "resolved": "https://registry.npmjs.org/@expo/config/-/config-9.0.3.tgz", - "integrity": "sha512-eOTNM8eOC8gZNHgenySRlc/lwmYY1NOgvjwA8LHuvPT7/eUwD93zrxu3lPD1Cc/P6C/2BcVdfH4hf0tLmDxnsg==", + "version": "9.0.4", + "resolved": "https://registry.npmjs.org/@expo/config/-/config-9.0.4.tgz", + "integrity": "sha512-g5ns5u1JSKudHYhjo1zaSfkJ/iZIcWmUmIQptMJZ6ag1C0ShL2sj8qdfU8MmAMuKLOgcIfSaiWlQnm4X3VJVkg==", + "license": "MIT", "dependencies": { "@babel/code-frame": "~7.10.4", "@expo/config-plugins": "~8.0.8", - "@expo/config-types": "^51.0.0-unreleased", + "@expo/config-types": "^51.0.3", "@expo/json-file": "^8.3.0", "getenv": "^1.0.0", "glob": "7.1.6", @@ -2483,11 +2484,12 @@ } }, "node_modules/@expo/config-plugins": { - "version": "8.0.8", - "resolved": "https://registry.npmjs.org/@expo/config-plugins/-/config-plugins-8.0.8.tgz", - "integrity": "sha512-Fvu6IO13EUw0R9WeqxUO37FkM62YJBNcZb9DyJAOgMz7Ez/vaKQGEjKt9cwT+Q6uirtCATMgaq6VWAW7YW8xXw==", + "version": "8.0.10", + "resolved": "https://registry.npmjs.org/@expo/config-plugins/-/config-plugins-8.0.10.tgz", + "integrity": "sha512-KG1fnSKRmsudPU9BWkl59PyE0byrE2HTnqbOrgwr2FAhqh7tfr9nRs6A9oLS/ntpGzmFxccTEcsV0L4apsuxxg==", + "license": "MIT", "dependencies": { - "@expo/config-types": "^51.0.0-unreleased", + "@expo/config-types": "^51.0.3", "@expo/json-file": "~8.3.0", "@expo/plist": "^0.1.0", "@expo/sdk-runtime-versions": "^1.0.0", @@ -2600,9 +2602,10 @@ } }, "node_modules/@expo/config-types": { - "version": "51.0.2", - "resolved": "https://registry.npmjs.org/@expo/config-types/-/config-types-51.0.2.tgz", - "integrity": "sha512-IglkIoiDwJMY01lYkF/ZSBoe/5cR+O3+Gx6fpLFjLfgZGBTdyPkKa1g8NWoWQCk+D3cKL2MDbszT2DyRRB0YqQ==" + "version": "51.0.3", + "resolved": "https://registry.npmjs.org/@expo/config-types/-/config-types-51.0.3.tgz", + "integrity": "sha512-hMfuq++b8VySb+m9uNNrlpbvGxYc8OcFCUX9yTmi9tlx6A4k8SDabWFBgmnr4ao3wEArvWrtUQIfQCVtPRdpKA==", + "license": "MIT" }, "node_modules/@expo/config/node_modules/@babel/code-frame": { "version": "7.10.4", @@ -3260,14 +3263,14 @@ } }, "node_modules/@expo/prebuild-config": { - "version": "7.0.8", - "resolved": "https://registry.npmjs.org/@expo/prebuild-config/-/prebuild-config-7.0.8.tgz", - "integrity": "sha512-wH9NVg6HiwF5y9x0TxiMEeBF+ITPGDXy5/i6OUheSrKpPgb0lF1Mwzl/f2fLPXBEpl+ZXOQ8LlLW32b7K9lrNg==", + "version": "7.0.9", + "resolved": "https://registry.npmjs.org/@expo/prebuild-config/-/prebuild-config-7.0.9.tgz", + "integrity": "sha512-9i6Cg7jInpnGEHN0jxnW0P+0BexnePiBzmbUvzSbRXpdXihYUX2AKMu73jgzxn5P1hXOSkzNS7umaY+BZ+aBag==", "license": "MIT", "dependencies": { "@expo/config": "~9.0.0-beta.0", "@expo/config-plugins": "~8.0.8", - "@expo/config-types": "^51.0.0-unreleased", + "@expo/config-types": "^51.0.3", "@expo/image-utils": "^0.5.0", "@expo/json-file": "^8.3.0", "@react-native/normalize-colors": "0.74.85", @@ -3365,9 +3368,10 @@ } }, "node_modules/@expo/vector-icons": { - "version": "14.0.2", - "resolved": "https://registry.npmjs.org/@expo/vector-icons/-/vector-icons-14.0.2.tgz", - "integrity": "sha512-70LpmXQu4xa8cMxjp1fydgRPsalefnHaXLzIwaHMEzcZhnyjw2acZz8azRrZOslPVAWlxItOa2Dd7WtD/kI+CA==", + "version": "14.0.4", + "resolved": "https://registry.npmjs.org/@expo/vector-icons/-/vector-icons-14.0.4.tgz", + "integrity": "sha512-+yKshcbpDfbV4zoXOgHxCwh7lkE9VVTT5T03OUlBsqfze1PLy6Hi4jp1vSb1GVbY6eskvMIivGVc9SKzIv0oEQ==", + "license": "MIT", "dependencies": { "prop-types": "^15.8.1" } @@ -6102,6 +6106,7 @@ "version": "0.74.85", "resolved": "https://registry.npmjs.org/@react-native/debugger-frontend/-/debugger-frontend-0.74.85.tgz", "integrity": "sha512-gUIhhpsYLUTYWlWw4vGztyHaX/kNlgVspSvKe2XaPA7o3jYKUoNLc3Ov7u70u/MBWfKdcEffWq44eSe3j3s5JQ==", + "license": "BSD-3-Clause", "engines": { "node": ">=18" } @@ -6110,6 +6115,7 @@ "version": "0.74.85", "resolved": "https://registry.npmjs.org/@react-native/dev-middleware/-/dev-middleware-0.74.85.tgz", "integrity": "sha512-BRmgCK5vnMmHaKRO+h8PKJmHHH3E6JFuerrcfE3wG2eZ1bcSr+QTu8DAlpxsDWvJvHpCi8tRJGauxd+Ssj/c7w==", + "license": "MIT", "dependencies": { "@isaacs/ttlcache": "^1.4.1", "@react-native/debugger-frontend": "0.74.85", @@ -6133,6 +6139,7 @@ "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", "dependencies": { "ms": "2.0.0" } @@ -6140,12 +6147,14 @@ "node_modules/@react-native/dev-middleware/node_modules/ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" }, "node_modules/@react-native/dev-middleware/node_modules/open": { "version": "7.4.2", "resolved": "https://registry.npmjs.org/open/-/open-7.4.2.tgz", "integrity": "sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q==", + "license": "MIT", "dependencies": { "is-docker": "^2.0.0", "is-wsl": "^2.1.1" @@ -6161,6 +6170,7 @@ "version": "6.2.3", "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.3.tgz", "integrity": "sha512-jmTjYU0j60B+vHey6TfR3Z7RD61z/hmxBS3VMSGIrroOWXQEneK1zNuotOUrGyBHQj0yrpsLHPWtigEFd13ndA==", + "license": "MIT", "dependencies": { "async-limiter": "~1.0.0" } @@ -6201,7 +6211,8 @@ "node_modules/@react-native/normalize-colors": { "version": "0.74.85", "resolved": "https://registry.npmjs.org/@react-native/normalize-colors/-/normalize-colors-0.74.85.tgz", - "integrity": "sha512-pcE4i0X7y3hsAE0SpIl7t6dUc0B0NZLd1yv7ssm4FrLhWG+CGyIq4eFDXpmPU1XHmL5PPySxTAjEMiwv6tAmOw==" + "integrity": "sha512-pcE4i0X7y3hsAE0SpIl7t6dUc0B0NZLd1yv7ssm4FrLhWG+CGyIq4eFDXpmPU1XHmL5PPySxTAjEMiwv6tAmOw==", + "license": "MIT" }, "node_modules/@react-native/virtualized-lists": { "version": "0.74.87", @@ -9613,24 +9624,24 @@ } }, "node_modules/expo": { - "version": "51.0.32", - "resolved": "https://registry.npmjs.org/expo/-/expo-51.0.32.tgz", - "integrity": "sha512-6GEhYvHRnyS/6BytQagGkClsaqbuwAtlN3A6oDfnNMRKLmz6NE/r+Rjg9zbQgUO6zigqb60Yj5lAX32DmixRDw==", + "version": "51.0.36", + "resolved": "https://registry.npmjs.org/expo/-/expo-51.0.36.tgz", + "integrity": "sha512-eQIC0l6fz3p4cU/hV8+QcyKSacyROhaoA1oohfCD6I3F09dxmC8b3SESpzGqHfuq8wsgcUc4q8ckX7ec25IV1g==", "license": "MIT", "dependencies": { "@babel/runtime": "^7.20.0", - "@expo/cli": "0.18.29", - "@expo/config": "9.0.3", - "@expo/config-plugins": "8.0.8", + "@expo/cli": "0.18.30", + "@expo/config": "9.0.4", + "@expo/config-plugins": "8.0.10", "@expo/metro-config": "0.18.11", - "@expo/vector-icons": "^14.0.0", + "@expo/vector-icons": "^14.0.3", "babel-preset-expo": "~11.0.14", "expo-asset": "~10.0.10", "expo-file-system": "~17.0.1", "expo-font": "~12.0.10", "expo-keep-awake": "~13.0.2", - "expo-modules-autolinking": "1.11.2", - "expo-modules-core": "1.12.24", + "expo-modules-autolinking": "1.11.3", + "expo-modules-core": "1.12.25", "fbemitter": "^3.0.0", "whatwg-url-without-unicode": "8.0.0-3" }, @@ -9674,18 +9685,6 @@ "invariant": "^2.2.4" } }, - "node_modules/expo-barcode-scanner": { - "version": "13.0.1", - "resolved": "https://registry.npmjs.org/expo-barcode-scanner/-/expo-barcode-scanner-13.0.1.tgz", - "integrity": "sha512-xBGLT1An2gpAMIQRTLU3oHydKohX8r8F9/ait1Fk9Vgd0GraFZbP4IiT7nHMlaw4H6E7Muucf7vXpGV6u7d4HQ==", - "license": "MIT", - "dependencies": { - "expo-image-loader": "~4.7.0" - }, - "peerDependencies": { - "expo": "*" - } - }, "node_modules/expo-camera": { "version": "15.0.16", "resolved": "https://registry.npmjs.org/expo-camera/-/expo-camera-15.0.16.tgz", @@ -9780,15 +9779,6 @@ "expo": "*" } }, - "node_modules/expo-image-loader": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/expo-image-loader/-/expo-image-loader-4.7.0.tgz", - "integrity": "sha512-cx+MxxsAMGl9AiWnQUzrkJMJH4eNOGlu7XkLGnAXSJrRoIiciGaKqzeaD326IyCTV+Z1fXvIliSgNW+DscvD8g==", - "license": "MIT", - "peerDependencies": { - "expo": "*" - } - }, "node_modules/expo-keep-awake": { "version": "13.0.2", "resolved": "https://registry.npmjs.org/expo-keep-awake/-/expo-keep-awake-13.0.2.tgz", @@ -9808,9 +9798,9 @@ } }, "node_modules/expo-modules-autolinking": { - "version": "1.11.2", - "resolved": "https://registry.npmjs.org/expo-modules-autolinking/-/expo-modules-autolinking-1.11.2.tgz", - "integrity": "sha512-fdcaNO8ucHA3yLNY52ZUENBcAG7KEx8QyMmnVNavO1JVBGRMZG8JyVcbrhYQDtVtpxkbai5YzwvLutINvbDZDQ==", + "version": "1.11.3", + "resolved": "https://registry.npmjs.org/expo-modules-autolinking/-/expo-modules-autolinking-1.11.3.tgz", + "integrity": "sha512-oYh8EZEvYF5TYppxEKUTTJmbr8j7eRRnrIxzZtMvxLTXoujThVPMFS/cbnSnf2bFm1lq50TdDNABhmEi7z0ngQ==", "license": "MIT", "dependencies": { "chalk": "^4.1.0", @@ -9932,9 +9922,9 @@ } }, "node_modules/expo-modules-core": { - "version": "1.12.24", - "resolved": "https://registry.npmjs.org/expo-modules-core/-/expo-modules-core-1.12.24.tgz", - "integrity": "sha512-3geIe2ecizlp7l26iY8Nmc59z2d1RUC5nQZtI9iJoi5uHEUV/zut8e4zRLFVnZb8KOcMcEDsrvaBL5DPnqdfpg==", + "version": "1.12.25", + "resolved": "https://registry.npmjs.org/expo-modules-core/-/expo-modules-core-1.12.25.tgz", + "integrity": "sha512-HB2LS2LEM41Xq1bG+Jtzqm6XgPaa+mM9BAvCdX1lDGMQ9Ay9vMTL/GVEs2gpsINPofICopjBRwD+wftyCbVrzg==", "license": "MIT", "dependencies": { "invariant": "^2.2.4" @@ -14120,9 +14110,9 @@ } }, "node_modules/package-json-from-dist": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.0.tgz", - "integrity": "sha512-dATvCeZN/8wQsGywez1mzHtTlP22H8OEfPrVMLNr4/eGa+ijtLn/6M5f0dY8UKNrC2O9UCU6SSoG3qRKnt7STw==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", "license": "BlueOak-1.0.0" }, "node_modules/parent-module": { @@ -14765,7 +14755,6 @@ "resolved": "https://registry.npmjs.org/react-freeze/-/react-freeze-1.0.4.tgz", "integrity": "sha512-r4F0Sec0BLxWicc7HEyo2x3/2icUTrRmDjaaRyzzn+7aDyFZliszMDOgLVwSnQnYENOlL1o569Ze2HZefk8clA==", "license": "MIT", - "peer": true, "engines": { "node": ">=10" }, @@ -14890,22 +14879,20 @@ } }, "node_modules/react-native-safe-area-context": { - "version": "4.11.0", - "resolved": "https://registry.npmjs.org/react-native-safe-area-context/-/react-native-safe-area-context-4.11.0.tgz", - "integrity": "sha512-Bg7bozxEB+ZS+H3tVYs5yY1cvxNXgR6nRQwpSMkYR9IN5CbxohLnSprrOPG/ostTCd4F6iCk0c51pExEhifSKQ==", + "version": "4.10.5", + "resolved": "https://registry.npmjs.org/react-native-safe-area-context/-/react-native-safe-area-context-4.10.5.tgz", + "integrity": "sha512-Wyb0Nqw2XJ6oZxW/cK8k5q7/UAhg/wbEG6UVf89rQqecDZTDA5ic//P9J6VvJRVZerzGmxWQpVuM7f+PRYUM4g==", "license": "MIT", - "peer": true, "peerDependencies": { "react": "*", "react-native": "*" } }, "node_modules/react-native-screens": { - "version": "3.34.0", - "resolved": "https://registry.npmjs.org/react-native-screens/-/react-native-screens-3.34.0.tgz", - "integrity": "sha512-8ri3Pd9QcpfXnVckOe/Lnto+BXmSPHV/Q0RB0XW0gDKsCv5wi5k7ez7g1SzgiYHl29MSdiqgjH30zUyOOowOaw==", + "version": "3.31.1", + "resolved": "https://registry.npmjs.org/react-native-screens/-/react-native-screens-3.31.1.tgz", + "integrity": "sha512-8fRW362pfZ9y4rS8KY5P3DFScrmwo/vu1RrRMMx0PNHbeC9TLq0Kw1ubD83591yz64gLNHFLTVkTJmWeWCXKtQ==", "license": "MIT", - "peer": true, "dependencies": { "react-freeze": "^1.0.0", "warn-once": "^0.1.0" diff --git a/package.json b/package.json index cb34ddb..eed76a0 100644 --- a/package.json +++ b/package.json @@ -19,18 +19,19 @@ "@react-navigation/native": "^6.1.18", "@react-navigation/native-stack": "^6.11.0", "@supabase/supabase-js": "^2.45.4", - "expo": "~51.0.21", + "expo": "~51.0.36", "expo-auth-session": "~5.5.2", "expo-camera": "~15.0.16", "expo-constants": "~16.0.2", "expo-device": "~6.0.2", - "expo-linking": "~6.3.1", "expo-status-bar": "~1.12.1", "react": "18.2.0", "react-native": "^0.74.5", "react-native-dotenv": "^3.4.11", "react-native-gesture-handler": "^2.20.0", "react-native-reanimated": "^3.15.4", + "react-native-safe-area-context": "4.10.5", + "react-native-screens": "3.31.1", "react-native-svg": "^15.7.1", "react-native-toast-message": "^2.2.1", "react-native-url-polyfill": "^2.0.0", diff --git a/src/components/QRCodeScanner/QRCodeScanner.tsx b/src/components/QRCodeScanner/QRCodeScanner.tsx new file mode 100644 index 0000000..4d71f2c --- /dev/null +++ b/src/components/QRCodeScanner/QRCodeScanner.tsx @@ -0,0 +1,99 @@ +import { useEffect, useRef, useState } from 'react'; +import { SafeAreaView, Text, TouchableOpacity, View } from 'react-native'; +import { + BarcodeScanningResult, + CameraView, + useCameraPermissions, +} from 'expo-camera'; +import { NativeStackScreenProps } from '@react-navigation/native-stack'; +import { RootStackParamList } from '@/types/navigation'; +import styles from './styles'; + +type QRCodeScannerProps = NativeStackScreenProps; + +export default function QRCodeScanner({ navigation }: QRCodeScannerProps) { + const [permission, requestPermission] = useCameraPermissions(); + const [qrCodeFound, setQrCodeFound] = useState(false); + const [qrCodeData, setQrCodeData] = useState(null); + const [flashEnabled, setFlashEnabled] = useState(false); + + const resetQrCodeFound = () => { + setQrCodeFound(false); + setQrCodeData(null); + }; + let qrCodeFoundTimeout = useRef | undefined>( + undefined, + ); + + const onBarcodeScanned = (data: BarcodeScanningResult) => { + setQrCodeFound(true); + setQrCodeData(data.data); + + // Reset the QR code found state after not seeing a QR for 100ms + clearTimeout(qrCodeFoundTimeout.current); + qrCodeFoundTimeout.current = setTimeout(resetQrCodeFound, 100); + }; + + useEffect(() => { + // Request camera permissions if not granted on mount + if (!permission?.granted) { + requestPermission(); + } + }, [permission, requestPermission]); + + // Camera permissions are still loading. + if (!permission) { + return ; + } + + // No perms :( + if (!permission.granted) { + return Permission for camera not granted.; + } + + return ( + + + setFlashEnabled(!flashEnabled)}> + Flash + + navigation.goBack()}> + X + + + + + + Scan QR Code + Aim the camera at the tree's code + + + + + + + + + navigation.push('TreeInfoPage', { treeId: qrCodeData ?? '' }) + } + disabled={!qrCodeFound} + > + Scan + + + ); +} diff --git a/src/components/QRCodeScanner/styles.ts b/src/components/QRCodeScanner/styles.ts new file mode 100644 index 0000000..7de0f02 --- /dev/null +++ b/src/components/QRCodeScanner/styles.ts @@ -0,0 +1,101 @@ +import { StyleSheet } from 'react-native'; +import colors from '@/styles/colors'; + +export default StyleSheet.create({ + container: { + flex: 1, + flexDirection: 'column', + justifyContent: 'space-between', + paddingHorizontal: 44, + backgroundColor: colors.primary_green, + }, + + iconFlex: { + flex: 0, + width: '100%', + paddingHorizontal: 44, + flexDirection: 'row', + justifyContent: 'space-between', + }, + + icon: { + backgroundColor: colors.white1, + padding: 8, + }, + + mainFlex: { + flex: 1, + padding: 24, + flexDirection: 'column', + justifyContent: 'flex-start', + gap: 86, + }, + + textFlex: { + flex: 0, + flexDirection: 'column', + gap: 8, + }, + header: { + textAlign: 'center', + fontSize: 24, + color: colors.white1, + }, + subtext: { + textAlign: 'center', + fontSize: 20, + color: colors.white1, + }, + + cameraView: { + alignSelf: 'center', + width: 285, + height: 248, + borderWidth: 2, + borderColor: colors.primary_green_2, + borderRadius: 12, + zIndex: 1, + }, + camera: { + flex: 1, + borderRadius: 12, + overflow: 'hidden', + }, + qrCodeFoundCamera: { + borderColor: colors.primary_yellow, + }, + + buttonContainer: { + flex: 1, + flexDirection: 'row', + backgroundColor: 'transparent', + margin: 64, + }, + scanButton: { + alignItems: 'center', + justifyContent: 'center', + paddingVertical: 10, + borderRadius: 10, + elevation: 3, + marginHorizontal: 44, + marginBottom: 64, + }, + + scanButtonDisabled: { + backgroundColor: colors.gray4, + }, + scanButtonEnabled: { + backgroundColor: colors.primary_yellow, + }, + + scanButtonText: { + fontSize: 18, + color: colors.white1, + }, + + text: { + fontSize: 24, + fontWeight: 'bold', + color: 'white', + }, +}); diff --git a/src/screens/Home/Home.tsx b/src/screens/Home/Home.tsx new file mode 100644 index 0000000..87aca35 --- /dev/null +++ b/src/screens/Home/Home.tsx @@ -0,0 +1,22 @@ +import { StyleSheet, Text, View } from 'react-native'; +import { StatusBar } from 'expo-status-bar'; +import Logo from '@/components/Logo'; + +export default function HomeScreen() { + return ( + + + Home Screen! + + + ); +} + +const styles = StyleSheet.create({ + container: { + flex: 1, + backgroundColor: '#fff', + alignItems: 'center', + justifyContent: 'center', + }, +}); diff --git a/src/screens/TreeInfo/TreeInfo.tsx b/src/screens/TreeInfo/TreeInfo.tsx new file mode 100644 index 0000000..7f568a9 --- /dev/null +++ b/src/screens/TreeInfo/TreeInfo.tsx @@ -0,0 +1,25 @@ +import { Pressable, Text, View } from 'react-native'; +import { StatusBar } from 'expo-status-bar'; +import { NativeStackScreenProps } from '@react-navigation/native-stack'; +import { RootStackParamList } from '@/types/navigation'; +import styles from './styles'; + +type TreeInfoPageProps = NativeStackScreenProps< + RootStackParamList, + 'TreeInfoPage' +>; + +export default function TreeInfo({ route, navigation }: TreeInfoPageProps) { + return ( + + Tree Id: + {route.params.treeId} + + navigation.push('Scanner')}> + Back to scanner + + + + + ); +} diff --git a/src/screens/TreeInfo/styles.ts b/src/screens/TreeInfo/styles.ts new file mode 100644 index 0000000..0cb1df7 --- /dev/null +++ b/src/screens/TreeInfo/styles.ts @@ -0,0 +1,10 @@ +import { StyleSheet } from 'react-native'; + +export default StyleSheet.create({ + container: { + flex: 1, + backgroundColor: '#fff', + alignItems: 'center', + justifyContent: 'center', + }, +}); diff --git a/src/styles/colors.ts b/src/styles/colors.ts new file mode 100644 index 0000000..23f24bd --- /dev/null +++ b/src/styles/colors.ts @@ -0,0 +1,12 @@ +export default { + primary_green: '#446127', + primary_green_2: '#9BA964', + primary_yellow: '#F9BD24', + white1: '#FFFFFF', + off_white: '#F9F4E8', + pure_black: '#000000', + black3: '#282828', + gray2: '#4F4F4F', + gray4: '#BDBDBD', + gray3: '#828282', +}; diff --git a/src/types/navigation.ts b/src/types/navigation.ts new file mode 100644 index 0000000..6511bcd --- /dev/null +++ b/src/types/navigation.ts @@ -0,0 +1,5 @@ +export type RootStackParamList = { + Home: undefined; + Scanner: undefined; + TreeInfoPage: { treeId: string }; +};