feat: add search functionality
This commit is contained in:
@@ -11,21 +11,7 @@ struct ContentView: View {
|
|||||||
@State private var showingSettings = false
|
@State private var showingSettings = false
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
VStack {
|
MeetingListView()
|
||||||
MeetingListView()
|
|
||||||
.toolbar {
|
|
||||||
ToolbarItem(placement: .automatic) {
|
|
||||||
Button {
|
|
||||||
showingSettings = true
|
|
||||||
} label: {
|
|
||||||
Label("Settings", systemImage: "gear")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.sheet(isPresented: $showingSettings) {
|
|
||||||
SettingsView()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,9 +7,28 @@ class MeetingListViewModel: ObservableObject {
|
|||||||
@Published var meetings: [Meeting] = []
|
@Published var meetings: [Meeting] = []
|
||||||
@Published var isLoading = false
|
@Published var isLoading = false
|
||||||
@Published var errorMessage: String?
|
@Published var errorMessage: String?
|
||||||
|
@Published var searchText: String = ""
|
||||||
|
|
||||||
private var cancellables = Set<AnyCancellable>()
|
private var cancellables = Set<AnyCancellable>()
|
||||||
|
|
||||||
|
// Computed property to filter meetings based on search text
|
||||||
|
var filteredMeetings: [Meeting] {
|
||||||
|
guard !searchText.isEmpty else { return meetings }
|
||||||
|
|
||||||
|
return meetings.filter { meeting in
|
||||||
|
// Search in title
|
||||||
|
meeting.title.localizedCaseInsensitiveContains(searchText) ||
|
||||||
|
// Search in user notes
|
||||||
|
meeting.userNotes.localizedCaseInsensitiveContains(searchText) ||
|
||||||
|
// Search in generated notes
|
||||||
|
meeting.generatedNotes.localizedCaseInsensitiveContains(searchText) ||
|
||||||
|
// Search in transcript text
|
||||||
|
meeting.transcriptChunks.contains { chunk in
|
||||||
|
chunk.text.localizedCaseInsensitiveContains(searchText)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
loadMeetings()
|
loadMeetings()
|
||||||
|
|
||||||
|
|||||||
@@ -3,15 +3,16 @@ import SwiftUI
|
|||||||
struct MeetingListView: View {
|
struct MeetingListView: View {
|
||||||
@StateObject private var viewModel = MeetingListViewModel()
|
@StateObject private var viewModel = MeetingListViewModel()
|
||||||
@State private var navigationPath = NavigationPath()
|
@State private var navigationPath = NavigationPath()
|
||||||
|
@State private var showingSettings = false
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
NavigationStack(path: $navigationPath) {
|
NavigationStack(path: $navigationPath) {
|
||||||
List {
|
List {
|
||||||
if viewModel.meetings.isEmpty && !viewModel.isLoading {
|
if viewModel.filteredMeetings.isEmpty && !viewModel.isLoading {
|
||||||
ContentUnavailableView(
|
ContentUnavailableView(
|
||||||
"No Meetings Yet",
|
viewModel.searchText.isEmpty ? "No Meetings Yet" : "No Results",
|
||||||
systemImage: "mic.slash",
|
systemImage: viewModel.searchText.isEmpty ? "mic.slash" : "magnifyingglass",
|
||||||
description: Text("Start a new meeting to begin transcribing")
|
description: Text(viewModel.searchText.isEmpty ? "Start a new meeting to begin transcribing" : "Try a different search term")
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
ForEach(groupedMeetings, id: \.day) { dayGroup in
|
ForEach(groupedMeetings, id: \.day) { dayGroup in
|
||||||
@@ -37,15 +38,38 @@ struct MeetingListView: View {
|
|||||||
}
|
}
|
||||||
.navigationTitle("Meetings")
|
.navigationTitle("Meetings")
|
||||||
.toolbar {
|
.toolbar {
|
||||||
ToolbarItem(placement: .primaryAction) {
|
ToolbarItemGroup(placement: .primaryAction) {
|
||||||
|
// Search field (leftmost within the right-aligned group)
|
||||||
|
HStack(spacing: 4) {
|
||||||
|
Image(systemName: "magnifyingglass")
|
||||||
|
.foregroundColor(.secondary)
|
||||||
|
TextField("Search meetings...", text: $viewModel.searchText)
|
||||||
|
.textFieldStyle(.plain)
|
||||||
|
}
|
||||||
|
.padding(6)
|
||||||
|
.background(
|
||||||
|
RoundedRectangle(cornerRadius: 6)
|
||||||
|
.stroke(Color.secondary.opacity(0.5))
|
||||||
|
)
|
||||||
|
.frame(width: 220)
|
||||||
|
// Settings button (middle)
|
||||||
|
Button {
|
||||||
|
showingSettings = true
|
||||||
|
} label: {
|
||||||
|
Image(systemName: "gearshape")
|
||||||
|
}
|
||||||
|
// Plus button (rightmost)
|
||||||
Button {
|
Button {
|
||||||
let newMeeting = viewModel.createNewMeeting()
|
let newMeeting = viewModel.createNewMeeting()
|
||||||
navigationPath.append(newMeeting)
|
navigationPath.append(newMeeting)
|
||||||
} label: {
|
} label: {
|
||||||
Label("New Meeting", systemImage: "plus")
|
Image(systemName: "plus")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.sheet(isPresented: $showingSettings) {
|
||||||
|
SettingsView()
|
||||||
|
}
|
||||||
.navigationDestination(for: Meeting.self) { meeting in
|
.navigationDestination(for: Meeting.self) { meeting in
|
||||||
MeetingDetailView(meeting: meeting)
|
MeetingDetailView(meeting: meeting)
|
||||||
}
|
}
|
||||||
@@ -64,7 +88,7 @@ struct MeetingListView: View {
|
|||||||
let calendar = Calendar.current
|
let calendar = Calendar.current
|
||||||
let now = Date()
|
let now = Date()
|
||||||
|
|
||||||
let grouped = Dictionary(grouping: viewModel.meetings) { meeting in
|
let grouped = Dictionary(grouping: viewModel.filteredMeetings) { meeting in
|
||||||
calendar.startOfDay(for: meeting.date)
|
calendar.startOfDay(for: meeting.date)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user