import SwiftUI struct DiaryEntryView: View { @Environment(\.modelContext) private var modelContext @Environment(DiaryViewModel.self) private var viewModel let entry: DiaryEntry @State private var selectedTab = 0 var body: some View { VStack(spacing: 0) { Picker(String(localized: "entry.viewMode"), selection: $selectedTab) { Text(String(localized: "entry.tab.summary")).tag(0) Text(String(localized: "entry.tab.transcripts")).tag(1) Text(String(localized: "entry.tab.memos")).tag(2) } .pickerStyle(.segmented) .padding() TabView(selection: $selectedTab) { summaryTab .tag(0) transcriptsTab .tag(1) memosTab .tag(2) } .tabViewStyle(.page(indexDisplayMode: .never)) } .navigationTitle(entry.formattedDate) .navigationBarTitleDisplayMode(.inline) } // MARK: - Summary Tab private var summaryTab: some View { ScrollView { VStack(alignment: .leading, spacing: 16) { if let summary = entry.summary { Text(LocalizedStringKey(summary)) .font(.body) .textSelection(.enabled) } else if viewModel.isSummarizing { ProgressView(String(localized: "summary.generating")) .frame(maxWidth: .infinity, alignment: .center) .padding(.top, 40) } else if entry.hasTranscripts { ContentUnavailableView { Label( String(localized: "summary.empty.title"), systemImage: "text.document" ) } description: { Text(String(localized: "summary.empty.description")) } actions: { Button { Task { await viewModel.generateSummary(for: entry) } } label: { Text(String(localized: "summary.generate")) } .buttonStyle(.borderedProminent) } } else { ContentUnavailableView( String(localized: "summary.noTranscripts.title"), systemImage: "waveform.slash", description: Text(String(localized: "summary.noTranscripts.description")) ) } } .padding() } } // MARK: - Transcripts Tab private var transcriptsTab: some View { ScrollView { VStack(alignment: .leading, spacing: 16) { if entry.combinedTranscript.isEmpty { ContentUnavailableView( String(localized: "transcripts.empty.title"), systemImage: "text.bubble", description: Text(String(localized: "transcripts.empty.description")) ) } else { Text(entry.combinedTranscript) .font(.body) .textSelection(.enabled) } } .padding() } } // MARK: - Memos Tab private var memosTab: some View { List { ForEach(entry.memos.sorted(by: { $0.recordedAt < $1.recordedAt })) { memo in MemoRow(memo: memo) } .onDelete { indexSet in let sorted = entry.memos.sorted { $0.recordedAt < $1.recordedAt } for index in indexSet { viewModel.deleteMemo(sorted[index], from: entry, context: modelContext) } } } .listStyle(.plain) .overlay { if entry.memos.isEmpty { ContentUnavailableView( String(localized: "memos.empty.title"), systemImage: "mic.slash", description: Text(String(localized: "memos.empty.description")) ) } } } } struct MemoRow: View { let memo: VoiceMemo var body: some View { VStack(alignment: .leading, spacing: 4) { HStack { Label(memo.formattedDuration, systemImage: "waveform") .font(.subheadline.weight(.medium)) Spacer() Text(memo.recordedAt, style: .time) .font(.caption) .foregroundStyle(.secondary) } if memo.isTranscribing { HStack(spacing: 6) { ProgressView() .controlSize(.small) Text(String(localized: "memo.transcribing")) .font(.caption) .foregroundStyle(.secondary) } } else if let transcript = memo.transcript { Text(transcript.prefix(100)) .font(.caption) .foregroundStyle(.secondary) .lineLimit(2) } } .padding(.vertical, 4) .accessibilityElement(children: .combine) } }