feat: replace the preset gallery with original schedules

Swap the six suggested rules for an original set so the preset offerings
are OpenAppLock's own:

- Focus Time: Morning Focus (08:00-11:30 weekdays), Deep Work (13:30-16:00 weekdays)
- Rest & Recharge: Evening Reset (21:00-23:00), Lights Out (23:00-06:30, crosses midnight)
- Healthy Balance: Family Dinner (18:00-19:30), Screen-Free Sunday (09:00-20:00, Sundays)

Update the spec preset gallery (sections 1, 3.3, 4) and the preset
unit/UI tests to the new ids, names, and times. Build green; the preset
draft unit test and the two preset UI tests pass.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-13 22:32:05 -04:00
parent d005ed4fb1
commit 44e6086e29
4 changed files with 51 additions and 51 deletions

View File

@@ -22,7 +22,7 @@ struct RulePreset: Identifiable, Hashable, Sendable {
} }
} }
/// A titled group of presets ("Get More Done", "Sleep, Relax and Reset", ). /// A titled group of presets ("Focus Time", "Rest & Recharge", ).
struct RulePresetSection: Identifiable, Hashable, Sendable { struct RulePresetSection: Identifiable, Hashable, Sendable {
let id: String let id: String
let title: String let title: String
@@ -31,20 +31,20 @@ struct RulePresetSection: Identifiable, Hashable, Sendable {
static let all: [RulePresetSection] = [ static let all: [RulePresetSection] = [
RulePresetSection( RulePresetSection(
id: "productivity", id: "focus",
title: "Get More Done", title: "Focus Time",
subtitle: "Maximize your productivity while staying sane.", subtitle: "Protect your deep-work hours.",
presets: [ presets: [
RulePreset( RulePreset(
id: "work-time", name: "Work Time", id: "morning-focus", name: "Morning Focus",
startMinutes: 9 * 60, endMinutes: 17 * 60, days: Weekday.weekdays, startMinutes: 8 * 60, endMinutes: 11 * 60 + 30, days: Weekday.weekdays,
symbolName: "briefcase.fill", symbolName: "sunrise.fill",
gradientTop: Color(red: 0.16, green: 0.27, blue: 0.22), gradientTop: Color(red: 0.33, green: 0.23, blue: 0.13),
gradientBottom: Color(red: 0.05, green: 0.10, blue: 0.08) gradientBottom: Color(red: 0.12, green: 0.07, blue: 0.03)
), ),
RulePreset( RulePreset(
id: "laser-focus", name: "Laser Focus", id: "deep-work", name: "Deep Work",
startMinutes: 14 * 60, endMinutes: 18 * 60, days: Weekday.weekdays, startMinutes: 13 * 60 + 30, endMinutes: 16 * 60, days: Weekday.weekdays,
symbolName: "scope", symbolName: "scope",
gradientTop: Color(red: 0.13, green: 0.20, blue: 0.33), gradientTop: Color(red: 0.13, green: 0.20, blue: 0.33),
gradientBottom: Color(red: 0.04, green: 0.06, blue: 0.12) gradientBottom: Color(red: 0.04, green: 0.06, blue: 0.12)
@@ -52,20 +52,20 @@ struct RulePresetSection: Identifiable, Hashable, Sendable {
] ]
), ),
RulePresetSection( RulePresetSection(
id: "sleep", id: "rest",
title: "Sleep, Relax and Reset", title: "Rest & Recharge",
subtitle: "Sleep better, rise refreshed.", subtitle: "Wind the day down on schedule.",
presets: [ presets: [
RulePreset( RulePreset(
id: "wind-down", name: "Wind Down", id: "evening-reset", name: "Evening Reset",
startMinutes: 20 * 60, endMinutes: 22 * 60, days: Weekday.everyDay, startMinutes: 21 * 60, endMinutes: 23 * 60, days: Weekday.everyDay,
symbolName: "moon.haze.fill", symbolName: "moon.haze.fill",
gradientTop: Color(red: 0.25, green: 0.18, blue: 0.33), gradientTop: Color(red: 0.25, green: 0.18, blue: 0.33),
gradientBottom: Color(red: 0.08, green: 0.05, blue: 0.12) gradientBottom: Color(red: 0.08, green: 0.05, blue: 0.12)
), ),
RulePreset( RulePreset(
id: "deep-sleep", name: "Deep Sleep", id: "lights-out", name: "Lights Out",
startMinutes: 22 * 60, endMinutes: 6 * 60, days: Weekday.everyDay, startMinutes: 23 * 60, endMinutes: 6 * 60 + 30, days: Weekday.everyDay,
symbolName: "moon.zzz.fill", symbolName: "moon.zzz.fill",
gradientTop: Color(red: 0.10, green: 0.13, blue: 0.30), gradientTop: Color(red: 0.10, green: 0.13, blue: 0.30),
gradientBottom: Color(red: 0.02, green: 0.03, blue: 0.10) gradientBottom: Color(red: 0.02, green: 0.03, blue: 0.10)
@@ -73,21 +73,21 @@ struct RulePresetSection: Identifiable, Hashable, Sendable {
] ]
), ),
RulePresetSection( RulePresetSection(
id: "habits", id: "balance",
title: "Build Healthy Habits", title: "Healthy Balance",
subtitle: "Spend time on important things.", subtitle: "Make room for what matters.",
presets: [ presets: [
RulePreset( RulePreset(
id: "reading-time", name: "Reading Time", id: "family-dinner", name: "Family Dinner",
startMinutes: 19 * 60 + 45, endMinutes: 20 * 60 + 45, days: Weekday.everyDay, startMinutes: 18 * 60, endMinutes: 19 * 60 + 30, days: Weekday.everyDay,
symbolName: "book.fill", symbolName: "fork.knife",
gradientTop: Color(red: 0.33, green: 0.23, blue: 0.13), gradientTop: Color(red: 0.16, green: 0.27, blue: 0.22),
gradientBottom: Color(red: 0.12, green: 0.07, blue: 0.03) gradientBottom: Color(red: 0.05, green: 0.10, blue: 0.08)
), ),
RulePreset( RulePreset(
id: "gym-time", name: "Gym Time", id: "screen-free-sunday", name: "Screen-Free Sunday",
startMinutes: 17 * 60 + 30, endMinutes: 18 * 60 + 30, days: Weekday.everyDay, startMinutes: 9 * 60, endMinutes: 20 * 60, days: [.sunday],
symbolName: "figure.run", symbolName: "leaf.fill",
gradientTop: Color(red: 0.13, green: 0.30, blue: 0.30), gradientTop: Color(red: 0.13, green: 0.30, blue: 0.30),
gradientBottom: Color(red: 0.03, green: 0.10, blue: 0.10) gradientBottom: Color(red: 0.03, green: 0.10, blue: 0.10)
), ),

View File

@@ -177,12 +177,12 @@ struct RuleDraftTests {
let preset = try #require( let preset = try #require(
RulePresetSection.all RulePresetSection.all
.flatMap(\.presets) .flatMap(\.presets)
.first { $0.id == "deep-sleep" } .first { $0.id == "lights-out" }
) )
let draft = RuleDraft(preset: preset) let draft = RuleDraft(preset: preset)
#expect(draft.name == "Deep Sleep") #expect(draft.name == "Lights Out")
#expect(draft.scheduleConfig.startMinutes == 22 * 60) #expect(draft.scheduleConfig.startMinutes == 23 * 60)
#expect(draft.scheduleConfig.endMinutes == 6 * 60) #expect(draft.scheduleConfig.endMinutes == 6 * 60 + 30)
#expect(draft.kind == .schedule) #expect(draft.kind == .schedule)
} }
} }

View File

@@ -37,11 +37,11 @@ final class RuleCreationUITests: XCTestCase {
app.goToRulesTab() app.goToRulesTab()
app.buttons["newRuleButton"].waitToAppear().tap() app.buttons["newRuleButton"].waitToAppear().tap()
app.buttons["preset-work-time"].waitToAppear().tap() app.buttons["preset-morning-focus"].waitToAppear().tap()
XCTAssertEqual(app.staticTexts["ruleEditorTitle"].waitToAppear().label, "Work Time") XCTAssertEqual(app.staticTexts["ruleEditorTitle"].waitToAppear().label, "Morning Focus")
app.buttons["commitRuleButton"].waitToAppear().tap() app.buttons["commitRuleButton"].waitToAppear().tap()
app.buttons["ruleCard-Work Time"].waitToAppear() app.buttons["ruleCard-Morning Focus"].waitToAppear()
} }
func testRenameRuleInEditor() throws { func testRenameRuleInEditor() throws {
@@ -190,8 +190,8 @@ final class RuleCreationUITests: XCTestCase {
app.buttons["ruleKind-schedule"].waitToAppear() app.buttons["ruleKind-schedule"].waitToAppear()
XCTAssertTrue(app.buttons["ruleKind-timeLimit"].exists) XCTAssertTrue(app.buttons["ruleKind-timeLimit"].exists)
XCTAssertTrue(app.buttons["ruleKind-openLimit"].exists) XCTAssertTrue(app.buttons["ruleKind-openLimit"].exists)
XCTAssertTrue(app.staticTexts["Get More Done"].exists) XCTAssertTrue(app.staticTexts["Focus Time"].exists)
XCTAssertTrue(app.buttons["preset-work-time"].exists) XCTAssertTrue(app.buttons["preset-morning-focus"].exists)
XCTAssertTrue(app.buttons["preset-laser-focus"].exists) XCTAssertTrue(app.buttons["preset-deep-work"].exists)
} }
} }

View File

@@ -20,7 +20,7 @@ offered, presented on the "New Rule" sheet:
**Common attributes** — present for *every* rule kind: **Common attributes** — present for *every* rule kind:
- **Name** — user-editable, free text (presets: "Work Time", "Weekend Zen", "Locked In", "Sleep", "Wind Down", "Deep Sleep", "Laser Focus", "Reading Time", "Gym Time"; defaults for new rules: "In the Zone" (schedule), "Time Keeper" (time limit)) - **Name** — user-editable, free text (presets: "Morning Focus", "Deep Work", "Evening Reset", "Lights Out", "Family Dinner", "Screen-Free Sunday"; defaults for new rules: "In the Zone" (schedule), "Time Keeper" (time limit))
- **Days of week** — 7-day toggle set, summarized as "Weekdays" / "Weekends" / "Every day" / custom - **Days of week** — 7-day toggle set, summarized as "Weekdays" / "Weekends" / "Every day" / custom
- **App List** *(OpenAppLock refinement)* — each rule points to exactly one - **App List** *(OpenAppLock refinement)* — each rule points to exactly one
**App List**: a named, reusable selection of apps/categories/websites stored **App List**: a named, reusable selection of apps/categories/websites stored
@@ -145,15 +145,15 @@ Presented from "+ New". Full-height sheet, scrollable.
defaults. defaults.
- **Preset gallery** — vertically scrolling sections, each with a bold header - **Preset gallery** — vertically scrolling sections, each with a bold header
+ grey subtitle, containing a 2-up grid of photo-backed preset cards: + grey subtitle, containing a 2-up grid of photo-backed preset cards:
- **Get More Done** — "Maximize your productivity while staying sane." - **Focus Time** — "Protect your deep-work hours."
- Work Time (Schedule 09:0017:00, Block, weekdays) - Morning Focus (Schedule 08:0011:30, Block, weekdays)
- Laser Focus (Schedule 14:0018:00, Block) - Deep Work (Schedule 13:3016:00, Block, weekdays)
- **Sleep, Relax and Reset** — "Sleep better, rise refreshed." - **Rest & Recharge** — "Wind the day down on schedule."
- Wind Down (Schedule 20:0022:00, Block) - Evening Reset (Schedule 21:0023:00, Block)
- Deep Sleep (Schedule 22:0006:00, Block) - Lights Out (Schedule 23:0006:30, Block)
- **Build Healthy Habits** — "Spend time on important things." - **Healthy Balance** — "Make room for what matters."
- Reading Time (Schedule ~19:4520:45, Block) - Family Dinner (Schedule 18:0019:30, Block)
- Gym Time (Schedule 17:3018:30, Block) - Screen-Free Sunday (Schedule 09:0020:00, Block, Sundays)
- **Preset card** anatomy: full-bleed background photo, top row icon pair - **Preset card** anatomy: full-bleed background photo, top row icon pair
(type → shield), time range caption, name, "Block" + suggested app icons, (type → shield), time range caption, name, "Block" + suggested app icons,
and a circular "+" button bottom-right. Tapping anywhere opens the and a circular "+" button bottom-right. Tapping anywhere opens the
@@ -266,8 +266,8 @@ Full-height sheet:
## 4. Behavioral spec ## 4. Behavioral spec
1. **Activation** — a Schedule rule becomes active at `From` on an enabled 1. **Activation** — a Schedule rule becomes active at `From` on an enabled
day and deactivates at `To` (windows crossing midnight, e.g. 22:0006:00, day and deactivates at `To` (windows crossing midnight, e.g. 23:0006:30,
must be supported — Deep Sleep preset does this). must be supported — Lights Out preset does this).
2. **While active** — the rule's app selection is shielded (and, when the 2. **While active** — the rule's app selection is shielded (and, when the
rule's Block Adult Content toggle is on, Screen Time's adult-website rule's Block Adult Content toggle is on, Screen Time's adult-website
filter is engaged for the same span); blocked apps also filter is engaged for the same span); blocked apps also