diff --git a/frontend/components/App/ScannerModal.vue b/frontend/components/App/ScannerModal.vue index 9aff71f5..0ee299bd 100644 --- a/frontend/components/App/ScannerModal.vue +++ b/frontend/components/App/ScannerModal.vue @@ -57,6 +57,17 @@ errorMessage.value = t("scanner.error"); }; + const checkPermissionsError = async () => { + if (navigator.permissions) { + const permissionStatus = await navigator.permissions.query({ name: "camera" as PermissionName }); + if (permissionStatus.state === "denied") { + errorMessage.value = t("scanner.permission_denied"); + console.error("Camera permission denied"); + return true; + } + } + }; + const startScanner = async () => { errorMessage.value = null; if (!(navigator && navigator.mediaDevices && "enumerateDevices" in navigator.mediaDevices)) { @@ -64,6 +75,10 @@ return; } + if (await checkPermissionsError()) { + return; + } + try { const devices = await codeReader.listVideoInputDevices(); sources.value = devices; diff --git a/frontend/layouts/default.vue b/frontend/layouts/default.vue index 68e0300e..c7d5768b 100644 --- a/frontend/layouts/default.vue +++ b/frontend/layouts/default.vue @@ -137,7 +137,7 @@
-
@@ -207,6 +207,7 @@ import { useDialog } from "~/components/ui/dialog-provider"; import { Input } from "~/components/ui/input"; import { Button } from "~/components/ui/button"; + import { toast } from "@/components/ui/sonner"; const { t, locale } = useI18n(); const username = computed(() => authCtx.user?.name || "User"); @@ -241,6 +242,23 @@ } }; + const openScanner = () => { + // request permission + if (navigator.mediaDevices) { + navigator.mediaDevices + .getUserMedia({ video: true }) + .then(() => { + openDialog("scanner"); + }) + .catch(err => { + console.error(err); + toast.error(t("scanner.permission_denied")); + }); + } else { + toast.error(t("scanner.unsupported")); + } + }; + // Preload currency format useFormatCurrency(); diff --git a/frontend/locales/en.json b/frontend/locales/en.json index 1c124e37..6dd7a352 100644 --- a/frontend/locales/en.json +++ b/frontend/locales/en.json @@ -378,7 +378,8 @@ "no_sources": "No video sources available", "select_video_source": "Pick a video source", "title": "Scanner", - "unsupported": "Media Stream API is not supported without HTTPS" + "unsupported": "Media Stream API is not supported without HTTPS", + "permission_denied": "Camera permission denied, please allow access to the camera in your browser settings" }, "tools": { "actions": "Inventory Actions",