Automatic keyboard and bottom sheet handling during navigation
This commit is contained in:
parent
696344f832
commit
33cd056570
|
@ -0,0 +1,80 @@
|
|||
package co.electriccoin.zcash.ui.design
|
||||
|
||||
import androidx.compose.foundation.layout.WindowInsets
|
||||
import androidx.compose.foundation.layout.ime
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.Stable
|
||||
import androidx.compose.runtime.State
|
||||
import androidx.compose.runtime.compositionLocalOf
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberUpdatedState
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
|
||||
import androidx.compose.ui.platform.SoftwareKeyboardController
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.filter
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.flow.update
|
||||
import kotlinx.coroutines.withTimeoutOrNull
|
||||
import kotlin.time.Duration.Companion.seconds
|
||||
|
||||
@Stable
|
||||
class KeyboardManager(
|
||||
isOpen: Boolean,
|
||||
private val softwareKeyboardController: SoftwareKeyboardController?
|
||||
) {
|
||||
private var targetState = MutableStateFlow(isOpen)
|
||||
|
||||
var isOpen by mutableStateOf(isOpen)
|
||||
private set
|
||||
|
||||
suspend fun close() {
|
||||
if (targetState.value) {
|
||||
withTimeoutOrNull(.5.seconds) {
|
||||
softwareKeyboardController?.hide()
|
||||
targetState.filter { !it }.first()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun onKeyboardOpened() {
|
||||
targetState.update { true }
|
||||
isOpen = true
|
||||
}
|
||||
|
||||
fun onKeyboardClosed() {
|
||||
targetState.update { false }
|
||||
isOpen = false
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun rememberKeyboardManager(): KeyboardManager {
|
||||
val isKeyboardOpen by rememberKeyboardState()
|
||||
val softwareKeyboardController = LocalSoftwareKeyboardController.current
|
||||
val keyboardManager = remember { KeyboardManager(isKeyboardOpen, softwareKeyboardController) }
|
||||
LaunchedEffect(isKeyboardOpen) {
|
||||
if (isKeyboardOpen) {
|
||||
keyboardManager.onKeyboardOpened()
|
||||
} else {
|
||||
keyboardManager.onKeyboardClosed()
|
||||
}
|
||||
}
|
||||
return keyboardManager
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun rememberKeyboardState(): State<Boolean> {
|
||||
val isImeVisible = WindowInsets.ime.getBottom(LocalDensity.current) > 0
|
||||
return rememberUpdatedState(isImeVisible)
|
||||
}
|
||||
|
||||
@Suppress("CompositionLocalAllowlist")
|
||||
val LocalKeyboardManager =
|
||||
compositionLocalOf<KeyboardManager> {
|
||||
error("Keyboard manager not provided")
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
package co.electriccoin.zcash.ui.design
|
||||
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.SheetState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.Stable
|
||||
import androidx.compose.runtime.compositionLocalOf
|
||||
import androidx.compose.runtime.remember
|
||||
import kotlinx.coroutines.withTimeoutOrNull
|
||||
import kotlin.time.Duration.Companion.seconds
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Stable
|
||||
class SheetStateManager {
|
||||
private var sheetState: SheetState? = null
|
||||
|
||||
fun onSheetOpened(sheetState: SheetState) {
|
||||
this.sheetState = sheetState
|
||||
}
|
||||
|
||||
fun onSheetDisposed(sheetState: SheetState) {
|
||||
if (this.sheetState == sheetState) {
|
||||
this.sheetState = null
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun hide() {
|
||||
withTimeoutOrNull(.5.seconds) {
|
||||
sheetState?.hide()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun rememberSheetStateManager() = remember { SheetStateManager() }
|
||||
|
||||
@Suppress("CompositionLocalAllowlist")
|
||||
val LocalSheetStateManager =
|
||||
compositionLocalOf<SheetStateManager> {
|
||||
error("Sheet state manager not provided")
|
||||
}
|
|
@ -9,6 +9,10 @@ import androidx.compose.material3.RippleConfiguration
|
|||
import androidx.compose.material3.RippleDefaults
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import co.electriccoin.zcash.ui.design.LocalKeyboardManager
|
||||
import co.electriccoin.zcash.ui.design.LocalSheetStateManager
|
||||
import co.electriccoin.zcash.ui.design.rememberKeyboardManager
|
||||
import co.electriccoin.zcash.ui.design.rememberSheetStateManager
|
||||
import co.electriccoin.zcash.ui.design.theme.balances.LocalBalancesAvailable
|
||||
import co.electriccoin.zcash.ui.design.theme.colors.DarkZashiColorsInternal
|
||||
import co.electriccoin.zcash.ui.design.theme.colors.LightZashiColorsInternal
|
||||
|
@ -49,7 +53,9 @@ fun ZcashTheme(
|
|||
LocalZashiColors provides zashiColors,
|
||||
LocalZashiTypography provides ZashiTypographyInternal,
|
||||
LocalRippleConfiguration provides MaterialRippleConfig,
|
||||
LocalBalancesAvailable provides balancesAvailable
|
||||
LocalBalancesAvailable provides balancesAvailable,
|
||||
LocalKeyboardManager provides rememberKeyboardManager(),
|
||||
LocalSheetStateManager provides rememberSheetStateManager()
|
||||
) {
|
||||
ProvideDimens {
|
||||
MaterialTheme(
|
||||
|
|
|
@ -118,7 +118,6 @@ class MainActivity : FragmentActivity() {
|
|||
}
|
||||
}
|
||||
|
||||
// Note this condition needs to be kept in sync with the condition in MainContent()
|
||||
SecretState.LOADING == walletViewModel.secretState.value
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,7 +6,6 @@ import androidx.compose.runtime.getValue
|
|||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.ui.platform.LocalFocusManager
|
||||
import androidx.compose.ui.window.DialogProperties
|
||||
import androidx.lifecycle.LifecycleCoroutineScope
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
|
@ -39,6 +38,8 @@ import co.electriccoin.zcash.ui.NavigationTargets.WHATS_NEW
|
|||
import co.electriccoin.zcash.ui.common.compose.LocalNavController
|
||||
import co.electriccoin.zcash.ui.common.provider.ApplicationStateProvider
|
||||
import co.electriccoin.zcash.ui.common.provider.isInForeground
|
||||
import co.electriccoin.zcash.ui.design.LocalKeyboardManager
|
||||
import co.electriccoin.zcash.ui.design.LocalSheetStateManager
|
||||
import co.electriccoin.zcash.ui.design.animation.ScreenAnimation.enterTransition
|
||||
import co.electriccoin.zcash.ui.design.animation.ScreenAnimation.exitTransition
|
||||
import co.electriccoin.zcash.ui.design.animation.ScreenAnimation.popEnterTransition
|
||||
|
@ -122,9 +123,10 @@ import org.koin.compose.koinInject
|
|||
@Suppress("LongMethod", "CyclomaticComplexMethod")
|
||||
internal fun MainActivity.Navigation() {
|
||||
val navController = LocalNavController.current
|
||||
val focusManager = LocalFocusManager.current
|
||||
val keyboardManager = LocalKeyboardManager.current
|
||||
val flexaViewModel = koinViewModel<FlexaViewModel>()
|
||||
val navigationRouter = koinInject<NavigationRouter>()
|
||||
val sheetStateManager = LocalSheetStateManager.current
|
||||
|
||||
// Helper properties for triggering the system security UI from callbacks
|
||||
val (exportPrivateDataAuthentication, setExportPrivateDataAuthentication) =
|
||||
|
@ -136,13 +138,15 @@ internal fun MainActivity.Navigation() {
|
|||
remember(
|
||||
navController,
|
||||
flexaViewModel,
|
||||
focusManager
|
||||
keyboardManager,
|
||||
sheetStateManager
|
||||
) {
|
||||
NavigatorImpl(
|
||||
activity = this@Navigation,
|
||||
navController = navController,
|
||||
flexaViewModel = flexaViewModel,
|
||||
focusManager = focusManager
|
||||
keyboardManager = keyboardManager,
|
||||
sheetStateManager = sheetStateManager
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -2,10 +2,11 @@ package co.electriccoin.zcash.ui
|
|||
|
||||
import android.annotation.SuppressLint
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.compose.ui.focus.FocusManager
|
||||
import androidx.navigation.NavHostController
|
||||
import androidx.navigation.NavOptionsBuilder
|
||||
import androidx.navigation.serialization.generateHashCode
|
||||
import co.electriccoin.zcash.ui.design.KeyboardManager
|
||||
import co.electriccoin.zcash.ui.design.SheetStateManager
|
||||
import co.electriccoin.zcash.ui.screen.ExternalUrl
|
||||
import co.electriccoin.zcash.ui.screen.about.util.WebBrowserUtil
|
||||
import co.electriccoin.zcash.ui.screen.flexa.FlexaViewModel
|
||||
|
@ -15,17 +16,19 @@ import kotlinx.serialization.InternalSerializationApi
|
|||
import kotlinx.serialization.serializer
|
||||
|
||||
interface Navigator {
|
||||
fun executeCommand(command: NavigationCommand)
|
||||
suspend fun executeCommand(command: NavigationCommand)
|
||||
}
|
||||
|
||||
class NavigatorImpl(
|
||||
private val activity: ComponentActivity,
|
||||
private val navController: NavHostController,
|
||||
private val flexaViewModel: FlexaViewModel,
|
||||
private val focusManager: FocusManager,
|
||||
private val keyboardManager: KeyboardManager,
|
||||
private val sheetStateManager: SheetStateManager,
|
||||
) : Navigator {
|
||||
override fun executeCommand(command: NavigationCommand) {
|
||||
focusManager.clearFocus(true)
|
||||
override suspend fun executeCommand(command: NavigationCommand) {
|
||||
keyboardManager.close()
|
||||
sheetStateManager.hide()
|
||||
when (command) {
|
||||
is NavigationCommand.Forward -> forward(command)
|
||||
is NavigationCommand.Replace -> replace(command)
|
||||
|
|
|
@ -8,12 +8,8 @@ class ApplyTransactionFiltersUseCase(
|
|||
private val transactionFilterRepository: TransactionFilterRepository,
|
||||
private val navigationRouter: NavigationRouter,
|
||||
) {
|
||||
suspend operator fun invoke(
|
||||
filters: List<TransactionFilter>,
|
||||
hideBottomSheet: suspend () -> Unit
|
||||
) {
|
||||
operator fun invoke(filters: List<TransactionFilter>) {
|
||||
transactionFilterRepository.apply(filters)
|
||||
hideBottomSheet()
|
||||
navigationRouter.back()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,11 +9,9 @@ class CreateOrUpdateTransactionNoteUseCase(
|
|||
) {
|
||||
suspend operator fun invoke(
|
||||
txId: String,
|
||||
note: String,
|
||||
closeBottomSheet: suspend () -> Unit
|
||||
note: String
|
||||
) {
|
||||
metadataRepository.createOrUpdateTxNote(txId, note.trim())
|
||||
closeBottomSheet()
|
||||
navigationRouter.back()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,12 +7,8 @@ class DeleteTransactionNoteUseCase(
|
|||
private val metadataRepository: MetadataRepository,
|
||||
private val navigationRouter: NavigationRouter
|
||||
) {
|
||||
suspend operator fun invoke(
|
||||
txId: String,
|
||||
closeBottomSheet: suspend () -> Unit
|
||||
) {
|
||||
suspend operator fun invoke(txId: String) {
|
||||
metadataRepository.deleteTxNote(txId)
|
||||
closeBottomSheet()
|
||||
navigationRouter.back()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,12 +8,8 @@ class SelectWalletAccountUseCase(
|
|||
private val accountDataSource: AccountDataSource,
|
||||
private val navigationRouter: NavigationRouter
|
||||
) {
|
||||
suspend operator fun invoke(
|
||||
account: WalletAccount,
|
||||
hideBottomSheet: suspend () -> Unit
|
||||
) {
|
||||
suspend operator fun invoke(account: WalletAccount) {
|
||||
accountDataSource.selectAccount(account)
|
||||
hideBottomSheet()
|
||||
navigationRouter.back()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,12 +4,14 @@ import android.view.WindowManager
|
|||
import androidx.activity.compose.BackHandler
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.DisposableEffect
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.SideEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.platform.LocalView
|
||||
import androidx.compose.ui.window.DialogWindowProvider
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import co.electriccoin.zcash.ui.design.LocalSheetStateManager
|
||||
import co.electriccoin.zcash.ui.design.component.rememberModalBottomSheetState
|
||||
import co.electriccoin.zcash.ui.screen.accountlist.view.AccountListView
|
||||
import co.electriccoin.zcash.ui.screen.accountlist.viewmodel.AccountListViewModel
|
||||
|
@ -22,6 +24,13 @@ fun AndroidAccountList() {
|
|||
val state by viewModel.state.collectAsStateWithLifecycle()
|
||||
|
||||
val sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true)
|
||||
val sheetManager = LocalSheetStateManager.current
|
||||
DisposableEffect(sheetState) {
|
||||
sheetManager.onSheetOpened(sheetState)
|
||||
onDispose {
|
||||
sheetManager.onSheetDisposed(sheetState)
|
||||
}
|
||||
}
|
||||
|
||||
val parent = LocalView.current.parent
|
||||
|
||||
|
@ -43,13 +52,6 @@ fun AndroidAccountList() {
|
|||
sheetState.show()
|
||||
}
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
viewModel.hideBottomSheetRequest.collect {
|
||||
sheetState.hide()
|
||||
state?.onBottomSheetHidden?.invoke()
|
||||
}
|
||||
}
|
||||
|
||||
BackHandler {
|
||||
state?.onBack?.invoke()
|
||||
}
|
||||
|
|
|
@ -8,7 +8,6 @@ import co.electriccoin.zcash.ui.design.util.StringResource
|
|||
data class AccountListState(
|
||||
val items: List<AccountListItem>?,
|
||||
val isLoading: Boolean,
|
||||
val onBottomSheetHidden: () -> Unit,
|
||||
val addWalletButton: ButtonState?,
|
||||
val onBack: () -> Unit,
|
||||
)
|
||||
|
|
|
@ -270,7 +270,6 @@ private fun Preview() =
|
|||
)
|
||||
),
|
||||
isLoading = false,
|
||||
onBottomSheetHidden = {},
|
||||
onBack = {},
|
||||
addWalletButton = ButtonState(stringRes("Connect Hardware Wallet"))
|
||||
),
|
||||
|
@ -315,7 +314,6 @@ private fun HardwareWalletAddedPreview() =
|
|||
),
|
||||
),
|
||||
isLoading = false,
|
||||
onBottomSheetHidden = {},
|
||||
onBack = {},
|
||||
addWalletButton = null
|
||||
),
|
||||
|
|
|
@ -19,10 +19,8 @@ import co.electriccoin.zcash.ui.screen.accountlist.model.AccountListState
|
|||
import co.electriccoin.zcash.ui.screen.accountlist.model.ZashiAccountListItemState
|
||||
import co.electriccoin.zcash.ui.screen.addressbook.viewmodel.ADDRESS_MAX_LENGTH
|
||||
import co.electriccoin.zcash.ui.screen.connectkeystone.ConnectKeystone
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.WhileSubscribed
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
import kotlinx.coroutines.launch
|
||||
|
@ -32,10 +30,6 @@ class AccountListViewModel(
|
|||
private val selectWalletAccount: SelectWalletAccountUseCase,
|
||||
private val navigationRouter: NavigationRouter,
|
||||
) : ViewModel() {
|
||||
val hideBottomSheetRequest = MutableSharedFlow<Unit>()
|
||||
|
||||
private val bottomSheetHiddenResponse = MutableSharedFlow<Unit>()
|
||||
|
||||
@Suppress("SpreadOperator")
|
||||
val state =
|
||||
getWalletAccounts.observe().map { accounts ->
|
||||
|
@ -77,7 +71,6 @@ class AccountListViewModel(
|
|||
AccountListState(
|
||||
items = items,
|
||||
isLoading = accounts == null,
|
||||
onBottomSheetHidden = ::onBottomSheetHidden,
|
||||
onBack = ::onBack,
|
||||
addWalletButton =
|
||||
ButtonState(
|
||||
|
@ -94,35 +87,14 @@ class AccountListViewModel(
|
|||
)
|
||||
|
||||
private fun onShowKeystonePromoClicked() =
|
||||
viewModelScope.launch {
|
||||
hideBottomSheet()
|
||||
navigationRouter.replace(ExternalUrl("https://keyst.one/shop/products/keystone-3-pro?discount=Zashi"))
|
||||
}
|
||||
|
||||
private suspend fun hideBottomSheet() {
|
||||
hideBottomSheetRequest.emit(Unit)
|
||||
bottomSheetHiddenResponse.first()
|
||||
}
|
||||
|
||||
private fun onBottomSheetHidden() =
|
||||
viewModelScope.launch {
|
||||
bottomSheetHiddenResponse.emit(Unit)
|
||||
}
|
||||
|
||||
private fun onAccountClicked(account: WalletAccount) =
|
||||
viewModelScope.launch {
|
||||
selectWalletAccount(account) { hideBottomSheet() }
|
||||
selectWalletAccount(account)
|
||||
}
|
||||
|
||||
private fun onAddWalletButtonClicked() =
|
||||
viewModelScope.launch {
|
||||
hideBottomSheet()
|
||||
navigationRouter.forward(ConnectKeystone)
|
||||
}
|
||||
private fun onAddWalletButtonClicked() = navigationRouter.forward(ConnectKeystone)
|
||||
|
||||
private fun onBack() =
|
||||
viewModelScope.launch {
|
||||
hideBottomSheet()
|
||||
navigationRouter.back()
|
||||
}
|
||||
private fun onBack() = navigationRouter.back()
|
||||
}
|
||||
|
|
|
@ -4,12 +4,14 @@ import android.view.WindowManager
|
|||
import androidx.activity.compose.BackHandler
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.DisposableEffect
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.SideEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.platform.LocalView
|
||||
import androidx.compose.ui.window.DialogWindowProvider
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import co.electriccoin.zcash.ui.design.LocalSheetStateManager
|
||||
import co.electriccoin.zcash.ui.design.component.rememberModalBottomSheetState
|
||||
import kotlinx.serialization.Serializable
|
||||
import org.koin.androidx.compose.koinViewModel
|
||||
|
@ -19,6 +21,13 @@ import org.koin.core.parameter.parametersOf
|
|||
@Composable
|
||||
fun AndroidDialogIntegrations() {
|
||||
val sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true)
|
||||
val sheetManager = LocalSheetStateManager.current
|
||||
DisposableEffect(sheetState) {
|
||||
sheetManager.onSheetOpened(sheetState)
|
||||
onDispose {
|
||||
sheetManager.onSheetDisposed(sheetState)
|
||||
}
|
||||
}
|
||||
val parent = LocalView.current.parent
|
||||
val viewModel = koinViewModel<IntegrationsViewModel> { parametersOf(true) }
|
||||
val state by viewModel.state.collectAsStateWithLifecycle()
|
||||
|
@ -44,13 +53,6 @@ fun AndroidDialogIntegrations() {
|
|||
LaunchedEffect(Unit) {
|
||||
sheetState.show()
|
||||
}
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
viewModel.hideBottomSheetRequest.collect {
|
||||
sheetState.hide()
|
||||
state?.onBottomSheetHidden?.invoke()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -132,7 +132,6 @@ private fun IntegrationSettings() =
|
|||
onClick = {}
|
||||
),
|
||||
),
|
||||
onBottomSheetHidden = {}
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
|
@ -8,5 +8,4 @@ data class IntegrationsState(
|
|||
val disabledInfo: StringResource?,
|
||||
val onBack: () -> Unit,
|
||||
val items: ImmutableList<ZashiListItemState>,
|
||||
val onBottomSheetHidden: () -> Unit,
|
||||
)
|
||||
|
|
|
@ -197,7 +197,6 @@ private fun IntegrationSettings() =
|
|||
onClick = {}
|
||||
),
|
||||
),
|
||||
onBottomSheetHidden = {}
|
||||
),
|
||||
topAppBarSubTitleState = TopAppBarSubTitleState.None,
|
||||
)
|
||||
|
|
|
@ -25,11 +25,9 @@ import co.electriccoin.zcash.ui.design.util.stringRes
|
|||
import co.electriccoin.zcash.ui.screen.connectkeystone.ConnectKeystone
|
||||
import co.electriccoin.zcash.ui.screen.flexa.Flexa
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.WhileSubscribed
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
import kotlinx.coroutines.launch
|
||||
|
@ -45,10 +43,6 @@ class IntegrationsViewModel(
|
|||
private val navigationRouter: NavigationRouter,
|
||||
private val navigateToCoinbase: NavigateToCoinbaseUseCase,
|
||||
) : ViewModel() {
|
||||
val hideBottomSheetRequest = MutableSharedFlow<Unit>()
|
||||
|
||||
private val bottomSheetHiddenResponse = MutableSharedFlow<Unit>()
|
||||
|
||||
private val isRestoring = getWalletRestoringState.observe().map { it == WalletRestoringState.RESTORING }
|
||||
|
||||
val state =
|
||||
|
@ -123,42 +117,24 @@ class IntegrationsViewModel(
|
|||
onClick = ::onConnectKeystoneClick
|
||||
).takeIf { keystoneStatus != UNAVAILABLE },
|
||||
).toImmutableList(),
|
||||
onBottomSheetHidden = ::onBottomSheetHidden
|
||||
)
|
||||
|
||||
private fun onBack() = navigationRouter.back()
|
||||
|
||||
private suspend fun hideBottomSheet() {
|
||||
if (isDialog) {
|
||||
hideBottomSheetRequest.emit(Unit)
|
||||
bottomSheetHiddenResponse.first()
|
||||
}
|
||||
}
|
||||
|
||||
private fun onBottomSheetHidden() =
|
||||
viewModelScope.launch {
|
||||
bottomSheetHiddenResponse.emit(Unit)
|
||||
}
|
||||
|
||||
private fun onBuyWithCoinbaseClicked() =
|
||||
viewModelScope.launch {
|
||||
hideBottomSheet()
|
||||
navigateToCoinbase(isDialog)
|
||||
}
|
||||
|
||||
private fun onConnectKeystoneClick() =
|
||||
viewModelScope.launch {
|
||||
hideBottomSheet()
|
||||
navigationRouter.replace(ConnectKeystone)
|
||||
}
|
||||
|
||||
private fun onFlexaClicked() =
|
||||
viewModelScope.launch {
|
||||
private fun onFlexaClicked() {
|
||||
if (isDialog) {
|
||||
hideBottomSheet()
|
||||
navigationRouter.replace(Flexa)
|
||||
} else {
|
||||
hideBottomSheet()
|
||||
navigationRouter.forward(Flexa)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,7 +6,6 @@ import android.content.Context
|
|||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.platform.LocalFocusManager
|
||||
import androidx.compose.ui.window.DialogProperties
|
||||
import androidx.navigation.compose.NavHost
|
||||
import androidx.navigation.compose.composable
|
||||
|
@ -27,6 +26,8 @@ import co.electriccoin.zcash.ui.common.compose.LocalNavController
|
|||
import co.electriccoin.zcash.ui.common.model.OnboardingState
|
||||
import co.electriccoin.zcash.ui.common.model.WalletRestoringState
|
||||
import co.electriccoin.zcash.ui.common.viewmodel.WalletViewModel
|
||||
import co.electriccoin.zcash.ui.design.LocalKeyboardManager
|
||||
import co.electriccoin.zcash.ui.design.LocalSheetStateManager
|
||||
import co.electriccoin.zcash.ui.design.animation.ScreenAnimation.enterTransition
|
||||
import co.electriccoin.zcash.ui.design.animation.ScreenAnimation.exitTransition
|
||||
import co.electriccoin.zcash.ui.design.animation.ScreenAnimation.popEnterTransition
|
||||
|
@ -51,19 +52,23 @@ fun MainActivity.OnboardingNavigation() {
|
|||
val activity = LocalActivity.current
|
||||
val navigationRouter = koinInject<NavigationRouter>()
|
||||
val navController = LocalNavController.current
|
||||
val focusManager = LocalFocusManager.current
|
||||
val keyboardManager = LocalKeyboardManager.current
|
||||
val flexaViewModel = koinViewModel<FlexaViewModel>()
|
||||
val sheetStateManager = LocalSheetStateManager.current
|
||||
|
||||
val navigator: Navigator =
|
||||
remember(
|
||||
navController,
|
||||
flexaViewModel,
|
||||
focusManager
|
||||
keyboardManager,
|
||||
sheetStateManager
|
||||
) {
|
||||
NavigatorImpl(
|
||||
activity = this@OnboardingNavigation,
|
||||
navController = navController,
|
||||
flexaViewModel = flexaViewModel,
|
||||
focusManager = focusManager
|
||||
keyboardManager = keyboardManager,
|
||||
sheetStateManager = sheetStateManager
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -3,12 +3,14 @@ package co.electriccoin.zcash.ui.screen.restore.info
|
|||
import android.view.WindowManager
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.DisposableEffect
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.SideEffect
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.ui.platform.LocalView
|
||||
import androidx.compose.ui.window.DialogWindowProvider
|
||||
import co.electriccoin.zcash.ui.NavigationRouter
|
||||
import co.electriccoin.zcash.ui.design.LocalSheetStateManager
|
||||
import co.electriccoin.zcash.ui.design.component.rememberModalBottomSheetState
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.serialization.Serializable
|
||||
|
@ -27,6 +29,14 @@ fun AndroidSeedInfo() {
|
|||
}
|
||||
|
||||
val sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true)
|
||||
val sheetManager = LocalSheetStateManager.current
|
||||
|
||||
DisposableEffect(sheetState) {
|
||||
sheetManager.onSheetOpened(sheetState)
|
||||
onDispose {
|
||||
sheetManager.onSheetDisposed(sheetState)
|
||||
}
|
||||
}
|
||||
|
||||
SeedInfoView(
|
||||
sheetState = sheetState,
|
||||
|
@ -34,14 +44,14 @@ fun AndroidSeedInfo() {
|
|||
SeedInfoState(
|
||||
onBack = {
|
||||
scope.launch {
|
||||
sheetState.hide()
|
||||
// sheetState.hide()
|
||||
navigationRouter.back()
|
||||
}
|
||||
}
|
||||
),
|
||||
onDismissRequest = {
|
||||
scope.launch {
|
||||
sheetState.hide()
|
||||
// sheetState.hide()
|
||||
navigationRouter.back()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,11 +8,9 @@ import androidx.compose.foundation.layout.Box
|
|||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.WindowInsets
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.ime
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.width
|
||||
|
@ -25,17 +23,14 @@ import androidx.compose.material3.Surface
|
|||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.State
|
||||
import androidx.compose.runtime.derivedStateOf
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableIntStateOf
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberUpdatedState
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.platform.LocalFocusManager
|
||||
import androidx.compose.ui.platform.testTag
|
||||
import androidx.compose.ui.res.stringResource
|
||||
|
@ -44,6 +39,7 @@ import androidx.compose.ui.text.style.TextAlign
|
|||
import androidx.compose.ui.unit.dp
|
||||
import co.electriccoin.zcash.ui.R
|
||||
import co.electriccoin.zcash.ui.common.appbar.ZashiTopAppBarTags
|
||||
import co.electriccoin.zcash.ui.design.LocalKeyboardManager
|
||||
import co.electriccoin.zcash.ui.design.component.BlankBgScaffold
|
||||
import co.electriccoin.zcash.ui.design.component.ButtonState
|
||||
import co.electriccoin.zcash.ui.design.component.IconButtonState
|
||||
|
@ -76,13 +72,12 @@ fun RestoreSeedView(
|
|||
|
||||
val focusManager = LocalFocusManager.current
|
||||
var wasKeyboardOpen by remember { mutableStateOf(false) }
|
||||
val isKeyboardOpen by rememberKeyboardState()
|
||||
|
||||
val keyboardManager = LocalKeyboardManager.current
|
||||
val isKeyboardOpen = keyboardManager.isOpen
|
||||
LaunchedEffect(isKeyboardOpen) {
|
||||
if (wasKeyboardOpen && !isKeyboardOpen) {
|
||||
focusManager.clearFocus(true)
|
||||
}
|
||||
|
||||
wasKeyboardOpen = isKeyboardOpen
|
||||
}
|
||||
|
||||
|
@ -278,12 +273,6 @@ private fun getFilteredSuggestions(
|
|||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun rememberKeyboardState(): State<Boolean> {
|
||||
val isImeVisible = WindowInsets.ime.getBottom(LocalDensity.current) > 0
|
||||
return rememberUpdatedState(isImeVisible)
|
||||
}
|
||||
|
||||
@PreviewScreens
|
||||
@Composable
|
||||
private fun Preview() =
|
||||
|
|
|
@ -4,12 +4,14 @@ import android.view.WindowManager
|
|||
import androidx.activity.compose.BackHandler
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.DisposableEffect
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.SideEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.platform.LocalView
|
||||
import androidx.compose.ui.window.DialogWindowProvider
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import co.electriccoin.zcash.ui.design.LocalSheetStateManager
|
||||
import co.electriccoin.zcash.ui.design.component.rememberModalBottomSheetState
|
||||
import co.electriccoin.zcash.ui.screen.transactionfilters.view.TransactionFiltersView
|
||||
import co.electriccoin.zcash.ui.screen.transactionfilters.viewmodel.TransactionFiltersViewModel
|
||||
|
@ -22,6 +24,13 @@ fun AndroidTransactionFiltersList() {
|
|||
val state by viewModel.state.collectAsStateWithLifecycle()
|
||||
|
||||
val sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true)
|
||||
val sheetManager = LocalSheetStateManager.current
|
||||
DisposableEffect(sheetState) {
|
||||
sheetManager.onSheetOpened(sheetState)
|
||||
onDispose {
|
||||
sheetManager.onSheetDisposed(sheetState)
|
||||
}
|
||||
}
|
||||
|
||||
val parent = LocalView.current.parent
|
||||
|
||||
|
@ -40,13 +49,6 @@ fun AndroidTransactionFiltersList() {
|
|||
sheetState.show()
|
||||
}
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
viewModel.hideBottomSheetRequest.collect {
|
||||
sheetState.hide()
|
||||
state?.onBottomSheetHidden?.invoke()
|
||||
}
|
||||
}
|
||||
|
||||
BackHandler(state != null) {
|
||||
state?.onBack?.invoke()
|
||||
}
|
||||
|
|
|
@ -55,14 +55,12 @@ object TransactionFiltersStateFixture {
|
|||
|
||||
fun new(
|
||||
onBack: () -> Unit = {},
|
||||
onBottomSheetHidden: () -> Unit = {},
|
||||
filters: List<TransactionFilterState> = FILTERS,
|
||||
primaryButtonState: ButtonState = PRIMARY_BUTTON_STATE,
|
||||
secondaryButtonState: ButtonState = SECONDARY_BUTTON_STATE
|
||||
) = TransactionFiltersState(
|
||||
filters = filters,
|
||||
onBack = onBack,
|
||||
onBottomSheetHidden = onBottomSheetHidden,
|
||||
primaryButton = primaryButtonState,
|
||||
secondaryButton = secondaryButtonState
|
||||
)
|
||||
|
|
|
@ -6,7 +6,6 @@ import co.electriccoin.zcash.ui.design.util.StringResource
|
|||
data class TransactionFiltersState(
|
||||
val filters: List<TransactionFilterState>,
|
||||
val onBack: () -> Unit,
|
||||
val onBottomSheetHidden: () -> Unit,
|
||||
val primaryButton: ButtonState,
|
||||
val secondaryButton: ButtonState
|
||||
)
|
||||
|
|
|
@ -19,26 +19,19 @@ import co.electriccoin.zcash.ui.design.util.stringRes
|
|||
import co.electriccoin.zcash.ui.screen.transactionfilters.model.TransactionFilterState
|
||||
import co.electriccoin.zcash.ui.screen.transactionfilters.model.TransactionFiltersState
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.WhileSubscribed
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.flow.mapLatest
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
import kotlinx.coroutines.flow.update
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
internal class TransactionFiltersViewModel(
|
||||
private val navigationRouter: NavigationRouter,
|
||||
getTransactionFilters: GetTransactionFiltersUseCase,
|
||||
private val applyTransactionFilters: ApplyTransactionFiltersUseCase,
|
||||
) : ViewModel() {
|
||||
val hideBottomSheetRequest = MutableSharedFlow<Unit>()
|
||||
|
||||
private val bottomSheetHiddenResponse = MutableSharedFlow<Unit>()
|
||||
|
||||
private val selectedFilters = MutableStateFlow(getTransactionFilters())
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
|
@ -87,7 +80,6 @@ internal class TransactionFiltersViewModel(
|
|||
}
|
||||
},
|
||||
onBack = ::onBack,
|
||||
onBottomSheetHidden = ::onBottomSheetHidden,
|
||||
primaryButton =
|
||||
ButtonState(
|
||||
text = stringRes(R.string.transaction_filters_btn_apply),
|
||||
|
@ -100,9 +92,9 @@ internal class TransactionFiltersViewModel(
|
|||
),
|
||||
)
|
||||
}.stateIn(
|
||||
viewModelScope,
|
||||
SharingStarted.WhileSubscribed(ANDROID_STATE_FLOW_TIMEOUT),
|
||||
null
|
||||
scope = viewModelScope,
|
||||
started = SharingStarted.WhileSubscribed(ANDROID_STATE_FLOW_TIMEOUT),
|
||||
initialValue = null
|
||||
)
|
||||
|
||||
private fun onTransactionFilterClicked(filter: TransactionFilter) {
|
||||
|
@ -115,29 +107,9 @@ internal class TransactionFiltersViewModel(
|
|||
}
|
||||
}
|
||||
|
||||
private suspend fun hideBottomSheet() {
|
||||
hideBottomSheetRequest.emit(Unit)
|
||||
bottomSheetHiddenResponse.first()
|
||||
}
|
||||
private fun onBack() = navigationRouter.back()
|
||||
|
||||
private fun onBottomSheetHidden() =
|
||||
viewModelScope.launch {
|
||||
bottomSheetHiddenResponse.emit(Unit)
|
||||
}
|
||||
private fun onApplyTransactionFiltersClick() = applyTransactionFilters(selectedFilters.value)
|
||||
|
||||
private fun onBack() =
|
||||
viewModelScope.launch {
|
||||
hideBottomSheet()
|
||||
navigationRouter.back()
|
||||
}
|
||||
|
||||
private fun onApplyTransactionFiltersClick() =
|
||||
viewModelScope.launch {
|
||||
applyTransactionFilters(selectedFilters.value) { hideBottomSheet() }
|
||||
}
|
||||
|
||||
private fun onResetTransactionFiltersClick() =
|
||||
viewModelScope.launch {
|
||||
selectedFilters.update { emptyList() }
|
||||
}
|
||||
private fun onResetTransactionFiltersClick() = selectedFilters.update { emptyList() }
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ import androidx.activity.compose.BackHandler
|
|||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.SheetValue.Expanded
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.DisposableEffect
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.SideEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
|
@ -12,6 +13,7 @@ import androidx.compose.runtime.snapshotFlow
|
|||
import androidx.compose.ui.platform.LocalView
|
||||
import androidx.compose.ui.window.DialogWindowProvider
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import co.electriccoin.zcash.ui.design.LocalSheetStateManager
|
||||
import co.electriccoin.zcash.ui.design.component.rememberModalBottomSheetState
|
||||
import co.electriccoin.zcash.ui.screen.transactionnote.view.TransactionNoteView
|
||||
import co.electriccoin.zcash.ui.screen.transactionnote.viewmodel.TransactionNoteViewModel
|
||||
|
@ -26,7 +28,13 @@ fun AndroidTransactionNote(transactionNote: TransactionNote) {
|
|||
val state by viewModel.state.collectAsStateWithLifecycle()
|
||||
|
||||
val sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true)
|
||||
|
||||
val sheetManager = LocalSheetStateManager.current
|
||||
DisposableEffect(sheetState) {
|
||||
sheetManager.onSheetOpened(sheetState)
|
||||
onDispose {
|
||||
sheetManager.onSheetDisposed(sheetState)
|
||||
}
|
||||
}
|
||||
val parent = LocalView.current.parent
|
||||
|
||||
SideEffect {
|
||||
|
@ -52,13 +60,6 @@ fun AndroidTransactionNote(transactionNote: TransactionNote) {
|
|||
}
|
||||
}
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
viewModel.hideBottomSheetRequest.collect {
|
||||
sheetState.hide()
|
||||
state.onBottomSheetHidden()
|
||||
}
|
||||
}
|
||||
|
||||
BackHandler {
|
||||
state.onBack()
|
||||
}
|
||||
|
|
|
@ -7,7 +7,6 @@ import co.electriccoin.zcash.ui.design.util.StyledStringResource
|
|||
|
||||
data class TransactionNoteState(
|
||||
val onBack: () -> Unit,
|
||||
val onBottomSheetHidden: () -> Unit,
|
||||
val title: StringResource,
|
||||
val note: TextFieldState,
|
||||
val noteCharacters: StyledStringResource,
|
||||
|
|
|
@ -137,7 +137,6 @@ private fun Preview() =
|
|||
state =
|
||||
TransactionNoteState(
|
||||
onBack = {},
|
||||
onBottomSheetHidden = {},
|
||||
title = stringRes("Title"),
|
||||
note = TextFieldState(stringRes("")) {},
|
||||
noteCharacters =
|
||||
|
|
|
@ -15,13 +15,11 @@ import co.electriccoin.zcash.ui.design.util.StyledStringResource
|
|||
import co.electriccoin.zcash.ui.design.util.stringRes
|
||||
import co.electriccoin.zcash.ui.screen.transactionnote.TransactionNote
|
||||
import co.electriccoin.zcash.ui.screen.transactionnote.model.TransactionNoteState
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.WhileSubscribed
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
import kotlinx.coroutines.flow.update
|
||||
import kotlinx.coroutines.launch
|
||||
|
@ -33,10 +31,6 @@ internal class TransactionNoteViewModel(
|
|||
private val createOrUpdateTransactionNote: CreateOrUpdateTransactionNoteUseCase,
|
||||
private val deleteTransactionNote: DeleteTransactionNoteUseCase
|
||||
) : ViewModel() {
|
||||
val hideBottomSheetRequest = MutableSharedFlow<Unit>()
|
||||
|
||||
private val bottomSheetHiddenResponse = MutableSharedFlow<Unit>()
|
||||
|
||||
private val noteText = MutableStateFlow("")
|
||||
private val foundNote = MutableStateFlow<String?>(null)
|
||||
|
||||
|
@ -54,12 +48,9 @@ internal class TransactionNoteViewModel(
|
|||
foundNote: String?
|
||||
): TransactionNoteState {
|
||||
val noteTextNormalized = noteText.trim()
|
||||
|
||||
val isNoteTextTooLong = noteText.length > MAX_NOTE_LENGTH
|
||||
|
||||
return TransactionNoteState(
|
||||
onBack = ::onBack,
|
||||
onBottomSheetHidden = ::onBottomSheetHidden,
|
||||
title =
|
||||
if (foundNote == null) {
|
||||
stringRes(R.string.transaction_note_add_note_title)
|
||||
|
@ -107,37 +98,19 @@ internal class TransactionNoteViewModel(
|
|||
|
||||
private fun onAddOrUpdateNoteClick() =
|
||||
viewModelScope.launch {
|
||||
createOrUpdateTransactionNote(txId = transactionNote.txId, note = noteText.value) {
|
||||
hideBottomSheet()
|
||||
}
|
||||
createOrUpdateTransactionNote(txId = transactionNote.txId, note = noteText.value)
|
||||
}
|
||||
|
||||
private fun onDeleteNoteClick() =
|
||||
viewModelScope.launch {
|
||||
deleteTransactionNote(transactionNote.txId) {
|
||||
hideBottomSheet()
|
||||
}
|
||||
deleteTransactionNote(transactionNote.txId)
|
||||
}
|
||||
|
||||
private fun onNoteTextChanged(newValue: String) {
|
||||
noteText.update { newValue }
|
||||
}
|
||||
|
||||
private suspend fun hideBottomSheet() {
|
||||
hideBottomSheetRequest.emit(Unit)
|
||||
bottomSheetHiddenResponse.first()
|
||||
}
|
||||
|
||||
private fun onBottomSheetHidden() =
|
||||
viewModelScope.launch {
|
||||
bottomSheetHiddenResponse.emit(Unit)
|
||||
}
|
||||
|
||||
private fun onBack() =
|
||||
viewModelScope.launch {
|
||||
hideBottomSheet()
|
||||
navigationRouter.back()
|
||||
}
|
||||
private fun onBack() = navigationRouter.back()
|
||||
}
|
||||
|
||||
private const val MAX_NOTE_LENGTH = 90
|
||||
|
|
Loading…
Reference in New Issue