feat: note templates
This commit is contained in:
@@ -29,6 +29,8 @@ class MeetingViewModel: ObservableObject {
|
||||
@Published var selectedTab: MeetingViewTab = .transcript // Default to transcript tab
|
||||
@Published var recordingState: RecordingState = .idle
|
||||
@Published var isDeleted = false
|
||||
@Published var templates: [NoteTemplate] = []
|
||||
@Published var selectedTemplateId: UUID?
|
||||
|
||||
private let audioManager = AudioManager()
|
||||
private var cancellables = Set<AnyCancellable>()
|
||||
@@ -62,6 +64,20 @@ class MeetingViewModel: ObservableObject {
|
||||
selectedTab = .transcript
|
||||
}
|
||||
|
||||
// Load templates and selected template
|
||||
loadTemplates()
|
||||
// Observe template selection: save to meeting and regenerate notes on changes (skip initial)
|
||||
$selectedTemplateId
|
||||
.dropFirst()
|
||||
.sink { [weak self] newTemplateId in
|
||||
guard let self = self else { return }
|
||||
self.meeting.templateId = newTemplateId
|
||||
Task {
|
||||
await self.generateNotes()
|
||||
}
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
|
||||
// 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
|
||||
@@ -144,6 +160,17 @@ class MeetingViewModel: ObservableObject {
|
||||
}
|
||||
}
|
||||
|
||||
func loadTemplates() {
|
||||
templates = LocalStorageManager.shared.loadTemplates()
|
||||
|
||||
// Load per-meeting template or default to Standard Meeting
|
||||
if let meetingTemplateId = meeting.templateId {
|
||||
selectedTemplateId = meetingTemplateId
|
||||
} else if let defaultTemplate = templates.first(where: { $0.title == "Standard Meeting" }) {
|
||||
selectedTemplateId = defaultTemplate.id
|
||||
}
|
||||
}
|
||||
|
||||
func generateNotes() async {
|
||||
isGeneratingNotes = true
|
||||
errorMessage = nil
|
||||
@@ -151,27 +178,24 @@ class MeetingViewModel: ObservableObject {
|
||||
// Clear existing notes for streaming
|
||||
meeting.generatedNotes = ""
|
||||
|
||||
do {
|
||||
// Load settings for generation
|
||||
let userBlurb = KeychainHelper.shared.get(forKey: "userBlurb") ?? ""
|
||||
let systemPrompt = KeychainHelper.shared.get(forKey: "systemPrompt") ?? Settings.defaultSystemPrompt()
|
||||
|
||||
// Use streaming generation
|
||||
let stream = NotesGenerator.shared.generateNotesStream(
|
||||
meeting: meeting,
|
||||
userBlurb: userBlurb,
|
||||
systemPrompt: systemPrompt
|
||||
)
|
||||
|
||||
for await chunk in stream {
|
||||
meeting.generatedNotes += chunk
|
||||
}
|
||||
|
||||
saveMeeting()
|
||||
} catch {
|
||||
errorMessage = "Failed to generate notes: \(error.localizedDescription)"
|
||||
// Load settings for generation
|
||||
let userBlurb = KeychainHelper.shared.get(forKey: "userBlurb") ?? ""
|
||||
let systemPrompt = KeychainHelper.shared.get(forKey: "systemPrompt") ?? Settings.defaultSystemPrompt()
|
||||
|
||||
// Use streaming generation
|
||||
let stream = NotesGenerator.shared.generateNotesStream(
|
||||
meeting: meeting,
|
||||
userBlurb: userBlurb,
|
||||
systemPrompt: systemPrompt,
|
||||
templateId: selectedTemplateId
|
||||
)
|
||||
|
||||
for await chunk in stream {
|
||||
meeting.generatedNotes += chunk
|
||||
}
|
||||
|
||||
saveMeeting()
|
||||
|
||||
isGeneratingNotes = false
|
||||
}
|
||||
|
||||
|
||||
@@ -5,33 +5,82 @@ class SettingsViewModel: ObservableObject {
|
||||
@Published var settings = Settings()
|
||||
@Published var saveMessage = ""
|
||||
@Published var showingSaveMessage = false
|
||||
@Published var templates: [NoteTemplate] = []
|
||||
|
||||
init() {
|
||||
loadSettings()
|
||||
loadTemplates()
|
||||
}
|
||||
|
||||
func loadSettings() {
|
||||
settings.openAIKey = KeychainHelper.shared.get(forKey: "openAIKey") ?? ""
|
||||
settings.userBlurb = KeychainHelper.shared.get(forKey: "userBlurb") ?? ""
|
||||
settings.systemPrompt = KeychainHelper.shared.get(forKey: "systemPrompt") ?? Settings.defaultSystemPrompt()
|
||||
|
||||
// Load selected template ID
|
||||
if let templateIdString = KeychainHelper.shared.get(forKey: "selectedTemplateId"),
|
||||
let templateId = UUID(uuidString: templateIdString) {
|
||||
settings.selectedTemplateId = templateId
|
||||
}
|
||||
}
|
||||
|
||||
func saveSettings() {
|
||||
func loadTemplates() {
|
||||
templates = LocalStorageManager.shared.loadTemplates()
|
||||
|
||||
// Validate that the selected template still exists
|
||||
if let selectedId = settings.selectedTemplateId {
|
||||
if !templates.contains(where: { $0.id == selectedId }) {
|
||||
// Selected template was deleted, clear the selection
|
||||
settings.selectedTemplateId = nil
|
||||
}
|
||||
}
|
||||
|
||||
// If no template is selected, select the first default template
|
||||
if settings.selectedTemplateId == nil {
|
||||
if let defaultTemplate = templates.first(where: { $0.title == "Standard Meeting" }) {
|
||||
settings.selectedTemplateId = defaultTemplate.id
|
||||
} else if let firstTemplate = templates.first {
|
||||
// Fallback to first available template
|
||||
settings.selectedTemplateId = firstTemplate.id
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func saveSettings(showMessage: Bool = true) {
|
||||
// Validate that systemPrompt contains all required template placeholders
|
||||
let requiredKeys = ["meeting_title", "meeting_date", "transcript", "user_blurb", "user_notes", "template_content"]
|
||||
let missing = requiredKeys.filter { !settings.systemPrompt.contains("{{\($0)}}") }
|
||||
if !missing.isEmpty {
|
||||
if showMessage {
|
||||
saveMessage = "Cannot save settings: missing placeholders \(missing.map { "{{\($0)}}" }.joined(separator: ", ")) in system prompt"
|
||||
showingSaveMessage = true
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
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 openAISaved && blurbSaved && promptSaved {
|
||||
saveMessage = "Settings saved successfully!"
|
||||
} else {
|
||||
saveMessage = "Error saving settings"
|
||||
|
||||
// Save selected template ID
|
||||
var templateIdSaved = true
|
||||
if let templateId = settings.selectedTemplateId {
|
||||
templateIdSaved = KeychainHelper.shared.save(templateId.uuidString, forKey: "selectedTemplateId")
|
||||
}
|
||||
|
||||
showingSaveMessage = true
|
||||
|
||||
// Hide the message after 3 seconds
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
|
||||
self.showingSaveMessage = false
|
||||
|
||||
if showMessage {
|
||||
if openAISaved && blurbSaved && promptSaved && templateIdSaved {
|
||||
saveMessage = "Settings saved successfully!"
|
||||
} else {
|
||||
saveMessage = "Error saving settings"
|
||||
}
|
||||
|
||||
showingSaveMessage = true
|
||||
|
||||
// Hide the message after 3 seconds
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
|
||||
self.showingSaveMessage = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
import Foundation
|
||||
import SwiftUI
|
||||
|
||||
@MainActor
|
||||
class TemplatesViewModel: ObservableObject {
|
||||
@Published var templates: [NoteTemplate] = []
|
||||
@Published var isLoading = false
|
||||
@Published var errorMessage: String?
|
||||
|
||||
init() {
|
||||
loadTemplates()
|
||||
}
|
||||
|
||||
func loadTemplates() {
|
||||
isLoading = true
|
||||
templates = LocalStorageManager.shared.loadTemplates()
|
||||
isLoading = false
|
||||
}
|
||||
|
||||
func saveTemplate(_ template: NoteTemplate) {
|
||||
if LocalStorageManager.shared.saveTemplate(template) {
|
||||
loadTemplates()
|
||||
} else {
|
||||
errorMessage = "Failed to save template"
|
||||
}
|
||||
}
|
||||
|
||||
func deleteTemplate(_ template: NoteTemplate) {
|
||||
if LocalStorageManager.shared.deleteTemplate(template) {
|
||||
loadTemplates()
|
||||
} else {
|
||||
errorMessage = "Cannot delete default templates"
|
||||
}
|
||||
}
|
||||
|
||||
func createNewTemplate() -> NoteTemplate {
|
||||
return NoteTemplate(
|
||||
title: "New Template",
|
||||
context: "",
|
||||
sections: [
|
||||
TemplateSection(title: "Section 1", description: "Description of section 1")
|
||||
]
|
||||
)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user