Skip to content

Commit

Permalink
support clear button while editing, move display logic to viewmodel
Browse files Browse the repository at this point in the history
  • Loading branch information
mike-dydx committed May 2, 2024
1 parent 5fc6a38 commit 50b4384
Show file tree
Hide file tree
Showing 4 changed files with 54 additions and 44 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ private protocol dydxTakeProfitStopLossViewPresenterProtocol: HostedViewPresente
private class dydxTakeProfitStopLossViewPresenter: HostedViewPresenter<dydxTakeProfitStopLossViewModel>, dydxTakeProfitStopLossViewPresenterProtocol {
fileprivate var marketId: String?
@SynchronizedLock private var pendingOrders: Int?

deinit {
clearTriggersInput()
}
Expand Down Expand Up @@ -180,13 +180,13 @@ private class dydxTakeProfitStopLossViewPresenter: HostedViewPresenter<dydxTakeP

let formattedTakeProfitUsdcDiff = dydxFormatter.shared.raw(number: triggerOrdersInput?.takeProfitOrder?.price?.usdcDiff?.doubleValue, digits: 2) ?? ""
let formattedTakeProfitUsdcPercentage = dydxFormatter.shared.raw(number: triggerOrdersInput?.takeProfitOrder?.price?.percentDiff?.doubleValue, digits: 2) ?? ""
viewModel?.takeProfitStopLossInputAreaViewModel?.gainInputViewModel?.dollars = formattedTakeProfitUsdcDiff
viewModel?.takeProfitStopLossInputAreaViewModel?.gainInputViewModel?.percentage = formattedTakeProfitUsdcPercentage
viewModel?.takeProfitStopLossInputAreaViewModel?.gainInputViewModel?.set(value: formattedTakeProfitUsdcDiff, forUnit: .dollars)
viewModel?.takeProfitStopLossInputAreaViewModel?.gainInputViewModel?.set(value: formattedTakeProfitUsdcPercentage, forUnit: .percentage)

let formattedStopLossUsdcDiff = dydxFormatter.shared.raw(number: triggerOrdersInput?.stopLossOrder?.price?.usdcDiff?.doubleValue, digits: 2) ?? ""
let formattedStopLossUsdcPercentage = dydxFormatter.shared.raw(number: triggerOrdersInput?.stopLossOrder?.price?.percentDiff?.doubleValue, digits: 2) ?? ""
viewModel?.takeProfitStopLossInputAreaViewModel?.lossInputViewModel?.dollars = formattedStopLossUsdcDiff
viewModel?.takeProfitStopLossInputAreaViewModel?.lossInputViewModel?.percentage = formattedStopLossUsdcPercentage
viewModel?.takeProfitStopLossInputAreaViewModel?.lossInputViewModel?.set(value: formattedStopLossUsdcDiff, forUnit: .dollars)
viewModel?.takeProfitStopLossInputAreaViewModel?.lossInputViewModel?.set(value: formattedStopLossUsdcPercentage, forUnit: .percentage)

// logic primarily to pre-populate custom amount. need to account 3 situations: 1 take profit order or 1 stop loss order or both
if let customSize = triggerOrdersInput?.size?.doubleValue.magnitude, customSize != position?.size?.current?.doubleValue.magnitude {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -427,7 +427,7 @@ extension AbacusStateManager {
case success
case failed(Abacus.ParsingError?)
}

/// places the currently drafted trigger order(s)
/// - Returns: the number of resulting cancel orders + place order requests
public func placeTriggerOrders(callback: @escaping ((SubmissionStatus) -> Void)) -> Int? {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand All @@ -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 {
Expand All @@ -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 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down

0 comments on commit 50b4384

Please sign in to comment.