The shield titled itself with the first enabled rule whose selection contained the app, regardless of whether that rule was the one actually blocking. When several rules covered the same app (e.g. a dormant schedule + the active limit) it named the wrong rule. Drop the rule-name lookup entirely: every shield now carries a generic "App Blocked" title. Open-limit shields keep their functional detail (running count + "Open (N left)" button) beneath that title. The text-only decision moves into a pure, unit-tested ShieldPresentation in Shared/, and the now-dead ShieldLookup.snapshot(containingApplication:) is removed. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
68 lines
2.6 KiB
Swift
68 lines
2.6 KiB
Swift
//
|
|
// ShieldConfigurationExtension.swift
|
|
// OpenAppLockShieldConfig
|
|
//
|
|
|
|
import ManagedSettings
|
|
import ManagedSettingsUI
|
|
import UIKit
|
|
|
|
/// Customizes the system shield. Apps under an open-limit rule show how many
|
|
/// opens were used and offer an "Open" secondary button while opens remain;
|
|
/// everything else gets a plain blocked shield. Shields never name a rule — the
|
|
/// text is decided by the pure `ShieldPresentation`.
|
|
final class ShieldConfigurationExtension: ShieldConfigurationDataSource {
|
|
override func configuration(shielding application: Application) -> ShieldConfiguration {
|
|
guard let token = application.token else { return configuration(for: .blocked) }
|
|
return configuration(forApplicationToken: token)
|
|
}
|
|
|
|
override func configuration(
|
|
shielding application: Application, in category: ActivityCategory
|
|
) -> ShieldConfiguration {
|
|
guard let token = application.token else { return configuration(for: .blocked) }
|
|
return configuration(forApplicationToken: token)
|
|
}
|
|
|
|
override func configuration(shielding webDomain: WebDomain) -> ShieldConfiguration {
|
|
configuration(for: .blocked)
|
|
}
|
|
|
|
override func configuration(
|
|
shielding webDomain: WebDomain, in category: ActivityCategory
|
|
) -> ShieldConfiguration {
|
|
configuration(for: .blocked)
|
|
}
|
|
|
|
private func configuration(
|
|
forApplicationToken token: ApplicationToken
|
|
) -> ShieldConfiguration {
|
|
let snapshots = RuleSnapshotStore().load()
|
|
guard
|
|
let snapshot = ShieldLookup.openLimitSnapshot(
|
|
containingApplication: token, in: snapshots)
|
|
else {
|
|
return configuration(for: .blocked)
|
|
}
|
|
let usage = UsageLedger().usage(for: snapshot.id, onDayContaining: .now)
|
|
return configuration(
|
|
for: .openLimit(
|
|
opensUsed: usage.opensUsed,
|
|
maxOpens: snapshot.maxOpens,
|
|
sessionMinutes: MonitoringPlan.openSessionMinutes))
|
|
}
|
|
|
|
private func configuration(for presentation: ShieldPresentation) -> ShieldConfiguration {
|
|
ShieldConfiguration(
|
|
backgroundBlurStyle: .systemMaterial,
|
|
title: .init(text: presentation.title, color: .label),
|
|
subtitle: .init(text: presentation.subtitle, color: .secondaryLabel),
|
|
primaryButtonLabel: .init(text: "OK", color: .white),
|
|
primaryButtonBackgroundColor: .systemBlue,
|
|
secondaryButtonLabel: presentation.secondaryButton.map {
|
|
.init(text: $0, color: .systemBlue)
|
|
}
|
|
)
|
|
}
|
|
}
|