190 lines
4.6 KiB
Swift
190 lines
4.6 KiB
Swift
import ComposableArchitecture
|
|
import EstimatedPressureDependency
|
|
import SharedModels
|
|
import Styleguide
|
|
import SwiftUI
|
|
|
|
@Reducer
|
|
public struct AirflowAtPressureFeature {
|
|
|
|
@ObservableState
|
|
public struct State: Equatable {
|
|
public var isPresentingInfoView: Bool = false
|
|
public var existingAirflow: Double?
|
|
public var existingPressure: Double?
|
|
public var targetPressure: Double?
|
|
public var calculatedAirflow: Positive<Double>?
|
|
|
|
public init(
|
|
existingAirflow: Double? = nil,
|
|
existingPressure: Double? = nil,
|
|
targetPressure: Double? = 0.5,
|
|
calculatedAirflow: Positive<Double>? = nil
|
|
) {
|
|
self.existingAirflow = existingAirflow
|
|
self.existingPressure = existingPressure
|
|
self.targetPressure = targetPressure
|
|
self.calculatedAirflow = calculatedAirflow
|
|
}
|
|
|
|
public var isValid: Bool {
|
|
existingAirflow != nil
|
|
&& existingPressure != nil
|
|
&& targetPressure != nil
|
|
}
|
|
}
|
|
|
|
public enum Action: BindableAction, ViewAction {
|
|
case binding(BindingAction<State>)
|
|
case receive(ReceiveAction)
|
|
case view(View)
|
|
|
|
@CasePathable
|
|
public enum ReceiveAction {
|
|
case calculatedAirflow(Positive<Double>?)
|
|
}
|
|
|
|
@CasePathable
|
|
public enum View {
|
|
case dismissInfoViewButtonTapped
|
|
case infoButtonTapped
|
|
case resetButtonTapped
|
|
case submit
|
|
}
|
|
}
|
|
|
|
|
|
@Dependency(\.estimatedPressuresClient) var estimatedPressuresClient
|
|
|
|
public var body: some Reducer<State, Action> {
|
|
BindingReducer()
|
|
Reduce<State, Action> { state, action in
|
|
switch action {
|
|
case .binding:
|
|
return .none
|
|
|
|
case let .receive(action):
|
|
switch action {
|
|
case let .calculatedAirflow(airflow):
|
|
state.calculatedAirflow = airflow
|
|
return .none
|
|
}
|
|
|
|
case let .view(action):
|
|
switch action {
|
|
case .dismissInfoViewButtonTapped:
|
|
state.isPresentingInfoView = false
|
|
return .none
|
|
|
|
case .infoButtonTapped:
|
|
state.isPresentingInfoView = true
|
|
return .none
|
|
|
|
case .resetButtonTapped:
|
|
state = .init()
|
|
return .none
|
|
|
|
case .submit:
|
|
guard state.isValid else { return .none }
|
|
return .run { [state] send in
|
|
await send(
|
|
.receive(.calculatedAirflow(
|
|
try? await estimatedPressuresClient.estimatedAirflow(.init(
|
|
existingAirflow: state.existingAirflow ?? 0,
|
|
existingPressure: state.existingPressure ?? 0,
|
|
targetPressure: state.targetPressure ?? 0
|
|
))
|
|
))
|
|
)
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
@ViewAction(for: AirflowAtPressureFeature.self)
|
|
public struct AirflowAtPressureView: View {
|
|
|
|
@Perception.Bindable
|
|
public var store: StoreOf<AirflowAtPressureFeature>
|
|
|
|
public init(store: StoreOf<AirflowAtPressureFeature>) {
|
|
self.store = store
|
|
}
|
|
|
|
public var body: some View {
|
|
Form {
|
|
Section {
|
|
TextField(
|
|
"Existing Airflow",
|
|
value: $store.existingAirflow,
|
|
format: .number,
|
|
prompt: Text("Airflow")
|
|
)
|
|
.onSubmit { send(.submit) }
|
|
.numberPad()
|
|
|
|
TextField(
|
|
"Existing Pressure",
|
|
value: $store.existingPressure,
|
|
format: .number,
|
|
prompt: Text("Existing Pressure")
|
|
)
|
|
.onSubmit { send(.submit) }
|
|
.decimalPad()
|
|
|
|
TextField(
|
|
"Target Pressure",
|
|
value: $store.targetPressure,
|
|
format: .number,
|
|
prompt: Text("Target Pressure")
|
|
)
|
|
.onSubmit { send(.submit) }
|
|
.decimalPad()
|
|
|
|
} header: {
|
|
HStack {
|
|
Text("Inputs")
|
|
Spacer()
|
|
InfoButton { send(.infoButtonTapped) }
|
|
}
|
|
.labelStyle(.iconOnly)
|
|
} footer: {
|
|
HStack {
|
|
Spacer()
|
|
Button("Reset") { send(.resetButtonTapped) }
|
|
Spacer()
|
|
}
|
|
.padding(.top)
|
|
.buttonStyle(.borderedProminent)
|
|
}
|
|
|
|
Section {
|
|
if let airflow = store.calculatedAirflow {
|
|
Text(airflow.positiveValue, format: .number.precision(.fractionLength(0)))
|
|
}
|
|
} header: {
|
|
Text("Calculated Airflow")
|
|
}
|
|
}
|
|
.sheet(isPresented: $store.isPresentingInfoView) {
|
|
NavigationStack {
|
|
AirflowAtPressureInfoView()
|
|
.toolbar {
|
|
Button("Done") { send(.dismissInfoViewButtonTapped) }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#Preview {
|
|
AirflowAtPressureView(
|
|
store: Store(initialState: AirflowAtPressureFeature.State()) {
|
|
AirflowAtPressureFeature()
|
|
}
|
|
)
|
|
}
|