89 lines
2.5 KiB
Swift
89 lines
2.5 KiB
Swift
import SwiftUI
|
|||
|
|
|
||
|
|
/// A view that drives a recording session.
|
||
|
|
///
|
||
|
|
/// The view shows a main button that starts, pauses, and resumes a recording, with a label above it displaying the elapsed recording time.
|
||
|
|
/// While paused, a send button lets the user submit the recording for processing, showing a progress indicator until the processing finishes.
|
||
|
|
///
|
||
|
|
/// All state and control behavior lives in the view's ``Model``; the view itself only renders it and forwards button presses and state changes.
|
||
|
|
public struct RecordingView: View {
|
||
|
|
|
||
|
|
// MARK: Properties
|
||
|
|
|
||
|
|
/// The model that owns the recording state and drives the view's controls.
|
||
|
|
@State private var model: Model
|
||
|
|
|
||
|
|
// MARK: Initializers
|
||
|
|
|
||
|
|
/// Creates a recording view in the not-recording state.
|
||
|
|
public init() {
|
||
|
|
self.model = .init()
|
||
|
|
}
|
||
|
|
|
||
|
|
// MARK: Body
|
||
|
|
|
||
|
|
/// The content of the view: the timer label above the main and send buttons, with every change of the model's state forwarded back
|
||
|
|
/// to ``Model/updatedState(shouldRestartTimer:)``.
|
||
|
|
public var body: some View {
|
||
|
|
VStack(
|
||
|
|
spacing: Constant.Spacing.stack
|
||
|
|
) {
|
||
|
|
if model.shouldShowTimer {
|
||
|
|
Text(model.timerText)
|
||
|
|
}
|
||
|
|
|
||
|
|
HStack(
|
||
|
|
spacing: Constant.Spacing.stack
|
||
|
|
) {
|
||
|
|
Button {
|
||
|
|
model.pressedMain()
|
||
|
|
} label: {
|
||
|
|
Image(model.iconMain)
|
||
|
|
}
|
||
|
|
.disabled(model.shouldDisableMain)
|
||
|
|
|
||
|
|
if model.shouldShowSend {
|
||
|
|
Button {
|
||
|
|
model.pressedSend()
|
||
|
|
} label: {
|
||
|
|
if let icon = model.iconSend {
|
||
|
|
Image(icon)
|
||
|
|
} else {
|
||
|
|
ProgressView()
|
||
|
|
}
|
||
|
|
}
|
||
|
|
.disabled(model.isProcessing)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
.onChange(
|
||
|
|
of: model.state,
|
||
|
|
initial: false
|
||
|
|
) { oldValue, _ in
|
||
|
|
model.updatedState(
|
||
|
|
shouldRestartTimer: oldValue == .notRecording
|
||
|
|
)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
}
|
||
|
|
|
||
|
|
// MARK: - Constants
|
||
|
|
|
||
|
|
/// The constant values used across the view.
|
||
|
|
private enum Constant {
|
||
|
|
/// The spacing constants.
|
||
|
|
enum Spacing {
|
||
|
|
/// The spacing between the elements of a stack.
|
||
|
|
static let stack: CGFloat = 16
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// MARK: - Previews
|
||
|
|
|
||
|
|
#Preview(
|
||
|
|
"Recording view"
|
||
|
|
) {
|
||
|
|
RecordingView()
|
||
|
|
}
|