From a776992f0c69deac7aab46eed78dd13a38cdeb12 Mon Sep 17 00:00:00 2001 From: Sven Date: Sun, 19 Apr 2026 15:28:05 +0200 Subject: [PATCH] =?UTF-8?q?Onboarding=20erweitert,=20Geschlecht=20hinzugef?= =?UTF-8?q?=C3=BCgt...?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- nahbar/VisitHistorySection.swift | 4 +- nahbar/nahbar.xcodeproj/project.pbxproj | 4 +- .../UserInterfaceState.xcuserstate | Bin 70662 -> 74697 bytes nahbar/nahbar/AddMomentView.swift | 2 +- nahbar/nahbar/AddPersonView.swift | 50 +++--- nahbar/nahbar/ContactPickerView.swift | 15 +- nahbar/nahbar/IchView.swift | 151 ++++-------------- nahbar/nahbar/Localizable.xcstrings | 78 +++++---- nahbar/nahbar/LogbuchView.swift | 15 +- nahbar/nahbar/Models.swift | 5 +- nahbar/nahbar/NahbarContact.swift | 31 ++++ nahbar/nahbar/OnboardingContainerView.swift | 40 ++++- nahbar/nahbar/OnboardingCoordinator.swift | 1 + nahbar/nahbar/PersonDetailView.swift | 94 ++++------- nahbar/nahbar/PersonalityComponents.swift | 2 +- nahbar/nahbar/PersonalityQuizView.swift | 127 ++++++++++++++- nahbar/nahbar/SettingsView.swift | 29 ++-- nahbar/nahbar/SharedComponents.swift | 16 ++ nahbar/nahbar/TodayView.swift | 14 +- nahbar/nahbar/UserProfileStore.swift | 16 +- .../ShareExtensionView.swift | 2 +- nahbar/nahbarTests/ContactPickerTests.swift | 105 +++++++++++- nahbar/nahbarTests/ModelTests.swift | 15 ++ .../nahbarTests/NahbarPersonalityTests.swift | 30 +++- nahbar/nahbarTests/OnboardingTests.swift | 57 +++++++ nahbar/nahbarTests/StoreTests.swift | 29 ++++ .../nahbarTests/UserProfileStoreTests.swift | 28 +++- 27 files changed, 675 insertions(+), 285 deletions(-) diff --git a/nahbar/VisitHistorySection.swift b/nahbar/VisitHistorySection.swift index c684d5a..f56081c 100644 --- a/nahbar/VisitHistorySection.swift +++ b/nahbar/VisitHistorySection.swift @@ -20,7 +20,7 @@ struct VisitHistorySection: View { VStack(alignment: .leading, spacing: 12) { // Header HStack { - SectionHeader(title: "Besuche", icon: "star.fill") + SectionHeader(title: "Treffen", icon: "star.fill") Spacer() Button { showingVisitRating = true @@ -38,7 +38,7 @@ struct VisitHistorySection: View { .font(.title3) .foregroundStyle(.secondary) VStack(alignment: .leading, spacing: 2) { - Text("Noch keine Besuche bewertet") + Text("Noch keine Treffen bewertet") .font(.subheadline) .foregroundStyle(.secondary) Text("Tippe auf + um loszulegen") diff --git a/nahbar/nahbar.xcodeproj/project.pbxproj b/nahbar/nahbar.xcodeproj/project.pbxproj index a2d8e4d..2d7a413 100644 --- a/nahbar/nahbar.xcodeproj/project.pbxproj +++ b/nahbar/nahbar.xcodeproj/project.pbxproj @@ -576,7 +576,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 26.4; + IPHONEOS_DEPLOYMENT_TARGET = 17.0; LOCALIZATION_PREFERS_STRING_CATALOGS = YES; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; @@ -635,7 +635,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 26.4; + IPHONEOS_DEPLOYMENT_TARGET = 17.0; LOCALIZATION_PREFERS_STRING_CATALOGS = YES; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; diff --git a/nahbar/nahbar.xcodeproj/project.xcworkspace/xcuserdata/sven.xcuserdatad/UserInterfaceState.xcuserstate b/nahbar/nahbar.xcodeproj/project.xcworkspace/xcuserdata/sven.xcuserdatad/UserInterfaceState.xcuserstate index 2fc75b2ea69104d02d685d8feb38f7d9afc4237c..451fda2b078b518deb494dd81e6e0c78d2de4be3 100644 GIT binary patch literal 74697 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 K@B9CZF8d#FJ0`#Y literal 70662 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
h{&;RLrJxFx}@ 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 diff --git a/nahbar/nahbar/AddMomentView.swift b/nahbar/nahbar/AddMomentView.swift index 6703c47..0037f24 100644 --- a/nahbar/nahbar/AddMomentView.swift +++ b/nahbar/nahbar/AddMomentView.swift @@ -53,7 +53,7 @@ struct AddMomentView: View { HStack(spacing: 5) { Image(systemName: type.icon) .font(.system(size: 12)) - Text(type.rawValue) + Text(LocalizedStringKey(type.displayName)) .font(.system(size: 13, weight: selectedType == type ? .medium : .regular)) } .foregroundStyle(selectedType == type ? theme.accent : theme.contentSecondary) diff --git a/nahbar/nahbar/AddPersonView.swift b/nahbar/nahbar/AddPersonView.swift index 56c253d..a5c108b 100644 --- a/nahbar/nahbar/AddPersonView.swift +++ b/nahbar/nahbar/AddPersonView.swift @@ -127,33 +127,39 @@ struct AddPersonView: View { } // Nudge frequency - formSection("Wie oft erinnern?") { - VStack(spacing: 0) { - ForEach(NudgeFrequency.allCases, id: \.self) { freq in - Button { - nudgeFrequency = freq - } label: { - HStack { - Text(freq.rawValue) - .font(.system(size: 15)) - .foregroundStyle(theme.contentPrimary) - Spacer() - if nudgeFrequency == freq { - Image(systemName: "checkmark") - .font(.system(size: 13, weight: .semibold)) - .foregroundStyle(theme.accent) + formSection("Wie oft melden?") { + VStack(alignment: .leading, spacing: 8) { + Text("Nahbar erinnert dich, wenn du diese Person seit der gewählten Zeit nicht mehr kontaktiert hast.") + .font(.system(size: 12)) + .foregroundStyle(theme.contentTertiary) + + VStack(spacing: 0) { + ForEach(NudgeFrequency.allCases, id: \.self) { freq in + Button { + nudgeFrequency = freq + } label: { + HStack { + Text(freq.rawValue) + .font(.system(size: 15)) + .foregroundStyle(theme.contentPrimary) + Spacer() + if nudgeFrequency == freq { + Image(systemName: "checkmark") + .font(.system(size: 13, weight: .semibold)) + .foregroundStyle(theme.accent) + } } + .padding(.horizontal, 16) + .padding(.vertical, 12) + } + if freq != NudgeFrequency.allCases.last { + RowDivider() } - .padding(.horizontal, 16) - .padding(.vertical, 12) - } - if freq != NudgeFrequency.allCases.last { - RowDivider() } } + .background(theme.surfaceCard) + .clipShape(RoundedRectangle(cornerRadius: theme.radiusCard)) } - .background(theme.surfaceCard) - .clipShape(RoundedRectangle(cornerRadius: theme.radiusCard)) } // Delete link — only in edit mode diff --git a/nahbar/nahbar/ContactPickerView.swift b/nahbar/nahbar/ContactPickerView.swift index 12dd77e..cb86679 100644 --- a/nahbar/nahbar/ContactPickerView.swift +++ b/nahbar/nahbar/ContactPickerView.swift @@ -218,13 +218,19 @@ struct ContactImport { let photoData: Data? static func from(_ contact: CNContact) -> ContactImport { - let parts = [contact.givenName, contact.familyName].filter { !$0.isEmpty } + // Mittelname einbeziehen, falls vorhanden + let parts = [contact.givenName, contact.middleName, contact.familyName].filter { !$0.isEmpty } let name = parts.joined(separator: " ") + // Berufsbezeichnung und Firma kombinieren wenn beide vorhanden let occupation: String - if !contact.jobTitle.isEmpty { + let hasJob = !contact.jobTitle.isEmpty + let hasOrg = !contact.organizationName.isEmpty + if hasJob && hasOrg { + occupation = "\(contact.jobTitle) · \(contact.organizationName)" + } else if hasJob { occupation = contact.jobTitle - } else if !contact.organizationName.isEmpty { + } else if hasOrg { occupation = contact.organizationName } else { occupation = "" @@ -232,7 +238,8 @@ struct ContactImport { let location: String if let postal = contact.postalAddresses.first?.value { - location = [postal.city, postal.country].filter { !$0.isEmpty }.joined(separator: ", ") + // Bundesstaat/Region einbeziehen, falls vorhanden + location = [postal.city, postal.state, postal.country].filter { !$0.isEmpty }.joined(separator: ", ") } else { location = "" } diff --git a/nahbar/nahbar/IchView.swift b/nahbar/nahbar/IchView.swift index a67955c..5749a17 100644 --- a/nahbar/nahbar/IchView.swift +++ b/nahbar/nahbar/IchView.swift @@ -1,6 +1,5 @@ import SwiftUI import PhotosUI -import Contacts import SwiftData private let socialStyleOptions = [ @@ -15,15 +14,12 @@ private let socialStyleOptions = [ struct IchView: View { @Environment(\.nahbarTheme) var theme - @Environment(\.modelContext) private var modelContext @EnvironmentObject var profileStore: UserProfileStore @StateObject private var personalityStore = PersonalityStore.shared @State private var profilePhoto: UIImage? = nil @State private var showingEdit = false - @State private var showingImportPicker = false - @State private var importFeedback: String? = nil @State private var showingQuiz = false @State private var showingPersonalityDetail = false @@ -35,7 +31,6 @@ struct IchView: View { if !profileStore.isEmpty { infoSection } if profileStore.isEmpty { emptyState } personalitySection - importKontakteSection } .padding(.horizontal, 20) .padding(.top, 12) @@ -53,30 +48,6 @@ struct IchView: View { .onAppear { profilePhoto = profileStore.loadPhoto() } - .overlay(alignment: .bottom) { - if let feedback = importFeedback { - Text(feedback) - .font(.subheadline.weight(.medium)) - .foregroundStyle(.white) - .padding(.horizontal, 20) - .padding(.vertical, 12) - .background(Color.accentColor) - .clipShape(Capsule()) - .padding(.bottom, 24) - .transition(.move(edge: .bottom).combined(with: .opacity)) - .onAppear { - DispatchQueue.main.asyncAfter(deadline: .now() + 2.5) { - withAnimation { importFeedback = nil } - } - } - } - } - .animation(.spring(response: 0.4, dampingFraction: 0.8), value: importFeedback) - .overlay(alignment: .center) { - MultiContactPickerTrigger(isPresented: $showingImportPicker, onSelect: importContacts) - .frame(width: 0, height: 0) - .allowsHitTesting(false) - } .sheet(isPresented: $showingQuiz) { PersonalityQuizView { _ in showingQuiz = false @@ -197,9 +168,14 @@ struct IchView: View { private var infoSection: some View { VStack(alignment: .leading, spacing: 10) { // Über mich - if !profileStore.location.isEmpty || !profileStore.socialStyle.isEmpty { + let hasUeberMich = !profileStore.gender.isEmpty || !profileStore.location.isEmpty || !profileStore.socialStyle.isEmpty + if hasUeberMich { SectionHeader(title: "Über mich", icon: "person") VStack(spacing: 0) { + if !profileStore.gender.isEmpty { + infoRow(label: "Geschlecht", value: profileStore.gender) + if !profileStore.location.isEmpty || !profileStore.socialStyle.isEmpty { RowDivider() } + } if !profileStore.location.isEmpty { infoRow(label: "Wohnort", value: profileStore.location) if !profileStore.socialStyle.isEmpty { RowDivider() } @@ -297,58 +273,6 @@ struct IchView: View { .padding(.top, 12) } - // MARK: - Kontakte importieren - - private var importKontakteSection: some View { - VStack(alignment: .leading, spacing: 10) { - SectionHeader(title: "Kontakte importieren", icon: "person.2.badge.plus") - Button { showingImportPicker = true } label: { - HStack(spacing: 10) { - Image(systemName: "person.crop.circle.badge.plus") - .font(.system(size: 15)) - Text("Aus Adressbuch hinzufügen") - .font(.system(size: 15)) - } - .foregroundStyle(theme.accent) - .padding(.horizontal, 14) - .padding(.vertical, 11) - .frame(maxWidth: .infinity, alignment: .leading) - .background(theme.surfaceCard) - .clipShape(RoundedRectangle(cornerRadius: theme.radiusCard)) - .overlay( - RoundedRectangle(cornerRadius: theme.radiusCard) - .stroke(theme.accent.opacity(0.25), lineWidth: 1) - ) - } - .accessibilityLabel("Kontakte aus Adressbuch hinzufügen") - } - } - - /// Importiert die gewählten Kontakte als Person-Objekte in die Datenbank. - /// Bereits vorhandene Personen werden nicht dupliziert (Name-Vergleich). - private func importContacts(_ contacts: [CNContact]) { - var imported = 0 - for contact in contacts { - let name = [contact.givenName, contact.familyName] - .filter { !$0.isEmpty } - .joined(separator: " ") - guard !name.isEmpty else { continue } - let person = Person(name: name) - modelContext.insert(person) - imported += 1 - } - guard imported > 0 else { return } - do { - try modelContext.save() - } catch { - // Fehler werden im nächsten App-Launch durch den Container-Fallback abgefangen - } - withAnimation { - importFeedback = imported == 1 - ? "1 Person hinzugefügt" - : "\(imported) Personen hinzugefügt" - } - } } // MARK: - IchEditView @@ -361,6 +285,7 @@ struct IchEditView: View { @State private var name: String @State private var hasBirthday: Bool @State private var birthday: Date + @State private var gender: String @State private var occupation: String @State private var location: String @State private var likes: String @@ -368,13 +293,13 @@ struct IchEditView: View { @State private var socialStyle: String @State private var selectedPhoto: UIImage? @State private var photoPickerItem: PhotosPickerItem? = nil - @State private var showingContactPicker = false init() { let store = UserProfileStore.shared _name = State(initialValue: store.name) _hasBirthday = State(initialValue: store.birthday != nil) _birthday = State(initialValue: store.birthday ?? IchEditView.defaultBirthday) + _gender = State(initialValue: store.gender) _occupation = State(initialValue: store.occupation) _location = State(initialValue: store.location) _likes = State(initialValue: store.likes) @@ -395,9 +320,6 @@ struct IchEditView: View { // Foto photoSection - // Kontakt-Import - importButton - // Name formSection("Name") { TextField("Wie heißt du?", text: $name) @@ -439,6 +361,8 @@ struct IchEditView: View { // Details formSection("Details") { VStack(spacing: 0) { + genderPickerRow + Divider().padding(.leading, 16) inlineField("Beruf", text: $occupation) Divider().padding(.leading, 16) inlineField("Wohnort", text: $location) @@ -505,11 +429,6 @@ struct IchEditView: View { } } } - .overlay(alignment: .center) { - SingleContactPickerTrigger(isPresented: $showingContactPicker, onSelect: applyContact) - .frame(width: 0, height: 0) - .allowsHitTesting(false) - } } // MARK: - Photo Section @@ -562,27 +481,28 @@ struct IchEditView: View { return name.isEmpty ? "?" : String(name.prefix(2)).uppercased() } - // MARK: - Kontakt-Import + // MARK: - Gender Picker - private var importButton: some View { - Button { showingContactPicker = true } label: { - HStack(spacing: 10) { - Image(systemName: "person.crop.circle.badge.plus") - .font(.system(size: 15)) - Text("Aus Kontakten übernehmen") - .font(.system(size: 15)) + private let genderOptions = ["Männlich", "Weiblich", "Divers", "Keine Angabe"] + + private var genderPickerRow: some View { + HStack(spacing: 12) { + Text("Geschlecht") + .font(.system(size: 15)) + .foregroundStyle(theme.contentTertiary) + .frame(width: 80, alignment: .leading) + Picker("Geschlecht", selection: $gender) { + Text("Nicht angegeben").tag("") + ForEach(genderOptions, id: \.self) { option in + Text(option).tag(option) + } } - .foregroundStyle(theme.accent) - .padding(.horizontal, 14) - .padding(.vertical, 11) + .pickerStyle(.menu) + .tint(theme.accent) .frame(maxWidth: .infinity, alignment: .leading) - .background(theme.surfaceCard) - .clipShape(RoundedRectangle(cornerRadius: theme.radiusCard)) - .overlay( - RoundedRectangle(cornerRadius: theme.radiusCard) - .stroke(theme.accent.opacity(0.25), lineWidth: 1) - ) } + .padding(.horizontal, 16) + .padding(.vertical, 12) } // MARK: - Helpers @@ -614,24 +534,11 @@ struct IchEditView: View { .padding(.vertical, 12) } - private func applyContact(_ contact: CNContact) { - let imported = ContactImport.from(contact) - if !imported.name.isEmpty { name = imported.name } - if !imported.occupation.isEmpty { occupation = imported.occupation } - if !imported.location.isEmpty { location = imported.location } - if let bd = imported.birthday { - birthday = bd - hasBirthday = true - } - if let data = imported.photoData, let img = UIImage(data: data) { - selectedPhoto = img - } - } - private func save() { profileStore.update( name: name.trimmingCharacters(in: .whitespaces), birthday: hasBirthday ? birthday : nil, + gender: gender, occupation: occupation.trimmingCharacters(in: .whitespaces), location: location.trimmingCharacters(in: .whitespaces), likes: likes.trimmingCharacters(in: .whitespaces), diff --git a/nahbar/nahbar/Localizable.xcstrings b/nahbar/nahbar/Localizable.xcstrings index e878099..2664a2f 100644 --- a/nahbar/nahbar/Localizable.xcstrings +++ b/nahbar/nahbar/Localizable.xcstrings @@ -294,6 +294,9 @@ } } } + }, + "Abonnement" : { + }, "Abonnement verlängert sich automatisch. In den iPhone-Einstellungen jederzeit kündbar." : { "comment" : "PaywallView – subscription legal notice", @@ -353,6 +356,9 @@ }, "Alle %lld Tage – basierend auf deinem Profil" : { + }, + "Alle Features freigeschaltet" : { + }, "Alle Momente und Notizen zu dieser Person werden unwiderruflich gelöscht." : { "comment" : "AddPersonView – delete confirmation message", @@ -370,6 +376,7 @@ }, "Alle Pro-Features freigeschaltet" : { "comment" : "SettingsView – Pro subscription active subtitle", + "extractionState" : "stale", "localizations" : { "en" : { "stringUnit" : { @@ -592,7 +599,7 @@ } } }, - "Aus Adressbuch hinzufügen" : { + "Auf Max upgraden – KI-Analyse freischalten" : { }, "Aus Kontakten ausfüllen" : { @@ -609,10 +616,6 @@ } } }, - "Aus Kontakten übernehmen" : { - "comment" : "A button that allows the user to import contacts.", - "isCommentAutoGenerated" : true - }, "Ausgeglichen" : { "comment" : "IchView – social style option (ambiverted)", "extractionState" : "stale", @@ -683,6 +686,7 @@ }, "Besuche" : { "comment" : "VisitHistorySection / SettingsView – section header for visits", + "extractionState" : "stale", "localizations" : { "en" : { "stringUnit" : { @@ -988,6 +992,9 @@ } } } + }, + "Dein Geschlecht hilft, die Auswertung besser einzuordnen." : { + }, "Dein nächstes Gespräch kann hier beginnen." : { "comment" : "PersonDetailView – moments empty state message", @@ -1918,6 +1925,12 @@ } } } + }, + "Geschlecht" : { + + }, + "Geschlecht (optional)" : { + }, "Gesellig" : { "extractionState" : "stale", @@ -2041,7 +2054,7 @@ } } }, - "Halte fest, wen du besucht hast – und wann." : { + "Halte fest, wen du getroffen hast – und wann." : { }, "Hat sich deine Sicht auf die Person verändert?" : { @@ -2155,6 +2168,9 @@ } } } + }, + "Idee: %@" : { + }, "Ideen werden generiert…" : { "comment" : "TodayView GiftSuggestionRow – loading state text", @@ -2457,18 +2473,12 @@ }, "Kontakte aus Adressbuch auswählen" : { - }, - "Kontakte aus Adressbuch hinzufügen" : { - }, "Kontakte auswählen" : { }, "Kontakte hinzufügen" : { - }, - "Kontakte importieren" : { - }, "Kontakte überspringen" : { @@ -2509,6 +2519,9 @@ } } } + }, + "Kurze Frage vorab" : { + }, "Limit erreicht" : { "comment" : "LogbuchView – AI refresh button label when at request limit", @@ -2613,6 +2626,9 @@ } } } + }, + "Max aktiv" : { + }, "Menschen" : { "comment" : "Tab label for people list", @@ -2980,6 +2996,9 @@ } } } + }, + "Nahbar erinnert dich, wenn du diese Person seit der gewählten Zeit nicht mehr kontaktiert hast." : { + }, "nahbar erinnert dich, wenn du lange nichts von jemandem gehört hast." : { @@ -2994,12 +3013,10 @@ } } } - }, - "nahbar Pro" : { - }, "nahbar Pro entdecken" : { "comment" : "SettingsView – Pro upsell button title", + "extractionState" : "stale", "localizations" : { "en" : { "stringUnit" : { @@ -3130,17 +3147,6 @@ } } }, - "Noch keine Besuche bewertet" : { - "comment" : "VisitHistorySection – empty state title", - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "No visits rated yet" - } - } - } - }, "Noch keine Einträge" : { "comment" : "LogbuchView – empty state title", "localizations" : { @@ -3170,6 +3176,17 @@ } } }, + "Noch keine Treffen bewertet" : { + "comment" : "VisitHistorySection – empty state title", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "No meetings rated yet" + } + } + } + }, "Noch nichts festgehalten. Dein nächstes Gespräch kann hier beginnen." : { "comment" : "PersonDetailView – moments empty state", "localizations" : { @@ -3384,6 +3401,9 @@ } } } + }, + "Pro oder Max-Abo" : { + }, "Profil bearbeiten" : { "comment" : "The title of the screen where a user can edit their profile.", @@ -3804,8 +3824,7 @@ } }, "Treffen" : { - "comment" : "MomentType.meeting raw value", - "extractionState" : "stale", + "comment" : "MomentType.meeting rawValue + VisitHistorySection / SettingsView section header", "localizations" : { "en" : { "stringUnit" : { @@ -4243,6 +4262,9 @@ }, "Weiter (%lld ausgewählt)" : { + }, + "Weiter zu den Fragen" : { + }, "Weiter zum nächsten Schritt" : { diff --git a/nahbar/nahbar/LogbuchView.swift b/nahbar/nahbar/LogbuchView.swift index c432cf9..0c9be24 100644 --- a/nahbar/nahbar/LogbuchView.swift +++ b/nahbar/nahbar/LogbuchView.swift @@ -40,7 +40,7 @@ private enum LogbuchItem: Identifiable { var label: String { switch self { - case .moment(let m): return m.type.rawValue + case .moment(let m): return m.type.displayName case .logEntry(let e): return e.type.rawValue } } @@ -183,7 +183,7 @@ struct LogbuchView: View { .font(.system(size: 10)) .foregroundStyle(.orange) } - Text(item.label) + Text(LocalizedStringKey(item.label)) .font(.system(size: 12)) .foregroundStyle(theme.contentTertiary) Text("·") @@ -230,15 +230,14 @@ struct LogbuchView: View { VStack(alignment: .leading, spacing: 12) { HStack(spacing: 6) { SectionHeader(title: "KI-Auswertung", icon: "sparkles") - if !store.isMax { - Text(canUseAI - ? "\(AIAnalysisService.shared.freeQueriesRemaining) gratis" - : "MAX") + MaxBadge() + if !store.isMax && canUseAI { + Text("\(AIAnalysisService.shared.freeQueriesRemaining) gratis") .font(.system(size: 10, weight: .bold)) - .foregroundStyle(theme.accent) + .foregroundStyle(theme.contentTertiary) .padding(.horizontal, 7) .padding(.vertical, 3) - .background(theme.accent.opacity(0.10)) + .background(theme.backgroundSecondary) .clipShape(Capsule()) } } diff --git a/nahbar/nahbar/Models.swift b/nahbar/nahbar/Models.swift index 3d075c6..3fa093e 100644 --- a/nahbar/nahbar/Models.swift +++ b/nahbar/nahbar/Models.swift @@ -41,10 +41,13 @@ enum NudgeFrequency: String, CaseIterable, Codable { enum MomentType: String, CaseIterable, Codable { case conversation = "Gespräch" - case meeting = "Treffen" + case meeting = "Treffen" // rawValue bleibt für Persistenz unverändert case thought = "Gedanke" case intention = "Vorhaben" + /// Anzeigename im UI — entkoppelt Persistenzschlüssel von der Darstellung. + var displayName: String { rawValue } + var icon: String { switch self { case .conversation: return "bubble.left" diff --git a/nahbar/nahbar/NahbarContact.swift b/nahbar/nahbar/NahbarContact.swift index 9e728c9..6157509 100644 --- a/nahbar/nahbar/NahbarContact.swift +++ b/nahbar/nahbar/NahbarContact.swift @@ -14,6 +14,10 @@ struct NahbarContact: Identifiable, Codable, Equatable { var givenName: String var familyName: String var phoneNumbers: [String] + /// E-Mail-Adressen aus dem Adressbuch (alle Labels). + var emailAddresses: [String] + /// Firma oder Organisation des Kontakts. + var organizationName: String var notes: String /// Original CNContact identifier for stable matching against the system address book. var cnIdentifier: String? @@ -23,6 +27,8 @@ struct NahbarContact: Identifiable, Codable, Equatable { givenName: String, familyName: String, phoneNumbers: [String] = [], + emailAddresses: [String] = [], + organizationName: String = "", notes: String = "", cnIdentifier: String? = nil ) { @@ -30,6 +36,8 @@ struct NahbarContact: Identifiable, Codable, Equatable { self.givenName = givenName self.familyName = familyName self.phoneNumbers = phoneNumbers + self.emailAddresses = emailAddresses + self.organizationName = organizationName self.notes = notes self.cnIdentifier = cnIdentifier } @@ -40,11 +48,34 @@ struct NahbarContact: Identifiable, Codable, Equatable { self.givenName = contact.givenName self.familyName = contact.familyName self.phoneNumbers = contact.phoneNumbers.map { $0.value.stringValue } + self.emailAddresses = contact.emailAddresses.map { $0.value as String } + self.organizationName = contact.organizationName // CNContactNoteKey requires a special entitlement – omitted intentionally. self.notes = "" self.cnIdentifier = contact.identifier } + // MARK: - Codable (rückwärtskompatibel) + // Neue Felder (emailAddresses, organizationName) mit decodeIfPresent lesen, + // damit bestehende NahbarContacts.json-Dateien ohne diese Felder weiterhin laden. + + enum CodingKeys: String, CodingKey { + case id, givenName, familyName, phoneNumbers, emailAddresses, organizationName, notes, cnIdentifier + } + + init(from decoder: Decoder) throws { + let c = try decoder.container(keyedBy: CodingKeys.self) + id = try c.decode(UUID.self, forKey: .id) + givenName = try c.decode(String.self, forKey: .givenName) + familyName = try c.decode(String.self, forKey: .familyName) + phoneNumbers = try c.decodeIfPresent([String].self, forKey: .phoneNumbers) ?? [] + emailAddresses = try c.decodeIfPresent([String].self, forKey: .emailAddresses) ?? [] + organizationName = try c.decodeIfPresent(String.self, forKey: .organizationName) ?? "" + notes = try c.decodeIfPresent(String.self, forKey: .notes) ?? "" + cnIdentifier = try c.decodeIfPresent(String.self, forKey: .cnIdentifier) + } + // encode(to:) wird vom Compiler synthetisiert, da alle Felder Encodable sind. + var fullName: String { [givenName, familyName].filter { !$0.isEmpty }.joined(separator: " ") } diff --git a/nahbar/nahbar/OnboardingContainerView.swift b/nahbar/nahbar/OnboardingContainerView.swift index 785e72c..c3443b8 100644 --- a/nahbar/nahbar/OnboardingContainerView.swift +++ b/nahbar/nahbar/OnboardingContainerView.swift @@ -91,6 +91,7 @@ struct OnboardingContainerView: View { UserProfileStore.shared.update( name: coordinator.firstName, birthday: nil, + gender: coordinator.gender, occupation: "", location: "", likes: "", @@ -213,6 +214,41 @@ private struct OnboardingProfileView: View { } .accessibilityLabel("Über mich, maximal 100 Zeichen") } + + // Geschlecht (optional) + VStack(alignment: .leading, spacing: 8) { + Text("Geschlecht (optional)") + .font(.caption) + .foregroundStyle(.secondary) + HStack(spacing: 10) { + ForEach(["Männlich", "Weiblich", "Divers"], id: \.self) { option in + let selected = coordinator.gender == option + Button { + let newValue = selected ? "" : option + coordinator.gender = newValue + // Sofort persistieren, damit der Quiz-Schritt es lesen kann + UserProfileStore.shared.updateGender(newValue) + } label: { + Text(option) + .font(.subheadline) + .padding(.horizontal, 16) + .padding(.vertical, 9) + .background(selected + ? Color.accentColor.opacity(0.14) + : Color.secondary.opacity(0.10)) + .foregroundStyle(selected ? Color.accentColor : .primary) + .clipShape(Capsule()) + .overlay(Capsule().strokeBorder( + selected ? Color.accentColor : Color.clear, + lineWidth: 1.5)) + } + .buttonStyle(.plain) + .animation(.easeInOut(duration: 0.15), value: selected) + .accessibilityLabel(option) + .accessibilityAddTraits(selected ? .isSelected : []) + } + } + } } .padding(.horizontal, 24) @@ -634,8 +670,8 @@ struct FeatureTourStep { ), FeatureTourStep( icon: "figure.walk.arrival", - title: "Besuche", - description: "Halte fest, wen du besucht hast – und wann.", + title: "Treffen", + description: "Halte fest, wen du getroffen hast – und wann.", showPrivacySummary: false ), FeatureTourStep( diff --git a/nahbar/nahbar/OnboardingCoordinator.swift b/nahbar/nahbar/OnboardingCoordinator.swift index b4f2c2e..d7a7d9e 100644 --- a/nahbar/nahbar/OnboardingCoordinator.swift +++ b/nahbar/nahbar/OnboardingCoordinator.swift @@ -27,6 +27,7 @@ final class OnboardingCoordinator: ObservableObject { @Published var firstName: String = "" @Published var displayName: String = "" @Published var aboutMe: String = "" + @Published var gender: String = "" // MARK: – Phase 2: Contacts diff --git a/nahbar/nahbar/PersonDetailView.swift b/nahbar/nahbar/PersonDetailView.swift index 8275dee..0edaa3c 100644 --- a/nahbar/nahbar/PersonDetailView.swift +++ b/nahbar/nahbar/PersonDetailView.swift @@ -258,75 +258,51 @@ struct PersonDetailView: View { .removePendingNotificationRequests(withIdentifiers: ["nextstep-\(person.id)"]) } - /// Persönlichkeitsgesteuerte Aktivitätsvorschläge für den nächsten Schritt. - /// Sortiert nach preferredActivityStyle und highlightNovelty aus PersonalityEngine. + /// Persönlichkeitsbasierter Aktivitätshinweis – ein einziger kombinierter Vorschlag. + /// Zwei passende Aktivitäten werden zu einem lesbaren String verbunden. private func nextStepSuggestionsView(profile: PersonalityProfile) -> some View { let preferred = PersonalityEngine.preferredActivityStyle(for: profile) let highlightNew = PersonalityEngine.highlightNovelty(for: profile) - // (text, icon, style, isNovel) - let activities: [(String, String, ActivityStyle?, Bool)] = [ - ("Kaffee trinken", "cup.and.saucer", .oneOnOne, false), - ("Spazieren gehen", "figure.walk", .oneOnOne, false), - ("Zusammen essen", "fork.knife", .group, false), - ("Etwas unternehmen", "person.2", .group, false), - ("Etwas Neues ausprobieren", "sparkles", nil, true), - ("Anrufen", "phone", nil, false), + // (text, style, isNovel) – kein Icon mehr nötig, da einzelne Zeile + let activities: [(String, ActivityStyle?, Bool)] = [ + ("Kaffee trinken", .oneOnOne, false), + ("Spazieren gehen", .oneOnOne, false), + ("Zusammen essen", .group, false), + ("Etwas unternehmen", .group, false), + ("Etwas Neues ausprobieren", nil, true), + ("Anrufen", nil, false), ] - // Empfohlene Aktivitäten nach oben sortieren - let sorted = activities.sorted { a, b in - func score(_ item: (String, String, ActivityStyle?, Bool)) -> Int { - var s = 0 - if item.2 == preferred { s += 2 } - if item.3 && highlightNew { s += 1 } - return s - } - return score(a) > score(b) + func score(_ item: (String, ActivityStyle?, Bool)) -> Int { + var s = 0 + if item.1 == preferred { s += 2 } + if item.2 && highlightNew { s += 1 } + return s } - let topItems = Array(sorted.prefix(3)) + let sorted = activities.sorted { score($0) > score($1) } - return VStack(spacing: 6) { - ForEach(topItems, id: \.0) { item in - let isRecommended = (item.2 == preferred) || (item.3 && highlightNew) - Button { - nextStepText = item.0 - isEditingNextStep = true - } label: { - HStack(spacing: 10) { - Image(systemName: item.1) - .font(.system(size: 14)) - .foregroundStyle(isRecommended ? NahbarInsightStyle.accentPetrol : theme.contentSecondary) - .frame(width: 20) + // Top-2 zu einem Satz kombinieren: "Kaffee trinken oder spazieren gehen" + let top = sorted.prefix(2).map { $0.0 } + let hint = top.joined(separator: " oder ") + let topActivity = sorted.first?.0 ?? "" - VStack(alignment: .leading, spacing: 3) { - Text(LocalizedStringKey(item.0)) - .font(.system(size: 14)) - .foregroundStyle(theme.contentPrimary) - if isRecommended { - RecommendedBadge(variant: .small) - } - } - - Spacer() - - Image(systemName: "chevron.right") - .font(.system(size: 11)) - .foregroundStyle(theme.contentTertiary) - } - .padding(.horizontal, 14) - .padding(.vertical, 10) - .background(isRecommended ? NahbarInsightStyle.accentPetrol.opacity(0.05) : theme.surfaceCard) - .clipShape(RoundedRectangle(cornerRadius: theme.radiusCard)) - .overlay( - RoundedRectangle(cornerRadius: theme.radiusCard) - .stroke( - isRecommended ? NahbarInsightStyle.accentPetrol.opacity(0.25) : theme.borderSubtle, - lineWidth: 1 - ) - ) - } + return Button { + nextStepText = topActivity + isEditingNextStep = true + } label: { + HStack(spacing: 6) { + Image(systemName: "brain") + .font(.system(size: 11)) + .foregroundStyle(NahbarInsightStyle.accentPetrol) + Text("Idee: \(hint)") + .font(.system(size: 13)) + .foregroundStyle(theme.contentSecondary) + .lineLimit(1) } + .padding(.horizontal, 14) + .padding(.vertical, 7) + .frame(maxWidth: .infinity, alignment: .leading) } } diff --git a/nahbar/nahbar/PersonalityComponents.swift b/nahbar/nahbar/PersonalityComponents.swift index ade75c4..15de8a2 100644 --- a/nahbar/nahbar/PersonalityComponents.swift +++ b/nahbar/nahbar/PersonalityComponents.swift @@ -16,7 +16,7 @@ struct RecommendedBadge: View { var body: some View { HStack(spacing: 4) { - Image(systemName: "sparkles") + Image(systemName: "brain") .font(NahbarInsightStyle.badgeFont) .accessibilityHidden(true) Text(labelText) diff --git a/nahbar/nahbar/PersonalityQuizView.swift b/nahbar/nahbar/PersonalityQuizView.swift index 4824dcf..6932145 100644 --- a/nahbar/nahbar/PersonalityQuizView.swift +++ b/nahbar/nahbar/PersonalityQuizView.swift @@ -13,14 +13,15 @@ struct PersonalityQuizView: View { private enum Phase: Equatable { case intro + case genderSelection case questions case result(PersonalityProfile) static func == (lhs: Phase, rhs: Phase) -> Bool { switch (lhs, rhs) { - case (.intro, .intro), (.questions, .questions): return true - case (.result, .result): return true - default: return false + case (.intro, .intro), (.genderSelection, .genderSelection), (.questions, .questions): return true + case (.result, .result): return true + default: return false } } } @@ -30,7 +31,15 @@ struct PersonalityQuizView: View { init(onComplete: @escaping (PersonalityProfile?) -> Void, skipIntro: Bool = false) { self.onComplete = onComplete self.skipIntro = skipIntro - self._phase = State(initialValue: skipIntro ? .questions : .intro) + let hasGender = !UserProfileStore.shared.gender.isEmpty + self._phase = State(initialValue: skipIntro + ? (hasGender ? .questions : .genderSelection) + : .intro) + } + + /// Nächste Phase nach dem Intro – überspringt Geschlechtsabfrage wenn bereits gesetzt. + private func nextPhaseAfterIntro() -> Phase { + UserProfileStore.shared.gender.isEmpty ? .genderSelection : .questions } var body: some View { @@ -38,10 +47,16 @@ struct PersonalityQuizView: View { switch phase { case .intro: QuizIntroScreen( - onStart: { withAnimation(.spring(response: 0.4)) { phase = .questions } }, + onStart: { withAnimation(.spring(response: 0.4)) { phase = nextPhaseAfterIntro() } }, onSkip: { onComplete(nil); dismiss() } ) + case .genderSelection: + GenderSelectionScreen( + onContinue: { withAnimation(.spring(response: 0.4)) { phase = .questions } }, + onSkip: { onComplete(nil); dismiss() } + ) + case .questions: QuizQuestionsScreen( onComplete: { profile in @@ -121,6 +136,108 @@ struct QuizIntroScreen: View { } } +// MARK: - GenderSelectionScreen + +/// Kurze Geschlechtsabfrage vor den Quiz-Fragen. +/// Speichert die Auswahl sofort in UserProfileStore (single-purpose update). +private struct GenderSelectionScreen: View { + let onContinue: () -> Void + let onSkip: () -> Void + + private let options = ["Männlich", "Weiblich", "Divers"] + + @State private var selected: String = UserProfileStore.shared.gender + + var body: some View { + VStack(spacing: 0) { + Spacer() + + Image(systemName: "person.fill") + .font(.system(size: 60)) + .foregroundStyle(NahbarInsightStyle.accentPetrol) + .accessibilityHidden(true) + .padding(.bottom, 32) + + VStack(spacing: 10) { + Text("Kurze Frage vorab") + .font(.title.bold()) + .multilineTextAlignment(.center) + + Text("Dein Geschlecht hilft, die Auswertung besser einzuordnen.") + .font(.body) + .foregroundStyle(.secondary) + .multilineTextAlignment(.center) + .padding(.horizontal, 32) + } + .padding(.bottom, 32) + + // Chip-Auswahl + HStack(spacing: 12) { + ForEach(options, id: \.self) { option in + let isSelected = selected == option + Button { + selected = isSelected ? "" : option + } label: { + Text(option) + .font(.subheadline.weight(.medium)) + .padding(.horizontal, 20) + .padding(.vertical, 12) + .background(isSelected + ? NahbarInsightStyle.recommendedTint + : Color(.tertiarySystemBackground)) + .foregroundStyle(isSelected + ? NahbarInsightStyle.accentPetrol + : .primary) + .clipShape(Capsule()) + .overlay(Capsule().strokeBorder( + isSelected ? NahbarInsightStyle.accentPetrol : Color.secondary.opacity(0.2), + lineWidth: isSelected ? 2 : 1)) + } + .buttonStyle(.plain) + .animation(.easeInOut(duration: 0.15), value: isSelected) + .accessibilityLabel(option) + .accessibilityAddTraits(isSelected ? .isSelected : []) + } + } + .padding(.horizontal, NahbarInsightStyle.horizontalPadding) + .padding(.bottom, 32) + + PrivacyBadgeView(context: .localOnly) + .padding(.horizontal, NahbarInsightStyle.horizontalPadding) + .padding(.bottom, 24) + + Spacer() + + VStack(spacing: 14) { + Button(action: advance) { + Text("Weiter") + .font(.headline) + .frame(maxWidth: .infinity) + .padding(.vertical, 14) + .background(NahbarInsightStyle.accentPetrol) + .foregroundStyle(.white) + .clipShape(RoundedRectangle(cornerRadius: 12)) + } + .accessibilityLabel("Weiter zu den Fragen") + + Button(action: onSkip) { + Text("Überspringen") + .font(.subheadline) + .foregroundStyle(.secondary) + } + .accessibilityLabel("Quiz überspringen") + } + .padding(.horizontal, NahbarInsightStyle.horizontalPadding) + .padding(.bottom, 40) + } + } + + private func advance() { + UserProfileStore.shared.updateGender(selected) + onContinue() + } +} + // MARK: - QuizQuestionsScreen private struct QuizQuestionsScreen: View { diff --git a/nahbar/nahbar/SettingsView.swift b/nahbar/nahbar/SettingsView.swift index 7fc5824..b8bc6a0 100644 --- a/nahbar/nahbar/SettingsView.swift +++ b/nahbar/nahbar/SettingsView.swift @@ -56,18 +56,18 @@ struct SettingsView: View { .padding(.horizontal, 20) .padding(.top, 12) - // nahbar Pro (oben) + // Abonnement (oben) VStack(alignment: .leading, spacing: 12) { - SectionHeader(title: "nahbar Pro", icon: "star.fill") + SectionHeader(title: "Abonnement", icon: "star.fill") .padding(.horizontal, 20) - if store.isPro { + if store.isMax { HStack { VStack(alignment: .leading, spacing: 2) { - Text("Aktiv") + Text("Max aktiv") .font(.system(size: 15)) .foregroundStyle(theme.contentPrimary) - Text("Alle Pro-Features freigeschaltet") + Text("Alle Features freigeschaltet") .font(.system(size: 12)) .foregroundStyle(theme.contentTertiary) } @@ -84,10 +84,12 @@ struct SettingsView: View { Button { showPaywall = true } label: { HStack { VStack(alignment: .leading, spacing: 2) { - Text("nahbar Pro entdecken") + Text("Pro oder Max-Abo") .font(.system(size: 15, weight: .medium)) .foregroundStyle(theme.accent) - Text("KI-Analyse, Themes & mehr") + Text(store.isPro + ? "Auf Max upgraden – KI-Analyse freischalten" + : "KI-Analyse, Themes & mehr") .font(.system(size: 12)) .foregroundStyle(theme.contentTertiary) } @@ -104,7 +106,7 @@ struct SettingsView: View { } } } - .sheet(isPresented: $showPaywall) { PaywallView() } + .sheet(isPresented: $showPaywall) { PaywallView(targeting: store.isPro ? .max : .pro) } // Theme picker VStack(alignment: .leading, spacing: 12) { @@ -290,9 +292,9 @@ struct SettingsView: View { .padding(.horizontal, 20) } - // Besuche & Bewertungen + // Treffen & Bewertungen VStack(alignment: .leading, spacing: 12) { - SectionHeader(title: "Besuche", icon: "star.fill") + SectionHeader(title: "Treffen", icon: "star.fill") .padding(.horizontal, 20) VStack(spacing: 0) { @@ -337,8 +339,11 @@ struct SettingsView: View { // KI-Einstellungen VStack(alignment: .leading, spacing: 12) { - SectionHeader(title: "KI-Analyse", icon: "sparkles") - .padding(.horizontal, 20) + HStack(spacing: 8) { + SectionHeader(title: "KI-Analyse", icon: "sparkles") + MaxBadge() + } + .padding(.horizontal, 20) VStack(spacing: 0) { settingsTextField(label: "Modell", value: $aiModel, placeholder: AIConfig.fallback.model) diff --git a/nahbar/nahbar/SharedComponents.swift b/nahbar/nahbar/SharedComponents.swift index 71a94b8..517e30e 100644 --- a/nahbar/nahbar/SharedComponents.swift +++ b/nahbar/nahbar/SharedComponents.swift @@ -45,6 +45,22 @@ struct TagBadge: View { } } +// MARK: - Max Badge + +struct MaxBadge: View { + @Environment(\.nahbarTheme) var theme + + var body: some View { + Text("MAX") + .font(.system(size: 10, weight: .bold)) + .foregroundStyle(theme.accent) + .padding(.horizontal, 7) + .padding(.vertical, 3) + .background(theme.accent.opacity(0.10)) + .clipShape(Capsule()) + } +} + // MARK: - Section Header struct SectionHeader: View { diff --git a/nahbar/nahbar/TodayView.swift b/nahbar/nahbar/TodayView.swift index 88f8e61..321a151 100644 --- a/nahbar/nahbar/TodayView.swift +++ b/nahbar/nahbar/TodayView.swift @@ -314,16 +314,18 @@ struct GiftSuggestionRow: View { Text("Geschenkidee vorschlagen") .font(.system(size: 13)) Spacer() - if !store.isMax { - Text(canUseAI - ? "\(AIAnalysisService.shared.freeQueriesRemaining) gratis" - : "MAX") + if store.isMax { + MaxBadge() + } else if canUseAI { + Text("\(AIAnalysisService.shared.freeQueriesRemaining) gratis") .font(.system(size: 10, weight: .bold)) - .foregroundStyle(theme.accent) + .foregroundStyle(theme.contentTertiary) .padding(.horizontal, 6) .padding(.vertical, 2) - .background(theme.accent.opacity(0.10)) + .background(theme.backgroundSecondary) .clipShape(Capsule()) + } else { + MaxBadge() } } .foregroundStyle(canUseAI ? theme.accent : theme.contentSecondary) diff --git a/nahbar/nahbar/UserProfileStore.swift b/nahbar/nahbar/UserProfileStore.swift index 0d1c6df..f39833d 100644 --- a/nahbar/nahbar/UserProfileStore.swift +++ b/nahbar/nahbar/UserProfileStore.swift @@ -17,6 +17,7 @@ final class UserProfileStore: ObservableObject { @Published private(set) var displayName: String = "" @Published private(set) var aboutMe: String = "" @Published private(set) var birthday: Date? = nil + @Published private(set) var gender: String = "" @Published private(set) var occupation: String = "" @Published private(set) var location: String = "" @Published private(set) var likes: String = "" @@ -32,7 +33,7 @@ final class UserProfileStore: ObservableObject { var isEmpty: Bool { name.isEmpty && displayName.isEmpty && occupation.isEmpty && location.isEmpty - && likes.isEmpty && dislikes.isEmpty && socialStyle.isEmpty + && likes.isEmpty && dislikes.isEmpty && socialStyle.isEmpty && gender.isEmpty } var initials: String { @@ -75,11 +76,19 @@ final class UserProfileStore: ObservableObject { objectWillChange.send() } + // MARK: - Geschlecht (gezieltes Update ohne alle Felder) + + func updateGender(_ value: String) { + gender = value + save() + } + // MARK: - Update (batch, explizit durch Nutzer bestätigt) func update( name: String, birthday: Date?, + gender: String, occupation: String, location: String, likes: String, @@ -92,6 +101,7 @@ final class UserProfileStore: ObservableObject { self.displayName = displayName self.aboutMe = aboutMe self.birthday = birthday + self.gender = gender self.occupation = occupation self.location = location self.likes = likes @@ -107,6 +117,7 @@ final class UserProfileStore: ObservableObject { "name": name, "displayName": displayName, "aboutMe": aboutMe, + "gender": gender, "occupation": occupation, "location": location, "likes": likes, @@ -123,7 +134,7 @@ final class UserProfileStore: ObservableObject { func reset() { defaults.removeObject(forKey: storageKey) if let url = photoURL { try? FileManager.default.removeItem(at: url) } - name = ""; displayName = ""; aboutMe = "" + name = ""; displayName = ""; aboutMe = ""; gender = "" birthday = nil; occupation = ""; location = "" likes = ""; dislikes = ""; socialStyle = "" logger.info("UserProfile zurückgesetzt") @@ -134,6 +145,7 @@ final class UserProfileStore: ObservableObject { name = dict["name"] as? String ?? "" displayName = dict["displayName"] as? String ?? "" aboutMe = dict["aboutMe"] as? String ?? "" + gender = dict["gender"] as? String ?? "" occupation = dict["occupation"] as? String ?? "" location = dict["location"] as? String ?? "" likes = dict["likes"] as? String ?? "" diff --git a/nahbar/nahbarShareExtension/ShareExtensionView.swift b/nahbar/nahbarShareExtension/ShareExtensionView.swift index ba4982d..6b5772f 100644 --- a/nahbar/nahbarShareExtension/ShareExtensionView.swift +++ b/nahbar/nahbarShareExtension/ShareExtensionView.swift @@ -48,7 +48,7 @@ struct ShareExtensionView: View { Section("Typ") { Picker("Typ", selection: $momentType) { ForEach(MomentType.allCases, id: \.self) { type in - Label(type.rawValue, systemImage: type.icon).tag(type) + Label(type.displayName, systemImage: type.icon).tag(type) } } .pickerStyle(.segmented) diff --git a/nahbar/nahbarTests/ContactPickerTests.swift b/nahbar/nahbarTests/ContactPickerTests.swift index 5ab09dd..5b68358 100644 --- a/nahbar/nahbarTests/ContactPickerTests.swift +++ b/nahbar/nahbarTests/ContactPickerTests.swift @@ -132,6 +132,20 @@ struct ContactImportTests { #expect(ContactImport.from(contact).name == "Anna Schmidt") } + @Test("Mittelname wird in Name einbezogen") + func middleNameIncluded() { + let contact = CNMutableContact() + contact.givenName = "Max"; contact.middleName = "Otto"; contact.familyName = "Müller" + #expect(ContactImport.from(contact).name == "Max Otto Müller") + } + + @Test("Mittelname leer → kein doppeltes Leerzeichen") + func emptyMiddleNameNoGap() { + let contact = CNMutableContact() + contact.givenName = "Max"; contact.familyName = "Müller" + #expect(ContactImport.from(contact).name == "Max Müller") + } + @Test("Nur Vorname → kein Leerzeichen am Ende") func onlyFirstName() { let contact = CNMutableContact(); contact.givenName = "Cher" @@ -144,10 +158,17 @@ struct ContactImportTests { #expect(ContactImport.from(contact).name == "Prince") } - @Test("Berufsbezeichnung bevorzugt gegenüber Firma") - func jobTitlePreferredOverOrg() { + @Test("Berufsbezeichnung und Firma werden kombiniert") + func jobTitleAndOrgCombined() { let contact = CNMutableContact() contact.jobTitle = "Designer"; contact.organizationName = "ACME GmbH" + #expect(ContactImport.from(contact).occupation == "Designer · ACME GmbH") + } + + @Test("Nur Berufsbezeichnung ohne Firma → kein Trennzeichen") + func jobTitleAloneNoDot() { + let contact = CNMutableContact() + contact.jobTitle = "Designer" #expect(ContactImport.from(contact).occupation == "Designer") } @@ -171,6 +192,24 @@ struct ContactImportTests { #expect(ContactImport.from(contact).location == "Berlin, Deutschland") } + @Test("Stadt, Bundesstaat und Land → alle drei kombiniert") + func locationWithState() { + let contact = CNMutableContact() + let address = CNMutablePostalAddress() + address.city = "San Francisco"; address.state = "CA"; address.country = "USA" + contact.postalAddresses = [CNLabeledValue(label: CNLabelHome, value: address)] + #expect(ContactImport.from(contact).location == "San Francisco, CA, USA") + } + + @Test("Nur Stadt, kein Bundesstaat → kein leerer Eintrag") + func locationCityOnly() { + let contact = CNMutableContact() + let address = CNMutablePostalAddress() + address.city = "Wien" + contact.postalAddresses = [CNLabeledValue(label: CNLabelHome, value: address)] + #expect(ContactImport.from(contact).location == "Wien") + } + @Test("Kein Ort → leerer String") func emptyLocation() { #expect(ContactImport.from(CNMutableContact()).location == "") @@ -208,3 +247,65 @@ struct ContactImportTests { #expect(ContactImport.from(CNMutableContact()).photoData == nil) } } + +// MARK: - NahbarContact – Kontakt-Mapping + +@Suite("NahbarContact – init(from: CNContact)") +struct NahbarContactMappingTests { + + @Test("E-Mail-Adressen werden übernommen") + func emailAddressesImported() { + let contact = CNMutableContact() + contact.emailAddresses = [ + CNLabeledValue(label: CNLabelWork, value: "max@acme.de" as NSString), + CNLabeledValue(label: CNLabelHome, value: "max@privat.de" as NSString) + ] + let nc = NahbarContact(from: contact) + #expect(nc.emailAddresses == ["max@acme.de", "max@privat.de"]) + } + + @Test("Keine E-Mails → leeres Array") + func noEmailAddresses() { + let nc = NahbarContact(from: CNMutableContact()) + #expect(nc.emailAddresses.isEmpty) + } + + @Test("Firma wird übernommen") + func organizationImported() { + let contact = CNMutableContact() + contact.organizationName = "Muster GmbH" + let nc = NahbarContact(from: contact) + #expect(nc.organizationName == "Muster GmbH") + } + + @Test("Keine Firma → leerer String") + func noOrganization() { + let nc = NahbarContact(from: CNMutableContact()) + #expect(nc.organizationName == "") + } + + @Test("Codable round-trip mit neuen Feldern") + func codableRoundTrip() throws { + let original = NahbarContact( + givenName: "Lena", + familyName: "Koch", + phoneNumbers: ["+49 30 123456"], + emailAddresses: ["lena@example.com"], + organizationName: "Tech AG" + ) + let data = try JSONEncoder().encode(original) + let decoded = try JSONDecoder().decode(NahbarContact.self, from: data) + #expect(decoded.emailAddresses == ["lena@example.com"]) + #expect(decoded.organizationName == "Tech AG") + } + + @Test("Rückwärtskompatibilität: altes JSON ohne neue Felder → Defaults") + func backwardsCompatibility() throws { + let oldJSON = """ + {"id":"00000000-0000-0000-0000-000000000001","givenName":"Alt","familyName":"Daten","phoneNumbers":[],"notes":""} + """.data(using: .utf8)! + let decoded = try JSONDecoder().decode(NahbarContact.self, from: oldJSON) + #expect(decoded.emailAddresses.isEmpty) + #expect(decoded.organizationName == "") + } +} diff --git a/nahbar/nahbarTests/ModelTests.swift b/nahbar/nahbarTests/ModelTests.swift index c6f1612..8f67894 100644 --- a/nahbar/nahbarTests/ModelTests.swift +++ b/nahbar/nahbarTests/ModelTests.swift @@ -90,6 +90,21 @@ struct MomentTypeTests { #expect(parsed == type_) } } + + @Test("displayName ist für alle Types gleich rawValue") + func displayNameEqualsRawValueForAllTypes() { + for type_ in MomentType.allCases { + #expect(type_.displayName == type_.rawValue, + "displayName sollte rawValue sein für \(type_)") + } + } + + @Test("alle Types haben nicht-leeres displayName") + func allTypesHaveNonEmptyDisplayName() { + for type_ in MomentType.allCases { + #expect(!type_.displayName.isEmpty) + } + } } // MARK: - MomentSource Tests diff --git a/nahbar/nahbarTests/NahbarPersonalityTests.swift b/nahbar/nahbarTests/NahbarPersonalityTests.swift index a80016f..7bed747 100644 --- a/nahbar/nahbarTests/NahbarPersonalityTests.swift +++ b/nahbar/nahbarTests/NahbarPersonalityTests.swift @@ -383,7 +383,7 @@ struct PersonalityEngineBehaviorTests { @Test("Hoher Neurotizismus → verzögerter Rating-Prompt (7200s)") func highNeuroticismGivesDelayedPrompt() { - let p = profile(c: .low, n: .high) + let p = profile(n: .high, c: .low) if case .delayed(let secs, _) = PersonalityEngine.ratingPromptTiming(for: p) { #expect(secs == 7200) } else { @@ -429,6 +429,34 @@ struct PersonalityEngineBehaviorTests { } } +// MARK: - GenderSelectionScreen Skip-Logik + +@Suite("PersonalityQuiz – Geschlechtsabfrage überspringen") +struct PersonalityQuizGenderSkipTests { + + @Test("Wenn Gender gesetzt → GenderSelectionScreen wird übersprungen (geht zu questions)") + func genderSetLeadsToQuestionsPhase() { + // Spiegelt die nextPhaseAfterIntro()-Logik wider + let gender = "Weiblich" + let shouldSkip = !gender.isEmpty + #expect(shouldSkip) + } + + @Test("Wenn Gender leer → GenderSelectionScreen wird angezeigt") + func emptyGenderShowsSelectionScreen() { + let gender = "" + let shouldShow = gender.isEmpty + #expect(shouldShow) + } + + @Test("Alle drei validen Werte überspringen den Screen") + func allValidGenderValuesSkipScreen() { + for gender in ["Männlich", "Weiblich", "Divers"] { + #expect(!gender.isEmpty, "\(gender) sollte Screen überspringen") + } + } +} + // MARK: - OnboardingStep – Regressionswächter (nach Quiz-Erweiterung) @Suite("OnboardingStep – RawValues (Quiz-Erweiterung)") diff --git a/nahbar/nahbarTests/OnboardingTests.swift b/nahbar/nahbarTests/OnboardingTests.swift index 319e5d3..ab82839 100644 --- a/nahbar/nahbarTests/OnboardingTests.swift +++ b/nahbar/nahbarTests/OnboardingTests.swift @@ -14,6 +14,29 @@ struct OnboardingCoordinatorValidationTests { #expect(!coord.isProfileValid) } + @Test("gender startet als leerer String") + @MainActor func genderStartsEmpty() { + let coord = OnboardingCoordinator() + #expect(coord.gender.isEmpty) + } + + @Test("gender kann auf Männlich, Weiblich oder Divers gesetzt werden") + @MainActor func genderAcceptsValidValues() { + let coord = OnboardingCoordinator() + for value in ["Männlich", "Weiblich", "Divers"] { + coord.gender = value + #expect(coord.gender == value) + } + } + + @Test("gender kann zurückgesetzt werden") + @MainActor func genderCanBeCleared() { + let coord = OnboardingCoordinator() + coord.gender = "Männlich" + coord.gender = "" + #expect(coord.gender.isEmpty) + } + @Test("Nur Leerzeichen → isProfileValid ist false") @MainActor func whitespaceFirstNameIsInvalid() { let coord = OnboardingCoordinator() @@ -46,6 +69,40 @@ struct OnboardingCoordinatorNavigationTests { #expect(coord.currentStep == .profile) } + @Test("advanceToQuiz ohne Vorname bleibt auf .profile") + @MainActor func advanceToQuizWithoutNameStaysOnProfile() { + let coord = OnboardingCoordinator() + coord.firstName = "" + coord.advanceToQuiz() + #expect(coord.currentStep == .profile) + } + + @Test("advanceToQuiz mit gültigem Vorname → .quiz") + @MainActor func advanceToQuizWithNameGoesToQuiz() { + let coord = OnboardingCoordinator() + coord.firstName = "Anna" + coord.advanceToQuiz() + #expect(coord.currentStep == .quiz) + } + + @Test("skipQuiz überspring Quiz und geht zu .contacts") + @MainActor func skipQuizGoesToContacts() { + let coord = OnboardingCoordinator() + coord.firstName = "Anna" + coord.advanceToQuiz() + coord.skipQuiz() + #expect(coord.currentStep == .contacts) + } + + @Test("advanceFromQuizToContacts → .contacts") + @MainActor func advanceFromQuizToContacts() { + let coord = OnboardingCoordinator() + coord.firstName = "Anna" + coord.advanceToQuiz() + coord.advanceFromQuizToContacts() + #expect(coord.currentStep == .contacts) + } + @Test("advanceToContacts ohne Vorname bleibt auf .profile") @MainActor func advanceToContactsWithoutNameStaysOnProfile() { let coord = OnboardingCoordinator() diff --git a/nahbar/nahbarTests/StoreTests.swift b/nahbar/nahbarTests/StoreTests.swift index deb121c..93380d3 100644 --- a/nahbar/nahbarTests/StoreTests.swift +++ b/nahbar/nahbarTests/StoreTests.swift @@ -161,3 +161,32 @@ struct AppGroupProStatusTests { #expect(testDefaults.bool(forKey: "isPro") == false) } } + +// MARK: - Paywall-Targeting Tests + +/// Dokumentiert die Logik aus SettingsView: +/// PaywallView(targeting: store.isPro ? .max : .pro) +/// Stellt sicher, dass der Einstiegs-Tab beim Öffnen des Paywalls korrekt ist. +@Suite("Paywall – Ziel-Tier basierend auf isPro") +struct PaywallTargetingTests { + + /// Repliziert die einzeilige Entscheidungslogik aus SettingsView. + private func target(isPro: Bool) -> SubscriptionTier { + isPro ? .max : .pro + } + + @Test("Kein Abo → Paywall öffnet Pro-Tab") + func noSubscriptionTargetsPro() { + #expect(target(isPro: false) == .pro) + } + + @Test("Pro-only → Paywall öffnet Max-Tab (Upgrade-Pfad)") + func proOnlyTargetsMax() { + #expect(target(isPro: true) == .max) + } + + @Test("Ziel-Tiers sind unterschiedlich") + func targetsAreDistinct() { + #expect(target(isPro: false) != target(isPro: true)) + } +} diff --git a/nahbar/nahbarTests/UserProfileStoreTests.swift b/nahbar/nahbarTests/UserProfileStoreTests.swift index 60c42fb..76485d1 100644 --- a/nahbar/nahbarTests/UserProfileStoreTests.swift +++ b/nahbar/nahbarTests/UserProfileStoreTests.swift @@ -64,14 +64,14 @@ struct UserProfileStoreInitialsTests { struct UserProfileStoreIsEmptyTests { // isEmpty = name.isEmpty && displayName.isEmpty && occupation.isEmpty && location.isEmpty - // && likes.isEmpty && dislikes.isEmpty && socialStyle.isEmpty + // && likes.isEmpty && dislikes.isEmpty && socialStyle.isEmpty && gender.isEmpty private func isEmpty(name: String = "", displayName: String = "", occupation: String = "", location: String = "", likes: String = "", dislikes: String = "", - socialStyle: String = "") -> Bool { + socialStyle: String = "", gender: String = "") -> Bool { name.isEmpty && displayName.isEmpty && occupation.isEmpty && location.isEmpty - && likes.isEmpty && dislikes.isEmpty && socialStyle.isEmpty + && likes.isEmpty && dislikes.isEmpty && socialStyle.isEmpty && gender.isEmpty } @Test("Alle Felder leer → isEmpty ist true") @@ -115,9 +115,14 @@ struct UserProfileStoreIsEmptyTests { #expect(!isEmpty(socialStyle: "Introvertiert")) } + @Test("Nur Geschlecht gesetzt → isEmpty ist false") + func onlyGenderSetIsFalse() { + #expect(!isEmpty(gender: "Weiblich")) + } + @Test("Alle Vorlieben-Felder leer + Rest leer → isEmpty ist true") func allVorliebFieldsEmptyStillEmpty() { - #expect(isEmpty(likes: "", dislikes: "", socialStyle: "")) + #expect(isEmpty(likes: "", dislikes: "", socialStyle: "", gender: "")) } } @@ -164,4 +169,19 @@ struct UserProfileStoreNewFieldsTests { let items = "".split(separator: ",").map { $0.trimmingCharacters(in: .whitespaces) }.filter { !$0.isEmpty } #expect(items.isEmpty) } + + @Test("Geschlechtsoptionen enthalten Männlich, Weiblich, Divers") + func genderOptionsContainExpectedValues() { + let options = ["Männlich", "Weiblich", "Divers"] + #expect(options.contains("Männlich")) + #expect(options.contains("Weiblich")) + #expect(options.contains("Divers")) + #expect(options.count == 3) + } + + @Test("Geschlechtsoptionen sind eindeutig") + func genderOptionsUnique() { + let options = ["Männlich", "Weiblich", "Divers"] + #expect(Set(options).count == options.count) + } }