Add AI-powered mental wellness features: Reflection Companion, Pattern Tags, Weekly Digest

Three new Foundation Models features to deepen user engagement with mental wellness:

1. AI Reflection Companion — personalized feedback after completing guided reflections,
   referencing the user's actual words with personality-pack-adapted tone
2. Mood Pattern Tags — auto-extracts theme tags (work, family, stress, etc.) from notes
   and reflections, displayed as colored pills on entries
3. Weekly Emotional Digest — BGTask-scheduled Sunday digest with headline, summary,
   highlight, and intention; shown as card in Insights tab with notification

All features: on-device (zero cost), premium-gated, iOS 26+ with graceful degradation.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Trey t
2026-04-04 00:47:28 -05:00
parent 43ff239781
commit ab8d8fbdc0
18 changed files with 1076 additions and 3 deletions

View File

@@ -24,6 +24,8 @@ struct GuidedReflectionView: View {
@State private var isSaving = false
@State private var showDiscardAlert = false
@State private var showInfoSheet = false
@State private var showFeedback = false
@State private var savedReflection: GuidedReflection?
private let initialDraft: GuidedReflectionDraft
@@ -77,8 +79,24 @@ struct GuidedReflectionView: View {
var body: some View {
NavigationStack {
ScrollViewReader { proxy in
reflectionSheetContent(with: proxy)
ZStack {
ScrollViewReader { proxy in
reflectionSheetContent(with: proxy)
}
.blur(radius: showFeedback ? 6 : 0)
.allowsHitTesting(!showFeedback)
if showFeedback, let savedReflection {
Color.black.opacity(0.3)
.ignoresSafeArea()
.onTapGesture { }
ReflectionFeedbackView(
mood: entry.mood,
reflection: savedReflection,
onDismiss: { dismiss() }
)
}
}
}
}
@@ -454,7 +472,22 @@ struct GuidedReflectionView: View {
)
if success {
dismiss()
// Fire-and-forget tag extraction
if #available(iOS 26, *), !IAPManager.shared.shouldShowPaywall {
Task {
await FoundationModelsTagService.shared.extractAndSaveTags(for: entry)
}
}
// Show AI feedback if reflection is complete and AI is potentially available
if reflection.isComplete {
savedReflection = reflection
withAnimation(.easeInOut(duration: 0.3)) {
showFeedback = true
}
} else {
dismiss()
}
} else {
isSaving = false
}