feat: Adds airflow at pressure feature.
This commit is contained in:
189
Sources/AirflowAtPressureFeature/AirflowAtPressure.swift
Normal file
189
Sources/AirflowAtPressureFeature/AirflowAtPressure.swift
Normal file
@@ -0,0 +1,189 @@
|
||||
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()
|
||||
}
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user