Rebuild the app around recurring screen-time blocking rules modeled on Opal's My Apps tab: - Onboarding with FamilyControls Screen Time authorization - Apps home with Blocked Apps tiles and live rule cards - New Rule sheet: Schedule/Time Limit/Open Limit types + preset gallery - Rule editors: time windows (incl. overnight), day picker, app selection via FamilyActivityPicker (Block/Allow Only), rename, Hold to Commit - Hard Mode: active hard rules cannot be edited, disabled, deleted, or unblocked until their window ends; soft rules pause until next window - Shield enforcement through per-rule ManagedSettingsStore + RuleEnforcer - 73 unit tests (Swift Testing) + 16 UI tests (XCUITest) with launch-arg harness for in-memory storage, mocked authorization, seeded scenarios - docs/RULES_FEATURE_SPEC.md: spec derived from the reference recording Known gap: background window transitions and time/open-limit thresholds need a DeviceActivityMonitor extension; shields currently sync while the app is running. Co-Authored-By: Claude <noreply@anthropic.com>
66 lines
2.0 KiB
Swift
66 lines
2.0 KiB
Swift
//
|
|
// Weekday.swift
|
|
// Severed
|
|
//
|
|
|
|
import Foundation
|
|
|
|
/// A day of the week, using `Calendar` weekday numbering (1 = Sunday … 7 = Saturday).
|
|
enum Weekday: Int, CaseIterable, Codable, Hashable, Sendable {
|
|
case sunday = 1
|
|
case monday = 2
|
|
case tuesday = 3
|
|
case wednesday = 4
|
|
case thursday = 5
|
|
case friday = 6
|
|
case saturday = 7
|
|
|
|
static let weekdays: Set<Weekday> = [.monday, .tuesday, .wednesday, .thursday, .friday]
|
|
static let weekends: Set<Weekday> = [.saturday, .sunday]
|
|
static let everyDay: Set<Weekday> = Set(Weekday.allCases)
|
|
|
|
/// Display order used by the day picker, matching the reference UI: S M T W T F S.
|
|
static let displayOrder: [Weekday] = [
|
|
.sunday, .monday, .tuesday, .wednesday, .thursday, .friday, .saturday,
|
|
]
|
|
|
|
/// Single-letter label for the circular day toggles.
|
|
var shortLabel: String {
|
|
switch self {
|
|
case .sunday, .saturday: "S"
|
|
case .monday: "M"
|
|
case .tuesday, .thursday: "T"
|
|
case .wednesday: "W"
|
|
case .friday: "F"
|
|
}
|
|
}
|
|
|
|
/// Three-letter abbreviation used in custom day summaries ("Mon, Wed, Fri").
|
|
var abbreviation: String {
|
|
switch self {
|
|
case .sunday: "Sun"
|
|
case .monday: "Mon"
|
|
case .tuesday: "Tue"
|
|
case .wednesday: "Wed"
|
|
case .thursday: "Thu"
|
|
case .friday: "Fri"
|
|
case .saturday: "Sat"
|
|
}
|
|
}
|
|
}
|
|
|
|
extension Set<Weekday> {
|
|
/// Human-readable summary shown next to the day picker and in rule details:
|
|
/// "Weekdays", "Weekends", "Every day", "Never", or a list like "Mon, Wed, Fri".
|
|
var summary: String {
|
|
if self == Weekday.everyDay { return "Every day" }
|
|
if self == Weekday.weekdays { return "Weekdays" }
|
|
if self == Weekday.weekends { return "Weekends" }
|
|
if isEmpty { return "Never" }
|
|
return Weekday.displayOrder
|
|
.filter(contains)
|
|
.map(\.abbreviation)
|
|
.joined(separator: ", ")
|
|
}
|
|
}
|