feat: auto-start recording and deletion of empty meetings

This commit is contained in:
Owen Gretzinger
2025-07-11 23:41:07 -04:00
parent 1abd323250
commit 1bfdda3a2c
3 changed files with 48 additions and 3 deletions
+45 -1
View File
@@ -26,12 +26,21 @@ class MeetingViewModel: ObservableObject {
@Published var isGeneratingNotes = false @Published var isGeneratingNotes = false
@Published var errorMessage: String? @Published var errorMessage: String?
@Published var isRecording = false @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 recordingState: RecordingState = .idle
@Published var isDeleted = false @Published var isDeleted = false
private let audioManager = AudioManager() private let audioManager = AudioManager()
private var cancellables = Set<AnyCancellable>() private var cancellables = Set<AnyCancellable>()
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()) { init(meeting: Meeting = Meeting()) {
// Load the latest version of the meeting from storage if it exists // Load the latest version of the meeting from storage if it exists
@@ -43,6 +52,16 @@ class MeetingViewModel: ObservableObject {
self.meeting = meeting 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 // 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. // published value doesn't overwrite the saved transcript with an empty array.
audioManager.transcriptChunks = self.meeting.transcriptChunks audioManager.transcriptChunks = self.meeting.transcriptChunks
@@ -70,6 +89,12 @@ class MeetingViewModel: ObservableObject {
self?.saveMeeting() self?.saveMeeting()
} }
.store(in: &cancellables) .store(in: &cancellables)
// Auto-start recording for empty meetings
if isNewMeeting {
print("🚀 Auto-starting recording for empty meeting")
startRecording()
}
} }
private func updateRecordingState() { private func updateRecordingState() {
@@ -107,6 +132,16 @@ class MeetingViewModel: ObservableObject {
func stopRecording() { func stopRecording() {
audioManager.stopRecording() audioManager.stopRecording()
saveMeeting() 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 { func generateNotes() async {
@@ -175,4 +210,13 @@ class MeetingViewModel: ObservableObject {
NotificationCenter.default.post(name: .meetingDeleted, object: meeting) NotificationCenter.default.post(name: .meetingDeleted, object: meeting)
} }
} }
func deleteIfEmpty() {
if isEmpty {
print("🗑️ Auto-deleting empty meeting")
deleteMeeting()
} else {
saveMeeting()
}
}
} }
+2 -1
View File
@@ -147,7 +147,8 @@ struct MeetingDetailView: View {
Text("Are you sure you want to delete this meeting? This action cannot be undone.") Text("Are you sure you want to delete this meeting? This action cannot be undone.")
} }
.onDisappear { .onDisappear {
viewModel.saveMeeting() // Auto-delete empty meetings when leaving, otherwise save
viewModel.deleteIfEmpty()
} }
} }
+1 -1
View File
@@ -23,7 +23,7 @@ struct RenderedNotesView: View {
} else if let level = headingLevel(for: trimmed) { } else if let level = headingLevel(for: trimmed) {
let content = String(trimmed.dropFirst(level + 1)).trimmingCharacters(in: .whitespaces) let content = String(trimmed.dropFirst(level + 1)).trimmingCharacters(in: .whitespaces)
Text(content) Text(content)
.font(.system(size: CGFloat(28 - level * 4), weight: .bold)) .font(.system(size: CGFloat(18 - level * 2), weight: .bold))
.foregroundColor(.white) .foregroundColor(.white)
.frame(maxWidth: .infinity, alignment: .leading) .frame(maxWidth: .infinity, alignment: .leading)
} else if let (indentLevel, bullet, content) = listItemInfo(for: line) { } else if let (indentLevel, bullet, content) = listItemInfo(for: line) {