Compare commits
3
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f04a0fe0f5 | ||
|
|
f35f605b58 | ||
|
|
7da277547b |
@@ -98,7 +98,7 @@ struct OnboardingView: View {
|
||||
private var welcomePage: some View {
|
||||
VStack(spacing: 0) {
|
||||
Spacer()
|
||||
AnimatedOnboardingIcon(tint: .accentColor) {
|
||||
AnimatedOnboardingIcon(tint: .accentColor, showsHalo: false) {
|
||||
// The app icon itself, with the standard iOS rounded-corner
|
||||
// treatment (~22% of the size), so onboarding matches the
|
||||
// Home Screen.
|
||||
@@ -393,6 +393,9 @@ struct AnimatedOnboardingIcon<Content: View>: View {
|
||||
|
||||
let tint: Color
|
||||
var motion: Motion = .gentle
|
||||
/// The soft circle behind the glyph. The welcome page turns it off: it shows
|
||||
/// the app icon itself and reads better with no container around it.
|
||||
var showsHalo: Bool = true
|
||||
@ViewBuilder var content: Content
|
||||
|
||||
@State private var settled = false
|
||||
@@ -402,16 +405,18 @@ struct AnimatedOnboardingIcon<Content: View>: View {
|
||||
|
||||
var body: some View {
|
||||
ZStack {
|
||||
Circle()
|
||||
.fill(tint.opacity(0.10))
|
||||
.frame(width: 108, height: 108)
|
||||
.scaleEffect(settled ? 1.0 : 0.94)
|
||||
if showsHalo {
|
||||
Circle()
|
||||
.fill(tint.opacity(0.10))
|
||||
.frame(width: 108, height: 108)
|
||||
.scaleEffect(settled ? 1.0 : 0.94)
|
||||
|
||||
Circle()
|
||||
.stroke(tint.opacity(0.18), lineWidth: 1)
|
||||
.frame(width: 118, height: 118)
|
||||
.scaleEffect(settled ? 1.03 : 0.97)
|
||||
.opacity(settled ? 1.0 : 0.55)
|
||||
Circle()
|
||||
.stroke(tint.opacity(0.18), lineWidth: 1)
|
||||
.frame(width: 118, height: 118)
|
||||
.scaleEffect(settled ? 1.03 : 0.97)
|
||||
.opacity(settled ? 1.0 : 0.55)
|
||||
}
|
||||
|
||||
content
|
||||
.scaleEffect(iconScale)
|
||||
|
||||
@@ -29,7 +29,7 @@ struct StationDetailView: View {
|
||||
var onToggleFavourite: (() -> Void)? = nil
|
||||
@Environment(\.dismiss) private var dismiss
|
||||
@State private var showingReportIssueConfirmation = false
|
||||
/// Transient "Copied" confirmation on the Address row. Cleared by a task so
|
||||
/// Transient "Postcode copied" confirmation on the Address row. Cleared by a task so
|
||||
/// the caption fades out on its own.
|
||||
@State private var showingCopied = false
|
||||
@State private var copyResetTask: Task<Void, Never>?
|
||||
@@ -137,7 +137,7 @@ struct StationDetailView: View {
|
||||
// The whole row is a copy control: it puts JUST the postcode
|
||||
// on the clipboard so it can be pasted into another
|
||||
// navigation app. The copy glyph sits ON the address line,
|
||||
// and the "Copied" confirmation is a capsule that floats OVER
|
||||
// and the "Postcode copied" confirmation is a capsule that floats OVER
|
||||
// THIS ROW — it confirms the address, so it must not hover
|
||||
// over the map. It is an overlay, so nothing moves while it is
|
||||
// up, and `allowsHitTesting(false)` keeps the row tappable
|
||||
@@ -172,7 +172,7 @@ struct StationDetailView: View {
|
||||
if showingCopied {
|
||||
HStack(spacing: 4) {
|
||||
Image(systemName: "checkmark")
|
||||
Text("Copied")
|
||||
Text("Postcode copied")
|
||||
}
|
||||
.font(.footnote.weight(.semibold))
|
||||
.foregroundStyle(.green)
|
||||
@@ -296,12 +296,12 @@ struct StationDetailView: View {
|
||||
)
|
||||
}
|
||||
}
|
||||
// The "Copied" fade-out task must not outlive the sheet.
|
||||
// The "Postcode copied" fade-out task must not outlive the sheet.
|
||||
.onDisappear { copyResetTask?.cancel() }
|
||||
#if DEBUG
|
||||
// QA hook: `-qaCopyPostcode` runs the REAL copy on open, so the
|
||||
// clipboard value (read back with `xcrun simctl pbpaste`) and the
|
||||
// "Copied" confirmation can be verified without a tap — simctl
|
||||
// "Postcode copied" confirmation can be verified without a tap — simctl
|
||||
// cannot drive taps into the Simulator.
|
||||
.task {
|
||||
guard ProcessInfo.processInfo.arguments.contains("-qaCopyPostcode") else { return }
|
||||
|
||||
@@ -317,11 +317,14 @@ struct StationsView: View {
|
||||
}
|
||||
|
||||
// The explanation is its own sheet now, opened from this
|
||||
// row. Deliberately NOT gated by Best Value: the Key sheet
|
||||
// is the legend and is reachable whatever the feature is
|
||||
// doing.
|
||||
Section {
|
||||
HowBestValueRow { showBestValueInfo = true }
|
||||
// row. Gated exactly like the More info row and the Best
|
||||
// Value card itself: `isConfigured` is the same condition
|
||||
// `savingsForStation` uses, so the row appears and
|
||||
// disappears with the feature.
|
||||
if fuelSavingsSettings.isConfigured {
|
||||
Section {
|
||||
HowBestValueRow { showBestValueInfo = true }
|
||||
}
|
||||
}
|
||||
}
|
||||
.navigationTitle("Key")
|
||||
|
||||
@@ -71,8 +71,8 @@
|
||||
"Name" = "Name";
|
||||
"Address" = "Address";
|
||||
"Distance" = "Distance";
|
||||
"Copied" = "Copied";
|
||||
"Copies the postcode" = "Copies the postcode";
|
||||
"Postcode copied" = "Postcode copied";
|
||||
"Address: %@" = "Address: %@";
|
||||
"Directions to %@" = "Directions to %@";
|
||||
"Opens Apple Maps with driving directions" = "Opens Apple Maps with driving directions";
|
||||
|
||||
Reference in New Issue
Block a user