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
43 changes: 32 additions & 11 deletions BetterBlue/Models/Account.swift
Original file line number Diff line number Diff line change
Expand Up @@ -702,37 +702,58 @@ extension BBAccount {
}
}

// MARK: - EV Trip Details
// MARK: - EV Trip Summary & Info

extension BBAccount {
/// Fetches EV trip details for a vehicle. Returns nil if the API doesn't support this feature.
/// Fetches EV trip summary for a vehicle. Returns nil if the API doesn't support this feature.
@MainActor
func fetchEVTripDetails(for bbVehicle: BBVehicle, modelContext: ModelContext) async throws -> [EVTripDetail]? {
func fetchEVTripSummary(for bbVehicle: BBVehicle, modelContext: ModelContext) async throws -> [EVTripSummary]? {
guard let api, let authToken else {
try await initialize(modelContext: modelContext)
return try await fetchEVTripDetails(for: bbVehicle, modelContext: modelContext)
return try await fetchEVTripSummary(for: bbVehicle, modelContext: modelContext)
}

let vehicle = bbVehicle.toVehicle()

do {
return try await api.fetchEVTripDetails(for: vehicle, authToken: authToken)
return try await api.fetchEVTripSummary(for: vehicle, authToken: authToken)
} catch let error as APIError where shouldReauthenticate(for: error) {
try await handleAPIError(error, modelContext: modelContext)
guard let api = self.api, let authToken = self.authToken else {
throw APIError.failedRetryLogin()
}
return try await api.fetchEVTripDetails(for: vehicle, authToken: authToken)
return try await api.fetchEVTripSummary(for: vehicle, authToken: authToken)
}
}

/// Returns true if the account's API supports EV trip details.
/// Delegates to the client (like `supportsMFA`) so new brand support in
/// BetterBlueKit lights up without app changes. False until the client
/// Fetches EV trip info for a specific date. Returns nil if the API doesn't support this feature.
@MainActor
func fetchEVTripInfo(for bbVehicle: BBVehicle, date: Date, modelContext: ModelContext) async throws -> [EVTripInfo]? {
guard let api, let authToken else {
try await initialize(modelContext: modelContext)
return try await fetchEVTripInfo(for: bbVehicle, date: date, modelContext: modelContext)
}

let vehicle = bbVehicle.toVehicle()

do {
return try await api.fetchEVTripInfo(for: vehicle, authToken: authToken, date: date)
} catch let error as APIError where shouldReauthenticate(for: error) {
try await handleAPIError(error, modelContext: modelContext)
guard let api = self.api, let authToken = self.authToken else {
throw APIError.failedRetryLogin()
}
return try await api.fetchEVTripInfo(for: vehicle, authToken: authToken, date: date)
}
}

/// Returns the EV trip types supported by the account's API.
/// Delegates to the client so new brand support in BetterBlueKit
/// lights up without app changes. Empty until the client
/// is initialized; the UI re-evaluates once startup init completes.
@MainActor
var supportsEVTripDetails: Bool {
api?.supportsEVTripDetails() ?? false
var supportedEVTripTypes: [EVTripType] {
api?.supportedEVTripTypes() ?? []
}
}

Expand Down
15 changes: 10 additions & 5 deletions BetterBlue/Utility/CachedAPIClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -205,16 +205,21 @@ class CachedAPIClient: APIClientProtocol {
try await task.value
}

func fetchEVTripDetails(for vehicle: Vehicle, authToken: AuthToken) async throws -> [EVTripDetail]? {
func fetchEVTripSummary(for vehicle: Vehicle, authToken: AuthToken) async throws -> [EVTripSummary]? {
// Trip details are not cached - forward directly to underlying client
BBLogger.debug(.api, "CachedAPIClient:Forwarding fetchEVTripDetails request for VIN: \(vehicle.vin)")
return try await underlyingClient.fetchEVTripDetails(for: vehicle, authToken: authToken)
BBLogger.debug(.api, "CachedAPIClient:Forwarding fetchEVTripSummary request for VIN: \(vehicle.vin)")
return try await underlyingClient.fetchEVTripSummary(for: vehicle, authToken: authToken)
}

func supportsEVTripDetails() -> Bool {
func fetchEVTripInfo(for vehicle: Vehicle, authToken: AuthToken, date: Date) async throws -> [EVTripInfo]? {
BBLogger.debug(.api, "CachedAPIClient:Forwarding fetchEVTripInfo request for VIN: \(vehicle.vin)")
return try await underlyingClient.fetchEVTripInfo(for: vehicle, authToken: authToken, date: date)
}

func supportedEVTripTypes() -> [EVTripType] {
// Without this forward the protocol's default (false) would answer
// for the wrapper and hide trip details for every account.
underlyingClient.supportsEVTripDetails()
underlyingClient.supportedEVTripTypes()
}

/// Invalidates the cached status for a specific vehicle
Expand Down
2 changes: 1 addition & 1 deletion BetterBlue/Views/Components/PersistentVehicleSheet.swift
Original file line number Diff line number Diff line change
Expand Up @@ -884,7 +884,7 @@ struct PersistentVehicleSheet: View {
}

if bbVehicle.fuelType.hasElectricCapability,
bbVehicle.account?.supportsEVTripDetails == true {
bbVehicle.account?.supportedEVTripTypes.contains(.summary) == true {
Button {
sheetPresentation.show(.tripDetails(vehicle: bbVehicle))
} label: {
Expand Down
Loading