expo-camera 13.4.3 → 13.4.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -10,6 +10,12 @@
10
10
 
11
11
  ### 💡 Others
12
12
 
13
+ ## 13.4.4 — 2023-09-11
14
+
15
+ ### 🐛 Bug fixes
16
+
17
+ - Remove @koale/useworker. ([#23967](https://github.com/expo/expo/pull/23967) by [@marklawlor](https://github.com/marklawlor))
18
+
13
19
  ## 13.4.3 — 2023-08-29
14
20
 
15
21
  ### 🐛 Bug fixes
@@ -3,7 +3,7 @@ apply plugin: 'kotlin-android'
3
3
  apply plugin: 'maven-publish'
4
4
 
5
5
  group = 'host.exp.exponent'
6
- version = '13.4.3'
6
+ version = '13.4.4'
7
7
 
8
8
  buildscript {
9
9
  def expoModulesCorePlugin = new File(project(":expo-modules-core").projectDir.absolutePath, "ExpoModulesCorePlugin.gradle")
@@ -67,7 +67,7 @@ android {
67
67
  minSdkVersion safeExtGet("minSdkVersion", 21)
68
68
  targetSdkVersion safeExtGet("targetSdkVersion", 33)
69
69
  versionCode 32
70
- versionName "13.4.3"
70
+ versionName "13.4.4"
71
71
  }
72
72
 
73
73
  lintOptions {
@@ -1 +1 @@
1
- {"version":3,"file":"useWebQRScanner.d.ts","sourceRoot":"","sources":["../src/useWebQRScanner.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAE/B,OAAO,EAAE,qBAAqB,EAAE,oBAAoB,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AA2CjG,wBAAgB,eAAe,CAC7B,KAAK,EAAE,KAAK,CAAC,gBAAgB,CAAC,gBAAgB,GAAG,IAAI,CAAC,EACtD,EACE,SAAS,EACT,cAAc,EACd,QAAQ,EACR,SAAS,EACT,OAAO,GACR,EAAE;IACD,SAAS,EAAE,OAAO,CAAC;IACnB,cAAc,EAAE,IAAI,CAAC,oBAAoB,EAAE,OAAO,GAAG,eAAe,CAAC,CAAC;IACtE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,CAAC,cAAc,EAAE;QAAE,WAAW,EAAE,qBAAqB,CAAA;KAAE,KAAK,IAAI,CAAC;IAC7E,OAAO,CAAC,EAAE,kBAAkB,CAAC;CAC9B,QA8DF"}
1
+ {"version":3,"file":"useWebQRScanner.d.ts","sourceRoot":"","sources":["../src/useWebQRScanner.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAE/B,OAAO,EAAE,qBAAqB,EAAE,oBAAoB,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AAwEjG,wBAAgB,eAAe,CAC7B,KAAK,EAAE,KAAK,CAAC,gBAAgB,CAAC,gBAAgB,GAAG,IAAI,CAAC,EACtD,EACE,SAAS,EACT,cAAc,EACd,QAAQ,EACR,SAAS,EACT,OAAO,GACR,EAAE;IACD,SAAS,EAAE,OAAO,CAAC;IACnB,cAAc,EAAE,IAAI,CAAC,oBAAoB,EAAE,OAAO,GAAG,eAAe,CAAC,CAAC;IACtE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,CAAC,cAAc,EAAE;QAAE,WAAW,EAAE,qBAAqB,CAAA;KAAE,KAAK,IAAI,CAAC;IAC7E,OAAO,CAAC,EAAE,kBAAkB,CAAC;CAC9B,QAyDF"}
@@ -1,4 +1,3 @@
1
- import { useWorker } from '@koale/useworker';
2
1
  import * as React from 'react';
3
2
  import { captureImageData } from './WebCameraUtils';
4
3
  const qrWorkerMethod = ({ data, width, height }) => {
@@ -32,16 +31,35 @@ const qrWorkerMethod = ({ data, width, height }) => {
32
31
  }
33
32
  return parsed;
34
33
  };
35
- function useRemoteJsQR() {
36
- return useWorker(qrWorkerMethod, {
37
- remoteDependencies: ['https://cdn.jsdelivr.net/npm/jsqr@1.2.0/dist/jsQR.min.js'],
38
- autoTerminate: false,
39
- });
40
- }
34
+ const createWorkerAsyncFunction = (fn, deps) => {
35
+ const stringifiedFn = [
36
+ `self.func = ${fn.toString()};`,
37
+ 'self.onmessage = (e) => {',
38
+ ' const result = self.func(e.data);',
39
+ ' self.postMessage(result);',
40
+ '};',
41
+ ];
42
+ if (deps.length > 0) {
43
+ stringifiedFn.unshift(`importScripts(${deps.map((dep) => `'${dep}'`).join(', ')});`);
44
+ }
45
+ const blob = new Blob(stringifiedFn, { type: 'text/javascript' });
46
+ const worker = new Worker(URL.createObjectURL(blob));
47
+ // First-In First-Out queue of promises
48
+ const promises = [];
49
+ worker.onmessage = (e) => promises.shift()?.resolve(e.data);
50
+ return (data) => {
51
+ return new Promise((resolve, reject) => {
52
+ promises.push({ resolve, reject });
53
+ worker.postMessage(data);
54
+ });
55
+ };
56
+ };
57
+ const decode = createWorkerAsyncFunction(qrWorkerMethod, [
58
+ 'https://cdn.jsdelivr.net/npm/jsqr@1.2.0/dist/jsQR.min.js',
59
+ ]);
41
60
  export function useWebQRScanner(video, { isEnabled, captureOptions, interval, onScanned, onError, }) {
42
61
  const isRunning = React.useRef(false);
43
62
  const timeout = React.useRef(undefined);
44
- const [decode, clearWorker] = useRemoteJsQR();
45
63
  async function scanAsync() {
46
64
  // If interval is 0 then only scan once.
47
65
  if (!isRunning.current || !onScanned) {
@@ -86,15 +104,11 @@ export function useWebQRScanner(video, { isEnabled, captureOptions, interval, on
86
104
  isRunning.current = true;
87
105
  scanAsync();
88
106
  }
89
- else {
90
- stop();
91
- }
92
- }, [isEnabled]);
93
- React.useEffect(() => {
94
107
  return () => {
95
- stop();
96
- clearWorker.kill();
108
+ if (isEnabled) {
109
+ stop();
110
+ }
97
111
  };
98
- }, []);
112
+ }, [isEnabled]);
99
113
  }
100
114
  //# sourceMappingURL=useWebQRScanner.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"useWebQRScanner.js","sourceRoot":"","sources":["../src/useWebQRScanner.ts"],"names":[],"mappings":"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","sourcesContent":["import { useWorker } from '@koale/useworker';\nimport * as React from 'react';\n\nimport { BarCodeScanningResult, CameraPictureOptions, MountErrorListener } from './Camera.types';\nimport { captureImageData } from './WebCameraUtils';\n\nconst qrWorkerMethod = ({ data, width, height }: ImageData): any => {\n // eslint-disable-next-line no-undef\n const decoded = (self as any).jsQR(data, width, height, {\n inversionAttempts: 'attemptBoth',\n });\n\n let parsed;\n try {\n parsed = JSON.parse(decoded);\n } catch {\n parsed = decoded;\n }\n\n if (parsed?.data) {\n const nativeEvent: BarCodeScanningResult = {\n type: 'qr',\n data: parsed.data,\n cornerPoints: [],\n bounds: { origin: { x: 0, y: 0 }, size: { width: 0, height: 0 } },\n };\n if (parsed.location) {\n nativeEvent.cornerPoints = [\n parsed.location.topLeftCorner,\n parsed.location.bottomLeftCorner,\n parsed.location.topRightCorner,\n parsed.location.bottomRightCorner,\n ];\n }\n return nativeEvent;\n }\n return parsed;\n};\n\nfunction useRemoteJsQR() {\n return useWorker(qrWorkerMethod, {\n remoteDependencies: ['https://cdn.jsdelivr.net/npm/jsqr@1.2.0/dist/jsQR.min.js'],\n autoTerminate: false,\n });\n}\n\nexport function useWebQRScanner(\n video: React.MutableRefObject<HTMLVideoElement | null>,\n {\n isEnabled,\n captureOptions,\n interval,\n onScanned,\n onError,\n }: {\n isEnabled: boolean;\n captureOptions: Pick<CameraPictureOptions, 'scale' | 'isImageMirror'>;\n interval?: number;\n onScanned?: (scanningResult: { nativeEvent: BarCodeScanningResult }) => void;\n onError?: MountErrorListener;\n }\n) {\n const isRunning = React.useRef<boolean>(false);\n const timeout = React.useRef<number | undefined>(undefined);\n\n const [decode, clearWorker] = useRemoteJsQR();\n\n async function scanAsync() {\n // If interval is 0 then only scan once.\n if (!isRunning.current || !onScanned) {\n stop();\n return;\n }\n try {\n const data = captureImageData(video.current, captureOptions);\n\n if (data) {\n const nativeEvent: BarCodeScanningResult | any = await decode(data);\n if (nativeEvent?.data) {\n onScanned({\n nativeEvent,\n });\n }\n }\n } catch (error) {\n if (onError) {\n onError({ nativeEvent: error });\n }\n } finally {\n // If interval is 0 then only scan once.\n if (interval === 0) {\n stop();\n return;\n }\n const intervalToUse = !interval || interval < 0 ? 16 : interval;\n // @ts-ignore: Type 'Timeout' is not assignable to type 'number'\n timeout.current = setTimeout(() => {\n scanAsync();\n }, intervalToUse);\n }\n }\n\n function stop() {\n isRunning.current = false;\n clearTimeout(timeout.current);\n }\n\n React.useEffect(() => {\n if (isEnabled) {\n isRunning.current = true;\n scanAsync();\n } else {\n stop();\n }\n }, [isEnabled]);\n\n React.useEffect(() => {\n return () => {\n stop();\n clearWorker.kill();\n };\n }, []);\n}\n"]}
1
+ {"version":3,"file":"useWebQRScanner.js","sourceRoot":"","sources":["../src/useWebQRScanner.ts"],"names":[],"mappings":"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","sourcesContent":["import * as React from 'react';\n\nimport { BarCodeScanningResult, CameraPictureOptions, MountErrorListener } from './Camera.types';\nimport { captureImageData } from './WebCameraUtils';\n\nconst qrWorkerMethod = ({ data, width, height }: ImageData): any => {\n // eslint-disable-next-line no-undef\n const decoded = (self as any).jsQR(data, width, height, {\n inversionAttempts: 'attemptBoth',\n });\n\n let parsed;\n try {\n parsed = JSON.parse(decoded);\n } catch {\n parsed = decoded;\n }\n\n if (parsed?.data) {\n const nativeEvent: BarCodeScanningResult = {\n type: 'qr',\n data: parsed.data,\n cornerPoints: [],\n bounds: { origin: { x: 0, y: 0 }, size: { width: 0, height: 0 } },\n };\n if (parsed.location) {\n nativeEvent.cornerPoints = [\n parsed.location.topLeftCorner,\n parsed.location.bottomLeftCorner,\n parsed.location.topRightCorner,\n parsed.location.bottomRightCorner,\n ];\n }\n return nativeEvent;\n }\n return parsed;\n};\n\nconst createWorkerAsyncFunction = <T extends (data: any) => any>(fn: T, deps: string[]) => {\n const stringifiedFn = [\n `self.func = ${fn.toString()};`,\n 'self.onmessage = (e) => {',\n ' const result = self.func(e.data);',\n ' self.postMessage(result);',\n '};',\n ];\n\n if (deps.length > 0) {\n stringifiedFn.unshift(`importScripts(${deps.map((dep) => `'${dep}'`).join(', ')});`);\n }\n\n const blob = new Blob(stringifiedFn, { type: 'text/javascript' });\n const worker = new Worker(URL.createObjectURL(blob));\n\n // First-In First-Out queue of promises\n const promises: {\n resolve: (value: ReturnType<T>) => void;\n reject: (reason?: any) => void;\n }[] = [];\n\n worker.onmessage = (e) => promises.shift()?.resolve(e.data);\n\n return (data: Parameters<T>[0]) => {\n return new Promise<ReturnType<T>>((resolve, reject) => {\n promises.push({ resolve, reject });\n worker.postMessage(data);\n });\n };\n};\n\nconst decode = createWorkerAsyncFunction(qrWorkerMethod, [\n 'https://cdn.jsdelivr.net/npm/jsqr@1.2.0/dist/jsQR.min.js',\n]);\n\nexport function useWebQRScanner(\n video: React.MutableRefObject<HTMLVideoElement | null>,\n {\n isEnabled,\n captureOptions,\n interval,\n onScanned,\n onError,\n }: {\n isEnabled: boolean;\n captureOptions: Pick<CameraPictureOptions, 'scale' | 'isImageMirror'>;\n interval?: number;\n onScanned?: (scanningResult: { nativeEvent: BarCodeScanningResult }) => void;\n onError?: MountErrorListener;\n }\n) {\n const isRunning = React.useRef<boolean>(false);\n const timeout = React.useRef<number | undefined>(undefined);\n\n async function scanAsync() {\n // If interval is 0 then only scan once.\n if (!isRunning.current || !onScanned) {\n stop();\n return;\n }\n try {\n const data = captureImageData(video.current, captureOptions);\n\n if (data) {\n const nativeEvent: BarCodeScanningResult | any = await decode(data);\n if (nativeEvent?.data) {\n onScanned({\n nativeEvent,\n });\n }\n }\n } catch (error) {\n if (onError) {\n onError({ nativeEvent: error });\n }\n } finally {\n // If interval is 0 then only scan once.\n if (interval === 0) {\n stop();\n return;\n }\n const intervalToUse = !interval || interval < 0 ? 16 : interval;\n // @ts-ignore: Type 'Timeout' is not assignable to type 'number'\n timeout.current = setTimeout(() => {\n scanAsync();\n }, intervalToUse);\n }\n }\n\n function stop() {\n isRunning.current = false;\n clearTimeout(timeout.current);\n }\n\n React.useEffect(() => {\n if (isEnabled) {\n isRunning.current = true;\n scanAsync();\n }\n\n return () => {\n if (isEnabled) {\n stop();\n }\n };\n }, [isEnabled]);\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "expo-camera",
3
- "version": "13.4.3",
3
+ "version": "13.4.4",
4
4
  "description": "A React component that renders a preview for the device's either front or back camera. Camera's parameters like zoom, auto focus, white balance and flash mode are adjustable. With expo-camera, one can also take photos and record videos that are saved to the app's cache. Morever, the component is also capable of detecting faces and bar codes appearing on the preview.",
5
5
  "main": "build/index.js",
6
6
  "types": "build/index.d.ts",
@@ -34,7 +34,6 @@
34
34
  "preset": "expo-module-scripts"
35
35
  },
36
36
  "dependencies": {
37
- "@koale/useworker": "^4.0.2",
38
37
  "invariant": "^2.2.4"
39
38
  },
40
39
  "devDependencies": {
@@ -43,5 +42,5 @@
43
42
  "peerDependencies": {
44
43
  "expo": "*"
45
44
  },
46
- "gitHead": "7a1079dcba56e0bb7504210049b6195b64f13834"
45
+ "gitHead": "3d0a9873d69fd01692b1c2dfb2ea7e3bd0c1f377"
47
46
  }
@@ -1,4 +1,3 @@
1
- import { useWorker } from '@koale/useworker';
2
1
  import * as React from 'react';
3
2
 
4
3
  import { BarCodeScanningResult, CameraPictureOptions, MountErrorListener } from './Camera.types';
@@ -37,12 +36,41 @@ const qrWorkerMethod = ({ data, width, height }: ImageData): any => {
37
36
  return parsed;
38
37
  };
39
38
 
40
- function useRemoteJsQR() {
41
- return useWorker(qrWorkerMethod, {
42
- remoteDependencies: ['https://cdn.jsdelivr.net/npm/jsqr@1.2.0/dist/jsQR.min.js'],
43
- autoTerminate: false,
44
- });
45
- }
39
+ const createWorkerAsyncFunction = <T extends (data: any) => any>(fn: T, deps: string[]) => {
40
+ const stringifiedFn = [
41
+ `self.func = ${fn.toString()};`,
42
+ 'self.onmessage = (e) => {',
43
+ ' const result = self.func(e.data);',
44
+ ' self.postMessage(result);',
45
+ '};',
46
+ ];
47
+
48
+ if (deps.length > 0) {
49
+ stringifiedFn.unshift(`importScripts(${deps.map((dep) => `'${dep}'`).join(', ')});`);
50
+ }
51
+
52
+ const blob = new Blob(stringifiedFn, { type: 'text/javascript' });
53
+ const worker = new Worker(URL.createObjectURL(blob));
54
+
55
+ // First-In First-Out queue of promises
56
+ const promises: {
57
+ resolve: (value: ReturnType<T>) => void;
58
+ reject: (reason?: any) => void;
59
+ }[] = [];
60
+
61
+ worker.onmessage = (e) => promises.shift()?.resolve(e.data);
62
+
63
+ return (data: Parameters<T>[0]) => {
64
+ return new Promise<ReturnType<T>>((resolve, reject) => {
65
+ promises.push({ resolve, reject });
66
+ worker.postMessage(data);
67
+ });
68
+ };
69
+ };
70
+
71
+ const decode = createWorkerAsyncFunction(qrWorkerMethod, [
72
+ 'https://cdn.jsdelivr.net/npm/jsqr@1.2.0/dist/jsQR.min.js',
73
+ ]);
46
74
 
47
75
  export function useWebQRScanner(
48
76
  video: React.MutableRefObject<HTMLVideoElement | null>,
@@ -63,8 +91,6 @@ export function useWebQRScanner(
63
91
  const isRunning = React.useRef<boolean>(false);
64
92
  const timeout = React.useRef<number | undefined>(undefined);
65
93
 
66
- const [decode, clearWorker] = useRemoteJsQR();
67
-
68
94
  async function scanAsync() {
69
95
  // If interval is 0 then only scan once.
70
96
  if (!isRunning.current || !onScanned) {
@@ -109,15 +135,12 @@ export function useWebQRScanner(
109
135
  if (isEnabled) {
110
136
  isRunning.current = true;
111
137
  scanAsync();
112
- } else {
113
- stop();
114
138
  }
115
- }, [isEnabled]);
116
139
 
117
- React.useEffect(() => {
118
140
  return () => {
119
- stop();
120
- clearWorker.kill();
141
+ if (isEnabled) {
142
+ stop();
143
+ }
121
144
  };
122
- }, []);
145
+ }, [isEnabled]);
123
146
  }