UNPKG

@interactify-live/pindo-wizard-react-native

Version:

React Native PindoWizard component for media capture and interaction management

317 lines (316 loc) 17.8 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const jsx_runtime_1 = require("react/jsx-runtime"); const react_1 = require("react"); const react_native_1 = require("react-native"); const react_native_image_picker_1 = require("react-native-image-picker"); const react_native_vision_camera_1 = require("react-native-vision-camera"); const Gallery_1 = __importDefault(require("./icons/Gallery")); const Rotate_1 = __importDefault(require("./icons/Rotate")); const CaptureButton_1 = __importDefault(require("./CaptureButton")); const CameraComponent = ({ onCapture, onGallerySelect, onClose: _onClose, settings: _settings = { isVideoActive: false, isInteractionActive: false, isCoverActive: false, numMedias: 1, flashEnabled: false, }, }) => { const [isRecording, setIsRecording] = (0, react_1.useState)(false); const [cameraType, setCameraType] = (0, react_1.useState)("back"); const [mode, setMode] = (0, react_1.useState)(_settings.isVideoActive ? "photo" : "photo"); const captureButtonRef = (0, react_1.useRef)(null); const cameraRef = (0, react_1.useRef)(null); // Use VisionCamera's built-in permission hooks const { hasPermission: hasCameraPermission, requestPermission: requestCameraPermission, } = (0, react_native_vision_camera_1.useCameraPermission)(); const { hasPermission: hasMicrophonePermission, requestPermission: requestMicrophonePermission, } = (0, react_native_vision_camera_1.useMicrophonePermission)(); // Get camera device based on type const device = (0, react_native_vision_camera_1.useCameraDevice)(cameraType); // Combined permission state const hasAllPermissions = hasCameraPermission && (mode === "photo" || hasMicrophonePermission); (0, react_1.useEffect)(() => { const requestPermissions = async () => { if (!hasCameraPermission) { await requestCameraPermission(); } if (mode === "video" && !hasMicrophonePermission) { await requestMicrophonePermission(); } }; requestPermissions(); }, [ hasCameraPermission, hasMicrophonePermission, mode, requestCameraPermission, requestMicrophonePermission, ]); // Ensure mode is 'photo' when video is not active (0, react_1.useEffect)(() => { if (!_settings.isVideoActive && mode !== "photo") { setMode("photo"); } }, [_settings.isVideoActive, mode]); const handleCapture = async () => { if (!cameraRef.current || !device) { react_native_1.Alert.alert("Error", "Camera is not available."); return; } if (!hasAllPermissions) { react_native_1.Alert.alert("Permission Required", mode === "video" ? "Both camera and microphone permissions are required to record video." : "Camera permission is required to take photos."); return; } try { if (mode === "video") { // Video mode if (isRecording) { // Stop recording setIsRecording(false); captureButtonRef.current?.stop(); await cameraRef.current.stopRecording(); } else { // Start recording setIsRecording(true); captureButtonRef.current?.start(5000); // 5 seconds animation await cameraRef.current.startRecording({ onRecordingFinished: (video) => { onCapture(`file://${video.path}`, "video"); }, onRecordingError: (error) => { console.error("Recording error:", error); react_native_1.Alert.alert("Error", "Failed to record video"); }, }); } } else { // Photo mode const photo = await cameraRef.current.takePhoto(); onCapture(`file://${photo.path}`, "photo"); } } catch (error) { console.error("Camera capture error:", error); react_native_1.Alert.alert("Error", "Failed to capture media"); setIsRecording(false); captureButtonRef.current?.stop(); } }; const handleGalleryPress = async () => { const options = { mediaType: "mixed", quality: 1, includeBase64: false, }; try { const response = await (0, react_native_image_picker_1.launchImageLibrary)(options); if (response.didCancel) { return; } if (response.errorCode) { react_native_1.Alert.alert("Error", `Gallery error: ${response.errorMessage}`); return; } if (response.assets && response.assets.length > 0) { const asset = response.assets[0]; if (asset.uri) { const type = asset.type?.startsWith("video") ? "video" : "photo"; onGallerySelect(asset.uri, type); } } } catch (error) { console.error("Gallery error:", error); react_native_1.Alert.alert("Error", "Failed to pick media from gallery"); } }; const toggleCamera = () => { setCameraType((prev) => (prev === "back" ? "front" : "back")); }; if (!hasAllPermissions) { return ((0, jsx_runtime_1.jsxs)(react_native_1.View, { style: styles.container, children: [_settings.isVideoActive && ((0, jsx_runtime_1.jsx)(react_native_1.View, { style: styles.topControls, children: (0, jsx_runtime_1.jsxs)(react_native_1.View, { style: styles.modeToggleContainer, children: [(0, jsx_runtime_1.jsx)(react_native_1.TouchableOpacity, { style: [ styles.modeButton, mode === "video" && styles.modeButtonActive, ], onPress: () => setMode("video"), children: (0, jsx_runtime_1.jsx)(react_native_1.Text, { style: [ styles.modeButtonText, mode === "video" && styles.modeButtonTextActive, ], children: "\u0648\u06CC\u062F\u0626\u0648" }) }), (0, jsx_runtime_1.jsx)(react_native_1.TouchableOpacity, { style: [ styles.modeButton, mode === "photo" && styles.modeButtonActive, ], onPress: () => setMode("photo"), children: (0, jsx_runtime_1.jsx)(react_native_1.Text, { style: [ styles.modeButtonText, mode === "photo" && styles.modeButtonTextActive, ], children: "\u0639\u06A9\u0633" }) })] }) })), (0, jsx_runtime_1.jsxs)(react_native_1.View, { style: styles.permissionContainer, children: [(0, jsx_runtime_1.jsx)(react_native_1.Text, { style: styles.permissionText, children: "No access to camera" }), (0, jsx_runtime_1.jsx)(react_native_1.TouchableOpacity, { style: styles.permissionButton, onPress: async () => { if (!hasCameraPermission) { await requestCameraPermission(); } if (mode === "video" && !hasMicrophonePermission) { await requestMicrophonePermission(); } }, children: (0, jsx_runtime_1.jsx)(react_native_1.Text, { style: styles.permissionButtonText, children: "Grant Permissions" }) })] }), (0, jsx_runtime_1.jsxs)(react_native_1.View, { style: styles.bottomControls, children: [(0, jsx_runtime_1.jsxs)(react_native_1.TouchableOpacity, { style: styles.galleryButton, onPress: handleGalleryPress, children: [(0, jsx_runtime_1.jsx)(react_native_1.View, { style: styles.galleryIcon, children: (0, jsx_runtime_1.jsx)(Gallery_1.default, { width: 24, height: 24, fill: "#ffffff" }) }), (0, jsx_runtime_1.jsx)(react_native_1.Text, { style: styles.galleryText, children: "\u06AF\u0627\u0644\u0631\u06CC" })] }), (0, jsx_runtime_1.jsx)(CaptureButton_1.default, { ref: captureButtonRef, onPress: handleCapture, captureType: mode }), (0, jsx_runtime_1.jsxs)(react_native_1.TouchableOpacity, { style: styles.flipButton, onPress: toggleCamera, children: [(0, jsx_runtime_1.jsx)(react_native_1.View, { style: styles.flipIcon, children: (0, jsx_runtime_1.jsx)(Rotate_1.default, { width: 24, height: 24, stroke: "#ffffff", strokeWidth: 1.5 }) }), (0, jsx_runtime_1.jsx)(react_native_1.Text, { style: styles.flipText, children: "\u0628\u0686\u0631\u062E" })] })] })] })); } if (!device) { return ((0, jsx_runtime_1.jsxs)(react_native_1.View, { style: styles.container, children: [_settings.isVideoActive && ((0, jsx_runtime_1.jsx)(react_native_1.View, { style: styles.topControls, children: (0, jsx_runtime_1.jsxs)(react_native_1.View, { style: styles.modeToggleContainer, children: [(0, jsx_runtime_1.jsx)(react_native_1.TouchableOpacity, { style: [ styles.modeButton, mode === "video" && styles.modeButtonActive, ], onPress: () => setMode("video"), children: (0, jsx_runtime_1.jsx)(react_native_1.Text, { style: [ styles.modeButtonText, mode === "video" && styles.modeButtonTextActive, ], children: "\u0648\u06CC\u062F\u0626\u0648" }) }), (0, jsx_runtime_1.jsx)(react_native_1.TouchableOpacity, { style: [ styles.modeButton, mode === "photo" && styles.modeButtonActive, ], onPress: () => setMode("photo"), children: (0, jsx_runtime_1.jsx)(react_native_1.Text, { style: [ styles.modeButtonText, mode === "photo" && styles.modeButtonTextActive, ], children: "\u0639\u06A9\u0633" }) })] }) })), (0, jsx_runtime_1.jsx)(react_native_1.View, { style: styles.loadingContainer, children: (0, jsx_runtime_1.jsx)(react_native_1.Text, { style: styles.loadingText, children: "Camera not available" }) }), (0, jsx_runtime_1.jsxs)(react_native_1.View, { style: styles.bottomControls, children: [(0, jsx_runtime_1.jsxs)(react_native_1.TouchableOpacity, { style: styles.galleryButton, onPress: handleGalleryPress, children: [(0, jsx_runtime_1.jsx)(react_native_1.View, { style: styles.galleryIcon, children: (0, jsx_runtime_1.jsx)(Gallery_1.default, { width: 24, height: 24, fill: "#ffffff" }) }), (0, jsx_runtime_1.jsx)(react_native_1.Text, { style: styles.galleryText, children: "\u06AF\u0627\u0644\u0631\u06CC" })] }), (0, jsx_runtime_1.jsx)(CaptureButton_1.default, { ref: captureButtonRef, onPress: handleCapture, captureType: mode }), (0, jsx_runtime_1.jsxs)(react_native_1.TouchableOpacity, { style: styles.flipButton, onPress: toggleCamera, children: [(0, jsx_runtime_1.jsx)(react_native_1.View, { style: styles.flipIcon, children: (0, jsx_runtime_1.jsx)(Rotate_1.default, { width: 24, height: 24, stroke: "#ffffff", strokeWidth: 1.5 }) }), (0, jsx_runtime_1.jsx)(react_native_1.Text, { style: styles.flipText, children: "\u0628\u0686\u0631\u062E" })] })] })] })); } return ((0, jsx_runtime_1.jsxs)(react_native_1.View, { style: styles.container, children: [_settings.isVideoActive && ((0, jsx_runtime_1.jsx)(react_native_1.View, { style: styles.topControls, children: (0, jsx_runtime_1.jsxs)(react_native_1.View, { style: styles.modeToggleContainer, children: [(0, jsx_runtime_1.jsx)(react_native_1.TouchableOpacity, { style: [ styles.modeButton, mode === "video" && styles.modeButtonActive, ], onPress: () => setMode("video"), children: (0, jsx_runtime_1.jsx)(react_native_1.Text, { style: [ styles.modeButtonText, mode === "video" && styles.modeButtonTextActive, ], children: "\u0648\u06CC\u062F\u0626\u0648" }) }), (0, jsx_runtime_1.jsx)(react_native_1.TouchableOpacity, { style: [ styles.modeButton, mode === "photo" && styles.modeButtonActive, ], onPress: () => setMode("photo"), children: (0, jsx_runtime_1.jsx)(react_native_1.Text, { style: [ styles.modeButtonText, mode === "photo" && styles.modeButtonTextActive, ], children: "\u0639\u06A9\u0633" }) })] }) })), (0, jsx_runtime_1.jsx)(react_native_1.View, { style: styles.cameraPreview, children: (0, jsx_runtime_1.jsx)(react_native_vision_camera_1.Camera, { ref: cameraRef, style: styles.camera, device: device, isActive: true, photo: true, video: true, audio: mode === "video" }) }), (0, jsx_runtime_1.jsxs)(react_native_1.View, { style: styles.bottomControls, children: [(0, jsx_runtime_1.jsxs)(react_native_1.TouchableOpacity, { style: styles.galleryButton, onPress: handleGalleryPress, children: [(0, jsx_runtime_1.jsx)(react_native_1.View, { style: styles.galleryIcon, children: (0, jsx_runtime_1.jsx)(Gallery_1.default, { width: 24, height: 24, fill: "#ffffff" }) }), (0, jsx_runtime_1.jsx)(react_native_1.Text, { style: styles.galleryText, children: "\u06AF\u0627\u0644\u0631\u06CC" })] }), (0, jsx_runtime_1.jsx)(CaptureButton_1.default, { ref: captureButtonRef, onPress: handleCapture, captureType: mode }), (0, jsx_runtime_1.jsxs)(react_native_1.TouchableOpacity, { style: styles.flipButton, onPress: toggleCamera, children: [(0, jsx_runtime_1.jsx)(react_native_1.View, { style: styles.flipIcon, children: (0, jsx_runtime_1.jsx)(Rotate_1.default, { width: 24, height: 24, stroke: "#ffffff", strokeWidth: 1.5 }) }), (0, jsx_runtime_1.jsx)(react_native_1.Text, { style: styles.flipText, children: "\u0628\u0686\u0631\u062E" })] })] })] })); }; const styles = react_native_1.StyleSheet.create({ container: { flex: 1, backgroundColor: "rgb(38, 38, 38)", flexDirection: "column", }, topControls: { height: 100, paddingTop: react_native_1.Platform.OS === "ios" ? 50 : 20, paddingBottom: 20, backgroundColor: "rgb(38, 38, 38)", alignItems: "center", zIndex: 10, }, modeToggleContainer: { flexDirection: "row", backgroundColor: "rgb(38, 38, 38)", borderRadius: 8, borderColor: "rgb(141, 143, 144)", borderWidth: 1, padding: 5, minWidth: 200, }, modeButton: { flex: 1, paddingVertical: 12, paddingHorizontal: 24, borderRadius: 21, alignItems: "center", justifyContent: "center", }, modeButtonActive: { backgroundColor: "#ffffff", }, modeButtonText: { color: "#ffffff", fontSize: 16, fontWeight: "600", }, modeButtonTextActive: { color: "#2a2a2a", }, cameraPreview: { flex: 1, backgroundColor: "#1a1a1a", margin: 10, }, camera: { flex: 1, width: "100%", }, bottomControls: { flexDirection: "row", justifyContent: "space-between", alignItems: "center", paddingHorizontal: 20, paddingBottom: 40, backgroundColor: "rgba(0, 0, 0, 0.8)", paddingTop: 20, }, galleryButton: { alignItems: "center", minWidth: 60, }, galleryIcon: { width: 40, height: 40, borderRadius: 20, backgroundColor: "rgba(255, 255, 255, 0.2)", justifyContent: "center", alignItems: "center", marginBottom: 4, }, galleryText: { color: "#ffffff", fontSize: 12, textAlign: "center", }, flipButton: { alignItems: "center", minWidth: 60, }, flipIcon: { width: 40, height: 40, borderRadius: 20, backgroundColor: "rgba(255, 255, 255, 0.2)", justifyContent: "center", alignItems: "center", marginBottom: 4, }, flipText: { color: "#ffffff", fontSize: 12, textAlign: "center", }, loadingContainer: { flex: 1, justifyContent: "center", alignItems: "center", }, loadingText: { color: "#ffffff", fontSize: 16, }, permissionContainer: { flex: 1, justifyContent: "center", alignItems: "center", padding: 20, }, permissionText: { color: "#ffffff", fontSize: 18, marginBottom: 20, textAlign: "center", }, permissionButton: { backgroundColor: "#007AFF", paddingHorizontal: 20, paddingVertical: 12, borderRadius: 8, marginBottom: 10, }, permissionButtonText: { color: "#ffffff", fontSize: 16, fontWeight: "600", }, }); exports.default = CameraComponent;