Skip to content

Commit

Permalink
Merge pull request #3 from Delemangi/feat/camera
Browse files Browse the repository at this point in the history
feat: add camera
  • Loading branch information
Delemangi authored Feb 11, 2024
2 parents 294d19a + 2fe7fe6 commit 16d7b5b
Show file tree
Hide file tree
Showing 6 changed files with 218 additions and 2 deletions.
16 changes: 14 additions & 2 deletions app.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,26 @@
"adaptiveIcon": {
"foregroundImage": "./assets/adaptive-icon.png",
"backgroundColor": "#ffffff"
}
},
"permissions": [
"android.permission.CAMERA",
"android.permission.RECORD_AUDIO"
],
"package": "com.anonymous.finsight"
},
"web": {
"bundler": "metro",
"favicon": "./assets/favicon.png"
},
"plugins": [
"expo-router"
"expo-router",
[
"expo-camera",
{
"cameraPermission": "Allow $(PRODUCT_NAME) to access your camera.",
"microphonePermission": "Allow $(PRODUCT_NAME) to access your microphone."
}
]
],
"experiments": {
"tsconfigPaths": true,
Expand Down
21 changes: 21 additions & 0 deletions package-lock.json

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

2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,11 @@
"@rneui/themed": "^0.0.0-edge.2",
"@tanstack/react-query": "^5.20.1",
"expo": "~50.0.6",
"expo-camera": "~14.0.4",
"expo-constants": "~15.4.5",
"expo-font": "~11.10.2",
"expo-linking": "~6.2.2",
"expo-media-library": "~15.9.1",
"expo-router": "~3.4.7",
"expo-splash-screen": "~0.26.4",
"expo-status-bar": "~1.11.1",
Expand Down
9 changes: 9 additions & 0 deletions src/app/_layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,15 @@ const RootLayout = () => {
),
}}
/>
<Tabs.Screen
name="camera/index"
options={{
title: "Camera",
tabBarIcon: () => (
<AntDesign name="camera" size={20} color="white" />
),
}}
/>
</Tabs>
</Providers>
);
Expand Down
135 changes: 135 additions & 0 deletions src/app/camera/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
import Button from "@components/CameraButton";
import { makeStyles } from "@rneui/themed";
import { Camera, CameraType } from "expo-camera";
import Constants from "expo-constants";
import * as MediaLibrary from "expo-media-library";
import React, { useEffect, useRef, useState } from "react";
import { Alert, Image, Text, View } from "react-native";

const useStyles = makeStyles((theme) => ({
container: {
flex: 1,
justifyContent: "center",
paddingTop: Constants.statusBarHeight,
backgroundColor: theme.colors.background,
},
controls: {
justifyContent: "center",
flex: 0.5,
},
button: {
height: 40,
borderRadius: 6,
flexDirection: "row",
alignItems: "center",
justifyContent: "center",
},
text: {
fontWeight: "bold",
fontSize: 16,
color: theme.colors.white,
marginLeft: 10,
},
camera: {
flex: 1,
borderRadius: 2,
paddingHorizontal: 200,
},
topControls: {
flex: 1,
},
}));

export default function App() {
const styles = useStyles();
const [hasCameraPermission, setHasCameraPermission] = useState(false);
const [image, setImage] = useState<string | null>(null);
const [type, setType] = useState(CameraType.back);
const cameraRef = useRef<Camera>(null);

useEffect(() => {
const func = async () => {
MediaLibrary.requestPermissionsAsync();
const cameraStatus = await Camera.requestCameraPermissionsAsync();
setHasCameraPermission(cameraStatus.status === "granted");
};

func();
}, []);

const takePicture = async () => {
if (cameraRef) {
try {
const data = await cameraRef.current?.takePictureAsync();
setImage(data?.uri ?? "");
} catch (error) {
console.log(error);
}
}
};

const savePicture = async () => {
if (image) {
try {
await MediaLibrary.createAssetAsync(image);
Alert.alert("Picture saved! 🎉");
setImage(null);
} catch (error) {
console.log(error);
}
}
};

if (!hasCameraPermission) {
return <Text>No access to camera</Text>;
}

return (
<View style={styles.container}>
{!image ? (
<Camera style={styles.camera} type={type} ref={cameraRef}>
<View
style={{
flexDirection: "row",
justifyContent: "space-between",
paddingHorizontal: 30,
}}
>
<Button
title=""
icon="retweet"
onPress={() => {
setType(
type === CameraType.back ? CameraType.front : CameraType.back,
);
}}
/>
</View>
</Camera>
) : (
<Image source={{ uri: image }} style={styles.camera} />
)}

<View style={styles.controls}>
{image ? (
<View
style={{
flexDirection: "row",
justifyContent: "space-between",
paddingHorizontal: 50,
}}
>
<Button
title="Re-take"
onPress={() => setImage(null)}
icon="retweet"
/>
<Button title="Save" onPress={savePicture} icon="check" />
</View>
) : (
<Button title="Take a picture" onPress={takePicture} icon="camera" />
)}
</View>
</View>
);
}
37 changes: 37 additions & 0 deletions src/components/CameraButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { Entypo } from "@expo/vector-icons";
import { makeStyles, useTheme } from "@rneui/themed";
import * as React from "react";
import { Text, TouchableOpacity } from "react-native";

const useStyles = makeStyles((theme) => ({
button: {
height: 40,
flexDirection: "row",
alignItems: "center",
justifyContent: "center",
},
text: {
fontWeight: "bold",
fontSize: 16,
color: theme.colors.white,
marginLeft: 10,
},
}));

type Props = {
title: string;
onPress: () => void;
icon: string;
};

export default function Button({ title, onPress, icon }: Props) {
const styles = useStyles();
const { theme } = useTheme();

return (
<TouchableOpacity onPress={onPress} style={styles.button}>
<Entypo name={icon as any} size={28} color={theme.colors.white} />
<Text style={styles.text}>{title}</Text>
</TouchableOpacity>
);
}

0 comments on commit 16d7b5b

Please sign in to comment.