1import { PermissionResponse, PermissionStatus, UnavailabilityError } from 'expo-modules-core';
2
3function getUserMedia(constraints: MediaStreamConstraints): Promise<MediaStream> {
4  if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
5    return navigator.mediaDevices.getUserMedia(constraints);
6  }
7
8  // Some browsers partially implement mediaDevices. We can't just assign an object
9  // with getUserMedia as it would overwrite existing properties.
10  // Here, we will just add the getUserMedia property if it's missing.
11
12  // First get ahold of the legacy getUserMedia, if present
13  const getUserMedia =
14    navigator.getUserMedia ||
15    (navigator as any).webkitGetUserMedia ||
16    (navigator as any).mozGetUserMedia ||
17    function () {
18      const error: any = new Error('Permission unimplemented');
19      error.code = 0;
20      error.name = 'NotAllowedError';
21      throw error;
22    };
23
24  return new Promise((resolve, reject) => {
25    getUserMedia.call(navigator, constraints, resolve, reject);
26  });
27}
28
29function handleGetUserMediaError({ message }: { message: string }): PermissionResponse {
30  // name: NotAllowedError
31  // code: 0
32  if (message === 'Permission dismissed') {
33    return {
34      status: PermissionStatus.UNDETERMINED,
35      expires: 'never',
36      canAskAgain: true,
37      granted: false,
38    };
39  } else {
40    return {
41      status: PermissionStatus.DENIED,
42      expires: 'never',
43      canAskAgain: true,
44      granted: false,
45    };
46  }
47}
48
49async function handleRequestPermissionsAsync(): Promise<PermissionResponse> {
50  try {
51    await getUserMedia({
52      video: true,
53    });
54    return {
55      status: PermissionStatus.GRANTED,
56      expires: 'never',
57      canAskAgain: true,
58      granted: true,
59    };
60  } catch ({ message }) {
61    return handleGetUserMediaError({ message });
62  }
63}
64
65async function handlePermissionsQueryAsync(): Promise<PermissionResponse> {
66  if (!navigator?.permissions?.query) {
67    throw new UnavailabilityError(
68      'expo-barcode-scanner',
69      'navigator.permissions API is not available'
70    );
71  }
72
73  const { state } = await navigator.permissions.query({ name: 'camera' });
74  switch (state) {
75    case 'prompt':
76      return {
77        status: PermissionStatus.UNDETERMINED,
78        expires: 'never',
79        canAskAgain: true,
80        granted: false,
81      };
82    case 'granted':
83      return {
84        status: PermissionStatus.GRANTED,
85        expires: 'never',
86        canAskAgain: true,
87        granted: true,
88      };
89    case 'denied':
90      return {
91        status: PermissionStatus.DENIED,
92        expires: 'never',
93        canAskAgain: true,
94        granted: false,
95      };
96  }
97}
98
99export default {
100  get name(): string {
101    return 'ExpoBarCodeScannerModule';
102  },
103  get BarCodeType() {
104    return {
105      code39mod43: 'code39mod43',
106      code138: 'code138',
107      interleaved2of5: 'interleaved2of5',
108      aztec: 'aztec',
109      ean13: 'ean13',
110      ean8: 'ean8',
111      qr: 'qr',
112      pdf417: 'pdf417',
113      upc_e: 'upc_e',
114      datamatrix: 'datamatrix',
115      code39: 'code39',
116      code93: 'code93',
117      itf14: 'itf14',
118      codabar: 'codabar',
119      code128: 'code128',
120      upc_a: 'upc_a',
121    };
122  },
123  get Type() {
124    return { front: 'front', back: 'back' };
125  },
126  async requestPermissionsAsync(): Promise<PermissionResponse> {
127    return handleRequestPermissionsAsync();
128  },
129  async getPermissionsAsync(): Promise<PermissionResponse> {
130    return handlePermissionsQueryAsync();
131  },
132};
133