diff --git a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/addcustomtoken/viewmodels/AddCustomTokenViewModel.kt b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/addcustomtoken/viewmodels/AddCustomTokenViewModel.kt index 71cb37a5e9..1b107076ea 100644 --- a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/addcustomtoken/viewmodels/AddCustomTokenViewModel.kt +++ b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/addcustomtoken/viewmodels/AddCustomTokenViewModel.kt @@ -5,7 +5,6 @@ import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.setValue import androidx.lifecycle.DefaultLifecycleObserver -import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import arrow.core.getOrElse @@ -105,10 +104,6 @@ internal class AddCustomTokenViewModel @Inject constructor( uiState = stateFactory.getInitialState() } - override fun onDestroy(owner: LifecycleOwner) { - uiState = stateFactory.getInitialState() - } - private suspend fun selectSuitableWallet(suitableUserWallets: List): UserWalletId? { val selectedWallet = getSelectedWalletSyncUseCase().getOrNull() val selectedWalletId = if (walletSupportsAddingTokens(selectedWallet) && suitableUserWallets.isNotEmpty()) { diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/WalletScreen.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/WalletScreen.kt index c7913c1fc6..47555b5577 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/WalletScreen.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/WalletScreen.kt @@ -13,7 +13,6 @@ import androidx.compose.material.pullrefresh.rememberPullRefreshState import androidx.compose.material3.* import androidx.compose.runtime.* import androidx.compose.ui.Alignment -import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.platform.LocalSoftwareKeyboardController @@ -23,6 +22,7 @@ import androidx.paging.compose.collectAsLazyPagingItems import com.google.accompanist.systemuicontroller.rememberSystemUiController import com.tangem.core.ui.components.Keyboard import com.tangem.core.ui.components.PrimaryButton +import com.tangem.core.ui.components.SystemBarsEffect import com.tangem.core.ui.components.atoms.Hand import com.tangem.core.ui.components.atoms.handComposableComponentHeight import com.tangem.core.ui.components.bottomsheets.chooseaddress.ChooseAddressBottomSheet @@ -104,7 +104,7 @@ private fun WalletContent( bottomSheetContent: @Composable () -> Unit, alertConfig: WalletAlertState?, ) { - var selectedWalletIndex by remember { mutableIntStateOf(state.selectedWalletIndex) } + var selectedWalletIndex by remember(state.selectedWalletIndex) { mutableIntStateOf(state.selectedWalletIndex) } val selectedWallet = state.wallets[selectedWalletIndex] val scaffoldContent: @Composable () -> Unit = { @@ -226,7 +226,7 @@ private fun WalletContent( } @Suppress("LongParameterList", "LongMethod") -@OptIn(ExperimentalMaterialApi::class, ExperimentalMaterial3Api::class, ExperimentalComposeUiApi::class) +@OptIn(ExperimentalMaterialApi::class, ExperimentalMaterial3Api::class) @Composable private fun BaseScaffoldManageTokenRedesign( state: WalletScreenState, @@ -237,41 +237,40 @@ private fun BaseScaffoldManageTokenRedesign( alertConfig: WalletAlertState?, content: @Composable () -> Unit, ) { - val scaffoldState = rememberBottomSheetScaffoldState() + // show the bottom sheet if there is at least one multicurrency wallet + val showManageTokensBottomSheet = remember(state.wallets) { + state.wallets.any { it is WalletState.MultiCurrency } + } + val bottomSheetState = rememberSheetStateEnhanced( + initialValue = if (showManageTokensBottomSheet) SheetValue.PartiallyExpanded else SheetValue.Hidden, + confirmValueChange = { sheetValue -> + when { + sheetValue == SheetValue.Hidden && showManageTokensBottomSheet -> false + sheetValue != SheetValue.Hidden && !showManageTokensBottomSheet -> false + else -> true + } + }, + skipHiddenState = showManageTokensBottomSheet, + ) + + val keyboardShown = keyboardAsState() + + BottomSheetStateEffects( + bottomSheetState = bottomSheetState, + showManageTokensBottomSheet = showManageTokensBottomSheet, + alertConfig = alertConfig, + keyboardShown = keyboardShown, + ) + + val scaffoldState = rememberBottomSheetScaffoldState( + bottomSheetState = bottomSheetState, + snackbarHostState = snackbarHostState, + ) + val bottomBarHeight = with(LocalDensity.current) { WindowInsets.systemBars.getBottom(this).toDp() } val statusBarHeight = with(LocalDensity.current) { WindowInsets.statusBars.getTop(this).toDp() } - val systemUiController = rememberSystemUiController() - val navigationBarColor = TangemTheme.colors.background.primary - val navigationBarColorWithout = TangemTheme.colors.background.secondary - - DisposableEffect( - navigationBarColor, - navigationBarColorWithout, - ) { - systemUiController.setNavigationBarColor(navigationBarColor) - onDispose { - systemUiController.setNavigationBarColor(navigationBarColorWithout) - } - } - - val keyboardShown by keyboardAsState() - // expand bottom sheet when keyboard appears - LaunchedEffect(keyboardShown is Keyboard.Opened) { - if (keyboardShown is Keyboard.Opened && alertConfig == null) { - scaffoldState.bottomSheetState.expand() - } - } - - val keyboardController = LocalSoftwareKeyboardController.current - val sheetHasBeenHidden = scaffoldState.bottomSheetState.targetValue == SheetValue.PartiallyExpanded - // hide keyboard when bottom sheet is about to be hidden - LaunchedEffect(sheetHasBeenHidden) { - if (sheetHasBeenHidden) { - keyboardController?.hide() - } - } - val peekHeight = bottomSheetHeaderHeightProvider() + handComposableComponentHeight + bottomBarHeight + val coroutineScope = rememberCoroutineScope() BottomSheetScaffold( @@ -301,10 +300,10 @@ private fun BaseScaffoldManageTokenRedesign( // hide bottom sheet when back pressed BackHandler( - keyboardShown is Keyboard.Closed && - scaffoldState.bottomSheetState.currentValue == SheetValue.Expanded, + keyboardShown.value is Keyboard.Closed && + bottomSheetState.currentValue == SheetValue.Expanded, ) { - coroutineScope.launch { scaffoldState.bottomSheetState.partialExpand() } + coroutineScope.launch { bottomSheetState.partialExpand() } } }, content = { paddingValues -> @@ -330,6 +329,100 @@ private fun BaseScaffoldManageTokenRedesign( ) } +@OptIn(ExperimentalMaterial3Api::class) +@Composable +private fun BottomSheetStateEffects( + bottomSheetState: SheetState, + showManageTokensBottomSheet: Boolean, + alertConfig: WalletAlertState?, + keyboardShown: State, +) { + // Bottom sheet during initialization internally expand partially after its content was remeasured, + // therefore initialValue = SheetValue.Hidden in rememberStandardBottomSheetState doesn't work as expected + // so we have to manually restrict expansion in this case + LaunchedEffect(bottomSheetState.targetValue, bottomSheetState.currentValue) { + if (!showManageTokensBottomSheet && + (bottomSheetState.targetValue != SheetValue.Hidden || bottomSheetState.currentValue != SheetValue.Hidden) + ) { + bottomSheetState.hide() + } + } + // react to changes in wallet list + LaunchedEffect(showManageTokensBottomSheet) { + when { + showManageTokensBottomSheet && bottomSheetState.currentValue != SheetValue.PartiallyExpanded -> { + bottomSheetState.partialExpand() + } + !showManageTokensBottomSheet && bottomSheetState.targetValue != SheetValue.Hidden -> { + bottomSheetState.hide() + } + } + } + + val systemUiController = rememberSystemUiController() + val navigationBarColor = TangemTheme.colors.background.primary + val navigationBarColorWithout = TangemTheme.colors.background.secondary + + SystemBarsEffect { + if (showManageTokensBottomSheet) { + setNavigationBarColor(navigationBarColor) + } + } + DisposableEffect( + showManageTokensBottomSheet, + ) { + onDispose { + if (showManageTokensBottomSheet) { + systemUiController.setNavigationBarColor(navigationBarColorWithout) + } + } + } + + // expand bottom sheet when keyboard appears + LaunchedEffect(keyboardShown.value is Keyboard.Opened) { + if (keyboardShown.value is Keyboard.Opened && alertConfig == null) { + bottomSheetState.expand() + } + } + + val keyboardController = LocalSoftwareKeyboardController.current + // hide keyboard when bottom sheet is about to be hidden + LaunchedEffect(Unit) { + snapshotFlow { + bottomSheetState.currentValue == SheetValue.Expanded && + bottomSheetState.targetValue == SheetValue.PartiallyExpanded + }.collect { sheetHasBeenHidden -> + if (sheetHasBeenHidden) { + keyboardController?.hide() + } + } + } +} + +/** + * Use a standard method when this is fixed https://issuetracker.google.com/issues/314796718 + * Current material3 version: 1.2.0 + */ +@Composable +@ExperimentalMaterial3Api +private fun rememberSheetStateEnhanced( + skipPartiallyExpanded: Boolean = false, + confirmValueChange: (SheetValue) -> Boolean = { true }, + initialValue: SheetValue = SheetValue.Hidden, + skipHiddenState: Boolean = false, +): SheetState { + val density = LocalDensity.current + return remember(initialValue, skipPartiallyExpanded, confirmValueChange, skipHiddenState) { + SheetState( + skipPartiallyExpanded = skipPartiallyExpanded, + density = density, + initialValue = initialValue, + confirmValueChange = confirmValueChange, + skipHiddenState = skipHiddenState, + ) + } +} + @OptIn(ExperimentalMaterialApi::class) @Composable private fun BaseScaffold(