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

This commit is contained in:
timka123 committed 2026-10-28 09:50:58 +03:00
1 parent 157a113c39
commit d280101bce
21 files changed
+481 -43

No files matched your search

@@ -39,7 +39,6 @@ data class BlockerRules(
val blockedApps: List<String> = emptyList(),
val killBlockedApps: Boolean = true,
val schedule: List<BlockWindow> = emptyList(),
val strict: Boolean = false,
val notifyOnBlock: Boolean = true,
) {
companion object {
@@ -246,6 +245,20 @@ fun evaluatePolicy(
}
}
/**
* The port of `guard_locked()`: whether the guard may be switched off right now.
*
* A free function rather than a field on [PolicyVerdict], because this is a verdict
* about *editing* the rules and not about whether to block — and the two answers do
* disagree: scheduled mode outside its window is `active = false`, `locked = true`.
*
* Any kind of interval counts, running or paused. Switching the guard *on* is never
* locked, which is why "and the guard is on" is folded in here rather than left to
* the screen.
*/
fun guardLocked(rules: BlockerRules, current: IntervalFacts?): Boolean =
rules.enabled && current != null && current.isActiveStatus
/** Convenience for callers that only have an IANA zone id string. */
fun zoneOrDefault(timezone: String?): ZoneId =
runCatching { ZoneId.of(timezone?.trim().orEmpty()) }.getOrElse { ZoneId.systemDefault() }
@@ -303,4 +303,42 @@ class BlockerTest {
assertEquals(listOf("reddit.com", "www.reddit.com", "m.reddit.com"), expandDomains(rules))
}
// ---- the lock -----------------------------------------------------------
// Parity with `blocker_service.guard_locked()`.
@Test
fun `a live interval of any kind locks the guard on`() {
val on = BlockerRules(enabled = true)
assertTrue(guardLocked(on, working))
assertTrue(guardLocked(on, IntervalFacts("work", "paused")))
assertTrue(guardLocked(on, breaking))
assertTrue(guardLocked(on, IntervalFacts("long_break", "paused")))
}
@Test
fun `nothing live leaves the guard switchable`() {
val on = BlockerRules(enabled = true)
assertFalse(guardLocked(on, null))
assertFalse(guardLocked(on, IntervalFacts("work", "completed")))
assertFalse(guardLocked(on, IntervalFacts("work", "abandoned")))
}
@Test
fun `a guard that is off is never locked, so it can always be switched on`() {
assertFalse(guardLocked(BlockerRules(enabled = false), working))
}
@Test
fun `scheduled mode outside its window is inactive and still locked`() {
// The distinction `active` cannot express: nothing is being blocked right now,
// and the guard still must not be switched off before the window opens.
val rules = BlockerRules(
enabled = true,
mode = "scheduled",
schedule = listOf(BlockWindow("Work hours", "09:00", "18:00", listOf(0))),
)
assertFalse(evaluatePolicy(rules, working, at("2026-10-19", "20:00")).active)
assertTrue(guardLocked(rules, working))
}
}