Repository navigation
Mobile: don't let a different id-less device inherit a saved TV's row (#154) #155
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
a6e53c3
067ac16
fb42eb4
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -47,6 +47,11 @@ class Session { | |
| /// True while Optimize is applying a plan; tabs lock so the loop can't be | ||
| /// orphaned by navigating away. | ||
| applyInProgress = $state(false); | ||
| /// Set when the last `rememberCurrentDevice()` found a different device at | ||
| /// this row's address than the one saved (see savedDevices.ts). Screens | ||
| /// read and clear it once to show an honest "different device" note rather | ||
| /// than silently keeping the old TV's name. | ||
| identityNote = $state(""); | ||
|
|
||
| // Shared health cache. `healthLoaded` tracks a load *attempt* (an errored | ||
| // load still counts as loaded so we render the error, not a spinner forever). | ||
|
|
@@ -114,6 +119,7 @@ class Session { | |
| const generation = this.nextGeneration(); | ||
| this.recoveryAttempted = false; | ||
| this.liveness = "connecting"; | ||
| this.identityNote = ""; | ||
| try { | ||
| const result = await api.wirelessConnect(host, port, generation); | ||
| if (generation !== this.connectionGeneration) return { ok: false, message: "Connection attempt canceled." }; | ||
|
|
@@ -194,7 +200,8 @@ class Session { | |
|
|
||
| rememberCurrentDevice(): void { | ||
| if (!this.host) return; | ||
| rememberDevice(this.host, this.connectPort, this.connectedDevice); | ||
| const result = rememberDevice(this.host, this.connectPort, this.connectedDevice); | ||
| if (result.mismatch) this.identityNote = "A different device is now at this address."; | ||
| } | ||
|
|
||
| reset(): void { | ||
|
|
@@ -272,6 +279,13 @@ class Session { | |
| await this.refreshDevices(generation); | ||
| if (generation !== this.connectionGeneration) return false; | ||
| const live = this.connectedDevice != null; | ||
| // A silent recovery redials the same host:port, exactly like an | ||
| // explicit reconnect -- so it needs the same identity check before | ||
| // being trusted as live. Without it, a different id-less TV that | ||
| // took over this address mid-session would be silently treated as | ||
| // "the" saved TV reconnecting, the same bug #154 fixed for an | ||
| // explicit connect. | ||
| if (live) this.rememberCurrentDevice(); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
When the heartbeat or connection-loss handler recovers while the user remains on Dashboard (or another screen), this new call can set AGENTS.md reference: AGENTS.md:L45-L48 Useful? React with 👍 / 👎.
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fixed in fb42eb4: the display is now global instead of per-screen. |
||
| this.liveness = live ? "live" : "lost"; | ||
| if (live) this.recoveryAttempted = false; | ||
| return live; | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When an id-less transport is restored through
recoverOrMarkLost, that path callswirelessConnectandrefreshDevicesdirectly and never reaches thisrememberDevicecheck. If the old address now belongs to a different model or manufacturer, the heartbeat recovery marks the new TV live while retaining the sole old saved row, allowingcachedDeviceNameto display the old TV's name and subsequent actions to target the wrong device; run the same identity check after a successful silent recovery before marking it live.AGENTS.md reference: AGENTS.md:L45-L48
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed in 067ac16:
recoverOrMarkLost()now callsrememberCurrentDevice()on a successful silent recovery, before setting liveness to "live" -- the same fingerprint check an explicit reconnect gets. On a mismatch it setsidentityNoteand records the connection as a new row rather than refreshing the old one, socachedDeviceNamebecomes ambiguous (not the old name) once that happens. Covered by a new Playwright case intests/session.test.mjs("a silent recovery onto a different device does not inherit the old saved row or its name"), verified to fail before the fix.