Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Where/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,7 @@ typed-route list (`SettingsSearch.swift`; every switch is exhaustive), so a
new drill-in is a set of compile errors to fill in; About stays the last
block and the demo-mode exit the first.

The Data and About screens lead with the shared privacy passport statement.
The About screen renders three live sources — the generated attribution
report (`WhereCore.AppAttribution`), `RegionDataSource`, and `BuildInfo` —
never a list hard-coded in the view. A missing report or unstamped build
Expand Down
7 changes: 5 additions & 2 deletions Where/WhereUI/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,11 @@ the feature [`Where/AGENTS.md`](../AGENTS.md) and this module's
Settings. Elsewhere is an entry card on Locations, Resolve a Locations toolbar
button, and the data screens (attachments, logged days, regions) sit in the
Settings "Data" group. Backup and destructive data management share one Data
drill-in. `AboutSettingsView` is the last Settings block — build
identity, the app's generated attribution report (linked libraries and
drill-in. Both Data and About lead with the same full-width passport-style
privacy statement on a passport-navy, tilt-reflective surface: location
history stays on the user's devices and in their private iCloud account,
never on Where-operated servers. `AboutSettingsView` is the last Settings block —
build identity, the app's generated attribution report (linked libraries and
development tools as separate sections), and bundled-data provenance, each
vended by whoever owns it rather than listed in the view; it renders an
explicit "no report" state, since only the app bundle carries one, and ends
Expand Down
10 changes: 10 additions & 0 deletions Where/WhereUI/SnapshotTests/DataSettingsViewSnapshotTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import SnapshotKitTesting
import Testing
@testable import WhereUI

@MainActor
struct DataSettingsViewSnapshotTests {
@Test func data() async {
await assertSnapshots(of: DataSettingsView.self)
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
24 changes: 24 additions & 0 deletions Where/WhereUI/Sources/Resources/Localizable.xcstrings
Original file line number Diff line number Diff line change
Expand Up @@ -6028,6 +6028,30 @@
}
}
},
"settings.privacy.detail" : {
"comment" : "Detail in the privacy passport card shown at the top of Data and About settings.",
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "new",
"value" : "Your location history stays on your devices and in your private iCloud account. Where sends no data to anyone."
}
}
}
},
"settings.privacy.title" : {
"comment" : "Headline in the privacy passport card shown at the top of Data and About settings.",
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "new",
"value" : "Private by design"
}
}
}
},
"settings.regions.empty" : {
"extractionState" : "manual",
"localizations" : {
Expand Down
89 changes: 7 additions & 82 deletions Where/WhereUI/Sources/Settings/AboutOpenSourceFooter.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,93 +5,18 @@ import SwiftUI
struct AboutOpenSourceFooter: View {
static let projectURL = URL(string: "https://github.com/kyleve/Stuff")!

@Environment(\.stylesheet) private var stylesheet

private var style: WhereStylesheet.AboutOpenSourceStyle {
stylesheet.aboutOpenSource
}

private var shape: RoundedRectangle {
RoundedRectangle(cornerRadius: style.cornerRadius)
}

var body: some View {
Link(destination: Self.projectURL) {
HStack(spacing: style.contentSpacing) {
sourceSeal

VStack(alignment: .leading, spacing: stylesheet.spacing.xxSmall) {
Text(String(localized: .settingsAboutSourceTitle))
.font(style.titleFont)
.foregroundStyle(.primary)
Text(String(localized: .settingsAboutSourceAction))
.font(style.actionFont)
.foregroundStyle(.secondary)
}

Spacer(minLength: 0)

Image(systemName: "arrow.up.right")
.foregroundStyle(.tint)
.accessibilityHidden(true)
}
.padding(style.padding)
.frame(maxWidth: .infinity, alignment: .leading)
.background {
let rosette = style.rosette
SecurityPrintRosette(
tint: .accentColor,
wobble: rosette.wobble,
lineWidth: rosette.lineWidth,
primaryRingSpacing: rosette.primaryRingSpacing,
secondaryRingSpacing: rosette.secondaryRingSpacing,
primaryOpacity: rosette.primaryOpacity,
secondaryOpacity: rosette.secondaryOpacity,
)
}
.glassEffect(
.regular.tint(Color.accentColor.opacity(style.glassTintOpacity))
.interactive(),
in: shape,
)
.clipShape(shape)
.contentShape(shape)
.shadow(
color: Color.accentColor.opacity(style.accentGlow.opacity),
radius: style.accentGlow.radius,
y: style.accentGlow.offsetY,
)
.shadow(
color: Color.black.opacity(style.liftShadow.opacity),
radius: style.liftShadow.radius,
y: style.liftShadow.offsetY,
PassportCard(
title: .settingsAboutSourceTitle,
detail: .settingsAboutSourceAction,
sealSystemImage: "chevron.left.forwardslash.chevron.right",
accessorySystemImage: "arrow.up.right",
isInteractive: true,
surface: .securityPrint,
)
}
.buttonStyle(.plain)
.accessibilityElement(children: .combine)
}

private var sourceSeal: some View {
let seal = style.seal
return ZStack {
Circle()
.strokeBorder(.tint, lineWidth: seal.outerLineWidth)
Circle()
.strokeBorder(
.tint.opacity(0.65),
style: StrokeStyle(
lineWidth: seal.innerLineWidth,
dash: [seal.dashLength, seal.dashSpacing],
),
)
.padding(seal.innerInset)
Image(systemName: "chevron.left.forwardslash.chevron.right")
.font(seal.symbolFont)
.foregroundStyle(.tint)
}
.frame(width: seal.size, height: seal.size)
.rotationEffect(.degrees(seal.rotationDegrees))
.accessibilityHidden(true)
}
}

Expand Down
9 changes: 6 additions & 3 deletions Where/WhereUI/Sources/Settings/AboutSettingsView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ import SnapshotKit
import SwiftUI
import WhereCore

/// Settings drill-in for what the app *is* rather than what it does: which build
/// is running, the third-party work it is built with, and where its bundled
/// region boundaries came from.
/// Settings drill-in for what the app *is* rather than what it does: its privacy
/// promise, which build is running, the third-party work it is built with, and
/// where its bundled region boundaries came from.
///
/// Every fact here is vended by whoever owns it — `BuildInfo` and the generated
/// attribution report from `WhereCore`, `RegionDataSource` from `RegionKit` — so
Expand Down Expand Up @@ -44,6 +44,9 @@ struct AboutSettingsView: View {
var body: some View {
SettingsFocusScope(focus: focus) {
Form {
PrivacyPassportCard()
.listRowBackground(Color.clear)
.listRowSeparator(.hidden)
versionSection
dependenciesSection
developmentToolsSection
Expand Down
31 changes: 20 additions & 11 deletions Where/WhereUI/Sources/Settings/DataSettingsView.swift
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import LifecycleKitUI
import SnapshotKit
import SwiftUI
import WhereCore

/// Settings drill-in for backup and data management: export or restore the
/// database, erase the selected year's data, or reset the entire app.
/// Settings drill-in for Where's privacy promise and data management: export or
/// restore the database, erase the selected year's data, or reset the entire app.
struct DataSettingsView: View {
let report: YearReportModel
let backup: BackupModel
Expand All @@ -22,6 +23,9 @@ struct DataSettingsView: View {
var body: some View {
SettingsFocusScope(focus: focus) {
Form {
PrivacyPassportCard()
.listRowBackground(Color.clear)
.listRowSeparator(.hidden)
BackupSettingsSection(backup: backup)
dataSection
resetSection
Expand Down Expand Up @@ -128,16 +132,21 @@ extension DataSettingsView: SettingsSection {
}

#if DEBUG
#Preview {
NavigationStack {
DataSettingsView(
report: PreviewSupport.loadedYearReportModel(),
backup: PreviewSupport.backupModel(),
)
.environment(PreviewSupport.loadedModel())
.environment(PreviewSupport.loadedSession())
extension DataSettingsView: SnapshotProviding {
static var snapshots: [SnapshotCase] {
whereSnapshot(name: "Default", configurations: .fullContentScreenDefaults) {
DataSettingsView(
report: PreviewSupport.loadedYearReportModel(),
backup: PreviewSupport.backupModel(),
)
.environment(PreviewSupport.loadedModel())
.environment(PreviewSupport.loadedSession())
}
}
.whereBroadwayRoot()
}

#Preview {
DataSettingsView.snapshotPreviews
}
#endif

Expand Down
Loading
Loading