SwiftUI + SwiftData + iCloud, Apple Speech transcription (German), audio recording, summarization service protocol (LLM-ready), localization scaffolding (EN/DE/ES/FR), basic tests. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
88 lines
2.0 KiB
Swift
88 lines
2.0 KiB
Swift
import SwiftUI
|
|
|
|
struct RecordingView: View {
|
|
@Environment(\.modelContext) private var modelContext
|
|
@Environment(\.dismiss) private var dismiss
|
|
@Bindable var viewModel: RecordingViewModel
|
|
|
|
var body: some View {
|
|
NavigationStack {
|
|
VStack(spacing: 40) {
|
|
Spacer()
|
|
|
|
timerDisplay
|
|
|
|
recordButton
|
|
|
|
Spacer()
|
|
|
|
if let error = viewModel.error {
|
|
Text(error.localizedDescription)
|
|
.font(.caption)
|
|
.foregroundStyle(.red)
|
|
.multilineTextAlignment(.center)
|
|
.padding(.horizontal)
|
|
}
|
|
}
|
|
.padding()
|
|
.navigationTitle(String(localized: "recording.title"))
|
|
.navigationBarTitleDisplayMode(.inline)
|
|
.toolbar {
|
|
ToolbarItem(placement: .cancellationAction) {
|
|
Button(String(localized: "general.done")) {
|
|
if viewModel.isRecording {
|
|
viewModel.stopRecording(context: modelContext)
|
|
}
|
|
dismiss()
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private var timerDisplay: some View {
|
|
Text(viewModel.formattedDuration)
|
|
.font(.system(size: 64, weight: .light, design: .monospaced))
|
|
.foregroundStyle(viewModel.isRecording ? .primary : .secondary)
|
|
.contentTransition(.numericText())
|
|
.animation(.default, value: viewModel.formattedDuration)
|
|
.accessibilityLabel(String(localized: "recording.duration"))
|
|
}
|
|
|
|
private var recordButton: some View {
|
|
Button {
|
|
if viewModel.isRecording {
|
|
viewModel.stopRecording(context: modelContext)
|
|
} else {
|
|
viewModel.startRecording()
|
|
}
|
|
} label: {
|
|
ZStack {
|
|
Circle()
|
|
.fill(viewModel.isRecording ? .red : .red.opacity(0.15))
|
|
.frame(width: 88, height: 88)
|
|
|
|
if viewModel.isRecording {
|
|
RoundedRectangle(cornerRadius: 6)
|
|
.fill(.white)
|
|
.frame(width: 28, height: 28)
|
|
} else {
|
|
Circle()
|
|
.fill(.red)
|
|
.frame(width: 72, height: 72)
|
|
}
|
|
}
|
|
}
|
|
.accessibilityLabel(
|
|
viewModel.isRecording
|
|
? String(localized: "recording.stop")
|
|
: String(localized: "recording.start")
|
|
)
|
|
.sensoryFeedback(.impact, trigger: viewModel.isRecording)
|
|
}
|
|
}
|
|
|
|
#Preview {
|
|
RecordingView(viewModel: RecordingViewModel())
|
|
}
|