disallow to disable guard during focus
docker / image (push) Failing after 24s
lint / ruff (push) Failing after 28s
tests / backend (push) Successful in 37s
tests / desktop (push) Successful in 22s
tests / android-core (push) Successful in 49s
tests / web (push) Successful in 1m20s
android / apk (push) Successful in 8m49s
docker / image (push) Failing after 24s
lint / ruff (push) Failing after 28s
tests / backend (push) Successful in 37s
tests / desktop (push) Successful in 22s
tests / android-core (push) Successful in 49s
tests / web (push) Successful in 1m20s
android / apk (push) Successful in 8m49s
This commit is contained in:
21 files changed
+481
-43
No files matched your search
@@ -187,12 +187,59 @@ def schedule_active(settings: AppSettings, moment: datetime | None = None) -> bo
|
||||
return False
|
||||
|
||||
|
||||
GUARD_LOCK_NOTE = "Finish or drop the interval to switch the focus guard off."
|
||||
"""Said in one voice: the 409 detail, and the `lock_reason` every client renders."""
|
||||
|
||||
|
||||
class GuardLocked(RuntimeError):
|
||||
"""Switching the guard off was refused because an interval is live."""
|
||||
|
||||
|
||||
def guard_locked(session: Session, settings: AppSettings) -> bool:
|
||||
"""The guard is on and an interval is live, so it may not be switched off.
|
||||
|
||||
Any kind, running or paused — `pomodoro.current_session` is exactly that. A
|
||||
break is not an escape hatch and neither is pausing the clock.
|
||||
|
||||
Deliberately keyed off `blocker.enabled` rather than off `policy()["active"]`:
|
||||
in `scheduled` mode outside every window nothing is being blocked right now,
|
||||
and the guard still must not be quietly switched off before the window opens.
|
||||
"""
|
||||
return settings.blocker.enabled and pomodoro.current_session(session) is not None
|
||||
|
||||
|
||||
def ensure_guard_not_switched_off(
|
||||
session: Session, before: AppSettings, after: AppSettings
|
||||
) -> None:
|
||||
"""Refuse a settings write that would turn the guard off mid-interval.
|
||||
|
||||
Compares the two documents rather than reading the patch. The patch is a deep
|
||||
merge of a shared object and arrives loosely typed — `{"enabled": "no"}`
|
||||
coerces to `False`, so inspecting the raw keys would have to re-implement
|
||||
pydantic to be right — and "did the stored value go from on to off" is the
|
||||
only question that cannot be fooled. It also means an unrelated patch, a
|
||||
full-document echo and a re-sent `enabled: false` all pass by construction.
|
||||
|
||||
Switching the guard *on* is never refused.
|
||||
"""
|
||||
if not after.blocker.enabled and guard_locked(session, before):
|
||||
raise GuardLocked(GUARD_LOCK_NOTE)
|
||||
|
||||
|
||||
def policy(session: Session, settings: AppSettings) -> dict[str, Any]:
|
||||
"""What the desktop app polls: should I block, and what."""
|
||||
"""What the desktop app polls: should I block, and what.
|
||||
|
||||
`active` and `locked` answer two different questions and do disagree: in
|
||||
`scheduled` mode outside every window, nothing is blocked (`active` false)
|
||||
while the guard is still locked on (`locked` true) because an interval runs.
|
||||
"""
|
||||
blocker = settings.blocker
|
||||
current = pomodoro.current_session(session)
|
||||
active = False
|
||||
reason = "disabled"
|
||||
# The same rule as `guard_locked()`, reusing the row this function already
|
||||
# fetched rather than querying twice — `test_blocker.py` pins the two together.
|
||||
locked = blocker.enabled and current is not None
|
||||
|
||||
if blocker.enabled:
|
||||
if blocker.mode == "always":
|
||||
@@ -218,7 +265,8 @@ def policy(session: Session, settings: AppSettings) -> dict[str, Any]:
|
||||
"reason": reason,
|
||||
"mode": blocker.mode,
|
||||
"enabled": blocker.enabled,
|
||||
"strict": blocker.strict,
|
||||
"locked": locked,
|
||||
"lock_reason": GUARD_LOCK_NOTE if locked else "",
|
||||
"redirect_ip": blocker.redirect_ip,
|
||||
"domains": expand_domains(settings) if active else [],
|
||||
"apps": [app.strip() for app in blocker.blocked_apps if app.strip()] if active else [],
|
||||
|
||||
Reference in new issue
Block a user