Skip to content

Handle camera permission errors gracefully#96

Merged
jpdevhub merged 2 commits into
jpdevhub:mainfrom
krishnendu07-code:fix-camera-permission-error
Jun 12, 2026
Merged

Handle camera permission errors gracefully#96
jpdevhub merged 2 commits into
jpdevhub:mainfrom
krishnendu07-code:fix-camera-permission-error

Conversation

@krishnendu07-code

@krishnendu07-code krishnendu07-code commented Jun 11, 2026

Copy link
Copy Markdown
Contributor

Fixes #90

Description

This PR improves the camera access experience in the scanner page by handling camera permission and device availability errors gracefully.

Changes Made

  • Added handling for NotAllowedError when camera permission is denied.
  • Added handling for NotFoundError when no camera device is available.
  • Displayed a user-friendly fallback UI inside the camera viewport.
  • Added guidance on how users can re-enable camera access.
  • Hid scanner instructions when camera access fails to prevent overlapping UI elements.

Result

Instead of seeing a blank camera screen, users now receive clear instructions explaining the issue and how to resolve it.

Before

Screenshot 2026-06-12 012253

After

Screenshot 2026-06-12 012402

Summary by CodeRabbit

  • New Features

    • Shareable fish-quality reports with automatic grade assignment for qualified scans (copyable report link when freshness ≥85)
    • Hybrid scan flow: faster online-first scanning with reliable offline fallback and preview thumbnail generation
    • Improved progress and mode indicators, plus color-coded freshness and grade display
  • Bug Fixes

    • Better camera streaming lifecycle (stops/clears on teardown) and clearer camera error messaging
    • Specific "No fish detected" feedback for relevant errors

@vercel

vercel Bot commented Jun 11, 2026

Copy link
Copy Markdown

@krishnendu07-code is attempting to deploy a commit to the karan3431's projects Team on Vercel.

A member of the Team first needs to authorize it.

@github-actions

Copy link
Copy Markdown

🎉 Thank you for your Pull Request! We're thrilled to have your contribution to FreshScan AI.

Before we review, please make sure you have:

  • Followed the CONTRIBUTING.md guidelines.
  • Ensured all automated CI checks (linting, tests) are passing.
  • Checked that your commit messages follow the Conventional Commits format.

A maintainer will review your code as soon as possible!

@coderabbitai

coderabbitai Bot commented Jun 11, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

.coderabbit.yaml has a parsing error

The CodeRabbit configuration file in this repository has a parsing error and default settings were used instead. Please fix the error(s) in the configuration file. You can initialize chat with CodeRabbit to get help with the configuration file.

💥 Parsing errors (1)
Validation error: Invalid input: expected object, received boolean at "reviews.auto_review"
⚙️ Configuration instructions
  • Please see the configuration documentation for more information.
  • You can also validate your configuration using the online YAML validator.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 8440e346-978f-4461-ab31-90dbde23d6c1

📥 Commits

Reviewing files that changed from the base of the PR and between 85fc7f3 and 5337ca0.

📒 Files selected for processing (1)
  • src/pages/ScannerPage.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/pages/ScannerPage.tsx

📝 Walkthrough

Walkthrough

ScannerPage.tsx refactors hybrid online/offline inference, unifies DisplayResult, adds ONNX readiness polling, updates frame capture to canvas-JPEG, pre-warms engine, improves camera lifecycle/error handling, and updates UI to color-code results and enable shareable grade reports via session-backed preview URLs.

Changes

Scanner Component Refactor: Hybrid Inference and Error Handling

Layer / File(s) Summary
Scan Foundation: Engine, Media Utilities, and Component State
src/pages/ScannerPage.tsx
Defines DisplayResult, deriveGrade, and labelColor; ONNX getEngine() now polls engineReady for concurrent callers; captureVideoBlob renders video to canvas and produces JPEG via toBlob; blobToImageElement waits for image load; adds typed component state and pre-warms engine; camera stream lifecycle supports cancellation, stops tracks, clears video.srcObject, and caps progress animation.
Hybrid Inference Execution: Online and Offline Paths
src/pages/ScannerPage.tsx
runScan always creates a preview object URL, stops camera, shows progress, attempts api.scanOnline(blob) and maps cloud fields into DisplayResult (stores lastScanId), falls back to ONNX engine.predictSingle if no online result, derives freshness/grade from fusion scores, displays local result, and best-effort submits a thumbnail via api.submitScan. Specialized parsing for NOT_A_FISH errors implemented.
UI Rendering, Freshness Visualization, and Grade Sharing
src/pages/ScannerPage.tsx
Idle hint hidden when cameraError present; scan-complete overlay colors label via labelColor(result.label) and shows confidence/grade; inference-mode badge classes updated and flash toggle gated to idle; processing caption depends on edge vs cloud mode; freshness numeric uses labelColor with threshold bar coloring; “GRADE-A REPORT” copies /report/{scanId} from sessionStorage.lastScanId only when freshness ≥ 85 and updates copied state.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐰 In frames of canvas, a fish is found,
Cloud and ONNX both gather round,
Engines pre-warmed, errors shown with care,
A grade to share when freshness is fair. 🎏✨

🚥 Pre-merge checks | ✅ 4 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Out of Scope Changes check ⚠️ Warning The PR includes substantial refactoring beyond the linked issue scope, including ONNX engine polling, media helpers, hybrid inference flow, and UI state standardization unrelated to camera permission handling. Focus the PR on camera permission error handling from issue #90. Defer unrelated refactoring changes to separate PRs to maintain clear scope and reviewability.
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Handle camera permission errors gracefully' directly matches the main objective from the linked issue #90, which focuses on handling camera permission denial errors.
Linked Issues check ✅ Passed The PR implements both objectives from issue #90: catches NotAllowedError and NotFoundError from getUserMedia, and displays a friendly UI with camera access guidance inside the camera viewport.
Description check ✅ Passed The PR provides comprehensive documentation of changes, before/after screenshots, and clear mapping to linked objectives for camera permission error handling.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/pages/ScannerPage.tsx (1)

58-77: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Infinite poll if initial engine load fails.

If loadModels() throws, engineLoading remains true and engineReady stays false. Subsequent getEngine() calls enter the polling branch (lines 60-70) and never resolve because engineReady never becomes true.

🔧 Proposed fix: reset flags on error and propagate failure
 async function getEngine(): Promise<FishFreshnessInference> {
   if (engineReady && engineInstance) return engineInstance;
   if (engineLoading) {
-    await new Promise<void>((resolve) => {
+    await new Promise<void>((resolve, reject) => {
+      let attempts = 0;
       const poll = setInterval(() => {
         if (engineReady) {
           clearInterval(poll);
           resolve();
+        } else if (!engineLoading) {
+          // Loading finished but not ready → previous load failed
+          clearInterval(poll);
+          reject(new Error("ONNX engine failed to load"));
+        } else if (++attempts > 100) {
+          // Timeout after ~10s
+          clearInterval(poll);
+          reject(new Error("ONNX engine load timeout"));
         }
       }, 100);
     });
     return engineInstance!;
   }
   engineLoading = true;
-  engineInstance = new FishFreshnessInference();
-  await engineInstance.loadModels();
-  engineReady = true;
-  engineLoading = false;
+  try {
+    engineInstance = new FishFreshnessInference();
+    await engineInstance.loadModels();
+    engineReady = true;
+  } finally {
+    engineLoading = false;
+  }
   return engineInstance;
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/pages/ScannerPage.tsx` around lines 58 - 77, getEngine currently leaves
engineLoading true if FishFreshnessInference.loadModels() throws, causing
callers to hang in the polling branch; wrap the model load in a try/catch around
the engineInstance.loadModels() call so that on error you reset engineLoading =
false, engineReady = false, clear engineInstance (set to undefined/null) and
rethrow the error to propagate failure; reference getEngine, engineLoading,
engineReady, engineInstance, FishFreshnessInference and loadModels when making
the change.
🧹 Nitpick comments (1)
src/pages/ScannerPage.tsx (1)

277-282: ⚡ Quick win

Unnecessary type assertion obscures the actual type.

api.submitScan returns Promise<ScanResponse>, where ScanResponse.scan is ScanResult which already has scan_id: string per src/lib/types.ts. The double cast through unknown is unnecessary and could mask future type mismatches.

♻️ Proposed simplification
-              if ((saved?.scan as unknown as { scan_id?: string })?.scan_id) {
-                sessionStorage.setItem(
-                  "lastScanId",
-                  (saved.scan as unknown as { scan_id: string }).scan_id,
-                );
-              }
+              if (saved?.scan?.scan_id) {
+                sessionStorage.setItem("lastScanId", saved.scan.scan_id);
+              }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/pages/ScannerPage.tsx` around lines 277 - 282, The code uses an
unnecessary double type assertion on saved.scan which obscures types; replace
the (saved?.scan as unknown as { scan_id?: string }) and (saved.scan as unknown
as { scan_id: string }) casts with the proper ScanResponse/ScanResult types (or
let TypeScript infer them) and access saved.scan.scan_id directly (e.g., ensure
saved is typed as ScanResponse and use saved.scan.scan_id) so the
sessionStorage.setItem call uses the real scan_id without the unknown cast.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@src/pages/ScannerPage.tsx`:
- Around line 58-77: getEngine currently leaves engineLoading true if
FishFreshnessInference.loadModels() throws, causing callers to hang in the
polling branch; wrap the model load in a try/catch around the
engineInstance.loadModels() call so that on error you reset engineLoading =
false, engineReady = false, clear engineInstance (set to undefined/null) and
rethrow the error to propagate failure; reference getEngine, engineLoading,
engineReady, engineInstance, FishFreshnessInference and loadModels when making
the change.

---

Nitpick comments:
In `@src/pages/ScannerPage.tsx`:
- Around line 277-282: The code uses an unnecessary double type assertion on
saved.scan which obscures types; replace the (saved?.scan as unknown as {
scan_id?: string }) and (saved.scan as unknown as { scan_id: string }) casts
with the proper ScanResponse/ScanResult types (or let TypeScript infer them) and
access saved.scan.scan_id directly (e.g., ensure saved is typed as ScanResponse
and use saved.scan.scan_id) so the sessionStorage.setItem call uses the real
scan_id without the unknown cast.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 39e96ebf-9f37-43d7-8899-30842c8906fb

📥 Commits

Reviewing files that changed from the base of the PR and between 8052c28 and 85fc7f3.

📒 Files selected for processing (1)
  • src/pages/ScannerPage.tsx

@jpdevhub jpdevhub left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for contributing.

@vercel

vercel Bot commented Jun 12, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
fresh-scan-ai Ready Ready Preview, Comment Jun 12, 2026 12:46pm

@jpdevhub jpdevhub merged commit 4063522 into jpdevhub:main Jun 12, 2026
9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Frontend] Handle Camera Permission Denied Gracefully

2 participants