From 0a264aaa9509f5ec6c63f98632839b7fd6129dc1 Mon Sep 17 00:00:00 2001 From: Rui Date: Tue, 7 May 2024 14:43:51 -0700 Subject: [PATCH 1/2] MOB-483 Add isolated / cross margin mode button, Add target leverage button --- .../dydxPresenters.xcodeproj/project.pbxproj | 20 ++++ .../_Features/routing_swiftui.json | 8 ++ .../Margin/dydxMarginModeViewBuilder.swift | 43 +++++++ .../dydxTargetLeverageViewBuilder.swift | 43 +++++++ .../dydxTradeInputMarginViewPresenter.swift | 40 +++++++ .../dydxTradeInputViewBuilder.swift | 5 +- .../dydxViews.xcodeproj/project.pbxproj | 20 ++++ .../Sections/dydxPortfolioPositionsView.swift | 4 +- .../_v4/Trade/Margin/dydxMarginModeView.swift | 65 +++++++++++ .../Trade/Margin/dydxTargetLeverageView.swift | 65 +++++++++++ .../dydxTradeInputMarginView.swift | 107 ++++++++++++++++++ .../Trade/TradeInput/dydxTradeInputView.swift | 48 ++++++-- 12 files changed, 454 insertions(+), 14 deletions(-) create mode 100644 dydx/dydxPresenters/dydxPresenters/_v4/Trade/Margin/dydxMarginModeViewBuilder.swift create mode 100644 dydx/dydxPresenters/dydxPresenters/_v4/Trade/Margin/dydxTargetLeverageViewBuilder.swift create mode 100644 dydx/dydxPresenters/dydxPresenters/_v4/Trade/TradeInput/Components/TradeInputFields/dydxTradeInputMarginViewPresenter.swift create mode 100644 dydx/dydxViews/dydxViews/_v4/Trade/Margin/dydxMarginModeView.swift create mode 100644 dydx/dydxViews/dydxViews/_v4/Trade/Margin/dydxTargetLeverageView.swift create mode 100644 dydx/dydxViews/dydxViews/_v4/Trade/TradeInput/Components/TradeInputFields/dydxTradeInputMarginView.swift diff --git a/dydx/dydxPresenters/dydxPresenters.xcodeproj/project.pbxproj b/dydx/dydxPresenters/dydxPresenters.xcodeproj/project.pbxproj index dea5459c9..ff6b180ee 100644 --- a/dydx/dydxPresenters/dydxPresenters.xcodeproj/project.pbxproj +++ b/dydx/dydxPresenters/dydxPresenters.xcodeproj/project.pbxproj @@ -36,6 +36,9 @@ 0238FE0A296EF91D002E1C1A /* dydxMarketPositionViewPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0238FE09296EF91D002E1C1A /* dydxMarketPositionViewPresenter.swift */; }; 0238FECB2970D681002E1C1A /* SharedAccountPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0238FECA2970D681002E1C1A /* SharedAccountPresenter.swift */; }; 0238FEED2972B5E8002E1C1A /* dydxTradeInputCtaButtonViewPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0238FEEC2972B5E8002E1C1A /* dydxTradeInputCtaButtonViewPresenter.swift */; }; + 023AB3B22BEACE14005230B2 /* dydxTradeInputMarginViewPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 023AB3B12BEACE14005230B2 /* dydxTradeInputMarginViewPresenter.swift */; }; + 023AB3C42BEAD56A005230B2 /* dydxMarginModeViewBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 023AB3C32BEAD56A005230B2 /* dydxMarginModeViewBuilder.swift */; }; + 023AB3C82BEAD5F3005230B2 /* dydxTargetLeverageViewBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 023AB3C72BEAD5F3005230B2 /* dydxTargetLeverageViewBuilder.swift */; }; 023DC88029CCBD21000DD920 /* dydxOnboardQRCodeViewBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 023DC87329CCBD21000DD920 /* dydxOnboardQRCodeViewBuilder.swift */; }; 02439CCB29B03EEE00A083FE /* dydxCartera.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 02439CC629B03ECD00A083FE /* dydxCartera.framework */; }; 0243A76129BE572C00A083FE /* dydxCancelOrderActionBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0243A76029BE572C00A083FE /* dydxCancelOrderActionBuilder.swift */; }; @@ -390,6 +393,9 @@ 0238FE09296EF91D002E1C1A /* dydxMarketPositionViewPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = dydxMarketPositionViewPresenter.swift; sourceTree = ""; }; 0238FECA2970D681002E1C1A /* SharedAccountPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SharedAccountPresenter.swift; sourceTree = ""; }; 0238FEEC2972B5E8002E1C1A /* dydxTradeInputCtaButtonViewPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = dydxTradeInputCtaButtonViewPresenter.swift; sourceTree = ""; }; + 023AB3B12BEACE14005230B2 /* dydxTradeInputMarginViewPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = dydxTradeInputMarginViewPresenter.swift; sourceTree = ""; }; + 023AB3C32BEAD56A005230B2 /* dydxMarginModeViewBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = dydxMarginModeViewBuilder.swift; sourceTree = ""; }; + 023AB3C72BEAD5F3005230B2 /* dydxTargetLeverageViewBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = dydxTargetLeverageViewBuilder.swift; sourceTree = ""; }; 023DC87329CCBD21000DD920 /* dydxOnboardQRCodeViewBuilder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = dydxOnboardQRCodeViewBuilder.swift; sourceTree = ""; }; 02439CC029B03ECD00A083FE /* dydxCartera.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = dydxCartera.xcodeproj; path = ../dydxCartera/dydxCartera.xcodeproj; sourceTree = ""; }; 0243A76029BE572C00A083FE /* dydxCancelOrderActionBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = dydxCancelOrderActionBuilder.swift; sourceTree = ""; }; @@ -568,6 +574,7 @@ 64A4DB9429664803008D8E20 /* dydxTradeInputEditPresenter.swift */, 270E7E232A5F6B1B00136793 /* dydxTradeInputSideViewPresenter.swift */, 64A4DB9A2966490C008D8E20 /* dydxTradeInputOrderTypePresenter.swift */, + 023AB3B12BEACE14005230B2 /* dydxTradeInputMarginViewPresenter.swift */, ); path = TradeInputFields; sourceTree = ""; @@ -739,6 +746,15 @@ path = Shared; sourceTree = ""; }; + 023AB3C22BEAD554005230B2 /* Margin */ = { + isa = PBXGroup; + children = ( + 023AB3C32BEAD56A005230B2 /* dydxMarginModeViewBuilder.swift */, + 023AB3C72BEAD5F3005230B2 /* dydxTargetLeverageViewBuilder.swift */, + ); + path = Margin; + sourceTree = ""; + }; 023DC87229CCBD21000DD920 /* QRCode */ = { isa = PBXGroup; children = ( @@ -1417,6 +1433,7 @@ 6496DC3C295E121000174CE7 /* Trade */ = { isa = PBXGroup; children = ( + 023AB3C22BEAD554005230B2 /* Margin */, 024B44CB2982D1A700E35D54 /* TradeInput */, 024B44D22982D21900E35D54 /* TradeStatus */, ); @@ -1841,6 +1858,7 @@ 0258B9E72991BC900098E1BE /* dydxNewsAlertsViewBuilder.swift in Sources */, 277E908B2B2118AE005CCBCB /* dydxRewardsHistoryViewPresenter.swift in Sources */, 0230376F28C11BE600412B72 /* dydxMarketsViewBuilder.swift in Sources */, + 023AB3C42BEAD56A005230B2 /* dydxMarginModeViewBuilder.swift in Sources */, 270BA8F32A6F278F009212EA /* dydxDebugThemeViewerBuilder.swift in Sources */, 02669B952AD87A9D00A756AA /* dydxGlobalWorkers.swift in Sources */, 027CB28729EEFF910069781A /* dydxTransferStatusViewBuilder.swift in Sources */, @@ -1869,6 +1887,7 @@ 0258BA23299294BF0098E1BE /* dydxProfileViewBuilder.swift in Sources */, 0208631028F5186E00C9D3A0 /* OrderbookLineDataPoint.swift in Sources */, 026388DA2BB34C17006DD6E8 /* AnalyticsEvent.swift in Sources */, + 023AB3C82BEAD5F3005230B2 /* dydxTargetLeverageViewBuilder.swift in Sources */, 278A4D932B8FA5E8003898EB /* dydxRateAppViewBuilder.swift in Sources */, 0276FA992A0DB8FD000BDF0B /* Model+Ext.swift in Sources */, 6488BBC7296F2CD50096502F /* AbacusUtils.swift in Sources */, @@ -1879,6 +1898,7 @@ 02E90C5A29D62719004E2311 /* dydxFeatureFlagsViewBuilder.swift in Sources */, 6453AB26299D98110041A0C4 /* dydxClosePositionInputEditPresenter.swift in Sources */, 0243A76129BE572C00A083FE /* dydxCancelOrderActionBuilder.swift in Sources */, + 023AB3B22BEACE14005230B2 /* dydxTradeInputMarginViewPresenter.swift in Sources */, 026388D82BB34B7A006DD6E8 /* OnboardingAnalytics.swift in Sources */, 64A4DB9929664818008D8E20 /* dydxTradeReceiptPresenter.swift in Sources */, 0236F0CB2968793A00EB995F /* dydxPortfolioFillsViewPresenter.swift in Sources */, diff --git a/dydx/dydxPresenters/dydxPresenters/_Features/routing_swiftui.json b/dydx/dydxPresenters/dydxPresenters/_Features/routing_swiftui.json index 42976396a..e7645794b 100644 --- a/dydx/dydxPresenters/dydxPresenters/_Features/routing_swiftui.json +++ b/dydx/dydxPresenters/dydxPresenters/_Features/routing_swiftui.json @@ -307,6 +307,14 @@ "destination":"dydxPresenters.dydxTakeProfitStopLossViewBuilder", "presentation":"prompt" }, + "/trade/margin_type":{ + "destination":"dydxPresenters.dydxMarginModeViewBuilder", + "presentation":"prompt" + }, + "/trade/target_leverage":{ + "destination":"dydxPresenters.dydxTargetLeverageViewBuilder", + "presentation":"prompt" + }, "/transfer":{ "destination":"dydxPresenters.dydxTransferViewBuilder", "presentation":"prompt" diff --git a/dydx/dydxPresenters/dydxPresenters/_v4/Trade/Margin/dydxMarginModeViewBuilder.swift b/dydx/dydxPresenters/dydxPresenters/_v4/Trade/Margin/dydxMarginModeViewBuilder.swift new file mode 100644 index 000000000..211d89319 --- /dev/null +++ b/dydx/dydxPresenters/dydxPresenters/_v4/Trade/Margin/dydxMarginModeViewBuilder.swift @@ -0,0 +1,43 @@ +// +// dydxMarginModeViewBuilder.swift +// dydxPresenters +// +// Created by Rui Huang on 07/05/2024. +// + +import Utilities +import dydxViews +import PlatformParticles +import RoutingKit +import ParticlesKit +import PlatformUI + +public class dydxMarginModeViewBuilder: NSObject, ObjectBuilderProtocol { + public func build() -> T? { + let presenter = dydxMarginModeViewPresenter() + let view = presenter.viewModel?.createView() ?? PlatformViewModel().createView() + return dydxMarginModeViewController(presenter: presenter, view: view, configuration: .default) as? T + // return HostingViewController(presenter: presenter, view: view) as? T + } +} + +private class dydxMarginModeViewController: HostingViewController { + override public func arrive(to request: RoutingRequest?, animated: Bool) -> Bool { + if request?.path == "/trade/margin_type" { + return true + } + return false + } +} + +private protocol dydxMarginModeViewPresenterProtocol: HostedViewPresenterProtocol { + var viewModel: dydxMarginModeViewModel? { get } +} + +private class dydxMarginModeViewPresenter: HostedViewPresenter, dydxMarginModeViewPresenterProtocol { + override init() { + super.init() + + viewModel = dydxMarginModeViewModel() + } +} diff --git a/dydx/dydxPresenters/dydxPresenters/_v4/Trade/Margin/dydxTargetLeverageViewBuilder.swift b/dydx/dydxPresenters/dydxPresenters/_v4/Trade/Margin/dydxTargetLeverageViewBuilder.swift new file mode 100644 index 000000000..c8a39d93f --- /dev/null +++ b/dydx/dydxPresenters/dydxPresenters/_v4/Trade/Margin/dydxTargetLeverageViewBuilder.swift @@ -0,0 +1,43 @@ +// +// dydxTargetLeverageViewBuilder.swift +// dydxPresenters +// +// Created by Rui Huang on 07/05/2024. +// + +import Utilities +import dydxViews +import PlatformParticles +import RoutingKit +import ParticlesKit +import PlatformUI + +public class dydxTargetLeverageViewBuilder: NSObject, ObjectBuilderProtocol { + public func build() -> T? { + let presenter = dydxTargetLeverageViewPresenter() + let view = presenter.viewModel?.createView() ?? PlatformViewModel().createView() + return dydxTargetLeverageViewController(presenter: presenter, view: view, configuration: .default) as? T + // return HostingViewController(presenter: presenter, view: view) as? T + } +} + +private class dydxTargetLeverageViewController: HostingViewController { + override public func arrive(to request: RoutingRequest?, animated: Bool) -> Bool { + if request?.path == "/trade/target_leverage" { + return true + } + return false + } +} + +private protocol dydxTargetLeverageViewPresenterProtocol: HostedViewPresenterProtocol { + var viewModel: dydxTargetLeverageViewModel? { get } +} + +private class dydxTargetLeverageViewPresenter: HostedViewPresenter, dydxTargetLeverageViewPresenterProtocol { + override init() { + super.init() + + viewModel = dydxTargetLeverageViewModel() + } +} diff --git a/dydx/dydxPresenters/dydxPresenters/_v4/Trade/TradeInput/Components/TradeInputFields/dydxTradeInputMarginViewPresenter.swift b/dydx/dydxPresenters/dydxPresenters/_v4/Trade/TradeInput/Components/TradeInputFields/dydxTradeInputMarginViewPresenter.swift new file mode 100644 index 000000000..4256ef1a3 --- /dev/null +++ b/dydx/dydxPresenters/dydxPresenters/_v4/Trade/TradeInput/Components/TradeInputFields/dydxTradeInputMarginViewPresenter.swift @@ -0,0 +1,40 @@ +// +// dydxTradeInputMarginViewPresenter.swift +// dydxPresenters +// +// Created by Rui Huang on 07/05/2024. +// + +import Utilities +import dydxViews +import PlatformParticles +import RoutingKit +import ParticlesKit +import PlatformUI + +protocol dydxTradeInputMarginViewPresenterProtocol: HostedViewPresenterProtocol { + var viewModel: dydxTradeInputMarginViewModel? { get } +} + +class dydxTradeInputMarginViewPresenter: HostedViewPresenter, dydxTradeInputMarginViewPresenterProtocol { + override init() { + super.init() + + viewModel = dydxTradeInputMarginViewModel() + } + + override func start() { + super.start() + + // TODO: Fetch from Abacus + viewModel?.marginMode = DataLocalizer.localize(path: "APP.GENERAL.ISOLATED") + viewModel?.marginLeverage = "2x" + + viewModel?.marginModeAction = { + Router.shared?.navigate(to: RoutingRequest(path: "/trade/margin_type"), animated: true, completion: nil) + } + viewModel?.marginLeverageAction = { + Router.shared?.navigate(to: RoutingRequest(path: "/trade/target_leverage"), animated: true, completion: nil) + } + } +} diff --git a/dydx/dydxPresenters/dydxPresenters/_v4/Trade/TradeInput/dydxTradeInputViewBuilder.swift b/dydx/dydxPresenters/dydxPresenters/_v4/Trade/TradeInput/dydxTradeInputViewBuilder.swift index b17ec3140..088c719ab 100644 --- a/dydx/dydxPresenters/dydxPresenters/_v4/Trade/TradeInput/dydxTradeInputViewBuilder.swift +++ b/dydx/dydxPresenters/dydxPresenters/_v4/Trade/TradeInput/dydxTradeInputViewBuilder.swift @@ -134,6 +134,7 @@ private class dydxTradeInputViewPresenter: HostedViewPresenter PlatformView { + PlatformView(viewModel: self, parentStyle: parentStyle, styleKey: styleKey) { [weak self] _ in + guard let self = self else { return AnyView(PlatformView.nilView) } + + return AnyView( + Text(self.text ?? "") + ) + } + } +} + +#if DEBUG +struct dydxMarginModeView_Previews_Dark: PreviewProvider { + @StateObject static var themeSettings = ThemeSettings.shared + + static var previews: some View { + ThemeSettings.applyDarkTheme() + ThemeSettings.applyStyles() + return dydxMarginModeViewModel.previewValue + .createView() + .themeColor(background: .layer0) + .environmentObject(themeSettings) + // .edgesIgnoringSafeArea(.bottom) + .previewLayout(.sizeThatFits) + } +} + +struct dydxMarginModeView_Previews_Light: PreviewProvider { + @StateObject static var themeSettings = ThemeSettings.shared + + static var previews: some View { + ThemeSettings.applyLightTheme() + ThemeSettings.applyStyles() + return dydxMarginModeViewModel.previewValue + .createView() + .themeColor(background: .layer0) + .environmentObject(themeSettings) + // .edgesIgnoringSafeArea(.bottom) + .previewLayout(.sizeThatFits) + } +} +#endif diff --git a/dydx/dydxViews/dydxViews/_v4/Trade/Margin/dydxTargetLeverageView.swift b/dydx/dydxViews/dydxViews/_v4/Trade/Margin/dydxTargetLeverageView.swift new file mode 100644 index 000000000..be03725b8 --- /dev/null +++ b/dydx/dydxViews/dydxViews/_v4/Trade/Margin/dydxTargetLeverageView.swift @@ -0,0 +1,65 @@ +// +// dydxTargetLeverageView.swift +// dydxUI +// +// Created by Rui Huang on 07/05/2024. +// Copyright © 2024 dYdX Trading Inc. All rights reserved. +// + +import SwiftUI +import PlatformUI +import Utilities + +public class dydxTargetLeverageViewModel: PlatformViewModel { + @Published public var text: String? + + public init() { } + + public static var previewValue: dydxTargetLeverageViewModel { + let vm = dydxTargetLeverageViewModel() + vm.text = "Test String" + return vm + } + + public override func createView(parentStyle: ThemeStyle = ThemeStyle.defaultStyle, styleKey: String? = nil) -> PlatformView { + PlatformView(viewModel: self, parentStyle: parentStyle, styleKey: styleKey) { [weak self] _ in + guard let self = self else { return AnyView(PlatformView.nilView) } + + return AnyView( + Text(self.text ?? "") + ) + } + } +} + +#if DEBUG +struct dydxTargetLeverageView_Previews_Dark: PreviewProvider { + @StateObject static var themeSettings = ThemeSettings.shared + + static var previews: some View { + ThemeSettings.applyDarkTheme() + ThemeSettings.applyStyles() + return dydxTargetLeverageViewModel.previewValue + .createView() + .themeColor(background: .layer0) + .environmentObject(themeSettings) + // .edgesIgnoringSafeArea(.bottom) + .previewLayout(.sizeThatFits) + } +} + +struct dydxTargetLeverageView_Previews_Light: PreviewProvider { + @StateObject static var themeSettings = ThemeSettings.shared + + static var previews: some View { + ThemeSettings.applyLightTheme() + ThemeSettings.applyStyles() + return dydxTargetLeverageViewModel.previewValue + .createView() + .themeColor(background: .layer0) + .environmentObject(themeSettings) + // .edgesIgnoringSafeArea(.bottom) + .previewLayout(.sizeThatFits) + } +} +#endif diff --git a/dydx/dydxViews/dydxViews/_v4/Trade/TradeInput/Components/TradeInputFields/dydxTradeInputMarginView.swift b/dydx/dydxViews/dydxViews/_v4/Trade/TradeInput/Components/TradeInputFields/dydxTradeInputMarginView.swift new file mode 100644 index 000000000..1818e32b3 --- /dev/null +++ b/dydx/dydxViews/dydxViews/_v4/Trade/TradeInput/Components/TradeInputFields/dydxTradeInputMarginView.swift @@ -0,0 +1,107 @@ +// +// dydxTradeInputMarginView.swift +// dydxUI +// +// Created by Rui Huang on 07/05/2024. +// Copyright © 2024 dYdX Trading Inc. All rights reserved. +// + +import SwiftUI +import PlatformUI +import Utilities + +public class dydxTradeInputMarginViewModel: PlatformViewModel { + @Published public var marginMode: String? + @Published public var marginLeverage: String? + @Published public var marginModeAction: (() -> Void)? + @Published public var marginLeverageAction: (() -> Void)? + + public init() { } + + public static var previewValue: dydxTradeInputMarginViewModel { + let vm = dydxTradeInputMarginViewModel() + vm.marginLeverage = "2x" + return vm + } + + public override func createView(parentStyle: ThemeStyle = ThemeStyle.defaultStyle, styleKey: String? = nil) -> PlatformView { + PlatformView(viewModel: self, parentStyle: parentStyle, styleKey: styleKey) { [weak self] style in + guard let self = self else { return AnyView(PlatformView.nilView) } + + return AnyView( + HStack(spacing: 8) { + let marginModeText = + self.createButtonContent(parentStyle: style, + text: self.marginMode ?? "") + .wrappedViewModel + PlatformButtonViewModel(content: marginModeText, + type: PlatformButtonType.iconType) { [weak self] in + self?.marginModeAction?() + } + .createView(parentStyle: style) + + let marginLeverageText = + self.createButtonContent(parentStyle: style, + text: self.marginLeverage ?? "") + .wrappedViewModel + PlatformButtonViewModel(content: marginLeverageText, + type: PlatformButtonType.iconType) { [weak self] in + self?.marginLeverageAction?() + } + .createView(parentStyle: style) + .frame(width: 60) + } + ) + } + } + + private let optionHeight: CGFloat = 44 + private let cornerRadius: CGFloat = 12 + private let optionPadding: CGFloat = 3 + + private func createButtonContent(parentStyle: ThemeStyle, text: String) -> some View { + Text(text) + .themeFont(fontSize: .medium) + .themeColor(foreground: .textPrimary) + .frame(height: optionHeight) + .frame(minWidth: 0, maxWidth: .infinity) + .overlay( + RoundedRectangle(cornerRadius: cornerRadius) + .strokeBorder(ThemeColor.SemanticColor.textTertiary.color, lineWidth: 1) + ) + .contentShape(RoundedRectangle(cornerRadius: cornerRadius)) + .padding(optionPadding) + } +} + +#if DEBUG +struct dydxTradeInputMarginView_Previews_Dark: PreviewProvider { + @StateObject static var themeSettings = ThemeSettings.shared + + static var previews: some View { + ThemeSettings.applyDarkTheme() + ThemeSettings.applyStyles() + return dydxTradeInputMarginViewModel.previewValue + .createView() + .themeColor(background: .layer0) + .environmentObject(themeSettings) + // .edgesIgnoringSafeArea(.bottom) + .previewLayout(.sizeThatFits) + } +} + +struct dydxTradeInputMarginView_Previews_Light: PreviewProvider { + @StateObject static var themeSettings = ThemeSettings.shared + + static var previews: some View { + ThemeSettings.applyLightTheme() + ThemeSettings.applyStyles() + return dydxTradeInputMarginViewModel.previewValue + .createView() + .themeColor(background: .layer0) + .environmentObject(themeSettings) + // .edgesIgnoringSafeArea(.bottom) + .previewLayout(.sizeThatFits) + } +} +#endif diff --git a/dydx/dydxViews/dydxViews/_v4/Trade/TradeInput/dydxTradeInputView.swift b/dydx/dydxViews/dydxViews/_v4/Trade/TradeInput/dydxTradeInputView.swift index 0107bf283..01be1bdd8 100644 --- a/dydx/dydxViews/dydxViews/_v4/Trade/TradeInput/dydxTradeInputView.swift +++ b/dydx/dydxViews/dydxViews/_v4/Trade/TradeInput/dydxTradeInputView.swift @@ -8,6 +8,7 @@ import PlatformUI import SwiftUI import Utilities +import dydxFormatter public class dydxTradeInputViewModel: PlatformViewModel { public enum DisplayState { @@ -29,8 +30,9 @@ public class dydxTradeInputViewModel: PlatformViewModel { @Published public var orderbookViewModel: dydxOrderbookViewModel? = dydxOrderbookViewModel() @Published public var ctaButtonViewModel: dydxTradeInputCtaButtonViewModel? = dydxTradeInputCtaButtonViewModel() @Published public var validationViewModel: dydxValidationViewModel? = dydxValidationViewModel() - @Published public var tipBuySellViewModel: dydxTradeSheetTipBuySellViewModel? = dydxTradeSheetTipBuySellViewModel() - @Published public var tipDraftViewModel: dydxTradeSheetTipDraftViewModel? = dydxTradeSheetTipDraftViewModel() + @Published public var tipBuySellViewModel: dydxTradeSheetTipBuySellViewModel? = dydxTradeSheetTipBuySellViewModel() + @Published public var tipDraftViewModel: dydxTradeSheetTipDraftViewModel? = dydxTradeSheetTipDraftViewModel() + @Published public var marginViewModel: dydxTradeInputMarginViewModel? = dydxTradeInputMarginViewModel() public override init(bodyBuilder: ((ThemeStyle) -> AnyView)? = nil) { super.init(bodyBuilder: bodyBuilder) @@ -53,6 +55,7 @@ public class dydxTradeInputViewModel: PlatformViewModel { vm.validationViewModel = .previewValue vm.tipBuySellViewModel = .previewValue vm.tipDraftViewModel = .previewValue + vm.marginViewModel = .previewValue return vm } @@ -73,16 +76,39 @@ public class dydxTradeInputViewModel: PlatformViewModel { self.createSwipeUpView(parentStyle: style) } - self.orderTypeViewModel?.createView(parentStyle: style) - .frame(height: 64) - .padding(.top, 32) - .padding(.horizontal, -spacing) + if dydxBoolFeatureFlag.enable_isolated_margins.isEnabled { + HStack(spacing: spacing) { + self.marginViewModel?.createView(parentStyle: style) + .frame(maxWidth: .infinity) + self.sideViewModel?.createView(parentStyle: style) + .frame(maxWidth: .infinity) + } + .padding(.top, 48) + .frame(height: 64) - HStack(spacing: spacing) { - self.orderbookManagerViewModel?.createView(parentStyle: style) - .frame(width: orderbookWdith) - self.sideViewModel?.createView(parentStyle: style) - .frame(width: editViewWidth) + Spacer(minLength: 16) + + HStack(spacing: spacing) { + self.orderbookManagerViewModel?.createView(parentStyle: style) + .frame(width: orderbookWdith) + self.orderTypeViewModel?.createView(parentStyle: style) + .frame(width: editViewWidth) + } + .frame(height: 64) + + } else { + + self.orderTypeViewModel?.createView(parentStyle: style) + .frame(height: 64) + .padding(.top, 32) + .padding(.horizontal, -spacing) + + HStack(spacing: spacing) { + self.orderbookManagerViewModel?.createView(parentStyle: style) + .frame(width: orderbookWdith) + self.sideViewModel?.createView(parentStyle: style) + .frame(width: editViewWidth) + } } Spacer(minLength: 8) From 93b3e1c9377b380bd41fafbb47508745ad93d784 Mon Sep 17 00:00:00 2001 From: Rui Date: Tue, 7 May 2024 20:27:20 -0700 Subject: [PATCH 2/2] MOB-357 Margin Mode Selection Screen --- .../Components/Icons/PlatformIcon.swift | 24 ++-- .../_Features/routing_swiftui.json | 2 +- .../Margin/dydxMarginModeViewBuilder.swift | 18 ++- .../dydxTradeInputMarginViewPresenter.swift | 2 +- .../_v4/Trade/Margin/dydxMarginModeView.swift | 115 +++++++++++++++++- 5 files changed, 140 insertions(+), 21 deletions(-) diff --git a/PlatformUI/PlatformUI/Components/Icons/PlatformIcon.swift b/PlatformUI/PlatformUI/Components/Icons/PlatformIcon.swift index fe1842f0b..354de2a4f 100644 --- a/PlatformUI/PlatformUI/Components/Icons/PlatformIcon.swift +++ b/PlatformUI/PlatformUI/Components/Icons/PlatformIcon.swift @@ -100,18 +100,20 @@ public class PlatformIconViewModel: PlatformViewModel { ) } else { let clippedView = Group { + ZStack { - ZStack { - view - .frame(width: size.width - spacing, height: size.height - spacing) - .clipped() - } - .frame(width: size.width, height: size.height) - .themeColor(background: background) - .clipShape(Circle()) - .overlay( - Circle().stroke(borderColor?.color ?? .clear, lineWidth: 1) - ) + Circle() + .fill(background.color) + .frame(width: size.width, height: size.height) + .themeColor(background: background) + .clipShape(Circle()) + .overlay( + Circle().stroke(borderColor?.color ?? .clear, lineWidth: 1) + ) + + view + .frame(width: size.width - spacing, height: size.height - spacing) + .clipped() } .themeStyle(style: style) } diff --git a/dydx/dydxPresenters/dydxPresenters/_Features/routing_swiftui.json b/dydx/dydxPresenters/dydxPresenters/_Features/routing_swiftui.json index e7645794b..3bfbe6ba1 100644 --- a/dydx/dydxPresenters/dydxPresenters/_Features/routing_swiftui.json +++ b/dydx/dydxPresenters/dydxPresenters/_Features/routing_swiftui.json @@ -309,7 +309,7 @@ }, "/trade/margin_type":{ "destination":"dydxPresenters.dydxMarginModeViewBuilder", - "presentation":"prompt" + "presentation":"half" }, "/trade/target_leverage":{ "destination":"dydxPresenters.dydxTargetLeverageViewBuilder", diff --git a/dydx/dydxPresenters/dydxPresenters/_v4/Trade/Margin/dydxMarginModeViewBuilder.swift b/dydx/dydxPresenters/dydxPresenters/_v4/Trade/Margin/dydxMarginModeViewBuilder.swift index 211d89319..4a7dc96d2 100644 --- a/dydx/dydxPresenters/dydxPresenters/_v4/Trade/Margin/dydxMarginModeViewBuilder.swift +++ b/dydx/dydxPresenters/dydxPresenters/_v4/Trade/Margin/dydxMarginModeViewBuilder.swift @@ -16,8 +16,7 @@ public class dydxMarginModeViewBuilder: NSObject, ObjectBuilderProtocol { public func build() -> T? { let presenter = dydxMarginModeViewPresenter() let view = presenter.viewModel?.createView() ?? PlatformViewModel().createView() - return dydxMarginModeViewController(presenter: presenter, view: view, configuration: .default) as? T - // return HostingViewController(presenter: presenter, view: view) as? T + return dydxMarginModeViewController(presenter: presenter, view: view, configuration: .ignoreSafeArea) as? T } } @@ -39,5 +38,20 @@ private class dydxMarginModeViewPresenter: HostedViewPresenter Void)? + + private let cornerRadius: CGFloat = 8 + + public init(title: String? = nil, detail: String? = nil, isSelected: Bool = false, selectedAction: (() -> Void)? = nil) { + self.title = title + self.detail = detail + self.isSelected = isSelected + self.selectedAction = selectedAction + } + + public override func createView(parentStyle: ThemeStyle = ThemeStyle.defaultStyle, styleKey: String? = nil) -> PlatformView { + PlatformView(viewModel: self, parentStyle: parentStyle, styleKey: styleKey) { [weak self] style in + guard let self = self else { return AnyView(PlatformView.nilView) } + + let buttonContent = VStack(alignment: .leading, spacing: 8) { + HStack { + Text(self.title ?? "") + .themeFont(fontSize: .medium) + .themeColor(foreground: .textPrimary) + + Spacer() + + if self.isSelected { + self.createSelectedCheckmark(parentStyle: style) + } else { + self.createUnselectedCheckmark(parentStyle: style) + } + } + + Text(self.detail ?? "") + .multilineTextAlignment(.leading) + .themeFont(fontSize: .small) + .themeColor(foreground: .textTertiary) + } + .padding(16) + .leftAligned() + .themeColor(background: self.isSelected ? ThemeColor.SemanticColor.layer1 : ThemeColor.SemanticColor.layer3) + .overlay( + RoundedRectangle(cornerRadius: cornerRadius) + .strokeBorder(self.isSelected ? ThemeColor.SemanticColor.colorPurple.color : ThemeColor.SemanticColor.textTertiary.color, lineWidth: 1) + ) + .contentShape(RoundedRectangle(cornerRadius: cornerRadius)) + .wrappedViewModel + + return AnyView( + PlatformButtonViewModel(content: buttonContent, + type: PlatformButtonType.iconType, + action: self.selectedAction ?? {}) + .createView(parentStyle: style) + ) + } + } + + private func createSelectedCheckmark(parentStyle: ThemeStyle) -> some View { + PlatformIconViewModel(type: .asset(name: "icon_checked", bundle: Bundle.dydxView), + clip: .circle(background: .colorPurple, + spacing: 12, + borderColor: nil), + size: CGSize(width: 20, height: 20), + templateColor: .textPrimary) + .createView(parentStyle: parentStyle) + } + + private func createUnselectedCheckmark(parentStyle: ThemeStyle) -> some View { + Circle() + .fill(ThemeColor.SemanticColor.layer1.color) + .frame(width: 20, height: 20) + .overlay( + Circle().stroke(ThemeColor.SemanticColor.layer5.color, lineWidth: 1) + ) + } +} + public class dydxMarginModeViewModel: PlatformViewModel { - @Published public var text: String? + @Published public var market: String? + @Published public var items: [dydxMarginModeItemViewModel] = [] public init() { } public static var previewValue: dydxMarginModeViewModel { let vm = dydxMarginModeViewModel() - vm.text = "Test String" + vm.market = "ETH-USD" return vm } public override func createView(parentStyle: ThemeStyle = ThemeStyle.defaultStyle, styleKey: String? = nil) -> PlatformView { - PlatformView(viewModel: self, parentStyle: parentStyle, styleKey: styleKey) { [weak self] _ in + PlatformView(viewModel: self, parentStyle: parentStyle, styleKey: styleKey) { [weak self] style in guard let self = self else { return AnyView(PlatformView.nilView) } - return AnyView( - Text(self.text ?? "") - ) + let view = VStack(alignment: .leading, spacing: 20) { + HStack(spacing: 8) { + Text(DataLocalizer.localize(path: "APP.GENERAL.MARGIN_MODE")) + .themeColor(foreground: .textPrimary) + + Text(self.market ?? "") + .themeColor(foreground: .textSecondary) + + Spacer() + } + .themeFont(fontType: .plus, fontSize: .largest) + .padding(.top, 40) + + VStack(alignment: .leading, spacing: 16) { + ForEach(self.items, id: \.id) { item in + item.createView(parentStyle: style) + } + } + + Spacer() + } + .padding(.horizontal) + .themeColor(background: .layer3) + .makeSheet(sheetStyle: .fitSize) + + // make it visible under the tabbar + return AnyView(view.ignoresSafeArea(edges: [.bottom])) } } }