Skip to content

Commit 07cb8e8

Browse files
author
Illia Obukhau
committed
fix(barcode-scanner-web): cleanup video stream
- Now, we controlling stream explicitly to make sure it disposed after use - add `stopAdyncDecode` coll to make sure we don't have setTimeout loops
1 parent 3a7ee77 commit 07cb8e8

File tree

5 files changed

+44
-51
lines changed

5 files changed

+44
-51
lines changed

packages/pluggableWidgets/barcode-scanner-web/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66

77
## [Unreleased]
88

9+
### Fixed
10+
11+
- We fixed issue with camera, when it keep capturing video after widget is closed.
12+
913
## [2.2.1] - 2022-07-27
1014

1115
### Fixed

packages/pluggableWidgets/barcode-scanner-web/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "barcode-scanner-web",
33
"widgetName": "BarcodeScanner",
4-
"version": "2.2.1",
4+
"version": "2.2.2",
55
"description": "Displays a barcode scanner",
66
"copyright": "© Mendix Technology BV 2022. All rights reserved.",
77
"license": "Apache-2.0",

packages/pluggableWidgets/barcode-scanner-web/src/hooks/useCustomErrorMessage.ts

Lines changed: 3 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,15 @@
1-
import { Exception } from "@zxing/library/cjs";
21
import { useState, useCallback } from "react";
32

43
type ErrorCb<E extends Error = Error> = (error: E) => void;
54

65
type UseCustomErrorMessageHook = () => [string | null, ErrorCb];
76

87
function getErrorMessage<E extends Error>(error: E): string | null {
9-
// Error comes from `zxing`
10-
if (error instanceof Exception) {
11-
return "Error in barcode scanner: an unexpected error occurred while detecting a barcode in the camera media stream.";
8+
if (error instanceof Error) {
9+
return `Error in barcode scanner: ${error.message}`;
1210
}
1311

14-
if (error.name === "NotFoundError") {
15-
return "Error in barcode scanner: no camera media devices were found.";
16-
}
17-
18-
// Ignore error if user restricted camera access
19-
if (error.name === "NotAllowedError") {
20-
return null;
21-
}
22-
23-
// Anything else will be a DOMException that we show as media stream error
24-
return "Error in barcode scanner: an unexpected error occurred while retrieving the camera media stream.";
12+
return "Error in barcode scanner: an unexpected error.";
2513
}
2614

2715
export const useCustomErrorMessage: UseCustomErrorMessageHook = () => {
Lines changed: 35 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { useEffect, useRef, RefObject } from "react";
2-
import { BarcodeFormat, BrowserMultiFormatReader, DecodeHintType } from "@zxing/library/cjs";
2+
import { BarcodeFormat, BrowserMultiFormatReader, DecodeHintType, NotFoundException } from "@zxing/library/cjs";
33
import { useEventCallback } from "@mendix/pluggable-widgets-commons";
44

55
const hints = new Map();
@@ -30,47 +30,48 @@ export const useReader: UseReaderHook = args => {
3030
const onSuccess = useEventCallback(args.onSuccess);
3131
const onError = useEventCallback(args.onError);
3232

33-
function setup(): (() => void) | void {
34-
if (!videoRef.current) {
35-
return;
36-
}
37-
let isCanceled = false;
38-
let reader: BrowserMultiFormatReader | null = null;
39-
const elt = videoRef.current;
33+
useEffect(() => {
34+
let stopped = false;
35+
const reader = new BrowserMultiFormatReader(hints, 2000);
4036

41-
const cleanup = (): void => {
42-
if (!isCanceled && reader) {
43-
reader.reset();
44-
reader = null;
45-
isCanceled = true;
46-
}
37+
const stop = (): void => {
38+
stopped = true;
39+
reader.stopAsyncDecode();
40+
reader.reset();
4741
};
4842

49-
const decodeFromReader = (): void => {
50-
const fn = async (): Promise<void> => {
51-
reader = new BrowserMultiFormatReader(hints, 2000);
52-
try {
53-
const result = await reader.decodeOnceFromConstraints(mediaStreamConstraints, elt);
54-
onSuccess(result.getText());
55-
} catch (e) {
56-
console.log(e.message);
57-
if (!isCanceled && onError) {
58-
onError(e);
43+
const start = async (): Promise<void> => {
44+
let stream;
45+
try {
46+
stream = await navigator.mediaDevices.getUserMedia(mediaStreamConstraints);
47+
if (!stopped && videoRef.current) {
48+
const result = await reader.decodeOnceFromStream(stream, videoRef.current);
49+
if (!stopped) {
50+
onSuccess(result.getText());
51+
}
52+
}
53+
} catch (error) {
54+
// Suppress not found error if widget is closed normally (eg. leaving page);
55+
const isNotFound = stopped && error instanceof NotFoundException;
56+
if (!isNotFound) {
57+
if (error instanceof Error) {
58+
console.error(error.message);
59+
}
60+
if (onError) {
61+
onError(error);
5962
}
60-
} finally {
61-
cleanup();
6263
}
63-
};
64-
fn();
64+
} finally {
65+
stop();
66+
stream?.getVideoTracks().forEach(track => track.stop());
67+
}
6568
};
6669

67-
decodeFromReader();
68-
69-
return cleanup;
70-
}
70+
start();
7171

72-
// eslint-disable-next-line react-hooks/exhaustive-deps
73-
useEffect(setup, []);
72+
return stop;
73+
// eslint-disable-next-line react-hooks/exhaustive-deps
74+
}, []);
7475

7576
return videoRef;
7677
};

packages/pluggableWidgets/barcode-scanner-web/src/package.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<?xml version="1.0" encoding="utf-8" ?>
22
<package xmlns="http://www.mendix.com/package/1.0/">
3-
<clientModule name="Barcode Scanner" version="2.2.1" xmlns="http://www.mendix.com/clientModule/1.0/">
3+
<clientModule name="Barcode Scanner" version="2.2.2" xmlns="http://www.mendix.com/clientModule/1.0/">
44
<widgetFiles>
55
<widgetFile path="BarcodeScanner.xml" />
66
</widgetFiles>

0 commit comments

Comments
 (0)