diff --git a/FuelBoard/FuelBoardApp.swift b/FuelBoard/FuelBoardApp.swift index 4513dd7..bd508f7 100644 --- a/FuelBoard/FuelBoardApp.swift +++ b/FuelBoard/FuelBoardApp.swift @@ -1,7 +1,22 @@ +import ActivityKit import SwiftUI @main struct FuelBoardApp: App { + init() { + #if DEBUG + // QA hook (Debug builds only): `-qaLiveActivity e10|e5|diesel` starts a + // Live Activity with a long station name so the Lock Screen / island + // layout can be rendered in the Simulator for visual QA. + let args = ProcessInfo.processInfo.arguments + if let idx = args.firstIndex(of: "-qaLiveActivity"), + args.indices.contains(idx + 1), + let fuel = FuelType(rawValue: args[idx + 1]) { + startQALiveActivity(fuel: fuel) + } + #endif + } + var body: some Scene { WindowGroup { ContentView() @@ -9,6 +24,34 @@ struct FuelBoardApp: App { } } + #if DEBUG + private func startQALiveActivity(fuel: FuelType) { + let state = FuelBoardLiveActivityAttributes.ContentState( + fuel: fuel, + stationID: "qa-phoenix", + stationName: "Phoenix Filling Stations", + brand: "Phoenix", + pricePence: 1499, + priceDisplayStyle: FuelStore.loadPriceDisplayStyle(), + distanceKM: 8.0, + lat: 51.5, + lng: -0.12, + updatedAt: Date() + ) + let attrs = FuelBoardLiveActivityAttributes() + do { + let activity = try Activity.request( + attributes: attrs, + content: .init(state: state, staleDate: nil), + pushType: nil + ) + print("QA-LIVE-ACTIVITY STARTED id=\(activity.id)") + } catch { + print("QA-LIVE-ACTIVITY FAILED: \(error)") + } + } + #endif + /// Handles deep links that end up in the app. Widget taps arrive here in /// two cases: /// - legacy/cached widget timelines using the `fuelboard://` relay, or diff --git a/FuelBoardWidgets/FuelBoardLiveActivityView.swift b/FuelBoardWidgets/FuelBoardLiveActivityView.swift index 5fb58f6..d667180 100644 --- a/FuelBoardWidgets/FuelBoardLiveActivityView.swift +++ b/FuelBoardWidgets/FuelBoardLiveActivityView.swift @@ -73,12 +73,13 @@ private struct FuelBoardLiveActivityView: View { /// "ideal" width past the iPhone Lock Screen and collapses the full card). private let compactWidthThreshold: CGFloat = 280 - /// Slightly darkened fuel tint for the pump circle so the white pump - /// symbol keeps contrast against the bright Lock Screen / card background - /// (the raw palette colours like yellow #FFD60A are too light to sit under - /// a white glyph). - private var pumpCircleColor: Color { - context.state.fuel.tintColor.mix(with: .black, by: 0.18) + /// The badge circle behind the pump. The user calls this "the white + /// circle" and wants it slightly darkened for contrast against the bright + /// fuel pump. Built as an explicit grey disc (the light-grey "white circle + /// darker") — NOT part of the SF Symbol glyph, so it can never swallow the + /// darkened colour the way `fuelpump.circle.fill` did. + private var badgeCircleColor: Color { + Color(white: 0.86) } var body: some View { @@ -107,11 +108,20 @@ private struct FuelBoardLiveActivityView: View { /// Full three-column design (unchanged): brand glyph · fuel+station · price. private var richBody: some View { HStack(spacing: 12) { - // LEFT — station brand glyph - Image(systemName: "fuelpump.circle.fill") - .font(.system(size: 32)) - .foregroundStyle(pumpCircleColor, .white) - .frame(width: 40, height: 40) + // LEFT — station brand badge: an explicit darkened circle with a + // bright fuel-tinted pump. Built from two shapes so the circle + // colour and pump colour are unambiguous (the myth that + // fuelpump.circle.fill's first style is the circle is what put the + // darkened colour on the pump). + ZStack { + Circle() + .fill(badgeCircleColor) + .frame(width: 40, height: 40) + Image(systemName: "fuelpump.fill") + .font(.system(size: 19, weight: .semibold)) + .foregroundStyle(context.state.fuel.tintColor) + } + .frame(width: 40, height: 40) // MIDDLE — fuel + station VStack(alignment: .leading, spacing: 2) {