feat: add meeting deletion
This commit is contained in:
@@ -20,6 +20,13 @@ class MeetingListViewModel: ObservableObject {
|
|||||||
self?.loadMeetings()
|
self?.loadMeetings()
|
||||||
}
|
}
|
||||||
.store(in: &cancellables)
|
.store(in: &cancellables)
|
||||||
|
|
||||||
|
NotificationCenter.default.publisher(for: .meetingDeleted)
|
||||||
|
.sink { [weak self] _ in
|
||||||
|
print("🔔 Meeting deleted notification received. Reloading meetings list...")
|
||||||
|
self?.loadMeetings()
|
||||||
|
}
|
||||||
|
.store(in: &cancellables)
|
||||||
}
|
}
|
||||||
|
|
||||||
func loadMeetings() {
|
func loadMeetings() {
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import Combine
|
|||||||
// Add notification name for meeting saved events
|
// Add notification name for meeting saved events
|
||||||
extension Notification.Name {
|
extension Notification.Name {
|
||||||
static let meetingSaved = Notification.Name("MeetingSaved")
|
static let meetingSaved = Notification.Name("MeetingSaved")
|
||||||
|
static let meetingDeleted = Notification.Name("MeetingDeleted")
|
||||||
}
|
}
|
||||||
|
|
||||||
enum MeetingViewTab: String, CaseIterable {
|
enum MeetingViewTab: String, CaseIterable {
|
||||||
@@ -27,6 +28,7 @@ class MeetingViewModel: ObservableObject {
|
|||||||
@Published var isRecording = false
|
@Published var isRecording = false
|
||||||
@Published var selectedTab: MeetingViewTab = .myNotes
|
@Published var selectedTab: MeetingViewTab = .myNotes
|
||||||
@Published var recordingState: RecordingState = .idle
|
@Published var recordingState: RecordingState = .idle
|
||||||
|
@Published var isDeleted = false
|
||||||
|
|
||||||
private let audioManager = AudioManager()
|
private let audioManager = AudioManager()
|
||||||
private var cancellables = Set<AnyCancellable>()
|
private var cancellables = Set<AnyCancellable>()
|
||||||
@@ -129,6 +131,7 @@ class MeetingViewModel: ObservableObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func saveMeeting() {
|
func saveMeeting() {
|
||||||
|
if isDeleted { return }
|
||||||
print("💾 Saving meeting: \(meeting.id)")
|
print("💾 Saving meeting: \(meeting.id)")
|
||||||
let success = LocalStorageManager.shared.saveMeeting(meeting)
|
let success = LocalStorageManager.shared.saveMeeting(meeting)
|
||||||
print("💾 Save result: \(success ? "SUCCESS" : "FAILED")")
|
print("💾 Save result: \(success ? "SUCCESS" : "FAILED")")
|
||||||
@@ -162,4 +165,12 @@ class MeetingViewModel: ObservableObject {
|
|||||||
|
|
||||||
NSPasteboard.general.setString(content, forType: .string)
|
NSPasteboard.general.setString(content, forType: .string)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func deleteMeeting() {
|
||||||
|
let success = LocalStorageManager.shared.deleteMeeting(meeting)
|
||||||
|
if success {
|
||||||
|
isDeleted = true
|
||||||
|
NotificationCenter.default.post(name: .meetingDeleted, object: meeting)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -30,6 +30,8 @@ struct CollapsedTranscriptChunkView: View {
|
|||||||
|
|
||||||
struct MeetingDetailView: View {
|
struct MeetingDetailView: View {
|
||||||
@StateObject private var viewModel: MeetingViewModel
|
@StateObject private var viewModel: MeetingViewModel
|
||||||
|
@Environment(\.dismiss) private var dismiss
|
||||||
|
@State private var showDeleteAlert = false
|
||||||
|
|
||||||
init(meeting: Meeting) {
|
init(meeting: Meeting) {
|
||||||
self._viewModel = StateObject(wrappedValue: MeetingViewModel(meeting: meeting))
|
self._viewModel = StateObject(wrappedValue: MeetingViewModel(meeting: meeting))
|
||||||
@@ -37,12 +39,33 @@ struct MeetingDetailView: View {
|
|||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
VStack(alignment: .leading, spacing: 20) {
|
VStack(alignment: .leading, spacing: 20) {
|
||||||
// Meeting Title
|
// Meeting Title with Menu
|
||||||
TextField("Meeting Title", text: $viewModel.meeting.title)
|
HStack {
|
||||||
.font(.title2)
|
TextField("Meeting Title", text: $viewModel.meeting.title)
|
||||||
.fontWeight(.semibold)
|
.font(.title2)
|
||||||
.textFieldStyle(.plain)
|
.fontWeight(.semibold)
|
||||||
.padding(.bottom, 10)
|
.textFieldStyle(.plain)
|
||||||
|
|
||||||
|
Spacer()
|
||||||
|
|
||||||
|
// Ellipsis menu
|
||||||
|
Menu {
|
||||||
|
Button("Delete Meeting", role: .destructive) {
|
||||||
|
showDeleteAlert = true
|
||||||
|
}
|
||||||
|
} label: {
|
||||||
|
Image(systemName: "ellipsis")
|
||||||
|
.resizable()
|
||||||
|
.scaledToFit()
|
||||||
|
.frame(width: 12, height: 12)
|
||||||
|
.foregroundColor(.secondary)
|
||||||
|
}
|
||||||
|
.labelStyle(.iconOnly)
|
||||||
|
.menuIndicator(.hidden)
|
||||||
|
.menuStyle(BorderlessButtonMenuStyle())
|
||||||
|
.frame(width: 20, height: 20)
|
||||||
|
}
|
||||||
|
.padding(.bottom, 10)
|
||||||
|
|
||||||
// Controls Section
|
// Controls Section
|
||||||
HStack {
|
HStack {
|
||||||
@@ -121,6 +144,15 @@ struct MeetingDetailView: View {
|
|||||||
} message: {
|
} message: {
|
||||||
Text(viewModel.errorMessage ?? "")
|
Text(viewModel.errorMessage ?? "")
|
||||||
}
|
}
|
||||||
|
.alert("Delete Meeting", isPresented: $showDeleteAlert) {
|
||||||
|
Button("Delete", role: .destructive) {
|
||||||
|
viewModel.deleteMeeting()
|
||||||
|
dismiss()
|
||||||
|
}
|
||||||
|
Button("Cancel", role: .cancel) { }
|
||||||
|
} message: {
|
||||||
|
Text("Are you sure you want to delete this meeting? This action cannot be undone.")
|
||||||
|
}
|
||||||
.onDisappear {
|
.onDisappear {
|
||||||
viewModel.saveMeeting()
|
viewModel.saveMeeting()
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user