spb/focale Public
Swift 100%
1//2// OnboardingView.swift3// Focale4//5// Author: Simon-Pierre Boucher6// Contact: contact@spboucher.ai7//8// The #1 product risk (CLAUDE.md §12): full photo access refused.9// We explain *why* before showing the system dialog — a naked dialog10// is a refused permission is a lost install. The fact that nothing11// leaves the device is the argument, said at exactly this spot.12//1314import SwiftUI1516struct OnboardingView: View {17 @Environment(AppModel.self) private var app18 @State private var isRequesting = false1920 var body: some View {21 VStack(spacing: 28) {22 Spacer()2324 Image(systemName: "camera.aperture")25 .font(.system(size: 64))26 .foregroundStyle(DesignTokens.accent)2728 Text("Tout ce que tu photographies\ndevient retrouvable")29 .font(.title2.bold())30 .multilineTextAlignment(.center)3132 VStack(alignment: .leading, spacing: 18) {33 explanationRow(34 symbol: "iphone",35 title: "Rien ne quitte ton iPhone. Jamais.",36 detail: "L'analyse se fait entièrement sur l'appareil. Aucun serveur, aucun compte, aucune option cloud."37 )38 explanationRow(39 symbol: "photo.stack",40 title: "Tes photos restent exactement où elles sont",41 detail: "Focale lit ta photothèque pour l'indexer. Aucune photo n'est copiée, déplacée ou modifiée."42 )43 explanationRow(44 symbol: "magnifyingglass",45 title: "L'accès complet, c'est ce qui rend la recherche possible",46 detail: "Reçus, tableaux blancs, numéros de série : pour les retrouver, Focale doit pouvoir voir toute ta photothèque."47 )48 }49 .padding(.horizontal, 8)5051 Spacer()5253 Button {54 isRequesting = true55 Task {56 await app.requestPhotoAccess()57 isRequesting = false58 }59 } label: {60 Text("Autoriser l'accès à mes photos")61 .font(.headline)62 .frame(maxWidth: .infinity)63 .padding(.vertical, 6)64 }65 .buttonStyle(.borderedProminent)66 .disabled(isRequesting)67 }68 .padding(24)69 .background(DesignTokens.surface)70 }7172 private func explanationRow(symbol: String, title: String, detail: String) -> some View {73 HStack(alignment: .top, spacing: 14) {74 Image(systemName: symbol)75 .font(.title3)76 .foregroundStyle(DesignTokens.accent)77 .frame(width: 30)78 VStack(alignment: .leading, spacing: 3) {79 Text(title).font(.subheadline.weight(.semibold))80 Text(detail)81 .font(.footnote)82 .foregroundStyle(DesignTokens.textSecondary)83 }84 }85 }86}8788/// Dedicated screen for `.limited` — an explanation, not an error message89/// (CLAUDE.md §2).90struct LimitedAccessView: View {91 var body: some View {92 VStack(spacing: 20) {93 Image(systemName: "photo.badge.exclamationmark")94 .font(.system(size: 52))95 .foregroundStyle(DesignTokens.accent)9697 Text("Focale a besoin de l'accès complet")98 .font(.title3.bold())99100 Text("""101 Avec un accès limité, Focale ne peut ni indexer ta photothèque \102 ni retrouver tes photos. Rien ne quitte ton iPhone — l'analyse \103 se fait entièrement sur l'appareil.104105 Dans Réglages → Confidentialité → Photos, choisis « Accès complet » \106 pour Focale.107 """)108 .font(.callout)109 .foregroundStyle(DesignTokens.textSecondary)110 .multilineTextAlignment(.center)111112 Button("Ouvrir les Réglages") {113 if let url = URL(string: UIApplication.openSettingsURLString) {114 UIApplication.shared.open(url)115 }116 }117 .buttonStyle(.borderedProminent)118 }119 .padding(28)120 .background(DesignTokens.surface)121 }122}123