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
@@ -266,7 +266,7 @@ class SetupControllerTest extends AppWebTestCase
$reloaded = $this->em()->find(Device::class, $deviceId);
$this->assertSame('new-claim03@example.com', $reloaded->getUser()->getEmail(), 'ownership transferred');
$this->assertSame('', $reloaded->getName(), 'name reset on takeover');
$this->assertSame([], $reloaded->getWakeTimes(), 'wakeTimes reset on takeover');
$this->assertSame([12 * 60], $reloaded->getWakeTimes(), 'wakeTimes reset to noon-daily default');
// Old history is gone.
$count = (int) $this->em()->createQueryBuilder()
+30 -1
View File
@@ -119,6 +119,8 @@ class DeviceServiceTest extends AppKernelTestCase
{
$oldOwner = $this->createUser('takeover-old@example.com');
$newOwner = $this->createUser('takeover-new@example.com');
$newOwner->setTimezone('America/Chicago');
$this->em()->flush();
$device = new Device();
$device->setMac('AA:BB:CC:DD:EE:06');
@@ -132,8 +134,35 @@ class DeviceServiceTest extends AppKernelTestCase
$this->em()->refresh($device);
$this->assertSame('', $device->getName(), 'name reset');
$this->assertSame([], $device->getWakeTimes(), 'wakeTimes reset');
$this->assertSame([12 * 60], $device->getWakeTimes(), 'wakeTimes default to noon-daily');
$this->assertSame('America/Chicago', $device->getTimezone(), 'timezone matches new owner');
$this->assertNull($device->getCurrentImage(), 'currentImage reset');
$this->assertNull($device->getNextPollExpectedAt(), 'next-poll reset');
}
public function test_link_creates_new_device_with_noon_daily_default(): void
{
$user = $this->createUser('new-device@example.com');
$user->setTimezone('Europe/Stockholm');
$this->em()->flush();
$device = $this->service->linkToUser('AA:BB:CC:DD:EE:09', $user);
$this->assertSame([12 * 60], $device->getWakeTimes(), 'noon-daily default');
$this->assertSame('Europe/Stockholm', $device->getTimezone(), 'inherits user tz');
}
public function test_link_falls_back_to_UTC_when_user_has_no_timezone(): void
{
$user = $this->createUser('no-tz@example.com');
// Force the user's timezone to empty to exercise the fallback —
// some legacy User rows may have NULL/blank timezone.
$ref = new \ReflectionProperty(\App\Entity\User::class, 'timezone');
$ref->setAccessible(true);
$ref->setValue($user, '');
$this->em()->flush();
$device = $this->service->linkToUser('AA:BB:CC:DD:EE:0A', $user);
$this->assertSame('UTC', $device->getTimezone());
}
}