UNPKG

3.83 kBTypeScriptView Raw
1import { UnavailabilityError } from '@unimodules/core';
2import mapValues from 'lodash/mapValues';
3import PropTypes from 'prop-types';
4import React from 'react';
5import { Platform, ViewProps, ViewPropTypes } from 'react-native';
6
7import ExpoBarCodeScannerModule from './ExpoBarCodeScannerModule';
8import ExpoBarCodeScannerView from './ExpoBarCodeScannerView';
9
10const { BarCodeType, Type } = ExpoBarCodeScannerModule;
11
12const EVENT_THROTTLE_MS = 500;
13
14type BarCodeEvent = {
15 type: string;
16 data: string;
17 [key: string]: any;
18};
19
20export type BarCodeEventCallbackArguments = {
21 nativeEvent: BarCodeEvent;
22};
23
24export type BarCodeScannedCallback = (params: BarCodeEvent) => void;
25
26export interface BarCodeScannerProps extends ViewProps {
27 type?: 'front' | 'back' | number;
28 barCodeTypes?: string[];
29 onBarCodeScanned: BarCodeScannedCallback;
30}
31
32export class BarCodeScanner extends React.Component<BarCodeScannerProps> {
33 lastEvents: { [key: string]: any } = {};
34 lastEventsTimes: { [key: string]: any } = {};
35
36 static Constants = {
37 BarCodeType,
38 Type,
39 };
40
41 static ConversionTables = {
42 type: Type,
43 };
44
45 static propTypes = {
46 ...ViewPropTypes,
47 onBarCodeScanned: PropTypes.func,
48 barCodeTypes: PropTypes.array,
49 type: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
50 };
51
52 static defaultProps = {
53 type: Type.back,
54 barCodeTypes: Object.values(BarCodeType),
55 };
56
57 static async scanFromURLAsync(
58 url: string,
59 barCodeTypes: string[] = Object.values(BarCodeType)
60 ): Promise<{ type: string; data: string }> {
61 if (!ExpoBarCodeScannerModule.scanFromURLAsync) {
62 throw new UnavailabilityError('expo-barcode-scanner', 'scanFromURLAsync');
63 }
64 if (Array.isArray(barCodeTypes) && !barCodeTypes.length) {
65 throw new Error('No barCodeTypes specified; provide at least one barCodeType for scanner');
66 }
67
68 if (Platform.OS === 'ios') {
69 if (Array.isArray(barCodeTypes) && !barCodeTypes.includes(BarCodeType.qr)) {
70 // Only QR type is supported on iOS, fail if one tries to use other types
71 throw new Error('Only QR type is supported by scanFromURLAsync() on iOS');
72 }
73 // on iOS use only supported QR type
74 return await ExpoBarCodeScannerModule.scanFromURLAsync(url, [BarCodeType.qr]);
75 }
76
77 // On other platforms, if barCodeTypes is not provided, use all available types
78 return await ExpoBarCodeScannerModule.scanFromURLAsync(url, barCodeTypes);
79 }
80
81 render() {
82 const nativeProps = this.convertNativeProps(this.props);
83 const { onBarCodeScanned } = this.props;
84 return (
85 <ExpoBarCodeScannerView
86 {...nativeProps}
87 onBarCodeScanned={this.onObjectDetected(onBarCodeScanned)}
88 />
89 );
90 }
91
92 // coordinates of cornerPoints and boundingBox are represented in DP (Display-Indepent Points) unit
93 // React Native is using the same unit
94 onObjectDetected = (callback?: BarCodeScannedCallback) => ({
95 nativeEvent,
96 }: BarCodeEventCallbackArguments) => {
97 const { type } = nativeEvent;
98 if (
99 this.lastEvents[type] &&
100 this.lastEventsTimes[type] &&
101 JSON.stringify(nativeEvent) === this.lastEvents[type] &&
102 Date.now() - this.lastEventsTimes[type] < EVENT_THROTTLE_MS
103 ) {
104 return;
105 }
106
107 if (callback) {
108 callback(nativeEvent);
109 this.lastEventsTimes[type] = new Date();
110 this.lastEvents[type] = JSON.stringify(nativeEvent);
111 }
112 };
113
114 convertNativeProps(props: BarCodeScannerProps) {
115 const newProps = mapValues(props, this.convertProp);
116 return newProps;
117 }
118
119 convertProp(value: any, key: string): any {
120 if (typeof value === 'string' && BarCodeScanner.ConversionTables[key]) {
121 return BarCodeScanner.ConversionTables[key][value];
122 }
123 return value;
124 }
125}
126
127export const { Constants } = BarCodeScanner;