117 lines
4.2 KiB
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)
|
|
}
|
|
}
|