From 1487ecb3f410955b9baa00fe82609aadceed0f69 Mon Sep 17 00:00:00 2001 From: Javier Cicchelli Date: Fri, 3 Jul 2026 18:52:21 +0200 Subject: [PATCH] Implemented the RecordingView view in the Recording package target. --- .../Sources/Recording/RecordingView.swift | 88 +++++++++++++++++++ 1 file changed, 88 insertions(+) create mode 100644 Packages/Features/Sources/Recording/RecordingView.swift diff --git a/Packages/Features/Sources/Recording/RecordingView.swift b/Packages/Features/Sources/Recording/RecordingView.swift new file mode 100644 index 0000000..d4a6219 --- /dev/null +++ b/Packages/Features/Sources/Recording/RecordingView.swift @@ -0,0 +1,88 @@ +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() +}