Skip to content

Commit

Permalink
Merge branch 'main' into cwz/ocf-17-user-authentication-setup
Browse files Browse the repository at this point in the history
  • Loading branch information
christophertorres1 authored Nov 13, 2024
2 parents 2e49e21 + 0f720a5 commit 7f0ae9d
Show file tree
Hide file tree
Showing 18 changed files with 452 additions and 12 deletions.
3 changes: 3 additions & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ node_modules/
dist/
web-build/

# Platforms
ios/
android/

# Native
*.orig.*
*.jks
Expand Down
51 changes: 43 additions & 8 deletions App.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,54 @@
import React from 'react';
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 AdminLoginScreen from '@/screens/AdminLoginScreen';
import HomeScreen from '@/screens/Home/Home';
import LoginScreen from '@/screens/LoginScreen';
import { LoginStackParamList } from '@/types/navigation';
import TreeInfoPage from '@/screens/TreeInfo/TreeInfo';
import { LoginStackParamList, RootStackParamList } from '@/types/navigation';

const Stack = createNativeStackNavigator<LoginStackParamList>();
const LoginStack = createNativeStackNavigator<LoginStackParamList>();
const RootStack = createNativeStackNavigator<RootStackParamList>();

export default function App() {
function LoginStackNavigator() {
return (
<LoginStack.Navigator initialRouteName="Login">
<LoginStack.Screen name="Login" component={LoginScreen} />
<LoginStack.Screen name="AdminLogin" component={AdminLoginScreen} />
</LoginStack.Navigator>
);
}

// eslint-disable-next-line no-unused-vars, @typescript-eslint/no-unused-vars
function RootStackNavigator() {
return (
<RootStack.Navigator initialRouteName="Home">
<RootStack.Screen
name="Home"
component={HomeScreen}
options={{ headerShown: false }}
/>
<RootStack.Screen
name="Scanner"
component={QRCodeScanner}
options={{ headerShown: false }}
/>
<RootStack.Screen
name="TreeInfoPage"
component={TreeInfoPage}
options={{ headerShown: false }}
/>
</RootStack.Navigator>
);
}

function App() {
return (
<NavigationContainer>
<Stack.Navigator initialRouteName="Login">
<Stack.Screen name="Login" component={LoginScreen} />
<Stack.Screen name="AdminLogin" component={AdminLoginScreen} />
</Stack.Navigator>
<LoginStackNavigator />
</NavigationContainer>
);
}

export default App;
8 changes: 8 additions & 0 deletions app.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
24 changes: 23 additions & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@
"expo-crypto": "~13.0.2",
"expo-dev-client": "~4.0.28",
"expo-device": "~6.0.2",
"expo-linking": "~6.3.1",
"expo-status-bar": "~1.12.1",
"expo-web-browser": "~13.0.3",
"nodemon": "^3.1.7",
Expand Down
2 changes: 1 addition & 1 deletion src/components/Login.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Text, TouchableOpacity, View } from 'react-native';
import { NativeStackScreenProps } from '@react-navigation/native-stack';
import { styles } from '@/screens/styles';
import { LoginStackParamList } from '@/types/navigation';
import { styles } from '../screens/styles';

type LoginProps = NativeStackScreenProps<LoginStackParamList, 'Login'>;

Expand Down
99 changes: 99 additions & 0 deletions src/components/QRCodeScanner/QRCodeScanner.tsx
Original file line number Diff line number Diff line change
@@ -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<RootStackParamList, 'Scanner'>;

export default function QRCodeScanner({ navigation }: QRCodeScannerProps) {
const [permission, requestPermission] = useCameraPermissions();
const [qrCodeFound, setQrCodeFound] = useState<boolean>(false);
const [qrCodeData, setQrCodeData] = useState<string | null>(null);
const [flashEnabled, setFlashEnabled] = useState<boolean>(false);

const resetQrCodeFound = () => {
setQrCodeFound(false);
setQrCodeData(null);
};
let qrCodeFoundTimeout = useRef<ReturnType<typeof setTimeout> | 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 <View />;
}

// No perms :(
if (!permission.granted) {
return <Text>Permission for camera not granted.</Text>;
}

return (
<SafeAreaView style={styles.container}>
<View style={styles.iconFlex}>
<TouchableOpacity onPress={() => setFlashEnabled(!flashEnabled)}>
<Text style={styles.icon}>Flash</Text>
</TouchableOpacity>
<TouchableOpacity onPress={() => navigation.goBack()}>
<Text style={styles.icon}>X</Text>
</TouchableOpacity>
</View>

<View style={styles.mainFlex}>
<View style={styles.textFlex}>
<Text style={styles.header}>Scan QR Code</Text>
<Text style={styles.subtext}>Aim the camera at the tree's code</Text>
</View>

<View
style={[styles.cameraView, qrCodeFound && styles.qrCodeFoundCamera]}
>
<CameraView
style={[styles.camera]}
onBarcodeScanned={onBarcodeScanned}
barcodeScannerSettings={{
barcodeTypes: ['qr'],
}}
enableTorch={flashEnabled}
/>
</View>
</View>

<TouchableOpacity
style={[
styles.scanButton,
qrCodeFound ? styles.scanButtonEnabled : styles.scanButtonDisabled,
]}
onPress={() =>
navigation.push('TreeInfoPage', { treeId: qrCodeData ?? '' })
}
disabled={!qrCodeFound}
>
<Text style={styles.scanButtonText}>Scan</Text>
</TouchableOpacity>
</SafeAreaView>
);
}
101 changes: 101 additions & 0 deletions src/components/QRCodeScanner/styles.ts
Original file line number Diff line number Diff line change
@@ -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',
},
});
22 changes: 22 additions & 0 deletions src/screens/Home/Home.tsx
Original file line number Diff line number Diff line change
@@ -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 (
<View style={styles.container}>
<Logo />
<Text>Home Screen!</Text>
<StatusBar style="auto" />
</View>
);
}

const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
});
Loading

0 comments on commit 7f0ae9d

Please sign in to comment.