feat: actually implement system audio + refactor
This commit is contained in:
@@ -0,0 +1,35 @@
|
||||
import Foundation
|
||||
import SwiftUI
|
||||
|
||||
@MainActor
|
||||
class MeetingListViewModel: ObservableObject {
|
||||
@Published var meetings: [Meeting] = []
|
||||
@Published var isLoading = false
|
||||
@Published var errorMessage: String?
|
||||
|
||||
init() {
|
||||
loadMeetings()
|
||||
}
|
||||
|
||||
func loadMeetings() {
|
||||
isLoading = true
|
||||
errorMessage = nil
|
||||
|
||||
DispatchQueue.main.async { [weak self] in
|
||||
self?.meetings = LocalStorageManager.shared.loadMeetings()
|
||||
self?.isLoading = false
|
||||
}
|
||||
}
|
||||
|
||||
func deleteMeeting(_ meeting: Meeting) {
|
||||
meetings.removeAll { $0.id == meeting.id }
|
||||
_ = LocalStorageManager.shared.deleteMeeting(meeting)
|
||||
}
|
||||
|
||||
func createNewMeeting() -> Meeting {
|
||||
let newMeeting = Meeting()
|
||||
meetings.insert(newMeeting, at: 0)
|
||||
_ = LocalStorageManager.shared.saveMeeting(newMeeting)
|
||||
return newMeeting
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
import Foundation
|
||||
import SwiftUI
|
||||
import Combine
|
||||
|
||||
@MainActor
|
||||
class MeetingViewModel: ObservableObject {
|
||||
@Published var meeting: Meeting
|
||||
@Published var isGeneratingNotes = false
|
||||
@Published var errorMessage: String?
|
||||
@Published var isRecording = false
|
||||
|
||||
private let audioManager = AudioManager()
|
||||
private var cancellables = Set<AnyCancellable>()
|
||||
|
||||
init(meeting: Meeting = Meeting()) {
|
||||
self.meeting = meeting
|
||||
|
||||
// Update meeting transcript when audio manager transcript changes
|
||||
audioManager.$transcript
|
||||
.sink { [weak self] newTranscript in
|
||||
self?.meeting.transcript = newTranscript
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
|
||||
// Update isRecording when audio manager recording state changes
|
||||
audioManager.$isRecording
|
||||
.sink { [weak self] isRecording in
|
||||
self?.isRecording = isRecording
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
}
|
||||
|
||||
var captureSystemAudio: Bool {
|
||||
get { audioManager.captureSystemAudio }
|
||||
set { audioManager.captureSystemAudio = newValue }
|
||||
}
|
||||
|
||||
func startRecording() {
|
||||
audioManager.startRecording()
|
||||
}
|
||||
|
||||
func stopRecording() {
|
||||
audioManager.stopRecording()
|
||||
saveMeeting()
|
||||
}
|
||||
|
||||
func generateNotes() async {
|
||||
isGeneratingNotes = true
|
||||
errorMessage = nil
|
||||
|
||||
do {
|
||||
// Load settings for generation
|
||||
let userBlurb = KeychainHelper.shared.get(forKey: "userBlurb") ?? ""
|
||||
let systemPrompt = KeychainHelper.shared.get(forKey: "systemPrompt") ?? Settings.defaultSystemPrompt
|
||||
|
||||
// Generate notes
|
||||
meeting.generatedNotes = try await NotesGenerator.shared.generateNotes(
|
||||
transcript: meeting.transcript,
|
||||
userNotes: meeting.userNotes,
|
||||
userBlurb: userBlurb,
|
||||
systemPrompt: systemPrompt
|
||||
)
|
||||
|
||||
saveMeeting()
|
||||
} catch {
|
||||
errorMessage = "Failed to generate notes: \(error.localizedDescription)"
|
||||
}
|
||||
|
||||
isGeneratingNotes = false
|
||||
}
|
||||
|
||||
func saveMeeting() {
|
||||
_ = LocalStorageManager.shared.saveMeeting(meeting)
|
||||
}
|
||||
|
||||
func copyTranscript() {
|
||||
NSPasteboard.general.clearContents()
|
||||
NSPasteboard.general.setString(meeting.transcript, forType: .string)
|
||||
}
|
||||
|
||||
func copyNotes() {
|
||||
NSPasteboard.general.clearContents()
|
||||
NSPasteboard.general.setString(meeting.generatedNotes, forType: .string)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
import Foundation
|
||||
import SwiftUI
|
||||
|
||||
@MainActor
|
||||
class SettingsViewModel: ObservableObject {
|
||||
@Published var settings: Settings
|
||||
@Published var isSaving = false
|
||||
@Published var saveSuccessful = false
|
||||
@Published var errorMessage: String?
|
||||
|
||||
init() {
|
||||
self.settings = Settings()
|
||||
loadSettings()
|
||||
}
|
||||
|
||||
func loadSettings() {
|
||||
// Load API keys from Keychain
|
||||
settings.deepgramKey = KeychainHelper.shared.get(forKey: "deepgramKey") ?? ""
|
||||
settings.openAIKey = KeychainHelper.shared.get(forKey: "openAIKey") ?? ""
|
||||
settings.userBlurb = KeychainHelper.shared.get(forKey: "userBlurb") ?? ""
|
||||
settings.systemPrompt = KeychainHelper.shared.get(forKey: "systemPrompt") ?? Settings.defaultSystemPrompt
|
||||
}
|
||||
|
||||
func saveSettings() {
|
||||
isSaving = true
|
||||
errorMessage = nil
|
||||
saveSuccessful = false
|
||||
|
||||
// Save to Keychain
|
||||
let deepgramSaved = KeychainHelper.shared.save(settings.deepgramKey, forKey: "deepgramKey")
|
||||
let openAISaved = KeychainHelper.shared.save(settings.openAIKey, forKey: "openAIKey")
|
||||
let blurbSaved = KeychainHelper.shared.save(settings.userBlurb, forKey: "userBlurb")
|
||||
let promptSaved = KeychainHelper.shared.save(settings.systemPrompt, forKey: "systemPrompt")
|
||||
|
||||
if deepgramSaved && openAISaved && blurbSaved && promptSaved {
|
||||
saveSuccessful = true
|
||||
// Show success briefly
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 2) { [weak self] in
|
||||
self?.saveSuccessful = false
|
||||
}
|
||||
} else {
|
||||
errorMessage = "Failed to save some settings. Please try again."
|
||||
}
|
||||
|
||||
isSaving = false
|
||||
}
|
||||
|
||||
func resetToDefaults() {
|
||||
settings.systemPrompt = Settings.defaultSystemPrompt
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user