From 3342756978c6dd50b2700bc4edb03ae5686cafa5 Mon Sep 17 00:00:00 2001 From: John Huang Date: Wed, 3 Apr 2024 09:42:05 -0700 Subject: [PATCH 01/21] Margin type and leverage screens skeleton --- .../trading/common/navigation/DydxRoutes.kt | 2 + .../trading/feature/trade/DydxTradeRouter.kt | 19 +++ .../DydxTradeInputMarketTypeView.kt | 143 ++++++++++++++++++ .../DydxTradeInputMarketTypeViewModel.kt | 41 +++++ .../DydxTradeInputTargetLeverageView.kt | 90 +++++++++++ .../DydxTradeInputTargetLeverageViewModel.kt | 41 +++++ .../trade/tradeinput/DydxTradeInputView.kt | 51 ++++++- .../tradeinput/DydxTradeInputViewModel.kt | 17 ++- 8 files changed, 401 insertions(+), 3 deletions(-) create mode 100644 v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/tradeinput/DydxTradeInputMarketTypeView.kt create mode 100644 v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/tradeinput/DydxTradeInputMarketTypeViewModel.kt create mode 100644 v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/tradeinput/DydxTradeInputTargetLeverageView.kt create mode 100644 v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/tradeinput/DydxTradeInputTargetLeverageViewModel.kt diff --git a/v4/common/src/main/java/exchange/dydx/trading/common/navigation/DydxRoutes.kt b/v4/common/src/main/java/exchange/dydx/trading/common/navigation/DydxRoutes.kt index d40dd50c..ae81c3ca 100644 --- a/v4/common/src/main/java/exchange/dydx/trading/common/navigation/DydxRoutes.kt +++ b/v4/common/src/main/java/exchange/dydx/trading/common/navigation/DydxRoutes.kt @@ -49,6 +49,8 @@ object MarketRoutes { object TradeRoutes { const val status = "trade/status" const val close_position = "trade/close" + const val market_type = "trade/market_type" + const val target_leverage = "trade/target_leverage" } object TransferRoutes { diff --git a/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/DydxTradeRouter.kt b/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/DydxTradeRouter.kt index a4d16269..c735847a 100644 --- a/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/DydxTradeRouter.kt +++ b/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/DydxTradeRouter.kt @@ -8,6 +8,8 @@ import exchange.dydx.trading.common.navigation.DydxRouter import exchange.dydx.trading.common.navigation.TradeRoutes import exchange.dydx.trading.common.navigation.dydxComposable import exchange.dydx.trading.feature.trade.closeposition.DydxClosePositionInputView +import exchange.dydx.trading.feature.trade.tradeinput.DydxTradeInputMarketTypeView +import exchange.dydx.trading.feature.trade.tradeinput.DydxTradeInputTargetLeverageView import exchange.dydx.trading.feature.trade.tradestatus.DydxTradeStatusView import timber.log.Timber @@ -38,4 +40,21 @@ fun NavGraphBuilder.tradeGraph( } DydxClosePositionInputView.Content(Modifier) } + + dydxComposable( + router = appRouter, + route = TradeRoutes.market_type, + deepLinks = appRouter.deeplinks(TradeRoutes.status), + ) { navBackStackEntry -> + DydxTradeInputMarketTypeView.Content(Modifier) + } + + dydxComposable( + router = appRouter, + route = TradeRoutes.target_leverage, + deepLinks = appRouter.deeplinks(TradeRoutes.status), + ) { navBackStackEntry -> + DydxTradeInputTargetLeverageView.Content(Modifier) + } + } diff --git a/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/tradeinput/DydxTradeInputMarketTypeView.kt b/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/tradeinput/DydxTradeInputMarketTypeView.kt new file mode 100644 index 00000000..b2b36aa1 --- /dev/null +++ b/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/tradeinput/DydxTradeInputMarketTypeView.kt @@ -0,0 +1,143 @@ +package exchange.dydx.trading.feature.trade.tradeinput + +import androidx.compose.animation.animateContentSize +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.width +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import androidx.hilt.navigation.compose.hiltViewModel +import exchange.dydx.platformui.components.PlatformInfoScaffold +import exchange.dydx.platformui.components.dividers.PlatformDivider +import exchange.dydx.platformui.designSystem.theme.ThemeColor +import exchange.dydx.platformui.designSystem.theme.ThemeFont +import exchange.dydx.platformui.designSystem.theme.ThemeShapes +import exchange.dydx.platformui.designSystem.theme.dydxDefault +import exchange.dydx.platformui.designSystem.theme.themeColor +import exchange.dydx.platformui.designSystem.theme.themeFont +import exchange.dydx.trading.common.component.DydxComponent +import exchange.dydx.trading.common.compose.collectAsStateWithLifecycle +import exchange.dydx.trading.common.navigation.DydxAnimation +import exchange.dydx.trading.common.theme.DydxThemedPreviewSurface +import exchange.dydx.trading.feature.shared.views.HeaderViewCloseBotton + +@Preview +@Composable +fun Preview_DydxTradeInputMarketTypeView() { + DydxThemedPreviewSurface { + DydxTradeInputMarketTypeView.Content( + Modifier, + DydxTradeInputMarketTypeView.ViewState.preview + ) + } +} + +object DydxTradeInputMarketTypeView : DydxComponent { + data class MarginTypeSelection( + val title: String, + val text: String, + val action: () -> Unit, + ) + + data class ViewState( + val title: String, + val asset: String, + val crossMargin: MarginTypeSelection, + val isolatedMargin: MarginTypeSelection, + val errorText: String?, + val closeAction: (() -> Unit)? = null, + ) { + companion object { + val preview = ViewState( + title = "Margin Mode", + asset = "for ETH-USD", + crossMargin = MarginTypeSelection( + title = "Cross Margin", + text = "This is the description text for cross margin", + action = {}, + ), + isolatedMargin = MarginTypeSelection( + title = "Isolated Margin", + text = "This is the description text for isolated margin", + action = {}, + ), + errorText = "Error", + ) + } + } + + @Composable + override fun Content(modifier: Modifier) { + val viewModel: DydxTradeInputMarketTypeViewModel = hiltViewModel() + + val state = viewModel.state.collectAsStateWithLifecycle(initialValue = null).value + PlatformInfoScaffold(modifier = modifier, platformInfo = viewModel.platformInfo) { + Content(modifier, state) + } + } + + @Composable + fun Content(modifier: Modifier, state: ViewState?) { + if (state == null) { + return + } + + Column( + modifier = modifier + .animateContentSize() + .fillMaxSize() + .themeColor(ThemeColor.SemanticColor.layer_3), + ) { + Row( + modifier + .fillMaxWidth() + .padding(vertical = ThemeShapes.VerticalPadding), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.Start, + ) { + Spacer(modifier = Modifier.width(16.dp)) + Text( + modifier = Modifier.padding(horizontal = 0.dp), + style = TextStyle.dydxDefault + .themeFont( + fontSize = ThemeFont.FontSize.large, + fontType = ThemeFont.FontType.plus, + ) + .themeColor(ThemeColor.SemanticColor.text_primary), + text = state.title, + ) + Spacer(modifier = Modifier.width(4.dp)) + Text( + modifier = Modifier.padding(horizontal = 4.dp), + style = TextStyle.dydxDefault + .themeFont( + fontSize = ThemeFont.FontSize.large, + fontType = ThemeFont.FontType.plus, + ) + .themeColor(ThemeColor.SemanticColor.text_secondary), + text = state.asset, + ) + Spacer(modifier = Modifier.weight(1f)) + HeaderViewCloseBotton( + closeAction = state.closeAction, + ) + } + + PlatformDivider() + + + + } + } +} + diff --git a/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/tradeinput/DydxTradeInputMarketTypeViewModel.kt b/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/tradeinput/DydxTradeInputMarketTypeViewModel.kt new file mode 100644 index 00000000..0269d09b --- /dev/null +++ b/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/tradeinput/DydxTradeInputMarketTypeViewModel.kt @@ -0,0 +1,41 @@ +package exchange.dydx.trading.feature.trade.tradeinput + +import androidx.lifecycle.ViewModel +import dagger.hilt.android.lifecycle.HiltViewModel +import exchange.dydx.abacus.output.PerpetualMarketSummary +import exchange.dydx.abacus.protocols.LocalizerProtocol +import exchange.dydx.dydxstatemanager.AbacusStateManagerProtocol +import exchange.dydx.platformui.components.PlatformInfo +import exchange.dydx.trading.common.AppConfig +import exchange.dydx.trading.common.DydxViewModel +import exchange.dydx.trading.common.formatter.DydxFormatter +import exchange.dydx.trading.common.navigation.DydxRouter +import exchange.dydx.trading.common.theme.DydxTheme +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.map +import javax.inject.Inject + +@HiltViewModel +class DydxTradeInputMarketTypeViewModel @Inject constructor( + private val localizer: LocalizerProtocol, + private val router: DydxRouter, + private val abacusStateManager: AbacusStateManagerProtocol, + private val formatter: DydxFormatter, + val platformInfo: PlatformInfo, +) : ViewModel(), DydxViewModel { + + val state: Flow = + abacusStateManager.state.marketSummary + .map { + createViewState(it) + } + .distinctUntilChanged() + + private fun createViewState(marketSummary: PerpetualMarketSummary?): DydxTradeInputMarketTypeView.ViewState { + val volume = formatter.dollarVolume(marketSummary?.volume24HUSDC) + return DydxTradeInputMarketTypeView.ViewState( + text = volume, + ) + } +} diff --git a/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/tradeinput/DydxTradeInputTargetLeverageView.kt b/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/tradeinput/DydxTradeInputTargetLeverageView.kt new file mode 100644 index 00000000..74d0c9b2 --- /dev/null +++ b/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/tradeinput/DydxTradeInputTargetLeverageView.kt @@ -0,0 +1,90 @@ +package exchange.dydx.trading.feature.trade.tradeinput + +import androidx.compose.animation.animateContentSize +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.tooling.preview.Preview +import androidx.hilt.navigation.compose.hiltViewModel +import exchange.dydx.platformui.components.PlatformInfoScaffold +import exchange.dydx.platformui.components.dividers.PlatformDivider +import exchange.dydx.platformui.designSystem.theme.ThemeColor +import exchange.dydx.platformui.designSystem.theme.ThemeShapes +import exchange.dydx.platformui.designSystem.theme.themeColor +import exchange.dydx.trading.common.component.DydxComponent +import exchange.dydx.trading.common.compose.collectAsStateWithLifecycle +import exchange.dydx.trading.common.navigation.DydxAnimation +import exchange.dydx.trading.common.theme.DydxThemedPreviewSurface +import exchange.dydx.trading.feature.shared.views.HeaderViewCloseBotton + +@Preview +@Composable +fun Preview_DydxTradeInputTargetLeverageView() { + DydxThemedPreviewSurface { + DydxTradeInputTargetLeverageView.Content( + Modifier, + DydxTradeInputTargetLeverageView.ViewState.preview + ) + } +} + +object DydxTradeInputTargetLeverageView : DydxComponent { + data class ViewState( + val text: String?, + val closeAction: (() -> Unit)? = null, + ) { + companion object { + val preview = ViewState( + text = "1.0M", + ) + } + } + + @Composable + override fun Content(modifier: Modifier) { + val viewModel: DydxTradeInputTargetLeverageViewModel = hiltViewModel() + + val state = viewModel.state.collectAsStateWithLifecycle(initialValue = null).value + PlatformInfoScaffold(modifier = modifier, platformInfo = viewModel.platformInfo) { + Content(modifier, state) + } + } + + @Composable + fun Content(modifier: Modifier, state: ViewState?) { + if (state == null) { + return + } + + Column( + modifier = modifier + .animateContentSize() + .fillMaxSize() + .themeColor(ThemeColor.SemanticColor.layer_3), + ) { + Row( + modifier + .fillMaxWidth() + .padding(vertical = ThemeShapes.VerticalPadding), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.Start, + ) { + HeaderViewCloseBotton( + closeAction = state.closeAction, + ) + } + + PlatformDivider() + + } + } +} + diff --git a/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/tradeinput/DydxTradeInputTargetLeverageViewModel.kt b/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/tradeinput/DydxTradeInputTargetLeverageViewModel.kt new file mode 100644 index 00000000..4e4edbff --- /dev/null +++ b/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/tradeinput/DydxTradeInputTargetLeverageViewModel.kt @@ -0,0 +1,41 @@ +package exchange.dydx.trading.feature.trade.tradeinput + +import androidx.lifecycle.ViewModel +import dagger.hilt.android.lifecycle.HiltViewModel +import exchange.dydx.abacus.output.PerpetualMarketSummary +import exchange.dydx.abacus.protocols.LocalizerProtocol +import exchange.dydx.dydxstatemanager.AbacusStateManagerProtocol +import exchange.dydx.platformui.components.PlatformInfo +import exchange.dydx.trading.common.AppConfig +import exchange.dydx.trading.common.DydxViewModel +import exchange.dydx.trading.common.formatter.DydxFormatter +import exchange.dydx.trading.common.navigation.DydxRouter +import exchange.dydx.trading.common.theme.DydxTheme +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.map +import javax.inject.Inject + +@HiltViewModel +class DydxTradeInputTargetLeverageViewModel @Inject constructor( + private val localizer: LocalizerProtocol, + private val router: DydxRouter, + private val abacusStateManager: AbacusStateManagerProtocol, + private val formatter: DydxFormatter, + val platformInfo: PlatformInfo, +) : ViewModel(), DydxViewModel { + + val state: Flow = + abacusStateManager.state.marketSummary + .map { + createViewState(it) + } + .distinctUntilChanged() + + private fun createViewState(marketSummary: PerpetualMarketSummary?): DydxTradeInputTargetLeverageView.ViewState { + val volume = formatter.dollarVolume(marketSummary?.volume24HUSDC) + return DydxTradeInputTargetLeverageView.ViewState( + text = volume, + ) + } +} diff --git a/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/tradeinput/DydxTradeInputView.kt b/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/tradeinput/DydxTradeInputView.kt index bc8c251d..250e42cf 100644 --- a/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/tradeinput/DydxTradeInputView.kt +++ b/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/tradeinput/DydxTradeInputView.kt @@ -5,6 +5,7 @@ import androidx.compose.animation.animateContentSize import androidx.compose.animation.expandHorizontally import androidx.compose.animation.shrinkHorizontally import androidx.compose.foundation.ExperimentalFoundationApi +import androidx.compose.foundation.background import androidx.compose.foundation.gestures.detectTapGestures import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box @@ -12,6 +13,7 @@ import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.RowScope import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.heightIn import androidx.compose.foundation.layout.offset import androidx.compose.foundation.layout.padding @@ -38,9 +40,15 @@ import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel +import androidx.lifecycle.ViewModel +import dagger.hilt.android.lifecycle.HiltViewModel import exchange.dydx.abacus.protocols.LocalizerProtocol +import exchange.dydx.platformui.components.buttons.PlatformButton +import exchange.dydx.platformui.components.buttons.PlatformButtonState import exchange.dydx.platformui.components.dividers.PlatformDivider +import exchange.dydx.platformui.designSystem.theme.ThemeColor import exchange.dydx.platformui.designSystem.theme.ThemeShapes +import exchange.dydx.platformui.designSystem.theme.color import exchange.dydx.trading.common.component.DydxComponent import exchange.dydx.trading.common.compose.collectAsStateWithLifecycle import exchange.dydx.trading.common.theme.DydxThemedPreviewSurface @@ -63,6 +71,7 @@ import exchange.dydx.trading.feature.trade.tradeinput.components.inputfields.siz import exchange.dydx.trading.feature.trade.tradeinput.components.inputfields.timeinforce.DydxTradeInputTimeInForceView import exchange.dydx.trading.feature.trade.tradeinput.components.inputfields.triggerprice.DydxTradeInputTriggerPriceView import exchange.dydx.trading.feature.trade.tradeinput.components.sheettip.DydxTradeSheetTipView +import kotlinx.coroutines.flow.Flow @OptIn(ExperimentalMaterial3Api::class) @Preview @@ -111,6 +120,8 @@ object DydxTradeInputView : DydxComponent { val inputFields: List = listOf(), val orderbookToggleState: OrderbookToggleState = OrderbookToggleState.Open, val requestedBottomSheetState: BottomSheetState? = null, + val onMarketType: () -> Unit = {}, + val onTargetLeverage: () -> Unit = {}, val onRequestedBottomSheetStateCompleted: () -> Unit = {}, ) { companion object { @@ -177,7 +188,43 @@ object DydxTradeInputView : DydxComponent { } if (state.isIsolatedMarketEnabled) { - DydxTradeInputSideView.Content(Modifier) + Row( + modifier = Modifier + .fillMaxWidth() + .height(44.dp) + .padding(horizontal = ThemeShapes.HorizontalPadding), + horizontalArrangement = Arrangement.spacedBy(0.dp), + ) { + Column( + verticalArrangement = Arrangement.spacedBy(12.dp), + ) { + Row( + horizontalArrangement = Arrangement.spacedBy(12.dp), + ) { + PlatformButton( + modifier = Modifier.height(52.dp), + state = PlatformButtonState.Secondary, + text = "Isolated" + ) { + state.onMarketType() + } + PlatformButton( + modifier = Modifier.height(52.dp), + state = PlatformButtonState.Secondary, + text = "2x" + ) { + state.onTargetLeverage() + } + } + } + + Column( + modifier = Modifier, + verticalArrangement = Arrangement.spacedBy(12.dp), + ) { + DydxTradeInputSideView.Content(Modifier) + } + } } else { DydxTradeInputOrderTypeView.Content( Modifier, @@ -347,4 +394,4 @@ object DydxTradeInputView : DydxComponent { } } } -} +} \ No newline at end of file diff --git a/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/tradeinput/DydxTradeInputViewModel.kt b/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/tradeinput/DydxTradeInputViewModel.kt index d349582b..e24146ea 100644 --- a/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/tradeinput/DydxTradeInputViewModel.kt +++ b/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/tradeinput/DydxTradeInputViewModel.kt @@ -8,6 +8,8 @@ import exchange.dydx.dydxstatemanager.AbacusStateManagerProtocol import exchange.dydx.trading.common.DydxViewModel import exchange.dydx.trading.common.featureflags.DydxFeatureFlag import exchange.dydx.trading.common.featureflags.DydxFeatureFlags +import exchange.dydx.trading.common.navigation.DydxRouter +import exchange.dydx.trading.common.navigation.TradeRoutes import exchange.dydx.trading.feature.receipt.ReceiptType import exchange.dydx.trading.feature.receipt.TradeReceiptType import kotlinx.coroutines.flow.Flow @@ -19,6 +21,7 @@ import javax.inject.Inject @HiltViewModel class DydxTradeInputViewModel @Inject constructor( private val localizer: LocalizerProtocol, + private val router: DydxRouter, private val abacusStateManager: AbacusStateManagerProtocol, private val featureFlags: DydxFeatureFlags, val receiptTypeFlow: MutableStateFlow<@JvmSuppressWildcards ReceiptType?>, @@ -49,7 +52,7 @@ class DydxTradeInputViewModel @Inject constructor( localizer = localizer, inputFields = listOfNotNull( if (tradeInput?.options?.needsSize == true) DydxTradeInputView.InputField.Size else null, - if (tradeInput?.options?.needsSize == true && tradeInput?.options?.needsLeverage == true) { + if (tradeInput?.options?.needsSize == true && tradeInput.options?.needsLeverage == true) { DydxTradeInputView.InputField.Leverage } else { null @@ -66,6 +69,18 @@ class DydxTradeInputViewModel @Inject constructor( isIsolatedMarketEnabled = featureFlags.isFeatureEnabled(DydxFeatureFlag.enable_isolated_market), orderbookToggleState = orderbookToggleState, requestedBottomSheetState = buttomSheetState, + onMarketType = { + router.navigateTo( + route = TradeRoutes.market_type, + presentation = DydxRouter.Presentation.Modal, + ) + }, + onTargetLeverage = { + router.navigateTo( + route = TradeRoutes.target_leverage, + presentation = DydxRouter.Presentation.Modal, + ) + }, onRequestedBottomSheetStateCompleted = { buttomSheetStateFlow.value = null }, From 8ebe53df296cab120e03721f027b4fd89d2c2ff1 Mon Sep 17 00:00:00 2001 From: John Huang Date: Wed, 3 Apr 2024 15:39:25 -0700 Subject: [PATCH 02/21] MOB-356 MOB-358 Margin mode screen --- .../trading/feature/trade/DydxTradeRouter.kt | 5 +- .../DydxTradeInputMarginModeView.kt | 226 ++++++++++++++++++ .../DydxTradeInputMarginModeViewModel.kt | 57 +++++ .../DydxTradeInputMarketTypeView.kt | 143 ----------- .../DydxTradeInputMarketTypeViewModel.kt | 41 ---- .../DydxTradeInputTargetLeverageView.kt | 17 +- .../DydxTradeInputTargetLeverageViewModel.kt | 37 ++- .../trade/tradeinput/DydxTradeInputView.kt | 12 +- 8 files changed, 328 insertions(+), 210 deletions(-) create mode 100644 v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/tradeinput/DydxTradeInputMarginModeView.kt create mode 100644 v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/tradeinput/DydxTradeInputMarginModeViewModel.kt delete mode 100644 v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/tradeinput/DydxTradeInputMarketTypeView.kt delete mode 100644 v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/tradeinput/DydxTradeInputMarketTypeViewModel.kt diff --git a/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/DydxTradeRouter.kt b/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/DydxTradeRouter.kt index c735847a..84d155c7 100644 --- a/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/DydxTradeRouter.kt +++ b/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/DydxTradeRouter.kt @@ -8,7 +8,7 @@ import exchange.dydx.trading.common.navigation.DydxRouter import exchange.dydx.trading.common.navigation.TradeRoutes import exchange.dydx.trading.common.navigation.dydxComposable import exchange.dydx.trading.feature.trade.closeposition.DydxClosePositionInputView -import exchange.dydx.trading.feature.trade.tradeinput.DydxTradeInputMarketTypeView +import exchange.dydx.trading.feature.trade.tradeinput.DydxTradeInputMarginModeView import exchange.dydx.trading.feature.trade.tradeinput.DydxTradeInputTargetLeverageView import exchange.dydx.trading.feature.trade.tradestatus.DydxTradeStatusView import timber.log.Timber @@ -46,7 +46,7 @@ fun NavGraphBuilder.tradeGraph( route = TradeRoutes.market_type, deepLinks = appRouter.deeplinks(TradeRoutes.status), ) { navBackStackEntry -> - DydxTradeInputMarketTypeView.Content(Modifier) + DydxTradeInputMarginModeView.Content(Modifier) } dydxComposable( @@ -56,5 +56,4 @@ fun NavGraphBuilder.tradeGraph( ) { navBackStackEntry -> DydxTradeInputTargetLeverageView.Content(Modifier) } - } diff --git a/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/tradeinput/DydxTradeInputMarginModeView.kt b/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/tradeinput/DydxTradeInputMarginModeView.kt new file mode 100644 index 00000000..41a08e93 --- /dev/null +++ b/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/tradeinput/DydxTradeInputMarginModeView.kt @@ -0,0 +1,226 @@ +package exchange.dydx.trading.feature.trade.tradeinput + +import androidx.compose.animation.animateContentSize +import androidx.compose.foundation.background +import androidx.compose.foundation.border +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import androidx.hilt.navigation.compose.hiltViewModel +import exchange.dydx.platformui.components.PlatformInfoScaffold +import exchange.dydx.platformui.components.dividers.PlatformDivider +import exchange.dydx.platformui.designSystem.theme.ThemeColor +import exchange.dydx.platformui.designSystem.theme.ThemeFont +import exchange.dydx.platformui.designSystem.theme.ThemeShapes +import exchange.dydx.platformui.designSystem.theme.color +import exchange.dydx.platformui.designSystem.theme.dydxDefault +import exchange.dydx.platformui.designSystem.theme.themeColor +import exchange.dydx.platformui.designSystem.theme.themeFont +import exchange.dydx.trading.common.component.DydxComponent +import exchange.dydx.trading.common.compose.collectAsStateWithLifecycle +import exchange.dydx.trading.common.theme.DydxThemedPreviewSurface +import exchange.dydx.trading.feature.shared.views.HeaderViewCloseBotton + +@Preview +@Composable +fun Preview_DydxTradeInputMarginModeView() { + DydxThemedPreviewSurface { + DydxTradeInputMarginModeView.Content( + Modifier, + DydxTradeInputMarginModeView.ViewState.preview, + ) + } +} + +object DydxTradeInputMarginModeView : DydxComponent { + data class MarginTypeSelection( + val title: String, + val text: String, + val selected: Boolean, + val action: () -> Unit, + ) + + data class ViewState( + val title: String, + val asset: String, + val crossMargin: MarginTypeSelection, + val isolatedMargin: MarginTypeSelection, + val errorText: String?, + val closeAction: (() -> Unit)? = null, + ) { + companion object { + val preview = ViewState( + title = "Margin Mode", + asset = "for ETH-USD", + crossMargin = MarginTypeSelection( + title = "Cross Margin", + text = "This is the description text for cross margin", + selected = true, + action = {}, + ), + isolatedMargin = MarginTypeSelection( + title = "Isolated Margin", + text = "This is the description text for isolated margin", + selected = false, + action = {}, + ), + errorText = "Error", + ) + } + } + + @Composable + override fun Content(modifier: Modifier) { + val viewModel: DydxTradeInputMarginModeViewModel = hiltViewModel() + + val state = viewModel.state.collectAsStateWithLifecycle(initialValue = null).value + PlatformInfoScaffold(modifier = modifier, platformInfo = viewModel.platformInfo) { + Content(modifier, state) + } + } + + @Composable + fun Content(modifier: Modifier, state: ViewState?) { + if (state == null) { + return + } + + Column( + modifier = modifier + .animateContentSize() + .fillMaxSize() + .themeColor(ThemeColor.SemanticColor.layer_3), + ) { + MarginModeViewHeader(state) + PlatformDivider() + Selection(state.crossMargin) + Spacer(modifier = Modifier.height(8.dp)) + Selection(state.isolatedMargin) + } + } + + @Composable + fun MarginModeViewHeader( + state: ViewState, + ) { + Row( + modifier = Modifier + .fillMaxWidth() + .padding(vertical = 8.dp), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.Start, + ) { + Spacer(modifier = Modifier.width(16.dp)) + Text( + modifier = Modifier.padding(horizontal = 0.dp), + style = TextStyle.dydxDefault + .themeFont( + fontSize = ThemeFont.FontSize.large, + fontType = ThemeFont.FontType.plus, + ) + .themeColor(ThemeColor.SemanticColor.text_primary), + text = state.title, + ) + Spacer(modifier = Modifier.width(4.dp)) + Text( + modifier = Modifier.padding(horizontal = 4.dp), + style = TextStyle.dydxDefault + .themeFont( + fontSize = ThemeFont.FontSize.large, + fontType = ThemeFont.FontType.plus, + ) + .themeColor(ThemeColor.SemanticColor.text_secondary), + text = state.asset, + ) + Spacer(modifier = Modifier.weight(1f)) + HeaderViewCloseBotton( + closeAction = state.closeAction, + ) + } + } + + @Composable + fun Selection(marginModeState: MarginTypeSelection) { + val shape = RoundedCornerShape(10.dp) + Row( + modifier = Modifier + .padding( + horizontal = ThemeShapes.HorizontalPadding, + vertical = ThemeShapes.VerticalPadding, + ) + .fillMaxWidth() + .background( + color = if (marginModeState.selected) { + ThemeColor.SemanticColor.layer_1.color + } else { + ThemeColor.SemanticColor.layer_4.color + }, + shape = shape, + ) + .border( + width = 1.dp, + color = if (marginModeState.selected) { + ThemeColor.SemanticColor.color_purple.color + } else { + ThemeColor.SemanticColor.layer_7.color + }, + shape = shape, + ) + .clip(shape) + .padding( + horizontal = ThemeShapes.HorizontalPadding, + vertical = 16.dp, + ) + .clickable { marginModeState.action() }, + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(8.dp), + ) { + Column( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 0.dp), + verticalArrangement = Arrangement.spacedBy(8.dp), + ) { + Text( + modifier = Modifier + .padding(horizontal = 0.dp), + style = TextStyle.dydxDefault + .themeFont( + fontSize = ThemeFont.FontSize.medium, + fontType = ThemeFont.FontType.book, + ) + .themeColor(ThemeColor.SemanticColor.text_primary), + text = marginModeState.title, + ) + + Text( + modifier = Modifier + .padding(horizontal = 0.dp), + style = TextStyle.dydxDefault + .themeFont( + fontSize = ThemeFont.FontSize.small, + fontType = ThemeFont.FontType.book, + ) + .themeColor(ThemeColor.SemanticColor.text_tertiary), + text = marginModeState.text, + ) + } + } + } +} diff --git a/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/tradeinput/DydxTradeInputMarginModeViewModel.kt b/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/tradeinput/DydxTradeInputMarginModeViewModel.kt new file mode 100644 index 00000000..9c6ba7a6 --- /dev/null +++ b/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/tradeinput/DydxTradeInputMarginModeViewModel.kt @@ -0,0 +1,57 @@ +package exchange.dydx.trading.feature.trade.tradeinput + +import androidx.lifecycle.ViewModel +import dagger.hilt.android.lifecycle.HiltViewModel +import exchange.dydx.abacus.output.input.MarginMode +import exchange.dydx.abacus.output.input.TradeInput +import exchange.dydx.abacus.protocols.LocalizerProtocol +import exchange.dydx.dydxstatemanager.AbacusStateManagerProtocol +import exchange.dydx.platformui.components.PlatformInfo +import exchange.dydx.trading.common.DydxViewModel +import exchange.dydx.trading.common.formatter.DydxFormatter +import exchange.dydx.trading.common.navigation.DydxRouter +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.map +import javax.inject.Inject + +@HiltViewModel +class DydxTradeInputMarginModeViewModel @Inject constructor( + private val localizer: LocalizerProtocol, + private val router: DydxRouter, + private val abacusStateManager: AbacusStateManagerProtocol, + private val formatter: DydxFormatter, + val platformInfo: PlatformInfo, +) : ViewModel(), DydxViewModel { + + val state: Flow = + abacusStateManager.state.tradeInput + .map { + createViewState(it) + } + .distinctUntilChanged() + + private fun createViewState(tradeInput: TradeInput?): DydxTradeInputMarginModeView.ViewState { + return DydxTradeInputMarginModeView.ViewState( + localizer.localize("APP.GENERAL.MARGIN_MODE"), + tradeInput?.marketId ?: "", + DydxTradeInputMarginModeView.MarginTypeSelection( + localizer.localize("APP.GENERAL.CROSS_MARGIN"), + localizer.localize("APP.GENERAL.CROSS_MARGIN_DESCRIPTION"), + tradeInput?.marginMode == MarginMode.cross, + { + }, + ), + DydxTradeInputMarginModeView.MarginTypeSelection( + localizer.localize("APP.GENERAL.ISOLATED_MARGIN"), + localizer.localize("APP.GENERAL.ISOLATED_MARGIN_DESCRIPTION"), + tradeInput?.marginMode == MarginMode.isolated, + { + }, + ), + null, + { + }, + ) + } +} diff --git a/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/tradeinput/DydxTradeInputMarketTypeView.kt b/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/tradeinput/DydxTradeInputMarketTypeView.kt deleted file mode 100644 index b2b36aa1..00000000 --- a/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/tradeinput/DydxTradeInputMarketTypeView.kt +++ /dev/null @@ -1,143 +0,0 @@ -package exchange.dydx.trading.feature.trade.tradeinput - -import androidx.compose.animation.animateContentSize -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.width -import androidx.compose.material.Text -import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.text.TextStyle -import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.dp -import androidx.hilt.navigation.compose.hiltViewModel -import exchange.dydx.platformui.components.PlatformInfoScaffold -import exchange.dydx.platformui.components.dividers.PlatformDivider -import exchange.dydx.platformui.designSystem.theme.ThemeColor -import exchange.dydx.platformui.designSystem.theme.ThemeFont -import exchange.dydx.platformui.designSystem.theme.ThemeShapes -import exchange.dydx.platformui.designSystem.theme.dydxDefault -import exchange.dydx.platformui.designSystem.theme.themeColor -import exchange.dydx.platformui.designSystem.theme.themeFont -import exchange.dydx.trading.common.component.DydxComponent -import exchange.dydx.trading.common.compose.collectAsStateWithLifecycle -import exchange.dydx.trading.common.navigation.DydxAnimation -import exchange.dydx.trading.common.theme.DydxThemedPreviewSurface -import exchange.dydx.trading.feature.shared.views.HeaderViewCloseBotton - -@Preview -@Composable -fun Preview_DydxTradeInputMarketTypeView() { - DydxThemedPreviewSurface { - DydxTradeInputMarketTypeView.Content( - Modifier, - DydxTradeInputMarketTypeView.ViewState.preview - ) - } -} - -object DydxTradeInputMarketTypeView : DydxComponent { - data class MarginTypeSelection( - val title: String, - val text: String, - val action: () -> Unit, - ) - - data class ViewState( - val title: String, - val asset: String, - val crossMargin: MarginTypeSelection, - val isolatedMargin: MarginTypeSelection, - val errorText: String?, - val closeAction: (() -> Unit)? = null, - ) { - companion object { - val preview = ViewState( - title = "Margin Mode", - asset = "for ETH-USD", - crossMargin = MarginTypeSelection( - title = "Cross Margin", - text = "This is the description text for cross margin", - action = {}, - ), - isolatedMargin = MarginTypeSelection( - title = "Isolated Margin", - text = "This is the description text for isolated margin", - action = {}, - ), - errorText = "Error", - ) - } - } - - @Composable - override fun Content(modifier: Modifier) { - val viewModel: DydxTradeInputMarketTypeViewModel = hiltViewModel() - - val state = viewModel.state.collectAsStateWithLifecycle(initialValue = null).value - PlatformInfoScaffold(modifier = modifier, platformInfo = viewModel.platformInfo) { - Content(modifier, state) - } - } - - @Composable - fun Content(modifier: Modifier, state: ViewState?) { - if (state == null) { - return - } - - Column( - modifier = modifier - .animateContentSize() - .fillMaxSize() - .themeColor(ThemeColor.SemanticColor.layer_3), - ) { - Row( - modifier - .fillMaxWidth() - .padding(vertical = ThemeShapes.VerticalPadding), - verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.Start, - ) { - Spacer(modifier = Modifier.width(16.dp)) - Text( - modifier = Modifier.padding(horizontal = 0.dp), - style = TextStyle.dydxDefault - .themeFont( - fontSize = ThemeFont.FontSize.large, - fontType = ThemeFont.FontType.plus, - ) - .themeColor(ThemeColor.SemanticColor.text_primary), - text = state.title, - ) - Spacer(modifier = Modifier.width(4.dp)) - Text( - modifier = Modifier.padding(horizontal = 4.dp), - style = TextStyle.dydxDefault - .themeFont( - fontSize = ThemeFont.FontSize.large, - fontType = ThemeFont.FontType.plus, - ) - .themeColor(ThemeColor.SemanticColor.text_secondary), - text = state.asset, - ) - Spacer(modifier = Modifier.weight(1f)) - HeaderViewCloseBotton( - closeAction = state.closeAction, - ) - } - - PlatformDivider() - - - - } - } -} - diff --git a/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/tradeinput/DydxTradeInputMarketTypeViewModel.kt b/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/tradeinput/DydxTradeInputMarketTypeViewModel.kt deleted file mode 100644 index 0269d09b..00000000 --- a/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/tradeinput/DydxTradeInputMarketTypeViewModel.kt +++ /dev/null @@ -1,41 +0,0 @@ -package exchange.dydx.trading.feature.trade.tradeinput - -import androidx.lifecycle.ViewModel -import dagger.hilt.android.lifecycle.HiltViewModel -import exchange.dydx.abacus.output.PerpetualMarketSummary -import exchange.dydx.abacus.protocols.LocalizerProtocol -import exchange.dydx.dydxstatemanager.AbacusStateManagerProtocol -import exchange.dydx.platformui.components.PlatformInfo -import exchange.dydx.trading.common.AppConfig -import exchange.dydx.trading.common.DydxViewModel -import exchange.dydx.trading.common.formatter.DydxFormatter -import exchange.dydx.trading.common.navigation.DydxRouter -import exchange.dydx.trading.common.theme.DydxTheme -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.distinctUntilChanged -import kotlinx.coroutines.flow.map -import javax.inject.Inject - -@HiltViewModel -class DydxTradeInputMarketTypeViewModel @Inject constructor( - private val localizer: LocalizerProtocol, - private val router: DydxRouter, - private val abacusStateManager: AbacusStateManagerProtocol, - private val formatter: DydxFormatter, - val platformInfo: PlatformInfo, -) : ViewModel(), DydxViewModel { - - val state: Flow = - abacusStateManager.state.marketSummary - .map { - createViewState(it) - } - .distinctUntilChanged() - - private fun createViewState(marketSummary: PerpetualMarketSummary?): DydxTradeInputMarketTypeView.ViewState { - val volume = formatter.dollarVolume(marketSummary?.volume24HUSDC) - return DydxTradeInputMarketTypeView.ViewState( - text = volume, - ) - } -} diff --git a/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/tradeinput/DydxTradeInputTargetLeverageView.kt b/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/tradeinput/DydxTradeInputTargetLeverageView.kt index 74d0c9b2..5ed2630e 100644 --- a/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/tradeinput/DydxTradeInputTargetLeverageView.kt +++ b/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/tradeinput/DydxTradeInputTargetLeverageView.kt @@ -4,11 +4,9 @@ import androidx.compose.animation.animateContentSize import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding -import androidx.compose.material.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier @@ -21,29 +19,36 @@ import exchange.dydx.platformui.designSystem.theme.ThemeShapes import exchange.dydx.platformui.designSystem.theme.themeColor import exchange.dydx.trading.common.component.DydxComponent import exchange.dydx.trading.common.compose.collectAsStateWithLifecycle -import exchange.dydx.trading.common.navigation.DydxAnimation import exchange.dydx.trading.common.theme.DydxThemedPreviewSurface import exchange.dydx.trading.feature.shared.views.HeaderViewCloseBotton +data class LeverageTextAndValue(val text: String, val value: Double) + @Preview @Composable fun Preview_DydxTradeInputTargetLeverageView() { DydxThemedPreviewSurface { DydxTradeInputTargetLeverageView.Content( Modifier, - DydxTradeInputTargetLeverageView.ViewState.preview + DydxTradeInputTargetLeverageView.ViewState.preview, ) } } object DydxTradeInputTargetLeverageView : DydxComponent { data class ViewState( + val title: String?, val text: String?, + val leverageText: String?, + val leverageOptions: List?, val closeAction: (() -> Unit)? = null, ) { companion object { val preview = ViewState( - text = "1.0M", + title = "title", + text = "text", + leverageText = "1.0", + leverageOptions = listOf(), ) } } @@ -83,8 +88,6 @@ object DydxTradeInputTargetLeverageView : DydxComponent { } PlatformDivider() - } } } - diff --git a/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/tradeinput/DydxTradeInputTargetLeverageViewModel.kt b/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/tradeinput/DydxTradeInputTargetLeverageViewModel.kt index 4e4edbff..1d21a0cd 100644 --- a/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/tradeinput/DydxTradeInputTargetLeverageViewModel.kt +++ b/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/tradeinput/DydxTradeInputTargetLeverageViewModel.kt @@ -2,15 +2,13 @@ package exchange.dydx.trading.feature.trade.tradeinput import androidx.lifecycle.ViewModel import dagger.hilt.android.lifecycle.HiltViewModel -import exchange.dydx.abacus.output.PerpetualMarketSummary +import exchange.dydx.abacus.output.input.TradeInput import exchange.dydx.abacus.protocols.LocalizerProtocol import exchange.dydx.dydxstatemanager.AbacusStateManagerProtocol import exchange.dydx.platformui.components.PlatformInfo -import exchange.dydx.trading.common.AppConfig import exchange.dydx.trading.common.DydxViewModel import exchange.dydx.trading.common.formatter.DydxFormatter import exchange.dydx.trading.common.navigation.DydxRouter -import exchange.dydx.trading.common.theme.DydxTheme import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.map @@ -26,16 +24,41 @@ class DydxTradeInputTargetLeverageViewModel @Inject constructor( ) : ViewModel(), DydxViewModel { val state: Flow = - abacusStateManager.state.marketSummary + abacusStateManager.state.tradeInput .map { createViewState(it) } .distinctUntilChanged() - private fun createViewState(marketSummary: PerpetualMarketSummary?): DydxTradeInputTargetLeverageView.ViewState { - val volume = formatter.dollarVolume(marketSummary?.volume24HUSDC) + private fun createViewState(tradeInput: TradeInput?): DydxTradeInputTargetLeverageView.ViewState { + val targetLeverage = tradeInput?.targetLeverage ?: 1.0 + val maxLeverage = tradeInput?.options?.maxLeverage ?: 5.0 + val leverages = leverageOptions(maxLeverage) return DydxTradeInputTargetLeverageView.ViewState( - text = volume, + localizer.localize("APP.TRADE.ADJUST_TARGET_LEVERAGE"), + localizer.localize("APP.TRADE.ADJUST_TARGET_LEVERAGE_DESCRIPTION"), + formatter.localFormatted(targetLeverage, 1), + leverages, + { + }, ) } + + private fun leverageOptions(max: Double): List { + val leverages = mutableListOf() + if (max > 1.0) { + leverages.add(LeverageTextAndValue("1.0", 1.0)) + } + if (max > 2.0) { + leverages.add(LeverageTextAndValue("2.0", 2.0)) + } + if (max > 5.0) { + leverages.add(LeverageTextAndValue("5.0", 2.0)) + } + if (max > 10.0) { + leverages.add(LeverageTextAndValue("10.0", 2.0)) + } + leverages.add(LeverageTextAndValue(localizer.localize("APP.GENERAL.MAX"), max)) + return leverages + } } diff --git a/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/tradeinput/DydxTradeInputView.kt b/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/tradeinput/DydxTradeInputView.kt index 250e42cf..e5507b5f 100644 --- a/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/tradeinput/DydxTradeInputView.kt +++ b/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/tradeinput/DydxTradeInputView.kt @@ -5,7 +5,6 @@ import androidx.compose.animation.animateContentSize import androidx.compose.animation.expandHorizontally import androidx.compose.animation.shrinkHorizontally import androidx.compose.foundation.ExperimentalFoundationApi -import androidx.compose.foundation.background import androidx.compose.foundation.gestures.detectTapGestures import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box @@ -40,15 +39,11 @@ import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel -import androidx.lifecycle.ViewModel -import dagger.hilt.android.lifecycle.HiltViewModel import exchange.dydx.abacus.protocols.LocalizerProtocol import exchange.dydx.platformui.components.buttons.PlatformButton import exchange.dydx.platformui.components.buttons.PlatformButtonState import exchange.dydx.platformui.components.dividers.PlatformDivider -import exchange.dydx.platformui.designSystem.theme.ThemeColor import exchange.dydx.platformui.designSystem.theme.ThemeShapes -import exchange.dydx.platformui.designSystem.theme.color import exchange.dydx.trading.common.component.DydxComponent import exchange.dydx.trading.common.compose.collectAsStateWithLifecycle import exchange.dydx.trading.common.theme.DydxThemedPreviewSurface @@ -71,7 +66,6 @@ import exchange.dydx.trading.feature.trade.tradeinput.components.inputfields.siz import exchange.dydx.trading.feature.trade.tradeinput.components.inputfields.timeinforce.DydxTradeInputTimeInForceView import exchange.dydx.trading.feature.trade.tradeinput.components.inputfields.triggerprice.DydxTradeInputTriggerPriceView import exchange.dydx.trading.feature.trade.tradeinput.components.sheettip.DydxTradeSheetTipView -import kotlinx.coroutines.flow.Flow @OptIn(ExperimentalMaterial3Api::class) @Preview @@ -204,14 +198,14 @@ object DydxTradeInputView : DydxComponent { PlatformButton( modifier = Modifier.height(52.dp), state = PlatformButtonState.Secondary, - text = "Isolated" + text = "Isolated", ) { state.onMarketType() } PlatformButton( modifier = Modifier.height(52.dp), state = PlatformButtonState.Secondary, - text = "2x" + text = "2x", ) { state.onTargetLeverage() } @@ -394,4 +388,4 @@ object DydxTradeInputView : DydxComponent { } } } -} \ No newline at end of file +} From 38ddb8a773337d19bba086ffe784db3dc86bb420 Mon Sep 17 00:00:00 2001 From: John Huang Date: Wed, 3 Apr 2024 15:44:20 -0700 Subject: [PATCH 03/21] Change bg color --- .../feature/trade/tradeinput/DydxTradeInputMarginModeView.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/tradeinput/DydxTradeInputMarginModeView.kt b/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/tradeinput/DydxTradeInputMarginModeView.kt index 41a08e93..75079ce8 100644 --- a/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/tradeinput/DydxTradeInputMarginModeView.kt +++ b/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/tradeinput/DydxTradeInputMarginModeView.kt @@ -105,7 +105,7 @@ object DydxTradeInputMarginModeView : DydxComponent { modifier = modifier .animateContentSize() .fillMaxSize() - .themeColor(ThemeColor.SemanticColor.layer_3), + .themeColor(ThemeColor.SemanticColor.layer_4), ) { MarginModeViewHeader(state) PlatformDivider() From f0ed82b62808a1cbe9d52c89c94b5d04ba9b4ef5 Mon Sep 17 00:00:00 2001 From: John Huang Date: Thu, 4 Apr 2024 15:00:32 -0700 Subject: [PATCH 04/21] MOB-360 rough UX for target leverage screen --- .../shared/scarfolds/InputFieldScarfold.kt | 12 +- .../DydxTradeInputMarginModeView.kt | 4 +- .../DydxTradeInputTargetLeverageView.kt | 195 ++++++++++++++++-- .../DydxTradeInputTargetLeverageViewModel.kt | 3 +- 4 files changed, 192 insertions(+), 22 deletions(-) diff --git a/v4/feature/shared/src/main/java/exchange/dydx/trading/feature/shared/scarfolds/InputFieldScarfold.kt b/v4/feature/shared/src/main/java/exchange/dydx/trading/feature/shared/scarfolds/InputFieldScarfold.kt index a367ef79..9739b69c 100644 --- a/v4/feature/shared/src/main/java/exchange/dydx/trading/feature/shared/scarfolds/InputFieldScarfold.kt +++ b/v4/feature/shared/src/main/java/exchange/dydx/trading/feature/shared/scarfolds/InputFieldScarfold.kt @@ -6,6 +6,7 @@ import androidx.compose.foundation.layout.Box import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip import androidx.compose.ui.unit.dp import exchange.dydx.platformui.designSystem.theme.ThemeColor import exchange.dydx.platformui.designSystem.theme.color @@ -15,14 +16,19 @@ fun InputFieldScarfold( modifier: Modifier = Modifier, content: @Composable () -> Unit, ) { + val shape = RoundedCornerShape(8.dp) Box( modifier = modifier - .background(color = ThemeColor.SemanticColor.layer_4.color, shape = RoundedCornerShape(8.dp)) + .background( + color = ThemeColor.SemanticColor.layer_4.color, + shape = shape, + ) .border( width = 1.dp, color = ThemeColor.SemanticColor.layer_6.color, - shape = RoundedCornerShape(8.dp), - ), + shape = shape, + ) + .clip(shape), ) { content() } diff --git a/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/tradeinput/DydxTradeInputMarginModeView.kt b/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/tradeinput/DydxTradeInputMarginModeView.kt index 75079ce8..481a616d 100644 --- a/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/tradeinput/DydxTradeInputMarginModeView.kt +++ b/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/tradeinput/DydxTradeInputMarginModeView.kt @@ -107,7 +107,7 @@ object DydxTradeInputMarginModeView : DydxComponent { .fillMaxSize() .themeColor(ThemeColor.SemanticColor.layer_4), ) { - MarginModeViewHeader(state) + NavigationHeader(state) PlatformDivider() Selection(state.crossMargin) Spacer(modifier = Modifier.height(8.dp)) @@ -116,7 +116,7 @@ object DydxTradeInputMarginModeView : DydxComponent { } @Composable - fun MarginModeViewHeader( + fun NavigationHeader( state: ViewState, ) { Row( diff --git a/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/tradeinput/DydxTradeInputTargetLeverageView.kt b/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/tradeinput/DydxTradeInputTargetLeverageView.kt index 5ed2630e..5b9bb166 100644 --- a/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/tradeinput/DydxTradeInputTargetLeverageView.kt +++ b/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/tradeinput/DydxTradeInputTargetLeverageView.kt @@ -4,23 +4,39 @@ import androidx.compose.animation.animateContentSize import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.width +import androidx.compose.material.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.text.TextStyle import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel +import exchange.dydx.abacus.protocols.LocalizerProtocol import exchange.dydx.platformui.components.PlatformInfoScaffold +import exchange.dydx.platformui.components.buttons.PlatformButton +import exchange.dydx.platformui.components.buttons.PlatformButtonState +import exchange.dydx.platformui.components.buttons.PlatformPillItem import exchange.dydx.platformui.components.dividers.PlatformDivider +import exchange.dydx.platformui.components.tabgroups.PlatformTabGroup import exchange.dydx.platformui.designSystem.theme.ThemeColor +import exchange.dydx.platformui.designSystem.theme.ThemeFont import exchange.dydx.platformui.designSystem.theme.ThemeShapes +import exchange.dydx.platformui.designSystem.theme.dydxDefault import exchange.dydx.platformui.designSystem.theme.themeColor +import exchange.dydx.platformui.designSystem.theme.themeFont import exchange.dydx.trading.common.component.DydxComponent import exchange.dydx.trading.common.compose.collectAsStateWithLifecycle import exchange.dydx.trading.common.theme.DydxThemedPreviewSurface +import exchange.dydx.trading.common.theme.MockLocalizer +import exchange.dydx.trading.feature.shared.scarfolds.InputFieldScarfold import exchange.dydx.trading.feature.shared.views.HeaderViewCloseBotton +import exchange.dydx.trading.feature.shared.views.LabeledTextInput data class LeverageTextAndValue(val text: String, val value: Double) @@ -37,16 +53,14 @@ fun Preview_DydxTradeInputTargetLeverageView() { object DydxTradeInputTargetLeverageView : DydxComponent { data class ViewState( - val title: String?, - val text: String?, + val localizer: LocalizerProtocol, val leverageText: String?, val leverageOptions: List?, val closeAction: (() -> Unit)? = null, ) { companion object { val preview = ViewState( - title = "title", - text = "text", + localizer = MockLocalizer(), leverageText = "1.0", leverageOptions = listOf(), ) @@ -73,21 +87,172 @@ object DydxTradeInputTargetLeverageView : DydxComponent { modifier = modifier .animateContentSize() .fillMaxSize() - .themeColor(ThemeColor.SemanticColor.layer_3), + .themeColor(ThemeColor.SemanticColor.layer_4), ) { - Row( - modifier - .fillMaxWidth() - .padding(vertical = ThemeShapes.VerticalPadding), - verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.Start, - ) { - HeaderViewCloseBotton( - closeAction = state.closeAction, + NavigationHeader(state) + PlatformDivider() + Description(state) + LeverageEditField(state) + LeverageOptions(state) + Spacer(modifier = Modifier.weight(1f)) + ActionButton(state) + } + } + + @Composable + fun NavigationHeader( + state: ViewState, + ) { + Row( + modifier = Modifier + .fillMaxWidth() + .padding(vertical = 8.dp), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.Start, + ) { + Spacer(modifier = Modifier.width(16.dp)) + Text( + modifier = Modifier.padding(horizontal = 0.dp), + style = TextStyle.dydxDefault + .themeFont( + fontSize = ThemeFont.FontSize.large, + fontType = ThemeFont.FontType.plus, + ) + .themeColor(ThemeColor.SemanticColor.text_primary), + text = state.localizer.localize("APP.TRADE.ADJUST_TARGET_LEVERAGE"), + ) + Spacer(modifier = Modifier.weight(1f)) + HeaderViewCloseBotton( + closeAction = state.closeAction, + ) + } + } + + @Composable + fun Description( + state: ViewState, + ) { + Row( + modifier = Modifier + .fillMaxWidth() + .padding(vertical = 8.dp), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.Start, + ) { + Spacer(modifier = Modifier.width(16.dp)) + Text( + modifier = Modifier.padding(horizontal = 0.dp), + text = state.localizer.localize("APP.TRADE.ADJUST_TARGET_LEVERAGE_DESCRIPTION"), + style = TextStyle.dydxDefault + .themeFont(fontSize = ThemeFont.FontSize.small), + ) + } + } + + @Composable + fun LeverageEditField(state: ViewState?) { + Row( + modifier = Modifier + .padding( + horizontal = ThemeShapes.HorizontalPadding, + vertical = 8.dp, + ) + .fillMaxWidth(), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(8.dp), + ) { + InputFieldScarfold(modifier = Modifier) { + LabeledTextInput.Content( + modifier = Modifier, + state = LabeledTextInput.ViewState( + localizer = MockLocalizer(), + label = state?.localizer?.localize("APP.TRADE.TARGET_LEVERAGE"), + value = state?.leverageText ?: "", + onValueChanged = {}, + ), ) } + } + } - PlatformDivider() + @Composable + fun LeverageOptions(state: ViewState?) { + Row( + modifier = Modifier + .padding( + horizontal = ThemeShapes.HorizontalPadding, + vertical = 8.dp, + ) + .fillMaxWidth(), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(8.dp), + ) { + PlatformTabGroup( + modifier = Modifier.fillMaxWidth(), + scrollingEnabled = false, + items = state?.leverageOptions?.map { + { modifier -> + PlatformPillItem( + modifier = Modifier + .padding( + vertical = 4.dp, + horizontal = 8.dp, + ), + backgroundColor = ThemeColor.SemanticColor.layer_5, + ) { + Text( + text = it.text, + modifier = Modifier, + style = TextStyle.dydxDefault + .themeColor(ThemeColor.SemanticColor.text_tertiary) + .themeFont(fontSize = ThemeFont.FontSize.small), + + ) + } + } + } ?: listOf(), + selectedItems = state?.leverageOptions?.map { + { modifier -> + PlatformPillItem( + modifier = Modifier + .padding( + vertical = 4.dp, + horizontal = 8.dp, + ), + backgroundColor = ThemeColor.SemanticColor.layer_2, + ) { + Text( + text = it.text, + modifier = Modifier, + style = TextStyle.dydxDefault + .themeColor(ThemeColor.SemanticColor.text_primary) + .themeFont(fontSize = ThemeFont.FontSize.small), + + ) + } + } + } ?: listOf(), + equalWeight = false, + currentSelection = state?.leverageOptions?.indexOfFirst { + it.text == state.leverageText + }, + onSelectionChanged = {}, + ) + } + } + + @Composable + fun ActionButton(state: ViewState?) { + PlatformButton( + modifier = Modifier + .padding( + horizontal = ThemeShapes.HorizontalPadding, + vertical = ThemeShapes.VerticalPadding, + ) + .fillMaxWidth(), + text = state?.localizer?.localize("APP.TRADE.CONFIRM_LEVERAGE"), + state = PlatformButtonState.Primary, + ) { } } } diff --git a/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/tradeinput/DydxTradeInputTargetLeverageViewModel.kt b/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/tradeinput/DydxTradeInputTargetLeverageViewModel.kt index 1d21a0cd..f3c9fa0d 100644 --- a/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/tradeinput/DydxTradeInputTargetLeverageViewModel.kt +++ b/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/tradeinput/DydxTradeInputTargetLeverageViewModel.kt @@ -35,8 +35,7 @@ class DydxTradeInputTargetLeverageViewModel @Inject constructor( val maxLeverage = tradeInput?.options?.maxLeverage ?: 5.0 val leverages = leverageOptions(maxLeverage) return DydxTradeInputTargetLeverageView.ViewState( - localizer.localize("APP.TRADE.ADJUST_TARGET_LEVERAGE"), - localizer.localize("APP.TRADE.ADJUST_TARGET_LEVERAGE_DESCRIPTION"), + localizer, formatter.localFormatted(targetLeverage, 1), leverages, { From d1c0ea9323d1698ab667b88043238f1425936418 Mon Sep 17 00:00:00 2001 From: John Huang Date: Thu, 4 Apr 2024 15:19:26 -0700 Subject: [PATCH 05/21] Fixed PR --- .../exchange/dydx/trading/feature/trade/DydxTradeRouter.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/DydxTradeRouter.kt b/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/DydxTradeRouter.kt index 48216333..074ecacf 100644 --- a/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/DydxTradeRouter.kt +++ b/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/DydxTradeRouter.kt @@ -61,7 +61,7 @@ fun NavGraphBuilder.tradeGraph( dydxComposable( router = appRouter, route = TradeRoutes.margin_type, - deepLinks = appRouter.deeplinks(TradeRoutes.status), + deepLinks = appRouter.deeplinks(TradeRoutes.margin_type), ) { navBackStackEntry -> DydxTradeInputMarginModeView.Content(Modifier) } @@ -69,7 +69,7 @@ fun NavGraphBuilder.tradeGraph( dydxComposable( router = appRouter, route = TradeRoutes.target_leverage, - deepLinks = appRouter.deeplinks(TradeRoutes.status), + deepLinks = appRouter.deeplinks(TradeRoutes.target_leverage), ) { navBackStackEntry -> DydxTradeInputTargetLeverageView.Content(Modifier) } From b376d4f4829ac001463302dcc317c9fd70b3f17c Mon Sep 17 00:00:00 2001 From: John Huang Date: Fri, 5 Apr 2024 10:32:58 -0700 Subject: [PATCH 06/21] move modifier to param --- .../DydxTradeInputMarginModeView.kt | 25 ++++++++++++++----- 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/tradeinput/DydxTradeInputMarginModeView.kt b/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/tradeinput/DydxTradeInputMarginModeView.kt index 75079ce8..f5401ace 100644 --- a/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/tradeinput/DydxTradeInputMarginModeView.kt +++ b/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/tradeinput/DydxTradeInputMarginModeView.kt @@ -107,20 +107,30 @@ object DydxTradeInputMarginModeView : DydxComponent { .fillMaxSize() .themeColor(ThemeColor.SemanticColor.layer_4), ) { - MarginModeViewHeader(state) + MarginModeViewHeader( + modifier = Modifier, + state = state, + ) PlatformDivider() - Selection(state.crossMargin) + Selection( + modifier = Modifier, + marginModeState = state.crossMargin, + ) Spacer(modifier = Modifier.height(8.dp)) - Selection(state.isolatedMargin) + Selection( + modifier = Modifier, + marginModeState = state.isolatedMargin, + ) } } @Composable fun MarginModeViewHeader( + modifier: Modifier, state: ViewState, ) { Row( - modifier = Modifier + modifier = modifier .fillMaxWidth() .padding(vertical = 8.dp), verticalAlignment = Alignment.CenterVertically, @@ -156,10 +166,13 @@ object DydxTradeInputMarginModeView : DydxComponent { } @Composable - fun Selection(marginModeState: MarginTypeSelection) { + fun Selection( + modifier: Modifier, + marginModeState: MarginTypeSelection + ) { val shape = RoundedCornerShape(10.dp) Row( - modifier = Modifier + modifier = modifier .padding( horizontal = ThemeShapes.HorizontalPadding, vertical = ThemeShapes.VerticalPadding, From fc2eb187e5f587323178464ef7216f90b79b510d Mon Sep 17 00:00:00 2001 From: John Huang Date: Fri, 5 Apr 2024 11:36:23 -0700 Subject: [PATCH 07/21] In the middle of coding --- .../trade/margin/DydxAdjustMarginInputView.kt | 150 ++++++++++++++++++ .../margin/DydxAdjustMarginInputViewModel.kt | 54 +++++++ 2 files changed, 204 insertions(+) create mode 100644 v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/margin/DydxAdjustMarginInputView.kt create mode 100644 v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/margin/DydxAdjustMarginInputViewModel.kt diff --git a/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/margin/DydxAdjustMarginInputView.kt b/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/margin/DydxAdjustMarginInputView.kt new file mode 100644 index 00000000..b290c496 --- /dev/null +++ b/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/margin/DydxAdjustMarginInputView.kt @@ -0,0 +1,150 @@ +package exchange.dydx.trading.feature.trade.margin + +import androidx.compose.animation.animateContentSize +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.height +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import androidx.hilt.navigation.compose.hiltViewModel +import exchange.dydx.platformui.components.PlatformInfoScaffold +import exchange.dydx.platformui.components.dividers.PlatformDivider +import exchange.dydx.platformui.designSystem.theme.ThemeColor +import exchange.dydx.platformui.designSystem.theme.themeColor +import exchange.dydx.trading.common.component.DydxComponent +import exchange.dydx.trading.common.compose.collectAsStateWithLifecycle +import exchange.dydx.trading.common.theme.DydxThemedPreviewSurface +import exchange.dydx.trading.feature.trade.tradeinput.DydxTradeInputMarginModeView +import exchange.dydx.trading.feature.trade.tradeinput.DydxTradeInputMarginModeViewModel + +@Preview +@Composable +fun Preview_DydxAdjustMarginInputView() { + DydxThemedPreviewSurface { + DydxAdjustMarginInputView.Content(Modifier, DydxAdjustMarginInputView.ViewState.preview) + } +} + +object DydxAdjustMarginInputView : DydxComponent { + enum class MarginDirection { + Add, + Remove, + } + + data class PercentageOption( + val text: String, + val percentage: Double, + ) + + data class SubaccountReceipt( + val freeCollateral: List, + val marginUsage: List, + ) + + data class PositionReceipt( + val freeCollateral: List, + val leverage: List, + val liquidationPrice: List, + ) + + data class ViewState( + val direction: MarginDirection = MarginDirection.Add, + val percentageText: String?, + val percentageOptions: List, + val amountText: String?, + val subaccountReceipt: SubaccountReceipt, + val positionReceipt: PositionReceipt, + val error: String?, + ) { + companion object { + val preview = ViewState( + direction = MarginDirection.Add, + percentageText = "50%", + percentageOptions = listOf( + PercentageOption("10%", 0.1), + PercentageOption("20%", 0.2), + PercentageOption("30%", 0.3), + PercentageOption("50%", 0.5), + ), + amountText = "500", + subaccountReceipt = SubaccountReceipt( + freeCollateral = listOf("1000.00", "500.00"), + marginUsage = listOf("19.34", "38.45"), + ), + positionReceipt = PositionReceipt( + freeCollateral = listOf("1000.00", "1500.00"), + leverage = listOf("3.1", "2.4"), + liquidationPrice = listOf("1200.00", "1000.00"), + ), + error = null, + ) + } + } + + @Composable + override fun Content(modifier: Modifier) { + val viewModel: DydxAdjustMarginInputViewModel = hiltViewModel() + + val state = viewModel.state.collectAsStateWithLifecycle(initialValue = null).value + PlatformInfoScaffold(modifier = modifier, platformInfo = viewModel.platformInfo) { + Content(modifier, state) + } + } + + @Composable + fun Content(modifier: Modifier, state: ViewState?) { + if (state == null) { + return + } + + Column( + modifier = modifier + .animateContentSize() + .fillMaxSize() + .themeColor(ThemeColor.SemanticColor.layer_4), + ) { + NavigationHeader( + modifier = Modifier, + state = state + ) + PlatformDivider() + // Add to remove margin + MarginDirection( + modifier = Modifier, + state = state, + ) + Spacer(modifier = Modifier.height(8.dp)) + PercentageOptions( + modifier = Modifier, + state = state, + ) + Spacer(modifier = Modifier.height(8.dp)) + InputAndSubaccountReceipt( + modifier = Modifier, + state = state, + ) + Spacer(modifier = Modifier.weight(1f)) + if (state.error == null) { + LiquidationPrice( + modifier = Modifier, + state = state, + ) + } else { + Error( + modifier = Modifier, + error = state.error, + ) + } + Spacer(modifier = Modifier.height(8.dp)) + PositionReceiptAndButton( + modifier = Modifier, + state = state, + ) + } + } +} + diff --git a/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/margin/DydxAdjustMarginInputViewModel.kt b/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/margin/DydxAdjustMarginInputViewModel.kt new file mode 100644 index 00000000..a500edc6 --- /dev/null +++ b/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/margin/DydxAdjustMarginInputViewModel.kt @@ -0,0 +1,54 @@ +package exchange.dydx.trading.feature.trade.margin + +import androidx.lifecycle.ViewModel +import dagger.hilt.android.lifecycle.HiltViewModel +import exchange.dydx.abacus.output.input.TradeInput +import exchange.dydx.abacus.protocols.LocalizerProtocol +import exchange.dydx.dydxstatemanager.AbacusStateManagerProtocol +import exchange.dydx.trading.common.DydxViewModel +import exchange.dydx.trading.common.formatter.DydxFormatter +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.map +import javax.inject.Inject + +@HiltViewModel +class DydxAdjustMarginInputViewModel @Inject constructor( + private val localizer: LocalizerProtocol, + private val abacusStateManager: AbacusStateManagerProtocol, + private val formatter: DydxFormatter, +) : ViewModel(), DydxViewModel { + + val state: Flow = abacusStateManager.state.tradeInput + .map { + createViewState(it) + } + .distinctUntilChanged() + + private fun createViewState(tradeInput: TradeInput?): DydxAdjustMarginInputView.ViewState { + /* + Abacus not implemented for adjust margin yet. This is a placeholder. + */ + return DydxAdjustMarginInputView.ViewState( + direction = DydxAdjustMarginInputView.MarginDirection.Add, + percentageText = "50%", + percentageOptions = listOf( + DydxAdjustMarginInputView.PercentageOption("10%", 0.1), + DydxAdjustMarginInputView.PercentageOption("20%", 0.2), + DydxAdjustMarginInputView.PercentageOption("30%", 0.3), + DydxAdjustMarginInputView.PercentageOption("50%", 0.5), + ), + amountText = "500", + subaccountReceipt = DydxAdjustMarginInputView.SubaccountReceipt( + freeCollateral = listOf("1000.00", "500.00"), + marginUsage = listOf("19.34", "38.45"), + ), + positionReceipt = DydxAdjustMarginInputView.PositionReceipt( + freeCollateral = listOf("1000.00", "1500.00"), + leverage = listOf("3.1", "2.4"), + liquidationPrice = listOf("1200.00", "1000.00"), + ), + error = null, + ) + } +} From 961ab59f87ba640ebcd7fc006761a70fff373257 Mon Sep 17 00:00:00 2001 From: John Huang Date: Fri, 5 Apr 2024 11:37:01 -0700 Subject: [PATCH 08/21] lint --- .../trade/tradeinput/DydxTradeInputMarginModeView.kt | 6 +++--- .../trade/tradeinput/DydxTradeInputTargetLeverageView.kt | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/tradeinput/DydxTradeInputMarginModeView.kt b/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/tradeinput/DydxTradeInputMarginModeView.kt index aa5351ac..fa6c8d29 100644 --- a/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/tradeinput/DydxTradeInputMarginModeView.kt +++ b/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/tradeinput/DydxTradeInputMarginModeView.kt @@ -109,17 +109,17 @@ object DydxTradeInputMarginModeView : DydxComponent { ) { NavigationHeader( modifier = Modifier, - state = state + state = state, ) PlatformDivider() Selection( modifier = Modifier, - marginModeState = state.crossMargin + marginModeState = state.crossMargin, ) Spacer(modifier = Modifier.height(8.dp)) Selection( modifier = Modifier, - marginModeState = state.isolatedMargin + marginModeState = state.isolatedMargin, ) } } diff --git a/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/tradeinput/DydxTradeInputTargetLeverageView.kt b/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/tradeinput/DydxTradeInputTargetLeverageView.kt index 512b9e8f..15cecb6e 100644 --- a/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/tradeinput/DydxTradeInputTargetLeverageView.kt +++ b/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/tradeinput/DydxTradeInputTargetLeverageView.kt @@ -230,7 +230,7 @@ object DydxTradeInputTargetLeverageView : DydxComponent { .themeColor(ThemeColor.SemanticColor.text_tertiary) .themeFont(fontSize = ThemeFont.FontSize.small), - ) + ) } } } ?: listOf(), @@ -251,7 +251,7 @@ object DydxTradeInputTargetLeverageView : DydxComponent { .themeColor(ThemeColor.SemanticColor.text_primary) .themeFont(fontSize = ThemeFont.FontSize.small), - ) + ) } } } ?: listOf(), From c28ebb0391815211ffd2e4f1e3204181ed02314f Mon Sep 17 00:00:00 2001 From: John Huang Date: Wed, 24 Apr 2024 09:35:07 -0700 Subject: [PATCH 09/21] There is no longer InputFieldScarfold --- .../DydxTradeInputTargetLeverageView.kt | 25 ++++++++----------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/tradeinput/DydxTradeInputTargetLeverageView.kt b/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/tradeinput/DydxTradeInputTargetLeverageView.kt index 30c828bf..4240ae96 100644 --- a/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/tradeinput/DydxTradeInputTargetLeverageView.kt +++ b/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/tradeinput/DydxTradeInputTargetLeverageView.kt @@ -34,7 +34,6 @@ import exchange.dydx.trading.common.component.DydxComponent import exchange.dydx.trading.common.compose.collectAsStateWithLifecycle import exchange.dydx.trading.common.theme.DydxThemedPreviewSurface import exchange.dydx.trading.common.theme.MockLocalizer -import exchange.dydx.trading.feature.shared.scarfolds.InputFieldScarfold import exchange.dydx.trading.feature.shared.views.HeaderViewCloseBotton import exchange.dydx.trading.feature.shared.views.LabeledTextInput @@ -181,17 +180,15 @@ object DydxTradeInputTargetLeverageView : DydxComponent { verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(8.dp), ) { - InputFieldScarfold(modifier = Modifier) { - LabeledTextInput.Content( - modifier = Modifier, - state = LabeledTextInput.ViewState( - localizer = MockLocalizer(), - label = state?.localizer?.localize("APP.TRADE.TARGET_LEVERAGE"), - value = state?.leverageText ?: "", - onValueChanged = {}, - ), - ) - } + LabeledTextInput.Content( + modifier = Modifier, + state = LabeledTextInput.ViewState( + localizer = MockLocalizer(), + label = state?.localizer?.localize("APP.TRADE.TARGET_LEVERAGE"), + value = state?.leverageText ?: "", + onValueChanged = {}, + ), + ) } } @@ -230,7 +227,7 @@ object DydxTradeInputTargetLeverageView : DydxComponent { .themeColor(ThemeColor.SemanticColor.text_tertiary) .themeFont(fontSize = ThemeFont.FontSize.small), - ) + ) } } } ?: listOf(), @@ -251,7 +248,7 @@ object DydxTradeInputTargetLeverageView : DydxComponent { .themeColor(ThemeColor.SemanticColor.text_primary) .themeFont(fontSize = ThemeFont.FontSize.small), - ) + ) } } } ?: listOf(), From aad0984e2cc2287806d4e7241dd7761a1b15394a Mon Sep 17 00:00:00 2001 From: John Huang Date: Wed, 24 Apr 2024 09:40:53 -0700 Subject: [PATCH 10/21] Put back InputFieldScaffold --- .../DydxTradeInputTargetLeverageView.kt | 25 +++++++++++-------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/tradeinput/DydxTradeInputTargetLeverageView.kt b/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/tradeinput/DydxTradeInputTargetLeverageView.kt index 4240ae96..e6fdd29b 100644 --- a/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/tradeinput/DydxTradeInputTargetLeverageView.kt +++ b/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/tradeinput/DydxTradeInputTargetLeverageView.kt @@ -34,6 +34,7 @@ import exchange.dydx.trading.common.component.DydxComponent import exchange.dydx.trading.common.compose.collectAsStateWithLifecycle import exchange.dydx.trading.common.theme.DydxThemedPreviewSurface import exchange.dydx.trading.common.theme.MockLocalizer +import exchange.dydx.trading.feature.shared.scaffolds.InputFieldScaffold import exchange.dydx.trading.feature.shared.views.HeaderViewCloseBotton import exchange.dydx.trading.feature.shared.views.LabeledTextInput @@ -180,15 +181,17 @@ object DydxTradeInputTargetLeverageView : DydxComponent { verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(8.dp), ) { - LabeledTextInput.Content( - modifier = Modifier, - state = LabeledTextInput.ViewState( - localizer = MockLocalizer(), - label = state?.localizer?.localize("APP.TRADE.TARGET_LEVERAGE"), - value = state?.leverageText ?: "", - onValueChanged = {}, - ), - ) + InputFieldScaffold(modifier) { + LabeledTextInput.Content( + modifier = Modifier, + state = LabeledTextInput.ViewState( + localizer = MockLocalizer(), + label = state?.localizer?.localize("APP.TRADE.TARGET_LEVERAGE"), + value = state?.leverageText ?: "", + onValueChanged = {}, + ), + ) + } } } @@ -227,7 +230,7 @@ object DydxTradeInputTargetLeverageView : DydxComponent { .themeColor(ThemeColor.SemanticColor.text_tertiary) .themeFont(fontSize = ThemeFont.FontSize.small), - ) + ) } } } ?: listOf(), @@ -248,7 +251,7 @@ object DydxTradeInputTargetLeverageView : DydxComponent { .themeColor(ThemeColor.SemanticColor.text_primary) .themeFont(fontSize = ThemeFont.FontSize.small), - ) + ) } } } ?: listOf(), From 9d17466b34616b8b73e3c9c29e1d0d1f05efe381 Mon Sep 17 00:00:00 2001 From: John Huang Date: Wed, 24 Apr 2024 12:05:45 -0700 Subject: [PATCH 11/21] More placeholder code and it compiles --- .../trade/margin/DydxAdjustMarginInputView.kt | 205 ++++++++++++++++-- .../margin/DydxAdjustMarginInputViewModel.kt | 10 +- .../DydxTradeInputMarginModeViewModel.kt | 23 +- .../DydxTradeInputTargetLeverageViewModel.kt | 11 +- 4 files changed, 211 insertions(+), 38 deletions(-) diff --git a/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/margin/DydxAdjustMarginInputView.kt b/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/margin/DydxAdjustMarginInputView.kt index b290c496..85a17eaa 100644 --- a/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/margin/DydxAdjustMarginInputView.kt +++ b/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/margin/DydxAdjustMarginInputView.kt @@ -1,25 +1,41 @@ package exchange.dydx.trading.feature.trade.margin import androidx.compose.animation.animateContentSize +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.Text import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.text.TextStyle import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel +import exchange.dydx.abacus.protocols.LocalizerProtocol import exchange.dydx.platformui.components.PlatformInfoScaffold +import exchange.dydx.platformui.components.buttons.PlatformPillItem import exchange.dydx.platformui.components.dividers.PlatformDivider +import exchange.dydx.platformui.components.tabgroups.PlatformTabGroup import exchange.dydx.platformui.designSystem.theme.ThemeColor +import exchange.dydx.platformui.designSystem.theme.ThemeFont +import exchange.dydx.platformui.designSystem.theme.ThemeShapes +import exchange.dydx.platformui.designSystem.theme.dydxDefault import exchange.dydx.platformui.designSystem.theme.themeColor +import exchange.dydx.platformui.designSystem.theme.themeFont import exchange.dydx.trading.common.component.DydxComponent import exchange.dydx.trading.common.compose.collectAsStateWithLifecycle import exchange.dydx.trading.common.theme.DydxThemedPreviewSurface -import exchange.dydx.trading.feature.trade.tradeinput.DydxTradeInputMarginModeView -import exchange.dydx.trading.feature.trade.tradeinput.DydxTradeInputMarginModeViewModel +import exchange.dydx.trading.common.theme.MockLocalizer +import exchange.dydx.trading.feature.shared.views.HeaderViewCloseBotton @Preview @Composable @@ -52,18 +68,24 @@ object DydxAdjustMarginInputView : DydxComponent { ) data class ViewState( + val localizer: LocalizerProtocol, val direction: MarginDirection = MarginDirection.Add, - val percentageText: String?, + val percentage: Double?, val percentageOptions: List, val amountText: String?, val subaccountReceipt: SubaccountReceipt, val positionReceipt: PositionReceipt, val error: String?, + val marginDirectionAction: (() -> Unit) = {}, + val percentageAction: (() -> Unit) = {}, + val action: (() -> Unit) = {}, + val closeAction: (() -> Unit) = {}, ) { companion object { val preview = ViewState( + localizer = MockLocalizer(), direction = MarginDirection.Add, - percentageText = "50%", + percentage = 0.5, percentageOptions = listOf( PercentageOption("10%", 0.1), PercentageOption("20%", 0.2), @@ -123,28 +145,171 @@ object DydxAdjustMarginInputView : DydxComponent { state = state, ) Spacer(modifier = Modifier.height(8.dp)) - InputAndSubaccountReceipt( - modifier = Modifier, - state = state, +// InputAndSubaccountReceipt( +// modifier = Modifier, +// state = state, +// ) +// Spacer(modifier = Modifier.weight(1f)) +// if (state.error == null) { +// LiquidationPrice( +// modifier = Modifier, +// state = state, +// ) +// } else { +// Error( +// modifier = Modifier, +// error = state.error, +// ) +// } +// Spacer(modifier = Modifier.height(8.dp)) +// PositionReceiptAndButton( +// modifier = Modifier, +// state = state, +// ) + } + } + + @Composable + fun NavigationHeader( + modifier: Modifier, + state: ViewState, + ) { + Row( + modifier = modifier + .fillMaxWidth() + .padding(vertical = 8.dp), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.Start, + ) { + Spacer(modifier = Modifier.width(16.dp)) + Text( + modifier = Modifier.padding(horizontal = 0.dp), + style = TextStyle.dydxDefault + .themeFont( + fontSize = ThemeFont.FontSize.large, + fontType = ThemeFont.FontType.plus, + ) + .themeColor(ThemeColor.SemanticColor.text_primary), + text = state.localizer.localize("APP.GENERAL.ADJUST_MARGIN"), ) Spacer(modifier = Modifier.weight(1f)) - if (state.error == null) { - LiquidationPrice( - modifier = Modifier, - state = state, + HeaderViewCloseBotton( + closeAction = state.closeAction, + ) + } + } + + + @Composable + fun MarginDirection( + modifier: Modifier, + state: ViewState, + ) { + val shape = RoundedCornerShape(10.dp) + Row( + modifier = Modifier + .padding( + horizontal = ThemeShapes.HorizontalPadding, + vertical = ThemeShapes.VerticalPadding, + ) + .fillMaxWidth() + .padding( + horizontal = ThemeShapes.HorizontalPadding, + vertical = 16.dp, + ) + .clickable { state.marginDirectionAction() }, + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(8.dp), + ) { + Column( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 0.dp), + verticalArrangement = Arrangement.spacedBy(8.dp), + ) { + Text( + modifier = Modifier + .padding(horizontal = 0.dp), + style = TextStyle.dydxDefault + .themeFont( + fontSize = ThemeFont.FontSize.medium, + fontType = ThemeFont.FontType.book, + ) + .themeColor(ThemeColor.SemanticColor.text_primary), + text = state.localizer.localize("APP.GENERAL.ADD_MARGIN"), ) - } else { - Error( - modifier = Modifier, - error = state.error, + + Text( + modifier = Modifier + .padding(horizontal = 0.dp), + style = TextStyle.dydxDefault + .themeFont( + fontSize = ThemeFont.FontSize.small, + fontType = ThemeFont.FontType.book, + ) + .themeColor(ThemeColor.SemanticColor.text_tertiary), + text = state.localizer.localize("APP.GENERAL.REMOVE_MARGIN"), ) } - Spacer(modifier = Modifier.height(8.dp)) - PositionReceiptAndButton( - modifier = Modifier, - state = state, - ) } } + + @Composable + fun PercentageOptions( + modifier: Modifier, + state: ViewState, + ) { + PlatformTabGroup( + modifier = Modifier.fillMaxWidth(), + scrollingEnabled = false, + items = state.percentageOptions.map { + { modifier -> + PlatformPillItem( + modifier = Modifier + .padding( + vertical = 4.dp, + horizontal = 8.dp, + ), + backgroundColor = ThemeColor.SemanticColor.layer_5, + ) { + Text( + text = it.text, + modifier = Modifier, + style = TextStyle.dydxDefault + .themeColor(ThemeColor.SemanticColor.text_tertiary) + .themeFont(fontSize = ThemeFont.FontSize.small), + + ) + } + } + } ?: listOf(), + selectedItems = state.percentageOptions.map { + { modifier -> + PlatformPillItem( + modifier = Modifier + .padding( + vertical = 4.dp, + horizontal = 8.dp, + ), + backgroundColor = ThemeColor.SemanticColor.layer_2, + ) { + Text( + text = it.text, + modifier = Modifier, + style = TextStyle.dydxDefault + .themeColor(ThemeColor.SemanticColor.text_primary) + .themeFont(fontSize = ThemeFont.FontSize.small), + + ) + } + } + } ?: listOf(), + equalWeight = false, + currentSelection = state.percentageOptions.indexOfFirst { + it.percentage == state.percentage + }, + onSelectionChanged = {}, + ) + } } diff --git a/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/margin/DydxAdjustMarginInputViewModel.kt b/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/margin/DydxAdjustMarginInputViewModel.kt index a500edc6..751adbba 100644 --- a/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/margin/DydxAdjustMarginInputViewModel.kt +++ b/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/margin/DydxAdjustMarginInputViewModel.kt @@ -5,8 +5,10 @@ import dagger.hilt.android.lifecycle.HiltViewModel import exchange.dydx.abacus.output.input.TradeInput import exchange.dydx.abacus.protocols.LocalizerProtocol import exchange.dydx.dydxstatemanager.AbacusStateManagerProtocol +import exchange.dydx.platformui.components.PlatformInfo import exchange.dydx.trading.common.DydxViewModel import exchange.dydx.trading.common.formatter.DydxFormatter +import exchange.dydx.trading.common.navigation.DydxRouter import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.map @@ -17,6 +19,8 @@ class DydxAdjustMarginInputViewModel @Inject constructor( private val localizer: LocalizerProtocol, private val abacusStateManager: AbacusStateManagerProtocol, private val formatter: DydxFormatter, + private val router: DydxRouter, + val platformInfo: PlatformInfo, ) : ViewModel(), DydxViewModel { val state: Flow = abacusStateManager.state.tradeInput @@ -30,8 +34,9 @@ class DydxAdjustMarginInputViewModel @Inject constructor( Abacus not implemented for adjust margin yet. This is a placeholder. */ return DydxAdjustMarginInputView.ViewState( + localizer = localizer, direction = DydxAdjustMarginInputView.MarginDirection.Add, - percentageText = "50%", + percentage = 0.5, percentageOptions = listOf( DydxAdjustMarginInputView.PercentageOption("10%", 0.1), DydxAdjustMarginInputView.PercentageOption("20%", 0.2), @@ -49,6 +54,9 @@ class DydxAdjustMarginInputViewModel @Inject constructor( liquidationPrice = listOf("1200.00", "1000.00"), ), error = null, + closeAction = { + router.navigateBack() + } ) } } diff --git a/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/tradeinput/DydxTradeInputMarginModeViewModel.kt b/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/tradeinput/DydxTradeInputMarginModeViewModel.kt index 9c6ba7a6..a4d48ac5 100644 --- a/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/tradeinput/DydxTradeInputMarginModeViewModel.kt +++ b/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/tradeinput/DydxTradeInputMarginModeViewModel.kt @@ -33,25 +33,24 @@ class DydxTradeInputMarginModeViewModel @Inject constructor( private fun createViewState(tradeInput: TradeInput?): DydxTradeInputMarginModeView.ViewState { return DydxTradeInputMarginModeView.ViewState( - localizer.localize("APP.GENERAL.MARGIN_MODE"), - tradeInput?.marketId ?: "", - DydxTradeInputMarginModeView.MarginTypeSelection( + title = localizer.localize("APP.GENERAL.MARGIN_MODE"), + asset = tradeInput?.marketId ?: "", + crossMargin = DydxTradeInputMarginModeView.MarginTypeSelection( localizer.localize("APP.GENERAL.CROSS_MARGIN"), localizer.localize("APP.GENERAL.CROSS_MARGIN_DESCRIPTION"), tradeInput?.marginMode == MarginMode.cross, - { - }, - ), - DydxTradeInputMarginModeView.MarginTypeSelection( + ) { + }, + isolatedMargin = DydxTradeInputMarginModeView.MarginTypeSelection( localizer.localize("APP.GENERAL.ISOLATED_MARGIN"), localizer.localize("APP.GENERAL.ISOLATED_MARGIN_DESCRIPTION"), tradeInput?.marginMode == MarginMode.isolated, - { - }, - ), - null, - { + ) { }, + errorText = null, + closeAction = { + router.navigateBack() + } ) } } diff --git a/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/tradeinput/DydxTradeInputTargetLeverageViewModel.kt b/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/tradeinput/DydxTradeInputTargetLeverageViewModel.kt index f3c9fa0d..04f16f4e 100644 --- a/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/tradeinput/DydxTradeInputTargetLeverageViewModel.kt +++ b/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/tradeinput/DydxTradeInputTargetLeverageViewModel.kt @@ -35,11 +35,12 @@ class DydxTradeInputTargetLeverageViewModel @Inject constructor( val maxLeverage = tradeInput?.options?.maxLeverage ?: 5.0 val leverages = leverageOptions(maxLeverage) return DydxTradeInputTargetLeverageView.ViewState( - localizer, - formatter.localFormatted(targetLeverage, 1), - leverages, - { - }, + localizer = localizer, + leverageText = formatter.localFormatted(targetLeverage, 1), + leverageOptions = leverages, + closeAction = { + router.navigateBack() + } ) } From ac1218ff47a03a634fe0ea3414e46620990ca6aa Mon Sep 17 00:00:00 2001 From: John Huang Date: Thu, 25 Apr 2024 11:39:41 -0700 Subject: [PATCH 12/21] rough amount input --- .../trade/margin/DydxAdjustMarginInputView.kt | 204 +++++++++++++++++- .../margin/DydxAdjustMarginInputViewModel.kt | 5 + 2 files changed, 204 insertions(+), 5 deletions(-) diff --git a/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/margin/DydxAdjustMarginInputView.kt b/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/margin/DydxAdjustMarginInputView.kt index 13c748d1..b145f115 100644 --- a/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/margin/DydxAdjustMarginInputView.kt +++ b/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/margin/DydxAdjustMarginInputView.kt @@ -1,6 +1,7 @@ package exchange.dydx.trading.feature.trade.margin import androidx.compose.animation.animateContentSize +import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column @@ -9,33 +10,44 @@ import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.offset import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.width import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.material.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp +import androidx.compose.ui.zIndex import androidx.hilt.navigation.compose.hiltViewModel import exchange.dydx.abacus.protocols.LocalizerProtocol import exchange.dydx.platformui.components.PlatformInfoScaffold import exchange.dydx.platformui.components.buttons.PlatformPillItem +import exchange.dydx.platformui.components.changes.PlatformAmountChange import exchange.dydx.platformui.components.dividers.PlatformDivider +import exchange.dydx.platformui.components.inputs.PlatformTextInput import exchange.dydx.platformui.components.tabgroups.PlatformTabGroup import exchange.dydx.platformui.designSystem.theme.ThemeColor import exchange.dydx.platformui.designSystem.theme.ThemeFont import exchange.dydx.platformui.designSystem.theme.ThemeShapes +import exchange.dydx.platformui.designSystem.theme.color import exchange.dydx.platformui.designSystem.theme.dydxDefault import exchange.dydx.platformui.designSystem.theme.themeColor import exchange.dydx.platformui.designSystem.theme.themeFont import exchange.dydx.trading.common.component.DydxComponent import exchange.dydx.trading.common.compose.collectAsStateWithLifecycle +import exchange.dydx.trading.common.formatter.DydxFormatter import exchange.dydx.trading.common.theme.DydxThemedPreviewSurface import exchange.dydx.trading.common.theme.MockLocalizer +import exchange.dydx.trading.feature.shared.scaffolds.InputFieldScaffold import exchange.dydx.trading.feature.shared.views.HeaderViewCloseBotton +import exchange.dydx.trading.feature.shared.views.SignedAmountView +import exchange.dydx.trading.feature.shared.views.SizeTextView @Preview @Composable @@ -69,6 +81,7 @@ object DydxAdjustMarginInputView : DydxComponent { data class ViewState( val localizer: LocalizerProtocol, + val formatter: DydxFormatter, val direction: MarginDirection = MarginDirection.Add, val percentage: Double?, val percentageOptions: List, @@ -78,12 +91,14 @@ object DydxAdjustMarginInputView : DydxComponent { val error: String?, val marginDirectionAction: (() -> Unit) = {}, val percentageAction: (() -> Unit) = {}, + val editAction: ((String) -> Unit) = {}, val action: (() -> Unit) = {}, val closeAction: (() -> Unit) = {}, ) { companion object { val preview = ViewState( localizer = MockLocalizer(), + formatter = DydxFormatter(), direction = MarginDirection.Add, percentage = 0.5, percentageOptions = listOf( @@ -145,11 +160,11 @@ object DydxAdjustMarginInputView : DydxComponent { state = state, ) Spacer(modifier = Modifier.height(8.dp)) -// InputAndSubaccountReceipt( -// modifier = Modifier, -// state = state, -// ) -// Spacer(modifier = Modifier.weight(1f)) + InputAndSubaccountReceipt( + modifier = Modifier, + state = state, + ) + Spacer(modifier = Modifier.weight(1f)) // if (state.error == null) { // LiquidationPrice( // modifier = Modifier, @@ -311,5 +326,184 @@ object DydxAdjustMarginInputView : DydxComponent { onSelectionChanged = {}, ) } + + @Composable + fun InputAndSubaccountReceipt( + modifier: Modifier, + state: ViewState, + ) { + Column { + InputFieldScaffold(modifier.zIndex(1f)) { + AmountBox(modifier, state) + } + val shape = RoundedCornerShape(0.dp, 0.dp, 8.dp, 8.dp) + Column( + modifier = modifier + .offset(y = (-4).dp) + .background(color = ThemeColor.SemanticColor.layer_1.color, shape = shape) + .padding(horizontal = ThemeShapes.HorizontalPadding) + .padding(vertical = ThemeShapes.VerticalPadding) + .padding(top = 4.dp), + ) { + CrossFreeCollateralContent(modifier = Modifier, state) + CrossMarginContent(modifier = Modifier, state) + } + } + } + + + @Composable + private fun AmountBox( + modifier: Modifier, + state: ViewState, + ) { + Row( + modifier = modifier + .padding(horizontal = ThemeShapes.HorizontalPadding) + .padding(vertical = ThemeShapes.VerticalPadding), + ) { + Column( + modifier = Modifier.weight(1f), + verticalArrangement = Arrangement.spacedBy(6.dp), + ) { + Text( + text = state.localizer.localize("APP.GENERAL.AMOUNT"), + style = TextStyle.dydxDefault + .themeColor(ThemeColor.SemanticColor.text_tertiary) + .themeFont(fontSize = ThemeFont.FontSize.mini), + ) + + PlatformTextInput( + modifier = Modifier.fillMaxWidth(), + value = state.amountText ?: "", + textStyle = TextStyle.dydxDefault + .themeColor(ThemeColor.SemanticColor.text_primary) + .themeFont(fontSize = ThemeFont.FontSize.medium), + placeHolder = if (state.amountText == null) { + state.formatter.raw(0.0, 2) + } else { + null + }, + onValueChange = { state.editAction.invoke(it) }, + keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number), + ) + } + } + } + + @Composable + private fun CrossFreeCollateralContent( + modifier: Modifier, + state: ViewState, + ) { + Row( + modifier = modifier, + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(4.dp), + ) { + Text( + text = state.localizer.localize("APP.GENERAL.CROSS_FREE_COLLATERAL"), + style = TextStyle.dydxDefault + .themeColor(ThemeColor.SemanticColor.text_tertiary) + .themeFont(fontSize = ThemeFont.FontSize.small), + ) + Spacer(modifier = Modifier.weight(1f)) + + CrossFreeCollateralChange(modifier = Modifier, state = state) + } + } + + @Composable + private fun CrossFreeCollateralChange( + modifier: Modifier, + state: ViewState, + ) { + Row( + modifier = modifier, + ) { + PlatformAmountChange( + before = { + SizeTextView.Content( + modifier = Modifier, + state = SizeTextView.ViewState( + localizer = state.localizer, + formatter = state.formatter, + size = state.subaccountReceipt.freeCollateral.firstOrNull()?.toDoubleOrNull(), + stepSize = 2, + ), + ) + }, + after = { + SizeTextView.Content( + modifier = Modifier, + state = SizeTextView.ViewState( + localizer = state.localizer, + formatter = state.formatter, + size = state.subaccountReceipt.freeCollateral.lastOrNull()?.toDoubleOrNull(), + stepSize = 2, + ), + ) + }, + ) + } + } + + + @Composable + private fun CrossMarginContent( + modifier: Modifier, + state: ViewState, + ) { + Row( + modifier = modifier, + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(4.dp), + ) { + Text( + text = state.localizer.localize("APP.GENERAL.CROSS_MARGIN_USAGE"), + style = TextStyle.dydxDefault + .themeColor(ThemeColor.SemanticColor.text_tertiary) + .themeFont(fontSize = ThemeFont.FontSize.small), + ) + Spacer(modifier = Modifier.weight(1f)) + + CrossMarginUsageChange(modifier = Modifier, state = state) + } + } + + @Composable + private fun CrossMarginUsageChange( + modifier: Modifier, + state: ViewState, + ) { + Row( + modifier = modifier, + ) { + PlatformAmountChange( + before = { + SizeTextView.Content( + modifier = Modifier, + state = SizeTextView.ViewState( + localizer = state.localizer, + formatter = state.formatter, + size = state.subaccountReceipt.marginUsage.firstOrNull()?.toDoubleOrNull(), + stepSize = 2, + ), + ) + }, + after = { + SizeTextView.Content( + modifier = Modifier, + state = SizeTextView.ViewState( + localizer = state.localizer, + formatter = state.formatter, + size = state.subaccountReceipt.marginUsage.lastOrNull()?.toDoubleOrNull(), + stepSize = 2, + ), + ) + }, + ) + } + } } diff --git a/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/margin/DydxAdjustMarginInputViewModel.kt b/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/margin/DydxAdjustMarginInputViewModel.kt index 751adbba..c6b50126 100644 --- a/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/margin/DydxAdjustMarginInputViewModel.kt +++ b/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/margin/DydxAdjustMarginInputViewModel.kt @@ -35,6 +35,7 @@ class DydxAdjustMarginInputViewModel @Inject constructor( */ return DydxAdjustMarginInputView.ViewState( localizer = localizer, + formatter = formatter, direction = DydxAdjustMarginInputView.MarginDirection.Add, percentage = 0.5, percentageOptions = listOf( @@ -54,6 +55,10 @@ class DydxAdjustMarginInputViewModel @Inject constructor( liquidationPrice = listOf("1200.00", "1000.00"), ), error = null, + marginDirectionAction = { }, + percentageAction = { }, + editAction = { }, + action = { }, closeAction = { router.navigateBack() } From 5e08ca08c16d78c329712238d6d41524bc613377 Mon Sep 17 00:00:00 2001 From: John Huang Date: Thu, 25 Apr 2024 14:20:48 -0700 Subject: [PATCH 13/21] Formatting "Add Margin" and "Remove Margin" --- .../DydxPortfolioPositionItemView.kt | 2 +- .../trading/feature/trade/DydxTradeRouter.kt | 1 - .../trade/margin/DydxAdjustMarginInputView.kt | 128 ++++++++++-------- .../margin/DydxAdjustMarginInputViewModel.kt | 2 +- .../DydxTradeInputMarginModeViewModel.kt | 2 +- .../DydxTradeInputTargetLeverageViewModel.kt | 2 +- 6 files changed, 75 insertions(+), 62 deletions(-) diff --git a/v4/feature/portfolio/src/main/java/exchange/dydx/trading/feature/portfolio/components/positions/DydxPortfolioPositionItemView.kt b/v4/feature/portfolio/src/main/java/exchange/dydx/trading/feature/portfolio/components/positions/DydxPortfolioPositionItemView.kt index 031fe9d2..cb9235f0 100644 --- a/v4/feature/portfolio/src/main/java/exchange/dydx/trading/feature/portfolio/components/positions/DydxPortfolioPositionItemView.kt +++ b/v4/feature/portfolio/src/main/java/exchange/dydx/trading/feature/portfolio/components/positions/DydxPortfolioPositionItemView.kt @@ -206,7 +206,7 @@ object DydxPortfolioPositionItemView { state = position.side?.copy( coloringOption = SideTextView.ColoringOption.COLORED, - ), + ), textStyle = TextStyle.dydxDefault .themeFont(fontSize = ThemeFont.FontSize.mini) .themeColor(ThemeColor.SemanticColor.text_primary), diff --git a/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/DydxTradeRouter.kt b/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/DydxTradeRouter.kt index 4a011ae1..126f17cc 100644 --- a/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/DydxTradeRouter.kt +++ b/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/DydxTradeRouter.kt @@ -90,5 +90,4 @@ fun NavGraphBuilder.tradeGraph( } DydxAdjustMarginInputView.Content(Modifier) } - } diff --git a/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/margin/DydxAdjustMarginInputView.kt b/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/margin/DydxAdjustMarginInputView.kt index b145f115..b28b5f25 100644 --- a/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/margin/DydxAdjustMarginInputView.kt +++ b/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/margin/DydxAdjustMarginInputView.kt @@ -2,7 +2,6 @@ package exchange.dydx.trading.feature.trade.margin import androidx.compose.animation.animateContentSize import androidx.compose.foundation.background -import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row @@ -46,7 +45,6 @@ import exchange.dydx.trading.common.theme.DydxThemedPreviewSurface import exchange.dydx.trading.common.theme.MockLocalizer import exchange.dydx.trading.feature.shared.scaffolds.InputFieldScaffold import exchange.dydx.trading.feature.shared.views.HeaderViewCloseBotton -import exchange.dydx.trading.feature.shared.views.SignedAmountView import exchange.dydx.trading.feature.shared.views.SizeTextView @Preview @@ -89,7 +87,7 @@ object DydxAdjustMarginInputView : DydxComponent { val subaccountReceipt: SubaccountReceipt, val positionReceipt: PositionReceipt, val error: String?, - val marginDirectionAction: (() -> Unit) = {}, + val marginDirectionAction: ((direction: MarginDirection) -> Unit) = {}, val percentageAction: (() -> Unit) = {}, val editAction: ((String) -> Unit) = {}, val action: (() -> Unit) = {}, @@ -142,24 +140,25 @@ object DydxAdjustMarginInputView : DydxComponent { modifier = modifier .animateContentSize() .fillMaxSize() - .themeColor(ThemeColor.SemanticColor.layer_4), + .themeColor(ThemeColor.SemanticColor.layer_4) + .padding(horizontal = 16.dp), ) { NavigationHeader( modifier = Modifier, - state = state + state = state, ) PlatformDivider() - // Add to remove margin + Spacer(modifier = Modifier.height(16.dp)) MarginDirection( modifier = Modifier, state = state, ) - Spacer(modifier = Modifier.height(8.dp)) + Spacer(modifier = Modifier.height(16.dp)) PercentageOptions( modifier = Modifier, state = state, ) - Spacer(modifier = Modifier.height(8.dp)) + Spacer(modifier = Modifier.height(16.dp)) InputAndSubaccountReceipt( modifier = Modifier, state = state, @@ -214,59 +213,77 @@ object DydxAdjustMarginInputView : DydxComponent { } } + private fun marginDirectionText(direction: MarginDirection, localizer: LocalizerProtocol): String { + return when (direction) { + MarginDirection.Add -> localizer.localize("APP.TRADE.ADD_MARGIN") + MarginDirection.Remove -> localizer.localize("APP.TRADE.REMOVE_MARGIN") + } + } @Composable fun MarginDirection( modifier: Modifier, state: ViewState, ) { - val shape = RoundedCornerShape(10.dp) - Row( - modifier = Modifier - .padding( - horizontal = ThemeShapes.HorizontalPadding, - vertical = ThemeShapes.VerticalPadding, - ) + val directions = listOf(MarginDirection.Add, MarginDirection.Remove) + + PlatformTabGroup( + modifier = modifier .fillMaxWidth() - .padding( - horizontal = ThemeShapes.HorizontalPadding, - vertical = 16.dp, - ) - .clickable { state.marginDirectionAction() }, - verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.spacedBy(8.dp), - ) { - Column( - modifier = Modifier - .fillMaxWidth() - .padding(horizontal = 0.dp), - verticalArrangement = Arrangement.spacedBy(8.dp), - ) { - Text( - modifier = Modifier - .padding(horizontal = 0.dp), - style = TextStyle.dydxDefault - .themeFont( - fontSize = ThemeFont.FontSize.medium, - fontType = ThemeFont.FontType.book, - ) - .themeColor(ThemeColor.SemanticColor.text_primary), - text = state.localizer.localize("APP.TRADE.ADD_MARGIN"), - ) + .height(42.dp), + scrollingEnabled = false, + items = directions.map { + { modifier -> + PlatformPillItem( + modifier = Modifier + .padding( + vertical = 4.dp, + horizontal = 8.dp, + ), + backgroundColor = ThemeColor.SemanticColor.layer_5, + ) { + Text( + text = marginDirectionText(it, state.localizer), + modifier = Modifier, + style = TextStyle.dydxDefault + .themeColor(ThemeColor.SemanticColor.text_tertiary) + .themeFont(fontSize = ThemeFont.FontSize.small), - Text( - modifier = Modifier - .padding(horizontal = 0.dp), - style = TextStyle.dydxDefault - .themeFont( - fontSize = ThemeFont.FontSize.small, - fontType = ThemeFont.FontType.book, ) - .themeColor(ThemeColor.SemanticColor.text_tertiary), - text = state.localizer.localize("APP.TRADE.REMOVE_MARGIN"), - ) - } - } + } + } + }, + selectedItems = directions.map { + { modifier -> + PlatformPillItem( + modifier = Modifier + .padding( + vertical = 4.dp, + horizontal = 8.dp, + ), + backgroundColor = ThemeColor.SemanticColor.layer_2, + ) { + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(8.dp), + ) { + Text( + text = marginDirectionText(it, state.localizer), + modifier = Modifier, + style = TextStyle.dydxDefault + .themeColor(ThemeColor.SemanticColor.text_primary) + .themeFont(fontSize = ThemeFont.FontSize.small), + ) + } + } + } + }, + currentSelection = if (state.direction == MarginDirection.Add) 0 else 1, + onSelectionChanged = { it -> + state.marginDirectionAction.invoke(directions[it]) + }, + horizontalArrangement = Arrangement.spacedBy(8.dp), + ) } @Composable @@ -294,7 +311,7 @@ object DydxAdjustMarginInputView : DydxComponent { .themeColor(ThemeColor.SemanticColor.text_tertiary) .themeFont(fontSize = ThemeFont.FontSize.small), - ) + ) } } } ?: listOf(), @@ -315,7 +332,7 @@ object DydxAdjustMarginInputView : DydxComponent { .themeColor(ThemeColor.SemanticColor.text_primary) .themeFont(fontSize = ThemeFont.FontSize.small), - ) + ) } } } ?: listOf(), @@ -351,7 +368,6 @@ object DydxAdjustMarginInputView : DydxComponent { } } - @Composable private fun AmountBox( modifier: Modifier, @@ -448,7 +464,6 @@ object DydxAdjustMarginInputView : DydxComponent { } } - @Composable private fun CrossMarginContent( modifier: Modifier, @@ -506,4 +521,3 @@ object DydxAdjustMarginInputView : DydxComponent { } } } - diff --git a/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/margin/DydxAdjustMarginInputViewModel.kt b/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/margin/DydxAdjustMarginInputViewModel.kt index c6b50126..f657cda6 100644 --- a/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/margin/DydxAdjustMarginInputViewModel.kt +++ b/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/margin/DydxAdjustMarginInputViewModel.kt @@ -61,7 +61,7 @@ class DydxAdjustMarginInputViewModel @Inject constructor( action = { }, closeAction = { router.navigateBack() - } + }, ) } } diff --git a/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/tradeinput/DydxTradeInputMarginModeViewModel.kt b/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/tradeinput/DydxTradeInputMarginModeViewModel.kt index a4d48ac5..38324f80 100644 --- a/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/tradeinput/DydxTradeInputMarginModeViewModel.kt +++ b/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/tradeinput/DydxTradeInputMarginModeViewModel.kt @@ -50,7 +50,7 @@ class DydxTradeInputMarginModeViewModel @Inject constructor( errorText = null, closeAction = { router.navigateBack() - } + }, ) } } diff --git a/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/tradeinput/DydxTradeInputTargetLeverageViewModel.kt b/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/tradeinput/DydxTradeInputTargetLeverageViewModel.kt index 04f16f4e..babc2cbc 100644 --- a/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/tradeinput/DydxTradeInputTargetLeverageViewModel.kt +++ b/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/tradeinput/DydxTradeInputTargetLeverageViewModel.kt @@ -40,7 +40,7 @@ class DydxTradeInputTargetLeverageViewModel @Inject constructor( leverageOptions = leverages, closeAction = { router.navigateBack() - } + }, ) } From 4069fd682d299cfb7a533d9f6b65b57d0ab7995f Mon Sep 17 00:00:00 2001 From: John Huang Date: Fri, 26 Apr 2024 11:24:54 -0700 Subject: [PATCH 14/21] MOB-400 placeholder for bottom --- .../trade/margin/DydxAdjustMarginInputView.kt | 56 +++++++++++++------ 1 file changed, 40 insertions(+), 16 deletions(-) diff --git a/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/margin/DydxAdjustMarginInputView.kt b/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/margin/DydxAdjustMarginInputView.kt index b28b5f25..0d21211b 100644 --- a/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/margin/DydxAdjustMarginInputView.kt +++ b/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/margin/DydxAdjustMarginInputView.kt @@ -164,22 +164,22 @@ object DydxAdjustMarginInputView : DydxComponent { state = state, ) Spacer(modifier = Modifier.weight(1f)) -// if (state.error == null) { -// LiquidationPrice( -// modifier = Modifier, -// state = state, -// ) -// } else { -// Error( -// modifier = Modifier, -// error = state.error, -// ) -// } -// Spacer(modifier = Modifier.height(8.dp)) -// PositionReceiptAndButton( -// modifier = Modifier, -// state = state, -// ) + if (state.error == null) { + LiquidationPrice( + modifier = Modifier, + state = state, + ) + } else { + Error( + modifier = Modifier, + error = state.error, + ) + } + Spacer(modifier = Modifier.height(8.dp)) + PositionReceiptAndButton( + modifier = Modifier, + state = state, + ) } } @@ -520,4 +520,28 @@ object DydxAdjustMarginInputView : DydxComponent { ) } } + + @Composable + private fun LiquidationPrice( + modifier: Modifier, + state: ViewState, + ) { + // TODO, implement this + } + + @Composable + private fun Error( + modifier: Modifier, + error: String, + ) { + // TODO, implement this + } + + @Composable + private fun PositionReceiptAndButton( + modifier: Modifier, + state: ViewState, + ) { + // TODO, implement this + } } From 8fcd6b88d6a023b4181e24b946ef123ba1681a5b Mon Sep 17 00:00:00 2001 From: John Huang Date: Mon, 29 Apr 2024 12:38:11 -0700 Subject: [PATCH 15/21] Adjust margin receipt area --- .../feature/receipt/DydxReceiptView.kt | 13 +- .../DydxReceiptFreeCollateralView.kt | 104 ++++++++++ .../DydxReceiptFreeCollateralViewModel.kt | 120 ++++++++++++ .../DydxReceiptLiquidationPriceView.kt | 104 ++++++++++ .../DydxReceiptLiquidationPriceViewModel.kt | 120 ++++++++++++ .../trade/margin/DydxAdjustMarginInputView.kt | 181 ++++++++++++------ .../margin/DydxAdjustMarginInputViewModel.kt | 93 +++++++-- .../components/DydxAdjustMarginCtaButton.kt | 50 +++++ .../DydxAdjustMarginCtaButtonModel.kt | 56 ++++++ 9 files changed, 768 insertions(+), 73 deletions(-) create mode 100644 v4/feature/receipt/src/main/java/exchange/dydx/trading/feature/receipt/components/freecollateral/DydxReceiptFreeCollateralView.kt create mode 100644 v4/feature/receipt/src/main/java/exchange/dydx/trading/feature/receipt/components/freecollateral/DydxReceiptFreeCollateralViewModel.kt create mode 100644 v4/feature/receipt/src/main/java/exchange/dydx/trading/feature/receipt/components/liquidationprice/DydxReceiptLiquidationPriceView.kt create mode 100644 v4/feature/receipt/src/main/java/exchange/dydx/trading/feature/receipt/components/liquidationprice/DydxReceiptLiquidationPriceViewModel.kt create mode 100644 v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/margin/components/DydxAdjustMarginCtaButton.kt create mode 100644 v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/margin/components/DydxAdjustMarginCtaButtonModel.kt diff --git a/v4/feature/receipt/src/main/java/exchange/dydx/trading/feature/receipt/DydxReceiptView.kt b/v4/feature/receipt/src/main/java/exchange/dydx/trading/feature/receipt/DydxReceiptView.kt index a1fe7080..a71c2adc 100644 --- a/v4/feature/receipt/src/main/java/exchange/dydx/trading/feature/receipt/DydxReceiptView.kt +++ b/v4/feature/receipt/src/main/java/exchange/dydx/trading/feature/receipt/DydxReceiptView.kt @@ -13,6 +13,7 @@ import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel import exchange.dydx.abacus.protocols.LocalizerProtocol @@ -24,6 +25,7 @@ import exchange.dydx.trading.common.compose.collectAsStateWithLifecycle import exchange.dydx.trading.common.theme.DydxThemedPreviewSurface import exchange.dydx.trading.common.theme.MockLocalizer import exchange.dydx.trading.feature.receipt.components.buyingpower.DydxReceiptBuyingPowerView +import exchange.dydx.trading.feature.receipt.components.buyingpower.DydxReceiptFreeCollateralView import exchange.dydx.trading.feature.receipt.components.equity.DydxReceiptEquityView import exchange.dydx.trading.feature.receipt.components.exchangerate.DydxReceiptExchangeRateView import exchange.dydx.trading.feature.receipt.components.exchangereceived.DydxReceiptExchangeReceivedView @@ -46,6 +48,7 @@ fun Preview_DydxReceiptView() { object DydxReceiptView : DydxComponent { enum class ReceiptLineType { + FreeCollateral, BuyingPower, MarginUsage, Fee, @@ -62,6 +65,8 @@ object DydxReceiptView : DydxComponent { data class ViewState( val localizer: LocalizerProtocol, + val height: Dp? = null, + val padding: Dp? = null, val lineTypes: List = emptyList(), ) { companion object { @@ -93,9 +98,9 @@ object DydxReceiptView : DydxComponent { Box( modifier = modifier - .height(210.dp) + .height(state.height ?: 210.dp) .fillMaxWidth() - .padding(horizontal = ThemeShapes.HorizontalPadding) + .padding(horizontal = state.padding ?: ThemeShapes.HorizontalPadding) .background( color = ThemeColor.SemanticColor.layer_1.color, shape = RoundedCornerShape(10.dp), @@ -110,6 +115,10 @@ object DydxReceiptView : DydxComponent { ) { items(state.lineTypes, key = { it }) { lineType -> when (lineType) { + ReceiptLineType.FreeCollateral -> { + DydxReceiptFreeCollateralView.Content(Modifier.animateItemPlacement()) + } + ReceiptLineType.BuyingPower -> { DydxReceiptBuyingPowerView.Content(Modifier.animateItemPlacement()) } diff --git a/v4/feature/receipt/src/main/java/exchange/dydx/trading/feature/receipt/components/freecollateral/DydxReceiptFreeCollateralView.kt b/v4/feature/receipt/src/main/java/exchange/dydx/trading/feature/receipt/components/freecollateral/DydxReceiptFreeCollateralView.kt new file mode 100644 index 00000000..9366aeea --- /dev/null +++ b/v4/feature/receipt/src/main/java/exchange/dydx/trading/feature/receipt/components/freecollateral/DydxReceiptFreeCollateralView.kt @@ -0,0 +1,104 @@ +package exchange.dydx.trading.feature.receipt.components.buyingpower + +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.tooling.preview.Preview +import androidx.hilt.navigation.compose.hiltViewModel +import exchange.dydx.abacus.protocols.LocalizerProtocol +import exchange.dydx.platformui.components.changes.PlatformAmountChange +import exchange.dydx.platformui.components.changes.PlatformDirection +import exchange.dydx.platformui.designSystem.theme.ThemeColor +import exchange.dydx.platformui.designSystem.theme.ThemeFont +import exchange.dydx.platformui.designSystem.theme.dydxDefault +import exchange.dydx.platformui.designSystem.theme.themeColor +import exchange.dydx.platformui.designSystem.theme.themeFont +import exchange.dydx.trading.common.component.DydxComponent +import exchange.dydx.trading.common.compose.collectAsStateWithLifecycle +import exchange.dydx.trading.common.theme.DydxThemedPreviewSurface +import exchange.dydx.trading.common.theme.MockLocalizer +import exchange.dydx.trading.feature.shared.views.AmountText + +@Preview +@Composable +fun Preview_DydxReceiptFreeCollateralView() { + DydxThemedPreviewSurface { + DydxReceiptFreeCollateralView.Content(Modifier, DydxReceiptFreeCollateralView.ViewState.preview) + } +} + +object DydxReceiptFreeCollateralView : DydxComponent { + data class ViewState( + val localizer: LocalizerProtocol, + val before: AmountText.ViewState? = null, + val after: AmountText.ViewState? = null, + + ) { + companion object { + val preview = ViewState( + localizer = MockLocalizer(), + before = AmountText.ViewState.preview, + after = AmountText.ViewState.preview, + ) + } + } + + @Composable + override fun Content(modifier: Modifier) { + val viewModel: DydxReceiptFreeCollateralViewModel = hiltViewModel() + + val state = viewModel.state.collectAsStateWithLifecycle(initialValue = null).value + Content(modifier, state) + } + + @Composable + fun Content(modifier: Modifier, state: ViewState?) { + if (state == null) return + + Row( + modifier = modifier, + verticalAlignment = Alignment.CenterVertically, + ) { + Text( + text = state.localizer.localize("APP.GENERAL.FREE_COLLATERAL"), + style = TextStyle.dydxDefault + .themeFont(fontSize = ThemeFont.FontSize.small) + .themeColor(ThemeColor.SemanticColor.text_tertiary), + ) + + Spacer(modifier = Modifier.weight(0.1f)) + + PlatformAmountChange( + modifier = Modifier.weight(1f), + before = if (state.before != null) { { + AmountText.Content( + state = state.before, + textStyle = TextStyle.dydxDefault + .themeFont(fontType = ThemeFont.FontType.number, fontSize = ThemeFont.FontSize.small) + .themeColor(ThemeColor.SemanticColor.text_tertiary), + ) + } } else { + null + }, + after = if (state.after != null) { { + AmountText.Content( + state = state.after, + textStyle = TextStyle.dydxDefault + .themeFont(fontType = ThemeFont.FontType.number, fontSize = ThemeFont.FontSize.small) + .themeColor(ThemeColor.SemanticColor.text_primary), + ) + } } else { + null + }, + direction = PlatformDirection.from(state.before?.amount, state.after?.amount), + textStyle = TextStyle.dydxDefault + .themeFont(fontSize = ThemeFont.FontSize.small) + .themeColor(ThemeColor.SemanticColor.text_tertiary), + ) + } + } +} diff --git a/v4/feature/receipt/src/main/java/exchange/dydx/trading/feature/receipt/components/freecollateral/DydxReceiptFreeCollateralViewModel.kt b/v4/feature/receipt/src/main/java/exchange/dydx/trading/feature/receipt/components/freecollateral/DydxReceiptFreeCollateralViewModel.kt new file mode 100644 index 00000000..0052a0cb --- /dev/null +++ b/v4/feature/receipt/src/main/java/exchange/dydx/trading/feature/receipt/components/freecollateral/DydxReceiptFreeCollateralViewModel.kt @@ -0,0 +1,120 @@ +package exchange.dydx.trading.feature.receipt.components.buyingpower + +import androidx.lifecycle.ViewModel +import dagger.hilt.android.lifecycle.HiltViewModel +import exchange.dydx.abacus.output.Subaccount +import exchange.dydx.abacus.output.SubaccountPosition +import exchange.dydx.abacus.output.input.TradeInput +import exchange.dydx.abacus.output.input.TransferInput +import exchange.dydx.abacus.protocols.LocalizerProtocol +import exchange.dydx.dydxstatemanager.AbacusStateManagerProtocol +import exchange.dydx.trading.common.DydxViewModel +import exchange.dydx.trading.common.formatter.DydxFormatter +import exchange.dydx.trading.feature.receipt.ReceiptType +import exchange.dydx.trading.feature.shared.views.AmountText +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.flatMapLatest +import kotlinx.coroutines.flow.flowOf +import javax.inject.Inject + +@HiltViewModel +class DydxReceiptFreeCollateralViewModel @Inject constructor( + private val localizer: LocalizerProtocol, + private val abacusStateManager: AbacusStateManagerProtocol, + private val formatter: DydxFormatter, + private val receiptTypeFlow: Flow<@JvmSuppressWildcards ReceiptType?>, +) : ViewModel(), DydxViewModel { + + @OptIn(ExperimentalCoroutinesApi::class) + val state: Flow = + receiptTypeFlow + .flatMapLatest { receiptType -> + when (receiptType) { + is ReceiptType.Trade -> { + combine( + abacusStateManager.state.selectedSubaccountPositions, + abacusStateManager.state.tradeInput, + ) { positions, tradeInput -> + createViewState(positions, tradeInput) + } + } + is ReceiptType.Transfer -> { + combine( + abacusStateManager.state.selectedSubaccount, + abacusStateManager.state.transferInput, + ) { subaccount, transferInput -> + createViewState(subaccount, transferInput) + } + } + else -> flowOf() + } + } + .distinctUntilChanged() + + private fun createViewState( + positions: List?, + tradeInput: TradeInput?, + ): DydxReceiptFreeCollateralView.ViewState { + val marketId = tradeInput?.marketId ?: "ETH-USD" + val position = positions?.firstOrNull { it.id == marketId } + return DydxReceiptFreeCollateralView.ViewState( + localizer = localizer, + before = if (position?.buyingPower?.current != null) { + AmountText.ViewState( + localizer = localizer, + formatter = formatter, + amount = position?.buyingPower?.current, + tickSize = 0, + requiresPositive = true, + ) + } else { + null + }, + after = if (position?.buyingPower?.postOrder != null) { + AmountText.ViewState( + localizer = localizer, + formatter = formatter, + amount = position?.buyingPower?.postOrder, + tickSize = 0, + requiresPositive = true, + ) + } else { + null + }, + ) + } + + private fun createViewState( + subaccount: Subaccount?, + transferInput: TransferInput?, + ): DydxReceiptFreeCollateralView.ViewState { + return DydxReceiptFreeCollateralView.ViewState( + localizer = localizer, + before = if (subaccount?.buyingPower?.current != null) { + AmountText.ViewState( + localizer = localizer, + formatter = formatter, + amount = subaccount?.buyingPower?.current, + tickSize = 0, + requiresPositive = true, + ) + } else { + null + }, + after = if (subaccount?.buyingPower?.postOrder != null) { + AmountText.ViewState( + localizer = localizer, + formatter = formatter, + amount = subaccount?.buyingPower?.postOrder, + tickSize = 0, + requiresPositive = true, + ) + } else { + null + }, + ) + } +} diff --git a/v4/feature/receipt/src/main/java/exchange/dydx/trading/feature/receipt/components/liquidationprice/DydxReceiptLiquidationPriceView.kt b/v4/feature/receipt/src/main/java/exchange/dydx/trading/feature/receipt/components/liquidationprice/DydxReceiptLiquidationPriceView.kt new file mode 100644 index 00000000..996ebac1 --- /dev/null +++ b/v4/feature/receipt/src/main/java/exchange/dydx/trading/feature/receipt/components/liquidationprice/DydxReceiptLiquidationPriceView.kt @@ -0,0 +1,104 @@ +package exchange.dydx.trading.feature.receipt.components.liquidationprice + +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.tooling.preview.Preview +import androidx.hilt.navigation.compose.hiltViewModel +import exchange.dydx.abacus.protocols.LocalizerProtocol +import exchange.dydx.platformui.components.changes.PlatformAmountChange +import exchange.dydx.platformui.components.changes.PlatformDirection +import exchange.dydx.platformui.designSystem.theme.ThemeColor +import exchange.dydx.platformui.designSystem.theme.ThemeFont +import exchange.dydx.platformui.designSystem.theme.dydxDefault +import exchange.dydx.platformui.designSystem.theme.themeColor +import exchange.dydx.platformui.designSystem.theme.themeFont +import exchange.dydx.trading.common.component.DydxComponent +import exchange.dydx.trading.common.compose.collectAsStateWithLifecycle +import exchange.dydx.trading.common.theme.DydxThemedPreviewSurface +import exchange.dydx.trading.common.theme.MockLocalizer +import exchange.dydx.trading.feature.shared.views.AmountText + +@Preview +@Composable +fun Preview_DydxReceiptLiquidationPriceView() { + DydxThemedPreviewSurface { + DydxReceiptLiquidationPriceView.Content(Modifier, DydxReceiptLiquidationPriceView.ViewState.preview) + } +} + +object DydxReceiptLiquidationPriceView : DydxComponent { + data class ViewState( + val localizer: LocalizerProtocol, + val before: AmountText.ViewState? = null, + val after: AmountText.ViewState? = null, + + ) { + companion object { + val preview = ViewState( + localizer = MockLocalizer(), + before = AmountText.ViewState.preview, + after = AmountText.ViewState.preview, + ) + } + } + + @Composable + override fun Content(modifier: Modifier) { + val viewModel: DydxReceiptLiquidationPriceViewModel = hiltViewModel() + + val state = viewModel.state.collectAsStateWithLifecycle(initialValue = null).value + Content(modifier, state) + } + + @Composable + fun Content(modifier: Modifier, state: ViewState?) { + if (state == null) return + + Row( + modifier = modifier, + verticalAlignment = Alignment.CenterVertically, + ) { + Text( + text = state.localizer.localize("APP.GENERAL.FREE_COLLATERAL"), + style = TextStyle.dydxDefault + .themeFont(fontSize = ThemeFont.FontSize.small) + .themeColor(ThemeColor.SemanticColor.text_tertiary), + ) + + Spacer(modifier = Modifier.weight(0.1f)) + + PlatformAmountChange( + modifier = Modifier.weight(1f), + before = if (state.before != null) { { + AmountText.Content( + state = state.before, + textStyle = TextStyle.dydxDefault + .themeFont(fontType = ThemeFont.FontType.number, fontSize = ThemeFont.FontSize.small) + .themeColor(ThemeColor.SemanticColor.text_tertiary), + ) + } } else { + null + }, + after = if (state.after != null) { { + AmountText.Content( + state = state.after, + textStyle = TextStyle.dydxDefault + .themeFont(fontType = ThemeFont.FontType.number, fontSize = ThemeFont.FontSize.small) + .themeColor(ThemeColor.SemanticColor.text_primary), + ) + } } else { + null + }, + direction = PlatformDirection.from(state.before?.amount, state.after?.amount), + textStyle = TextStyle.dydxDefault + .themeFont(fontSize = ThemeFont.FontSize.small) + .themeColor(ThemeColor.SemanticColor.text_tertiary), + ) + } + } +} diff --git a/v4/feature/receipt/src/main/java/exchange/dydx/trading/feature/receipt/components/liquidationprice/DydxReceiptLiquidationPriceViewModel.kt b/v4/feature/receipt/src/main/java/exchange/dydx/trading/feature/receipt/components/liquidationprice/DydxReceiptLiquidationPriceViewModel.kt new file mode 100644 index 00000000..a5cece7a --- /dev/null +++ b/v4/feature/receipt/src/main/java/exchange/dydx/trading/feature/receipt/components/liquidationprice/DydxReceiptLiquidationPriceViewModel.kt @@ -0,0 +1,120 @@ +package exchange.dydx.trading.feature.receipt.components.liquidationprice + +import androidx.lifecycle.ViewModel +import dagger.hilt.android.lifecycle.HiltViewModel +import exchange.dydx.abacus.output.Subaccount +import exchange.dydx.abacus.output.SubaccountPosition +import exchange.dydx.abacus.output.input.TradeInput +import exchange.dydx.abacus.output.input.TransferInput +import exchange.dydx.abacus.protocols.LocalizerProtocol +import exchange.dydx.dydxstatemanager.AbacusStateManagerProtocol +import exchange.dydx.trading.common.DydxViewModel +import exchange.dydx.trading.common.formatter.DydxFormatter +import exchange.dydx.trading.feature.receipt.ReceiptType +import exchange.dydx.trading.feature.shared.views.AmountText +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.flatMapLatest +import kotlinx.coroutines.flow.flowOf +import javax.inject.Inject + +@HiltViewModel +class DydxReceiptLiquidationPriceViewModel @Inject constructor( + private val localizer: LocalizerProtocol, + private val abacusStateManager: AbacusStateManagerProtocol, + private val formatter: DydxFormatter, + private val receiptTypeFlow: Flow<@JvmSuppressWildcards ReceiptType?>, +) : ViewModel(), DydxViewModel { + + @OptIn(ExperimentalCoroutinesApi::class) + val state: Flow = + receiptTypeFlow + .flatMapLatest { receiptType -> + when (receiptType) { + is ReceiptType.Trade -> { + combine( + abacusStateManager.state.selectedSubaccountPositions, + abacusStateManager.state.tradeInput, + ) { positions, tradeInput -> + createViewState(positions, tradeInput) + } + } + is ReceiptType.Transfer -> { + combine( + abacusStateManager.state.selectedSubaccount, + abacusStateManager.state.transferInput, + ) { subaccount, transferInput -> + createViewState(subaccount, transferInput) + } + } + else -> flowOf() + } + } + .distinctUntilChanged() + + private fun createViewState( + positions: List?, + tradeInput: TradeInput?, + ): DydxReceiptLiquidationPriceView.ViewState { + val marketId = tradeInput?.marketId ?: "ETH-USD" + val position = positions?.firstOrNull { it.id == marketId } + return DydxReceiptLiquidationPriceView.ViewState( + localizer = localizer, + before = if (position?.buyingPower?.current != null) { + AmountText.ViewState( + localizer = localizer, + formatter = formatter, + amount = position?.buyingPower?.current, + tickSize = 0, + requiresPositive = true, + ) + } else { + null + }, + after = if (position?.buyingPower?.postOrder != null) { + AmountText.ViewState( + localizer = localizer, + formatter = formatter, + amount = position?.buyingPower?.postOrder, + tickSize = 0, + requiresPositive = true, + ) + } else { + null + }, + ) + } + + private fun createViewState( + subaccount: Subaccount?, + transferInput: TransferInput?, + ): DydxReceiptLiquidationPriceView.ViewState { + return DydxReceiptLiquidationPriceView.ViewState( + localizer = localizer, + before = if (subaccount?.buyingPower?.current != null) { + AmountText.ViewState( + localizer = localizer, + formatter = formatter, + amount = subaccount?.buyingPower?.current, + tickSize = 0, + requiresPositive = true, + ) + } else { + null + }, + after = if (subaccount?.buyingPower?.postOrder != null) { + AmountText.ViewState( + localizer = localizer, + formatter = formatter, + amount = subaccount?.buyingPower?.postOrder, + tickSize = 0, + requiresPositive = true, + ) + } else { + null + }, + ) + } +} diff --git a/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/margin/DydxAdjustMarginInputView.kt b/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/margin/DydxAdjustMarginInputView.kt index 0d21211b..fb8b3220 100644 --- a/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/margin/DydxAdjustMarginInputView.kt +++ b/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/margin/DydxAdjustMarginInputView.kt @@ -28,6 +28,7 @@ import exchange.dydx.abacus.protocols.LocalizerProtocol import exchange.dydx.platformui.components.PlatformInfoScaffold import exchange.dydx.platformui.components.buttons.PlatformPillItem import exchange.dydx.platformui.components.changes.PlatformAmountChange +import exchange.dydx.platformui.components.changes.PlatformDirection import exchange.dydx.platformui.components.dividers.PlatformDivider import exchange.dydx.platformui.components.inputs.PlatformTextInput import exchange.dydx.platformui.components.tabgroups.PlatformTabGroup @@ -43,9 +44,15 @@ import exchange.dydx.trading.common.compose.collectAsStateWithLifecycle import exchange.dydx.trading.common.formatter.DydxFormatter import exchange.dydx.trading.common.theme.DydxThemedPreviewSurface import exchange.dydx.trading.common.theme.MockLocalizer +import exchange.dydx.trading.feature.receipt.DydxReceiptView +import exchange.dydx.trading.feature.receipt.components.buyingpower.DydxReceiptFreeCollateralView +import exchange.dydx.trading.feature.receipt.components.liquidationprice.DydxReceiptLiquidationPriceView +import exchange.dydx.trading.feature.receipt.components.marginusage.DydxReceiptMarginUsageView import exchange.dydx.trading.feature.shared.scaffolds.InputFieldScaffold +import exchange.dydx.trading.feature.shared.views.AmountText import exchange.dydx.trading.feature.shared.views.HeaderViewCloseBotton -import exchange.dydx.trading.feature.shared.views.SizeTextView +import exchange.dydx.trading.feature.shared.views.MarginUsageView +import exchange.dydx.trading.feature.trade.margin.components.DydxAdjustMarginCtaButton @Preview @Composable @@ -66,15 +73,14 @@ object DydxAdjustMarginInputView : DydxComponent { val percentage: Double, ) - data class SubaccountReceipt( - val freeCollateral: List, - val marginUsage: List, + data class IsolatedMarginReceipt( + val freeCollateral: DydxReceiptFreeCollateralView.ViewState, + val marginUsage: DydxReceiptMarginUsageView.ViewState, ) - data class PositionReceipt( - val freeCollateral: List, - val leverage: List, - val liquidationPrice: List, + data class ParentSubaccountReceipt( + val liquidationPrice: DydxReceiptLiquidationPriceView.ViewState, + val receipts: DydxReceiptView.ViewState, ) data class ViewState( @@ -84,8 +90,8 @@ object DydxAdjustMarginInputView : DydxComponent { val percentage: Double?, val percentageOptions: List, val amountText: String?, - val subaccountReceipt: SubaccountReceipt, - val positionReceipt: PositionReceipt, + val isolatedMarginReceipt: IsolatedMarginReceipt, + val parentSubaccountReceipt: ParentSubaccountReceipt, val error: String?, val marginDirectionAction: ((direction: MarginDirection) -> Unit) = {}, val percentageAction: (() -> Unit) = {}, @@ -106,14 +112,19 @@ object DydxAdjustMarginInputView : DydxComponent { PercentageOption("50%", 0.5), ), amountText = "500", - subaccountReceipt = SubaccountReceipt( - freeCollateral = listOf("1000.00", "500.00"), - marginUsage = listOf("19.34", "38.45"), + isolatedMarginReceipt = IsolatedMarginReceipt( + freeCollateral = DydxReceiptFreeCollateralView.ViewState.preview, + marginUsage = DydxReceiptMarginUsageView.ViewState.preview, ), - positionReceipt = PositionReceipt( - freeCollateral = listOf("1000.00", "1500.00"), - leverage = listOf("3.1", "2.4"), - liquidationPrice = listOf("1200.00", "1000.00"), + parentSubaccountReceipt = ParentSubaccountReceipt( + liquidationPrice = DydxReceiptLiquidationPriceView.ViewState.preview, + receipts = DydxReceiptView.ViewState( + localizer = MockLocalizer(), + lineTypes = listOf( + DydxReceiptView.ReceiptLineType.FreeCollateral, + DydxReceiptView.ReceiptLineType.MarginUsage, + ), + ), ), error = null, ) @@ -213,7 +224,10 @@ object DydxAdjustMarginInputView : DydxComponent { } } - private fun marginDirectionText(direction: MarginDirection, localizer: LocalizerProtocol): String { + private fun marginDirectionText( + direction: MarginDirection, + localizer: LocalizerProtocol + ): String { return when (direction) { MarginDirection.Add -> localizer.localize("APP.TRADE.ADD_MARGIN") MarginDirection.Remove -> localizer.localize("APP.TRADE.REMOVE_MARGIN") @@ -292,7 +306,7 @@ object DydxAdjustMarginInputView : DydxComponent { state: ViewState, ) { PlatformTabGroup( - modifier = Modifier.fillMaxWidth(), + modifier = modifier.fillMaxWidth(), scrollingEnabled = false, items = state.percentageOptions.map { { modifier -> @@ -438,28 +452,44 @@ object DydxAdjustMarginInputView : DydxComponent { modifier = modifier, ) { PlatformAmountChange( - before = { - SizeTextView.Content( - modifier = Modifier, - state = SizeTextView.ViewState( - localizer = state.localizer, - formatter = state.formatter, - size = state.subaccountReceipt.freeCollateral.firstOrNull()?.toDoubleOrNull(), - stepSize = 2, - ), - ) + modifier = Modifier.weight(1f), + before = if (state.isolatedMarginReceipt.freeCollateral.before != null) { + { + AmountText.Content( + state = state.isolatedMarginReceipt.freeCollateral.before, + textStyle = TextStyle.dydxDefault + .themeFont( + fontType = ThemeFont.FontType.number, + fontSize = ThemeFont.FontSize.small, + ) + .themeColor(ThemeColor.SemanticColor.text_tertiary), + ) + } + } else { + null }, - after = { - SizeTextView.Content( - modifier = Modifier, - state = SizeTextView.ViewState( - localizer = state.localizer, - formatter = state.formatter, - size = state.subaccountReceipt.freeCollateral.lastOrNull()?.toDoubleOrNull(), - stepSize = 2, - ), - ) + after = if (state.isolatedMarginReceipt.freeCollateral.after != null) { + { + AmountText.Content( + state = state.isolatedMarginReceipt.freeCollateral.after, + textStyle = TextStyle.dydxDefault + .themeFont( + fontType = ThemeFont.FontType.number, + fontSize = ThemeFont.FontSize.small, + ) + .themeColor(ThemeColor.SemanticColor.text_primary), + ) + } + } else { + null }, + direction = PlatformDirection.from( + state.isolatedMarginReceipt.freeCollateral.before?.amount, + state.isolatedMarginReceipt.freeCollateral.after?.amount, + ), + textStyle = TextStyle.dydxDefault + .themeFont(fontSize = ThemeFont.FontSize.small) + .themeColor(ThemeColor.SemanticColor.text_tertiary), ) } } @@ -495,28 +525,47 @@ object DydxAdjustMarginInputView : DydxComponent { modifier = modifier, ) { PlatformAmountChange( - before = { - SizeTextView.Content( - modifier = Modifier, - state = SizeTextView.ViewState( - localizer = state.localizer, + modifier = Modifier.weight(1f), + before = if (state.isolatedMarginReceipt.marginUsage.before != null) { + { + MarginUsageView.Content( + state = state.isolatedMarginReceipt.marginUsage.before, formatter = state.formatter, - size = state.subaccountReceipt.marginUsage.firstOrNull()?.toDoubleOrNull(), - stepSize = 2, - ), - ) + textStyle = TextStyle.dydxDefault + .themeFont( + fontSize = ThemeFont.FontSize.small, + fontType = ThemeFont.FontType.number, + ) + .themeColor(ThemeColor.SemanticColor.text_tertiary), + ) + } + } else { + null }, - after = { - SizeTextView.Content( - modifier = Modifier, - state = SizeTextView.ViewState( - localizer = state.localizer, + after = + if (state.isolatedMarginReceipt.marginUsage.after != null) { + { + MarginUsageView.Content( + state = state.isolatedMarginReceipt.marginUsage.after, formatter = state.formatter, - size = state.subaccountReceipt.marginUsage.lastOrNull()?.toDoubleOrNull(), - stepSize = 2, - ), - ) + textStyle = TextStyle.dydxDefault + .themeFont( + fontSize = ThemeFont.FontSize.small, + fontType = ThemeFont.FontType.number, + ) + .themeColor(ThemeColor.SemanticColor.text_primary), + ) + } + } else { + null }, + direction = PlatformDirection.from( + state.isolatedMarginReceipt.marginUsage.after?.percent, + state.isolatedMarginReceipt.marginUsage.before?.percent, + ), + textStyle = TextStyle.dydxDefault + .themeFont(fontSize = ThemeFont.FontSize.small) + .themeColor(ThemeColor.SemanticColor.text_tertiary), ) } } @@ -542,6 +591,22 @@ object DydxAdjustMarginInputView : DydxComponent { modifier: Modifier, state: ViewState, ) { - // TODO, implement this + // Spacer(modifier = Modifier.weight(1f)) + + Column( + modifier = modifier + .fillMaxWidth(), + ) { + DydxReceiptView.Content( + modifier = Modifier + .offset(y = ThemeShapes.VerticalPadding), + state = state.parentSubaccountReceipt.receipts, + ) + DydxAdjustMarginCtaButton.Content( + Modifier + .fillMaxWidth() + .padding(bottom = ThemeShapes.VerticalPadding * 2), + ) + } } } diff --git a/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/margin/DydxAdjustMarginInputViewModel.kt b/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/margin/DydxAdjustMarginInputViewModel.kt index f657cda6..41abe9a1 100644 --- a/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/margin/DydxAdjustMarginInputViewModel.kt +++ b/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/margin/DydxAdjustMarginInputViewModel.kt @@ -1,7 +1,10 @@ package exchange.dydx.trading.feature.trade.margin +import androidx.compose.ui.unit.dp import androidx.lifecycle.ViewModel import dagger.hilt.android.lifecycle.HiltViewModel +import exchange.dydx.abacus.output.Subaccount +import exchange.dydx.abacus.output.SubaccountPosition import exchange.dydx.abacus.output.input.TradeInput import exchange.dydx.abacus.protocols.LocalizerProtocol import exchange.dydx.dydxstatemanager.AbacusStateManagerProtocol @@ -9,9 +12,15 @@ import exchange.dydx.platformui.components.PlatformInfo import exchange.dydx.trading.common.DydxViewModel import exchange.dydx.trading.common.formatter.DydxFormatter import exchange.dydx.trading.common.navigation.DydxRouter +import exchange.dydx.trading.feature.receipt.DydxReceiptView +import exchange.dydx.trading.feature.receipt.components.buyingpower.DydxReceiptFreeCollateralView +import exchange.dydx.trading.feature.receipt.components.liquidationprice.DydxReceiptLiquidationPriceView +import exchange.dydx.trading.feature.receipt.components.marginusage.DydxReceiptMarginUsageView +import exchange.dydx.trading.feature.shared.views.AmountText +import exchange.dydx.trading.feature.shared.views.MarginUsageView import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.distinctUntilChanged -import kotlinx.coroutines.flow.map import javax.inject.Inject @HiltViewModel @@ -23,13 +32,23 @@ class DydxAdjustMarginInputViewModel @Inject constructor( val platformInfo: PlatformInfo, ) : ViewModel(), DydxViewModel { - val state: Flow = abacusStateManager.state.tradeInput - .map { - createViewState(it) + val state: Flow = + combine( + abacusStateManager.state.tradeInput, + abacusStateManager.state.selectedSubaccount, + abacusStateManager.state.selectedSubaccountPositions, + ) { tradeInput, subaccount, positions -> + createViewState(tradeInput, subaccount, positions) } - .distinctUntilChanged() + .distinctUntilChanged() + + private fun createViewState( + tradeInput: TradeInput?, + subaccount: Subaccount?, + positions: List?, + ): DydxAdjustMarginInputView.ViewState { + val isolatedMargin = positions?.firstOrNull() - private fun createViewState(tradeInput: TradeInput?): DydxAdjustMarginInputView.ViewState { /* Abacus not implemented for adjust margin yet. This is a placeholder. */ @@ -45,14 +64,62 @@ class DydxAdjustMarginInputViewModel @Inject constructor( DydxAdjustMarginInputView.PercentageOption("50%", 0.5), ), amountText = "500", - subaccountReceipt = DydxAdjustMarginInputView.SubaccountReceipt( - freeCollateral = listOf("1000.00", "500.00"), - marginUsage = listOf("19.34", "38.45"), + isolatedMarginReceipt = DydxAdjustMarginInputView.IsolatedMarginReceipt( + freeCollateral = DydxReceiptFreeCollateralView.ViewState( + localizer = localizer, + before = AmountText.ViewState( + localizer = localizer, + formatter = formatter, + amount = isolatedMargin?.freeCollateral?.current, + tickSize = 2, + ), + after = AmountText.ViewState( + localizer = localizer, + formatter = formatter, + amount = isolatedMargin?.freeCollateral?.postOrder, + tickSize = 2, + ), + ), + marginUsage = DydxReceiptMarginUsageView.ViewState( + localizer = localizer, + formatter = formatter, + before = MarginUsageView.ViewState( + localizer = localizer, + displayOption = MarginUsageView.DisplayOption.IconAndValue, +// percent = isolatedMargin?.marginUsage?.current ?: 0.5, + ), + after = MarginUsageView.ViewState( + localizer = localizer, + displayOption = MarginUsageView.DisplayOption.IconAndValue, +// percent = isolatedMargin?.marginUsage?.current ?: 0.5, + ), + ), ), - positionReceipt = DydxAdjustMarginInputView.PositionReceipt( - freeCollateral = listOf("1000.00", "1500.00"), - leverage = listOf("3.1", "2.4"), - liquidationPrice = listOf("1200.00", "1000.00"), + parentSubaccountReceipt = DydxAdjustMarginInputView.ParentSubaccountReceipt( + liquidationPrice = DydxReceiptLiquidationPriceView.ViewState( + localizer = localizer, + before = AmountText.ViewState( + localizer = localizer, + formatter = formatter, + amount = isolatedMargin?.liquidationPrice?.current, + tickSize = 2, + ), + after = AmountText.ViewState( + localizer = localizer, + formatter = formatter, + amount = isolatedMargin?.liquidationPrice?.postOrder, + tickSize = 2, + ), + ), + receipts = DydxReceiptView.ViewState( + localizer = localizer, + height = 128.dp, + padding = 0.dp, + lineTypes = listOf( + DydxReceiptView.ReceiptLineType.FreeCollateral, + DydxReceiptView.ReceiptLineType.MarginUsage, + ), + ), ), error = null, marginDirectionAction = { }, diff --git a/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/margin/components/DydxAdjustMarginCtaButton.kt b/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/margin/components/DydxAdjustMarginCtaButton.kt new file mode 100644 index 00000000..529159c5 --- /dev/null +++ b/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/margin/components/DydxAdjustMarginCtaButton.kt @@ -0,0 +1,50 @@ +package exchange.dydx.trading.feature.trade.margin.components + +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.tooling.preview.Preview +import androidx.hilt.navigation.compose.hiltViewModel +import exchange.dydx.trading.common.component.DydxComponent +import exchange.dydx.trading.common.compose.collectAsStateWithLifecycle +import exchange.dydx.trading.common.theme.DydxThemedPreviewSurface +import exchange.dydx.trading.feature.shared.views.InputCtaButton + +@Preview +@Composable +fun Preview_DydxAdjustMarginCtaButton() { + DydxThemedPreviewSurface { + DydxAdjustMarginCtaButton.Content(Modifier, DydxAdjustMarginCtaButton.ViewState.preview) + } +} + +object DydxAdjustMarginCtaButton : DydxComponent { + data class ViewState( + val ctaButton: InputCtaButton.ViewState? = null, + ) { + companion object { + val preview = ViewState( + ctaButton = InputCtaButton.ViewState.preview, + ) + } + } + + @Composable + override fun Content(modifier: Modifier) { + val viewModel: DydxAdjustMarginCtaButtonModel = hiltViewModel() + + val state = viewModel.state.collectAsStateWithLifecycle(initialValue = null).value + Content(modifier, state) + } + + @Composable + fun Content(modifier: Modifier, state: ViewState?) { + if (state == null) { + return + } + + InputCtaButton.Content( + modifier = modifier, + state = state.ctaButton, + ) + } +} diff --git a/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/margin/components/DydxAdjustMarginCtaButtonModel.kt b/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/margin/components/DydxAdjustMarginCtaButtonModel.kt new file mode 100644 index 00000000..441d4564 --- /dev/null +++ b/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/margin/components/DydxAdjustMarginCtaButtonModel.kt @@ -0,0 +1,56 @@ +package exchange.dydx.trading.feature.trade.margin.components + +import androidx.lifecycle.ViewModel +import dagger.hilt.android.lifecycle.HiltViewModel +import exchange.dydx.abacus.output.input.ErrorType +import exchange.dydx.abacus.output.input.TradeInput +import exchange.dydx.abacus.output.input.ValidationError +import exchange.dydx.abacus.protocols.LocalizerProtocol +import exchange.dydx.dydxstatemanager.AbacusStateManagerProtocol +import exchange.dydx.trading.common.AppConfig +import exchange.dydx.trading.common.DydxViewModel +import exchange.dydx.trading.common.formatter.DydxFormatter +import exchange.dydx.trading.common.theme.DydxTheme +import exchange.dydx.trading.feature.shared.views.InputCtaButton +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.distinctUntilChanged +import javax.inject.Inject + +@HiltViewModel +class DydxAdjustMarginCtaButtonModel @Inject constructor( + private val appConfig: AppConfig, + private val theme: DydxTheme, + private val localizer: LocalizerProtocol, + private val abacusStateManager: AbacusStateManagerProtocol, + private val formatter: DydxFormatter, +) : ViewModel(), DydxViewModel { + val state: Flow = + combine( + abacusStateManager.state.tradeInput, + abacusStateManager.state.validationErrors, + ) { tradeInput, validationErrors -> + createViewState(tradeInput, validationErrors) + } + .distinctUntilChanged() + + private fun createViewState( + tradeInput: TradeInput?, + validationErrors: List, + ): DydxAdjustMarginCtaButton.ViewState { + val firstBlockingError = + validationErrors.firstOrNull { it.type == ErrorType.required || it.type == ErrorType.error } + + return DydxAdjustMarginCtaButton.ViewState( + ctaButton = InputCtaButton.ViewState( + localizer = localizer, + ctaButtonState = InputCtaButton.State.Enabled( + localizer.localize("APP.TRADE.ADD_MARGIN"), + ), + ctaAction = { + // TODO, Submit the orders + }, + ), + ) + } +} From a7d3fc7019f084d5cd06024f4a8d7ee6df00bc5f Mon Sep 17 00:00:00 2001 From: John Huang Date: Mon, 29 Apr 2024 13:26:57 -0700 Subject: [PATCH 16/21] MOB-400 rearranging receipt data --- .../feature/receipt/DydxReceiptView.kt | 12 ++ ...DydxReceiptIsolatedPositionLeverageView.kt | 109 ++++++++++++++++++ ...eceiptIsolatedPositionLeverageViewModel.kt | 63 ++++++++++ ...xReceiptIsolatedPositionMarginUsageView.kt | 109 ++++++++++++++++++ ...iptIsolatedPositionMarginUsageViewModel.kt | 62 ++++++++++ .../feature/shared/views/LeverageView.kt | 96 +++++++++++++++ .../trade/margin/DydxAdjustMarginInputView.kt | 36 +++--- .../margin/DydxAdjustMarginInputViewModel.kt | 16 +-- 8 files changed, 477 insertions(+), 26 deletions(-) create mode 100644 v4/feature/receipt/src/main/java/exchange/dydx/trading/feature/receipt/components/isolatedmargin/DydxReceiptIsolatedPositionLeverageView.kt create mode 100644 v4/feature/receipt/src/main/java/exchange/dydx/trading/feature/receipt/components/isolatedmargin/DydxReceiptIsolatedPositionLeverageViewModel.kt create mode 100644 v4/feature/receipt/src/main/java/exchange/dydx/trading/feature/receipt/components/isolatedmargin/DydxReceiptIsolatedPositionMarginUsageView.kt create mode 100644 v4/feature/receipt/src/main/java/exchange/dydx/trading/feature/receipt/components/isolatedmargin/DydxReceiptIsolatedPositionMarginUsageViewModel.kt create mode 100644 v4/feature/shared/src/main/java/exchange/dydx/trading/feature/shared/views/LeverageView.kt diff --git a/v4/feature/receipt/src/main/java/exchange/dydx/trading/feature/receipt/DydxReceiptView.kt b/v4/feature/receipt/src/main/java/exchange/dydx/trading/feature/receipt/DydxReceiptView.kt index a71c2adc..422342d3 100644 --- a/v4/feature/receipt/src/main/java/exchange/dydx/trading/feature/receipt/DydxReceiptView.kt +++ b/v4/feature/receipt/src/main/java/exchange/dydx/trading/feature/receipt/DydxReceiptView.kt @@ -33,6 +33,8 @@ import exchange.dydx.trading.feature.receipt.components.expectedprice.DydxReceip import exchange.dydx.trading.feature.receipt.components.fee.DydxReceiptBridgeFeeView import exchange.dydx.trading.feature.receipt.components.fee.DydxReceiptFeeView import exchange.dydx.trading.feature.receipt.components.fee.DydxReceiptGasFeeView +import exchange.dydx.trading.feature.receipt.components.isolatedmargin.DydxReceiptIsolatedPositionLeverageView +import exchange.dydx.trading.feature.receipt.components.isolatedmargin.DydxReceiptIsolatedPositionMarginUsageView import exchange.dydx.trading.feature.receipt.components.marginusage.DydxReceiptMarginUsageView import exchange.dydx.trading.feature.receipt.components.rewards.DydxReceiptRewardsView import exchange.dydx.trading.feature.receipt.components.slippage.DydxReceiptSlippageView @@ -51,6 +53,8 @@ object DydxReceiptView : DydxComponent { FreeCollateral, BuyingPower, MarginUsage, + IsolatedPositionLeverage, + IsolatedPositionMarginUsage, Fee, GasFee, BridgeFee, @@ -127,6 +131,14 @@ object DydxReceiptView : DydxComponent { DydxReceiptMarginUsageView.Content(Modifier.animateItemPlacement()) } + ReceiptLineType.IsolatedPositionLeverage -> { + DydxReceiptIsolatedPositionLeverageView.Content(Modifier.animateItemPlacement()) + } + + ReceiptLineType.IsolatedPositionMarginUsage -> { + DydxReceiptIsolatedPositionMarginUsageView.Content(Modifier.animateItemPlacement()) + } + ReceiptLineType.Fee -> { DydxReceiptFeeView.Content(Modifier.animateItemPlacement()) } diff --git a/v4/feature/receipt/src/main/java/exchange/dydx/trading/feature/receipt/components/isolatedmargin/DydxReceiptIsolatedPositionLeverageView.kt b/v4/feature/receipt/src/main/java/exchange/dydx/trading/feature/receipt/components/isolatedmargin/DydxReceiptIsolatedPositionLeverageView.kt new file mode 100644 index 00000000..b9ebd55c --- /dev/null +++ b/v4/feature/receipt/src/main/java/exchange/dydx/trading/feature/receipt/components/isolatedmargin/DydxReceiptIsolatedPositionLeverageView.kt @@ -0,0 +1,109 @@ +package exchange.dydx.trading.feature.receipt.components.isolatedmargin + +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.tooling.preview.Preview +import androidx.hilt.navigation.compose.hiltViewModel +import exchange.dydx.abacus.protocols.LocalizerProtocol +import exchange.dydx.platformui.components.changes.PlatformAmountChange +import exchange.dydx.platformui.components.changes.PlatformDirection +import exchange.dydx.platformui.designSystem.theme.ThemeColor +import exchange.dydx.platformui.designSystem.theme.ThemeFont +import exchange.dydx.platformui.designSystem.theme.dydxDefault +import exchange.dydx.platformui.designSystem.theme.themeColor +import exchange.dydx.platformui.designSystem.theme.themeFont +import exchange.dydx.trading.common.component.DydxComponent +import exchange.dydx.trading.common.compose.collectAsStateWithLifecycle +import exchange.dydx.trading.common.formatter.DydxFormatter +import exchange.dydx.trading.common.theme.DydxThemedPreviewSurface +import exchange.dydx.trading.common.theme.MockLocalizer +import exchange.dydx.trading.feature.shared.views.LeverageView + +@Preview +@Composable +fun Preview_DydxReceiptLeverageView() { + DydxThemedPreviewSurface { + DydxReceiptIsolatedPositionLeverageView.Content(Modifier, DydxReceiptIsolatedPositionLeverageView.ViewState.preview) + } +} + +object DydxReceiptIsolatedPositionLeverageView : DydxComponent { + data class ViewState( + val localizer: LocalizerProtocol, + val formatter: DydxFormatter, + val before: LeverageView.ViewState? = null, + val after: LeverageView.ViewState? = null, + ) { + companion object { + val preview = ViewState( + localizer = MockLocalizer(), + formatter = DydxFormatter(), + before = LeverageView.ViewState.preview, + after = LeverageView.ViewState.preview, + ) + } + } + + @Composable + override fun Content(modifier: Modifier) { + val viewModel: DydxReceiptIsolatedPositionLeverageViewModel = hiltViewModel() + + val state = viewModel.state.collectAsStateWithLifecycle(initialValue = null).value + Content(modifier, state) + } + + @Composable + fun Content(modifier: Modifier, state: ViewState?) { + if (state == null) return + + Row( + modifier = modifier, + verticalAlignment = Alignment.CenterVertically, + ) { + Text( + text = state.localizer.localize("APP.TRADE.POSITION_LEVERAGE"), + style = TextStyle.dydxDefault + .themeFont(fontSize = ThemeFont.FontSize.small) + .themeColor(ThemeColor.SemanticColor.text_tertiary), + ) + + Spacer(modifier = Modifier.weight(0.1f)) + + PlatformAmountChange( + modifier = Modifier.weight(1f), + before = if (state.before != null) { { + LeverageView.Content( + state = state.before, + formatter = state.formatter, + textStyle = TextStyle.dydxDefault + .themeFont(fontSize = ThemeFont.FontSize.small, fontType = ThemeFont.FontType.number) + .themeColor(ThemeColor.SemanticColor.text_tertiary), + ) + } } else { + null + }, + after = + if (state.after != null) { { + LeverageView.Content( + state = state.after, + formatter = state.formatter, + textStyle = TextStyle.dydxDefault + .themeFont(fontSize = ThemeFont.FontSize.small, fontType = ThemeFont.FontType.number) + .themeColor(ThemeColor.SemanticColor.text_primary), + ) + } } else { + null + }, + direction = PlatformDirection.from(state.after?.leverage, state.before?.leverage), + textStyle = TextStyle.dydxDefault + .themeFont(fontSize = ThemeFont.FontSize.small) + .themeColor(ThemeColor.SemanticColor.text_tertiary), + ) + } + } +} diff --git a/v4/feature/receipt/src/main/java/exchange/dydx/trading/feature/receipt/components/isolatedmargin/DydxReceiptIsolatedPositionLeverageViewModel.kt b/v4/feature/receipt/src/main/java/exchange/dydx/trading/feature/receipt/components/isolatedmargin/DydxReceiptIsolatedPositionLeverageViewModel.kt new file mode 100644 index 00000000..81d6ca17 --- /dev/null +++ b/v4/feature/receipt/src/main/java/exchange/dydx/trading/feature/receipt/components/isolatedmargin/DydxReceiptIsolatedPositionLeverageViewModel.kt @@ -0,0 +1,63 @@ +package exchange.dydx.trading.feature.receipt.components.isolatedmargin + +import androidx.lifecycle.ViewModel +import dagger.hilt.android.lifecycle.HiltViewModel +import exchange.dydx.abacus.output.TradeStatesWithDoubleValues +import exchange.dydx.abacus.protocols.LocalizerProtocol +import exchange.dydx.dydxstatemanager.AbacusStateManagerProtocol +import exchange.dydx.trading.common.DydxViewModel +import exchange.dydx.trading.common.formatter.DydxFormatter +import exchange.dydx.trading.feature.shared.views.LeverageView +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.map +import javax.inject.Inject + +@HiltViewModel +class DydxReceiptIsolatedPositionLeverageViewModel @Inject constructor( + private val localizer: LocalizerProtocol, + private val abacusStateManager: AbacusStateManagerProtocol, + private val formatter: DydxFormatter, +) : ViewModel(), DydxViewModel { + + val state: Flow = + abacusStateManager.marketId + .map { + createViewState(it) + } + .distinctUntilChanged() + + private fun createViewState( + marketId: String? + ): DydxReceiptIsolatedPositionLeverageView.ViewState { + val position = if (marketId != null) abacusStateManager.state.selectedSubaccountPositionOfMarket(marketId).value else null + val leverage: TradeStatesWithDoubleValues? = position?.leverage + val margin: TradeStatesWithDoubleValues? = null // position?.margin + /* + TODO: After abacus exposes Leverage, changes to next line + if (marketId != null) abacusStateManager.state.selectedSubaccountPositionOfMarket(marketId).value?.leverage else null + */ + return DydxReceiptIsolatedPositionLeverageView.ViewState( + localizer = localizer, + formatter = formatter, + before = if (leverage?.current != null) { + LeverageView.ViewState( + localizer = localizer, + leverage = leverage.current ?: 0.0, + margin = margin?.current, + ) + } else { + null + }, + after = if (leverage?.postOrder != null) { + LeverageView.ViewState( + localizer = localizer, + leverage = leverage.postOrder ?: 0.0, + margin = margin?.postOrder, + ) + } else { + null + }, + ) + } +} diff --git a/v4/feature/receipt/src/main/java/exchange/dydx/trading/feature/receipt/components/isolatedmargin/DydxReceiptIsolatedPositionMarginUsageView.kt b/v4/feature/receipt/src/main/java/exchange/dydx/trading/feature/receipt/components/isolatedmargin/DydxReceiptIsolatedPositionMarginUsageView.kt new file mode 100644 index 00000000..0eabf582 --- /dev/null +++ b/v4/feature/receipt/src/main/java/exchange/dydx/trading/feature/receipt/components/isolatedmargin/DydxReceiptIsolatedPositionMarginUsageView.kt @@ -0,0 +1,109 @@ +package exchange.dydx.trading.feature.receipt.components.isolatedmargin + +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.tooling.preview.Preview +import androidx.hilt.navigation.compose.hiltViewModel +import exchange.dydx.abacus.protocols.LocalizerProtocol +import exchange.dydx.platformui.components.changes.PlatformAmountChange +import exchange.dydx.platformui.components.changes.PlatformDirection +import exchange.dydx.platformui.designSystem.theme.ThemeColor +import exchange.dydx.platformui.designSystem.theme.ThemeFont +import exchange.dydx.platformui.designSystem.theme.dydxDefault +import exchange.dydx.platformui.designSystem.theme.themeColor +import exchange.dydx.platformui.designSystem.theme.themeFont +import exchange.dydx.trading.common.component.DydxComponent +import exchange.dydx.trading.common.compose.collectAsStateWithLifecycle +import exchange.dydx.trading.common.formatter.DydxFormatter +import exchange.dydx.trading.common.theme.DydxThemedPreviewSurface +import exchange.dydx.trading.common.theme.MockLocalizer +import exchange.dydx.trading.feature.shared.views.MarginUsageView + +@Preview +@Composable +fun Preview_DydxReceiptMarginUsageView() { + DydxThemedPreviewSurface { + DydxReceiptIsolatedPositionMarginUsageView.Content(Modifier, DydxReceiptIsolatedPositionMarginUsageView.ViewState.preview) + } +} + +object DydxReceiptIsolatedPositionMarginUsageView : DydxComponent { + data class ViewState( + val localizer: LocalizerProtocol, + val formatter: DydxFormatter, + val before: MarginUsageView.ViewState? = null, + val after: MarginUsageView.ViewState? = null, + ) { + companion object { + val preview = ViewState( + localizer = MockLocalizer(), + formatter = DydxFormatter(), + before = MarginUsageView.ViewState.preview, + after = MarginUsageView.ViewState.preview, + ) + } + } + + @Composable + override fun Content(modifier: Modifier) { + val viewModel: DydxReceiptIsolatedPositionMarginUsageViewModel = hiltViewModel() + + val state = viewModel.state.collectAsStateWithLifecycle(initialValue = null).value + Content(modifier, state) + } + + @Composable + fun Content(modifier: Modifier, state: ViewState?) { + if (state == null) return + + Row( + modifier = modifier, + verticalAlignment = Alignment.CenterVertically, + ) { + Text( + text = state.localizer.localize("APP.TRADE.POSITION_MARGIN"), + style = TextStyle.dydxDefault + .themeFont(fontSize = ThemeFont.FontSize.small) + .themeColor(ThemeColor.SemanticColor.text_tertiary), + ) + + Spacer(modifier = Modifier.weight(0.1f)) + + PlatformAmountChange( + modifier = Modifier.weight(1f), + before = if (state.before != null) { { + MarginUsageView.Content( + state = state.before, + formatter = state.formatter, + textStyle = TextStyle.dydxDefault + .themeFont(fontSize = ThemeFont.FontSize.small, fontType = ThemeFont.FontType.number) + .themeColor(ThemeColor.SemanticColor.text_tertiary), + ) + } } else { + null + }, + after = + if (state.after != null) { { + MarginUsageView.Content( + state = state.after, + formatter = state.formatter, + textStyle = TextStyle.dydxDefault + .themeFont(fontSize = ThemeFont.FontSize.small, fontType = ThemeFont.FontType.number) + .themeColor(ThemeColor.SemanticColor.text_primary), + ) + } } else { + null + }, + direction = PlatformDirection.from(state.after?.percent, state.before?.percent), + textStyle = TextStyle.dydxDefault + .themeFont(fontSize = ThemeFont.FontSize.small) + .themeColor(ThemeColor.SemanticColor.text_tertiary), + ) + } + } +} diff --git a/v4/feature/receipt/src/main/java/exchange/dydx/trading/feature/receipt/components/isolatedmargin/DydxReceiptIsolatedPositionMarginUsageViewModel.kt b/v4/feature/receipt/src/main/java/exchange/dydx/trading/feature/receipt/components/isolatedmargin/DydxReceiptIsolatedPositionMarginUsageViewModel.kt new file mode 100644 index 00000000..82a128ad --- /dev/null +++ b/v4/feature/receipt/src/main/java/exchange/dydx/trading/feature/receipt/components/isolatedmargin/DydxReceiptIsolatedPositionMarginUsageViewModel.kt @@ -0,0 +1,62 @@ +package exchange.dydx.trading.feature.receipt.components.isolatedmargin + +import androidx.lifecycle.ViewModel +import dagger.hilt.android.lifecycle.HiltViewModel +import exchange.dydx.abacus.output.TradeStatesWithDoubleValues +import exchange.dydx.abacus.protocols.LocalizerProtocol +import exchange.dydx.dydxstatemanager.AbacusStateManagerProtocol +import exchange.dydx.trading.common.DydxViewModel +import exchange.dydx.trading.common.formatter.DydxFormatter +import exchange.dydx.trading.feature.shared.views.MarginUsageView +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.map +import javax.inject.Inject + +@HiltViewModel +class DydxReceiptIsolatedPositionMarginUsageViewModel @Inject constructor( + private val localizer: LocalizerProtocol, + private val abacusStateManager: AbacusStateManagerProtocol, + private val formatter: DydxFormatter, +) : ViewModel(), DydxViewModel { + + val state: Flow = + abacusStateManager.marketId + .map { + createViewState(it) + } + .distinctUntilChanged() + + private fun createViewState( + marketId: String? + ): DydxReceiptIsolatedPositionMarginUsageView.ViewState { + val marginUsage: TradeStatesWithDoubleValues? = + null + /* + TODO: After abacus exposes marginUsage, changes to next line + if (marketId != null) abacusStateManager.state.selectedSubaccountPositionOfMarket(marketId).value?.leverage else null + */ + return DydxReceiptIsolatedPositionMarginUsageView.ViewState( + localizer = localizer, + formatter = formatter, + before = if (marginUsage?.current != null) { + MarginUsageView.ViewState( + localizer = localizer, + displayOption = MarginUsageView.DisplayOption.IconAndValue, + percent = marginUsage?.current ?: 0.0, + ) + } else { + null + }, + after = if (marginUsage?.postOrder != null) { + MarginUsageView.ViewState( + localizer = localizer, + displayOption = MarginUsageView.DisplayOption.IconAndValue, + percent = marginUsage?.postOrder ?: 0.0, + ) + } else { + null + }, + ) + } +} diff --git a/v4/feature/shared/src/main/java/exchange/dydx/trading/feature/shared/views/LeverageView.kt b/v4/feature/shared/src/main/java/exchange/dydx/trading/feature/shared/views/LeverageView.kt new file mode 100644 index 00000000..6262eb68 --- /dev/null +++ b/v4/feature/shared/src/main/java/exchange/dydx/trading/feature/shared/views/LeverageView.kt @@ -0,0 +1,96 @@ +package exchange.dydx.trading.feature.shared.views + +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.width +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import exchange.dydx.abacus.protocols.LocalizerProtocol +import exchange.dydx.platformui.designSystem.theme.ThemeColor +import exchange.dydx.platformui.designSystem.theme.dydxDefault +import exchange.dydx.platformui.designSystem.theme.themeColor +import exchange.dydx.trading.common.formatter.DydxFormatter +import exchange.dydx.trading.common.theme.DydxThemedPreviewSurface +import exchange.dydx.trading.common.theme.MockLocalizer + +@Preview +@Composable +fun Preview_LeverageView() { + DydxThemedPreviewSurface { + LeverageView.Content(Modifier, LeverageView.ViewState.preview, DydxFormatter()) + } +} + +object LeverageView { + + enum class DisplayOption { + IconOnly, IconAndValue + } + + data class ViewState( + val localizer: LocalizerProtocol, + val leverage: Double = 3.0, + val margin: Double? = null, + ) { + companion object { + val preview = ViewState( + localizer = MockLocalizer(), + ) + } + } + + @Composable + fun Content( + modifier: Modifier = Modifier, + state: ViewState?, + formatter: DydxFormatter, + textStyle: TextStyle = TextStyle.dydxDefault, + ) { + if (state == null) return + + val leverageText = formatter.leverage(state.leverage) + val leverageIcon = if (state.margin != null) { + LeverageRiskView.ViewState( + localizer = state.localizer, + level = LeverageRiskView.Level.createFromMarginUsage(state.margin), + ) + } else { + null + } + + Row( + verticalAlignment = androidx.compose.ui.Alignment.CenterVertically, + ) { + if (leverageIcon != null) { + LeverageRiskView.Content( + modifier = Modifier, + state = leverageIcon.copy( + displayOption = LeverageRiskView.DisplayOption.IconOnly, + viewSize = 14.dp, + ), + ) + Spacer(modifier = Modifier.width(6.dp)) + } + CreateValueText(Modifier, leverageText) + } + } + + @Composable + private fun CreateValueText( + modifier: Modifier, + value: String?, + ) { + Text( + text = value ?: "-", + style = TextStyle.dydxDefault + .themeColor(ThemeColor.SemanticColor.text_primary), + maxLines = 1, + overflow = TextOverflow.Ellipsis, + ) + } +} diff --git a/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/margin/DydxAdjustMarginInputView.kt b/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/margin/DydxAdjustMarginInputView.kt index fb8b3220..5906e328 100644 --- a/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/margin/DydxAdjustMarginInputView.kt +++ b/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/margin/DydxAdjustMarginInputView.kt @@ -73,12 +73,12 @@ object DydxAdjustMarginInputView : DydxComponent { val percentage: Double, ) - data class IsolatedMarginReceipt( + data class CrossMarginReceipt( val freeCollateral: DydxReceiptFreeCollateralView.ViewState, val marginUsage: DydxReceiptMarginUsageView.ViewState, ) - data class ParentSubaccountReceipt( + data class IsolatedMarginReceipt( val liquidationPrice: DydxReceiptLiquidationPriceView.ViewState, val receipts: DydxReceiptView.ViewState, ) @@ -90,8 +90,8 @@ object DydxAdjustMarginInputView : DydxComponent { val percentage: Double?, val percentageOptions: List, val amountText: String?, + val crossMarginReceipt: CrossMarginReceipt, val isolatedMarginReceipt: IsolatedMarginReceipt, - val parentSubaccountReceipt: ParentSubaccountReceipt, val error: String?, val marginDirectionAction: ((direction: MarginDirection) -> Unit) = {}, val percentageAction: (() -> Unit) = {}, @@ -112,11 +112,11 @@ object DydxAdjustMarginInputView : DydxComponent { PercentageOption("50%", 0.5), ), amountText = "500", - isolatedMarginReceipt = IsolatedMarginReceipt( + crossMarginReceipt = CrossMarginReceipt( freeCollateral = DydxReceiptFreeCollateralView.ViewState.preview, marginUsage = DydxReceiptMarginUsageView.ViewState.preview, ), - parentSubaccountReceipt = ParentSubaccountReceipt( + isolatedMarginReceipt = IsolatedMarginReceipt( liquidationPrice = DydxReceiptLiquidationPriceView.ViewState.preview, receipts = DydxReceiptView.ViewState( localizer = MockLocalizer(), @@ -453,10 +453,10 @@ object DydxAdjustMarginInputView : DydxComponent { ) { PlatformAmountChange( modifier = Modifier.weight(1f), - before = if (state.isolatedMarginReceipt.freeCollateral.before != null) { + before = if (state.crossMarginReceipt.freeCollateral.before != null) { { AmountText.Content( - state = state.isolatedMarginReceipt.freeCollateral.before, + state = state.crossMarginReceipt.freeCollateral.before, textStyle = TextStyle.dydxDefault .themeFont( fontType = ThemeFont.FontType.number, @@ -468,10 +468,10 @@ object DydxAdjustMarginInputView : DydxComponent { } else { null }, - after = if (state.isolatedMarginReceipt.freeCollateral.after != null) { + after = if (state.crossMarginReceipt.freeCollateral.after != null) { { AmountText.Content( - state = state.isolatedMarginReceipt.freeCollateral.after, + state = state.crossMarginReceipt.freeCollateral.after, textStyle = TextStyle.dydxDefault .themeFont( fontType = ThemeFont.FontType.number, @@ -484,8 +484,8 @@ object DydxAdjustMarginInputView : DydxComponent { null }, direction = PlatformDirection.from( - state.isolatedMarginReceipt.freeCollateral.before?.amount, - state.isolatedMarginReceipt.freeCollateral.after?.amount, + state.crossMarginReceipt.freeCollateral.before?.amount, + state.crossMarginReceipt.freeCollateral.after?.amount, ), textStyle = TextStyle.dydxDefault .themeFont(fontSize = ThemeFont.FontSize.small) @@ -526,10 +526,10 @@ object DydxAdjustMarginInputView : DydxComponent { ) { PlatformAmountChange( modifier = Modifier.weight(1f), - before = if (state.isolatedMarginReceipt.marginUsage.before != null) { + before = if (state.crossMarginReceipt.marginUsage.before != null) { { MarginUsageView.Content( - state = state.isolatedMarginReceipt.marginUsage.before, + state = state.crossMarginReceipt.marginUsage.before, formatter = state.formatter, textStyle = TextStyle.dydxDefault .themeFont( @@ -543,10 +543,10 @@ object DydxAdjustMarginInputView : DydxComponent { null }, after = - if (state.isolatedMarginReceipt.marginUsage.after != null) { + if (state.crossMarginReceipt.marginUsage.after != null) { { MarginUsageView.Content( - state = state.isolatedMarginReceipt.marginUsage.after, + state = state.crossMarginReceipt.marginUsage.after, formatter = state.formatter, textStyle = TextStyle.dydxDefault .themeFont( @@ -560,8 +560,8 @@ object DydxAdjustMarginInputView : DydxComponent { null }, direction = PlatformDirection.from( - state.isolatedMarginReceipt.marginUsage.after?.percent, - state.isolatedMarginReceipt.marginUsage.before?.percent, + state.crossMarginReceipt.marginUsage.after?.percent, + state.crossMarginReceipt.marginUsage.before?.percent, ), textStyle = TextStyle.dydxDefault .themeFont(fontSize = ThemeFont.FontSize.small) @@ -600,7 +600,7 @@ object DydxAdjustMarginInputView : DydxComponent { DydxReceiptView.Content( modifier = Modifier .offset(y = ThemeShapes.VerticalPadding), - state = state.parentSubaccountReceipt.receipts, + state = state.isolatedMarginReceipt.receipts, ) DydxAdjustMarginCtaButton.Content( Modifier diff --git a/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/margin/DydxAdjustMarginInputViewModel.kt b/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/margin/DydxAdjustMarginInputViewModel.kt index 41abe9a1..89176c9b 100644 --- a/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/margin/DydxAdjustMarginInputViewModel.kt +++ b/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/margin/DydxAdjustMarginInputViewModel.kt @@ -64,19 +64,19 @@ class DydxAdjustMarginInputViewModel @Inject constructor( DydxAdjustMarginInputView.PercentageOption("50%", 0.5), ), amountText = "500", - isolatedMarginReceipt = DydxAdjustMarginInputView.IsolatedMarginReceipt( + crossMarginReceipt = DydxAdjustMarginInputView.CrossMarginReceipt( freeCollateral = DydxReceiptFreeCollateralView.ViewState( localizer = localizer, before = AmountText.ViewState( localizer = localizer, formatter = formatter, - amount = isolatedMargin?.freeCollateral?.current, + amount = subaccount?.freeCollateral?.current, tickSize = 2, ), after = AmountText.ViewState( localizer = localizer, formatter = formatter, - amount = isolatedMargin?.freeCollateral?.postOrder, + amount = subaccount?.freeCollateral?.postOrder, tickSize = 2, ), ), @@ -86,16 +86,16 @@ class DydxAdjustMarginInputViewModel @Inject constructor( before = MarginUsageView.ViewState( localizer = localizer, displayOption = MarginUsageView.DisplayOption.IconAndValue, -// percent = isolatedMargin?.marginUsage?.current ?: 0.5, + percent = subaccount?.marginUsage?.current ?: 0.5, ), after = MarginUsageView.ViewState( localizer = localizer, displayOption = MarginUsageView.DisplayOption.IconAndValue, -// percent = isolatedMargin?.marginUsage?.current ?: 0.5, + percent = subaccount?.marginUsage?.current ?: 0.5, ), ), ), - parentSubaccountReceipt = DydxAdjustMarginInputView.ParentSubaccountReceipt( + isolatedMarginReceipt = DydxAdjustMarginInputView.IsolatedMarginReceipt( liquidationPrice = DydxReceiptLiquidationPriceView.ViewState( localizer = localizer, before = AmountText.ViewState( @@ -116,8 +116,8 @@ class DydxAdjustMarginInputViewModel @Inject constructor( height = 128.dp, padding = 0.dp, lineTypes = listOf( - DydxReceiptView.ReceiptLineType.FreeCollateral, - DydxReceiptView.ReceiptLineType.MarginUsage, + DydxReceiptView.ReceiptLineType.IsolatedPositionMarginUsage, + DydxReceiptView.ReceiptLineType.IsolatedPositionLeverage, ), ), ), From edfc6fdae1e5e52a4ad89e0974aea935094bd36b Mon Sep 17 00:00:00 2001 From: John Huang Date: Mon, 29 Apr 2024 15:01:44 -0700 Subject: [PATCH 17/21] MOB-400 liquidation price --- .../trade/margin/DydxAdjustMarginInputView.kt | 57 ++++++++++++++++++- 1 file changed, 54 insertions(+), 3 deletions(-) diff --git a/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/margin/DydxAdjustMarginInputView.kt b/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/margin/DydxAdjustMarginInputView.kt index 5906e328..9b9d2950 100644 --- a/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/margin/DydxAdjustMarginInputView.kt +++ b/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/margin/DydxAdjustMarginInputView.kt @@ -575,7 +575,60 @@ object DydxAdjustMarginInputView : DydxComponent { modifier: Modifier, state: ViewState, ) { - // TODO, implement this + val shape = RoundedCornerShape(8.dp) + Row( + modifier = modifier + .fillMaxWidth() + .height(80.dp) + .background(color = ThemeColor.SemanticColor.layer_5.color, shape = shape) + .padding(horizontal = ThemeShapes.HorizontalPadding) + .padding(vertical = ThemeShapes.VerticalPadding), + ) { + Column( + modifier = Modifier, + verticalArrangement = Arrangement.spacedBy(2.dp), + ) { + Spacer(modifier = Modifier.weight(1f)) + Text( + text = state.localizer.localize("APP.GENERAL.ESTIMATED"), + style = TextStyle.dydxDefault + .themeColor(ThemeColor.SemanticColor.text_tertiary) + .themeFont(fontSize = ThemeFont.FontSize.small), + ) + Text( + text = state.localizer.localize("APP.TRADE.LIQUIDATION_PRICE"), + style = TextStyle.dydxDefault + .themeColor(ThemeColor.SemanticColor.text_secondary) + .themeFont(fontSize = ThemeFont.FontSize.small), + ) + + Spacer(modifier = Modifier.weight(1f)) + } + + Spacer(modifier = Modifier.weight(1f)) + Column( + modifier = Modifier, + verticalArrangement = Arrangement.spacedBy(2.dp), + ) { + Spacer(modifier = Modifier.weight(1f)) + if (state.isolatedMarginReceipt.liquidationPrice.before != null) { + AmountText.Content( + state = state.isolatedMarginReceipt.liquidationPrice.before, + textStyle = TextStyle.dydxDefault + .themeFont(fontSize = ThemeFont.FontSize.small, fontType = ThemeFont.FontType.number) + .themeColor(ThemeColor.SemanticColor.text_tertiary), + ) + } + AmountText.Content( + state = state.isolatedMarginReceipt.liquidationPrice.after, + textStyle = TextStyle.dydxDefault + .themeFont(fontSize = ThemeFont.FontSize.medium, fontType = ThemeFont.FontType.number) + .themeColor(ThemeColor.SemanticColor.text_primary), + ) + + Spacer(modifier = Modifier.weight(1f)) + } + } } @Composable @@ -591,8 +644,6 @@ object DydxAdjustMarginInputView : DydxComponent { modifier: Modifier, state: ViewState, ) { - // Spacer(modifier = Modifier.weight(1f)) - Column( modifier = modifier .fillMaxWidth(), From 269c87786bac6ecf92ab991577462a6af4048ece Mon Sep 17 00:00:00 2001 From: John Huang Date: Mon, 29 Apr 2024 16:12:34 -0700 Subject: [PATCH 18/21] Fix copy and modified code --- .../DydxReceiptFreeCollateralView.kt | 52 ++++++++++++------- .../DydxReceiptFreeCollateralViewModel.kt | 16 +++--- .../feature/shared/views/AmountText.kt | 2 +- .../trade/margin/DydxAdjustMarginInputView.kt | 26 +++++++--- 4 files changed, 62 insertions(+), 34 deletions(-) diff --git a/v4/feature/receipt/src/main/java/exchange/dydx/trading/feature/receipt/components/freecollateral/DydxReceiptFreeCollateralView.kt b/v4/feature/receipt/src/main/java/exchange/dydx/trading/feature/receipt/components/freecollateral/DydxReceiptFreeCollateralView.kt index 9366aeea..55b5d1d8 100644 --- a/v4/feature/receipt/src/main/java/exchange/dydx/trading/feature/receipt/components/freecollateral/DydxReceiptFreeCollateralView.kt +++ b/v4/feature/receipt/src/main/java/exchange/dydx/trading/feature/receipt/components/freecollateral/DydxReceiptFreeCollateralView.kt @@ -2,16 +2,19 @@ package exchange.dydx.trading.feature.receipt.components.buyingpower import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.size import androidx.compose.material.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.text.TextStyle import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel import exchange.dydx.abacus.protocols.LocalizerProtocol import exchange.dydx.platformui.components.changes.PlatformAmountChange import exchange.dydx.platformui.components.changes.PlatformDirection +import exchange.dydx.platformui.components.changes.PlatformDirectionArrow import exchange.dydx.platformui.designSystem.theme.ThemeColor import exchange.dydx.platformui.designSystem.theme.ThemeFont import exchange.dydx.platformui.designSystem.theme.dydxDefault @@ -27,7 +30,10 @@ import exchange.dydx.trading.feature.shared.views.AmountText @Composable fun Preview_DydxReceiptFreeCollateralView() { DydxThemedPreviewSurface { - DydxReceiptFreeCollateralView.Content(Modifier, DydxReceiptFreeCollateralView.ViewState.preview) + DydxReceiptFreeCollateralView.Content( + Modifier, + DydxReceiptFreeCollateralView.ViewState.preview + ) } } @@ -37,7 +43,7 @@ object DydxReceiptFreeCollateralView : DydxComponent { val before: AmountText.ViewState? = null, val after: AmountText.ViewState? = null, - ) { + ) { companion object { val preview = ViewState( localizer = MockLocalizer(), @@ -74,24 +80,34 @@ object DydxReceiptFreeCollateralView : DydxComponent { PlatformAmountChange( modifier = Modifier.weight(1f), - before = if (state.before != null) { { - AmountText.Content( - state = state.before, - textStyle = TextStyle.dydxDefault - .themeFont(fontType = ThemeFont.FontType.number, fontSize = ThemeFont.FontSize.small) - .themeColor(ThemeColor.SemanticColor.text_tertiary), - ) - } } else { + before = if (state.before != null) { + { + AmountText.Content( + state = state.before, + textStyle = TextStyle.dydxDefault + .themeFont( + fontType = ThemeFont.FontType.number, + fontSize = ThemeFont.FontSize.small + ) + .themeColor(ThemeColor.SemanticColor.text_tertiary), + ) + } + } else { null }, - after = if (state.after != null) { { - AmountText.Content( - state = state.after, - textStyle = TextStyle.dydxDefault - .themeFont(fontType = ThemeFont.FontType.number, fontSize = ThemeFont.FontSize.small) - .themeColor(ThemeColor.SemanticColor.text_primary), - ) - } } else { + after = if (state.after != null) { + { + AmountText.Content( + state = state.after, + textStyle = TextStyle.dydxDefault + .themeFont( + fontType = ThemeFont.FontType.number, + fontSize = ThemeFont.FontSize.small + ) + .themeColor(ThemeColor.SemanticColor.text_primary), + ) + } + } else { null }, direction = PlatformDirection.from(state.before?.amount, state.after?.amount), diff --git a/v4/feature/receipt/src/main/java/exchange/dydx/trading/feature/receipt/components/freecollateral/DydxReceiptFreeCollateralViewModel.kt b/v4/feature/receipt/src/main/java/exchange/dydx/trading/feature/receipt/components/freecollateral/DydxReceiptFreeCollateralViewModel.kt index 0052a0cb..4898face 100644 --- a/v4/feature/receipt/src/main/java/exchange/dydx/trading/feature/receipt/components/freecollateral/DydxReceiptFreeCollateralViewModel.kt +++ b/v4/feature/receipt/src/main/java/exchange/dydx/trading/feature/receipt/components/freecollateral/DydxReceiptFreeCollateralViewModel.kt @@ -62,22 +62,22 @@ class DydxReceiptFreeCollateralViewModel @Inject constructor( val position = positions?.firstOrNull { it.id == marketId } return DydxReceiptFreeCollateralView.ViewState( localizer = localizer, - before = if (position?.buyingPower?.current != null) { + before = if (position?.freeCollateral?.current != null) { AmountText.ViewState( localizer = localizer, formatter = formatter, - amount = position?.buyingPower?.current, + amount = position.freeCollateral?.current, tickSize = 0, requiresPositive = true, ) } else { null }, - after = if (position?.buyingPower?.postOrder != null) { + after = if (position?.freeCollateral?.postOrder != null) { AmountText.ViewState( localizer = localizer, formatter = formatter, - amount = position?.buyingPower?.postOrder, + amount = position.freeCollateral?.postOrder, tickSize = 0, requiresPositive = true, ) @@ -93,22 +93,22 @@ class DydxReceiptFreeCollateralViewModel @Inject constructor( ): DydxReceiptFreeCollateralView.ViewState { return DydxReceiptFreeCollateralView.ViewState( localizer = localizer, - before = if (subaccount?.buyingPower?.current != null) { + before = if (subaccount?.freeCollateral?.current != null) { AmountText.ViewState( localizer = localizer, formatter = formatter, - amount = subaccount?.buyingPower?.current, + amount = subaccount.freeCollateral?.current, tickSize = 0, requiresPositive = true, ) } else { null }, - after = if (subaccount?.buyingPower?.postOrder != null) { + after = if (subaccount?.freeCollateral?.postOrder != null) { AmountText.ViewState( localizer = localizer, formatter = formatter, - amount = subaccount?.buyingPower?.postOrder, + amount = subaccount.freeCollateral?.postOrder, tickSize = 0, requiresPositive = true, ) diff --git a/v4/feature/shared/src/main/java/exchange/dydx/trading/feature/shared/views/AmountText.kt b/v4/feature/shared/src/main/java/exchange/dydx/trading/feature/shared/views/AmountText.kt index 73a40de8..6aff8916 100644 --- a/v4/feature/shared/src/main/java/exchange/dydx/trading/feature/shared/views/AmountText.kt +++ b/v4/feature/shared/src/main/java/exchange/dydx/trading/feature/shared/views/AmountText.kt @@ -58,7 +58,7 @@ object AmountText { Text( modifier = modifier, - text = state.formatter.dollar(amount, state.tickSize ?: 2) ?: "0", + text = state.formatter.dollar(amount, state.tickSize ?: 2) ?: "", style = textStyle, ) } diff --git a/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/margin/DydxAdjustMarginInputView.kt b/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/margin/DydxAdjustMarginInputView.kt index 9b9d2950..0bcd3aef 100644 --- a/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/margin/DydxAdjustMarginInputView.kt +++ b/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/margin/DydxAdjustMarginInputView.kt @@ -11,6 +11,7 @@ import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.offset import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.text.KeyboardOptions @@ -29,6 +30,7 @@ import exchange.dydx.platformui.components.PlatformInfoScaffold import exchange.dydx.platformui.components.buttons.PlatformPillItem import exchange.dydx.platformui.components.changes.PlatformAmountChange import exchange.dydx.platformui.components.changes.PlatformDirection +import exchange.dydx.platformui.components.changes.PlatformDirectionArrow import exchange.dydx.platformui.components.dividers.PlatformDivider import exchange.dydx.platformui.components.inputs.PlatformTextInput import exchange.dydx.platformui.components.tabgroups.PlatformTabGroup @@ -609,6 +611,7 @@ object DydxAdjustMarginInputView : DydxComponent { Column( modifier = Modifier, verticalArrangement = Arrangement.spacedBy(2.dp), + horizontalAlignment = Alignment.End, ) { Spacer(modifier = Modifier.weight(1f)) if (state.isolatedMarginReceipt.liquidationPrice.before != null) { @@ -619,13 +622,22 @@ object DydxAdjustMarginInputView : DydxComponent { .themeColor(ThemeColor.SemanticColor.text_tertiary), ) } - AmountText.Content( - state = state.isolatedMarginReceipt.liquidationPrice.after, - textStyle = TextStyle.dydxDefault - .themeFont(fontSize = ThemeFont.FontSize.medium, fontType = ThemeFont.FontType.number) - .themeColor(ThemeColor.SemanticColor.text_primary), - ) - + Row( + modifier = Modifier, + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(4.dp), + ) { + PlatformDirectionArrow( + direction = PlatformDirection.None, + modifier = Modifier.size(12.dp) + ) + AmountText.Content( + state = state.isolatedMarginReceipt.liquidationPrice.after, + textStyle = TextStyle.dydxDefault + .themeFont(fontSize = ThemeFont.FontSize.medium, fontType = ThemeFont.FontType.number) + .themeColor(ThemeColor.SemanticColor.text_primary), + ) + } Spacer(modifier = Modifier.weight(1f)) } } From 568b11696eee28e84d03409704581af96ebfc285 Mon Sep 17 00:00:00 2001 From: John Huang Date: Thu, 9 May 2024 10:13:28 -0700 Subject: [PATCH 19/21] PR review --- .../dydx/trading/feature/shared/views/LeverageView.kt | 7 ++++--- .../margin/components/DydxAdjustMarginCtaButtonModel.kt | 1 - 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/v4/feature/shared/src/main/java/exchange/dydx/trading/feature/shared/views/LeverageView.kt b/v4/feature/shared/src/main/java/exchange/dydx/trading/feature/shared/views/LeverageView.kt index 6262eb68..8893aec9 100644 --- a/v4/feature/shared/src/main/java/exchange/dydx/trading/feature/shared/views/LeverageView.kt +++ b/v4/feature/shared/src/main/java/exchange/dydx/trading/feature/shared/views/LeverageView.kt @@ -22,7 +22,7 @@ import exchange.dydx.trading.common.theme.MockLocalizer @Composable fun Preview_LeverageView() { DydxThemedPreviewSurface { - LeverageView.Content(Modifier, LeverageView.ViewState.preview, DydxFormatter()) + LeverageView.Content(Modifier, LeverageView.ViewState.preview) } } @@ -34,12 +34,14 @@ object LeverageView { data class ViewState( val localizer: LocalizerProtocol, + val formatter: DydxFormatter, val leverage: Double = 3.0, val margin: Double? = null, ) { companion object { val preview = ViewState( localizer = MockLocalizer(), + formatter = DydxFormatter(), ) } } @@ -48,12 +50,11 @@ object LeverageView { fun Content( modifier: Modifier = Modifier, state: ViewState?, - formatter: DydxFormatter, textStyle: TextStyle = TextStyle.dydxDefault, ) { if (state == null) return - val leverageText = formatter.leverage(state.leverage) + val leverageText = state.formatter.leverage(state.leverage) val leverageIcon = if (state.margin != null) { LeverageRiskView.ViewState( localizer = state.localizer, diff --git a/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/margin/components/DydxAdjustMarginCtaButtonModel.kt b/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/margin/components/DydxAdjustMarginCtaButtonModel.kt index 441d4564..f9d970f0 100644 --- a/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/margin/components/DydxAdjustMarginCtaButtonModel.kt +++ b/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/margin/components/DydxAdjustMarginCtaButtonModel.kt @@ -23,7 +23,6 @@ class DydxAdjustMarginCtaButtonModel @Inject constructor( private val theme: DydxTheme, private val localizer: LocalizerProtocol, private val abacusStateManager: AbacusStateManagerProtocol, - private val formatter: DydxFormatter, ) : ViewModel(), DydxViewModel { val state: Flow = combine( From 0b4cd4da9b7a8bc685ef969123ac7dda8c89b4e6 Mon Sep 17 00:00:00 2001 From: John Huang Date: Thu, 9 May 2024 11:30:54 -0700 Subject: [PATCH 20/21] spotless --- .../freecollateral/DydxReceiptFreeCollateralView.kt | 11 ++++------- .../feature/trade/margin/DydxAdjustMarginInputView.kt | 2 +- .../components/DydxAdjustMarginCtaButtonModel.kt | 1 - 3 files changed, 5 insertions(+), 9 deletions(-) diff --git a/v4/feature/receipt/src/main/java/exchange/dydx/trading/feature/receipt/components/freecollateral/DydxReceiptFreeCollateralView.kt b/v4/feature/receipt/src/main/java/exchange/dydx/trading/feature/receipt/components/freecollateral/DydxReceiptFreeCollateralView.kt index 55b5d1d8..3a13d22b 100644 --- a/v4/feature/receipt/src/main/java/exchange/dydx/trading/feature/receipt/components/freecollateral/DydxReceiptFreeCollateralView.kt +++ b/v4/feature/receipt/src/main/java/exchange/dydx/trading/feature/receipt/components/freecollateral/DydxReceiptFreeCollateralView.kt @@ -2,19 +2,16 @@ package exchange.dydx.trading.feature.receipt.components.buyingpower import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.size import androidx.compose.material.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.text.TextStyle import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel import exchange.dydx.abacus.protocols.LocalizerProtocol import exchange.dydx.platformui.components.changes.PlatformAmountChange import exchange.dydx.platformui.components.changes.PlatformDirection -import exchange.dydx.platformui.components.changes.PlatformDirectionArrow import exchange.dydx.platformui.designSystem.theme.ThemeColor import exchange.dydx.platformui.designSystem.theme.ThemeFont import exchange.dydx.platformui.designSystem.theme.dydxDefault @@ -32,7 +29,7 @@ fun Preview_DydxReceiptFreeCollateralView() { DydxThemedPreviewSurface { DydxReceiptFreeCollateralView.Content( Modifier, - DydxReceiptFreeCollateralView.ViewState.preview + DydxReceiptFreeCollateralView.ViewState.preview, ) } } @@ -43,7 +40,7 @@ object DydxReceiptFreeCollateralView : DydxComponent { val before: AmountText.ViewState? = null, val after: AmountText.ViewState? = null, - ) { + ) { companion object { val preview = ViewState( localizer = MockLocalizer(), @@ -87,7 +84,7 @@ object DydxReceiptFreeCollateralView : DydxComponent { textStyle = TextStyle.dydxDefault .themeFont( fontType = ThemeFont.FontType.number, - fontSize = ThemeFont.FontSize.small + fontSize = ThemeFont.FontSize.small, ) .themeColor(ThemeColor.SemanticColor.text_tertiary), ) @@ -102,7 +99,7 @@ object DydxReceiptFreeCollateralView : DydxComponent { textStyle = TextStyle.dydxDefault .themeFont( fontType = ThemeFont.FontType.number, - fontSize = ThemeFont.FontSize.small + fontSize = ThemeFont.FontSize.small, ) .themeColor(ThemeColor.SemanticColor.text_primary), ) diff --git a/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/margin/DydxAdjustMarginInputView.kt b/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/margin/DydxAdjustMarginInputView.kt index 8e6944b6..56caf9d7 100644 --- a/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/margin/DydxAdjustMarginInputView.kt +++ b/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/margin/DydxAdjustMarginInputView.kt @@ -626,7 +626,7 @@ object DydxAdjustMarginInputView : DydxComponent { ) { PlatformDirectionArrow( direction = PlatformDirection.None, - modifier = Modifier.size(12.dp) + modifier = Modifier.size(12.dp), ) AmountText.Content( state = state.isolatedMarginReceipt.liquidationPrice.after, diff --git a/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/margin/components/DydxAdjustMarginCtaButtonModel.kt b/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/margin/components/DydxAdjustMarginCtaButtonModel.kt index f9d970f0..eda45451 100644 --- a/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/margin/components/DydxAdjustMarginCtaButtonModel.kt +++ b/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/margin/components/DydxAdjustMarginCtaButtonModel.kt @@ -9,7 +9,6 @@ import exchange.dydx.abacus.protocols.LocalizerProtocol import exchange.dydx.dydxstatemanager.AbacusStateManagerProtocol import exchange.dydx.trading.common.AppConfig import exchange.dydx.trading.common.DydxViewModel -import exchange.dydx.trading.common.formatter.DydxFormatter import exchange.dydx.trading.common.theme.DydxTheme import exchange.dydx.trading.feature.shared.views.InputCtaButton import kotlinx.coroutines.flow.Flow From a4ee741e16b3559aaa806f1df2d4152eae5728d3 Mon Sep 17 00:00:00 2001 From: John Huang Date: Thu, 9 May 2024 11:43:27 -0700 Subject: [PATCH 21/21] fix build --- .../isolatedmargin/DydxReceiptIsolatedPositionLeverageView.kt | 2 -- .../DydxReceiptIsolatedPositionLeverageViewModel.kt | 2 ++ 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/v4/feature/receipt/src/main/java/exchange/dydx/trading/feature/receipt/components/isolatedmargin/DydxReceiptIsolatedPositionLeverageView.kt b/v4/feature/receipt/src/main/java/exchange/dydx/trading/feature/receipt/components/isolatedmargin/DydxReceiptIsolatedPositionLeverageView.kt index b9ebd55c..7a3ee865 100644 --- a/v4/feature/receipt/src/main/java/exchange/dydx/trading/feature/receipt/components/isolatedmargin/DydxReceiptIsolatedPositionLeverageView.kt +++ b/v4/feature/receipt/src/main/java/exchange/dydx/trading/feature/receipt/components/isolatedmargin/DydxReceiptIsolatedPositionLeverageView.kt @@ -79,7 +79,6 @@ object DydxReceiptIsolatedPositionLeverageView : DydxComponent { before = if (state.before != null) { { LeverageView.Content( state = state.before, - formatter = state.formatter, textStyle = TextStyle.dydxDefault .themeFont(fontSize = ThemeFont.FontSize.small, fontType = ThemeFont.FontType.number) .themeColor(ThemeColor.SemanticColor.text_tertiary), @@ -91,7 +90,6 @@ object DydxReceiptIsolatedPositionLeverageView : DydxComponent { if (state.after != null) { { LeverageView.Content( state = state.after, - formatter = state.formatter, textStyle = TextStyle.dydxDefault .themeFont(fontSize = ThemeFont.FontSize.small, fontType = ThemeFont.FontType.number) .themeColor(ThemeColor.SemanticColor.text_primary), diff --git a/v4/feature/receipt/src/main/java/exchange/dydx/trading/feature/receipt/components/isolatedmargin/DydxReceiptIsolatedPositionLeverageViewModel.kt b/v4/feature/receipt/src/main/java/exchange/dydx/trading/feature/receipt/components/isolatedmargin/DydxReceiptIsolatedPositionLeverageViewModel.kt index 81d6ca17..c892e35c 100644 --- a/v4/feature/receipt/src/main/java/exchange/dydx/trading/feature/receipt/components/isolatedmargin/DydxReceiptIsolatedPositionLeverageViewModel.kt +++ b/v4/feature/receipt/src/main/java/exchange/dydx/trading/feature/receipt/components/isolatedmargin/DydxReceiptIsolatedPositionLeverageViewModel.kt @@ -43,6 +43,7 @@ class DydxReceiptIsolatedPositionLeverageViewModel @Inject constructor( before = if (leverage?.current != null) { LeverageView.ViewState( localizer = localizer, + formatter = formatter, leverage = leverage.current ?: 0.0, margin = margin?.current, ) @@ -52,6 +53,7 @@ class DydxReceiptIsolatedPositionLeverageViewModel @Inject constructor( after = if (leverage?.postOrder != null) { LeverageView.ViewState( localizer = localizer, + formatter = formatter, leverage = leverage.postOrder ?: 0.0, margin = margin?.postOrder, )