Skip to content

Commit 5344191

Browse files
authored
Autoclose android (#394)
* Add autoclose domain types for perpetual TP/SL modify * Add autoclose UI model and factory * Add coordinator support for perpetual modify * Add shared autoclose UI components * Add autoclose modify viewmodel and scene * Wire perpetual position autoclose entry point and route Also fixes pull-to-refresh indicator showing on first open by splitting fetch() (initial load, no refresh state) from refresh() (user gesture, sets refresh state). * Add open-time autoclose support * Add modify autoclose summary to confirm screen * Extract NavEntryViewModelStoreOwner to shared :ui utility * Suppress autoclose PnL when trigger price is directionally invalid * Keep GemTextField caret at end on external value updates * Add asset header and polish AmountAutocloseSheet open flow * Restructure autoclose modify flow as nested nav graph * Match autoclose sheet height to modal-sheet convention * Replace core submodule with main's vendored core (pre-merge, local) * Fix AppIcons * Parse Auto Close TP/SL prices locale-aware Add NumericFormatter.double(from:) and route every take-profit/stop-loss parse through it (modify view model, open-position provider, and sheet), matching iOS which parses numeric input with a locale formatter. The open-position flow previously used toDoubleOrNull(), which dropped the trigger in comma-decimal locales. The typed USD amount now parses via the canonical parseNumber() so the preview matches the built order. * Consolidate Auto Close scene to single onAction Replace the per-callback parameters with a sealed AutocloseAction dispatched through onAction, matching PerpetualPositionScene. Use the shared CenterAlignedTopAppBar header so the input title matches the confirm screen. * Fix Auto Close chevron spacing The Auto Close rows hand-rolled the chevron with a 12dp spacer, leaving a gap no other row has. Use the standard DataBadgeChevron so the chevron sits flush against the value. * Defer Auto Close validation until submit Keep the confirm button enabled while there is a pending change and validate on press (revealing the inline error) instead of disabling on an invalid value, matching iOS's manual-validation behavior. * Tidy Auto Close estimator and validator Remove the unused AutocloseValidator.isValid and make AutocloseEstimator.priceChangePercent private (only roe uses it). * Localize Auto Close trigger errors and separate them from the field Route the trigger-price errors through the errors.perpetual.* localization keys on both platforms (Android R.string, iOS PerpetualError -> Localized.Errors.Perpetual, dropping the hardcoded TODO), and add a divider before the field error to match iOS. * Update PerpetualError.swift * Add Auto Close defaults and suggestion tiers to perpetual config Expose take-profit/stop-loss percent options, their defaults, and a leverage-tiered get_autoclose_suggestions() helper from the shared perpetual config. * Source Auto Close suggestions and options from core Drop the hardcoded leverage->suggestion tiers duplicated in iOS and Android and read them, plus the TP/SL percent options and defaults, through the Gemstone config wrappers. * Generalize leverage picker into reusable WheelPickerSheet Replace LeveragePickerSheet/LeveragePickerView with a generic WheelPickerSheet over WheelPickerDisplayable so it can back any wheel-style option, and migrate the leverage selector to it. * Add default Take Profit and Stop Loss preferences on iOS Persist default TP/SL percents alongside leverage, add the AutocloseOption type and two picker rows to Preferences, and make the leverage default fall back to the core default via a 0 sentinel. * Prefill Auto Close from defaults and recompute on leverage change Seed TP/SL from the saved default percents when opening a position and re-derive the target prices when leverage changes, unless the user has already edited them. * Add default Take Profit and Stop Loss preferences on Android Store default TP/SL percents in UserConfig, expose them from SettingsViewModel, and add the rows to Preferences via a generic OptionPickerLinkItem. * Prefill Auto Close from defaults via trigger flows Derive TP/SL trigger prices from the saved default percents reactively against market price and leverage, falling back to user edits once a field is touched. * Show Auto Close suggestions only when the field is empty Match iOS by gating the suggestions bar on an empty active field, and stop the amount sheet from overwriting in-progress input on every reactive trigger update. * Support indented LinkItem; reorder Perpetual VM Add an indented flag to LinkItem (with Spacer import) to reserve the leading icon space when no painter is provided, and enable it in the preferences option picker. Also refactor AmountPerpetualViewModel by moving transferData/leverage properties and autoclose-related methods (onChangeLeverage, updateAutoclose) to a different location for clarity—no behavior changes intended. * Remove Done button from Auto Close suggestions bar Android dismisses the keyboard via the system IME affordance, so the suggestions bar no longer needs its own Done button; drop it and the now-unused focus manager wiring. * Extend stop-loss percent options with 3% and 5% * Show Perpetuals settings without developer mode on Android Match iOS: the Perpetuals toggle is always visible in Preferences (no developer-mode gate), and the default leverage/take-profit/stop-loss rows show whenever the toggle is on. Feature visibility elsewhere still uses isPerpetualEnabled && hasPerpetualsSupport, identical to iOS. * React to Perpetuals toggle for data sync on Android Match iOS updatePerpetualConnection: observe session + isPerpetualEnabled and (re)sync perpetuals/positions when a perps-capable wallet has the toggle on, instead of snapshotting the flag once at wallet change.
1 parent c056970 commit 5344191

79 files changed

Lines changed: 2726 additions & 293 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

android/app/src/main/kotlin/com/gemwallet/android/ui/navigation/RouteArgumentsNavEntryDecorator.kt

Lines changed: 3 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,11 @@ package com.gemwallet.android.ui.navigation
33
import androidx.compose.runtime.Composable
44
import androidx.compose.runtime.CompositionLocalProvider
55
import androidx.compose.runtime.remember
6-
import androidx.lifecycle.DEFAULT_ARGS_KEY
7-
import androidx.lifecycle.HasDefaultViewModelProviderFactory
8-
import androidx.lifecycle.SAVED_STATE_REGISTRY_OWNER_KEY
9-
import androidx.lifecycle.SavedStateViewModelFactory
10-
import androidx.lifecycle.VIEW_MODEL_STORE_OWNER_KEY
116
import androidx.lifecycle.ViewModel
127
import androidx.lifecycle.ViewModelProvider
138
import androidx.lifecycle.ViewModelStore
149
import androidx.lifecycle.ViewModelStoreOwner
15-
import androidx.lifecycle.enableSavedStateHandles
1610
import androidx.lifecycle.viewmodel.CreationExtras
17-
import androidx.lifecycle.viewmodel.MutableCreationExtras
1811
import androidx.lifecycle.viewmodel.compose.LocalViewModelStoreOwner
1912
import androidx.navigation3.runtime.NavEntry
2013
import androidx.navigation3.runtime.NavEntryDecorator
@@ -23,12 +16,12 @@ import androidx.navigation3.runtime.NavMetadataKey
2316
import androidx.navigation3.runtime.get
2417
import androidx.navigation3.runtime.metadata
2518
import androidx.savedstate.SavedState
26-
import androidx.savedstate.SavedStateRegistry
2719
import androidx.savedstate.SavedStateRegistryOwner
2820
import androidx.savedstate.compose.LocalSavedStateRegistryOwner
2921
import androidx.savedstate.savedState
3022
import com.gemwallet.android.ext.toIdentifier
3123
import com.gemwallet.android.ui.models.navigation.RouteArgument
24+
import com.gemwallet.android.ui.viewmodel.NavEntryViewModelStoreOwner
3225
import com.wallet.core.primitives.AssetId
3326
import kotlin.reflect.KClass
3427

@@ -110,7 +103,7 @@ private fun rememberEntryViewModelStoreOwner(
110103
entryViewModelStores.store(contentKey)
111104
}
112105
return remember(parent, store, defaultArgs, savedStateRegistryOwner) {
113-
RouteArgumentsViewModelStoreOwner(
106+
NavEntryViewModelStoreOwner(
114107
parent = parent,
115108
store = store,
116109
savedStateRegistryOwner = checkNotNull(savedStateRegistryOwner) {
@@ -158,43 +151,6 @@ private object EntryViewModelStoresFactory : ViewModelProvider.Factory {
158151
}
159152
}
160153

161-
private class RouteArgumentsViewModelStoreOwner(
162-
private val parent: ViewModelStoreOwner,
163-
private val store: ViewModelStore,
164-
private val savedStateRegistryOwner: SavedStateRegistryOwner,
165-
private val defaultArgs: SavedState,
166-
) : ViewModelStoreOwner,
167-
SavedStateRegistryOwner,
168-
HasDefaultViewModelProviderFactory {
169-
170-
init {
171-
enableSavedStateHandles()
172-
}
173-
174-
override val viewModelStore: ViewModelStore
175-
get() = store
176-
177-
override val savedStateRegistry: SavedStateRegistry
178-
get() = savedStateRegistryOwner.savedStateRegistry
179-
180-
override val lifecycle
181-
get() = savedStateRegistryOwner.lifecycle
182-
183-
override val defaultViewModelProviderFactory: ViewModelProvider.Factory
184-
get() = (parent as? HasDefaultViewModelProviderFactory)?.defaultViewModelProviderFactory
185-
?: SavedStateViewModelFactory()
186-
187-
override val defaultViewModelCreationExtras: CreationExtras
188-
get() = MutableCreationExtras(
189-
(parent as? HasDefaultViewModelProviderFactory)?.defaultViewModelCreationExtras
190-
?: CreationExtras.Empty
191-
).apply {
192-
this[SAVED_STATE_REGISTRY_OWNER_KEY] = this@RouteArgumentsViewModelStoreOwner
193-
this[VIEW_MODEL_STORE_OWNER_KEY] = this@RouteArgumentsViewModelStoreOwner
194-
this[DEFAULT_ARGS_KEY] = defaultArgs
195-
}
196-
}
197-
198154
internal fun NavEntry<NavKey>.withOccurrenceContentKey(
199155
key: NavKey,
200156
occurrence: Int,
@@ -209,3 +165,4 @@ internal fun NavEntry<NavKey>.withOccurrenceContentKey(
209165
entry.Content()
210166
}
211167
}
168+

android/app/src/main/kotlin/com/gemwallet/android/ui/navigation/routes/Perpetual.kt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import androidx.navigation3.runtime.NavKey
55
import com.gemwallet.android.features.perpetual.views.market.PerpetualMarketNavScreen
66
import com.gemwallet.android.features.perpetual.views.position.PerpetualPositionNavScreen
77
import com.gemwallet.android.ui.models.actions.AmountTransactionAction
8+
import com.gemwallet.android.ui.models.actions.AssetIdAction
89
import com.gemwallet.android.ui.models.actions.ConfirmTransactionAction
910
import com.gemwallet.android.ui.navigation.assetIdArgument
1011
import com.gemwallet.android.ui.navigation.routeArguments
@@ -20,15 +21,15 @@ data class PerpetualPositionRoute(val assetId: AssetId) : NavKey
2021

2122
fun EntryProviderScope<NavKey>.perpetualScreen(
2223
onCancel: () -> Unit,
23-
onOpenPerpetualDetails: (AssetId) -> Unit,
24+
onOpenPerpetualDetails: AssetIdAction,
2425
amountAction: AmountTransactionAction,
2526
confirmAction: ConfirmTransactionAction,
2627
onTransaction: (TransactionId) -> Unit,
2728
) {
2829
entry<PerpetualRoute> {
2930
PerpetualMarketNavScreen(
3031
onOpenPerpetualDetails = onOpenPerpetualDetails,
31-
onCancel = onCancel
32+
onCancel = onCancel,
3233
)
3334
}
3435

android/data/coordinators/src/main/kotlin/com/gemwallet/android/data/coordinators/perpetuals/BuildPerpetualParamsImpl.kt

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ import com.wallet.core.primitives.PerpetualData
1515
import com.wallet.core.primitives.PerpetualDirection
1616
import com.wallet.core.primitives.PerpetualId
1717
import com.wallet.core.primitives.PerpetualMarginType
18+
import com.wallet.core.primitives.PerpetualModifyConfirmData
19+
import com.wallet.core.primitives.PerpetualModifyPositionType
1820
import com.wallet.core.primitives.PerpetualPosition
1921
import com.wallet.core.primitives.PerpetualType
2022
import kotlinx.coroutines.flow.firstOrNull
@@ -63,6 +65,27 @@ class BuildPerpetualParamsImpl(
6365
.perpetual(PerpetualType.Close(confirmData))
6466
}
6567

68+
override suspend fun modify(
69+
perpetualId: PerpetualId,
70+
modifyTypes: List<PerpetualModifyPositionType>,
71+
takeProfitOrderId: ULong?,
72+
stopLossOrderId: ULong?,
73+
): ConfirmParams.PerpetualParams? {
74+
if (modifyTypes.isEmpty()) return null
75+
val data = getPerpetual(perpetualId) ?: return null
76+
val assetIndex = data.perpetual.identifier.toIntOrNull() ?: return null
77+
val account = sessionRepository.session().value?.wallet?.hyperliquidAccount ?: return null
78+
val confirmData = PerpetualModifyConfirmData(
79+
baseAsset = HypercoreUSDC,
80+
assetIndex = assetIndex,
81+
modifyTypes = modifyTypes,
82+
takeProfitOrderId = takeProfitOrderId?.toLong(),
83+
stopLossOrderId = stopLossOrderId?.toLong(),
84+
)
85+
return ConfirmParams.Builder(data.asset, account)
86+
.perpetual(PerpetualType.Modify(confirmData))
87+
}
88+
6689
private suspend fun getPerpetual(perpetualId: PerpetualId): PerpetualData? =
6790
perpetualRepository.getPerpetual(perpetualId).firstOrNull()
6891

android/data/repositories/src/main/kotlin/com/gemwallet/android/data/repositories/config/UserConfig.kt

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,24 @@ class UserConfig(
8181
}
8282
}
8383

84+
fun perpetualTakeProfit(): Flow<Int> = context.dataStore.data
85+
.map { preferences -> preferences[Key.PerpetualTakeProfit] ?: PerpetualConfig.defaultTakeProfit }
86+
87+
suspend fun setPerpetualTakeProfit(value: Int) {
88+
context.dataStore.edit { preferences ->
89+
preferences[Key.PerpetualTakeProfit] = value
90+
}
91+
}
92+
93+
fun perpetualStopLoss(): Flow<Int> = context.dataStore.data
94+
.map { preferences -> preferences[Key.PerpetualStopLoss] ?: PerpetualConfig.defaultStopLoss }
95+
96+
suspend fun setPerpetualStopLoss(value: Int) {
97+
context.dataStore.edit { preferences ->
98+
preferences[Key.PerpetualStopLoss] = value
99+
}
100+
}
101+
84102
fun getLatestAppUpdate(): Flow<AppUpdateInfo?> = context.dataStore.data
85103
.map { preferences ->
86104
val version = preferences[Key.LatestVersion].orEmpty()
@@ -206,6 +224,8 @@ class UserConfig(
206224
val AskNotifications = longPreferencesKey("ask_notifications")
207225
val IsPerpetualEnabled = booleanPreferencesKey("is_perpetual_enabled")
208226
val PerpetualLeverage = intPreferencesKey("perpetual_leverage")
227+
val PerpetualTakeProfit = intPreferencesKey("perpetual_take_profit")
228+
val PerpetualStopLoss = intPreferencesKey("perpetual_stop_loss")
209229
}
210230

211231
}

android/data/repositories/src/main/kotlin/com/gemwallet/android/data/repositories/stream/StreamObserverService.kt

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import com.gemwallet.android.application.perpetual.coordinators.SyncPerpetuals
88
import com.gemwallet.android.data.repositories.config.UserConfig
99
import com.gemwallet.android.data.repositories.session.SessionRepository
1010
import com.gemwallet.android.ext.hasPerpetualsSupport
11+
import com.gemwallet.android.model.Session
1112
import com.gemwallet.android.data.services.gemapi.http.DeviceRequestSigner
1213
import com.gemwallet.android.serializer.StreamEventSerializer
1314
import com.gemwallet.android.serializer.jsonEncoder
@@ -28,8 +29,10 @@ import kotlinx.coroutines.Dispatchers
2829
import kotlinx.coroutines.Job
2930
import kotlinx.coroutines.delay
3031
import kotlinx.coroutines.isActive
32+
import kotlinx.coroutines.flow.Flow
3133
import kotlinx.coroutines.flow.collectLatest
32-
import kotlinx.coroutines.flow.first
34+
import kotlinx.coroutines.flow.combine
35+
import kotlinx.coroutines.flow.distinctUntilChanged
3336
import kotlinx.coroutines.launch
3437
import kotlinx.serialization.encodeToString
3538

@@ -64,12 +67,14 @@ class StreamObserverService(
6467
subscriptionService.setupAssets(wallet.id)
6568
if (connectionJob == null) start()
6669
runCatching { syncAssets() }
67-
if (wallet.hasPerpetualsSupport && userConfig.isPerpetualEnabled().first()) {
68-
runCatching { syncPerpetuals.syncPerpetuals() }
69-
runCatching { syncPerpetualPositions.syncPerpetualPositions() }
70-
}
7170
}
7271
}
72+
scope.launchPerpetualSync(
73+
session = sessionRepository.session(),
74+
isPerpetualEnabled = userConfig.isPerpetualEnabled(),
75+
syncPerpetuals = syncPerpetuals,
76+
syncPerpetualPositions = syncPerpetualPositions,
77+
)
7378
}
7479

7580
fun start() {
@@ -139,3 +144,21 @@ class StreamObserverService(
139144
private const val PING_INTERVAL_MS = 30_000L
140145
}
141146
}
147+
148+
internal fun CoroutineScope.launchPerpetualSync(
149+
session: Flow<Session?>,
150+
isPerpetualEnabled: Flow<Boolean>,
151+
syncPerpetuals: SyncPerpetuals,
152+
syncPerpetualPositions: SyncPerpetualPositions,
153+
): Job = launch {
154+
combine(session, isPerpetualEnabled) { current, enabled ->
155+
val wallet = current?.wallet
156+
if (wallet != null && wallet.hasPerpetualsSupport && enabled) wallet.id.id else null
157+
}
158+
.distinctUntilChanged()
159+
.collectLatest { walletId ->
160+
if (walletId == null) return@collectLatest
161+
runCatching { syncPerpetuals.syncPerpetuals() }
162+
runCatching { syncPerpetualPositions.syncPerpetualPositions() }
163+
}
164+
}

android/features/confirm/presents/src/main/kotlin/com/gemwallet/android/features/confirm/presents/ConfirmScreen.kt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ import com.gemwallet.android.model.AuthRequest
3939
import com.gemwallet.android.model.ConfirmParams
4040
import com.gemwallet.android.model.ValueFormatter
4141
import com.gemwallet.android.ui.R
42+
import com.gemwallet.android.ui.components.perpetual.AutocloseSummaryRow
4243
import com.gemwallet.android.ui.components.perpetual.PerpetualDetailsBottomSheet
4344
import com.gemwallet.android.ui.components.perpetual.PerpetualDetailsSummaryItem
4445
import com.gemwallet.android.ui.components.perpetual.title
@@ -317,6 +318,11 @@ private fun ConfirmDetailElementRow(
317318
onClick = onClick,
318319
listPosition = listPosition,
319320
)
321+
is ConfirmDetailElement.PerpetualModifyAutoclose -> AutocloseSummaryRow(
322+
takeProfitText = item.takeProfitText,
323+
stopLossText = item.stopLossText,
324+
listPosition = listPosition,
325+
)
320326
}
321327
}
322328

@@ -340,6 +346,8 @@ private fun ConfirmDetailElementBottomSheet(
340346
onDismiss = onDismiss,
341347
)
342348

349+
is ConfirmDetailElement.PerpetualModifyAutoclose -> Unit
350+
343351
null -> Unit
344352
}
345353
}

android/features/confirm/viewmodels/src/main/kotlin/com/gemwallet/android/features/confirm/models/ConfirmDetailElement.kt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,9 @@ sealed interface ConfirmDetailElement {
1111
data class PerpetualDetails(
1212
val model: PerpetualConfirmDetailsUIModel,
1313
) : ConfirmDetailElement
14+
15+
data class PerpetualModifyAutoclose(
16+
val takeProfitText: String?,
17+
val stopLossText: String?,
18+
) : ConfirmDetailElement
1419
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package com.gemwallet.android.features.confirm.models
2+
3+
import com.gemwallet.android.model.CurrencyFormatter
4+
import com.wallet.core.primitives.Currency
5+
import com.wallet.core.primitives.PerpetualModifyConfirmData
6+
import com.wallet.core.primitives.PerpetualModifyPositionType
7+
8+
object PerpetualModifyAutocloseFactory {
9+
10+
private const val ClearedPlaceholder: String = "-"
11+
12+
fun create(data: PerpetualModifyConfirmData): ConfirmDetailElement.PerpetualModifyAutoclose? {
13+
val tpsl = data.modifyTypes
14+
.filterIsInstance<PerpetualModifyPositionType.Tpsl>()
15+
.firstOrNull()?.content
16+
val cancelOrderIds = data.modifyTypes
17+
.filterIsInstance<PerpetualModifyPositionType.Cancel>()
18+
.flatMap { it.content }
19+
.map { it.orderId }
20+
.toSet()
21+
val takeProfitCanceled = data.takeProfitOrderId != null &&
22+
data.takeProfitOrderId in cancelOrderIds
23+
val stopLossCanceled = data.stopLossOrderId != null &&
24+
data.stopLossOrderId in cancelOrderIds
25+
val formatter = CurrencyFormatter(currency = Currency.USD)
26+
val takeProfitText: String? = when {
27+
tpsl?.takeProfit != null -> tpsl.takeProfit?.toDoubleOrNull()?.let(formatter::string)
28+
takeProfitCanceled -> ClearedPlaceholder
29+
else -> null
30+
}
31+
val stopLossText: String? = when {
32+
tpsl?.stopLoss != null -> tpsl.stopLoss?.toDoubleOrNull()?.let(formatter::string)
33+
stopLossCanceled -> ClearedPlaceholder
34+
else -> null
35+
}
36+
if (takeProfitText == null && stopLossText == null) return null
37+
return ConfirmDetailElement.PerpetualModifyAutoclose(takeProfitText, stopLossText)
38+
}
39+
}

android/features/confirm/viewmodels/src/main/kotlin/com/gemwallet/android/features/confirm/viewmodels/ConfirmViewModel.kt

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,13 @@ import com.gemwallet.android.ui.models.swap.SwapProviderUIModelFactory
2727
import com.gemwallet.android.ui.models.actions.FinishConfirmAction
2828
import com.gemwallet.android.domains.confirm.AmountUIModel
2929
import com.gemwallet.android.features.confirm.models.ConfirmDetailElement
30+
import com.gemwallet.android.features.confirm.models.PerpetualModifyAutocloseFactory
3031
import com.gemwallet.android.domains.confirm.ConfirmError
3132
import com.gemwallet.android.domains.confirm.ConfirmState
3233
import com.gemwallet.android.domains.confirm.FeeUIModel
3334
import com.wallet.core.primitives.AssetId
3435
import com.wallet.core.primitives.Currency
36+
import com.wallet.core.primitives.PerpetualType
3537
import com.wallet.core.primitives.FeePriority
3638
import dagger.hilt.android.lifecycle.HiltViewModel
3739
import kotlinx.coroutines.Dispatchers
@@ -323,9 +325,10 @@ class ConfirmViewModel @Inject constructor(
323325

324326
private fun buildPerpetualDetailElement(
325327
params: ConfirmParams.PerpetualParams?,
326-
): ConfirmDetailElement.PerpetualDetails? {
327-
val model = PerpetualConfirmDetailsUIModelFactory.create(params?.perpetualType ?: return null) ?: return null
328-
return ConfirmDetailElement.PerpetualDetails(model)
328+
): ConfirmDetailElement? = when (val type = params?.perpetualType) {
329+
null -> null
330+
is PerpetualType.Modify -> PerpetualModifyAutocloseFactory.create(type.content)
331+
else -> PerpetualConfirmDetailsUIModelFactory.create(type)?.let(ConfirmDetailElement::PerpetualDetails)
329332
}
330333

331334
private fun buildSwapDetailElement(

0 commit comments

Comments
 (0)