From 16cbdeb82ff2423ec7e7ef020d8fea7a5d7bf89d Mon Sep 17 00:00:00 2001 From: mike-dydx Date: Thu, 2 May 2024 14:55:21 -0400 Subject: [PATCH] support clear button while editing, move display logic to viewmodel --- .../dydxTakeProfitStopLossViewPresenter.swift | 10 +-- .../dydxStateManager/AbacusStateManager.swift | 2 +- .../dydxGainLossInputViewModel.swift | 83 ++++++++++--------- ...TakeProfitStopLossInputAreaViewModel.swift | 3 +- 4 files changed, 54 insertions(+), 44 deletions(-) diff --git a/dydx/dydxPresenters/dydxPresenters/_v4/TakeProfitStopLoss/dydxTakeProfitStopLossViewPresenter.swift b/dydx/dydxPresenters/dydxPresenters/_v4/TakeProfitStopLoss/dydxTakeProfitStopLossViewPresenter.swift index c2ebbd18c..779250772 100644 --- a/dydx/dydxPresenters/dydxPresenters/_v4/TakeProfitStopLoss/dydxTakeProfitStopLossViewPresenter.swift +++ b/dydx/dydxPresenters/dydxPresenters/_v4/TakeProfitStopLoss/dydxTakeProfitStopLossViewPresenter.swift @@ -44,7 +44,7 @@ private protocol dydxTakeProfitStopLossViewPresenterProtocol: HostedViewPresente private class dydxTakeProfitStopLossViewPresenter: HostedViewPresenter, dydxTakeProfitStopLossViewPresenterProtocol { fileprivate var marketId: String? @SynchronizedLock private var pendingOrders: Int? - + deinit { clearTriggersInput() } @@ -180,13 +180,13 @@ private class dydxTakeProfitStopLossViewPresenter: HostedViewPresenter Void)) -> Int? { diff --git a/dydx/dydxViews/dydxViews/_v4/TakeProfitStopLoss/Components/dydxGainLossInputViewModel.swift b/dydx/dydxViews/dydxViews/_v4/TakeProfitStopLoss/Components/dydxGainLossInputViewModel.swift index a35b8c9cb..a8e05a422 100644 --- a/dydx/dydxViews/dydxViews/_v4/TakeProfitStopLoss/Components/dydxGainLossInputViewModel.swift +++ b/dydx/dydxViews/dydxViews/_v4/TakeProfitStopLoss/Components/dydxGainLossInputViewModel.swift @@ -30,25 +30,49 @@ public class dydxGainLossInputViewModel: PlatformViewModeling { @Published fileprivate var isFocused: Bool = false @Published fileprivate var triggerType: dydxTakeProfitStopLossInputAreaModel.TriggerType - @Published fileprivate var curUnit: Unit = .percentage + @Published fileprivate var curUnit: Unit = .percentage { + didSet { + updateDisplayText() + } + } + @Published fileprivate var onEdited: ((String?, Unit) -> Void)? @Published fileprivate var isPresentingUnitOptions: Bool = false - @Published public var dollars: String = "" - @Published public var percentage: String = "" + /// text that is edited by user (or in some cases, programmatically) + @Published private var dollars: String = "" + @Published private var percentage: String = "" + + @Published fileprivate var displayText: String = "" + + /// sets the value text for the specified unit, but will not override active edit if user has this input focused + public func set(value: String, forUnit unit: Unit) { + switch unit { + case .dollars: + dollars = value + case .percentage: + percentage = value + } + + if !isFocused { + updateDisplayText() + } + } - fileprivate var displayText: String { + private func updateDisplayText() { switch curUnit { case .dollars: - return dollars + displayText = dollars case .percentage: - return percentage + displayText = percentage } } + /// force clear the texts, even if user is actively editing public func clear() { - dollars = "" - percentage = "" + set(value: "", forUnit: .dollars) + set(value: "", forUnit: .percentage) + displayText = "" } public init(triggerType: dydxTakeProfitStopLossInputAreaModel.TriggerType, onEdited: ((String?, Unit) -> Void)? = nil) { @@ -65,17 +89,10 @@ public class dydxGainLossInputViewModel: PlatformViewModeling { struct dydxGainLossInputView: View { @FocusState private var isFocused: Bool - @State private var text: String @ObservedObject private var viewModel: dydxGainLossInputViewModel fileprivate init(viewModel: dydxGainLossInputViewModel) { self.viewModel = viewModel - switch viewModel.curUnit { - case .dollars: - text = viewModel.dollars - case .percentage: - text = viewModel.percentage - } } var header: some View { @@ -91,39 +108,31 @@ struct dydxGainLossInputView: View { } var textInput: some View { - let textField = TextField("", text: $text, prompt: placeholder) + let textField = TextField("", text: $viewModel.displayText, prompt: placeholder) .themeFont(fontType: .number, fontSize: .large) .themeColor(foreground: .textPrimary) .keyboardType(.decimalPad) .focused($isFocused) if #available(iOS 17.0, *) { return textField - .onChange(of: text) { _, newValue in - // only propagate updates if they are from user editing - guard isFocused else { return } - viewModel.onEdited?(newValue, viewModel.curUnit) - } - .onChange(of: viewModel.displayText) { _, newValue in - // do not overwrite user entry while user is editing - guard !isFocused else { return } - text = newValue - } - .onChange(of: isFocused) { _, newValue in - viewModel.isFocused = newValue - viewModel.onEdited?(text, viewModel.curUnit) - } + .onChange(of: viewModel.displayText) { displayTextOnChange(newValue: $1) } + .onChange(of: isFocused) { isFocusedOnChange(newValue: $1) } } else { return textField - .onChange(of: text) { newValue in - viewModel.onEdited?(newValue, viewModel.curUnit) - } - .onChange(of: isFocused) { newValue in - viewModel.isFocused = newValue - viewModel.onEdited?(text, viewModel.curUnit) - } + .onChange(of: viewModel.displayText) { displayTextOnChange(newValue: $0) } + .onChange(of: isFocused) { isFocusedOnChange(newValue: $0) } } } + private func displayTextOnChange(newValue: String) { + viewModel.onEdited?(newValue, viewModel.curUnit) + } + + private func isFocusedOnChange(newValue: Bool) { + viewModel.isFocused = newValue + viewModel.onEdited?(viewModel.displayText, viewModel.curUnit) + } + var displaySelector: some View { Button(action: { if !viewModel.isPresentingUnitOptions { diff --git a/dydx/dydxViews/dydxViews/_v4/TakeProfitStopLoss/Components/dydxTakeProfitStopLossInputAreaViewModel.swift b/dydx/dydxViews/dydxViews/_v4/TakeProfitStopLoss/Components/dydxTakeProfitStopLossInputAreaViewModel.swift index 04228a177..a67277bbd 100644 --- a/dydx/dydxViews/dydxViews/_v4/TakeProfitStopLoss/Components/dydxTakeProfitStopLossInputAreaViewModel.swift +++ b/dydx/dydxViews/dydxViews/_v4/TakeProfitStopLoss/Components/dydxTakeProfitStopLossInputAreaViewModel.swift @@ -100,11 +100,12 @@ public class dydxTakeProfitStopLossInputAreaModel: PlatformViewModel { private func createClearButton(triggerType: dydxTakeProfitStopLossInputAreaModel.TriggerType) -> AnyView? { guard let numOrders = triggerType == .takeProfit ? numOpenTakeProfitOrders : numOpenStopLossOrders else { return nil } - if numOrders == 1 { + if numOrders <= 1 { return Text(localizerPathKey: "APP.GENERAL.CLEAR") .themeFont(fontType: .base, fontSize: .medium) .themeColor(foreground: .colorRed) .onTapGesture { [weak self] in + PlatformView.hideKeyboard() switch triggerType { case .takeProfit: self?.takeProfitPriceInputViewModel?.onEdited?(nil)