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:
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user