Add AI enablement guidance with reason-specific UI and localized translations

Show specific guidance when Apple Intelligence is unavailable:
- Device not eligible: "iPhone 15 Pro or later required"
- Not enabled: step-by-step path + "Open Settings" button
- Model downloading: "Please wait" + "Try Again" button
- Pre-iOS 26: "Update required"

Auto re-checks availability when app returns to foreground so enabling
Apple Intelligence in Settings immediately triggers insight generation.

Adds translations for all new AI strings across de, es, fr, ja, ko, pt-BR.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Trey t
2026-04-04 21:36:04 -05:00
parent 7a6c4056d8
commit b0cd4be8d7
4 changed files with 19737 additions and 18882 deletions

View File

@@ -21,6 +21,7 @@ struct InsightsView: View {
@AppStorage(UserDefaultsStore.Keys.moodTint.rawValue, store: GroupUserDefaults.groupDefaults) private var moodTint: MoodTints = .Default
@AppStorage(UserDefaultsStore.Keys.moodImages.rawValue, store: GroupUserDefaults.groupDefaults) private var imagePack: MoodImages = .FontAwesome
@Environment(\.colorScheme) private var colorScheme
@Environment(\.scenePhase) private var scenePhase
private var textColor: Color { theme.currentTheme.labelColor }
@@ -107,6 +108,11 @@ struct InsightsView: View {
viewModel.generateInsights()
loadWeeklyDigest()
}
.onChange(of: scenePhase) { _, newPhase in
if newPhase == .active {
viewModel.recheckAvailability()
}
}
.padding(.top)
}
@@ -136,6 +142,11 @@ struct InsightsView: View {
private var insightsContent: some View {
ScrollView {
VStack(spacing: 20) {
// AI enablement guidance when not available
if !viewModel.isAIAvailable && !iapManager.shouldShowPaywall {
aiEnablementCard
}
// Weekly Digest Card
if let digest = weeklyDigest {
WeeklyDigestCardView(digest: digest)
@@ -193,6 +204,91 @@ struct InsightsView: View {
.disabled(iapManager.shouldShowPaywall)
}
// MARK: - AI Enablement Card
private var aiEnablementCard: some View {
VStack(spacing: 16) {
Image(systemName: aiEnablementIcon)
.font(.system(size: 36))
.foregroundStyle(.secondary)
Text(aiEnablementTitle)
.font(.headline)
.foregroundColor(textColor)
Text(aiEnablementDescription)
.font(.subheadline)
.foregroundStyle(.secondary)
.multilineTextAlignment(.center)
if viewModel.aiUnavailableReason == .notEnabled {
Button {
if let url = URL(string: UIApplication.openSettingsURLString) {
UIApplication.shared.open(url)
}
} label: {
Label(String(localized: "Open Settings"), systemImage: "gear")
.font(.subheadline.weight(.semibold))
.frame(maxWidth: .infinity)
.padding(.vertical, 12)
}
.buttonStyle(.borderedProminent)
.tint(.purple)
}
if viewModel.aiUnavailableReason == .modelDownloading {
Button {
viewModel.recheckAvailability()
} label: {
Label(String(localized: "Try Again"), systemImage: "arrow.clockwise")
.font(.subheadline.weight(.medium))
}
.buttonStyle(.bordered)
}
}
.padding(24)
.background(
RoundedRectangle(cornerRadius: 20)
.fill(Color(.secondarySystemBackground))
)
.padding(.horizontal)
}
private var aiEnablementIcon: String {
switch viewModel.aiUnavailableReason {
case .deviceNotEligible: return "iphone.slash"
case .notEnabled: return "gearshape.fill"
case .modelDownloading: return "arrow.down.circle"
case .preiOS26: return "arrow.up.circle"
case .unknown: return "brain.head.profile"
}
}
private var aiEnablementTitle: String {
switch viewModel.aiUnavailableReason {
case .deviceNotEligible: return String(localized: "Device Not Supported")
case .notEnabled: return String(localized: "Enable Apple Intelligence")
case .modelDownloading: return String(localized: "AI Model Downloading")
case .preiOS26: return String(localized: "Update Required")
case .unknown: return String(localized: "AI Unavailable")
}
}
private var aiEnablementDescription: String {
switch viewModel.aiUnavailableReason {
case .deviceNotEligible:
return String(localized: "AI insights require iPhone 15 Pro or later with Apple Intelligence.")
case .notEnabled:
return String(localized: "Turn on Apple Intelligence to unlock personalized mood insights.\n\nSettings → Apple Intelligence & Siri → Apple Intelligence")
case .modelDownloading:
return String(localized: "The AI model is still downloading. This may take a few minutes.")
case .preiOS26:
return String(localized: "AI insights require iOS 26 or later with Apple Intelligence.")
case .unknown:
return String(localized: "Apple Intelligence is required for personalized insights.")
}
}
// MARK: - Generating State
private var isGeneratingInsights: Bool {