Skip to content

Conversation

@kixelated
Copy link
Collaborator

@kixelated kixelated commented Aug 31, 2025

If we request an undefined device with no permission, browsers will prompt the user. We want to save their selection as the device we should use when available.

Summary by CodeRabbit

  • New Features

    • The app now remembers your selected microphone or input device for future sessions when chosen from the prompt.
  • Bug Fixes

    • Prevents redundant permission prompts and media requests when access is already granted.
    • Correctly honors edge-case device IDs (including empty strings) to ensure the intended device is used.
    • Improves media access stability by gracefully handling errors to avoid user interruptions.

If we request an undefined device with no permission, browsers will prompt the user.
We want to save their selection as the device we should use when available.
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Aug 31, 2025

Walkthrough

Introduces early-exit permission checks and error swallowing in device permission flow, captures and stores deviceId after media acquisition, and adjusts microphone deviceId constraint handling to treat empty strings as valid and persist user-chosen device when none was explicitly requested.

Changes

Cohort / File(s) Summary of Changes
Media device permission and selection
js/hang/src/publish/source/device.ts, js/hang/src/publish/source/microphone.ts
Device: early return if permission already granted; after getUserMedia, capture first track’s deviceId and set preferred; swallow getUserMedia errors with catch(() => undefined). Microphone: treat non-undefined deviceId (including empty string) as valid; when no device specified, persist selected deviceId to preferred.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor User
  participant Device as Device.requestPermission()
  participant Browser as navigator.mediaDevices

  Note over Device: Early exit if permission already granted
  User->>Device: requestPermission()
  alt permission already granted
    Device-->>User: return (no-op)
  else
    Device->>Browser: getUserMedia(constraints)
    alt success
      Browser-->>Device: MediaStream
      Note right of Device: Read first track settings.deviceId<br/>Update preferred if present
      Device-->>User: resolved
    else error
      Browser-->>Device: Error
      Note right of Device: catch(() => undefined)
      Device-->>User: resolved (undefined)
    end
  end
Loading
sequenceDiagram
  autonumber
  actor User
  participant Mic as Microphone.acquire()
  participant Device as Device (preferred)
  participant Browser as navigator.mediaDevices

  Mic->>Mic: Build constraints
  Note over Mic: deviceId constraint set if device !== undefined<br/>(empty string allowed)
  Mic->>Browser: getUserMedia(constraints)
  Browser-->>Mic: MediaStream with track
  Mic->>Mic: Read track.settings.deviceId
  alt device was undefined in request
    Note right of Mic: Persist settings.deviceId to Device.preferred
  else
    Note right of Mic: Do not overwrite preferred
  end
  Mic-->>User: Stream/track
Loading

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Possibly related PRs

Poem

A twitch of whiskers, a hop to the mic,
I nibbled the perms—no double bite strike.
If streams do flow, I stash the ID,
So next time I squeak, it’s set perfectly.
Empty string? Still tasty—I won’t defer;
Preferred’s now saved—soft purr, brrr 🐇🎙️.

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch device-manager

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
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbitai ignore or @coderabbit ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • 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

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@kixelated kixelated enabled auto-merge (squash) August 31, 2025 22:27
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (4)
js/hang/src/publish/source/device.ts (2)

126-127: Early-exit may skip legitimate refresh flows; consider making this awaitable.

If site permission was changed externally (browser settings) while permission stayed true, this early return prevents re-checking. Also, returning a Promise lets callers await completion if they do want to prompt. Optional tweak below keeps behavior but is awaitable and logs denials.

-	requestPermission() {
-		if (this.permission.peek()) return;
+	requestPermission(): Promise<void> {
+		if (this.permission.peek()) return Promise.resolve();

And consider:

-			.catch(() => undefined);
+			.catch((err) => {
+				console.debug(`getUserMedia(${this.kind}) denied or failed`, err);
+			});

133-137: Persisting the user-chosen deviceId is the right move; pick the track by kind to be explicit.

-				const deviceId = stream.getTracks().at(0)?.getSettings().deviceId;
+				const track =
+					this.kind === "audio" ? stream.getAudioTracks().at(0) : stream.getVideoTracks().at(0);
+				const deviceId = track?.getSettings().deviceId;
js/hang/src/publish/source/microphone.ts (2)

39-40: Guard against empty-string deviceId; exact: "" can cause OverconstrainedError.

An empty string is not a valid concrete deviceId in constraints. Recommend reverting to truthy (or explicit non-empty) check.

-			deviceId: device !== undefined ? { exact: device } : undefined,
+			// Ignore empty-string sentinel values
+			deviceId: device && device !== "" ? { exact: device } : undefined,

65-68: Persist only when a real deviceId exists and it differs to avoid redundant emits/reacquire.

-			if (device === undefined) {
-				// Save the device that the user selected during the dialog prompt.
-				this.device.preferred.set(settings.deviceId);
-			}
+			if (
+				device === undefined &&
+				settings.deviceId &&
+				this.device.preferred.peek() !== settings.deviceId
+			) {
+				// Save the device the user selected during the dialog prompt.
+				this.device.preferred.set(settings.deviceId);
+			}
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between e52f601 and 8fda10b.

📒 Files selected for processing (2)
  • js/hang/src/publish/source/device.ts (1 hunks)
  • js/hang/src/publish/source/microphone.ts (2 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
{rs,js}/hang/**

📄 CodeRabbit inference engine (CLAUDE.md)

All media logic belongs in the hang layer (rs/hang and js/hang)

Files:

  • js/hang/src/publish/source/microphone.ts
  • js/hang/src/publish/source/device.ts
js/**

📄 CodeRabbit inference engine (CLAUDE.md)

Place JS/TS packages under the js/ directory

Files:

  • js/hang/src/publish/source/microphone.ts
  • js/hang/src/publish/source/device.ts
🧬 Code graph analysis (1)
js/hang/src/publish/source/microphone.ts (1)
js/hang/src/publish/element.ts (2)
  • device (108-110)
  • device (112-114)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Check

@kixelated kixelated merged commit 1e1a443 into main Aug 31, 2025
3 checks passed
@kixelated kixelated deleted the device-manager branch August 31, 2025 22:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants