From 1bfdda3a2c2beeb95a424995f216da75cb2ef90c Mon Sep 17 00:00:00 2001 From: Owen Gretzinger Date: Fri, 11 Jul 2025 23:41:07 -0400 Subject: [PATCH] feat: auto-start recording and deletion of empty meetings --- notetaker/ViewModels/MeetingViewModel.swift | 46 ++++++++++++++++++++- notetaker/Views/MeetingDetailView.swift | 3 +- notetaker/Views/RenderedNotesView.swift | 2 +- 3 files changed, 48 insertions(+), 3 deletions(-) diff --git a/notetaker/ViewModels/MeetingViewModel.swift b/notetaker/ViewModels/MeetingViewModel.swift index f48b883..b8a4ee6 100644 --- a/notetaker/ViewModels/MeetingViewModel.swift +++ b/notetaker/ViewModels/MeetingViewModel.swift @@ -26,12 +26,21 @@ class MeetingViewModel: ObservableObject { @Published var isGeneratingNotes = false @Published var errorMessage: String? @Published var isRecording = false - @Published var selectedTab: MeetingViewTab = .myNotes + @Published var selectedTab: MeetingViewTab = .transcript // Default to transcript tab @Published var recordingState: RecordingState = .idle @Published var isDeleted = false private let audioManager = AudioManager() private var cancellables = Set() + private var isNewMeeting = false + + // Computed property to check if meeting is empty + var isEmpty: Bool { + return meeting.transcriptChunks.isEmpty && + meeting.userNotes.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty && + meeting.generatedNotes.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty && + meeting.title.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty + } init(meeting: Meeting = Meeting()) { // Load the latest version of the meeting from storage if it exists @@ -43,6 +52,16 @@ class MeetingViewModel: ObservableObject { self.meeting = meeting } + // Detect if this is a new meeting based on content, not storage existence + isNewMeeting = isEmpty + + // Set initial tab based on notes existence + if !self.meeting.generatedNotes.isEmpty { + selectedTab = .enhancedNotes + } else { + selectedTab = .transcript + } + // NEW: Seed the audio manager with any existing transcript chunks so the initial // published value doesn't overwrite the saved transcript with an empty array. audioManager.transcriptChunks = self.meeting.transcriptChunks @@ -70,6 +89,12 @@ class MeetingViewModel: ObservableObject { self?.saveMeeting() } .store(in: &cancellables) + + // Auto-start recording for empty meetings + if isNewMeeting { + print("🚀 Auto-starting recording for empty meeting") + startRecording() + } } private func updateRecordingState() { @@ -107,6 +132,16 @@ class MeetingViewModel: ObservableObject { func stopRecording() { audioManager.stopRecording() saveMeeting() + + // Auto-generate notes if there's a transcript and no existing notes + if !meeting.formattedTranscript.isEmpty && meeting.generatedNotes.isEmpty { + print("🤖 Auto-generating notes after recording stopped") + // Switch to enhanced notes tab immediately when starting generation + selectedTab = .enhancedNotes + Task { + await generateNotes() + } + } } func generateNotes() async { @@ -175,4 +210,13 @@ class MeetingViewModel: ObservableObject { NotificationCenter.default.post(name: .meetingDeleted, object: meeting) } } + + func deleteIfEmpty() { + if isEmpty { + print("🗑️ Auto-deleting empty meeting") + deleteMeeting() + } else { + saveMeeting() + } + } } \ No newline at end of file diff --git a/notetaker/Views/MeetingDetailView.swift b/notetaker/Views/MeetingDetailView.swift index ccd152e..e14bfd5 100644 --- a/notetaker/Views/MeetingDetailView.swift +++ b/notetaker/Views/MeetingDetailView.swift @@ -147,7 +147,8 @@ struct MeetingDetailView: View { Text("Are you sure you want to delete this meeting? This action cannot be undone.") } .onDisappear { - viewModel.saveMeeting() + // Auto-delete empty meetings when leaving, otherwise save + viewModel.deleteIfEmpty() } } diff --git a/notetaker/Views/RenderedNotesView.swift b/notetaker/Views/RenderedNotesView.swift index 377a8b2..59351eb 100644 --- a/notetaker/Views/RenderedNotesView.swift +++ b/notetaker/Views/RenderedNotesView.swift @@ -23,7 +23,7 @@ struct RenderedNotesView: View { } else if let level = headingLevel(for: trimmed) { let content = String(trimmed.dropFirst(level + 1)).trimmingCharacters(in: .whitespaces) Text(content) - .font(.system(size: CGFloat(28 - level * 4), weight: .bold)) + .font(.system(size: CGFloat(18 - level * 2), weight: .bold)) .foregroundColor(.white) .frame(maxWidth: .infinity, alignment: .leading) } else if let (indentLevel, bullet, content) = listItemInfo(for: line) {