@robotical/webapp-types 3.21.12 → 3.21.13

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.
@@ -1,7 +1,10 @@
1
1
  {
2
2
  "name": "marty-web-app",
3
- "version": "3.21.12",
3
+ "version": "3.21.13",
4
4
  "private": true,
5
+ "engines": {
6
+ "node": ">=20"
7
+ },
5
8
  "dependencies": {
6
9
  "@radix-ui/react-dropdown-menu": "^2.1.2",
7
10
  "@radix-ui/react-icons": "^1.3.2",
@@ -23,7 +26,7 @@
23
26
  "@robotical/skiing-game": "^0.1.24",
24
27
  "@robotical/space-shooter-game": "^0.1.15",
25
28
  "@sentry/react": "^8.55.2",
26
- "@yudiel/react-qr-scanner": "2.5.1",
29
+ "@yudiel/react-qr-scanner": "2.6.0",
27
30
  "aws-sdk": "^2.1394.0",
28
31
  "bowser": "^2.11.0",
29
32
  "core-js": "^3.6.5",
@@ -44,20 +47,26 @@
44
47
  "semver": "^7.3.7",
45
48
  "styled-components": "^5.3.3",
46
49
  "typescript": "^4.6.2",
47
- "web-vitals": "^2.1.4"
50
+ "web-vitals": "^2.1.4",
51
+ "zxing-wasm": "3.1.1"
48
52
  },
49
53
  "scripts": {
50
54
  "copy-space-shooter-assets": "node copy-space-shooter-assets.js",
55
+ "copy-qr-wasm": "shx rm -rf ./public/qr-scanner && shx mkdir -p ./public/qr-scanner && shx cp ./node_modules/zxing-wasm/dist/reader/zxing_reader.wasm ./public/qr-scanner/zxing_reader.wasm",
51
56
  "copy-mb": "shx rm -rf ./public/MartyBlocks && shx mkdir -p ./public/MartyBlocks && shx cp -r ./node_modules/@robotical/martyblocks/dist/. ./public/MartyBlocks/",
52
57
  "copy-mbjr": "shx rm -rf ./public/MartyBlocksJR && shx mkdir -p ./public/MartyBlocksJR && shx cp -r ./node_modules/@robotical/martyblocksjr/editions/free/src/. ./public/MartyBlocksJR/",
53
58
  "clean-mb": "shx rm -r ./node_modules/@robotical/martyblocks",
54
59
  "clean-mbjr": "shx rm -r ./node_modules/@robotical/martyblocksjr",
55
60
  "transpile-public-legacy": "node scripts/transpile-public-legacy.js",
56
- "prepare": "npm run copy-mb && npm run copy-mbjr && npm run copy-space-shooter-assets && NODE_OPTIONS=\"--max-old-space-size=8192\" npm run transpile-public-legacy",
61
+ "prepare": "npm run copy-mb && npm run copy-mbjr && npm run copy-space-shooter-assets && npm run copy-qr-wasm && NODE_OPTIONS=\"--max-old-space-size=8192\" npm run transpile-public-legacy",
62
+ "prestart": "npm run copy-qr-wasm",
57
63
  "start": "react-scripts start",
58
64
  "start-pwa": "http-server build -c-1 -p 3001 --directory build",
65
+ "prebuild": "npm run copy-qr-wasm",
59
66
  "build": "react-scripts build",
67
+ "prebuild:prod": "npm run copy-qr-wasm",
60
68
  "build:prod": "GENERATE_SOURCEMAP=false react-scripts build && tsc --project tsconfig.prod.json",
69
+ "prebuild:sentry": "npm run copy-qr-wasm",
61
70
  "build:sentry": "GENERATE_SOURCEMAP=true react-scripts build && tsc --project tsconfig.prod.json",
62
71
  "sentry:upload-sourcemaps": "sentry-cli sourcemaps inject build/static/js && sentry-cli sourcemaps upload --release \"marty-web-app@$npm_package_version\" --validate --strict --wait build/static/js",
63
72
  "sentry:remove-sourcemaps": "find build/static/js -type f -name '*.map' -delete",
@@ -0,0 +1,2 @@
1
+ export declare const locateBarcodeDetectorFile: (path: string, prefix: string) => string;
2
+ export declare const resetBarcodeDetectorModule: () => void;
@@ -0,0 +1,11 @@
1
+ import { setZXingModuleOverrides } from "@yudiel/react-qr-scanner";
2
+ var qrScannerAssetUrl = "".concat((process.env.PUBLIC_URL || "").replace(/\/$/, ""), "/qr-scanner/zxing_reader.wasm");
3
+ export var locateBarcodeDetectorFile = function (path, prefix) {
4
+ return path === "zxing_reader.wasm" ? qrScannerAssetUrl : "".concat(prefix).concat(path);
5
+ };
6
+ // A fresh overrides object also drops zxing-wasm's cached module promise,
7
+ // allowing a retry after a transient initialization failure.
8
+ export var resetBarcodeDetectorModule = function () {
9
+ return setZXingModuleOverrides({ locateFile: locateBarcodeDetectorFile });
10
+ };
11
+ resetBarcodeDetectorModule();
@@ -1,4 +1,5 @@
1
1
  import { type IScannerProps } from "@yudiel/react-qr-scanner";
2
+ import "./configure-barcode-detector";
2
3
  type Props = IScannerProps;
3
4
  export default function SwitchableCameraScanner({ constraints, ...scannerProps }: Props): import("react/jsx-runtime").JSX.Element;
4
5
  export {};
@@ -26,6 +26,7 @@ import { useTranslation } from "react-i18next";
26
26
  import { MdCameraswitch } from "react-icons/md";
27
27
  import { Scanner } from "@yudiel/react-qr-scanner";
28
28
  import isPhoneApp from "../../../utils/phone-app-communication/is-phone-app";
29
+ import "./configure-barcode-detector";
29
30
  import { acquireCameraStreamLifecycleGuard, addCameraStreamLifecycleOwner, } from "./camera-stream-lifecycle-guard";
30
31
  import styles from "./styles.module.css";
31
32
  function LifecycleGuardedScanner(props) {
@@ -1,5 +1,7 @@
1
1
  import { RaftTypeE } from "../../../types/raft";
2
- export declare const reportQRScannerError: (error: unknown) => void;
2
+ export type QRScannerErrorDisposition = "permission-denied" | "detector-unavailable" | "unexpected";
3
+ export declare const classifyQRScannerError: (error: unknown) => QRScannerErrorDisposition;
4
+ export declare const reportQRScannerError: (error: unknown) => QRScannerErrorDisposition;
3
5
  type Props = {
4
6
  targetRaftType: RaftTypeE.COG;
5
7
  allowSwitchToMarty?: boolean;
@@ -46,33 +46,65 @@ var __generator = (this && this.__generator) || function (thisArg, body) {
46
46
  }
47
47
  };
48
48
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
49
- import { useEffect, useState } from "react";
49
+ import { useEffect, useRef, useState } from "react";
50
50
  import modalState from "../../../state-observables/modal/ModalState";
51
51
  import styles from "./styles.module.css";
52
52
  import { HiOutlineLightBulb } from "react-icons/hi";
53
53
  import { MdQrCode } from "react-icons/md";
54
54
  import { ReactComponent as BluetoothSVG } from "../../../assets/configuration/bluetooth.svg";
55
55
  import { MAIN_BLUE } from "../../../styles/colors";
56
- //@ts-ignore
57
- import { centerText } from '@yudiel/react-qr-scanner';
56
+ import { centerText, } from "@yudiel/react-qr-scanner";
58
57
  import { ConnManager } from "@robotical/roboticaljs";
59
58
  import { useTranslation } from "react-i18next";
60
59
  import SwitchableCameraScanner from "../../disposables/SwitchableCameraScanner";
60
+ import { resetBarcodeDetectorModule } from "../../disposables/SwitchableCameraScanner/configure-barcode-detector";
61
61
  import Logger from "../../../services/logger/Logger";
62
62
  var SHOW_LOGS = true;
63
63
  var TAG = "LEDLightsOrQRVerificationModal";
64
+ var getErrorProperty = function (error, property) {
65
+ if (error === null || typeof error !== "object") {
66
+ return undefined;
67
+ }
68
+ var errorRecord = error;
69
+ return property in errorRecord ? errorRecord[property] : undefined;
70
+ };
71
+ export var classifyQRScannerError = function (error) {
72
+ var cause = getErrorProperty(error, "cause");
73
+ var errorKind = getErrorProperty(error, "kind");
74
+ var errorName = getErrorProperty(error, "name");
75
+ var causeName = getErrorProperty(cause, "name");
76
+ var errorMessage = getErrorProperty(error, "message");
77
+ var causeMessage = getErrorProperty(cause, "message");
78
+ if (errorKind === "permission-denied" ||
79
+ errorName === "NotAllowedError" ||
80
+ errorName === "PermissionDeniedError" ||
81
+ causeName === "NotAllowedError" ||
82
+ causeName === "PermissionDeniedError") {
83
+ return "permission-denied";
84
+ }
85
+ if ((errorName === "NotSupportedError" || causeName === "NotSupportedError") &&
86
+ [errorMessage, causeMessage].some(function (message) {
87
+ return typeof message === "string" &&
88
+ message.includes("Barcode detection service unavailable");
89
+ })) {
90
+ return "detector-unavailable";
91
+ }
92
+ return "unexpected";
93
+ };
64
94
  export var reportQRScannerError = function (error) {
65
- var errorName = error !== null &&
66
- typeof error === "object" &&
67
- "name" in error &&
68
- typeof error.name === "string"
69
- ? error.name
70
- : "";
71
- if (errorName === "NotAllowedError" || errorName === "PermissionDeniedError") {
95
+ var disposition = classifyQRScannerError(error);
96
+ if (disposition === "permission-denied") {
72
97
  Logger.warn(SHOW_LOGS, TAG, "QR camera permission was not granted", error);
73
- return;
98
+ return disposition;
99
+ }
100
+ var cause = getErrorProperty(error, "cause");
101
+ if (cause !== undefined) {
102
+ Logger.error(SHOW_LOGS, TAG, "QR scanner failed", cause, error);
74
103
  }
75
- Logger.error(SHOW_LOGS, TAG, error);
104
+ else {
105
+ Logger.error(SHOW_LOGS, TAG, error);
106
+ }
107
+ return disposition;
76
108
  };
77
109
  export default function LEDLightsOrQRVerificationModal(_a) {
78
110
  var _this = this;
@@ -134,12 +166,19 @@ export default function LEDLightsOrQRVerificationModal(_a) {
134
166
  var id = _a.id, title = _a.title, subtitle = _a.subtitle, icon = _a.icon;
135
167
  return (_jsxs("button", __assign({ type: "button", className: qrSelected && id === 'qr' ? [styles.card, styles.qrselected].join(" ") : styles.card, onClick: function () { return handleSelect(id); }, "aria-label": title, "aria-pressed": id === "qr" ? qrSelected : false }, { children: [_jsx("div", __assign({ className: styles.icon }, { children: icon })), _jsx("h3", __assign({ className: styles.title }, { children: title })), _jsx("p", __assign({ className: styles.subtitle }, { children: subtitle }))] }), id));
136
168
  }) })) :
137
- _jsx("div", __assign({ className: styles.cogNote }, { children: _jsxs("strong", { children: ["Don't forget to ", _jsx("span", __assign({ className: styles.cogNoteHighlight }, { children: "turn on" })), " your Cog!"] }) }))] }), qrSelected && _jsx(QRContent, { setSerialNo: setSerialNoFromQR }), _jsxs("div", __assign({ className: styles.buttonsContainer }, { children: [isButtonToConnectVisible && _jsx("button", __assign({ type: "button", className: styles.connectButton, onClick: handleConnect }, { children: t('connection.qr_connect_button') })), _jsx("button", __assign({ type: "button", className: styles.cancelButton, onClick: function () { return modalState.closeModal(cancelResult); } }, { children: t('connection.cancel') }))] }))] })));
169
+ _jsx("div", __assign({ className: styles.cogNote }, { children: _jsxs("strong", { children: ["Don't forget to ", _jsx("span", __assign({ className: styles.cogNoteHighlight }, { children: "turn on" })), " your Cog!"] }) }))] }), qrSelected && (_jsx(QRContent, { setSerialNo: setSerialNoFromQR, onScannerUnavailable: function () { return setQRSelected(false); } })), _jsxs("div", __assign({ className: styles.buttonsContainer }, { children: [isButtonToConnectVisible && _jsx("button", __assign({ type: "button", className: styles.connectButton, onClick: handleConnect }, { children: t('connection.qr_connect_button') })), _jsx("button", __assign({ type: "button", className: styles.cancelButton, onClick: function () { return modalState.closeModal(cancelResult); } }, { children: t('connection.cancel') }))] }))] })));
138
170
  }
139
171
  function QRContent(_a) {
140
172
  var _this = this;
141
173
  var _b;
142
- var setSerialNo = _a.setSerialNo;
174
+ var setSerialNo = _a.setSerialNo, onScannerUnavailable = _a.onScannerUnavailable;
175
+ var isActiveRef = useRef(true);
176
+ useEffect(function () {
177
+ isActiveRef.current = true;
178
+ return function () {
179
+ isActiveRef.current = false;
180
+ };
181
+ }, []);
143
182
  var onQrCodeFound = function (result) { return __awaiter(_this, void 0, void 0, function () {
144
183
  var rawValue, serialNo;
145
184
  var _a;
@@ -154,8 +193,19 @@ function QRContent(_a) {
154
193
  return [2 /*return*/];
155
194
  });
156
195
  }); };
196
+ var onScannerError = function (error) {
197
+ if (!isActiveRef.current) {
198
+ return;
199
+ }
200
+ if (reportQRScannerError(error) !== "detector-unavailable") {
201
+ return;
202
+ }
203
+ resetBarcodeDetectorModule();
204
+ onScannerUnavailable();
205
+ window.applicationManager.toaster.error("QR scanning is unavailable. Please use LED verification or select QR Code Verification to try again.", { autoClose: false });
206
+ };
157
207
  var isWebapp = ((_b = window.applicationManager.deviceInfo) === null || _b === void 0 ? void 0 : _b.manufacturer) === "Webapp";
158
- return (_jsx("div", __assign({ className: styles.QRcontainer }, { children: _jsx("div", __assign({ className: styles.QRscannerContainer }, { children: _jsx(SwitchableCameraScanner, { styles: { container: { transform: isWebapp ? 'scaleX(-1)' : 'none' } }, onError: reportQRScannerError, formats: [
208
+ return (_jsx("div", __assign({ className: styles.QRcontainer }, { children: _jsx("div", __assign({ className: styles.QRscannerContainer }, { children: _jsx(SwitchableCameraScanner, { styles: { container: { transform: isWebapp ? 'scaleX(-1)' : 'none' } }, onError: onScannerError, formats: [
159
209
  "qr_code",
160
210
  "micro_qr_code",
161
211
  "rm_qr_code",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@robotical/webapp-types",
3
- "version": "3.21.12",
3
+ "version": "3.21.13",
4
4
  "description": "Type definitions for the Application Manager",
5
5
  "main": "dist/application-manager.d.ts",
6
6
  "types": "dist/application-manager.d.ts",