SPB Git

spb/ka-ios

Public
11.3 KB · 250 lines swift
1// Auteur : Simon-Pierre Boucher — contact@spboucher.ai2// ProfileView.swift — profil : KA ID (compte unique de l'écosystème), univers3// favoris, apparence, contacts complets du Groupe KA, pages légales, à propos.4import SwiftUI56struct ProfileView: View {7    @AppStorage("ka.favUniverses") private var favUniversesRaw = ""8    @AppStorage("ka.appearance") private var appearance = "clair"9    @Environment(\.colorScheme) private var scheme10    @StateObject private var kaid = KAIDManager.shared11    @EnvironmentObject private var favorites: FavoritesStore12    @EnvironmentObject private var recents: RecentsStore13    @State private var showHubSite = false1415    private var favIDs: Set<String> {16        Set(favUniversesRaw.split(separator: ",").map(String.init))17    }1819    var body: some View {20        NavigationStack {21            List {22                Section {23                    if let p = kaid.profile {24                        KAIDCard(profile: p)25                            .listRowInsets(EdgeInsets())26                            .listRowBackground(Color.clear)27                        Link(destination: Ecosystem.hubURL.appendingPathComponent("/compte")) {28                            Label("Gérer mon profil sur groupe-ka.com", systemImage: "person.text.rectangle")29                        }30                        Button(role: .destructive) { kaid.logout() } label: {31                            Label("Se déconnecter", systemImage: "rectangle.portrait.and.arrow.right")32                        }33                    } else {34                        HStack(spacing: 14) {35                            ZStack {36                                RoundedRectangle(cornerRadius: 14, style: .continuous)37                                    .fill(KATheme.inkLight)38                                    .frame(width: 56, height: 56)39                                Text("KA").font(.system(size: 22, weight: .bold, design: .rounded))40                                    .foregroundStyle(KATheme.lime)41                                    .rotationEffect(.degrees(-4))42                            }43                            VStack(alignment: .leading, spacing: 3) {44                                Text("Votre KA ID").font(.headline)45                                Text("Un seul compte pour les 13 plateformes de l'écosystème.")46                                    .font(.caption).foregroundStyle(.secondary)47                            }48                        }49                        Button {50                            kaid.login()51                        } label: {52                            HStack {53                                Label("Se connecter avec KA ID", systemImage: "person.crop.circle.badge.checkmark")54                                Spacer()55                                if kaid.busy { ProgressView() }56                            }57                        }58                        .disabled(kaid.busy)59                        Link(destination: Ecosystem.signupURL) {60                            Label("Créer un compte sur groupe-ka.com", systemImage: "arrow.up.right.square")61                        }62                        if let e = kaid.lastError {63                            Text(e).font(.caption).foregroundStyle(.red)64                        }65                    }66                }6768                Section("Mes contenus") {69                    NavigationLink { FavoritesView() } label: {70                        Label("Favoris & collections", systemImage: "heart.fill")71                            .badge(favorites.allItems.count)72                    }73                    NavigationLink { HistoryView() } label: {74                        Label("Historique consulté", systemImage: "clock.arrow.circlepath")75                            .badge(recents.viewed.count)76                    }77                    NavigationLink { SavedSearchesView() } label: {78                        Label("Recherches sauvegardées & alertes", systemImage: "bell.badge")79                            .badge(recents.alertCount > 0 ? "\(recents.alertCount) nouv." : "")80                    }81                }8283                Section {84                    ForEach(Ecosystem.all) { u in85                        Button {86                            var ids = favIDs87                            if ids.contains(u.id) { ids.remove(u.id) } else { ids.insert(u.id) }88                            favUniversesRaw = ids.sorted().joined(separator: ",")89                            Haptics.tap()90                        } label: {91                            HStack {92                                KALogo(universe: u, size: 26)93                                Text(u.name).foregroundStyle(.primary)94                                Spacer()95                                if favIDs.contains(u.id) {96                                    Image(systemName: "star.fill").foregroundStyle(.yellow)97                                }98                            }99                        }100                    }101                } header: {102                    Text("Mes univers favoris")103                } footer: {104                    Text("Vos univers favoris remontent en tête de l'accueil.")105                }106107                Section("Apparence") {108                    Picker("Thème", selection: $appearance) {109                        Text("Automatique").tag("auto")110                        Text("Clair").tag("clair")111                        Text("Sombre").tag("sombre")112                    }113                }114115                Section("Joindre le Groupe KA") {116                    ForEach(Ecosystem.contacts, id: \.email) { c in117                        Link(destination: URL(string: "mailto:\(c.email)")!) {118                            VStack(alignment: .leading, spacing: 2) {119                                Text(c.email).font(.subheadline.weight(.semibold))120                                Text(c.role).font(.caption).foregroundStyle(.secondary)121                            }122                        }123                    }124                    Button { showHubSite = true } label: {125                        HStack(spacing: 10) {126                            KALogo(assetID: "groupe-ka", size: 22)127                            Text("groupe-ka.com — le portail")128                        }129                    }130                    Link(destination: Ecosystem.statusURL) {131                        Label("État des services en direct", systemImage: "waveform.path.ecg")132                    }133                }134135                Section("Légal") {136                    ForEach(Ecosystem.legal, id: \.path) { l in137                        Link(destination: Ecosystem.hubURL.appendingPathComponent(l.path)) {138                            Text(l.label)139                        }140                    }141                    Text(Ecosystem.disclaimer)142                        .font(.caption2).foregroundStyle(.secondary)143                }144145                Section("À propos") {146                    LabeledContent("Version", value: Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String ?? "1.0.0")147                    LabeledContent("Écosystème", value: "\(Ecosystem.all.count) univers")148                    Text("KA — tout l'écosystème Groupe-KA dans votre poche. Fait au Québec. 💚")149                        .font(.caption).foregroundStyle(.secondary)150                }151            }152            .navigationTitle("Profil")153            .scrollContentBackground(.hidden)154            .background(KATheme.paper(scheme))155            .fullScreenCover(isPresented: $showHubSite) {156                SiteBrowserCover(title: "Groupe KA", accent: KATheme.green,157                                 logoID: "groupe-ka", url: Ecosystem.hubURL)158            }159        }160    }161}162163164// MARK: - Historique consulté165166struct HistoryView: View {167    @EnvironmentObject private var recents: RecentsStore168    @Environment(\.colorScheme) private var scheme169    var body: some View {170        ScrollView {171            LazyVStack(spacing: 10) {172                if recents.viewed.isEmpty {173                    KAEmptyState(symbol: "clock", title: "Rien de consulté encore",174                                 message: "Les fiches que vous ouvrez apparaîtront ici.")175                }176                ForEach(recents.viewed) { item in177                    NavigationLink(value: item) { KAItemRow(item: item) }178                        .buttonStyle(KAPressStyle())179                }180            }181            .padding(14)182        }183        .background(KATheme.paper(scheme))184        .navigationTitle("Historique")185        .navigationDestination(for: KAItem.self) { ItemDetailView(item: $0) }186        .toolbar {187            if !recents.viewed.isEmpty {188                ToolbarItem(placement: .topBarTrailing) {189                    Button("Effacer") { recents.clearHistory() }190                }191            }192        }193    }194}195196// MARK: - Recherches sauvegardées & alertes197198struct SavedSearchesView: View {199    @EnvironmentObject private var recents: RecentsStore200    @Environment(\.colorScheme) private var scheme201    var body: some View {202        List {203            if recents.savedSearches.isEmpty {204                KAEmptyState(symbol: "bell.slash", title: "Aucune recherche sauvegardée",205                             message: "Dans Recherche, touchez 🔔 pour suivre une recherche : KA recomptera les résultats et affichera les nouveautés.")206                    .listRowBackground(Color.clear)207            }208            ForEach($recents.savedSearches) { $s in209                VStack(alignment: .leading, spacing: 6) {210                    HStack {211                        Text(s.name).font(.headline)212                        if s.newCount > 0 {213                            Text("+\(s.newCount) nouveautés")214                                .font(.system(size: 10, design: .monospaced).weight(.bold))215                                .padding(.horizontal, 7).padding(.vertical, 3)216                                .background(.red, in: Capsule()).foregroundStyle(.white)217                        }218                        Spacer()219                        Toggle("", isOn: $s.alertsOn).labelsHidden()220                            .accessibilityLabel("Alerte pour \(s.name)")221                    }222                    HStack(spacing: 8) {223                        if let u = Ecosystem.universe(s.universeID) { KAChip(text: u.wordmark, accent: u.accent) }224                        Text("« \(s.query) »").font(.caption).foregroundStyle(.secondary)225                        Spacer()226                        if let total = s.lastTotal {227                            Text("\(total + s.newCount) résultats")228                                .font(.system(.caption2, design: .monospaced))229                                .foregroundStyle(.tertiary)230                        }231                    }232                    if s.newCount > 0 {233                        Button("Marquer comme vues") { recents.markSeen(s.id) }234                            .font(.caption.weight(.semibold))235                    }236                }237                .swipeActions {238                    Button(role: .destructive) { recents.remove(s.id) } label: {239                        Label("Supprimer", systemImage: "trash")240                    }241                }242            }243        }244        .navigationTitle("Recherches & alertes")245        .refreshable { await recents.refreshAlerts() }246        .scrollContentBackground(.hidden)247        .background(KATheme.paper(scheme))248    }249}250
Files · 49720973fdf7a4e5315f9bb1e1fdf61c629116cd
Menu

Theme