fix(home): preview tracks frame state even with locked images and 304 polls
CI / test (push) Has been cancelled
CI / test (push) Has been cancelled
Three problems were stacked:
1. The 200 serving path didn't set currentImage when a locked image was
served (RotationService.advance bypassed). The frame got the locked
photo; the DB kept the previous one; Home showed the old one.
2. The 304 path didn't flush at all. lastSeenAt (markSeen) was lost on
every no-change poll, and any drift in currentImage couldn't self-heal.
For a frame that's been locked for a while, polls cycle as 304 forever
and the DB stays wrong indefinitely.
3. Pull-to-refresh fetched via fetchDevices(), which flips loading=true
and replaces the cards with "Loading…" mid-fetch. The PTR spinner was
working but users couldn't see the result of their refresh.
Fixes:
- Both 200 and 304 paths now set currentImage = $image and flush. The
304 path becomes self-healing for any device whose currentImage drifted
from reality (e.g., from before the 200-path fix).
- fetchDevices / fetchImages take an optional { silent: true } that
skips toggling loading.value. PTR refresh callbacks pass silent so
the cards stay visible during background refresh.
- HomeView also listens on visibilitychange and silently re-fetches when
the PWA returns to foreground, so reopening the app shows current
state without a manual pull.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -100,6 +100,16 @@ class DeviceImageController extends AbstractController
|
||||
&& $device->getCurrentImageOrientation() === $device->getOrientation()
|
||||
&& $renderedAt !== null
|
||||
&& $device->getCurrentRenderedAt()?->getTimestamp() === $renderedAt->getTimestamp()) {
|
||||
// Self-heal currentImage: locked-image polls bypass advance() which
|
||||
// would otherwise have set this. Without the assignment, currentImage
|
||||
// stays stale — Home would keep showing the previous photo even
|
||||
// though the device has been confirming the new one for cycles.
|
||||
// Also flush so markSeen() above is persisted on every 304 (lastSeenAt
|
||||
// would otherwise freeze whenever the device polls and gets no
|
||||
// change, causing the status badge to drift to "offline").
|
||||
$device->setCurrentImage($image);
|
||||
$em->flush();
|
||||
|
||||
$this->logger->info('device.poll.no_change', [
|
||||
'device_id' => $device->getId(),
|
||||
'mac' => $mac,
|
||||
@@ -125,8 +135,16 @@ class DeviceImageController extends AbstractController
|
||||
return $r;
|
||||
}
|
||||
|
||||
// Record the orientation and rendered_at we're serving at so the next
|
||||
// poll's 304 check can detect a flip or a re-render and force a re-fetch.
|
||||
// Record what the device is now showing, plus the orientation and
|
||||
// rendered_at we served at (so the next 304 check can detect a flip
|
||||
// or a re-render and force a re-fetch).
|
||||
//
|
||||
// currentImage must be set here for the locked-image path: rotation
|
||||
// is bypassed when a lock is in effect, so RotationService.advance()
|
||||
// never runs to update currentImage. Without this assignment, Home
|
||||
// would keep showing the previous photo even after the device pulled
|
||||
// the locked one.
|
||||
$device->setCurrentImage($image);
|
||||
$device->setCurrentImageOrientation($device->getOrientation());
|
||||
$device->setCurrentRenderedAt($renderedAt);
|
||||
$em->flush();
|
||||
|
||||
Reference in New Issue
Block a user