mirror of
https://github.com/4gray/iptvnator.git
synced 2026-10-10 10:06:15 -08:00
fix(settings): keep the parental lock switch on the saved state and roll back to the recovered value
The Settings switch snaps back to the saved state when clicked and follows it once the PIN action succeeds, so a cancelled or refused PIN no longer leaves it showing the opposite state. A failed switch write is undone to the value read after the settings retry instead of the hard-coded inverse. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
ed48e7fd2b
commit
8d11f674cf
6 files changed
+129
-5
No files matched your search
@@ -0,0 +1,51 @@
|
||||
import { persistParentalLockEnabled } from './parental-lock-settings-writer';
|
||||
|
||||
describe('persistParentalLockEnabled', () => {
|
||||
it('rolls a failed write back to the value recovered by the settings retry', async () => {
|
||||
jest.spyOn(console, 'error').mockImplementation(() => undefined);
|
||||
let failure: 'load' | 'save' | null = 'load';
|
||||
let stored: boolean | undefined = undefined;
|
||||
const settings = {
|
||||
storageFailure: () => failure,
|
||||
parentalLockEnabled: () => stored,
|
||||
// The retried read recovers: the switch was already off.
|
||||
loadSettings: jest.fn(async () => {
|
||||
failure = null;
|
||||
stored = false;
|
||||
}),
|
||||
updateSettings: jest
|
||||
.fn()
|
||||
.mockRejectedValueOnce(new Error('disk full'))
|
||||
.mockResolvedValue(undefined),
|
||||
};
|
||||
|
||||
await expect(persistParentalLockEnabled(settings, false)).resolves.toBe(
|
||||
false
|
||||
);
|
||||
|
||||
// Undone to the recovered `false`, not the hard-coded inverse.
|
||||
expect(settings.updateSettings).toHaveBeenLastCalledWith({
|
||||
parentalLockEnabled: false,
|
||||
});
|
||||
});
|
||||
|
||||
it('undoes to the inverse when the switch was never persisted', async () => {
|
||||
jest.spyOn(console, 'error').mockImplementation(() => undefined);
|
||||
const settings = {
|
||||
storageFailure: () => null,
|
||||
loadSettings: jest.fn(),
|
||||
updateSettings: jest
|
||||
.fn()
|
||||
.mockRejectedValueOnce(new Error('disk full'))
|
||||
.mockResolvedValue(undefined),
|
||||
};
|
||||
|
||||
await expect(persistParentalLockEnabled(settings, true)).resolves.toBe(
|
||||
false
|
||||
);
|
||||
|
||||
expect(settings.updateSettings).toHaveBeenLastCalledWith({
|
||||
parentalLockEnabled: false,
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -5,7 +5,10 @@ import { mirrorParentalLockEnabledSetting } from './parental-lock-bridge';
|
||||
type SettingsWriter = Pick<
|
||||
InstanceType<typeof SettingsStore>,
|
||||
'updateSettings' | 'loadSettings'
|
||||
> & { storageFailure?: () => 'load' | 'save' | null };
|
||||
> & {
|
||||
storageFailure?: () => 'load' | 'save' | null;
|
||||
parentalLockEnabled?: () => boolean | undefined;
|
||||
};
|
||||
|
||||
/**
|
||||
* `updateSettings` writes the WHOLE settings object. After a failed startup
|
||||
@@ -39,9 +42,14 @@ export async function persistParentalLockEnabled(
|
||||
if (!(await ensureParentalLockSettingsReadable(settings))) {
|
||||
return false;
|
||||
}
|
||||
// Read AFTER the retry, as for the relock timeout: a recovered read may
|
||||
// already hold `enabled` (e.g. disable() entered while the unknown
|
||||
// switch followed the stored PIN), and undoing to the inverse would
|
||||
// then write the opposite of what was persisted.
|
||||
const previous = settings.parentalLockEnabled?.() ?? !enabled;
|
||||
const undo = () =>
|
||||
settings
|
||||
.updateSettings({ parentalLockEnabled: !enabled })
|
||||
.updateSettings({ parentalLockEnabled: previous })
|
||||
.catch(() => undefined);
|
||||
try {
|
||||
await settings.updateSettings({ parentalLockEnabled: enabled });
|
||||
|
||||
Reference in new issue
Block a user