Base fixes and test harness
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import SwiftUI
|
||||
import CoreData
|
||||
import Charts
|
||||
|
||||
struct SourceDetailView: View {
|
||||
@@ -7,7 +8,8 @@ struct SourceDetailView: View {
|
||||
@AppStorage("calmModeEnabled") private var calmModeEnabled = true
|
||||
|
||||
@State private var showingDeleteConfirmation = false
|
||||
@State private var editingSnapshot: Snapshot?
|
||||
@Environment(\.managedObjectContext) private var viewContext
|
||||
@State private var editingSnapshotSelection: SnapshotSelection?
|
||||
|
||||
init(source: InvestmentSource, iapService: IAPService) {
|
||||
_viewModel = StateObject(wrappedValue: SourceDetailViewModel(
|
||||
@@ -17,6 +19,19 @@ struct SourceDetailView: View {
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
Group {
|
||||
if viewModel.isDeleted {
|
||||
Color.clear
|
||||
.onAppear {
|
||||
dismiss()
|
||||
}
|
||||
} else {
|
||||
contentView
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private var contentView: some View {
|
||||
ZStack {
|
||||
AppBackground()
|
||||
|
||||
@@ -49,7 +64,7 @@ struct SourceDetailView: View {
|
||||
.padding()
|
||||
}
|
||||
}
|
||||
.navigationTitle(viewModel.source.name)
|
||||
.navigationTitle(viewModel.safeSourceName)
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
.toolbar {
|
||||
ToolbarItem(placement: .navigationBarTrailing) {
|
||||
@@ -70,12 +85,16 @@ struct SourceDetailView: View {
|
||||
}
|
||||
}
|
||||
}
|
||||
.sheet(isPresented: $viewModel.showingAddSnapshot) {
|
||||
AddSnapshotView(source: viewModel.source)
|
||||
}
|
||||
.sheet(item: $editingSnapshot) { snapshot in
|
||||
.sheet(isPresented: $viewModel.showingAddSnapshot) {
|
||||
AddSnapshotView(source: viewModel.source)
|
||||
}
|
||||
.sheet(item: $editingSnapshotSelection) { selection in
|
||||
if let snapshot = try? viewContext.existingObject(with: selection.id) as? Snapshot {
|
||||
AddSnapshotView(source: viewModel.source, snapshot: snapshot)
|
||||
} else {
|
||||
MissingSnapshotView()
|
||||
}
|
||||
}
|
||||
.sheet(isPresented: $viewModel.showingAddTransaction) {
|
||||
AddTransactionView { type, date, shares, price, amount, notes in
|
||||
viewModel.addTransaction(
|
||||
@@ -100,13 +119,16 @@ struct SourceDetailView: View {
|
||||
titleVisibility: .visible
|
||||
) {
|
||||
Button("Delete", role: .destructive) {
|
||||
// Delete and dismiss
|
||||
let repository = InvestmentSourceRepository()
|
||||
repository.deleteSource(viewModel.source)
|
||||
viewModel.deleteSource()
|
||||
dismiss()
|
||||
}
|
||||
} message: {
|
||||
Text("This will permanently delete \(viewModel.source.name) and all its snapshots.")
|
||||
Text("This will permanently delete \(viewModel.safeSourceName) and all its snapshots.")
|
||||
}
|
||||
.onChange(of: viewModel.isDeleted) { _, deleted in
|
||||
if deleted {
|
||||
dismiss()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -426,25 +448,25 @@ struct SourceDetailView: View {
|
||||
|
||||
// Use LazyVStack for better performance with many snapshots
|
||||
LazyVStack(spacing: 0) {
|
||||
ForEach(viewModel.visibleSnapshots) { snapshot in
|
||||
ForEach(viewModel.visibleSnapshots, id: \.objectID) { snapshot in
|
||||
VStack(spacing: 0) {
|
||||
SnapshotRowView(snapshot: snapshot, onEdit: {
|
||||
editingSnapshot = snapshot
|
||||
editingSnapshotSelection = SnapshotSelection(id: snapshot.objectID)
|
||||
})
|
||||
.contentShape(Rectangle())
|
||||
.onTapGesture {
|
||||
editingSnapshot = snapshot
|
||||
editingSnapshotSelection = SnapshotSelection(id: snapshot.objectID)
|
||||
}
|
||||
.contextMenu {
|
||||
Button {
|
||||
editingSnapshot = snapshot
|
||||
editingSnapshotSelection = SnapshotSelection(id: snapshot.objectID)
|
||||
} label: {
|
||||
Label("Edit", systemImage: "pencil")
|
||||
}
|
||||
}
|
||||
.swipeActions(edge: .trailing) {
|
||||
Button {
|
||||
editingSnapshot = snapshot
|
||||
editingSnapshotSelection = SnapshotSelection(id: snapshot.objectID)
|
||||
} label: {
|
||||
Label("Edit", systemImage: "pencil")
|
||||
}
|
||||
@@ -456,7 +478,7 @@ struct SourceDetailView: View {
|
||||
}
|
||||
}
|
||||
|
||||
if snapshot.id != viewModel.visibleSnapshots.last?.id {
|
||||
if snapshot.objectID != viewModel.visibleSnapshots.last?.objectID {
|
||||
Divider()
|
||||
}
|
||||
}
|
||||
@@ -505,7 +527,7 @@ struct SnapshotRowView: View {
|
||||
var body: some View {
|
||||
HStack(spacing: 12) {
|
||||
VStack(alignment: .leading, spacing: 2) {
|
||||
Text(snapshot.date.friendlyDescription)
|
||||
Text(snapshot.safeDate.friendlyDescription)
|
||||
.font(.subheadline)
|
||||
|
||||
if let notes = snapshot.notes, !notes.isEmpty {
|
||||
@@ -591,6 +613,27 @@ struct MetricChip: View {
|
||||
}
|
||||
}
|
||||
|
||||
private struct SnapshotSelection: Identifiable {
|
||||
let id: NSManagedObjectID
|
||||
}
|
||||
|
||||
private struct MissingSnapshotView: View {
|
||||
var body: some View {
|
||||
VStack(spacing: 12) {
|
||||
Image(systemName: "exclamationmark.triangle")
|
||||
.font(.system(size: 40))
|
||||
.foregroundStyle(.orange)
|
||||
Text("Snapshot not available")
|
||||
.font(.headline)
|
||||
Text("This snapshot was removed or is no longer accessible.")
|
||||
.font(.subheadline)
|
||||
.multilineTextAlignment(.center)
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
.padding()
|
||||
}
|
||||
}
|
||||
|
||||
#Preview {
|
||||
NavigationStack {
|
||||
Text("Source Detail Preview")
|
||||
|
||||
Reference in New Issue
Block a user