feat(setup): noon-daily default + force-refresh hint + inline remove confirm
CI / test (push) Has been cancelled

Three coordinated UX changes touching defaults and the settings sheet.

1. Server defaults: DeviceService::linkToUser now sets timezone =
   user.timezone and wakeTimes = [12*60] (noon-daily) when creating a
   new Device row OR transferring ownership on takeover. Replaces the
   prior "1440-min interval anchored to last-seen-time" default that
   could land a recipient's first photo at 3 am.

2. PWA propagation note: now mentions "briefly disconnect and reconnect
   the frame's power" as the immediate-refresh gesture. Pairs with the
   existing X-Boot-Reason: cold force-resync — the firmware already
   honors a power-cycle as a deliberate refresh request, but users had
   no way to discover that.

3. Remove-this-frame: replaced the native window.confirm() with an
   in-sheet confirmation panel showing the explanatory text. Inline
   keeps the gesture inside the existing sheet flow and gives the
   destructive button a fixed location, instead of a floating native
   dialog that varies per browser. The confirm body explicitly says
   "this can't be undone" to match the irreversibility.

Tests:
  - DeviceServiceTest: new-device default, takeover-resets-with-default,
    UTC fallback when user has empty timezone.
  - SetupControllerTest: claim-takes-over-defaults updated to assert
    [12*60] wakeTimes.
  - HomeView.test: 4 cases covering open-confirm, yes-confirm, cancel,
    propagation-note text.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-08 16:19:51 -04:00
parent 6b13312fdd
commit e4f811581a
15 changed files with 190 additions and 51 deletions
+12 -1
View File
@@ -50,8 +50,15 @@ class DeviceService
$device = $this->repo->findOneBy(['mac' => $mac]);
if ($device === null) {
// Default new devices to "once a day at noon, in the user's
// timezone." Prior default was a 1440-minute *interval* anchored
// to last-seen time, which meant gift recipients saw their first
// photo at 3am if the device happened to set up at 3am — bad
// first-impression. Noon-daily is forgiving and predictable.
$device = new Device();
$device->setMac($mac);
$device->setTimezone($newOwner->getTimezone() ?: 'UTC');
$device->setWakeTimes([12 * 60]);
} elseif ($device->getUser() !== null && $device->getUser()->getId() !== $newOwner->getId()) {
if (!$allowClaim) {
throw new DeviceClaimRequiredException();
@@ -60,13 +67,17 @@ class DeviceService
$this->purgeDeviceHistory($device);
// Reset device-specific state so the new owner doesn't inherit
// schedule, locked image, current image, or pending poll info.
// Schedule reset matches the new-device default (noon-daily in
// the new owner's timezone) so the new owner gets the same
// forgiving first-image behavior.
$device->setLockedImage(null);
$device->setCurrentImage(null);
$device->setCurrentImageOrientation(null);
$device->setCurrentRenderedAt(null);
$device->setNextPollExpectedAt(null);
$device->setName('');
$device->setWakeTimes([]);
$device->setTimezone($newOwner->getTimezone() ?: 'UTC');
$device->setWakeTimes([12 * 60]);
}
$device->setUser($newOwner);