import React, { useEffect, useMemo, useState } from 'react'; import { View, Text, StyleSheet, TouchableOpacity, Alert, Modal, Dimensions, } from 'react-native'; import { CameraView, useCameraPermissions } from 'expo-camera'; import { useRouter } from 'expo-router'; import { MaterialCommunityIcons } from '@expo/vector-icons'; import * as hrefs from '../../navigation/hrefs'; import { spacing, fontSizes, borderRadius, useAppColors, type AppColors } from '../../theme'; const { width, height } = Dimensions.get('window'); interface QRCodeScannerProps { visible: boolean; onClose: () => void; } function createQrScannerStyles(colors: AppColors) { return StyleSheet.create({ container: { flex: 1, backgroundColor: '#000', }, camera: { flex: 1, }, overlay: { flex: 1, backgroundColor: 'rgba(0,0,0,0.5)', }, header: { flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', paddingHorizontal: spacing.md, paddingTop: spacing.xl, paddingBottom: spacing.md, }, closeButton: { padding: spacing.sm, }, headerTitle: { fontSize: fontSizes.lg, fontWeight: '600', color: '#fff', }, placeholder: { width: 40, }, scanArea: { flex: 1, justifyContent: 'center', alignItems: 'center', }, scanFrame: { width: width * 0.7, height: width * 0.7, position: 'relative', }, corner: { position: 'absolute', width: 20, height: 20, borderColor: colors.primary.main, borderWidth: 3, }, topLeft: { top: 0, left: 0, borderRightWidth: 0, borderBottomWidth: 0, }, topRight: { top: 0, right: 0, borderLeftWidth: 0, borderBottomWidth: 0, }, bottomLeft: { bottom: 0, left: 0, borderRightWidth: 0, borderTopWidth: 0, }, bottomRight: { bottom: 0, right: 0, borderLeftWidth: 0, borderTopWidth: 0, }, scanText: { marginTop: spacing.lg, fontSize: fontSizes.md, color: '#fff', textAlign: 'center', }, footer: { padding: spacing.xl, alignItems: 'center', }, flashButton: { padding: spacing.md, backgroundColor: 'rgba(255,255,255,0.2)', borderRadius: borderRadius.full, }, permissionContainer: { flex: 1, justifyContent: 'center', alignItems: 'center', padding: spacing.xl, }, permissionText: { marginTop: spacing.lg, fontSize: fontSizes.md, color: 'rgba(255,255,255,0.72)', textAlign: 'center', }, permissionButton: { marginTop: spacing.xl, paddingVertical: spacing.md, paddingHorizontal: spacing.xl, backgroundColor: colors.primary.main, borderRadius: borderRadius.md, }, permissionButtonText: { color: '#fff', fontSize: fontSizes.md, fontWeight: '600', }, }); } export const QRCodeScanner: React.FC = ({ visible, onClose }) => { const router = useRouter(); const themeColors = useAppColors(); const styles = useMemo(() => createQrScannerStyles(themeColors), [themeColors]); const [permission, requestPermission] = useCameraPermissions(); const [scanned, setScanned] = useState(false); useEffect(() => { if (visible) { setScanned(false); if (!permission?.granted) { requestPermission(); } } }, [visible]); const handleBarCodeScanned = ({ type, data }: { type: string; data: string }) => { if (scanned) return; setScanned(true); onClose(); if (data.startsWith('carrotbbs://qrcode/login')) { const sessionId = extractSessionId(data); if (sessionId) { router.push(hrefs.hrefQrLoginConfirm(sessionId)); } else { Alert.alert('无效的二维码', '无法识别该二维码'); } } else { Alert.alert('无效的二维码', '请扫描网页端的登录二维码'); } }; const extractSessionId = (url: string): string | null => { try { const sessionIdMatch = url.match(/session_id=([^&]+)/); return sessionIdMatch ? sessionIdMatch[1] : null; } catch { return null; } }; if (!permission?.granted) { return ( 扫码登录 需要相机权限才能扫码 授予权限 ); } return ( 扫码登录 将二维码放入框内即可扫描 {}}> ); }; export default QRCodeScanner;