import Recording import SwiftUI /// The root view of the Attendi sample app. /// /// The view hosts the `RecordingView` feature from the `Recording` target of the `Features` package, which drives the whole recording flow, /// inside a navigation stack, and presents the transcribed text of every processed recording in a modal sheet of its own navigation stack. /// /// The services attached to the feature and the transcription shown in the sheet live in the view's ``Model``; the view itself only renders it and /// forwards the feature's output and the sheet's dismissal. The navigation titles of the stacks are localized through the app's string catalog. struct ContentView: View { // MARK: Properties /// The model that owns the attached services and the transcription presented in the modal sheet. @State private var model = Model() // MARK: Body /// The content of the view: a navigation stack with the recording feature's view, attached to the model's services, with a toolbar picker /// for the locale of the spoken language to transcribe, and a modal sheet presenting the transcribed text of every processed recording — /// or a content unavailable message when the transcription is empty — closable through its toolbar button or a swipe. var body: some View { NavigationStack { RecordingView( capturer: model.capturer, transcriber: model.transcriber, locale: $model.locale, ) { transcription in model.received(transcription) } .navigationTitle("view.recording.navigation.title") #if !os(macOS) .navigationBarTitleDisplayMode(.inline) #endif .toolbar { ToolbarItem(placement: .primaryAction) { Menu { Picker( "view.recording.picker.locale.title", selection: $model.locale ) { ForEach( model.locales, id: \.self ) { Text(model.name(for: $0)) .tag($0) } } .pickerStyle(.inline) } label: { Label( "view.recording.picker.locale.title", systemImage: "globe" ) } .disabled(model.locales.isEmpty) } } .task { await model.load() } } .sheet(item: $model.transcription) { transcription in NavigationStack { Group { if transcription.isEmpty { ContentUnavailableView( "view.transcription.unavailable.title", systemImage: "text.page.slash", description: Text("view.transcription.unavailable.description") ) } else { ScrollView { Text(transcription.text) .font(.body) .fontWeight(.regular) .foregroundStyle(.primary) .frame( maxWidth: .infinity, alignment: .leading ) .padding() } } } .navigationTitle("view.transcription.navigation.title") #if !os(macOS) .navigationBarTitleDisplayMode(.inline) #endif .toolbar { Button(role: .close) { model.dismissed() } } } .presentationDetents([.medium, .large]) } } } // MARK: - Previews #Preview( "Content view" ) { ContentView() }