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
@@ -4,6 +4,22 @@ struct DashboardSectionConfig: Identifiable, Codable, Hashable {
let id: String
var isVisible: Bool
var isCollapsed: Bool
var columnSpan: Int
init(id: String, isVisible: Bool, isCollapsed: Bool, columnSpan: Int = 1) {
self.id = id
self.isVisible = isVisible
self.isCollapsed = isCollapsed
self.columnSpan = columnSpan
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
id = try container.decode(String.self, forKey: .id)
isVisible = try container.decode(Bool.self, forKey: .isVisible)
isCollapsed = try container.decode(Bool.self, forKey: .isCollapsed)
columnSpan = try container.decodeIfPresent(Int.self, forKey: .columnSpan) ?? 1
}
}
enum DashboardSection: String, CaseIterable, Identifiable {
@@ -16,6 +32,7 @@ enum DashboardSection: String, CaseIterable, Identifiable {
case goals
case pendingUpdates
case periodReturns
case contributionsVsReturns
var id: String { rawValue }
@@ -39,6 +56,8 @@ enum DashboardSection: String, CaseIterable, Identifiable {
return "Pending Updates"
case .periodReturns:
return "Returns"
case .contributionsVsReturns:
return "Invested vs. Returns"
}
}
}