Files
OpenAppLock/Severed/Models/RuleDraft.swift
Brendan Chen c99538565c feat: adult content filter toggle + native New Rule navigation
- Add Block Adult Content toggle to all three rule editors, persisted as
  BlockingRule.blockAdultContent (inline default for clean migration of
  existing stores) and surfaced in the detail sheet as an
  'Adult websites: Blocked/Allowed' row
- Engage Screen Time's adult-website filter
  (webContent.blockedByFilter = .auto()) alongside the rule's shield and
  clear it when the shield clears
- Replace the New Rule sheet's view-swap with a NavigationStack push
  (navigationDestination(item:)); the editor uses native chrome there
  (system back button, inline title, toolbar rename), enabling the push
  animation and edge-swipe back
- Tests: 93 passing (+2 unit: draft round-trip and enforcer forwarding;
  +3 UI: toggle-to-detail flow, default-allowed row, swipe-back as a
  behavioral proof of native navigation)
- Spec updated accordingly (editor sections, behavior, data model,
  navigation note)

Co-Authored-By: Claude <noreply@anthropic.com>
2026-06-12 13:34:52 -04:00

90 lines
2.7 KiB
Swift
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//
// RuleDraft.swift
// Severed
//
import Foundation
/// Value-type working copy of a rule used by the editors, so cancelling an
/// edit never touches the persisted model. Hashable so it can drive
/// `navigationDestination(item:)`.
struct RuleDraft: Hashable {
var name: String
var kind: RuleKind
var days: Set<Weekday>
var startMinutes: Int
var endMinutes: Int
var dailyLimitMinutes: Int
var maxOpens: Int
var hardMode: Bool
var blockAdultContent: Bool
var selectionMode: SelectionMode
var selectionData: Data?
var selectionCount: Int
/// A fresh draft for a new rule of the given kind, using the reference
/// app's defaults (95 weekdays schedule, 45m/day, 5 opens/day).
init(kind: RuleKind) {
self.name = kind.defaultRuleName
self.kind = kind
self.days = Weekday.weekdays
self.startMinutes = 9 * 60
self.endMinutes = 17 * 60
self.dailyLimitMinutes = 45
self.maxOpens = 5
self.hardMode = false
self.blockAdultContent = false
self.selectionMode = .block
self.selectionData = nil
self.selectionCount = 0
}
init(rule: BlockingRule) {
self.name = rule.name
self.kind = rule.kind
self.days = rule.days
self.startMinutes = rule.startMinutes
self.endMinutes = rule.endMinutes
self.dailyLimitMinutes = rule.dailyLimitMinutes
self.maxOpens = rule.maxOpens
self.hardMode = rule.hardMode
self.blockAdultContent = rule.blockAdultContent
self.selectionMode = rule.selectionMode
self.selectionData = rule.selectionData
self.selectionCount = rule.selectionCount
}
init(preset: RulePreset) {
self.init(kind: .schedule)
self.name = preset.name
self.days = preset.days
self.startMinutes = preset.startMinutes
self.endMinutes = preset.endMinutes
}
func apply(to rule: BlockingRule) {
rule.name = name
rule.kind = kind
rule.days = days
rule.startMinutes = startMinutes
rule.endMinutes = endMinutes
rule.dailyLimitMinutes = dailyLimitMinutes
rule.maxOpens = maxOpens
rule.hardMode = hardMode
rule.blockAdultContent = blockAdultContent
rule.selectionMode = selectionMode
rule.selectionData = selectionData
rule.selectionCount = selectionCount
}
func makeRule() -> BlockingRule {
let rule = BlockingRule(name: name, kind: kind)
apply(to: rule)
return rule
}
var schedule: RuleSchedule {
RuleSchedule(startMinutes: startMinutes, endMinutes: endMinutes, days: days)
}
}