bugger/Sources/Views/MainWindow/MainWindowView.swift

117 lines
4.2 KiB
Swift

import SwiftUI
struct MainWindowView: View {
@State private var bugStore = BugStore.shared
@State private var windowState = MainWindowState.shared
@State private var showResolved = false
@State private var searchText = ""
private var filteredBugs: [Bug] {
let base = showResolved
? bugStore.bugs.sorted { lhs, rhs in
if lhs.priority != rhs.priority { return lhs.priority < rhs.priority }
return lhs.createdAt < rhs.createdAt
}
: bugStore.bugsSortedByPriority
guard !searchText.isEmpty else { return base }
return base.filter { bug in
bug.title.localizedCaseInsensitiveContains(searchText)
|| bug.customer?.localizedCaseInsensitiveContains(searchText) == true
|| bug.reporter?.localizedCaseInsensitiveContains(searchText) == true
}
}
var body: some View {
NavigationSplitView {
VStack(spacing: 0) {
Picker("", selection: $showResolved) {
Text("Active").tag(false)
Text("All").tag(true)
}
.pickerStyle(.segmented)
.labelsHidden()
.padding(.horizontal, 12)
.padding(.vertical, 8)
List(selection: $windowState.selectedBugID) {
ForEach(filteredBugs) { bug in
SidebarBugRow(
bug: bug,
isUnseen: bugStore.unseenBugs.contains(bug.id)
)
.tag(bug.id)
}
}
}
.navigationSplitViewColumnWidth(min: 240, ideal: 280, max: 360)
.searchable(text: $searchText, placement: .sidebar, prompt: "Search bugs")
} detail: {
if let selectedID = windowState.selectedBugID {
if let bug = bugStore.bugs.first(where: { $0.id == selectedID }) {
BugDetailView(bug: bug, isUnseen: bugStore.unseenBugs.contains(bug.id))
} else {
ContentUnavailableView(
"Bug Not Found",
systemImage: "questionmark.circle",
description: Text("This bug is no longer in the list.")
)
}
} else {
ContentUnavailableView(
"Select a Bug",
systemImage: "ant",
description: Text("Choose a bug from the list to see its details.")
)
}
}
.onChange(of: windowState.selectedBugID) { _, newID in
guard let newID else { return }
bugStore.markSeen(newID)
AppDelegate.shared?.updateBadge(count: bugStore.activeBugs.count)
}
.toolbar {
ToolbarItem(placement: .primaryAction) {
Button {
Task { await PollerService.shared.fetchNow() }
} label: {
Label("Refresh", systemImage: "arrow.clockwise")
}
.disabled(bugStore.isLoading)
}
}
}
}
private struct SidebarBugRow: View {
let bug: Bug
let isUnseen: Bool
var body: some View {
HStack(spacing: 8) {
PriorityBadge(priority: bug.priority)
VStack(alignment: .leading, spacing: 2) {
HStack {
Text(bug.title)
.font(.system(size: 13))
.lineLimit(1)
if isUnseen {
Circle()
.fill(.blue)
.frame(width: 6, height: 6)
}
}
HStack(spacing: 6) {
StatusPill(status: bug.status)
if let customer = bug.customer {
Text(customer)
.font(.caption2)
.foregroundStyle(.secondary)
.lineLimit(1)
}
}
}
}
.padding(.vertical, 2)
}
}