Fix #20: Aktivitätsvorschläge-Feature entfernt
Funktion war zu unspezifisch und wenig nützlich. Komplett entfernt: - PersonalityEngine: suggestedActivities, ActivityStyle, ActivitySuggestion, preferredActivityStyle, highlightNovelty - PersonDetailView: activityHint, personalityStore, intentionSuggestionButton(), refreshActivityHint() - NahbarPersonalityTests: highlightNovelty-Tests + SuggestedActivitiesTests-Suite 500 Tests, 0 Fehler. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -618,6 +618,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Alle %lld Tage – basierend auf deinem Profil" : {
|
"Alle %lld Tage – basierend auf deinem Profil" : {
|
||||||
|
"extractionState" : "stale",
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
"en" : {
|
"en" : {
|
||||||
"stringUnit" : {
|
"stringUnit" : {
|
||||||
@@ -918,6 +919,7 @@
|
|||||||
},
|
},
|
||||||
"App-Schutz" : {
|
"App-Schutz" : {
|
||||||
"comment" : "SettingsView – section header for app lock settings",
|
"comment" : "SettingsView – section header for app lock settings",
|
||||||
|
"extractionState" : "stale",
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
"en" : {
|
"en" : {
|
||||||
"stringUnit" : {
|
"stringUnit" : {
|
||||||
@@ -971,8 +973,12 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"Auf Max upgraden" : {
|
||||||
|
|
||||||
},
|
},
|
||||||
"Auf Max upgraden – KI-Analyse freischalten" : {
|
"Auf Max upgraden – KI-Analyse freischalten" : {
|
||||||
|
"extractionState" : "stale",
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
"en" : {
|
"en" : {
|
||||||
"stringUnit" : {
|
"stringUnit" : {
|
||||||
@@ -1275,6 +1281,9 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"Darstellung & Profil" : {
|
||||||
|
|
||||||
},
|
},
|
||||||
"Das kann bis zu einer Minute dauern." : {
|
"Das kann bis zu einer Minute dauern." : {
|
||||||
"comment" : "LogbuchView – AI analysis loading subtitle",
|
"comment" : "LogbuchView – AI analysis loading subtitle",
|
||||||
@@ -1300,6 +1309,7 @@
|
|||||||
},
|
},
|
||||||
"Daten werden geräteübergreifend synchronisiert" : {
|
"Daten werden geräteübergreifend synchronisiert" : {
|
||||||
"comment" : "SettingsView – iCloud sync enabled subtitle",
|
"comment" : "SettingsView – iCloud sync enabled subtitle",
|
||||||
|
"extractionState" : "stale",
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
"en" : {
|
"en" : {
|
||||||
"stringUnit" : {
|
"stringUnit" : {
|
||||||
@@ -1323,6 +1333,7 @@
|
|||||||
},
|
},
|
||||||
"Daten werden nur lokal gespeichert" : {
|
"Daten werden nur lokal gespeichert" : {
|
||||||
"comment" : "SettingsView – iCloud sync disabled subtitle",
|
"comment" : "SettingsView – iCloud sync disabled subtitle",
|
||||||
|
"extractionState" : "stale",
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
"en" : {
|
"en" : {
|
||||||
"stringUnit" : {
|
"stringUnit" : {
|
||||||
@@ -1567,6 +1578,7 @@
|
|||||||
},
|
},
|
||||||
"Diagnose" : {
|
"Diagnose" : {
|
||||||
"comment" : "SettingsView – section header for developer diagnostics",
|
"comment" : "SettingsView – section header for developer diagnostics",
|
||||||
|
"extractionState" : "stale",
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
"en" : {
|
"en" : {
|
||||||
"stringUnit" : {
|
"stringUnit" : {
|
||||||
@@ -2101,6 +2113,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Empfohlenes Nudge-Intervall" : {
|
"Empfohlenes Nudge-Intervall" : {
|
||||||
|
"extractionState" : "stale",
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
"en" : {
|
"en" : {
|
||||||
"stringUnit" : {
|
"stringUnit" : {
|
||||||
@@ -2132,6 +2145,9 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"Entwickler" : {
|
||||||
|
|
||||||
},
|
},
|
||||||
"Entwickler-Log" : {
|
"Entwickler-Log" : {
|
||||||
"comment" : "SettingsView / LogExportView – developer log nav title",
|
"comment" : "SettingsView / LogExportView – developer log nav title",
|
||||||
@@ -2512,6 +2528,9 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"Funktionen" : {
|
||||||
|
|
||||||
},
|
},
|
||||||
"Für wen?" : {
|
"Für wen?" : {
|
||||||
"comment" : "TodayPersonPickerSheet – navigation title",
|
"comment" : "TodayPersonPickerSheet – navigation title",
|
||||||
@@ -2614,6 +2633,9 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"Geräteübergreifend synchronisiert" : {
|
||||||
|
|
||||||
},
|
},
|
||||||
"Geschenkidee anzeigen" : {
|
"Geschenkidee anzeigen" : {
|
||||||
"comment" : "TodayView GiftSuggestionRow – collapsed state button",
|
"comment" : "TodayView GiftSuggestionRow – collapsed state button",
|
||||||
@@ -2970,6 +2992,7 @@
|
|||||||
},
|
},
|
||||||
"iCloud" : {
|
"iCloud" : {
|
||||||
"comment" : "SettingsView – iCloud section header",
|
"comment" : "SettingsView – iCloud section header",
|
||||||
|
"extractionState" : "stale",
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
"en" : {
|
"en" : {
|
||||||
"stringUnit" : {
|
"stringUnit" : {
|
||||||
@@ -2991,6 +3014,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Idee: %@" : {
|
"Idee: %@" : {
|
||||||
|
"extractionState" : "stale",
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
"en" : {
|
"en" : {
|
||||||
"stringUnit" : {
|
"stringUnit" : {
|
||||||
@@ -3168,9 +3192,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
|
||||||
"Kalender-Einstellungen" : {
|
|
||||||
|
|
||||||
},
|
},
|
||||||
"Kauf wiederherstellen" : {
|
"Kauf wiederherstellen" : {
|
||||||
"comment" : "PaywallView – restore purchases button",
|
"comment" : "PaywallView – restore purchases button",
|
||||||
@@ -3249,9 +3270,30 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"KI Insights freischalten" : {
|
||||||
|
|
||||||
|
},
|
||||||
|
"KI Insights zu %@" : {
|
||||||
|
"comment" : "AIAnalysisSheet – navigation title mit Personenname",
|
||||||
|
"localizations" : {
|
||||||
|
"en" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "AI Insights on %@"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"KI Insights, Themes & mehr" : {
|
||||||
|
|
||||||
|
},
|
||||||
|
"KI Modell" : {
|
||||||
|
|
||||||
},
|
},
|
||||||
"KI-Analyse" : {
|
"KI-Analyse" : {
|
||||||
"comment" : "SettingsView – section header for AI settings",
|
"comment" : "SettingsView – section header for AI settings",
|
||||||
|
"extractionState" : "stale",
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
"en" : {
|
"en" : {
|
||||||
"stringUnit" : {
|
"stringUnit" : {
|
||||||
@@ -3263,6 +3305,7 @@
|
|||||||
},
|
},
|
||||||
"KI-Analyse, Themes & mehr" : {
|
"KI-Analyse, Themes & mehr" : {
|
||||||
"comment" : "SettingsView – Pro upsell button subtitle",
|
"comment" : "SettingsView – Pro upsell button subtitle",
|
||||||
|
"extractionState" : "stale",
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
"en" : {
|
"en" : {
|
||||||
"stringUnit" : {
|
"stringUnit" : {
|
||||||
@@ -4158,6 +4201,7 @@
|
|||||||
},
|
},
|
||||||
"nahbar Max freischalten für KI-Analyse" : {
|
"nahbar Max freischalten für KI-Analyse" : {
|
||||||
"comment" : "LogbuchView – upsell button for AI analysis",
|
"comment" : "LogbuchView – upsell button for AI analysis",
|
||||||
|
"extractionState" : "stale",
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
"en" : {
|
"en" : {
|
||||||
"stringUnit" : {
|
"stringUnit" : {
|
||||||
@@ -4190,6 +4234,9 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"nahbar Pro oder Max" : {
|
||||||
|
|
||||||
},
|
},
|
||||||
"nahbar-log.txt" : {
|
"nahbar-log.txt" : {
|
||||||
"comment" : "The file name of the log export.",
|
"comment" : "The file name of the log export.",
|
||||||
@@ -4462,6 +4509,12 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"Nudge alle %lld Tage · Quiz abgeschlossen" : {
|
||||||
|
|
||||||
|
},
|
||||||
|
"Nur lokal gespeichert" : {
|
||||||
|
|
||||||
},
|
},
|
||||||
"Nur Moment löschen" : {
|
"Nur Moment löschen" : {
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
@@ -4674,6 +4727,9 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"Persönlichkeitsquiz" : {
|
||||||
|
|
||||||
},
|
},
|
||||||
"Persönlichkeitsquiz starten" : {
|
"Persönlichkeitsquiz starten" : {
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
@@ -4731,6 +4787,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Pro oder Max-Abo" : {
|
"Pro oder Max-Abo" : {
|
||||||
|
"extractionState" : "stale",
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
"en" : {
|
"en" : {
|
||||||
"stringUnit" : {
|
"stringUnit" : {
|
||||||
@@ -4773,9 +4830,13 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"Push nach dem Treffen" : {
|
||||||
|
|
||||||
},
|
},
|
||||||
"Push-Benachrichtigung nach dem Besuch" : {
|
"Push-Benachrichtigung nach dem Besuch" : {
|
||||||
"comment" : "SettingsView – aftermath notification toggle subtitle",
|
"comment" : "SettingsView – aftermath notification toggle subtitle",
|
||||||
|
"extractionState" : "stale",
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
"en" : {
|
"en" : {
|
||||||
"stringUnit" : {
|
"stringUnit" : {
|
||||||
@@ -4838,6 +4899,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Quiz zurücksetzen" : {
|
"Quiz zurücksetzen" : {
|
||||||
|
"extractionState" : "stale",
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
"en" : {
|
"en" : {
|
||||||
"stringUnit" : {
|
"stringUnit" : {
|
||||||
@@ -5124,6 +5186,9 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"System" : {
|
||||||
|
|
||||||
},
|
},
|
||||||
"Tag" : {
|
"Tag" : {
|
||||||
"comment" : "PaywallView – subscription period label (day)",
|
"comment" : "PaywallView – subscription period label (day)",
|
||||||
@@ -5146,6 +5211,9 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"Tägliche Erinnerung für Anrufe" : {
|
||||||
|
|
||||||
},
|
},
|
||||||
"Teile WhatsApp-Nachrichten direkt in nahbar – sie werden als Momente gespeichert." : {
|
"Teile WhatsApp-Nachrichten direkt in nahbar – sie werden als Momente gespeichert." : {
|
||||||
"comment" : "FeatureTourStep description – WhatsApp share feature",
|
"comment" : "FeatureTourStep description – WhatsApp share feature",
|
||||||
@@ -5227,6 +5295,9 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"Termine & Geburtstage" : {
|
||||||
|
|
||||||
},
|
},
|
||||||
"Themenvorschläge" : {
|
"Themenvorschläge" : {
|
||||||
"comment" : "AddMomentView – conversation suggestions section title",
|
"comment" : "AddMomentView – conversation suggestions section title",
|
||||||
@@ -5547,6 +5618,7 @@
|
|||||||
},
|
},
|
||||||
"Über nahbar" : {
|
"Über nahbar" : {
|
||||||
"comment" : "SettingsView – about section header",
|
"comment" : "SettingsView – about section header",
|
||||||
|
"extractionState" : "stale",
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
"en" : {
|
"en" : {
|
||||||
"stringUnit" : {
|
"stringUnit" : {
|
||||||
@@ -5670,7 +5742,6 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Verlauf" : {
|
"Verlauf" : {
|
||||||
"extractionState" : "stale",
|
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
"en" : {
|
"en" : {
|
||||||
"stringUnit" : {
|
"stringUnit" : {
|
||||||
@@ -5680,6 +5751,18 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"Verlauf & KI Insights zu %@" : {
|
||||||
|
"comment" : "PersonDetailView – logbuch section header mit Personenname",
|
||||||
|
"extractionState" : "stale",
|
||||||
|
"localizations" : {
|
||||||
|
"en" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "History & AI Insights on %@"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"Verlauf & KI-Analyse" : {
|
"Verlauf & KI-Analyse" : {
|
||||||
"comment" : "PersonDetailView – logbuch section header (legacy, nicht mehr in Verwendung)",
|
"comment" : "PersonDetailView – logbuch section header (legacy, nicht mehr in Verwendung)",
|
||||||
"extractionState" : "stale",
|
"extractionState" : "stale",
|
||||||
@@ -5692,28 +5775,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Verlauf & KI Insights zu %@" : {
|
|
||||||
"comment" : "PersonDetailView – logbuch section header mit Personenname",
|
|
||||||
"localizations" : {
|
|
||||||
"en" : {
|
|
||||||
"stringUnit" : {
|
|
||||||
"state" : "translated",
|
|
||||||
"value" : "History & AI Insights on %@"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"KI Insights zu %@" : {
|
|
||||||
"comment" : "AIAnalysisSheet – navigation title mit Personenname",
|
|
||||||
"localizations" : {
|
|
||||||
"en" : {
|
|
||||||
"stringUnit" : {
|
|
||||||
"state" : "translated",
|
|
||||||
"value" : "AI Insights on %@"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"Version" : {
|
"Version" : {
|
||||||
"comment" : "SettingsView – version info row label",
|
"comment" : "SettingsView – version info row label",
|
||||||
"extractionState" : "stale",
|
"extractionState" : "stale",
|
||||||
@@ -5888,9 +5949,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
|
||||||
"Vorschau Geburtstage & Termine" : {
|
|
||||||
|
|
||||||
},
|
},
|
||||||
"Vorschläge fehlgeschlagen" : {
|
"Vorschläge fehlgeschlagen" : {
|
||||||
"comment" : "AddMomentView – conversation suggestions error title",
|
"comment" : "AddMomentView – conversation suggestions error title",
|
||||||
@@ -6516,6 +6574,9 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"Zurücksetzen" : {
|
||||||
|
|
||||||
},
|
},
|
||||||
"Zusammen essen" : {
|
"Zusammen essen" : {
|
||||||
"comment" : "PersonDetailView – activity suggestion: have a meal together (group)",
|
"comment" : "PersonDetailView – activity suggestion: have a meal together (group)",
|
||||||
|
|||||||
@@ -55,9 +55,7 @@ struct PersonDetailView: View {
|
|||||||
// Fallback wenn keine Mail-App installiert
|
// Fallback wenn keine Mail-App installiert
|
||||||
@State private var showingEmailFallback = false
|
@State private var showingEmailFallback = false
|
||||||
|
|
||||||
@StateObject private var personalityStore = PersonalityStore.shared
|
|
||||||
@StateObject private var storeManager = StoreManager.shared
|
@StateObject private var storeManager = StoreManager.shared
|
||||||
@State private var activityHint: String = ""
|
|
||||||
|
|
||||||
// KI-Analyse
|
// KI-Analyse
|
||||||
@State private var showingAIAnalysis = false
|
@State private var showingAIAnalysis = false
|
||||||
@@ -422,12 +420,6 @@ struct PersonDetailView: View {
|
|||||||
.tourTarget(.addMomentButton)
|
.tourTarget(.addMomentButton)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Persönlichkeitsbasierte Vorhaben-Vorschläge (ersetzt nextStepSection)
|
|
||||||
if person.openIntentions.isEmpty,
|
|
||||||
let profile = personalityStore.profile, profile.isComplete {
|
|
||||||
intentionSuggestionButton(profile: profile)
|
|
||||||
}
|
|
||||||
|
|
||||||
if person.sortedMoments.isEmpty {
|
if person.sortedMoments.isEmpty {
|
||||||
Text("Noch nichts festgehalten. Dein nächstes Gespräch kann hier beginnen.")
|
Text("Noch nichts festgehalten. Dein nächstes Gespräch kann hier beginnen.")
|
||||||
.font(.system(size: 14))
|
.font(.system(size: 14))
|
||||||
@@ -458,52 +450,6 @@ struct PersonDetailView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Vorhaben-Vorschlag
|
|
||||||
|
|
||||||
private func intentionSuggestionButton(profile: PersonalityProfile) -> some View {
|
|
||||||
let hint = activityHint.isEmpty ? refreshActivityHint(profile: profile) : activityHint
|
|
||||||
|
|
||||||
return HStack(spacing: 0) {
|
|
||||||
Button {
|
|
||||||
showingAddMoment = 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(.leading, 14)
|
|
||||||
.padding(.vertical, 7)
|
|
||||||
.frame(maxWidth: .infinity, alignment: .leading)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Neue Idee würfeln
|
|
||||||
Button {
|
|
||||||
activityHint = refreshActivityHint(profile: profile)
|
|
||||||
} label: {
|
|
||||||
Image(systemName: "arrow.clockwise")
|
|
||||||
.font(.system(size: 12))
|
|
||||||
.foregroundStyle(theme.contentTertiary)
|
|
||||||
.padding(.horizontal, 12)
|
|
||||||
.padding(.vertical, 7)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@discardableResult
|
|
||||||
private func refreshActivityHint(profile: PersonalityProfile) -> String {
|
|
||||||
let suggestions = PersonalityEngine.suggestedActivities(
|
|
||||||
for: profile, tag: person.tag, count: 2
|
|
||||||
)
|
|
||||||
let hint = suggestions.joined(separator: " oder ")
|
|
||||||
activityHint = hint
|
|
||||||
return hint
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - Logbuch Vorschau
|
// MARK: - Logbuch Vorschau
|
||||||
|
|
||||||
private let logbuchPreviewLimit = 5
|
private let logbuchPreviewLimit = 5
|
||||||
|
|||||||
@@ -185,95 +185,6 @@ enum PersonalityEngine {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Vorhaben-Priorisierung
|
|
||||||
|
|
||||||
/// Gibt an, welche Art von Aktivität zuerst angezeigt werden soll.
|
|
||||||
static func preferredActivityStyle(for profile: PersonalityProfile?) -> ActivityStyle {
|
|
||||||
guard let profile else { return .oneOnOne }
|
|
||||||
switch profile.level(for: .extraversion) {
|
|
||||||
case .high: return .group
|
|
||||||
case .medium: return .oneOnOne
|
|
||||||
case .low: return .oneOnOne
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Gibt an, ob Erlebnis-Aktivitäten hervorgehoben werden sollen.
|
|
||||||
static func highlightNovelty(for profile: PersonalityProfile?) -> Bool {
|
|
||||||
profile?.level(for: .openness) == .high
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Gibt `count` Aktivitätsvorschläge zurück, gewichtet nach Persönlichkeit und Kontakt-Tag.
|
|
||||||
/// Innerhalb gleicher Scores wird zufällig variiert – jeder Aufruf kann andere Ergebnisse liefern.
|
|
||||||
static func suggestedActivities(
|
|
||||||
for profile: PersonalityProfile?,
|
|
||||||
tag: PersonTag?,
|
|
||||||
count: Int = 2
|
|
||||||
) -> [String] {
|
|
||||||
let preferred = preferredActivityStyle(for: profile)
|
|
||||||
let highlightNew = highlightNovelty(for: profile)
|
|
||||||
|
|
||||||
func score(_ s: ActivitySuggestion) -> Int {
|
|
||||||
var p = 0
|
|
||||||
if s.style == preferred { p += 2 }
|
|
||||||
if s.isNovelty && highlightNew { p += 1 }
|
|
||||||
if let t = s.preferredTag, t == tag { p += 1 }
|
|
||||||
return p
|
|
||||||
}
|
|
||||||
|
|
||||||
// Nach Score gruppieren, innerhalb jeder Gruppe mischen → Abwechslung
|
|
||||||
let grouped = Dictionary(grouping: activityPool) { score($0) }
|
|
||||||
var result: [String] = []
|
|
||||||
for key in grouped.keys.sorted(by: >) {
|
|
||||||
guard result.count < count else { break }
|
|
||||||
let bucket = (grouped[key] ?? []).shuffled()
|
|
||||||
for item in bucket {
|
|
||||||
guard result.count < count else { break }
|
|
||||||
result.append(item.text)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - Aktivitäts-Pool (intern, für Tests zugänglich via suggestedActivities)
|
|
||||||
|
|
||||||
static let activityPool: [ActivitySuggestion] = [
|
|
||||||
// ── 1:1 ──────────────────────────────────────────────────────────────
|
|
||||||
ActivitySuggestion("Kaffee trinken", style: .oneOnOne, isNovelty: false, preferredTag: nil),
|
|
||||||
ActivitySuggestion("Spazieren gehen", style: .oneOnOne, isNovelty: false, preferredTag: nil),
|
|
||||||
ActivitySuggestion("Zusammen frühstücken", style: .oneOnOne, isNovelty: false, preferredTag: nil),
|
|
||||||
ActivitySuggestion("Mittagessen", style: .oneOnOne, isNovelty: false, preferredTag: .work),
|
|
||||||
ActivitySuggestion("Auf ein Getränk treffen", style: .oneOnOne, isNovelty: false, preferredTag: nil),
|
|
||||||
ActivitySuggestion("Zusammen kochen", style: .oneOnOne, isNovelty: false, preferredTag: .family),
|
|
||||||
ActivitySuggestion("Bummeln gehen", style: .oneOnOne, isNovelty: false, preferredTag: nil),
|
|
||||||
ActivitySuggestion("Rad fahren", style: .oneOnOne, isNovelty: false, preferredTag: nil),
|
|
||||||
ActivitySuggestion("Joggen gehen", style: .oneOnOne, isNovelty: false, preferredTag: nil),
|
|
||||||
ActivitySuggestion("Picknick", style: .oneOnOne, isNovelty: false, preferredTag: nil),
|
|
||||||
ActivitySuggestion("Besuch machen", style: .oneOnOne, isNovelty: false, preferredTag: .family),
|
|
||||||
ActivitySuggestion("Gemeinsam lesen", style: .oneOnOne, isNovelty: false, preferredTag: nil),
|
|
||||||
// ── Gruppe ───────────────────────────────────────────────────────────
|
|
||||||
ActivitySuggestion("Abendessen", style: .group, isNovelty: false, preferredTag: nil),
|
|
||||||
ActivitySuggestion("Spieleabend", style: .group, isNovelty: false, preferredTag: .friends),
|
|
||||||
ActivitySuggestion("Kino", style: .group, isNovelty: false, preferredTag: .friends),
|
|
||||||
ActivitySuggestion("Konzert oder Show", style: .group, isNovelty: false, preferredTag: nil),
|
|
||||||
ActivitySuggestion("Museum besuchen", style: .group, isNovelty: false, preferredTag: nil),
|
|
||||||
ActivitySuggestion("Wandern", style: .group, isNovelty: false, preferredTag: nil),
|
|
||||||
ActivitySuggestion("Grillabend", style: .group, isNovelty: false, preferredTag: .friends),
|
|
||||||
ActivitySuggestion("Sportevent", style: .group, isNovelty: false, preferredTag: nil),
|
|
||||||
ActivitySuggestion("Veranstaltung besuchen", style: .group, isNovelty: false, preferredTag: .community),
|
|
||||||
// ── Erlebnis ─────────────────────────────────────────────────────────
|
|
||||||
ActivitySuggestion("Etwas Neues ausprobieren", style: nil, isNovelty: true, preferredTag: nil),
|
|
||||||
ActivitySuggestion("Escape Room", style: nil, isNovelty: true, preferredTag: .friends),
|
|
||||||
ActivitySuggestion("Kochkurs", style: nil, isNovelty: true, preferredTag: nil),
|
|
||||||
ActivitySuggestion("Weinprobe oder Tasting", style: nil, isNovelty: true, preferredTag: nil),
|
|
||||||
ActivitySuggestion("Kletterpark", style: nil, isNovelty: true, preferredTag: .friends),
|
|
||||||
ActivitySuggestion("Workshop besuchen", style: nil, isNovelty: true, preferredTag: .community),
|
|
||||||
ActivitySuggestion("Karaoke", style: nil, isNovelty: true, preferredTag: .friends),
|
|
||||||
// ── Einfach / Remote ─────────────────────────────────────────────────
|
|
||||||
ActivitySuggestion("Anrufen", style: nil, isNovelty: false, preferredTag: nil),
|
|
||||||
ActivitySuggestion("Nachricht schicken", style: nil, isNovelty: false, preferredTag: nil),
|
|
||||||
ActivitySuggestion("Artikel oder Tipp teilen", style: nil, isNovelty: false, preferredTag: nil),
|
|
||||||
]
|
|
||||||
|
|
||||||
// MARK: - Intervall-Empfehlung für Einstellungen
|
// MARK: - Intervall-Empfehlung für Einstellungen
|
||||||
|
|
||||||
/// Gibt den empfohlenen Benachrichtigungs-Intervall für das Einstellungsmenü zurück.
|
/// Gibt den empfohlenen Benachrichtigungs-Intervall für das Einstellungsmenü zurück.
|
||||||
@@ -301,23 +212,4 @@ enum RatingPromptTiming {
|
|||||||
case delayed(seconds: Int, copy: String?)
|
case delayed(seconds: Int, copy: String?)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Präferierter Aktivitätsstil für Vorhaben-Vorschläge.
|
|
||||||
enum ActivityStyle {
|
|
||||||
case group
|
|
||||||
case oneOnOne
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Ein einzelner Aktivitätsvorschlag aus dem Pool.
|
|
||||||
struct ActivitySuggestion {
|
|
||||||
let text: String
|
|
||||||
let style: ActivityStyle?
|
|
||||||
let isNovelty: Bool
|
|
||||||
let preferredTag: PersonTag?
|
|
||||||
|
|
||||||
init(_ text: String, style: ActivityStyle?, isNovelty: Bool, preferredTag: PersonTag?) {
|
|
||||||
self.text = text
|
|
||||||
self.style = style
|
|
||||||
self.isNovelty = isNovelty
|
|
||||||
self.preferredTag = preferredTag
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -416,79 +416,6 @@ struct PersonalityEngineBehaviorTests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test("Hohe Offenheit → highlightNovelty true")
|
|
||||||
func highOpennessHighlightsNovelty() {
|
|
||||||
let p = profile(o: .high)
|
|
||||||
#expect(PersonalityEngine.highlightNovelty(for: p))
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test("Niedrige Offenheit → highlightNovelty false")
|
|
||||||
func lowOpennessDoesNotHighlightNovelty() {
|
|
||||||
let p = profile(o: .low)
|
|
||||||
#expect(!PersonalityEngine.highlightNovelty(for: p))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - suggestedActivities Tests
|
|
||||||
|
|
||||||
@Suite("PersonalityEngine – suggestedActivities")
|
|
||||||
struct SuggestedActivitiesTests {
|
|
||||||
|
|
||||||
@Test("Gibt genau count Elemente zurück")
|
|
||||||
func returnsRequestedCount() {
|
|
||||||
let result = PersonalityEngine.suggestedActivities(for: nil, tag: nil, count: 2)
|
|
||||||
#expect(result.count == 2)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test("count: 1 → genau ein Vorschlag")
|
|
||||||
func countOne() {
|
|
||||||
let result = PersonalityEngine.suggestedActivities(for: nil, tag: nil, count: 1)
|
|
||||||
#expect(result.count == 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test("Alle zurückgegebenen Texte stammen aus dem Pool")
|
|
||||||
func resultsAreFromPool() {
|
|
||||||
let poolTexts = Set(PersonalityEngine.activityPool.map(\.text))
|
|
||||||
let result = PersonalityEngine.suggestedActivities(for: nil, tag: nil, count: 5)
|
|
||||||
for text in result {
|
|
||||||
#expect(poolTexts.contains(text), "'\(text)' nicht im Pool")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test("Pool hat mindestens 20 Einträge")
|
|
||||||
func poolIsSufficient() {
|
|
||||||
#expect(PersonalityEngine.activityPool.count >= 20)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test("Keine Duplikate in einem Ergebnis")
|
|
||||||
func noDuplicates() {
|
|
||||||
let result = PersonalityEngine.suggestedActivities(for: nil, tag: nil, count: 5)
|
|
||||||
#expect(result.count == Set(result).count)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test("Ergebnis ist nicht leer wenn Pool vorhanden")
|
|
||||||
func notEmptyWhenPoolExists() {
|
|
||||||
let result = PersonalityEngine.suggestedActivities(for: nil, tag: nil, count: 2)
|
|
||||||
#expect(!result.isEmpty)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test("Pool enthält Erlebnis-Aktivitäten (isNovelty)")
|
|
||||||
func poolContainsNoveltyActivities() {
|
|
||||||
#expect(PersonalityEngine.activityPool.contains { $0.isNovelty })
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test("Pool enthält 1:1 und Gruppen-Aktivitäten")
|
|
||||||
func poolContainsBothStyles() {
|
|
||||||
#expect(PersonalityEngine.activityPool.contains { $0.style == .oneOnOne })
|
|
||||||
#expect(PersonalityEngine.activityPool.contains { $0.style == .group })
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test("Pool enthält Tag-spezifische Aktivitäten")
|
|
||||||
func poolContainsTagSpecificActivities() {
|
|
||||||
#expect(PersonalityEngine.activityPool.contains { $0.preferredTag == .friends })
|
|
||||||
#expect(PersonalityEngine.activityPool.contains { $0.preferredTag == .family })
|
|
||||||
#expect(PersonalityEngine.activityPool.contains { $0.preferredTag == .work })
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - GenderSelectionScreen Skip-Logik
|
// MARK: - GenderSelectionScreen Skip-Logik
|
||||||
|
|||||||
Reference in New Issue
Block a user