Rebase follow-up. #1274 split the settings page into facades and #1277 split
its spec along the same seams, so this PR's renderer changes moved:
- the failed-read warning is now raised by `SettingsFormFacade.loadSettings()`,
which owns the store call
- the cover-size and EPG-view-mode writes moved into the facade, so their
rejection handling went with them
- `showStorageFailure()` became `SettingsSnackbarService.storageFailure()`,
because the component and the facade both need it and neither can reach the
other's copy
- `onSubmit()` still owns the dialog, so the catch and the "close only after
the write succeeded" ordering stay in the component
The four regression tests live in `settings.component.storage-failure.spec.ts`
rather than being appended to `settings.component.form.spec.ts`, which would
have pushed that file to 427 lines, past the 400-line lint maximum. They assert
against the MatDialog the component actually calls — spying on the instance
from `TestBed.inject(MatDialog)` does not observe it — and one of them stubs
the post-save EPG fetch, since a throw there would otherwise land in the same
catch and look like a failed write.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Settings live in the renderer's IndexedDB. Two failure modes made them
look saved while nothing reached disk:
- A second app instance sharing the same userData directory cannot take
the Chromium storage lock, so its renderer reads defaults and every
write is dropped. The app now holds a single-instance lock and focuses
the running window instead of starting a rival copy. The lock is
requested after the userData override so E2E runs with their own data
dir keep independent locks, and after Squirrel event handling.
IPTVNATOR_ALLOW_MULTIPLE_INSTANCES=1 opts out for local CDP debugging.
- updateSettings() patches in-memory state before persisting and
onSubmit() had no rejection handler, so a failed write produced an
unhandled rejection and no user-visible feedback. SettingsStore now
records which half of the round-trip failed and the settings page
surfaces it via a dismissible error snackbar; the dialog stays open on
failure so the save can be retried.
Closes#1156Closes#102
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
settings.component.spec.ts was 1516 lines and the last settings file in the
max-lines baseline. The behaviour that moved into facades now has its own
specs, driven directly instead of through the rendered page.
- settings-app-update.facade.spec.ts: status polling/retry, bridge actions,
release notes dialog, version messaging, dispose
- settings-epg.facade.spec.ts: refresh, clear flow, post-save re-fetch
- settings-playlist-reset.facade.spec.ts: summary, dialog, Electron progress,
browser fallback, failure snackbar
- settings-backup.facade.spec.ts: desktop export, browser download fallback
- settings.component.spec.ts keeps the page shell, the facade lifecycle seam
and runtime capabilities; settings.component.form.spec.ts takes hydration,
section outputs, dashboard controls and submit
- settings-section-scroll.directive.spec.ts gives the directive its first spec
- shared TestBed fixtures live in settings/test-stubs/, kept out of both the
app build (.stub.ts) and the coverage ratchet (test-stubs/)
105 settings tests, up from 94; every file is under the 400-line limit, so
settings.component.spec.ts leaves the baseline.