feat: Reverts static pressure.

This commit is contained in:
2024-06-18 14:09:29 -04:00
parent 710811878d
commit 4e66a516e0
4 changed files with 120 additions and 288 deletions

View File

@@ -5,7 +5,6 @@ import SharedModels
import Styleguide
import SwiftUI
#warning("Revert rated static pressures section.")
@Reducer
public struct EquipmentSettingsForm {
@@ -24,56 +23,50 @@ public struct EquipmentSettingsForm {
@ObservableState
public struct State: Equatable {
@Presents public var destination: Destination.State?
public var includesFilterDrop: Bool
public var equipmentType: EquipmentMeasurement.EquipmentType
public var focusedField: Field? = nil
@Shared public var sharedSettings: SharedPressureEstimationState
public var ratedStaticPressures: RatedStaticPressuresSection.State
public var maxStaticPressure: Double?
public var minStaticPressure: Double?
public var ratedStaticPressure: Double?
public init(
destination: Destination.State? = nil,
includesFilterDrop: Bool = false,
equipmentType: EquipmentMeasurement.EquipmentType = .airHandler,
sharedSettings: Shared<SharedPressureEstimationState>
) {
self.destination = destination
self.includesFilterDrop = includesFilterDrop
self.equipmentType = equipmentType
self._sharedSettings = sharedSettings
self.ratedStaticPressures = .init(staticPressures: sharedSettings.equipmentMetadata.ratedStaticPressures)
self.maxStaticPressure = sharedSettings.ratedStaticPressures.maximum.wrappedValue
self.minStaticPressure = sharedSettings.ratedStaticPressures.minimum.wrappedValue
self.ratedStaticPressure = sharedSettings.ratedStaticPressures.rated.wrappedValue
}
private var equipmentTypeValidation: Bool {
guard equipmentType == .furnaceAndCoil else { return true }
return sharedSettings.heatingCapacity != nil
}
private var filterDropValidation: Bool {
guard includesFilterDrop else { return true }
return sharedSettings.manufacturersIncludedFilterPressureDrop != nil
private var staticPressuresValidation: Bool {
maxStaticPressure != nil
&& minStaticPressure != nil
&& ratedStaticPressure != nil
}
public var isValid: Bool {
ratedStaticPressures.isValid
&& equipmentTypeValidation
&& filterDropValidation
equipmentTypeValidation
&& staticPressuresValidation
}
// Note: These need to be in display order.
public enum Field: Hashable, CaseIterable, FocusableField, Identifiable {
case heatingCapacity
case ratedStaticPressure(RatedStaticPressuresSection.State.FocusedField)
case maxStaticPressure
case minStaticPressure
case ratedStaticPressure
case manufacturersIncludedFilterPressureDrop
public static var allCases: [EquipmentSettingsForm.State.Field] {
[
.heatingCapacity,
.ratedStaticPressure(.maximum),
.ratedStaticPressure(.minimum),
.ratedStaticPressure(.rated),
.manufacturersIncludedFilterPressureDrop
]
}
public var id: Self { self }
}
@@ -82,7 +75,6 @@ public struct EquipmentSettingsForm {
public enum Action: BindableAction, ViewAction {
case binding(BindingAction<State>)
case destination(PresentationAction<Destination.Action>)
case ratedStaticPressures(RatedStaticPressuresSection.Action)
case view(View)
@CasePathable
@@ -95,21 +87,19 @@ public struct EquipmentSettingsForm {
public var body: some Reducer<State, Action> {
BindingReducer()
Scope(state: \.ratedStaticPressures, action: \.ratedStaticPressures) {
RatedStaticPressuresSection()
}
Reduce<State, Action> { state, action in
switch action {
case .binding(\.includesFilterDrop):
guard state.includesFilterDrop else {
state.sharedSettings.manufacturersIncludedFilterPressureDrop = nil
return .none
}
guard state.sharedSettings.manufacturersIncludedFilterPressureDrop != nil else {
return .none
}
state.sharedSettings.manufacturersIncludedFilterPressureDrop = 0.1
case .binding(\.maxStaticPressure):
handleStaticPressure(\.maximum, \.maxStaticPressure, &state)
return .none
case .binding(\.minStaticPressure):
handleStaticPressure(\.minimum, \.minStaticPressure, &state)
return .none
case .binding(\.ratedStaticPressure):
handleStaticPressure(\.rated, \.ratedStaticPressure, &state)
return .none
case .binding:
@@ -122,17 +112,9 @@ public struct EquipmentSettingsForm {
case .destination:
return .none
case .ratedStaticPressures(.delegate(.infoButtonTapped)):
state.destination = .infoView(.init(view: .ratedStaticPressures))
return .none
case .ratedStaticPressures:
return .none
case let .view(action):
switch action {
case let .infoButtonTapped(infoView):
state.destination = .infoView(.init(view: infoView))
return .none
@@ -150,6 +132,17 @@ public struct EquipmentSettingsForm {
}
.ifLet(\.$destination, action: \.destination)
}
private func handleStaticPressure(
_ staticKeyPath: WritableKeyPath<RatedStaticPressures, Double>,
_ stateKeyPath: KeyPath<State, Double?>,
_ state: inout State
) {
let value = state[keyPath: stateKeyPath]
state.sharedSettings.equipmentMetadata.ratedStaticPressures[keyPath: staticKeyPath] = value ?? 0
}
}
@ViewAction(for: EquipmentSettingsForm.self)
@@ -219,30 +212,68 @@ public struct EquipmentSettingsFormView: View {
}
}
RatedStaticPressuresSectionView(
store: store.scope(state: \.ratedStaticPressures, action: \.ratedStaticPressures)
)
Section {
Grid(alignment: .leading, horizontalSpacing: 40) {
GridRow {
TextLabel("Maximum")
TextField(
"Maximum",
value: $store.maxStaticPressure,
fractionLength: 2,
prompt: Text("Maximum Pressure")
)
.decimalPad()
.focused($focusedField, equals: .maxStaticPressure)
}
GridRow {
TextLabel("Minimum")
TextField(
"Minimum",
value: $store.minStaticPressure,
fractionLength: 2,
prompt: Text("Minimum Pressure")
)
.decimalPad()
.focused($focusedField, equals: .minStaticPressure)
}
GridRow {
TextLabel("Rated")
TextField(
"Rated",
value: $store.ratedStaticPressure,
fractionLength: 2,
prompt: Text("Rated Pressure")
)
.decimalPad()
.focused($focusedField, equals: .ratedStaticPressure)
}
}
.dynamicBottomPadding()
} header: {
header("Rated Static Pressures", infoView: .ratedStaticPressures)
}
Section {
VStack(alignment: .leading) {
HStack {
TextLabel("Includes Filter Drop")
Spacer()
Toggle("Includes Filter Drop", isOn: $store.includesFilterDrop)
}
if store.includesFilterDrop {
// HStack {
// TextLabel("Includes Filter Drop")
// Spacer()
// Toggle("Includes Filter Drop", isOn: $store.includesFilterDrop)
// }
// if store.includesFilterDrop {
HStack {
TextLabel("Filter Drop")
Spacer()
textField(
"Filter Drop",
"Optional",
value: $store.sharedSettings.manufacturersIncludedFilterPressureDrop
)
.focused($focusedField, equals: .manufacturersIncludedFilterPressureDrop)
.decimalPad()
.padding(.leading, 40)
}
}
// }
}
} header: {
header(infoView: .manufacturersIncludedFilterPressureDrop) {

View File

@@ -1,147 +0,0 @@
import ComposableArchitecture
import SharedModels
import Styleguide
import SwiftUI
/// Allows for rated static pressure fields to be optional values, setting their corresponding shared state values to zero when they're nilled out.
///
@Reducer
public struct RatedStaticPressuresSection {
public init() { }
@ObservableState
public struct State: Equatable {
@Shared public var staticPressures: RatedStaticPressures
public var focusedField: FocusedField?
public var maxPressure: Double?
public var minPressure: Double?
public var ratedPressure: Double?
public init(
staticPressures: Shared<RatedStaticPressures>
) {
self._staticPressures = staticPressures
self.maxPressure = staticPressures.maximum.wrappedValue
self.minPressure = staticPressures.minimum.wrappedValue
self.ratedPressure = staticPressures.rated.wrappedValue
}
public var isValid: Bool {
maxPressure != nil
&& minPressure != nil
&& ratedPressure != nil
}
public enum FocusedField: String, Hashable, CaseIterable, FocusableField {
case maximum
case minimum
case rated
public var label: String { rawValue.capitalized }
public var prompt: String { "\(label) Pressure"}
}
}
public enum Action: BindableAction {
case binding(BindingAction<State>)
case delegate(DelegateAction)
public enum DelegateAction {
case infoButtonTapped
}
}
public var body: some Reducer<State, Action> {
BindingReducer()
Reduce<State, Action> { state, action in
switch action {
case .binding(\.maxPressure):
state.staticPressures.maximum = state.maxPressure ?? 0
return .none
case .binding(\.minPressure):
state.staticPressures.minimum = state.minPressure ?? 0
return .none
case .binding(\.ratedPressure):
state.staticPressures.rated = state.ratedPressure ?? 0
return .none
case .binding:
return .none
case .delegate:
return .none
}
}
}
}
public struct RatedStaticPressuresSectionView: View {
@FocusState private var focusedField: RatedStaticPressuresSection.State.FocusedField?
@Bindable var store: StoreOf<RatedStaticPressuresSection>
public init(store: StoreOf<RatedStaticPressuresSection>) {
self.store = store
}
public var body: some View {
Section {
Grid(alignment: .leading, horizontalSpacing: 40) {
GridRow {
label(for: .maximum)
TextField(
"Maximum",
value: $store.maxPressure,
fractionLength: 2,
prompt: prompt(for: .maximum)
)
.decimalPad()
.focused($focusedField, equals: .maximum)
}
GridRow {
label(for: .minimum)
TextField(
"Minimum",
value: $store.minPressure,
fractionLength: 2,
prompt: prompt(for: .minimum)
)
.decimalPad()
.focused($focusedField, equals: .minimum)
}
GridRow {
label(for: .rated)
TextField(
"Rated",
value: $store.ratedPressure,
fractionLength: 2,
prompt: prompt(for: .rated)
)
.decimalPad()
.focused($focusedField, equals: .rated)
}
}
.dynamicBottomPadding()
} header: {
HStack {
SectionHeaderLabel("Rated Static Pressures")
Spacer()
InfoButton { store.send(.delegate(.infoButtonTapped)) }
}
}
.bind($focusedField, to: $store.focusedField)
}
private func label(for field: RatedStaticPressuresSection.State.FocusedField) -> some View {
TextLabel(field.label)
}
private func prompt(for field: RatedStaticPressuresSection.State.FocusedField) -> Text {
Text(field.prompt)
}
}