scanQrPage.tsx 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247
  1. import { CameraView, useCameraPermissions } from 'expo-camera';
  2. import { useEffect, useRef, useState } from 'react';
  3. import {
  4. Dimensions,
  5. Pressable,
  6. ScrollView,
  7. StyleSheet,
  8. Text,
  9. View
  10. } from 'react-native';
  11. import ChooseCarForChargingRow from '../../../../component/global/chooseCarForChargingRow';
  12. import {
  13. CrossLogoWhiteSvg,
  14. QuestionSvg
  15. } from '../../../../component/global/SVG';
  16. import { router } from 'expo-router';
  17. const ScanQrPage = () => {
  18. const [permission, requestPermission] = useCameraPermissions();
  19. const [scanned, setScanned] = useState(false);
  20. const viewRef = useRef(null);
  21. useEffect(() => {
  22. (async () => {
  23. const { status } = await requestPermission();
  24. if (status !== 'granted') {
  25. alert('Please grant camera permission to use the QR scanner');
  26. }
  27. })();
  28. }, []);
  29. if (!permission) {
  30. return <View />;
  31. }
  32. if (!permission.granted) {
  33. return (
  34. <View className="flex-1 justify-center items-center">
  35. <Text style={{ textAlign: 'center' }}>
  36. We need your permission to access the camera QR code
  37. function. Please go to your device settings and enable
  38. camera permissions for this application.
  39. </Text>
  40. </View>
  41. );
  42. }
  43. const { width: screenWidth, height: screenHeight } =
  44. Dimensions.get('window');
  45. const handleBarCodeScanned = ({ bounds, data, type }) => {
  46. const { origin, size } = bounds;
  47. // Calculate the size of the square transparent area
  48. const transparentAreaSize = Math.min(
  49. screenWidth * 0.6,
  50. screenHeight * 0.3
  51. );
  52. const transparentAreaX = (screenWidth - transparentAreaSize) / 2;
  53. const transparentAreaY = (screenHeight - transparentAreaSize) / 2;
  54. // Check if the barcode is within the transparent area
  55. if (
  56. origin.x >= transparentAreaX &&
  57. origin.y >= transparentAreaY &&
  58. origin.x + size.width <= transparentAreaX + transparentAreaSize &&
  59. origin.y + size.height <= transparentAreaY + transparentAreaSize
  60. ) {
  61. setScanned(true);
  62. console.log(` type: ${type} data: ${data} `);
  63. if (type === 'qr') {
  64. console.log('QR Code link:', data);
  65. }
  66. setTimeout(() => {
  67. setScanned(false);
  68. }, 5000);
  69. }
  70. };
  71. const dummyDataChooseCarForCharging = [
  72. {
  73. imageUrl: require('../../../../assets/car1.png'),
  74. VehicleName: 'TESLA - Model 3',
  75. isDefault: true
  76. },
  77. { VehicleName: 'TESLA - Model Y', isDefault: false },
  78. {
  79. imageUrl: require('../../../../assets/car1.png'),
  80. VehicleName: 'TESLA - Model X',
  81. isDefault: false
  82. },
  83. { VehicleName: 'TESLA - Model 3', isDefault: false }
  84. ];
  85. const ChooseCar = () => {
  86. const isLargeScreen = screenHeight >= 800;
  87. return (
  88. <View
  89. style={{
  90. ...(isLargeScreen
  91. ? {
  92. marginTop: '10%',
  93. marginBottom: '12%',
  94. paddingBottom: 12,
  95. backgroundColor: 'rgba(0,0,0,0.7)'
  96. }
  97. : {
  98. marginVertical: '5%',
  99. paddingBottom: 12,
  100. backgroundColor: 'rgba(0,0,0,0.7)'
  101. })
  102. }}
  103. >
  104. <View className="mx-[5%] items-center">
  105. <View className="flex-row items-center justify-between w-full">
  106. <View className="pt-2">
  107. <Pressable
  108. className=""
  109. onPress={() => {
  110. if (router.canGoBack()) {
  111. router.back();
  112. } else {
  113. router.replace('mainPage');
  114. }
  115. }}
  116. >
  117. <CrossLogoWhiteSvg />
  118. </Pressable>
  119. </View>
  120. <Text className="text-base text-white pt-2">
  121. 選擇充電車輛
  122. </Text>
  123. <Text className="text-xl text-white pt-2"></Text>
  124. </View>
  125. <ScrollView
  126. horizontal={true}
  127. showsHorizontalScrollIndicator={false}
  128. contentContainerStyle={{
  129. alignItems: 'center',
  130. flexDirection: 'row',
  131. marginVertical: 8
  132. }}
  133. className="space-x-2"
  134. >
  135. {dummyDataChooseCarForCharging.map((car, index) => (
  136. <ChooseCarForChargingRow
  137. navigationLink="/(auth)/(tabs)/(charging)/chargingPage"
  138. imageUrl={car.imageUrl}
  139. key={`${car.VehicleName}+${index}`}
  140. VehicleName={car.VehicleName}
  141. isDefault={car.isDefault}
  142. />
  143. ))}
  144. </ScrollView>
  145. </View>
  146. </View>
  147. );
  148. };
  149. return (
  150. <View style={styles.container} ref={viewRef}>
  151. <CameraView
  152. style={styles.camera}
  153. facing="back"
  154. barcodeScannerSettings={{
  155. barcodeTypes: ['qr']
  156. }}
  157. onBarcodeScanned={scanned ? undefined : handleBarCodeScanned}
  158. responsiveOrientationWhenOrientationLocked={true}
  159. >
  160. <View style={styles.overlay}>
  161. <View style={styles.topOverlay}>
  162. <ChooseCar />
  163. </View>
  164. <View style={styles.centerRow}>
  165. <View style={styles.leftOverlay}></View>
  166. <View style={styles.transparentArea}></View>
  167. <View style={styles.rightOverlay} />
  168. </View>
  169. <View
  170. className="items-center justify-between"
  171. style={styles.bottomOverlay}
  172. >
  173. <View>
  174. <Text className="text-white text-lg font-bold mt-2 ">
  175. 請掃瞄充電座上的二維碼
  176. </Text>
  177. </View>
  178. <View className="flex-row space-x-2 items-center ">
  179. <QuestionSvg />
  180. <Pressable onPress={() => console.log('需要協助?')}>
  181. <Text className="text-white text-base">
  182. 需要協助?
  183. </Text>
  184. </Pressable>
  185. </View>
  186. <View />
  187. </View>
  188. </View>
  189. </CameraView>
  190. </View>
  191. );
  192. };
  193. const styles = StyleSheet.create({
  194. container: {
  195. flex: 1
  196. },
  197. camera: {
  198. flex: 1
  199. },
  200. overlay: {
  201. flex: 1
  202. },
  203. topOverlay: {
  204. flex: 35,
  205. alignItems: 'center',
  206. backgroundColor: 'rgba(0,0,0,0.5)'
  207. },
  208. centerRow: {
  209. flex: 30,
  210. flexDirection: 'row'
  211. },
  212. leftOverlay: {
  213. flex: 20,
  214. backgroundColor: 'rgba(0,0,0,0.5)'
  215. },
  216. transparentArea: {
  217. flex: 60,
  218. aspectRatio: 1,
  219. position: 'relative'
  220. },
  221. rightOverlay: {
  222. flex: 20,
  223. backgroundColor: 'rgba(0,0,0,0.5)'
  224. },
  225. bottomOverlay: {
  226. flex: 35,
  227. backgroundColor: 'rgba(0,0,0,0.5)'
  228. }
  229. });
  230. export default ScanQrPage;