Base fixes and test harness

This commit is contained in:
alexandrev-tibco
2026-02-01 11:12:57 +01:00
parent f5b13ec924
commit e328767c4a
39 changed files with 3575 additions and 142 deletions
@@ -1,17 +1,21 @@
import SwiftUI
import CoreData
struct SourceListView: View {
@EnvironmentObject var iapService: IAPService
@EnvironmentObject var accountStore: AccountStore
@Environment(\.managedObjectContext) private var context
@StateObject private var viewModel: SourceListViewModel
@AppStorage("calmModeEnabled") private var calmModeEnabled = true
@State private var sourceToDelete: InvestmentSource?
@State private var navigationPath = NavigationPath()
init(iapService: IAPService) {
_viewModel = StateObject(wrappedValue: SourceListViewModel(iapService: iapService))
}
var body: some View {
NavigationStack {
NavigationStack(path: $navigationPath) {
ZStack {
AppBackground()
@@ -58,6 +62,36 @@ struct SourceListView: View {
.onReceive(accountStore.$showAllAccounts) { showAll in
viewModel.showAllAccounts = showAll
}
.navigationDestination(for: NSManagedObjectID.self) { objectID in
if let source = try? context.existingObject(with: objectID) as? InvestmentSource,
!source.isDeleted {
SourceDetailView(source: source, iapService: iapService)
} else {
MissingSourceView()
}
}
.confirmationDialog(
"Delete Source",
isPresented: Binding(
get: { sourceToDelete != nil },
set: { if !$0 { sourceToDelete = nil } }
),
titleVisibility: .visible
) {
Button("Delete", role: .destructive) {
if let source = sourceToDelete {
viewModel.deleteSource(source)
sourceToDelete = nil
}
}
Button("Cancel", role: .cancel) {
sourceToDelete = nil
}
} message: {
if let source = sourceToDelete {
Text("This will permanently delete \(source.name) and all its snapshots.")
}
}
}
}
@@ -114,15 +148,19 @@ struct SourceListView: View {
// Sources
Section {
ForEach(viewModel.sources) { source in
NavigationLink {
SourceDetailView(source: source, iapService: iapService)
} label: {
SourceRowView(source: source, calmModeEnabled: calmModeEnabled)
}
}
.onDelete { indexSet in
viewModel.deleteSource(at: indexSet)
ForEach(viewModel.sources, id: \.objectID) { source in
SourceRowView(source: source, calmModeEnabled: calmModeEnabled)
.contentShape(Rectangle())
.onTapGesture {
navigationPath.append(source.objectID)
}
.swipeActions(edge: .trailing, allowsFullSwipe: false) {
Button(role: .destructive) {
sourceToDelete = source
} label: {
Label("Delete", systemImage: "trash")
}
}
}
} header: {
if viewModel.isFiltered {
@@ -263,6 +301,22 @@ struct SourceListView: View {
}
}
private struct MissingSourceView: View {
var body: some View {
VStack(spacing: 12) {
Image(systemName: "exclamationmark.triangle")
.font(.system(size: 42))
.foregroundColor(.secondary)
Text("Source not available")
.font(.headline)
Text("This source was deleted.")
.font(.subheadline)
.foregroundColor(.secondary)
}
.padding()
}
}
// MARK: - Source Row View
struct SourceRowView: View {