Favourites: drag-and-drop reorder per fuel; stored order drives single-widget favourite
This commit is contained in:
@@ -1,10 +1,12 @@
|
||||
import SwiftUI
|
||||
|
||||
/// Favourites tab — starred stations, split by fuel type. Each fuel type
|
||||
/// with at least one favourite appears as its own tab; that tab's favourites
|
||||
/// are ranked cheapest-first for that fuel, with the cheapest called out.
|
||||
/// Favourites are fuel-scoped entries — starring a station only pins it for
|
||||
/// the fuel you were viewing, so an Unleaded favourite never shows as Diesel.
|
||||
/// with at least one favourite appears as its own tab. The list shows the
|
||||
/// USER'S manual order (drag to reorder — Edit button in the toolbar): the
|
||||
/// FIRST station in each fuel's list is the one used as "the favourite" in
|
||||
/// single widgets. Favourites are fuel-scoped entries — starring a station
|
||||
/// only pins it for the fuel you were viewing, so an Unleaded favourite
|
||||
/// never shows as Diesel.
|
||||
struct FavouritesView: View {
|
||||
let favourites: [FavouriteEntry]
|
||||
/// The app's currently selected fuel — used only as the initial tab.
|
||||
@@ -12,6 +14,8 @@ struct FavouritesView: View {
|
||||
let location: Coordinate?
|
||||
let distanceUnit: DistanceUnit
|
||||
var onToggleFavourite: (FuelStation, FuelType) -> Void = { _, _ in }
|
||||
/// Persists a reordered favourites array (after drag-and-drop).
|
||||
var onReorder: ([FavouriteEntry]) -> Void = { _ in }
|
||||
|
||||
/// Fuel types that currently have at least one favourite — these are the
|
||||
/// only tabs shown (a fuel with no favourites gets no tab).
|
||||
@@ -29,40 +33,50 @@ struct FavouritesView: View {
|
||||
availableFuels.contains(fuel) ? fuel : (availableFuels.first ?? .e10)
|
||||
}
|
||||
|
||||
/// Stations favourited for the active fuel, cheapest first (distance tiebreak).
|
||||
private var ranked: [FuelStation] {
|
||||
let available = favourites.filter { $0.fuel == activeFuel }.map(\.station)
|
||||
return available.sorted { lhs, rhs in
|
||||
let lPrice = lhs.prices[activeFuel]!
|
||||
let rPrice = rhs.prices[activeFuel]!
|
||||
if lPrice != rPrice { return lPrice < rPrice }
|
||||
guard let location else { return false }
|
||||
return lhs.distanceKM(to: location.lat, lng2: location.lng) <
|
||||
rhs.distanceKM(to: location.lat, lng2: location.lng)
|
||||
}
|
||||
/// Stations favourited for the active fuel, in the USER'S stored order —
|
||||
/// NOT price-sorted. The first row is the favourite used by single
|
||||
/// widgets (see footer note).
|
||||
private var ordered: [FuelStation] {
|
||||
favourites.filter { $0.fuel == activeFuel }.map(\.station)
|
||||
}
|
||||
|
||||
/// Cheapest favourited station for the active fuel — the RAG baseline and
|
||||
/// the "cheapest favourite" callout, independent of the manual order.
|
||||
private var cheapest: FuelStation? {
|
||||
ordered.min { ($0.prices[activeFuel] ?? .infinity) < ($1.prices[activeFuel] ?? .infinity) }
|
||||
}
|
||||
private var cheapestPrice: Double? { cheapest?.prices[activeFuel] }
|
||||
|
||||
/// IDs favourited for the active fuel — the star state on each row.
|
||||
private var activeFuelFavouriteIDs: Set<String> {
|
||||
Set(favourites.filter { $0.fuel == activeFuel }.map(\.station.id))
|
||||
}
|
||||
|
||||
private var cheapest: FuelStation? { ranked.first }
|
||||
private var cheapestPrice: Double? { cheapest?.prices[activeFuel] }
|
||||
|
||||
init(favourites: [FavouriteEntry],
|
||||
selectedFuel: FuelType,
|
||||
location: Coordinate?,
|
||||
distanceUnit: DistanceUnit,
|
||||
onToggleFavourite: @escaping (FuelStation, FuelType) -> Void = { _, _ in }) {
|
||||
onToggleFavourite: @escaping (FuelStation, FuelType) -> Void = { _, _ in },
|
||||
onReorder: @escaping ([FavouriteEntry]) -> Void = { _ in }) {
|
||||
self.favourites = favourites
|
||||
self.selectedFuel = selectedFuel
|
||||
self.location = location
|
||||
self.distanceUnit = distanceUnit
|
||||
self.onToggleFavourite = onToggleFavourite
|
||||
self.onReorder = onReorder
|
||||
_fuel = State(initialValue: selectedFuel)
|
||||
}
|
||||
|
||||
/// Drag-and-drop reorder (List edit mode): the active fuel's entries are
|
||||
/// reordered within the global array; the fuel's block keeps its original
|
||||
/// position and other fuels keep their relative order (FuelStore helper).
|
||||
private func moveFavourite(fromOffsets source: IndexSet, toOffset destination: Int) {
|
||||
onReorder(FuelStore.reorderedFavourites(
|
||||
favourites, fuel: activeFuel,
|
||||
fromOffsets: source, toOffset: destination
|
||||
))
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
NavigationStack {
|
||||
List {
|
||||
@@ -74,7 +88,7 @@ struct FavouritesView: View {
|
||||
.foregroundStyle(.secondary)
|
||||
Text("No favourites yet")
|
||||
.font(.headline)
|
||||
Text("Tap the star on any station in the Stations tab to pin it here, ranked by which is cheapest for that fuel.")
|
||||
Text("Tap the star on any station in the Stations tab to pin it here. The first station in each list becomes the favourite used in single widgets — drag to put your pick first.")
|
||||
.font(.caption)
|
||||
.foregroundStyle(.secondary)
|
||||
.multilineTextAlignment(.center)
|
||||
@@ -90,8 +104,8 @@ struct FavouritesView: View {
|
||||
)
|
||||
}
|
||||
|
||||
Section("\(activeFuel.displayName) favourites — cheapest first") {
|
||||
ForEach(Array(ranked.enumerated()), id: \.element.id) { index, station in
|
||||
Section {
|
||||
ForEach(Array(ordered.enumerated()), id: \.element.id) { index, station in
|
||||
StationRow(
|
||||
station: station,
|
||||
fuel: activeFuel,
|
||||
@@ -103,6 +117,11 @@ struct FavouritesView: View {
|
||||
onToggleFavourite: { onToggleFavourite(station, activeFuel) }
|
||||
)
|
||||
}
|
||||
.onMove(perform: moveFavourite)
|
||||
} header: {
|
||||
Text("\(activeFuel.displayName) favourites")
|
||||
} footer: {
|
||||
Text("The first station is the favourite used in single widgets. Tap Edit, then drag to reorder.")
|
||||
}
|
||||
|
||||
if let cheapest = cheapest {
|
||||
@@ -115,6 +134,11 @@ struct FavouritesView: View {
|
||||
}
|
||||
}
|
||||
.navigationTitle("Favourites")
|
||||
.toolbar {
|
||||
if !favourites.isEmpty {
|
||||
EditButton()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user