scanQrPage.tsx 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676
  1. // import { CameraView, useCameraPermissions } from 'expo-camera';
  2. // import { useEffect, useRef, useState } from 'react';
  3. // import { ActivityIndicator, Dimensions, Pressable, ScrollView, StyleSheet, Text, Vibration, View } from 'react-native';
  4. // import ChooseCarForChargingRow from '../../../../component/global/chooseCarForChargingRow';
  5. // import { CrossLogoWhiteSvg, QuestionSvg } from '../../../../component/global/SVG';
  6. // import { router } from 'expo-router';
  7. // import { chargeStationService } from '../../../../service/chargeStationService';
  8. // import { authenticationService } from '../../../../service/authService';
  9. // import { walletService } from '../../../../service/walletService';
  10. // const ScanQrPage = () => {
  11. // const [permission, requestPermission] = useCameraPermissions();
  12. // const [scanned, setScanned] = useState(false);
  13. // const viewRef = useRef(null);
  14. // const [scannedResult, setScannedResult] = useState('');
  15. // const [selectedCar, setSelectedCar] = useState('');
  16. // const [userID, setUserID] = useState<string>('');
  17. // const now = new Date();
  18. // useEffect(() => {
  19. // (async () => {
  20. // const { status } = await requestPermission();
  21. // if (status !== 'granted') {
  22. // alert('需要相機權限以掃描QR碼');
  23. // }
  24. // })();
  25. // }, []);
  26. // useEffect(() => {
  27. // const fetchID = async () => {
  28. // try {
  29. // const response = await authenticationService.getUserInfo();
  30. // if (response) {
  31. // setUserID(response.data.id);
  32. // } else {
  33. // console.log('fail to set user ID');
  34. // }
  35. // } catch (error) {
  36. // console.log(error);
  37. // }
  38. // };
  39. // fetchID();
  40. // }, []);
  41. // // useEffect(() => {
  42. // // console.log(selectedCar);
  43. // // }, [selectedCar]);
  44. // if (!permission) {
  45. // return <View />;
  46. // }
  47. // if (!permission.granted) {
  48. // return (
  49. // <View className="flex-1 justify-center items-center">
  50. // <Text style={{ textAlign: 'center' }}>需要相機權限以掃描QR碼,請在設定中開啟相機權限</Text>
  51. // </View>
  52. // );
  53. // }
  54. // const { width: screenWidth, height: screenHeight } = Dimensions.get('window');
  55. // const handleBarCodeScanned = ({ bounds, data, type }: { bounds: any; data: any; type: any }) => {
  56. // const { origin, size } = bounds;
  57. // // Calculate the size of the square transparent area
  58. // const transparentAreaSize = Math.min(screenWidth * 0.6, screenHeight * 0.3);
  59. // const transparentAreaX = (screenWidth - transparentAreaSize) / 2;
  60. // const transparentAreaY = (screenHeight - transparentAreaSize) / 2;
  61. // // Check if the barcode is within the transparent area
  62. // if (
  63. // origin.x >= transparentAreaX &&
  64. // origin.y >= transparentAreaY &&
  65. // origin.x + size.width <= transparentAreaX + transparentAreaSize &&
  66. // origin.y + size.height <= transparentAreaY + transparentAreaSize
  67. // ) {
  68. // setScanned(true);
  69. // setScannedResult(data);
  70. // Vibration.vibrate(100);
  71. // console.log(` type: ${type} data: ${data} typeofData ${typeof data}`);
  72. // startCharging(data);
  73. // setTimeout(() => {
  74. // setScanned(false);
  75. // }, 5000);
  76. // }
  77. // };
  78. // const ChooseCar = () => {
  79. // const [loading, setLoading] = useState(false);
  80. // const isLargeScreen = screenHeight >= 800;
  81. // const [carData, setCarData] = useState([]);
  82. // const defaultImageUrl = require('../../../../assets/car1.png');
  83. // useEffect(() => {
  84. // const fetchAllCars = async () => {
  85. // setLoading(true);
  86. // try {
  87. // const response = await chargeStationService.getUserCars();
  88. // if (response) {
  89. // // console.log(response.data);
  90. // const carTypes = response.data.map((item: any) => ({
  91. // id: item.id,
  92. // name: item.car_type.name,
  93. // image: item.car_type.type_image_url
  94. // }));
  95. // // console.log('aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa', carTypes);
  96. // let updatedCarTypes = [...carTypes];
  97. // for (let i = 0; i < carTypes.length; i++) {
  98. // const car = updatedCarTypes[i];
  99. // const imageUrl = await chargeStationService.getProcessedImageUrl(car.image);
  100. // updatedCarTypes[i] = {
  101. // ...car,
  102. // image: imageUrl
  103. // };
  104. // }
  105. // setCarData(updatedCarTypes);
  106. // return true;
  107. // }
  108. // } catch (error) {
  109. // console.log(error);
  110. // } finally {
  111. // setLoading(false);
  112. // }
  113. // };
  114. // fetchAllCars();
  115. // }, []);
  116. // return (
  117. // <View
  118. // style={{
  119. // ...(isLargeScreen
  120. // ? {
  121. // marginTop: '10%',
  122. // marginBottom: '12%',
  123. // paddingBottom: 12
  124. // }
  125. // : {
  126. // flex: 1,
  127. // alignItems: 'center',
  128. // justifyContent: 'center'
  129. // })
  130. // }}
  131. // >
  132. // <View className="justify-center items-center flex-1 ">
  133. // <View
  134. // style={{
  135. // ...(isLargeScreen
  136. // ? {}
  137. // : {
  138. // backgroundColor: 'rgba(0,0,0,0.7)'
  139. // })
  140. // }}
  141. // >
  142. // {loading ? (
  143. // <View className="w-full">
  144. // <ActivityIndicator color="#34657b" />
  145. // </View>
  146. // ) : (
  147. // <View className="w-full bg-[#000000B3]">
  148. // <View className="flex-row items-center justify-between mx-[5%] ">
  149. // <Pressable
  150. // className="pt-4 "
  151. // onPress={() => {
  152. // if (router.canGoBack()) {
  153. // router.back();
  154. // } else {
  155. // router.replace('mainPage');
  156. // }
  157. // }}
  158. // >
  159. // <CrossLogoWhiteSvg />
  160. // </Pressable>
  161. // <Text className="text-base text-white pt-2">選擇充電車輛</Text>
  162. // <Text className="text-xl text-white pt-2"></Text>
  163. // </View>
  164. // <ScrollView
  165. // horizontal={true}
  166. // showsHorizontalScrollIndicator={false}
  167. // contentContainerStyle={{
  168. // alignItems: 'center',
  169. // flexDirection: 'row',
  170. // marginVertical: 12
  171. // }}
  172. // className="space-x-2 mx-[5%]"
  173. // >
  174. // {carData.map((car, index) => (
  175. // <ChooseCarForChargingRow
  176. // key={`${car.name}+${index}`}
  177. // image={car.image}
  178. // onPress={() => {
  179. // setSelectedCar(car.id);
  180. // console.log(car.id);
  181. // }}
  182. // isSelected={selectedCar === car.id}
  183. // // imageUrl={image}
  184. // VehicleName={car.name}
  185. // isDefault={car.isDefault}
  186. // />
  187. // ))}
  188. // </ScrollView>
  189. // </View>
  190. // )}
  191. // </View>
  192. // </View>
  193. // </View>
  194. // );
  195. // };
  196. // const dataForSubmission = {
  197. // stationID: '2405311022116801000',
  198. // connector: scannedResult,
  199. // user: userID,
  200. // book_time: now,
  201. // end_time: now,
  202. // total_power: 0,
  203. // total_fee: 0,
  204. // promotion_code: '',
  205. // car: selectedCar,
  206. // type: 'walking'
  207. // };
  208. // const startCharging = async () => {
  209. // try {
  210. // const response = await walletService.submitPayment(
  211. // dataForSubmission.stationID,
  212. // dataForSubmission.connector,
  213. // dataForSubmission.user,
  214. // dataForSubmission.book_time,
  215. // dataForSubmission.end_time,
  216. // dataForSubmission.total_power,
  217. // dataForSubmission.total_fee,
  218. // dataForSubmission.promotion_code,
  219. // dataForSubmission.car,
  220. // dataForSubmission.type
  221. // );
  222. // if (response) {
  223. // console.log('Charging started', response);
  224. // router.push('(auth)/(tabs)/(charging)/chargingPage');
  225. // } else {
  226. // console.log('Failed to start charging:', response);
  227. // }
  228. // } catch (error) {
  229. // console.log('Failed to start charging:', error);
  230. // }
  231. // };
  232. // return (
  233. // <View style={styles.container} ref={viewRef}>
  234. // <CameraView
  235. // style={styles.camera}
  236. // facing="back"
  237. // barcodeScannerSettings={{
  238. // barcodeTypes: ['qr']
  239. // }}
  240. // onBarcodeScanned={scanned ? undefined : handleBarCodeScanned}
  241. // responsiveOrientationWhenOrientationLocked={true}
  242. // >
  243. // <View style={styles.overlay}>
  244. // <View style={styles.topOverlay}>
  245. // <ChooseCar />
  246. // </View>
  247. // <View style={styles.centerRow}>
  248. // <View style={styles.leftOverlay}></View>
  249. // <View style={styles.transparentArea}></View>
  250. // <View style={styles.rightOverlay} />
  251. // </View>
  252. // <View className="items-center justify-between" style={styles.bottomOverlay}>
  253. // <View>
  254. // <Text className="text-white text-lg font-bold mt-2 ">請掃瞄充電座上的二維碼</Text>
  255. // </View>
  256. // <View className="flex-row space-x-2 items-center ">
  257. // <QuestionSvg />
  258. // <Pressable onPress={() => router.push('assistancePage')}>
  259. // <Text className="text-white text-base">需要協助?</Text>
  260. // </Pressable>
  261. // </View>
  262. // <View />
  263. // </View>
  264. // </View>
  265. // </CameraView>
  266. // </View>
  267. // );
  268. // };
  269. // const styles = StyleSheet.create({
  270. // container: {
  271. // flex: 1
  272. // },
  273. // camera: {
  274. // flex: 1
  275. // },
  276. // overlay: {
  277. // flex: 1
  278. // },
  279. // topOverlay: {
  280. // flex: 35,
  281. // alignItems: 'center',
  282. // backgroundColor: 'rgba(0,0,0,0.5)'
  283. // },
  284. // centerRow: {
  285. // flex: 30,
  286. // flexDirection: 'row'
  287. // },
  288. // leftOverlay: {
  289. // flex: 20,
  290. // backgroundColor: 'rgba(0,0,0,0.5)'
  291. // },
  292. // transparentArea: {
  293. // flex: 60,
  294. // aspectRatio: 1,
  295. // position: 'relative'
  296. // },
  297. // rightOverlay: {
  298. // flex: 20,
  299. // backgroundColor: 'rgba(0,0,0,0.5)'
  300. // },
  301. // bottomOverlay: {
  302. // flex: 35,
  303. // backgroundColor: 'rgba(0,0,0,0.5)'
  304. // }
  305. // });
  306. // export default ScanQrPage;
  307. import { CameraView, useCameraPermissions } from 'expo-camera';
  308. import { useEffect, useRef, useState } from 'react';
  309. import {
  310. ActivityIndicator,
  311. Alert,
  312. Dimensions,
  313. Pressable,
  314. ScrollView,
  315. StyleSheet,
  316. Text,
  317. Vibration,
  318. View
  319. } from 'react-native';
  320. import ChooseCarForChargingRow from '../../../../component/global/chooseCarForChargingRow';
  321. import { CrossLogoWhiteSvg, QuestionSvg } from '../../../../component/global/SVG';
  322. import { router } from 'expo-router';
  323. import { chargeStationService } from '../../../../service/chargeStationService';
  324. import { authenticationService } from '../../../../service/authService';
  325. import { walletService } from '../../../../service/walletService';
  326. const ScanQrPage = () => {
  327. const [permission, requestPermission] = useCameraPermissions();
  328. const [scanned, setScanned] = useState(false);
  329. const viewRef = useRef(null);
  330. const [scannedResult, setScannedResult] = useState('');
  331. const [selectedCar, setSelectedCar] = useState('');
  332. const [userID, setUserID] = useState<string>('');
  333. const now = new Date();
  334. const [loading, setLoading] = useState(false);
  335. const [carData, setCarData] = useState([]);
  336. useEffect(() => {
  337. (async () => {
  338. const { status } = await requestPermission();
  339. if (status !== 'granted') {
  340. alert('需要相機權限以掃描QR碼');
  341. }
  342. })();
  343. }, []);
  344. useEffect(() => {
  345. const fetchAllCars = async () => {
  346. setLoading(true);
  347. try {
  348. const response = await chargeStationService.getUserCars();
  349. if (response) {
  350. // console.log(response.data);
  351. const carTypes = response.data.map((item: any) => ({
  352. id: item.id,
  353. name: item.car_type.name,
  354. image: item.car_type.type_image_url
  355. }));
  356. // console.log('aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa', carTypes);
  357. let updatedCarTypes = [...carTypes];
  358. for (let i = 0; i < carTypes.length; i++) {
  359. const car = updatedCarTypes[i];
  360. const imageUrl = await chargeStationService.getProcessedImageUrl(car.image);
  361. updatedCarTypes[i] = {
  362. ...car,
  363. image: imageUrl
  364. };
  365. }
  366. setCarData(updatedCarTypes);
  367. return true;
  368. }
  369. } catch (error) {
  370. console.log(error);
  371. } finally {
  372. setLoading(false);
  373. }
  374. };
  375. fetchAllCars();
  376. }, []);
  377. useEffect(() => {
  378. const fetchID = async () => {
  379. try {
  380. const response = await authenticationService.getUserInfo();
  381. if (response) {
  382. setUserID(response.data.id);
  383. } else {
  384. console.log('fail to set user ID');
  385. }
  386. } catch (error) {
  387. console.log(error);
  388. }
  389. };
  390. fetchID();
  391. }, []);
  392. useEffect(() => {
  393. console.log(selectedCar);
  394. }, [selectedCar]);
  395. if (!permission) {
  396. return <View />;
  397. }
  398. if (!permission.granted) {
  399. return (
  400. <View className="flex-1 justify-center items-center">
  401. <Text style={{ textAlign: 'center' }}>需要相機權限以掃描QR碼,請在設定中開啟相機權限</Text>
  402. </View>
  403. );
  404. }
  405. const { width: screenWidth, height: screenHeight } = Dimensions.get('window');
  406. const handleBarCodeScanned = ({ bounds, data, type }: { bounds: any; data: any; type: any }) => {
  407. const { origin, size } = bounds;
  408. // Calculate the size of the square transparent area
  409. const transparentAreaSize = Math.min(screenWidth * 0.6, screenHeight * 0.3);
  410. const transparentAreaX = (screenWidth - transparentAreaSize) / 2;
  411. const transparentAreaY = (screenHeight - transparentAreaSize) / 2;
  412. // Check if the barcode is within the transparent area
  413. if (
  414. origin.x >= transparentAreaX &&
  415. origin.y >= transparentAreaY &&
  416. origin.x + size.width <= transparentAreaX + transparentAreaSize &&
  417. origin.y + size.height <= transparentAreaY + transparentAreaSize
  418. ) {
  419. setScanned(true);
  420. setScannedResult(data);
  421. Vibration.vibrate(100);
  422. console.log(` type: ${type} data: ${data} typeofData ${typeof data}`);
  423. startCharging(data);
  424. setTimeout(() => {
  425. setScanned(false);
  426. }, 2000);
  427. }
  428. };
  429. const ChooseCar = ({ carData, loading, selectedCar, setSelectedCar }) => {
  430. const isLargeScreen = screenHeight >= 800;
  431. const defaultImageUrl = require('../../../../assets/car1.png');
  432. return (
  433. <View
  434. style={{
  435. ...(isLargeScreen
  436. ? {
  437. marginTop: '10%',
  438. marginBottom: '12%',
  439. paddingBottom: 12
  440. }
  441. : {
  442. flex: 1,
  443. alignItems: 'center',
  444. justifyContent: 'center'
  445. })
  446. }}
  447. >
  448. <View className="justify-center items-center flex-1 ">
  449. <View
  450. style={{
  451. ...(isLargeScreen
  452. ? {}
  453. : {
  454. backgroundColor: 'rgba(0,0,0,0.7)'
  455. })
  456. }}
  457. >
  458. {loading ? (
  459. <View className="w-full">
  460. <ActivityIndicator color="#34657b" />
  461. </View>
  462. ) : (
  463. <View className="w-full bg-[#000000B3]">
  464. <View className="flex-row items-center justify-between mx-[5%] ">
  465. <Pressable
  466. className="pt-4 "
  467. onPress={() => {
  468. if (router.canGoBack()) {
  469. router.back();
  470. } else {
  471. router.replace('mainPage');
  472. }
  473. }}
  474. >
  475. <CrossLogoWhiteSvg />
  476. </Pressable>
  477. <Text className="text-base text-white pt-2">選擇充電車輛</Text>
  478. <Text className="text-xl text-white pt-2"></Text>
  479. </View>
  480. <ScrollView
  481. horizontal={true}
  482. showsHorizontalScrollIndicator={false}
  483. contentContainerStyle={{
  484. alignItems: 'center',
  485. flexDirection: 'row',
  486. marginVertical: 12
  487. }}
  488. className="space-x-2 mx-[5%]"
  489. >
  490. {carData.map((car, index) => (
  491. <ChooseCarForChargingRow
  492. key={`${car.name}+${index}`}
  493. image={car.image}
  494. onPress={() => {
  495. setSelectedCar(car.id);
  496. console.log(car.id);
  497. }}
  498. isSelected={selectedCar === car.id}
  499. // imageUrl={image}
  500. VehicleName={car.name}
  501. isDefault={car.isDefault}
  502. />
  503. ))}
  504. </ScrollView>
  505. </View>
  506. )}
  507. </View>
  508. </View>
  509. </View>
  510. );
  511. };
  512. const dataForSubmission = {
  513. stationID: '2405311022116801000',
  514. connector: scannedResult,
  515. user: userID,
  516. book_time: now,
  517. end_time: now,
  518. total_power: 0,
  519. total_fee: 0,
  520. promotion_code: '',
  521. car: selectedCar,
  522. type: 'walking'
  523. };
  524. const startCharging = async (scanResult) => {
  525. try {
  526. if (selectedCar === '') {
  527. Alert.alert('請選擇車輛');
  528. return;
  529. }
  530. const response = await walletService.submitPayment(
  531. dataForSubmission.stationID,
  532. scanResult,
  533. dataForSubmission.user,
  534. dataForSubmission.book_time,
  535. dataForSubmission.end_time,
  536. dataForSubmission.total_power,
  537. dataForSubmission.total_fee,
  538. dataForSubmission.promotion_code,
  539. dataForSubmission.car,
  540. dataForSubmission.type
  541. );
  542. if (response) {
  543. console.log('Charging stasdasdasdarted', response);
  544. router.push('(auth)/(tabs)/(charging)/chargingPage');
  545. } else {
  546. console.log('Failed to start chargi12312312ng:', response);
  547. Alert.alert('掃描失敗 請稍後再試。', response);
  548. }
  549. } catch (error) {
  550. console.log('Failed to start charging:', error);
  551. }
  552. };
  553. return (
  554. <View style={styles.container} ref={viewRef}>
  555. <CameraView
  556. style={styles.camera}
  557. facing="back"
  558. barcodeScannerSettings={{
  559. barcodeTypes: ['qr']
  560. }}
  561. onBarcodeScanned={scanned ? undefined : handleBarCodeScanned}
  562. responsiveOrientationWhenOrientationLocked={true}
  563. >
  564. <View style={styles.overlay}>
  565. <View style={styles.topOverlay}>
  566. <ChooseCar
  567. carData={carData}
  568. loading={loading}
  569. selectedCar={selectedCar}
  570. setSelectedCar={setSelectedCar}
  571. />
  572. </View>
  573. <View style={styles.centerRow}>
  574. <View style={styles.leftOverlay}></View>
  575. <View style={styles.transparentArea}></View>
  576. <View style={styles.rightOverlay} />
  577. </View>
  578. <View className="items-center justify-between" style={styles.bottomOverlay}>
  579. <View>
  580. <Text className="text-white text-lg font-bold mt-2 ">請掃瞄充電座上的二維碼</Text>
  581. </View>
  582. <View className="flex-row space-x-2 items-center ">
  583. <QuestionSvg />
  584. <Pressable onPress={() => router.push('assistancePage')}>
  585. <Text className="text-white text-base">需要協助?</Text>
  586. </Pressable>
  587. </View>
  588. <View />
  589. </View>
  590. </View>
  591. </CameraView>
  592. </View>
  593. );
  594. };
  595. const styles = StyleSheet.create({
  596. container: {
  597. flex: 1
  598. },
  599. camera: {
  600. flex: 1
  601. },
  602. overlay: {
  603. flex: 1
  604. },
  605. topOverlay: {
  606. flex: 35,
  607. alignItems: 'center',
  608. backgroundColor: 'rgba(0,0,0,0.5)'
  609. },
  610. centerRow: {
  611. flex: 30,
  612. flexDirection: 'row'
  613. },
  614. leftOverlay: {
  615. flex: 20,
  616. backgroundColor: 'rgba(0,0,0,0.5)'
  617. },
  618. transparentArea: {
  619. flex: 60,
  620. aspectRatio: 1,
  621. position: 'relative'
  622. },
  623. rightOverlay: {
  624. flex: 20,
  625. backgroundColor: 'rgba(0,0,0,0.5)'
  626. },
  627. bottomOverlay: {
  628. flex: 35,
  629. backgroundColor: 'rgba(0,0,0,0.5)'
  630. }
  631. });
  632. export default ScanQrPage;