refactor: re-skin UI with native iOS components
Replace the Opal-style custom presentation with the bare iOS design language, keeping the backend, flows, and accessibility identifiers: - Home: NavigationStack + List; rules now presented as list rows with kind icon, block summary, and trailing live status; '+' in the toolbar - New Rule: plain list of rule types and presets; editor still pushed via navigationDestination - Editor: native Form (DatePicker rows, day circles with summary in the section header, toggle rows with footers, stepper rows); create commits with a prominent 'Add Rule' button replacing Hold to Commit; edit uses toolbar Done plus red Disable/Delete rows - Detail: sheet with inline title + status caption, LabeledContent rows; Edit Rule pushes the editor natively - Default color scheme: drop forced dark mode and custom tint; system appearance and accent throughout - Delete Theme, HoldToCommitButton, RuleCardView and custom chrome - Use concrete Color.primary/secondary in button row labels (the hierarchical styles resolve against the button tint) - Tests: 93 passing; only delta is holdToCommitButton press → commitRuleButton tap in 5 call sites - Spec: add §6 mapping the original presentation to the native one
This commit is contained in:
@@ -5,9 +5,9 @@
|
||||
|
||||
import SwiftUI
|
||||
|
||||
/// Two-step onboarding: a welcome screen, then the Screen Time permission
|
||||
/// request. Onboarding only completes once authorization is approved — the
|
||||
/// app cannot block anything without it.
|
||||
/// Two-step onboarding using system styling: a welcome screen, then the
|
||||
/// Screen Time permission request. Onboarding only completes once
|
||||
/// authorization is approved — the app cannot block anything without it.
|
||||
struct OnboardingView: View {
|
||||
@Environment(ScreenTimeAuthorization.self) private var authorization
|
||||
@Environment(\.openURL) private var openURL
|
||||
@@ -31,23 +31,18 @@ struct OnboardingView: View {
|
||||
Spacer()
|
||||
footer
|
||||
}
|
||||
.padding(.horizontal, 28)
|
||||
.padding(.bottom, 24)
|
||||
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
||||
.foregroundStyle(.white)
|
||||
.background(Theme.background)
|
||||
.padding()
|
||||
}
|
||||
|
||||
private var welcome: some View {
|
||||
VStack(spacing: 18) {
|
||||
VStack(spacing: 16) {
|
||||
Image(systemName: "scissors")
|
||||
.font(.system(size: 56, weight: .semibold))
|
||||
.foregroundStyle(Theme.accent)
|
||||
.font(.system(size: 56))
|
||||
.foregroundStyle(.tint)
|
||||
Text("Severed")
|
||||
.font(.system(size: 38, weight: .bold))
|
||||
.font(.largeTitle.bold())
|
||||
Text("Block your most distracting apps with rules that keep you honest — on a schedule, with no way out when you choose Hard Mode.")
|
||||
.font(.system(size: 16))
|
||||
.foregroundStyle(Theme.textSecondary)
|
||||
.foregroundStyle(.secondary)
|
||||
.multilineTextAlignment(.center)
|
||||
}
|
||||
}
|
||||
@@ -55,10 +50,10 @@ struct OnboardingView: View {
|
||||
private var permission: some View {
|
||||
VStack(spacing: 24) {
|
||||
Image(systemName: "hourglass")
|
||||
.font(.system(size: 48, weight: .semibold))
|
||||
.foregroundStyle(Theme.accent)
|
||||
.font(.system(size: 48))
|
||||
.foregroundStyle(.tint)
|
||||
Text("Allow Screen Time Access")
|
||||
.font(.system(size: 28, weight: .bold))
|
||||
.font(.title.bold())
|
||||
.multilineTextAlignment(.center)
|
||||
VStack(alignment: .leading, spacing: 14) {
|
||||
bullet("shield.fill", "Severed uses Apple's Screen Time framework to block the apps you choose.")
|
||||
@@ -68,8 +63,8 @@ struct OnboardingView: View {
|
||||
if authorization.status == .denied || authorization.lastRequestFailed {
|
||||
VStack(spacing: 10) {
|
||||
Text("Screen Time access was declined. Severed can't block apps without it.")
|
||||
.font(.system(size: 13))
|
||||
.foregroundStyle(Theme.destructive)
|
||||
.font(.footnote)
|
||||
.foregroundStyle(.red)
|
||||
.multilineTextAlignment(.center)
|
||||
.accessibilityIdentifier("permissionDeniedLabel")
|
||||
Button("Open Settings") {
|
||||
@@ -77,8 +72,6 @@ struct OnboardingView: View {
|
||||
openURL(url)
|
||||
}
|
||||
}
|
||||
.font(.system(size: 15, weight: .semibold))
|
||||
.foregroundStyle(Theme.accent)
|
||||
.accessibilityIdentifier("openSettingsButton")
|
||||
}
|
||||
}
|
||||
@@ -88,12 +81,11 @@ struct OnboardingView: View {
|
||||
private func bullet(_ systemImage: String, _ text: String) -> some View {
|
||||
HStack(alignment: .top, spacing: 12) {
|
||||
Image(systemName: systemImage)
|
||||
.font(.system(size: 15, weight: .semibold))
|
||||
.foregroundStyle(Theme.accent)
|
||||
.frame(width: 22)
|
||||
.foregroundStyle(.tint)
|
||||
.frame(width: 24)
|
||||
Text(text)
|
||||
.font(.system(size: 15))
|
||||
.foregroundStyle(Theme.textSecondary)
|
||||
.font(.subheadline)
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -127,13 +119,10 @@ struct OnboardingView: View {
|
||||
) -> some View {
|
||||
Button(action: action) {
|
||||
Text(title)
|
||||
.font(.system(size: 17, weight: .semibold))
|
||||
.foregroundStyle(.black)
|
||||
.frame(maxWidth: .infinity)
|
||||
.frame(height: 54)
|
||||
.background(.white, in: RoundedRectangle(cornerRadius: 27))
|
||||
}
|
||||
.buttonStyle(.plain)
|
||||
.buttonStyle(.borderedProminent)
|
||||
.controlSize(.large)
|
||||
.accessibilityIdentifier(identifier)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user