import { CameraView, useCameraPermissions } from 'expo-camera'; import { useEffect, useRef, useState } from 'react'; import { ActivityIndicator, Alert, Dimensions, Pressable, ScrollView, StyleSheet, Text, Vibration, View } from 'react-native'; import ChooseCarForChargingRow from '../../../../component/global/chooseCarForChargingRow'; import { CrossLogoWhiteSvg, QuestionSvg } from '../../../../component/global/SVG'; import { router } from 'expo-router'; import { chargeStationService } from '../../../../service/chargeStationService'; import { authenticationService } from '../../../../service/authService'; import { walletService } from '../../../../service/walletService'; import useUserInfoStore from '../../../../providers/userinfo_store'; const { width: screenWidth, height: screenHeight } = Dimensions.get('window'); const ChooseCar = ({ carData, loading, selectedCar, setSelectedCar }) => { const isLargeScreen = screenHeight >= 800; const defaultImageUrl = require('../../../../assets/car1.png'); return ( {loading ? ( ) : ( { if (router.canGoBack()) { router.back(); } else { router.replace('mainPage'); } }} > 選擇充電車輛 {carData.map((car, index) => ( { setSelectedCar(car.id); console.log(car.id); }} isSelected={selectedCar === car.id} // imageUrl={image} VehicleName={car.name} isDefault={car.isDefault} /> ))} )} ); }; //reminder: scan qr code page, ic call should be false const ScanQrPage = () => { const { userID, setUserID } = useUserInfoStore(); // State declarations const [permission, requestPermission] = useCameraPermissions(); const [scanned, setScanned] = useState(false); const viewRef = useRef(null); const [scannedResult, setScannedResult] = useState(''); const [selectedCar, setSelectedCar] = useState(''); const now = new Date(); const [loading, setLoading] = useState(true); const [loading2, setLoading2] = useState(false); const [loading3, setLoading3] = useState(false); const [carData, setCarData] = useState([]); // Effect for requesting camera permissions useEffect(() => { (async () => { const { status } = await requestPermission(); if (status !== 'granted') { alert( '我們需要相機權限來掃描機器上的二維碼,以便識別並啟動充電機器。我們不會儲存或共享任何掃描到的資訊。 請前往設定開啟相機權限' ); } })(); }, []); // Effect for fetching user's cars useEffect(() => { const fetchAllCars = async () => { try { const response = await chargeStationService.getUserCars(); if (response) { // console.log(response.data); const carTypes = response.data.map((item: any) => ({ id: item.id, name: item.car_type.name, image: item.car_type.type_image_url })); console.log('carTypes', carTypes); // console.log('aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa', carTypes); let updatedCarTypes = [...carTypes]; for (let i = 0; i < carTypes.length; i++) { const car=updatedCarTypes[i]; const imageUrl=await chargeStationService.getProcessedImageUrl(car.image); updatedCarTypes[i]={ ...car, image: imageUrl }; } setCarData(updatedCarTypes); console.log('updatedCarTypes', updatedCarTypes); return true; } } catch (error) { console.log(error); } finally { setLoading(false); } }; fetchAllCars(); }, []); if (!permission) { return ; } if (!permission.granted) { return ( 我們需要相機權限來掃描機器上的二維碼,以便識別並啟動充電機器。我們不會儲存或共享任何掃描到的資訊。 請前往設定開啟相機權限 ); } // Function to handle barcode scanning const handleBarCodeScanned = async ({ bounds, data, type }: { bounds: any; data: any; type: any }) => { const { origin, size } = bounds; // Calculate the size of the square transparent area const transparentAreaSize = Math.min(screenWidth * 0.6, screenHeight * 0.3); const transparentAreaX = (screenWidth - transparentAreaSize) / 2; const transparentAreaY = (screenHeight - transparentAreaSize) / 2; // Check if the barcode is within the transparent area if ( origin.x >= transparentAreaX && origin.y >= transparentAreaY && origin.x + size.width <= transparentAreaX + transparentAreaSize && origin.y + size.height <=transparentAreaY + transparentAreaSize ) { setScanned(true); setScannedResult(data); Vibration.vibrate(100); console.log(` type: ${type} data: ${data} typeofData ${typeof data}`); try { const response=await chargeStationService.getTodayReservation(); if (response) { const now=new Date(); const onlyThisConnector=response.filter( (reservation: any)=> reservation.connector.ConnectorID === data ); onlyThisConnector.sort((a: any, b: any) => { const timeA = new Date(a.book_time).getTime(); const timeB = new Date(b.book_time).getTime(); return timeA - timeB; }); let previousReservation = null; let upcomingReservation = null; for (let i = 0; i < onlyThisConnector.length; i++) { const reservationTime=new Date(onlyThisConnector[i].book_time).getTime(); if (reservationTime <=now.getTime()) { previousReservation=onlyThisConnector[i]; } else { upcomingReservation=onlyThisConnector[i]; break; } } const relevantReservations=[previousReservation || null, upcomingReservation || null]; console.log('Relevant reservations:', relevantReservations); const getNearestSlot=(date: Date)=> { const minutes = date.getMinutes(); const nearestSlot = new Date(date); nearestSlot.setMinutes(minutes < 30 ? 0 : 30); nearestSlot.setSeconds(0); nearestSlot.setMilliseconds(0); return nearestSlot; }; const currentSlot=getNearestSlot(now); const nextSlot=new Date(currentSlot.getTime() + 30 * 60 * 1000); const AreOrdersAdjacentToNow=relevantReservations.map((reservation, index)=> { if (!reservation) return false; const reservationTime = new Date(reservation.book_time); return index === 0 ? reservationTime.getTime() === currentSlot.getTime() : reservationTime.getTime() === nextSlot.getTime(); }); console.log('AreOrdersAdjacentToNow:', AreOrdersAdjacentToNow); if (!AreOrdersAdjacentToNow[0] && !AreOrdersAdjacentToNow[1]) { console.log('Charging machine is available. Starting charging process...'); startCharging(data); } else if (AreOrdersAdjacentToNow[0]) { const previousReservation = relevantReservations[0]; if (previousReservation && previousReservation.user.id === userID) { const reservationTime = new Date(previousReservation.book_time); const timeDifference = now.getTime() - reservationTime.getTime(); const minutesDifference = timeDifference / (1000 * 60); if (minutesDifference <= 15) { console.log('User arrived within 15 minutes of their reservation.'); startCharging(data); } else { console.log('User arrived more than 15 minutes late for their reservation.'); if (!AreOrdersAdjacentToNow[1]) { console.log('Next slot is available. Allowing charging despite late arrival.'); startCharging(data); } else { Alert.alert( '預約已過期' , '您的預約時間已經過期,且下一個時段已被預約。請重新預約。' ); console.log('Next slot is not available. Charging not allowed.'); } } } else { Alert.alert('無法使用', '此充電槍已經被預約,請選擇其他位置' ); } } else if (AreOrdersAdjacentToNow[1]) { const upcomingReservation=relevantReservations[1]; if (upcomingReservation && upcomingReservation.user.id===userID) { const minutesUntilReservation=(new Date(upcomingReservation.book_time).getTime() - now.getTime()) / (1000 * 60); if (minutesUntilReservation <=5) { console.log('User arrived slightly early for their upcoming reservation.'); startCharging(data); } else { Alert.alert( '預約時間未到' , `您的預約時間還有 ${Math.round(minutesUntilReservation)} 分鐘開始。請稍後再試。` ); } } else { Alert.alert('已被預約', '此充電槍已被其他用戶預約。請選擇其他位置。' ); } } else { Alert.alert('無法使用', '此充電槍目前無法使用。請選擇其他位置或稍後再試。' ); } } else { console.log('No response from getTodayReservation'); Alert.alert('系統錯誤', '無法獲取預約信息。請稍後再試。' ); } } catch (error) { console.error("Error fetching today's reservations:", error); Alert.alert('系統錯誤', '發生未知錯誤。請稍後再試。' ); } setTimeout(()=> { setScanned(false); }, 2000); } }; //prepare data for submission const dataForSubmission = { stationID: '2405311022116801000', connector: scannedResult, user: userID, book_time: now, end_time: now, total_power: 0, total_fee: 0, promotion_code: '', car: selectedCar, type: 'walking', is_ic_call: false }; const startCharging = async (scanResult: string) => { try { if (selectedCar === '') { Alert.alert('請選擇車輛'); return; } const response = await walletService.submitPayment( dataForSubmission.stationID, scanResult, dataForSubmission.user, dataForSubmission.book_time, dataForSubmission.end_time, dataForSubmission.total_power, dataForSubmission.total_fee, dataForSubmission.promotion_code, dataForSubmission.car, dataForSubmission.type, dataForSubmission.is_ic_call ); if (response) { console.log('Charging started from startCharging', response); router.push('(auth)/(tabs)/(charging)/chargingPage'); } else { console.log('Failed to start chargi12312312ng:', response); Alert.alert('掃描失敗 請稍後再試。', response); } } catch (error) { console.log('Failed to start charging:', error); } }; return ( {loading ? ( ) : ( 請選擇充電車輛{'\n'}及掃瞄充電座上的二維碼 router.push('assistancePage')}> 需要協助? )} ); }; const styles = StyleSheet.create({ container: { flex: 1 }, camera: { flex: 1 }, overlay: { flex: 1 }, topOverlay: { flex: 35, alignItems: 'center', backgroundColor: 'rgba(0,0,0,0.5)' }, centerRow: { flex: 30, flexDirection: 'row' }, leftOverlay: { flex: 20, backgroundColor: 'rgba(0,0,0,0.5)' }, transparentArea: { flex: 60, aspectRatio: 1, position: 'relative' }, rightOverlay: { flex: 20, backgroundColor: 'rgba(0,0,0,0.5)' }, bottomOverlay: { flex: 35, backgroundColor: 'rgba(0,0,0,0.5)' } }); export default ScanQrPage;