← Back to index

WatchSitesView

Spots Watch App
CodeWhat It DoesHow It Does It
▶ IMPORTS
import SwiftUIFramework importsImports SwiftUI.
struct WatchSitesView: View { @EnvironmentObject var store: WatchEntryStore // MARK: - Data /// Sites grouped by priority (5 → 1 → 0), then alphabetical within each group. private var byPriority: [(priority: Int, entries: [WatchEntry])] { [5, 4, 3, 2, 1, 0].compactMap { p in let group = store.entries .filter { $0.entryMode == .place && !$0.beenThere && $0.rating == p } .sorted { $0.playName.lowercased() < $1.playName.lowercased() } return group.isEmpty ? nil : (priority: p, entries: group) } } private var beenSites: [WatchEntry] { store.entries .filter { $0.entryMode == .place && $0.beenThere } .sorted { ($0.starRating, $0.playName) > ($1.starRating, $1.playName) } } private var hasAny: Bool { !byPriority.isEmpty || !beenSites.isEmpty } // MARK: - Body var body: some View { Group { if !store.isLoaded { ProgressView() } else if !hasAny { VStack(spacing: 8) { Image(systemName: "mappin.and.ellipse") .font(.system(size: 28)) .foregroundColor(.orange) Text("No sites yet") .font(.caption) .foregroundColor(.secondary) } } else { List { // ── To Visit (grouped by priority) ────────────────── if !byPriority.isEmpty { Section("To Visit") { ForEach(Array(byPriority.enumerated()), id: \.element.priority) { idx, group in // Priority dots header row if group.priority > 0 { HStack(spacing: 3) { ForEach(1...5, id: \.self) { dot in Image(systemName: dot <= group.priority ? "circle.fill" : "circle") .font(.system(size: 8)) .foregroundColor(dot <= group.priority ? .white : .gray.opacity(0.3)) } } .frame(height: 14) .listRowBackground(Color.clear) .listRowInsets(EdgeInsets(top: idx == 0 ? 2 : 4, leading: 8, bottom: 0, trailing: 8)) } ForEach(group.entries) { entry in NavigationLink(destination: WatchEntryDetailView(entry: entry)) { SiteRow(entry: entry, showAsBeen: false) } } } } } // ── Been ──────────────────────────────────────────── if !beenSites.isEmpty { Section("Been") { ForEach(beenSites) { entry in NavigationLink(destination: WatchEntryDetailView(entry: entry)) { SiteRow(entry: entry, showAsBeen: true) } } } } } .listStyle(.plain) .environment(\.defaultMinListRowHeight, 0) } } .navigationTitle("Sites") .navigationBarTitleDisplayMode(.inline) } }`WatchSitesView` structDefines the `WatchSitesView` struct. Conforms to View.
▶ ROW
private struct SiteRow: View { let entry: WatchEntry let showAsBeen: Bool var body: some View { VStack(alignment: .leading, spacing: 2) { Text(entry.playName) .font(.system(size: 14, weight: .semibold)) .foregroundColor(lavender) .lineLimit(2) if !entry.neighborhood.isEmpty { Text(entry.neighborhood) .font(.system(size: 11)) .foregroundColor(.white.opacity(0.75)) .lineLimit(1) } else if !entry.cuisine.isEmpty { Text(entry.cuisine) .font(.system(size: 11)) .foregroundColor(.white.opacity(0.75)) .lineLimit(1) } if showAsBeen && entry.starRating > 0 { HStack(spacing: 2) { ForEach(1...5, id: \.self) { star in Image(systemName: star <= entry.starRating ? "star.fill" : "star") .font(.system(size: 7)) .foregroundColor(star <= entry.starRating ? .yellow : .gray.opacity(0.4)) } } } } .padding(.vertical, 2) } }`SiteRow` structDefines the `SiteRow` struct. Conforms to View.
#Preview { NavigationStack { WatchSitesView() } .environmentObject(WatchEntryStore()) }Code blockSee source code for full implementation.