White Blank Screen
- Dominant language
- TypeScript
- Stars
- 3.7k
- Forks
- 1.1k
- Avg merge
- 11h 15m
- Merged PRs (30d)
- 1
Description
I have noticed that when i take a picture in a bright light most times, my screen turns white requesting me to restart the app. Here is my hook
`import {
CameraRecordingOptions,
CameraType,
CameraView,
useCameraPermissions,
} from "expo-camera";
import * as MediaLibrary from "expo-media-library";
import { useCallback, useEffect, useRef, useState } from "react";
export function useCamera() {
const [cameraPermission, requestCameraPermission] = useCameraPermissions();
const [mediaPermission, requestMediaPermission] =
MediaLibrary.usePermissions();
const cameraRef = useRef(null);
const [isReady, setIsReady] = useState(false);
const [type, setType] = useState("front");
const [mode, setMode] = useState<"photo" | "video">("photo");
const [isRecording, setIsRecording] = useState(false);
// ✅ Now managed inside the hook
const [photoUri, setPhotoUri] = useState(null);
const [videoUri, setVideoUri] = useState(null);
// Request permissions at startup
useEffect(() => {
if (!cameraPermission?.granted) {
requestCameraPermission();
}
if (!mediaPermission?.granted) {
requestMediaPermission();
}
}, [
cameraPermission,
mediaPermission,
requestCameraPermission,
requestMediaPermission,
]);
const toggleCameraType = useCallback(() => {
setType((prev) => (prev === "back" ? "front" : "back"));
}, []);
const toggleMode = useCallback(() => {
setMode((prev) => (prev === "photo" ? "video" : "photo"));
}, []);
const takePicture = useCallback(
async (options?: { base64?: boolean }) => {
if (!cameraRef.current || !isReady || mode !== "photo") {
return null;
}
try {
const photo = await cameraRef.current.takePictureAsync({
quality: 0.5,
base64: options?.base64 ?? false,
skipProcessing: true,
});
setPhotoUri(photo.uri); // ✅ store inside hook
return photo;
} catch (err) {
console.error("Error taking picture:", err);
return null;
}
},
[isReady, mode]
);
const startRecording = useCallback(async () => {
if (!cameraRef.current || !isReady || mode !== "video" || isRecording) {
return null;
}
try {
setIsRecording(true);
const opts: CameraRecordingOptions = {
maxDuration: 60, // seconds
};
const video = await cameraRef.current.recordAsync(opts);
if (video && video.uri) {
setVideoUri(video.uri); // ✅ store inside hook
}
setIsRecording(false);
return video;
} catch (err) {
console.error("Error recording video:", err);
setIsRecording(false);
return null;
}
}, [isReady, mode, isRecording]);
const stopRecording = useCallback(() => {
if (cameraRef.current && isRecording) {
cameraRef.current.stopRecording();
setIsRecording(false);
}
}, [isRecording]);
// Reset captured media
const resetMedia = useCallback(() => {
setPhotoUri(null);
setVideoUri(null);
}, []);
return {
cameraRef,
hasPermission: cameraPermission?.granted,
requestCameraPermission,
hasMediaPermission: mediaPermission?.granted,
requestMediaPermission,
isReady,
setIsReady,
type,
toggleCameraType,
mode,
toggleMode,
takePicture,
startRecording,
stopRecording,
isRecording,
photoUri, // ✅ exposed state
videoUri, // ✅ exposed state
resetMedia,
};
}
`
Here is my implementation
`import CancelBlueCircle from "@/assets/svg/cancel-blue-circle.svg";
import CheckBlue3 from "@/assets/svg/check-blue-3.svg";
import { images } from "@/constants/images";
import { useLocationContext } from "@/context/LocationContext";
import { useNetwork } from "@/context/NetworkContext";
import { useStaffFaceClockin } from "@/hooks/queries/clockin";
import { useCamera } from "@/hooks/useCamera";
import { cn } from "@/utils/clsx";
import { getDeviceId } from "@/utils/getDeviceID";
import { getDeviceInfo } from "@/utils/getDeviceInfo";
import { CameraView } from "expo-camera";
import { LinearGradient } from "expo-linear-gradient";
import React, { useCallback } from "react";
import {
ActivityIndicator,
Alert,
Dimensions,
Image,
Platform,
StatusBar,
Text,
TouchableOpacity,
View,
} from "react-native";
import Modal from "react-native-modal";
import CustomButton2 from "../CustomButton2";
const { width, height } = Dimensions.get("window");
const FaceIDClockinModal = ({
isVisible,
setIsVisible,
}: {
isVisible: boolean;
setIsVisible: React.Dispatch>;
}) => {
const { locationReady, location } = useLocationContext();
const { ip } = useNetwork();
const {
cameraRef,
hasPermission,
requestCameraPermission,
setIsReady,
type,
takePicture,
photoUri,
resetMedia,
} = useCamera();
const { clockinWithFace, clockingInWithFace } = useStaffFaceClockin({
onSuccess: (values) => {
Alert.alert("Success", values.message);
onCancelPress();
},
onError: (message) => {
Alert.alert("Attention", message);
},
});
const onCancelPress = () => {
resetMedia();
setIsVisible(false);
};
const handleClockin = useCallback(async () => {
if (!locationReady) {
Alert.alert(
"Location Unavailable",
"Your GPS may be turned off. Please check your settings and try again.",
[{ text: "OK" }]
);
return;
}
if (!photoUri) {
//no image available
Alert.alert("Attention", "No captured image found");
return false;
}
if (!(await getDeviceId())) {
//no device id found
Alert.alert("Attention", "Device ID not found");
return false;
}
if (!getDeviceInfo().deviceName) {
//no device name found
Alert.alert("Attention", "Device name not found");
return false;
}
//instantiate formData
const formData = new FormData();
//add all images to formData
const fileName = photoUri.split("/").pop();
const ext = fileName?.split(".").pop()?.toLowerCase();
const mimeMap: Record = {
jpg: "image/jpeg",
jpeg: "image/jpeg",
png: "image/png",
heic: "image/heic",
gif: "image/gif",
webp: "image/webp",
};
const fileType = mimeMap[ext || ""] || "image/jpeg"; // default to jpeg
formData.append("image", {
uri: photoUri,
type: fileType,
name: fileName,
} as any); // TypeScript needs this cast
//add device ID to formData
formData.append(
"device_id",
(await getDeviceId()) as unknown as string
);
//add device name to formData
formData.append(
"device_name",
getDeviceInfo().deviceName as unknown as string
);
//add ip address to formData
formData.append("ip_address", ip as unknown as string);
//add longitude to formData
formData.append(
"long_x",
location?.coords.longitude as unknown as string
);
//add latitude to formData
formData.append(
"lat_y",
location?.coords.latitude as unknown as string
);
//add clockin mode to formData
formData.append("clock_mode", "Facial Recognition");
//clockin/clockout
clockinWithFace({ payload: formData });
}, [locationReady, photoUri, location, ip, clockinWithFace]);
const onActionBtnPress = useCallback(() => {
if (!hasPermission) {
requestCameraPermission();
} else if (!photoUri) {
takePicture({ base64: false });
} else {
handleClockin();
}
}, [
handleClockin,
hasPermission,
photoUri,
requestCameraPermission,
takePicture,
]);
return (
{/* Modal */}
setIsVisible(false)}
backdropOpacity={0.9}
backdropColor="#000"
useNativeDriver
statusBarTranslucent
avoidKeyboard={true}
propagateSwipe={true}
deviceHeight={
height +
(Platform.OS === "android"
? StatusBar.currentHeight ?? 0
: 0)
}
>
{!hasPermission ? (
) : photoUri ? (
) : (
setIsReady(true)}
flash="off"
mirror={false}
/>
)}
{hasPermission && (
{photoUri
? "Great! Face capture complete."
: "Keep your face inside the circle and look straight"}
)}
{photoUri && (
Capture Done
)}
{!hasPermission
? "Grant Permission"
: photoUri
? "Clock-In / Clock-Out"
: "Capture"}
{clockingInWithFace && (
)}
);
};
export default React.memo(FaceIDClockinModal);
`
Contributor guide
Assessment
This issue has not been assessed yet.