From 0dac19b10989a9752b6e6f10d25263be25852694 Mon Sep 17 00:00:00 2001 From: alexandrev-tibco Date: Wed, 11 Feb 2026 23:52:38 +0100 Subject: [PATCH] Allow viewing evolution chart for individual or multiple sources across categories Adds multi-source selection to the evolution chart source filter. Users can now tap multiple sources to compare their evolution side by side, regardless of which category they belong to. Fixes #24 Co-Authored-By: Claude Opus 4.6 --- .../ViewModels/ChartsViewModel.swift | 8 +++++++- .../Views/Charts/ChartsContainerView.swift | 18 +++++++++++++----- 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/PortfolioJournal/ViewModels/ChartsViewModel.swift b/PortfolioJournal/ViewModels/ChartsViewModel.swift index f6f20ae..699bdfb 100644 --- a/PortfolioJournal/ViewModels/ChartsViewModel.swift +++ b/PortfolioJournal/ViewModels/ChartsViewModel.swift @@ -86,6 +86,7 @@ class ChartsViewModel: ObservableObject { @Published var selectedChartType: ChartType = .evolution @Published var selectedCategory: Category? @Published var selectedSource: InvestmentSource? + @Published var selectedSourceIds: Set = [] @Published var selectedTimeRange: TimeRange = .year @Published var selectedAccount: Account? @Published var showAllAccounts = true @@ -290,7 +291,12 @@ class ChartsViewModel: ObservableObject { } let sources: [InvestmentSource] - if let selectedSource { + if !selectedSourceIds.isEmpty { + sources = sourceRepository.sources.filter { source in + guard let id = source.id else { return false } + return selectedSourceIds.contains(id) && shouldIncludeSource(source) + } + } else if let selectedSource { sources = sourceRepository.sources.filter { $0.id == selectedSource.id && shouldIncludeSource($0) } } else if let category = category { sources = sourceRepository.fetchSources(for: category).filter { shouldIncludeSource($0) } diff --git a/PortfolioJournal/Views/Charts/ChartsContainerView.swift b/PortfolioJournal/Views/Charts/ChartsContainerView.swift index f329fad..909951e 100644 --- a/PortfolioJournal/Views/Charts/ChartsContainerView.swift +++ b/PortfolioJournal/Views/Charts/ChartsContainerView.swift @@ -208,18 +208,19 @@ struct ChartsContainerView: View { if availableSources.count > 1 { Button { viewModel.selectedSource = nil + viewModel.selectedSourceIds.removeAll() } label: { Text("All Sources") .font(.caption.weight(.medium)) .padding(.horizontal, 12) .padding(.vertical, 6) .background( - viewModel.selectedSource == nil + viewModel.selectedSourceIds.isEmpty && viewModel.selectedSource == nil ? Color.appPrimary : Color.gray.opacity(0.1) ) .foregroundColor( - viewModel.selectedSource == nil + viewModel.selectedSourceIds.isEmpty && viewModel.selectedSource == nil ? .white : .primary ) @@ -228,21 +229,28 @@ struct ChartsContainerView: View { } ForEach(availableSources, id: \.id) { source in + let sourceId = source.id ?? UUID() + let isSelected = viewModel.selectedSourceIds.contains(sourceId) Button { - viewModel.selectedSource = source + viewModel.selectedSource = nil viewModel.selectedCategory = nil + if isSelected { + viewModel.selectedSourceIds.remove(sourceId) + } else { + viewModel.selectedSourceIds.insert(sourceId) + } } label: { Text(source.name) .font(.caption.weight(.medium)) .padding(.horizontal, 12) .padding(.vertical, 6) .background( - viewModel.selectedSource?.id == source.id + isSelected ? Color.appPrimary : Color.gray.opacity(0.1) ) .foregroundColor( - viewModel.selectedSource?.id == source.id + isSelected ? .white : .primary )