feat: re-skin to native three-tab UI and add Uninstall Protection
Replace the custom themed presentation with the bare native iOS design language across a three-tab TabView (Home / Rules / Settings): - MainTabView hosts the enforcement lifecycle (refresh loop, rule-change and scene-active reconcile) so it runs regardless of the selected tab - HomeView: "Currently Blocking" + "Usage" sections (replaces AppsHomeView) - RulesListView: rules grouped into Schedule / Time Limit / Open Limit - SettingsView: Uninstall Protection toggle + Manage App Lists - AppListLibraryView / ManageAppListsView: shared App List management - AppSettings: app-group-backed settings store Uninstall Protection: while enabled and any Hard Mode rule is actively blocking, deny device app-removal via a dedicated ManagedSettingsStore (RulePolicy.shouldDenyAppRemoval -> RuleEnforcer -> ShieldController). Slim AppListPickerSheet to the picker role; drop AppsHomeView and the custom Theme/HoldToCommitButton/RuleCardView chrome. Update spec section 6 and the unit/UI test suites accordingly. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -254,4 +254,14 @@ struct UsageDisplayTests {
|
||||
let over = RuleUsage(minutesUsed: 60)
|
||||
#expect(UsageDisplay.subtitle(for: timeRule, usage: over) == "45m of 45m used today")
|
||||
}
|
||||
|
||||
@Test("Typed subtitles prefix the rule kind so type is clear without the icon")
|
||||
func typedSubtitles() {
|
||||
#expect(
|
||||
UsageDisplay.typedSubtitle(for: timeRule, usage: RuleUsage(minutesUsed: 18))
|
||||
== "Time Limit · 18m of 45m used today")
|
||||
#expect(
|
||||
UsageDisplay.typedSubtitle(for: openRule, usage: RuleUsage(opensUsed: 2))
|
||||
== "Open Limit · 2 of 5 opens today")
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user