Files
meetingnotes/meetingnotes/Models/Meeting.swift
T

175 lines
5.0 KiB
Swift

import Foundation
enum TranscriptTimestampFormatter {
static let formatter: DateFormatter = {
let formatter = DateFormatter()
formatter.dateFormat = "HH:mm:ss"
return formatter
}()
static func string(from date: Date) -> String {
formatter.string(from: date)
}
}
enum AudioSource: String, Codable, CaseIterable {
case mic = "MIC"
case system = "SYS"
var displayName: String {
switch self {
case .mic:
return "Me"
case .system:
return "Them"
}
}
var copyPrefix: String {
switch self {
case .mic:
return "Me"
case .system:
return "Them"
}
}
var icon: String {
switch self {
case .mic:
return "mic.fill"
case .system:
return "speaker.wave.2.fill"
}
}
}
struct TranscriptChunk: Codable, Identifiable, Hashable {
let id: UUID
let timestamp: Date
let source: AudioSource
let speaker: Int?
let text: String
let isFinal: Bool
init(id: UUID = UUID(), timestamp: Date = Date(), source: AudioSource, speaker: Int? = nil, text: String, isFinal: Bool = false) {
self.id = id
self.timestamp = timestamp
self.source = source
self.speaker = speaker
self.text = text
self.isFinal = isFinal
}
var displayName: String {
if source == .mic { return "Me" }
if let speaker { return "Speaker \(speaker)" }
return source.displayName
}
}
struct CollapsedTranscriptChunk: Identifiable {
let id: UUID
let timestamp: Date
let source: AudioSource
let speaker: Int?
let combinedText: String
init(id: UUID = UUID(), timestamp: Date, source: AudioSource, speaker: Int? = nil, combinedText: String) {
self.id = id
self.timestamp = timestamp
self.source = source
self.speaker = speaker
self.combinedText = combinedText
}
var displayName: String {
if source == .mic { return "Me" }
if let speaker { return "Speaker \(speaker)" }
return source.displayName
}
}
struct Meeting: Codable, Identifiable, Hashable {
let id: UUID
let date: Date
var title: String
var transcriptChunks: [TranscriptChunk]
var userNotes: String
var generatedNotes: String
var templateId: UUID? // Add property to track per-meeting template
var recoveryAudioFolderName: String?
// MARK: - Data versioning
/// Version of this Meeting record on disk. Useful for migration.
var dataVersion: Int
/// Current app data version. Increment whenever you make a breaking change to `Meeting` that requires migration.
static let currentDataVersion = 1
init(id: UUID = UUID(),
date: Date = Date(),
title: String = "",
transcriptChunks: [TranscriptChunk] = [],
userNotes: String = "",
generatedNotes: String = "",
templateId: UUID? = nil,
recoveryAudioFolderName: String? = nil,
dataVersion: Int = Meeting.currentDataVersion) {
self.id = id
self.date = date
self.title = title
self.transcriptChunks = transcriptChunks
self.userNotes = userNotes
self.generatedNotes = generatedNotes
self.templateId = templateId
self.recoveryAudioFolderName = recoveryAudioFolderName
self.dataVersion = dataVersion
}
// `Codable` conformance now uses the compiler-synthesised implementation.
// Computed property for backward compatibility with existing code
var transcript: String {
return transcriptChunks
.filter { $0.isFinal }
.map { "[\($0.displayName)] \($0.text)" }
.joined(separator: " ")
}
// Formatted transcript for copying with collapsed sequential chunks
var formattedTranscript: String {
let finalChunks = transcriptChunks.filter { $0.isFinal }
return finalChunks.map { chunk in
"[\(TranscriptTimestampFormatter.string(from: chunk.timestamp))] \(chunk.displayName): \(chunk.text)"
}.joined(separator: "\n")
}
// Collapsed chunks for UI display
var collapsedTranscriptChunks: [CollapsedTranscriptChunk] {
transcriptChunks.filter(\.isFinal).map { chunk in
CollapsedTranscriptChunk(
id: chunk.id,
timestamp: chunk.timestamp,
source: chunk.source,
speaker: chunk.speaker,
combinedText: chunk.text
)
}
}
// Separate computed properties for mic and system transcripts
var micTranscript: String {
return transcriptChunks
.filter { $0.source == .mic && $0.isFinal }
.map { $0.text }
.joined(separator: " ")
}
var systemTranscript: String {
return transcriptChunks
.filter { $0.source == .system && $0.isFinal }
.map { $0.text }
.joined(separator: " ")
}
}