// ----------------------------------------------------------------------------- // Lou-Ka — Agrégateur de logements à louer (province de Québec) // Auteur : Simon-Pierre Boucher — contact@spboucher.ai // App.swift : point d'entrée, modèle global (facettes / sources / stats), onglets // ----------------------------------------------------------------------------- import SwiftUI import Observation @MainActor @Observable final class AppModel { var facets: Facets? var sources: [SourceInfo] = [] var stats: Stats? private var loaded = false func loadGlobals(force: Bool = false) async { if loaded && !force { return } loaded = true async let facetsTask = try? API.facets() async let sourcesTask = try? API.sources() async let statsTask = try? API.stats() let (f, s, st) = await (facetsTask, sourcesTask, statsTask) if let f { facets = f } if let s { sources = s.sources } if let st { stats = st } } /// id de source → nom lisible (ex. « logisco » → « Logisco ») func sourceName(_ id: String) -> String { sources.first(where: { $0.id == id })?.name ?? id } } @main struct LouKaApp: App { @State private var model = AppModel() @State private var reco = RecoEngine() init() { // Barre de navigation « éditoriale » : papier opaque, trait encre, // titres en Space Grotesk — cohérent sur toutes les pages. let nav = UINavigationBarAppearance() nav.configureWithOpaqueBackground() nav.backgroundColor = UIColor(LK.paper) nav.shadowColor = UIColor(LK.ink) var titleAttrs: [NSAttributedString.Key: Any] = [.foregroundColor: UIColor(LK.ink)] var largeAttrs = titleAttrs if let f = UIFont(name: "SpaceGrotesk-Bold", size: 17) { titleAttrs[.font] = f } if let f = UIFont(name: "SpaceGrotesk-Bold", size: 30) { largeAttrs[.font] = f } nav.titleTextAttributes = titleAttrs nav.largeTitleTextAttributes = largeAttrs UINavigationBar.appearance().standardAppearance = nav UINavigationBar.appearance().scrollEdgeAppearance = nav } var body: some Scene { WindowGroup { RootView() .environment(model) .environment(reco) .tint(LK.ink) // Le design Lou-Ka est un thème clair (papier/encre) : on le force // pour que les couleurs système (titres, champs, pickers) ne passent // jamais au blanc en mode sombre → texte invisible. .preferredColorScheme(.light) } } } struct RootView: View { @Environment(AppModel.self) private var model var body: some View { TabView { HomeView() .tabItem { Label("Annonces", systemImage: "list.bullet.rectangle.portrait") } DiscoverView() .tabItem { Label("Découvrir", systemImage: "rectangle.stack.badge.play") } MapTabView() .tabItem { Label("Carte", systemImage: "map") } StatsView() .tabItem { Label("Stats", systemImage: "chart.bar.xaxis") } SourcesView() .tabItem { Label("Sources", systemImage: "building.2") } } .task { await model.loadGlobals() } } }