Files
fuelboard/FuelBoard/KeyboardSupport.swift
T
FuelBoard Contributor d7a0ecb79a Add Worth the Drive fuel savings with road-routed estimates
Optional Fuel Savings feature (off by default): vehicle MPG in UK imperial gallons, fill amount in pounds or litres, persisted through the existing keychain/app-group store. Ranks the WHOLE station pool by estimated net saving (fuel-price saving at each pump minus the extra return-trip driving cost) using MapKit road distances with a straight-line fallback marked (est.).

The closest station with a valid price for the selected fuel is the comparison baseline and is labelled "Closest station"; other rows read "Save £X.XX after travel" / "£X.XX worse off after travel" / "Break even". Labels live in the shared model so they are unit-tested. The TOP badge still marks the cheapest pump price and reads "CHEAPEST" when the list is ranked by net saving.

Road routing now prioritises the Worth-the-Drive candidates plus the baseline and merges cache entries instead of replacing them, so far-out stations get real routes rather than permanent straight-line estimates. Numeric fields gain a Done button above the keypad (decimalPad has no Return key); the fill-up control is hidden unless the feature is configured; Favourites shares one session fill amount with the station list. 131 tests.
2026-10-11 14:34:53 +01:00

42 lines
1.7 KiB
Swift

import SwiftUI
import UIKit
extension View {
/// Puts a Done button above the keyboard.
///
/// The numeric fields (Settings → Average fuel economy and Typical
/// fill-up, and the Worth the Drive fill-up box in the station list) use
/// `.decimalPad`, which has NO Return key. Without this there is no way to
/// dismiss the keyboard once such a field is tapped, and a
/// `TextField(value:format:)` only commits the typed value when editing
/// ends — so the number never stuck either.
///
/// Apply ONCE per screen, to the list or form containing the fields: the
/// keyboard toolbar belongs to the focused field, so a single attachment
/// covers every field inside it. Attaching it to each field would show
/// several Done buttons at once.
///
/// - Parameter action: clears the screen's focus state. Prefer this where a
/// `@FocusState` exists; the default resigns the current first responder,
/// which works for fields inside subviews that have no focus binding.
func keyboardDoneButton(action: (() -> Void)? = nil) -> some View {
toolbar {
ToolbarItemGroup(placement: .keyboard) {
Spacer()
Button("Done") {
if let action {
action()
} else {
UIApplication.shared.sendAction(
#selector(UIResponder.resignFirstResponder),
to: nil,
from: nil,
for: nil
)
}
}
}
}
}
}