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:
@@ -8,7 +8,7 @@ class SourceListViewModel: ObservableObject {
|
||||
|
||||
@Published var sources: [InvestmentSource] = []
|
||||
@Published var categories: [Category] = []
|
||||
@Published var selectedCategory: Category?
|
||||
@Published var selectedCategoryIds: Set<UUID> = []
|
||||
@Published var searchText = ""
|
||||
@Published var selectedAccount: Account?
|
||||
@Published var showAllAccounts = true
|
||||
@@ -55,7 +55,7 @@ class SourceListViewModel: ObservableObject {
|
||||
Publishers.CombineLatest4(
|
||||
sourceRepository.$sources,
|
||||
$searchText.debounce(for: .milliseconds(300), scheduler: DispatchQueue.main),
|
||||
$selectedCategory,
|
||||
$selectedCategoryIds,
|
||||
$selectedAccount
|
||||
)
|
||||
.combineLatest($showAllAccounts)
|
||||
@@ -88,9 +88,12 @@ class SourceListViewModel: ObservableObject {
|
||||
filtered = filtered.filter { $0.account?.id == selectedAccountId }
|
||||
}
|
||||
|
||||
// Filter by category
|
||||
if let category = selectedCategory {
|
||||
filtered = filtered.filter { $0.category?.id == category.id }
|
||||
// Filter by category (multi-select)
|
||||
if !selectedCategoryIds.isEmpty {
|
||||
filtered = filtered.filter {
|
||||
guard let id = $0.category?.id else { return false }
|
||||
return selectedCategoryIds.contains(id)
|
||||
}
|
||||
}
|
||||
|
||||
// Filter by search text
|
||||
@@ -200,21 +203,29 @@ class SourceListViewModel: ObservableObject {
|
||||
}
|
||||
|
||||
var isEmpty: Bool {
|
||||
sources.isEmpty && searchText.isEmpty && selectedCategory == nil
|
||||
sources.isEmpty && searchText.isEmpty && selectedCategoryIds.isEmpty
|
||||
}
|
||||
|
||||
var isFiltered: Bool {
|
||||
!searchText.isEmpty || selectedCategory != nil
|
||||
!searchText.isEmpty || !selectedCategoryIds.isEmpty
|
||||
}
|
||||
|
||||
// MARK: - Category Filter
|
||||
|
||||
func selectCategory(_ category: Category?) {
|
||||
selectedCategory = category
|
||||
guard let category else {
|
||||
selectedCategoryIds = []
|
||||
return
|
||||
}
|
||||
if selectedCategoryIds.contains(category.id) {
|
||||
selectedCategoryIds.remove(category.id)
|
||||
} else {
|
||||
selectedCategoryIds.insert(category.id)
|
||||
}
|
||||
}
|
||||
|
||||
func clearFilters() {
|
||||
searchText = ""
|
||||
selectedCategory = nil
|
||||
selectedCategoryIds = []
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user