| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052 |
- import { CameraView, useCameraPermissions } from 'expo-camera';
- import { useEffect, useRef, useState } from 'react';
- import {
- ActivityIndicator,
- Alert,
- AppState,
- Dimensions,
- Linking,
- Pressable,
- ScrollView,
- StyleSheet,
- Text,
- Vibration,
- View
- } from 'react-native';
- import sha256 from 'crypto-js/sha256';
- 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';
- import Modal from 'react-native-modal';
- import NormalButton from '../../../../component/global/normal_button';
- import { ceil } from 'lodash';
- import AsyncStorage from '@react-native-async-storage/async-storage';
- 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 (
- // <View
- // style={{
- // ...(isLargeScreen
- // ? {
- // marginTop: '10%',
- // marginBottom: '12%',
- // paddingBottom: 12
- // }
- // : {
- // flex: 1,
- // alignItems: 'center',
- // justifyContent: 'center'
- // })
- // }}
- // >
- // <View className="justify-center items-center flex-1 ">
- // <View
- // style={{
- // ...(isLargeScreen
- // ? {}
- // : {
- // backgroundColor: 'rgba(0,0,0,0.7)'
- // })
- // }}
- // >
- // {loading ? (
- // <View className="w-full">
- // <ActivityIndicator color="#34657b" />
- // </View>
- // ) : (
- // <View className="w-screen bg-[#000000B3]">
- // <View className="flex-row items-center justify-between mx-[5%] ">
- // <Pressable
- // className="pt-4 "
- // onPress={() => {
- // if (router.canGoBack()) {
- // router.back();
- // } else {
- // router.replace('mainPage');
- // }
- // }}
- // >
- // <CrossLogoWhiteSvg />
- // </Pressable>
- // <Text className="text-base text-white pt-2">選擇充電車輛</Text>
- // <Text className="text-xl text-white pt-2"></Text>
- // </View>
- // <ScrollView
- // horizontal={true}
- // showsHorizontalScrollIndicator={false}
- // contentContainerStyle={{
- // alignItems: 'center',
- // flexDirection: 'row',
- // marginVertical: 12
- // }}
- // className="space-x-2 mx-[5%]"
- // >
- // {carData.map((car, index) => (
- // <ChooseCarForChargingRow
- // key={`${car.name}+${index}`}
- // image={car.image}
- // onPress={() => {
- // setSelectedCar(car.id);
- // console.log(car.id);
- // }}
- // isSelected={selectedCar === car.id}
- // // imageUrl={image}
- // VehicleName={car.name}
- // isDefault={car.isDefault}
- // />
- // ))}
- // </ScrollView>
- // </View>
- // )}
- // </View>
- // </View>
- // </View>
- // );
- // };
- //reminder: scan qr code page, ic call should be false
- const ScanQrPage = () => {
- const { userID, currentPrice } = useUserInfoStore();
- const { width: screenWidth, height: screenHeight } = Dimensions.get('window');
- 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([]);
- const [isModalVisible, setModalVisible] = useState(false);
- const [isConfirmLoading, setIsConfirmLoading] = useState(false);
- const [availableSlots, setAvailableSlots] = useState({
- 25: false,
- 30: false,
- 40: false,
- 45: false,
- full: false
- });
- const [selectedDuration, setSelectedDuration] = useState(null);
- const appState = useRef(AppState.currentState);
- const [paymentStatus, setPaymentStatus] = useState(null);
- const [isExpectingPayment, setIsExpectingPayment] = useState(false);
- const paymentInitiatedTime = useRef(null);
- const PAYMENT_CHECK_TIMEOUT = 5 * 60 * 1000; // 5 minutes in milliseconds
- const [outTradeNo, setOutTradeNo] = useState('');
- const [totalFee, setTotalFee] = useState(0);
- // 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('data', response.data);
- // const carTypes = response.data.map((item: any) => ({
- // id: item.id,
- // name: item.car_typ e.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();
- // }, []);
- useEffect(() => {
- const fetchDefaultCar = async () => {
- try {
- const response = await chargeStationService.getUserDefaultCars();
- if (response) {
- console.log('default car', response.data.id);
- setSelectedCar(response.data.id);
- }
- } catch (error) {
- console.log(error);
- } finally {
- setLoading(false);
- }
- };
- fetchDefaultCar();
- }, []);
- const planMap = {
- 25: { duration: 40, kWh: 20, displayDuration: 25, fee: 20 * currentPrice },
- 30: { duration: 45, kWh: 25, displayDuration: 30, fee: 25 * currentPrice },
- 40: { duration: 55, kWh: 30, displayDuration: 40, fee: 30 * currentPrice },
- 45: { duration: 60, kWh: 40, displayDuration: 45, fee: 40 * currentPrice },
- full: { duration: 120, displayDuration: '充滿停機', fee: 80 * currentPrice }
- };
- // Function to handle barcode scanning
- const handleBarCodeScanned = async ({ bounds, data, type }: { bounds?: any; data: any; type: any }) => {
- if (
- !bounds ||
- typeof bounds.origin?.x !== 'number' ||
- typeof bounds.origin?.y !== 'number' ||
- typeof bounds.size?.width !== 'number' ||
- typeof bounds.size?.height !== 'number'
- ) {
- console.log('Invalid or missing bounds data:', bounds);
- // Proceed with scanning logic without bounds checking
- 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
- );
- // Check availability for each duration
- const availability = {
- 25: checkAvailability(onlyThisConnector, now, 40),
- 30: checkAvailability(onlyThisConnector, now, 45),
- 40: checkAvailability(onlyThisConnector, now, 55),
- 45: checkAvailability(onlyThisConnector, now, 60),
- full: checkAvailability(onlyThisConnector, now, 120)
- };
- setAvailableSlots(availability);
- setModalVisible(true);
- } else {
- Alert.alert('系統錯誤', '無法獲取預約信息。請稍後再試。');
- }
- } catch (error) {
- console.error("Error fetching today's reservations:", error);
- Alert.alert('系統錯誤', '發生未知錯誤。請稍後再試。');
- }
- setTimeout(() => {
- setScanned(false);
- }, 2000);
- return;
- }
- 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
- );
- console.log('onlyThisConnector', onlyThisConnector);
- // Check availability for each duration
- const availability = {
- 25: checkAvailability(onlyThisConnector, now, 40),
- 30: checkAvailability(onlyThisConnector, now, 45),
- 40: checkAvailability(onlyThisConnector, now, 55),
- 45: checkAvailability(onlyThisConnector, now, 60),
- full: checkAvailability(onlyThisConnector, now, 120)
- };
- setAvailableSlots(availability);
- setModalVisible(true);
- } else {
- Alert.alert('系統錯誤', '無法獲取預約信息。請稍後再試。');
- }
- } catch (error) {
- console.error("Error fetching today's reservations:", error);
- Alert.alert('系統錯誤', '發生未知錯誤。請稍後再試。');
- }
- setTimeout(() => {
- setScanned(false);
- }, 2000);
- }
- };
- const checkAvailability = (reservations, startTime, duration) => {
- const endTime = new Date(startTime.getTime() + duration * 60000);
- console.log('now', startTime);
- console.log('endTime', endTime);
- console.log('reservations', reservations);
- return !reservations.some((reservation) => {
- // Ignore reservations with status '9' (cancelled)
- if (reservation.status.id === '9') {
- return false;
- }
- // For status '8' (early finished), check actual_end_time
- if (reservation.status.id === '8' && reservation.actual_end_time) {
- const actualEndTime = new Date(reservation.actual_end_time);
- if (actualEndTime <= startTime) {
- return false; // Treat as available if actual end time is before or at start time
- }
- }
- const resStart = new Date(reservation.book_time);
- const resEnd = new Date(reservation.end_time);
- return startTime < resEnd && endTime > resStart;
- });
- };
- const handleDurationSelect = (duration) => {
- setSelectedDuration(duration);
- console.log(duration);
- };
- const handleCancel = () => {
- setSelectedDuration(null);
- setModalVisible(false);
- if (router.canGoBack()) {
- router.back();
- } else {
- router.push('/mainPage');
- }
- };
- const handleConfirm = () => {
- if (selectedDuration !== null) {
- const now = new Date();
- let endTime;
- let fee;
- let totalPower;
- if (selectedDuration === 'full') {
- endTime = new Date(now.getTime() + 2 * 60 * 60 * 1000); // 2 hours for "充滿停機"
- fee = planMap.full.fee;
- totalPower = 0; // Set to 0 for "充滿停機"
- } else {
- const durationInMinutes = parseInt(selectedDuration);
- endTime = new Date(now.getTime() + durationInMinutes * 60 * 1000);
- console.log('endTime', endTime);
- fee = planMap[selectedDuration].fee;
- totalPower = durationInMinutes; // Use the actual duration for other cases
- }
- setTotalFee(fee);
- const dataForSubmission = {
- stationID: '2405311022116801000',
- connector: scannedResult,
- user: userID,
- book_time: now,
- end_time: endTime,
- total_power: totalPower,
- total_fee: fee,
- promotion_code: '',
- car: selectedCar,
- type: 'walking',
- is_ic_call: false
- };
- startCharging(dataForSubmission);
- setIsConfirmLoading(true);
- }
- };
- //below commented is the original WORKING startCharging, if i fucked up, return back to using this!!!
- // const startCharging = async (dataForSubmission) => {
- // try {
- // const wallet = await walletService.getWalletBalance();
- // console.log('wallet in startCharging in scanQrPage', wallet);
- // const response = await walletService.submitPayment(
- // dataForSubmission.stationID,
- // dataForSubmission.connector,
- // 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.status === 200 || response.status === 201) {
- // setSelectedDuration(null);
- // console.log('Charging started from startCharging', response);
- // setIsConfirmLoading(false);
- // // Set a flag in AsyncStorage to indicate charging has started
- // await AsyncStorage.setItem('chargingStarted', 'true');
- // Alert.alert('啟動成功', '請稍後等待頁面自動跳轉至充電介面', [
- // {
- // text: 'OK',
- // onPress: async () => {
- // setModalVisible(false);
- // setLoading(true);
- // // Wait for 2 seconds
- // await new Promise((resolve) => setTimeout(resolve, 2000));
- // // Hide loading spinner and navigate
- // setLoading(false);
- // router.push('(auth)/(tabs)/(charging)/chargingPage');
- // }
- // }
- // ]);
- // // Start the navigation attempt loop
- // // startNavigationAttempts();
- // } else if (response.status === 400) {
- // console.log('400 error in paymentSummaryPageComponent');
- // Alert.alert('餘額不足', '您的餘額不足,請充值後再試。');
- // } else {
- // console.log('Failed to start charging:', response);
- // Alert.alert('掃描失敗 請稍後再試。', response);
- // }
- // } catch (error) {
- // console.log('Failed to start chasssssssrging:', error);
- // }
- // };
- //below is the new flow for startCharging.
- // useEffect(() => {
- // const subscription = AppState.addEventListener('change', (nextAppState) => {
- // if (
- // appState.current.match(/inactive|background/) &&
- // nextAppState === 'active' &&
- // isExpectingPayment &&
- // // outTradeNo &&
- // paymentInitiatedTime.current
- // ) {
- // const currentTime = new Date().getTime();
- // if (currentTime - paymentInitiatedTime.current < PAYMENT_CHECK_TIMEOUT) {
- // checkPaymentStatus();
- // } else {
- // // Payment check timeout reached
- // setIsExpectingPayment(false);
- // setOutTradeNo('');
- // paymentInitiatedTime.current = null;
- // Alert.alert(
- // 'Payment Timeout',
- // 'The payment status check has timed out. Please check your payment history.'
- // );
- // }
- // }
- // appState.current = nextAppState;
- // });
- // return () => {
- // subscription.remove();
- // };
- // }, [outTradeNo, isExpectingPayment]);
- //check payment status
- useEffect(() => {
- const subscription = AppState.addEventListener('change', (nextAppState) => {
- if (
- appState.current.match(/inactive|background/) &&
- nextAppState === 'active' &&
- isExpectingPayment &&
- // outTradeNo &&
- paymentInitiatedTime.current
- ) {
- const currentTime = new Date().getTime();
- if (currentTime - paymentInitiatedTime.current < PAYMENT_CHECK_TIMEOUT) {
- checkPaymentStatus();
- } else {
- // Payment check timeout reached
- setIsExpectingPayment(false);
- setOutTradeNo('');
- paymentInitiatedTime.current = null;
- Alert.alert(
- 'Payment Timeout',
- 'The payment status check has timed out. Please check your payment history.'
- );
- }
- }
- appState.current = nextAppState;
- });
- return () => {
- subscription.remove();
- };
- }, [outTradeNo, isExpectingPayment]);
- const checkPaymentStatus = async () => {
- try {
- console.log('outTradeNo in scanQR Page checkpaymentstatus ', outTradeNo);
- const result = await walletService.checkPaymentStatus(outTradeNo);
- setPaymentStatus(result);
- console.log('checkPaymentStatus from scan QR checkpaymentStatus', result);
- if (result && !result.some((item) => item.errmsg?.includes('處理中'))) {
- // Payment successful
- console.log('totalFee', totalFee);
- Alert.alert('付款已成功', `你已成功增值HKD $${totalFee}。請重新掃描去啟動充電槍。`, [
- {
- text: '確認',
- onPress: async () => {
- setModalVisible(false);
- router.dismiss();
- }
- }
- ]);
- } else {
- Alert.alert('付款失敗', '請再試一次。', [
- {
- text: '確定',
- onPress: () => {
- setModalVisible(false);
- router.dismiss();
- }
- }
- ]);
- }
- setIsExpectingPayment(false);
- setOutTradeNo('');
- paymentInitiatedTime.current = null;
- } catch (error) {
- console.error('Failed to check payment status:', error);
- Alert.alert('Error', 'Failed to check payment status. Please check your payment history.');
- }
- };
- function formatTime(utcTimeString) {
- // Parse the UTC time string
- const date = new Date(utcTimeString);
- // Add 8 hours
- date.setHours(date.getHours());
- // Format the date
- const year = date.getFullYear();
- const month = String(date.getMonth() + 1).padStart(2, '0');
- const day = String(date.getDate()).padStart(2, '0');
- const hours = String(date.getHours()).padStart(2, '0');
- const minutes = String(date.getMinutes()).padStart(2, '0');
- const seconds = String(date.getSeconds()).padStart(2, '0');
- // Return the formatted string
- return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
- }
- const oneTimeCharging = async (inputAmount) => {
- try {
- const response = await walletService.getOutTradeNo();
- console.log('outtradeno in oneTimeCharging', response);
- if (response) {
- setOutTradeNo(response);
- setIsExpectingPayment(true);
- paymentInitiatedTime.current = new Date().getTime();
- const now = new Date();
- const formattedTime = formatTime(now);
- console.log('formattedTime in oneTimeCharging', formattedTime);
- let amount = inputAmount * 100;
- console.log('amount in scanqr oneTimeCharging', amount);
- const origin = 'https://openapi-hk.qfapi.com/checkstand/#/?';
- const obj = {
- appcode: '6937EF25DF6D4FA78BB2285441BC05E9',
- goods_name: 'Crazy Charge 錢包增值',
- out_trade_no: response,
- paysource: 'crazycharge_checkout',
- return_url: 'https://www.google.com',
- failed_url: 'https://www.google.com',
- notify_url: 'https://api.crazycharge.com.hk/api/v1/clients/qfpay/webhook',
- sign_type: 'sha256',
- txamt: amount,
- txcurrcd: 'HKD',
- txdtm: formattedTime
- };
- const paramStringify = (json, flag?) => {
- let str = '';
- let keysArr = Object.keys(json);
- keysArr.sort().forEach((val) => {
- if (!json[val]) return;
- str += `${val}=${flag ? encodeURIComponent(json[val]) : json[val]}&`;
- });
- return str.slice(0, -1);
- };
- const api_key = '8F59E31F6ADF4D2894365F2BB6D2FF2C';
- const params = paramStringify(obj);
- const sign = sha256(`${params}${api_key}`).toString();
- const url = `${origin}${paramStringify(obj, true)}&sign=${sign}`;
- try {
- console.log(url);
- const supported = await Linking.canOpenURL(url);
- if (supported) {
- await Linking.openURL(url);
- } else {
- Alert.alert('錯誤', '請稍後再試');
- }
- } catch (error) {
- console.error('Top-up failed:', error);
- Alert.alert('Error', '一次性付款失敗,請稍後再試');
- }
- } else {
- console.log('nasdasdasdsdfgo');
- }
- } catch (error) {
- console.log(error);
- }
- };
- const startCharging = async (dataForSubmission) => {
- try {
- const wallet = await walletService.getWalletBalance();
- console.log('wallet in startCharging in scanQrPage', wallet);
- oneTimeCharging(dataForSubmission.total_fee);
- if (wallet < dataForSubmission.total_fee) {
- oneTimeCharging(dataForSubmission.total_fee);
- return;
- }
- const response = await walletService.submitPayment(
- dataForSubmission.stationID,
- dataForSubmission.connector,
- 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.status === 200 || response.status === 201) {
- setSelectedDuration(null);
- console.log('Charging started from startCharging', response);
- setIsConfirmLoading(false);
- // Set a flag in AsyncStorage to indicate charging has started
- await AsyncStorage.setItem('chargingStarted', 'true');
- Alert.alert('啟動成功', '請稍後等待頁面自動跳轉至充電介面', [
- {
- text: 'OK',
- onPress: async () => {
- setModalVisible(false);
- setLoading(true);
- // Wait for 2 seconds
- await new Promise((resolve) => setTimeout(resolve, 2000));
- // Hide loading spinner and navigate
- setLoading(false);
- router.push('(auth)/(tabs)/(charging)/chargingPage');
- }
- }
- ]);
- // Start the navigation attempt loop
- // startNavigationAttempts();
- } else if (response.status === 400) {
- console.log('400 error in paymentSummaryPageComponent');
- Alert.alert('餘額不足', '您的餘額不足,請充值後再試。');
- } else {
- console.log('Failed to start charging:', response);
- Alert.alert('掃描失敗 請稍後再試。', response);
- }
- } catch (error) {
- console.log('Failed to start chasssssssrging:', error);
- }
- };
- // const startNavigationAttempts = () => {
- // let attempts = 0;
- // const maxAttempts = 10; // Try for about 2.5 minutes (10 * 15 seconds)
- // const attemptNavigation = async () => {
- // try {
- // const chargingStarted = await AsyncStorage.getItem('chargingStarted');
- // if (chargingStarted === 'true') {
- // // Wait for 2 seconds before navigating
- // await new Promise((resolve) => setTimeout(resolve, 2000));
- // await AsyncStorage.removeItem('chargingStarted');
- // router.push('(auth)/(tabs)/(charging)/chargingPage');
- // // If navigation is successful, clear the flag
- // } else {
- // throw new Error('Navigation not ready');
- // }
- // } catch (error) {
- // attempts++;
- // if (attempts < maxAttempts) {
- // // If navigation fails, try again after 15 seconds
- // setTimeout(attemptNavigation, 15000);
- // } else {
- // // If all attempts fail, show an alert to the user
- // Alert.alert('導航失敗', '無法自動跳轉到充電頁面。請手動導航到充電頁面。', [
- // { text: 'OK', onPress: () => {} }
- // ]);
- // }
- // }
- // };
- // // Start the first attempt after 15 seconds
- // setTimeout(attemptNavigation, 15000);
- // };
- // return (
- // <View style={styles.container} ref={viewRef}>
- // {loading ? (
- // <View className="flex-1 items-center justify-center">
- // <ActivityIndicator />
- // </View>
- // ) : (
- // <CameraView
- // style={styles.camera}
- // facing="back"
- // barcodeScannerSettings={{
- // barcodeTypes: ['qr']
- // }}
- // onBarcodeScanned={scanned ? undefined : handleBarCodeScanned}
- // responsiveOrientationWhenOrientationLocked={true}
- // >
- // <View style={styles.overlay}>
- // <View style={styles.topOverlay}>
- // {/* <ChooseCar
- // carData={carData}
- // loading={loading}
- // selectedCar={selectedCar}
- // setSelectedCar={setSelectedCar}
- // /> */}
- // <Pressable
- // // style={styles.closeButton}
- // className="absolute top-20 left-10 z-10 "
- // onPress={() => {
- // if (router.canGoBack()) {
- // router.back();
- // } else {
- // router.push('/mainPage');
- // }
- // }}
- // >
- // <CrossLogoWhiteSvg />
- // </Pressable>
- // </View>
- // <View style={styles.centerRow}>
- // <View style={styles.leftOverlay}></View>
- // <View style={styles.transparentArea}></View>
- // <View style={styles.rightOverlay} />
- // </View>
- // <View className="items-center justify-between" style={styles.bottomOverlay}>
- // <View>
- // <Text className="text-white text-lg font-bold mt-2 text-center">
- // 請掃瞄充電座上的二維碼
- // </Text>
- // </View>
- // <View className="flex-row space-x-2 items-center ">
- // <QuestionSvg />
- // <Pressable onPress={() => router.push('assistancePage')}>
- // <Text className="text-white text-base">需要協助?</Text>
- // </Pressable>
- // </View>
- // <View />
- // </View>
- // </View>
- // </CameraView>
- // )}
- // <Modal isVisible={isModalVisible} backdropOpacity={0.5} animationIn="fadeIn" animationOut="fadeOut">
- // <View style={styles.modalContent} className="flex flex-col">
- // <Text className="text-xl font-bold mt-2 text-center">請選擇充電時間</Text>
- // <Text className="text-base m-2 mb-4 text-center">按鈕呈紅色代表該時段已被他人預約</Text>
- // <View className="flex flex-row flex-wrap ">
- // {Object.entries(availableSlots).map(([duration, available]) => (
- // <NormalButton
- // key={duration}
- // title={
- // duration === 'full' ? (
- // <Text className={selectedDuration === duration ? 'text-white' : ''}>
- // 充滿停機
- // </Text>
- // ) : (
- // <Text
- // className={selectedDuration === duration ? 'text-white' : ''}
- // >{`${planMap[duration].kWh} 度電 - ${planMap[duration].displayDuration} 分鐘`}</Text>
- // )
- // }
- // onPress={() => handleDurationSelect(duration)}
- // extendedStyle={[
- // styles.durationButton,
- // {
- // backgroundColor: available
- // ? selectedDuration === duration
- // ? '#02677d'
- // : 'white'
- // : 'red',
- // borderColor: available ? 'black' : 'red',
- // borderWidth: 1
- // }
- // ]}
- // disabled={!available}
- // />
- // ))}
- // </View>
- // {selectedDuration && (
- // <NormalButton
- // title={
- // isConfirmLoading ? (
- // <ActivityIndicator color="white" />
- // ) : (
- // <Text className="text-white">確認</Text>
- // )
- // }
- // onPress={handleConfirm}
- // extendedStyle={styles.confirmButton}
- // />
- // )}
- // <NormalButton
- // title={<Text className="">取消</Text>}
- // onPress={handleCancel}
- // extendedStyle={styles.cancelButton}
- // />
- // </View>
- // </Modal>
- // </View>
- // );
- return (
- <View style={styles.container} ref={viewRef}>
- {!permission ? (
- <View />
- ) : !permission.granted ? (
- <View className="flex-1 justify-center items-center">
- <Text style={{ textAlign: 'center' }}>
- 我們需要相機權限來掃描機器上的二維碼,以便識別並啟動充電機器。我們不會儲存或共享任何掃描到的資訊。
- 請前往設定開啟相機權限
- </Text>
- </View>
- ) : loading ? (
- <View className="flex-1 items-center justify-center">
- <ActivityIndicator />
- </View>
- ) : (
- <CameraView
- style={styles.camera}
- facing="back"
- barcodeScannerSettings={{
- barcodeTypes: ['qr']
- }}
- onBarcodeScanned={scanned ? undefined : handleBarCodeScanned}
- responsiveOrientationWhenOrientationLocked={true}
- >
- <View style={styles.overlay}>
- <View style={styles.topOverlay}>
- <Pressable
- className="absolute top-20 left-10 z-10 "
- onPress={() => {
- if (router.canGoBack()) {
- router.back();
- } else {
- router.push('/mainPage');
- }
- }}
- >
- <CrossLogoWhiteSvg />
- </Pressable>
- </View>
- <View style={styles.centerRow}>
- <View style={styles.leftOverlay}></View>
- <View style={styles.transparentArea}></View>
- <View style={styles.rightOverlay} />
- </View>
- <View className="items-center justify-between" style={styles.bottomOverlay}>
- <View>
- <Text className="text-white text-lg font-bold mt-2 text-center">
- 請掃瞄充電座上的二維碼
- </Text>
- </View>
- <View className="flex-row space-x-2 items-center ">
- <QuestionSvg />
- <Pressable onPress={() => router.push('assistancePage')}>
- <Text className="text-white text-base">需要協助?</Text>
- </Pressable>
- </View>
- <View />
- </View>
- </View>
- </CameraView>
- )}
- <Modal isVisible={isModalVisible} backdropOpacity={0.5} animationIn="fadeIn" animationOut="fadeOut">
- <View style={styles.modalContent} className="flex flex-col">
- <Text className="text-xl font-bold mt-2 text-center">請選擇充電時間</Text>
- <Text className="text-base m-2 mb-4 text-center">按鈕呈紅色代表該時段已被他人預約</Text>
- <View className="flex flex-row flex-wrap ">
- {Object.entries(availableSlots).map(([duration, available]) => (
- <NormalButton
- key={duration}
- title={
- duration === 'full' ? (
- <Text className={selectedDuration === duration ? 'text-white' : ''}>
- 充滿停機
- </Text>
- ) : (
- <Text
- className={selectedDuration === duration ? 'text-white' : ''}
- >{`${planMap[duration].kWh} 度電 - ${planMap[duration].displayDuration} 分鐘`}</Text>
- )
- }
- onPress={() => handleDurationSelect(duration)}
- extendedStyle={[
- styles.durationButton,
- {
- backgroundColor: available
- ? selectedDuration === duration
- ? '#02677d'
- : 'white'
- : 'red',
- borderColor: available ? 'black' : 'red',
- borderWidth: 1
- }
- ]}
- disabled={!available}
- />
- ))}
- </View>
- {selectedDuration && (
- <NormalButton
- title={
- isConfirmLoading ? (
- <ActivityIndicator color="white" />
- ) : (
- <Text className="text-white">確認</Text>
- )
- }
- onPress={handleConfirm}
- extendedStyle={styles.confirmButton}
- />
- )}
- <NormalButton
- title={<Text className="">取消</Text>}
- onPress={handleCancel}
- extendedStyle={styles.cancelButton}
- />
- </View>
- </Modal>
- </View>
- );
- };
- 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)'
- },
- closeButton: {
- position: 'absolute',
- top: 40,
- left: 20,
- zIndex: 1
- },
- modalContent: {
- backgroundColor: 'white',
- padding: 22,
- alignItems: 'center',
- borderRadius: 4,
- borderColor: 'rgba(0, 0, 0, 0.1)'
- },
- durationButton: { margin: 5 },
- confirmButton: {
- marginTop: 20,
- width: '100%'
- },
- cancelButton: {
- marginTop: 20,
- width: '100%',
- backgroundColor: 'white',
- borderColor: 'black',
- borderWidth: 1,
- color: 'black'
- }
- });
- export default ScanQrPage;
|