Release 1.4.0 (build 31): retención, quick update, streak, what's new

- 1A: Notificación mensual de resumen de portfolio (día 5 de cada mes)
- 1B: Badge de racha mensual en Dashboard (streak counter)
- 1C: Empty states mejorados en Goals y Journal con CTAs claros
- 2A: Quick Update sheet — actualiza todas las fuentes desde una pantalla
- 2B: Notificación batch update redirige al Quick Update sheet
- 2C: Widget deep link portfoliojournal://quickupdate abre Quick Update
- 3A: App Store version check banner en Settings
- 3C: What's New sheet en primer launch de versión nueva
- Localización completa en 7 idiomas para todas las nuevas strings

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
alexandrev-tibco
2026-05-21 23:09:36 +02:00
parent 773da6800b
commit b48a47ce10
41 changed files with 2659 additions and 483 deletions
@@ -15,6 +15,7 @@ struct SettingsView: View {
@AppStorage("lockOnBackground") private var lockOnBackground = false
@ObservedObject private var cloudStack = CoreDataStack.shared
@ObservedObject private var updateService = AppUpdateService.shared
@State private var showingPinSetup = false
@State private var showingPinChange = false
@@ -39,6 +40,9 @@ struct SettingsView: View {
AppBackground()
List {
if updateService.updateAvailable {
updateAvailableSection
}
brandSection
// Premium Section
premiumSection
@@ -181,6 +185,33 @@ struct SettingsView: View {
if viewModel.backupsEnabled {
viewModel.refreshBackups()
}
updateService.checkForUpdate()
}
}
}
// MARK: - Update Available Section
private var updateAvailableSection: some View {
Section {
Link(destination: URL(string: "https://apps.apple.com/app/id6741412965")!) {
HStack {
Image(systemName: "arrow.down.circle.fill")
.foregroundColor(.appPrimary)
VStack(alignment: .leading, spacing: 2) {
Text(String(localized: "update_available_title"))
.font(.subheadline.weight(.semibold))
if let v = updateService.latestVersion {
Text(String(format: String(localized: "update_available_body"), v))
.font(.caption)
.foregroundColor(.secondary)
}
}
Spacer()
Image(systemName: "chevron.right")
.foregroundColor(.secondary)
.font(.caption)
}
}
}
}
@@ -460,6 +491,12 @@ struct SettingsView: View {
}
}
NavigationLink {
CategoriesView()
} label: {
Label("Categories", systemImage: "tag")
}
Button {
if viewModel.canExport {
viewModel.showingExportOptions = true