diff --git a/v4/build.gradle b/v4/build.gradle index 5ffcf2a6..d15cf62c 100644 --- a/v4/build.gradle +++ b/v4/build.gradle @@ -89,7 +89,7 @@ ext { compileSdkVersion = 34 // App dependencies - abacusVersion = '1.6.26' + abacusVersion = '1.6.30' carteraVersion = '0.1.13' kollectionsVersion = '2.0.16' diff --git a/v4/common/src/main/java/exchange/dydx/trading/common/navigation/DydxAnimation.kt b/v4/common/src/main/java/exchange/dydx/trading/common/navigation/DydxAnimation.kt index 2b277a15..76a68753 100644 --- a/v4/common/src/main/java/exchange/dydx/trading/common/navigation/DydxAnimation.kt +++ b/v4/common/src/main/java/exchange/dydx/trading/common/navigation/DydxAnimation.kt @@ -8,8 +8,10 @@ import androidx.compose.animation.ExitTransition import androidx.compose.animation.core.Easing import androidx.compose.animation.core.FastOutSlowInEasing import androidx.compose.animation.core.tween +import androidx.compose.animation.expandVertically import androidx.compose.animation.fadeIn import androidx.compose.animation.fadeOut +import androidx.compose.animation.shrinkVertically import androidx.compose.runtime.Composable import androidx.navigation.NavBackStackEntry import kotlinx.serialization.json.JsonNull.content @@ -144,4 +146,17 @@ object DydxAnimation { content = content, ) } + + @Composable + fun AnimateExpandInOut( + visible: Boolean, + content: @Composable AnimatedVisibilityScope.() -> Unit + ) { + AnimatedVisibility( + visible = visible, + enter = expandVertically(), + exit = shrinkVertically(), + content = content, + ) + } } diff --git a/v4/feature/market/src/main/java/exchange/dydx/trading/feature/market/marketinfo/components/paging/DydxMarketInfoPagingView.kt b/v4/feature/market/src/main/java/exchange/dydx/trading/feature/market/marketinfo/components/paging/DydxMarketInfoPagingView.kt index ef882613..f3436bb5 100644 --- a/v4/feature/market/src/main/java/exchange/dydx/trading/feature/market/marketinfo/components/paging/DydxMarketInfoPagingView.kt +++ b/v4/feature/market/src/main/java/exchange/dydx/trading/feature/market/marketinfo/components/paging/DydxMarketInfoPagingView.kt @@ -1,9 +1,5 @@ package exchange.dydx.trading.feature.market.marketinfo.components.paging -import androidx.compose.animation.AnimatedVisibility -import androidx.compose.animation.AnimatedVisibilityScope -import androidx.compose.animation.fadeIn -import androidx.compose.animation.fadeOut import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.tooling.preview.Preview @@ -88,17 +84,4 @@ object DydxMarketInfoPagingView : DydxComponent { DydxMarketFundingRateView.Content(modifier) } } - - @Composable - private fun AnimateFadeInOut( - visible: Boolean, - content: @Composable AnimatedVisibilityScope.() -> Unit - ) { - AnimatedVisibility( - visible = visible, - enter = fadeIn(), - exit = fadeOut(), - content = content, - ) - } } diff --git a/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/trigger/DydxTriggerOrderInputView.kt b/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/trigger/DydxTriggerOrderInputView.kt index 0829d02e..fc36cfa1 100644 --- a/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/trigger/DydxTriggerOrderInputView.kt +++ b/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/trigger/DydxTriggerOrderInputView.kt @@ -19,6 +19,7 @@ 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.dividers.PlatformDivider import exchange.dydx.platformui.designSystem.theme.ThemeColor import exchange.dydx.platformui.designSystem.theme.ThemeFont import exchange.dydx.platformui.designSystem.theme.ThemeShapes @@ -29,9 +30,16 @@ 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.HeaderView import exchange.dydx.trading.feature.shared.views.HeaderViewCloseBotton import exchange.dydx.trading.feature.trade.trigger.components.DydxTriggerOrderCtaButtonView import exchange.dydx.trading.feature.trade.trigger.components.DydxTriggerOrderReceiptView +import exchange.dydx.trading.feature.trade.trigger.components.inputfields.DydxTriggerOrderInputType +import exchange.dydx.trading.feature.trade.trigger.components.inputfields.DydxTriggerOrderPriceInputType +import exchange.dydx.trading.feature.trade.trigger.components.inputfields.gainloss.DydxTriggerOrderGainLossView +import exchange.dydx.trading.feature.trade.trigger.components.inputfields.limitprice.DydxTriggerOrderLimitPriceSectionView +import exchange.dydx.trading.feature.trade.trigger.components.inputfields.price.DydxTriggerOrderPriceView +import exchange.dydx.trading.feature.trade.trigger.components.inputfields.size.DydxTriggerOrderSizeView @Preview @Composable @@ -78,17 +86,45 @@ object DydxTriggerOrderInputView : DydxComponent { focusManager.clearFocus() }) }, + verticalArrangement = Arrangement.spacedBy(12.dp), ) { HeaderView(modifier = Modifier, state = state) DydxTriggerOrderReceiptView.Content( - modifier = Modifier.padding(top = 16.dp), + modifier = Modifier, + ) + + TakeProfitSectionView( + modifier = Modifier, + state = state, + ) + + StopLossSectionView( + modifier = Modifier, + state = state, + ) + + AdvacedDividerView( + modifier = Modifier, + state = state, + ) + + DydxTriggerOrderSizeView.Content( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = ThemeShapes.HorizontalPadding), + ) + + DydxTriggerOrderLimitPriceSectionView.Content( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = ThemeShapes.HorizontalPadding), ) Spacer(modifier = Modifier.weight(1f)) DydxTriggerOrderCtaButtonView.Content( - Modifier + modifier = Modifier .fillMaxWidth() .padding(horizontal = ThemeShapes.HorizontalPadding) .padding(bottom = ThemeShapes.VerticalPadding * 2), @@ -131,4 +167,91 @@ object DydxTriggerOrderInputView : DydxComponent { ) } } + + @Composable + private fun TakeProfitSectionView(modifier: Modifier, state: ViewState) { + Column( + modifier = modifier + .fillMaxWidth() + .padding(horizontal = ThemeShapes.HorizontalPadding), + ) { + Text( + text = state.localizer.localize("TRADE.BRACKET_ORDER_TP.TITLE"), + style = TextStyle.dydxDefault + .themeFont(fontSize = ThemeFont.FontSize.base) + .themeColor(ThemeColor.SemanticColor.text_secondary), + ) + + Row( + modifier = modifier + .fillMaxWidth() + .padding(top = 8.dp), + horizontalArrangement = Arrangement.spacedBy(12.dp), + ) { + DydxTriggerOrderPriceView.Content( + modifier = Modifier.weight(1f), + inputType = DydxTriggerOrderPriceInputType.TakeProfit, + ) + + DydxTriggerOrderGainLossView.Content( + modifier = Modifier.weight(1f), + inputType = DydxTriggerOrderInputType.TakeProfit, + ) + } + } + } + + @Composable + private fun StopLossSectionView(modifier: Modifier, state: ViewState) { + Column( + modifier = modifier + .fillMaxWidth() + .padding(horizontal = ThemeShapes.HorizontalPadding), + ) { + Text( + text = state.localizer.localize("TRADE.BRACKET_ORDER_SL.TITLE"), + style = TextStyle.dydxDefault + .themeFont(fontSize = ThemeFont.FontSize.base) + .themeColor(ThemeColor.SemanticColor.text_secondary), + ) + + Row( + modifier = modifier + .fillMaxWidth() + .padding(top = 8.dp), + horizontalArrangement = Arrangement.spacedBy(12.dp), + ) { + DydxTriggerOrderPriceView.Content( + modifier = Modifier.weight(1f), + inputType = DydxTriggerOrderPriceInputType.StopLoss, + ) + + DydxTriggerOrderGainLossView.Content( + modifier = Modifier.weight(1f), + inputType = DydxTriggerOrderInputType.StopLoss, + ) + } + } + } + + @Composable + private fun AdvacedDividerView(modifier: Modifier, state: ViewState) { + Row( + modifier = modifier + .fillMaxWidth() + .padding(horizontal = ThemeShapes.HorizontalPadding), + horizontalArrangement = Arrangement.spacedBy(12.dp), + verticalAlignment = Alignment.CenterVertically, + ) { + Text( + text = state.localizer.localize("APP.GENERAL.ADVANCED"), + style = TextStyle.dydxDefault + .themeFont(fontSize = ThemeFont.FontSize.small) + .themeColor(ThemeColor.SemanticColor.text_tertiary), + ) + PlatformDivider( + modifier = Modifier.weight(1f), + ) + } + } } diff --git a/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/trigger/DydxTriggerOrderInputViewModel.kt b/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/trigger/DydxTriggerOrderInputViewModel.kt index 3d0baa02..b304d825 100644 --- a/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/trigger/DydxTriggerOrderInputViewModel.kt +++ b/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/trigger/DydxTriggerOrderInputViewModel.kt @@ -4,6 +4,7 @@ import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.ViewModel import dagger.hilt.android.lifecycle.HiltViewModel import exchange.dydx.abacus.protocols.LocalizerProtocol +import exchange.dydx.abacus.state.model.TriggerOrdersInputField import exchange.dydx.dydxstatemanager.AbacusStateManagerProtocol import exchange.dydx.trading.common.DydxViewModel import exchange.dydx.trading.common.formatter.DydxFormatter @@ -30,6 +31,7 @@ class DydxTriggerOrderInputViewModel @Inject constructor( router.navigateBack() } else { abacusStateManager.setMarket(marketId = marketId) + abacusStateManager.triggerOrders(input = marketId, type = TriggerOrdersInputField.marketId) } } diff --git a/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/trigger/components/inputfields/DydxTriggerOrderInputType.kt b/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/trigger/components/inputfields/DydxTriggerOrderInputType.kt new file mode 100644 index 00000000..6c001c8a --- /dev/null +++ b/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/trigger/components/inputfields/DydxTriggerOrderInputType.kt @@ -0,0 +1,12 @@ +package exchange.dydx.trading.feature.trade.trigger.components.inputfields + +enum class DydxTriggerOrderInputType { + TakeProfit, + StopLoss, +} +enum class DydxTriggerOrderPriceInputType { + TakeProfit, + StopLoss, + TakeProfitLimit, + StopLossLimit, +} diff --git a/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/trigger/components/inputfields/gainloss/DydxTriggerOrderGainLossView.kt b/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/trigger/components/inputfields/gainloss/DydxTriggerOrderGainLossView.kt new file mode 100644 index 00000000..fde06b0c --- /dev/null +++ b/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/trigger/components/inputfields/gainloss/DydxTriggerOrderGainLossView.kt @@ -0,0 +1,133 @@ +package exchange.dydx.trading.feature.trade.trigger.components.inputfields.gainloss + +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.padding +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.MutableState +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +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.buttons.PlatformAccessoryButton +import exchange.dydx.platformui.components.menus.PlatformDropdownMenu +import exchange.dydx.platformui.components.menus.PlatformMenuItem +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.LabeledSelectionInput +import exchange.dydx.trading.feature.shared.views.LabeledTextInput +import exchange.dydx.trading.feature.trade.trigger.components.inputfields.DydxTriggerOrderInputType + +@Preview +@Composable +fun Preview_DydxTriggerOrderGainLossView() { + DydxThemedPreviewSurface { + DydxTriggerOrderGainLossView.Content( + Modifier, + DydxTriggerOrderGainLossView.ViewState.preview, + ) + } +} + +object DydxTriggerOrderGainLossView : DydxComponent { + data class ViewState( + val localizer: LocalizerProtocol, + val labeledTextInput: LabeledTextInput.ViewState? = null, + val labeledSelectionInput: LabeledSelectionInput.ViewState? = null, + ) { + companion object { + val preview = ViewState( + localizer = MockLocalizer(), + labeledTextInput = LabeledTextInput.ViewState.preview, + labeledSelectionInput = LabeledSelectionInput.ViewState.preview, + ) + } + } + + @Composable + override fun Content(modifier: Modifier) { + Content(modifier, DydxTriggerOrderInputType.TakeProfit) + } + + @Composable + fun Content(modifier: Modifier, inputType: DydxTriggerOrderInputType) { + when (inputType) { + DydxTriggerOrderInputType.TakeProfit -> { + val viewModel: DydxTriggerOrderGainLossTakeProfitViewModel = hiltViewModel() + val state = viewModel.state.collectAsStateWithLifecycle(initialValue = null).value + Content(modifier, state) + } + DydxTriggerOrderInputType.StopLoss -> { + val viewModel: DydxTriggerOrderGainLossStopLossViewModel = hiltViewModel() + val state = viewModel.state.collectAsStateWithLifecycle(initialValue = null).value + Content(modifier, state) + } + } + } + + @Composable + fun Content(modifier: Modifier, state: ViewState?) { + if (state == null) { + return + } + + val expanded: MutableState = remember { + mutableStateOf(false) + } + + InputFieldScarfold(modifier) { + Row( + verticalAlignment = Alignment.CenterVertically, + ) { + LabeledTextInput.Content( + modifier = Modifier.weight(1f), + state = state.labeledTextInput, + ) + + Box { + PlatformAccessoryButton( + modifier = Modifier, + action = { expanded.value = !expanded.value }, + ) { + Text( + modifier = Modifier.padding(ThemeShapes.InputPaddingValues), + text = state.labeledSelectionInput?.options?.getOrNull( + state.labeledSelectionInput?.selectedIndex ?: 0, + ) ?: "", + style = TextStyle.dydxDefault + .themeFont(fontSize = ThemeFont.FontSize.small) + .themeColor(ThemeColor.SemanticColor.text_primary), + ) + } + + PlatformDropdownMenu( + expanded = expanded, + items = state.labeledSelectionInput?.options?.mapIndexed { index, option -> + PlatformMenuItem( + text = option, + onClick = { + expanded.value = false + state.labeledSelectionInput?.onSelectionChanged?.invoke(index) + }, + ) + } ?: listOf(), + ) + } + } + } + } +} diff --git a/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/trigger/components/inputfields/gainloss/DydxTriggerOrderGainLossViewModel.kt b/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/trigger/components/inputfields/gainloss/DydxTriggerOrderGainLossViewModel.kt new file mode 100644 index 00000000..bd9820d8 --- /dev/null +++ b/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/trigger/components/inputfields/gainloss/DydxTriggerOrderGainLossViewModel.kt @@ -0,0 +1,92 @@ +package exchange.dydx.trading.feature.trade.trigger.components.inputfields.gainloss + +import androidx.lifecycle.ViewModel +import dagger.hilt.android.lifecycle.HiltViewModel +import exchange.dydx.abacus.output.input.TriggerOrdersInput +import exchange.dydx.abacus.protocols.LocalizerProtocol +import exchange.dydx.abacus.state.model.TradeInputField +import exchange.dydx.dydxstatemanager.AbacusStateManagerProtocol +import exchange.dydx.dydxstatemanager.MarketConfigsAndAsset +import exchange.dydx.trading.common.DydxViewModel +import exchange.dydx.trading.common.formatter.DydxFormatter +import exchange.dydx.trading.feature.shared.views.LabeledSelectionInput +import exchange.dydx.trading.feature.shared.views.LabeledTextInput +import exchange.dydx.trading.feature.trade.trigger.components.inputfields.DydxTriggerOrderInputType +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.distinctUntilChanged +import javax.inject.Inject + +@HiltViewModel +class DydxTriggerOrderGainLossTakeProfitViewModel @Inject constructor( + private val localizer: LocalizerProtocol, + private val abacusStateManager: AbacusStateManagerProtocol, + private val formatter: DydxFormatter, +) : DydxTriggerOrderGainLossViewModel( + localizer, + abacusStateManager, + formatter, + DydxTriggerOrderInputType.TakeProfit, +) + +@HiltViewModel +class DydxTriggerOrderGainLossStopLossViewModel @Inject constructor( + private val localizer: LocalizerProtocol, + private val abacusStateManager: AbacusStateManagerProtocol, + private val formatter: DydxFormatter, +) : DydxTriggerOrderGainLossViewModel( + localizer, + abacusStateManager, + formatter, + DydxTriggerOrderInputType.StopLoss, +) + +open class DydxTriggerOrderGainLossViewModel( + private val localizer: LocalizerProtocol, + private val abacusStateManager: AbacusStateManagerProtocol, + private val formatter: DydxFormatter, + val inputType: DydxTriggerOrderInputType, +) : ViewModel(), DydxViewModel { + + val state: Flow = + combine( + abacusStateManager.state.triggerOrdersInput, + abacusStateManager.state.configsAndAssetMap, + ) { triggerOrdersInput, configsAndAssetMap -> + val marketId = triggerOrdersInput?.marketId ?: return@combine null + createViewState(triggerOrdersInput, configsAndAssetMap?.get(marketId)) + } + .distinctUntilChanged() + + private fun createViewState( + triggerOrdersInput: TriggerOrdersInput?, + configsAndAsset: MarketConfigsAndAsset?, + ): DydxTriggerOrderGainLossView.ViewState { + val label = when (inputType) { + DydxTriggerOrderInputType.TakeProfit -> localizer.localize("APP.GENERAL.GAIN") + DydxTriggerOrderInputType.StopLoss -> localizer.localize("APP.GENERAL.LOSS") + } + return DydxTriggerOrderGainLossView.ViewState( + localizer = localizer, + labeledTextInput = LabeledTextInput.ViewState( + localizer = localizer, + label = label, + value = null, + onValueChanged = { value -> + abacusStateManager.trade(value, TradeInputField.goodTilDuration) + }, + ), + labeledSelectionInput = LabeledSelectionInput.ViewState( + localizer = localizer, + options = listOf("%", "$"), + selectedIndex = 0, + onSelectionChanged = { index -> +// val type = tradeInput?.options?.goodTilUnitOptions?.getOrNull(index)?.type +// if (type != null) { +// abacusStateManager.trade(type, TradeInputField.goodTilUnit) +// } + }, + ), + ) + } +} diff --git a/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/trigger/components/inputfields/limitprice/DydxTriggerOrderLimitPriceSectionView.kt b/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/trigger/components/inputfields/limitprice/DydxTriggerOrderLimitPriceSectionView.kt new file mode 100644 index 00000000..a564886f --- /dev/null +++ b/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/trigger/components/inputfields/limitprice/DydxTriggerOrderLimitPriceSectionView.kt @@ -0,0 +1,106 @@ +package exchange.dydx.trading.feature.trade.trigger.components.inputfields.limitprice + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.font.FontVariation.weight +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.inputs.PlatformSwitchInput +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.navigation.DydxAnimation +import exchange.dydx.trading.common.theme.DydxThemedPreviewSurface +import exchange.dydx.trading.common.theme.MockLocalizer +import exchange.dydx.trading.feature.trade.trigger.components.inputfields.DydxTriggerOrderPriceInputType +import exchange.dydx.trading.feature.trade.trigger.components.inputfields.price.DydxTriggerOrderPriceView + +@Preview +@Composable +fun Preview_DydxTriggerOrderLimitPriceSectionView() { + DydxThemedPreviewSurface { + DydxTriggerOrderLimitPriceSectionView.Content( + Modifier, + DydxTriggerOrderLimitPriceSectionView.ViewState.preview, + ) + } +} + +object DydxTriggerOrderLimitPriceSectionView : DydxComponent { + data class ViewState( + val localizer: LocalizerProtocol, + val enabled: Boolean = true, + val onEnabledChanged: (Boolean) -> Unit = {}, + ) { + companion object { + val preview = ViewState( + localizer = MockLocalizer(), + ) + } + } + + @Composable + override fun Content(modifier: Modifier) { + val viewModel: DydxTriggerOrderLimitPriceSectionViewModel = hiltViewModel() + + val state = viewModel.state.collectAsStateWithLifecycle(initialValue = null).value + Content(modifier, state) + } + + @Composable + fun Content(modifier: Modifier, state: ViewState?) { + if (state == null) { + return + } + + Column( + modifier = modifier, + verticalArrangement = Arrangement.spacedBy(12.dp), + ) { + PlatformSwitchInput( + modifier = Modifier.fillMaxWidth(), + label = state.localizer.localize("APP.TRADE.LIMIT_PRICE"), + textStyle = TextStyle.dydxDefault + .themeFont(fontSize = ThemeFont.FontSize.base) + .themeColor(ThemeColor.SemanticColor.text_secondary), + value = state.enabled, + onValueChange = state.onEnabledChanged, + ) + + DydxAnimation.AnimateExpandInOut( + visible = state.enabled, + ) { + LimitPricesContent(Modifier) + } + } + } + + @Composable + private fun LimitPricesContent(modifier: Modifier) { + Row( + modifier = modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.spacedBy(12.dp), + ) { + DydxTriggerOrderPriceView.Content( + modifier = Modifier.fillMaxWidth().weight(1f), + inputType = DydxTriggerOrderPriceInputType.TakeProfitLimit, + ) + + DydxTriggerOrderPriceView.Content( + modifier = Modifier.fillMaxWidth().weight(1f), + inputType = DydxTriggerOrderPriceInputType.StopLossLimit, + ) + } + } +} diff --git a/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/trigger/components/inputfields/limitprice/DydxTriggerOrderLimitPriceSectionViewModel.kt b/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/trigger/components/inputfields/limitprice/DydxTriggerOrderLimitPriceSectionViewModel.kt new file mode 100644 index 00000000..66415bda --- /dev/null +++ b/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/trigger/components/inputfields/limitprice/DydxTriggerOrderLimitPriceSectionViewModel.kt @@ -0,0 +1,48 @@ +package exchange.dydx.trading.feature.trade.trigger.components.inputfields.limitprice + +import androidx.lifecycle.ViewModel +import dagger.hilt.android.lifecycle.HiltViewModel +import exchange.dydx.abacus.output.input.TriggerOrdersInput +import exchange.dydx.abacus.protocols.LocalizerProtocol +import exchange.dydx.dydxstatemanager.AbacusStateManagerProtocol +import exchange.dydx.dydxstatemanager.MarketConfigsAndAsset +import exchange.dydx.trading.common.DydxViewModel +import exchange.dydx.trading.common.formatter.DydxFormatter +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.distinctUntilChanged +import javax.inject.Inject + +@HiltViewModel +class DydxTriggerOrderLimitPriceSectionViewModel @Inject constructor( + private val localizer: LocalizerProtocol, + private val abacusStateManager: AbacusStateManagerProtocol, + private val formatter: DydxFormatter, +) : ViewModel(), DydxViewModel { + + private val enabled = MutableStateFlow(false) + + val state: Flow = + combine( + enabled, + abacusStateManager.state.triggerOrdersInput, + abacusStateManager.state.configsAndAssetMap, + ) { sizeEnabled, triggerOrdersInput, configsAndAssetMap -> + val marketId = triggerOrdersInput?.marketId ?: return@combine null + createViewState(sizeEnabled, triggerOrdersInput, configsAndAssetMap?.get(marketId)) + } + .distinctUntilChanged() + + private fun createViewState( + sizeEnabled: Boolean, + triggerOrdersInput: TriggerOrdersInput?, + configsAndAsset: MarketConfigsAndAsset?, + ): DydxTriggerOrderLimitPriceSectionView.ViewState { + return DydxTriggerOrderLimitPriceSectionView.ViewState( + localizer = localizer, + enabled = sizeEnabled, + onEnabledChanged = { enabled.value = it }, + ) + } +} diff --git a/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/trigger/components/inputfields/price/DydxTriggerOrderPriceView.kt b/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/trigger/components/inputfields/price/DydxTriggerOrderPriceView.kt new file mode 100644 index 00000000..30c1af8b --- /dev/null +++ b/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/trigger/components/inputfields/price/DydxTriggerOrderPriceView.kt @@ -0,0 +1,82 @@ +package exchange.dydx.trading.feature.trade.trigger.components.inputfields.price + +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.abacus.protocols.LocalizerProtocol +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.LabeledTextInput +import exchange.dydx.trading.feature.trade.trigger.components.inputfields.DydxTriggerOrderPriceInputType + +@Preview +@Composable +fun Preview_DydxTriggerOrderPriceView() { + DydxThemedPreviewSurface { + DydxTriggerOrderPriceView.Content(Modifier, DydxTriggerOrderPriceView.ViewState.preview) + } +} + +object DydxTriggerOrderPriceView : DydxComponent { + + data class ViewState( + val localizer: LocalizerProtocol, + val labeledTextInput: LabeledTextInput.ViewState, + ) { + companion object { + val preview = ViewState( + localizer = MockLocalizer(), + labeledTextInput = LabeledTextInput.ViewState.preview, + ) + } + } + + @Composable + override fun Content(modifier: Modifier) { + Content(modifier, DydxTriggerOrderPriceInputType.TakeProfit) + } + + @Composable + fun Content(modifier: Modifier, inputType: DydxTriggerOrderPriceInputType) { + when (inputType) { + DydxTriggerOrderPriceInputType.TakeProfit -> { + val viewModel: DydxTriggerOrderTakeProfitPriceViewModel = hiltViewModel() + val state = viewModel.state.collectAsStateWithLifecycle(initialValue = null).value + Content(modifier, state) + } + DydxTriggerOrderPriceInputType.StopLoss -> { + val viewModel: DydxTriggerOrderStopLossPriceViewModel = hiltViewModel() + val state = viewModel.state.collectAsStateWithLifecycle(initialValue = null).value + Content(modifier, state) + } + DydxTriggerOrderPriceInputType.TakeProfitLimit -> { + val viewModel: DydxTriggerOrderTakeProfitLimitPriceViewModel = hiltViewModel() + val state = viewModel.state.collectAsStateWithLifecycle(initialValue = null).value + Content(modifier, state) + } + DydxTriggerOrderPriceInputType.StopLossLimit -> { + val viewModel: DydxTriggerOrderStopLossLimitPriceViewModel = hiltViewModel() + val state = viewModel.state.collectAsStateWithLifecycle(initialValue = null).value + Content(modifier, state) + } + } + } + + @Composable + fun Content(modifier: Modifier, state: ViewState?) { + if (state == null) { + return + } + + InputFieldScarfold(modifier) { + LabeledTextInput.Content( + modifier = Modifier, + state = state.labeledTextInput, + ) + } + } +} diff --git a/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/trigger/components/inputfields/price/DydxTriggerOrderPriceViewModel.kt b/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/trigger/components/inputfields/price/DydxTriggerOrderPriceViewModel.kt new file mode 100644 index 00000000..2da80069 --- /dev/null +++ b/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/trigger/components/inputfields/price/DydxTriggerOrderPriceViewModel.kt @@ -0,0 +1,114 @@ +package exchange.dydx.trading.feature.trade.trigger.components.inputfields.price + +import androidx.lifecycle.ViewModel +import dagger.hilt.android.lifecycle.HiltViewModel +import exchange.dydx.abacus.output.input.TriggerOrdersInput +import exchange.dydx.abacus.protocols.LocalizerProtocol +import exchange.dydx.dydxstatemanager.AbacusStateManagerProtocol +import exchange.dydx.dydxstatemanager.MarketConfigsAndAsset +import exchange.dydx.trading.common.DydxViewModel +import exchange.dydx.trading.common.formatter.DydxFormatter +import exchange.dydx.trading.feature.shared.views.LabeledTextInput +import exchange.dydx.trading.feature.trade.trigger.components.inputfields.DydxTriggerOrderPriceInputType +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.distinctUntilChanged +import javax.inject.Inject + +@HiltViewModel +class DydxTriggerOrderTakeProfitPriceViewModel @Inject constructor( + private val localizer: LocalizerProtocol, + private val abacusStateManager: AbacusStateManagerProtocol, + private val formatter: DydxFormatter, +) : DydxTriggerOrderPriceViewModel( + localizer, + abacusStateManager, + formatter, + DydxTriggerOrderPriceInputType.TakeProfit, +) + +@HiltViewModel +class DydxTriggerOrderStopLossPriceViewModel @Inject constructor( + private val localizer: LocalizerProtocol, + private val abacusStateManager: AbacusStateManagerProtocol, + private val formatter: DydxFormatter, +) : DydxTriggerOrderPriceViewModel( + localizer, + abacusStateManager, + formatter, + DydxTriggerOrderPriceInputType.StopLoss, +) + +@HiltViewModel +class DydxTriggerOrderTakeProfitLimitPriceViewModel @Inject constructor( + private val localizer: LocalizerProtocol, + private val abacusStateManager: AbacusStateManagerProtocol, + private val formatter: DydxFormatter, +) : DydxTriggerOrderPriceViewModel( + localizer, + abacusStateManager, + formatter, + DydxTriggerOrderPriceInputType.TakeProfitLimit, +) + +@HiltViewModel +class DydxTriggerOrderStopLossLimitPriceViewModel @Inject constructor( + private val localizer: LocalizerProtocol, + private val abacusStateManager: AbacusStateManagerProtocol, + private val formatter: DydxFormatter, +) : DydxTriggerOrderPriceViewModel( + localizer, + abacusStateManager, + formatter, + DydxTriggerOrderPriceInputType.StopLossLimit, +) + +open class DydxTriggerOrderPriceViewModel( + private val localizer: LocalizerProtocol, + private val abacusStateManager: AbacusStateManagerProtocol, + private val formatter: DydxFormatter, + val inputType: DydxTriggerOrderPriceInputType, +) : ViewModel(), DydxViewModel { + + val state: Flow = + combine( + abacusStateManager.state.triggerOrdersInput, + abacusStateManager.state.configsAndAssetMap, + ) { triggerOrdersInput, configsAndAssetMap -> + val marketId = triggerOrdersInput?.marketId ?: return@combine null + createViewState(triggerOrdersInput, configsAndAssetMap?.get(marketId)) + } + .distinctUntilChanged() + + private fun createViewState( + triggerOrdersInput: TriggerOrdersInput?, + configsAndAsset: MarketConfigsAndAsset?, + ): DydxTriggerOrderPriceView.ViewState { + val marketConfigs = configsAndAsset?.configs + val value = when (inputType) { + DydxTriggerOrderPriceInputType.TakeProfit -> null + DydxTriggerOrderPriceInputType.StopLoss -> null + DydxTriggerOrderPriceInputType.TakeProfitLimit -> null + DydxTriggerOrderPriceInputType.StopLossLimit -> null + } + val label = when (inputType) { + DydxTriggerOrderPriceInputType.TakeProfit -> localizer.localize("APP.TRIGGERS_MODAL.TP_PRICE") + DydxTriggerOrderPriceInputType.StopLoss -> localizer.localize("APP.TRIGGERS_MODAL.SL_PRICE") + DydxTriggerOrderPriceInputType.TakeProfitLimit -> localizer.localize("APP.TRIGGERS_MODAL.TP_LIMIT") + DydxTriggerOrderPriceInputType.StopLossLimit -> localizer.localize("APP.TRIGGERS_MODAL.SL_LIMIT") + } + return DydxTriggerOrderPriceView.ViewState( + localizer = localizer, + labeledTextInput = LabeledTextInput.ViewState( + localizer = localizer, + label = label, + token = "USD", + value = value, + placeholder = formatter.raw(0.0, marketConfigs?.displayTickSizeDecimals ?: 0), + onValueChanged = { value -> + // abacusStateManager.trade(value, TradeInputField.limitPrice) + }, + ), + ) + } +} diff --git a/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/trigger/components/inputfields/size/DydxTriggerOrderSizeView.kt b/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/trigger/components/inputfields/size/DydxTriggerOrderSizeView.kt new file mode 100644 index 00000000..606d2864 --- /dev/null +++ b/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/trigger/components/inputfields/size/DydxTriggerOrderSizeView.kt @@ -0,0 +1,141 @@ +package exchange.dydx.trading.feature.trade.trigger.components.inputfields.size + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.width +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Brush +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.inputs.PlatformSwitchInput +import exchange.dydx.platformui.components.slider.CustomSlider +import exchange.dydx.platformui.components.slider.CustomSliderDefaults +import exchange.dydx.platformui.components.slider.progress +import exchange.dydx.platformui.designSystem.theme.ThemeColor +import exchange.dydx.platformui.designSystem.theme.ThemeFont +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.navigation.DydxAnimation +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.LabeledTextInput + +@Preview +@Composable +fun Preview_DydxTriggerOrderSizeView() { + DydxThemedPreviewSurface { + DydxTriggerOrderSizeView.Content(Modifier, DydxTriggerOrderSizeView.ViewState.preview) + } +} + +object DydxTriggerOrderSizeView : DydxComponent { + data class ViewState( + val localizer: LocalizerProtocol, + val enabled: Boolean = true, + val onEnabledChanged: (Boolean) -> Unit = {}, + val labeledTextInput: LabeledTextInput.ViewState, + val percentage: Float = 40.0f, + val onPercentageChanged: (Float) -> Unit = {}, + ) { + companion object { + val preview = ViewState( + localizer = MockLocalizer(), + labeledTextInput = LabeledTextInput.ViewState.preview, + ) + } + } + + @Composable + override fun Content(modifier: Modifier) { + val viewModel: DydxTriggerOrderSizeViewModel = hiltViewModel() + + val state = viewModel.state.collectAsStateWithLifecycle(initialValue = null).value + Content(modifier, state) + } + + @Composable + fun Content(modifier: Modifier, state: ViewState?) { + if (state == null) { + return + } + + Column( + modifier = modifier, + verticalArrangement = Arrangement.spacedBy(12.dp), + ) { + PlatformSwitchInput( + modifier = Modifier.fillMaxWidth(), + label = state.localizer.localize("APP.GENERAL.CUSTOM_AMOUNT"), + textStyle = TextStyle.dydxDefault + .themeFont(fontSize = ThemeFont.FontSize.base) + .themeColor(ThemeColor.SemanticColor.text_secondary), + value = state.enabled, + onValueChange = state.onEnabledChanged, + ) + + DydxAnimation.AnimateExpandInOut( + visible = state.enabled, + ) { + SizeContent(modifier, state) + } + } + } + + @Composable + @OptIn(ExperimentalMaterial3Api::class) + private fun SizeContent(modifier: Modifier, state: ViewState) { + Row( + modifier = Modifier.fillMaxWidth(), + verticalAlignment = Alignment.CenterVertically, + ) { + CustomSlider( + value = state.percentage, + onValueChange = state.onPercentageChanged, + valueRange = 0.0f..100.0f, + modifier = modifier.weight(1f), + showLabel = false, + showIndicator = false, + thumb = { + // CustomSliderDefaults.Thumb("${abs(it)}x") + CustomSliderDefaults.Thumb("") + }, + track = { + Box( + modifier = Modifier + .progress(sliderState = it) + .background( + brush = Brush.linearGradient( + 0.0f to ThemeColor.SemanticColor.text_tertiary.color, + 1.0f to ThemeColor.SemanticColor.text_primary.color, + ), + ), + ) + }, + ) + + InputFieldScarfold( + modifier = Modifier.width(120.dp), + ) { + LabeledTextInput.Content( + modifier = Modifier, + state = state.labeledTextInput, + ) + } + } + } +} diff --git a/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/trigger/components/inputfields/size/DydxTriggerOrderSizeViewModel.kt b/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/trigger/components/inputfields/size/DydxTriggerOrderSizeViewModel.kt new file mode 100644 index 00000000..b68c61f0 --- /dev/null +++ b/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/trigger/components/inputfields/size/DydxTriggerOrderSizeViewModel.kt @@ -0,0 +1,60 @@ +package exchange.dydx.trading.feature.trade.trigger.components.inputfields.size + +import androidx.lifecycle.ViewModel +import dagger.hilt.android.lifecycle.HiltViewModel +import exchange.dydx.abacus.output.input.TriggerOrdersInput +import exchange.dydx.abacus.protocols.LocalizerProtocol +import exchange.dydx.dydxstatemanager.AbacusStateManagerProtocol +import exchange.dydx.dydxstatemanager.MarketConfigsAndAsset +import exchange.dydx.trading.common.DydxViewModel +import exchange.dydx.trading.common.formatter.DydxFormatter +import exchange.dydx.trading.feature.shared.views.LabeledTextInput +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.distinctUntilChanged +import javax.inject.Inject + +@HiltViewModel +class DydxTriggerOrderSizeViewModel @Inject constructor( + private val localizer: LocalizerProtocol, + private val abacusStateManager: AbacusStateManagerProtocol, + private val formatter: DydxFormatter, +) : ViewModel(), DydxViewModel { + + private val enabled = MutableStateFlow(false) + + val state: Flow = + combine( + enabled, + abacusStateManager.state.triggerOrdersInput, + abacusStateManager.state.configsAndAssetMap, + ) { sizeEnabled, triggerOrdersInput, configsAndAssetMap -> + val marketId = triggerOrdersInput?.marketId ?: return@combine null + createViewState(sizeEnabled, triggerOrdersInput, configsAndAssetMap?.get(marketId)) + } + .distinctUntilChanged() + + private fun createViewState( + sizeEnabled: Boolean, + triggerOrdersInput: TriggerOrdersInput?, + configsAndAsset: MarketConfigsAndAsset?, + ): DydxTriggerOrderSizeView.ViewState { + val marketConfigs = configsAndAsset?.configs + return DydxTriggerOrderSizeView.ViewState( + localizer = localizer, + enabled = sizeEnabled, + onEnabledChanged = { enabled.value = it }, + labeledTextInput = LabeledTextInput.ViewState( + localizer = localizer, + label = localizer.localize("APP.GENERAL.AMOUNT"), + token = configsAndAsset?.asset?.name, + value = null, + placeholder = formatter.raw(0.0, marketConfigs?.displayStepSizeDecimals ?: 0), + onValueChanged = { value -> + // abacusStateManager.trade(value, TradeInputField.limitPrice) + }, + ), + ) + } +} diff --git a/v4/integration/dydxStateManager/src/main/java/exchange/dydx/dydxstatemanager/AbacusState.kt b/v4/integration/dydxStateManager/src/main/java/exchange/dydx/dydxstatemanager/AbacusState.kt index a0875953..2857b7f2 100644 --- a/v4/integration/dydxStateManager/src/main/java/exchange/dydx/dydxstatemanager/AbacusState.kt +++ b/v4/integration/dydxStateManager/src/main/java/exchange/dydx/dydxstatemanager/AbacusState.kt @@ -1,5 +1,6 @@ package exchange.dydx.dydxstatemanager +import com.hoc081098.flowext.ThrottleConfiguration import com.hoc081098.flowext.throttleTime import exchange.dydx.abacus.output.Account import exchange.dydx.abacus.output.Asset @@ -30,6 +31,7 @@ import exchange.dydx.abacus.output.input.ClosePositionInput import exchange.dydx.abacus.output.input.ReceiptLine import exchange.dydx.abacus.output.input.TradeInput import exchange.dydx.abacus.output.input.TransferInput +import exchange.dydx.abacus.output.input.TriggerOrdersInput import exchange.dydx.abacus.output.input.ValidationError import exchange.dydx.abacus.protocols.ParserProtocol import exchange.dydx.abacus.responses.ParsingError @@ -386,7 +388,7 @@ class AbacusState( val tradeInput: StateFlow by lazy { perpetualState .map { it?.input?.trade } - .throttleTime(10) + .throttleTime(10, throttleConfiguration = ThrottleConfiguration.LEADING_AND_TRAILING) .stateIn(stateManagerScope, SharingStarted.Lazily, null) } @@ -396,7 +398,7 @@ class AbacusState( val closePositionInput: StateFlow by lazy { perpetualState .map { it?.input?.closePosition } - .throttleTime(10) + .throttleTime(10, throttleConfiguration = ThrottleConfiguration.LEADING_AND_TRAILING) .stateIn(stateManagerScope, SharingStarted.Lazily, null) } @@ -406,7 +408,7 @@ class AbacusState( val transferInput: StateFlow by lazy { perpetualState .map { it?.input?.transfer } - .throttleTime(10) + .throttleTime(10, throttleConfiguration = ThrottleConfiguration.LEADING_AND_TRAILING) .stateIn(stateManagerScope, SharingStarted.Lazily, null) } @@ -416,7 +418,7 @@ class AbacusState( val receipts: StateFlow> by lazy { perpetualState .map { it?.input?.receiptLines ?: emptyList() } - .throttleTime(10) + .throttleTime(10, throttleConfiguration = ThrottleConfiguration.LEADING_AND_TRAILING) .stateIn(stateManagerScope, SharingStarted.Lazily, emptyList()) } @@ -426,7 +428,7 @@ class AbacusState( val validationErrors: StateFlow> by lazy { perpetualState .map { it?.input?.errors ?: emptyList() } - .throttleTime(10) + .throttleTime(10, throttleConfiguration = ThrottleConfiguration.LEADING_AND_TRAILING) .stateIn(stateManagerScope, SharingStarted.Lazily, emptyList()) } @@ -505,6 +507,17 @@ class AbacusState( .stateIn(stateManagerScope, SharingStarted.Lazily, Restriction.NO_RESTRICTION) } + /** + Trigger order input + **/ + val triggerOrdersInput: StateFlow by lazy { + perpetualState + .map { + it?.input?.triggerOrders + } + .throttleTime(10, throttleConfiguration = ThrottleConfiguration.LEADING_AND_TRAILING) + .stateIn(stateManagerScope, SharingStarted.Lazily, null) + } private val subaccountNumber: String? get() = parser.asString(abacusStateManager.subaccountNumber) } diff --git a/v4/integration/dydxStateManager/src/main/java/exchange/dydx/dydxstatemanager/AbacusStateManager.kt b/v4/integration/dydxStateManager/src/main/java/exchange/dydx/dydxstatemanager/AbacusStateManager.kt index 6a497f2d..4a0e10ff 100644 --- a/v4/integration/dydxStateManager/src/main/java/exchange/dydx/dydxstatemanager/AbacusStateManager.kt +++ b/v4/integration/dydxStateManager/src/main/java/exchange/dydx/dydxstatemanager/AbacusStateManager.kt @@ -32,6 +32,7 @@ import exchange.dydx.abacus.state.manager.V4Environment import exchange.dydx.abacus.state.model.ClosePositionInputField import exchange.dydx.abacus.state.model.TradeInputField import exchange.dydx.abacus.state.model.TransferInputField +import exchange.dydx.abacus.state.model.TriggerOrdersInputField import exchange.dydx.abacus.state.v2.manager.AsyncAbacusStateManagerV2 import exchange.dydx.abacus.state.v2.supervisor.AppConfigsV2 import exchange.dydx.abacus.state.v2.supervisor.OnboardingConfigs @@ -106,6 +107,8 @@ interface AbacusStateManagerProtocol { fun screen(address: String, callback: ((Restriction) -> Unit)) fun commitCCTPWithdraw(callback: (Boolean, ParsingError?, Any?) -> Unit) + fun triggerOrders(input: String?, type: TriggerOrdersInputField?) + // extensions fun resetTransferInputFields() { transfer(null, TransferInputField.size) @@ -376,6 +379,10 @@ class AbacusStateManager @Inject constructor( asyncStateManager.commitCCTPWithdraw(callback) } + override fun triggerOrders(input: String?, type: TriggerOrdersInputField?) { + asyncStateManager.triggerOrders(input, type) + } + // MARK: StateNotificationProtocol override fun apiStateChanged(apiState: ApiState?) { diff --git a/v4/platformUI/src/main/java/exchange/dydx/platformui/components/inputs/PlatformSwitchInput.kt b/v4/platformUI/src/main/java/exchange/dydx/platformui/components/inputs/PlatformSwitchInput.kt index 0f63c718..d2b37d3b 100644 --- a/v4/platformUI/src/main/java/exchange/dydx/platformui/components/inputs/PlatformSwitchInput.kt +++ b/v4/platformUI/src/main/java/exchange/dydx/platformui/components/inputs/PlatformSwitchInput.kt @@ -19,6 +19,9 @@ import exchange.dydx.platformui.designSystem.theme.themeFont fun PlatformSwitchInput( modifier: Modifier = Modifier, label: String? = null, + textStyle: TextStyle = TextStyle.dydxDefault + .themeColor(ThemeColor.SemanticColor.text_tertiary) + .themeFont(fontSize = ThemeFont.FontSize.small), value: Boolean? = null, onValueChange: (Boolean) -> Unit = {}, ) { @@ -30,9 +33,7 @@ fun PlatformSwitchInput( Text( modifier = Modifier.weight(1f), text = label, - style = TextStyle.dydxDefault - .themeColor(ThemeColor.SemanticColor.text_tertiary) - .themeFont(fontSize = ThemeFont.FontSize.small), + style = textStyle, ) }