1import { UnavailabilityError } from '@unimodules/core'; 2import { PermissionResponse, PermissionStatus } from 'expo-modules-core'; 3import * as React from 'react'; 4import { Platform, ViewProps } from 'react-native'; 5 6import ExpoBarCodeScannerModule from './ExpoBarCodeScannerModule'; 7import ExpoBarCodeScannerView from './ExpoBarCodeScannerView'; 8 9const { BarCodeType, Type } = ExpoBarCodeScannerModule; 10 11const EVENT_THROTTLE_MS = 500; 12 13export type BarCodePoint = { 14 x: number; 15 y: number; 16}; 17 18export type BarCodeSize = { 19 height: number; 20 width: number; 21}; 22 23export type BarCodeBounds = { 24 origin: BarCodePoint; 25 size: BarCodeSize; 26}; 27 28export type BarCodeScannerResult = { 29 type: string; 30 data: string; 31 bounds?: BarCodeBounds; 32 cornerPoints?: BarCodePoint[]; 33}; 34 35export type BarCodeEvent = BarCodeScannerResult & { 36 target?: number; 37}; 38 39export type BarCodeEventCallbackArguments = { 40 nativeEvent: BarCodeEvent; 41}; 42 43export type BarCodeScannedCallback = (params: BarCodeEvent) => void; 44 45export { PermissionResponse, PermissionStatus }; 46 47export interface BarCodeScannerProps extends ViewProps { 48 type?: 'front' | 'back' | number; 49 barCodeTypes?: string[]; 50 onBarCodeScanned?: BarCodeScannedCallback; 51} 52 53export class BarCodeScanner extends React.Component<BarCodeScannerProps> { 54 lastEvents: { [key: string]: any } = {}; 55 lastEventsTimes: { [key: string]: any } = {}; 56 57 static Constants = { 58 BarCodeType, 59 Type, 60 }; 61 62 static ConversionTables = { 63 type: Type, 64 }; 65 66 static defaultProps = { 67 type: Type.back, 68 barCodeTypes: Object.values(BarCodeType), 69 }; 70 71 static async getPermissionsAsync(): Promise<PermissionResponse> { 72 return ExpoBarCodeScannerModule.getPermissionsAsync(); 73 } 74 75 static async requestPermissionsAsync(): Promise<PermissionResponse> { 76 return ExpoBarCodeScannerModule.requestPermissionsAsync(); 77 } 78 79 static async scanFromURLAsync( 80 url: string, 81 barCodeTypes: string[] = Object.values(BarCodeType) 82 ): Promise<BarCodeScannerResult[]> { 83 if (!ExpoBarCodeScannerModule.scanFromURLAsync) { 84 throw new UnavailabilityError('expo-barcode-scanner', 'scanFromURLAsync'); 85 } 86 if (Array.isArray(barCodeTypes) && !barCodeTypes.length) { 87 throw new Error('No barCodeTypes specified; provide at least one barCodeType for scanner'); 88 } 89 90 if (Platform.OS === 'ios') { 91 if (Array.isArray(barCodeTypes) && !barCodeTypes.includes(BarCodeType.qr)) { 92 // Only QR type is supported on iOS, fail if one tries to use other types 93 throw new Error('Only QR type is supported by scanFromURLAsync() on iOS'); 94 } 95 // on iOS use only supported QR type 96 return await ExpoBarCodeScannerModule.scanFromURLAsync(url, [BarCodeType.qr]); 97 } 98 99 // On other platforms, if barCodeTypes is not provided, use all available types 100 return await ExpoBarCodeScannerModule.scanFromURLAsync(url, barCodeTypes); 101 } 102 103 render() { 104 const nativeProps = this.convertNativeProps(this.props); 105 const { onBarCodeScanned } = this.props; 106 return ( 107 <ExpoBarCodeScannerView 108 {...nativeProps} 109 onBarCodeScanned={this.onObjectDetected(onBarCodeScanned)} 110 /> 111 ); 112 } 113 114 onObjectDetected = (callback?: BarCodeScannedCallback) => ({ 115 nativeEvent, 116 }: BarCodeEventCallbackArguments) => { 117 const { type } = nativeEvent; 118 if ( 119 this.lastEvents[type] && 120 this.lastEventsTimes[type] && 121 JSON.stringify(nativeEvent) === this.lastEvents[type] && 122 Date.now() - this.lastEventsTimes[type] < EVENT_THROTTLE_MS 123 ) { 124 return; 125 } 126 127 if (callback) { 128 callback(nativeEvent); 129 this.lastEventsTimes[type] = new Date(); 130 this.lastEvents[type] = JSON.stringify(nativeEvent); 131 } 132 }; 133 134 convertNativeProps(props: BarCodeScannerProps) { 135 const nativeProps: BarCodeScannerProps = {}; 136 137 for (const [key, value] of Object.entries(props)) { 138 if (typeof value === 'string' && BarCodeScanner.ConversionTables[key]) { 139 nativeProps[key] = BarCodeScanner.ConversionTables[key][value]; 140 } else { 141 nativeProps[key] = value; 142 } 143 } 144 145 return nativeProps; 146 } 147} 148 149export const { Constants, getPermissionsAsync, requestPermissionsAsync } = BarCodeScanner; 150