Refactor MakeTransferScreen to improve account selection using BottomSheetScaffold (#1874)

This commit is contained in:
Hekmatullah 2025-06-28 12:29:09 +01:00 committed by GitHub
parent df938aba4e
commit f8d584beaa
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 145 additions and 79 deletions

View File

@ -10,6 +10,7 @@
package org.mifospay.core.designsystem.component
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.ColumnScope
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.RowScope
import androidx.compose.foundation.layout.WindowInsets
@ -17,6 +18,9 @@ import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.imePadding
import androidx.compose.foundation.layout.navigationBarsPadding
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.BottomSheetDefaults
import androidx.compose.material3.BottomSheetScaffold
import androidx.compose.material3.BottomSheetScaffoldState
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.FabPosition
import androidx.compose.material3.FloatingActionButton
@ -25,14 +29,19 @@ import androidx.compose.material3.Scaffold
import androidx.compose.material3.ScaffoldDefaults
import androidx.compose.material3.SnackbarHost
import androidx.compose.material3.SnackbarHostState
import androidx.compose.material3.contentColorFor
import androidx.compose.material3.pulltorefresh.PullToRefreshDefaults
import androidx.compose.material3.pulltorefresh.pullToRefresh
import androidx.compose.material3.pulltorefresh.rememberPullToRefreshState
import androidx.compose.material3.rememberBottomSheetScaffoldState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
@OptIn(ExperimentalMaterial3Api::class)
@Composable
@ -152,6 +161,48 @@ fun MifosScaffold(
)
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun MifosBottomSheetScaffold(
sheetContent: @Composable ColumnScope.() -> Unit,
modifier: Modifier = Modifier,
scaffoldState: BottomSheetScaffoldState = rememberBottomSheetScaffoldState(),
sheetPeekHeight: Dp = BottomSheetDefaults.SheetPeekHeight,
sheetMaxWidth: Dp = BottomSheetDefaults.SheetMaxWidth,
sheetShape: Shape = BottomSheetDefaults.ExpandedShape,
sheetContainerColor: Color = BottomSheetDefaults.ContainerColor,
sheetContentColor: Color = contentColorFor(sheetContainerColor),
sheetTonalElevation: Dp = 0.dp,
sheetShadowElevation: Dp = BottomSheetDefaults.Elevation,
sheetDragHandle: @Composable (() -> Unit)? = { BottomSheetDefaults.DragHandle() },
sheetSwipeEnabled: Boolean = true,
topBar: @Composable (() -> Unit)? = null,
snackbarHost: @Composable (SnackbarHostState) -> Unit = { SnackbarHost(it) },
containerColor: Color = MaterialTheme.colorScheme.surface,
contentColor: Color = contentColorFor(containerColor),
content: @Composable (PaddingValues) -> Unit,
) {
BottomSheetScaffold(
sheetContent = sheetContent,
modifier = modifier,
scaffoldState = scaffoldState,
sheetPeekHeight = sheetPeekHeight,
sheetMaxWidth = sheetMaxWidth,
sheetShape = sheetShape,
sheetContainerColor = sheetContainerColor,
sheetContentColor = sheetContentColor,
sheetTonalElevation = sheetTonalElevation,
sheetShadowElevation = sheetShadowElevation,
sheetDragHandle = sheetDragHandle,
sheetSwipeEnabled = sheetSwipeEnabled,
topBar = topBar,
snackbarHost = snackbarHost,
containerColor = containerColor,
contentColor = contentColor,
content = content,
)
}
data class FloatingActionButtonContent(
val onClick: (() -> Unit),
val contentColor: Color,

View File

@ -15,4 +15,16 @@
<string name="feature_make_transfer_amount">Amount</string>
<string name="feature_make_transfer_transaction_success">Transaction successful</string>
<string name="feature_make_transfer_transaction_unable_to_process">Unable to process transfer</string>
<string name="feature_make_transfer_review_title">Review Transfer</string>
<string name="feature_make_transfer_description_label">Description</string>
<string name="feature_make_transfer_to_account">To Account</string>
<string name="feature_make_transfer_from_account">From Account</string>
<string name="feature_make_transfer_continue_button">Continue</string>
<string name="feature_make_transfer_oops_title">Oops!</string>
<string name="feature_make_transfer_no_accounts_found">No accounts found!</string>
<string name="feature_make_transfer_check_icon_description">check</string>
<string name="feature_make_transfer_badge_wallet">WALLET</string>
<string name="feature_make_transfer_badge_saving">SAVING</string>
<string name="feature_make_transfer_icon_selected_description">Selected account indicator</string>
</resources>

View File

@ -21,15 +21,16 @@ import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.CardDefaults
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.ListItem
import androidx.compose.material3.ListItemDefaults
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedCard
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
@ -39,15 +40,29 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import mobile_wallet.feature.make_transfer.generated.resources.Res
import mobile_wallet.feature.make_transfer.generated.resources.feature_make_transfer_amount
import mobile_wallet.feature.make_transfer.generated.resources.feature_make_transfer_badge_saving
import mobile_wallet.feature.make_transfer.generated.resources.feature_make_transfer_badge_wallet
import mobile_wallet.feature.make_transfer.generated.resources.feature_make_transfer_check_icon_description
import mobile_wallet.feature.make_transfer.generated.resources.feature_make_transfer_continue_button
import mobile_wallet.feature.make_transfer.generated.resources.feature_make_transfer_description_label
import mobile_wallet.feature.make_transfer.generated.resources.feature_make_transfer_from_account
import mobile_wallet.feature.make_transfer.generated.resources.feature_make_transfer_loading
import mobile_wallet.feature.make_transfer.generated.resources.feature_make_transfer_no_accounts_found
import mobile_wallet.feature.make_transfer.generated.resources.feature_make_transfer_oops_title
import mobile_wallet.feature.make_transfer.generated.resources.feature_make_transfer_review_title
import mobile_wallet.feature.make_transfer.generated.resources.feature_make_transfer_to_account
import org.jetbrains.compose.resources.stringResource
import org.koin.compose.viewmodel.koinViewModel
import org.mifospay.core.common.utils.maskString
import org.mifospay.core.designsystem.component.BasicDialogState
import org.mifospay.core.designsystem.component.LoadingDialogState
import org.mifospay.core.designsystem.component.MifosBasicDialog
import org.mifospay.core.designsystem.component.MifosBottomSheetScaffold
import org.mifospay.core.designsystem.component.MifosButton
import org.mifospay.core.designsystem.component.MifosLoadingDialog
import org.mifospay.core.designsystem.component.MifosLoadingWheel
import org.mifospay.core.designsystem.component.MifosScaffold
import org.mifospay.core.designsystem.component.MifosTextField
import org.mifospay.core.designsystem.component.MifosTopBar
import org.mifospay.core.designsystem.icon.MifosIcons
@ -91,6 +106,7 @@ internal fun MakeTransferScreen(
)
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
internal fun MakeTransferScreen(
state: MakeTransferState,
@ -99,16 +115,16 @@ internal fun MakeTransferScreen(
lazyListState: LazyListState = rememberLazyListState(),
onAction: (MakeTransferAction) -> Unit,
) {
MifosScaffold(
MifosBottomSheetScaffold(
topBar = {
MifosTopBar(
topBarTitle = "Review Transfer",
topBarTitle = stringResource(Res.string.feature_make_transfer_review_title),
backPress = {
onAction(MakeTransferAction.NavigateBack)
},
)
},
bottomBar = {
sheetContent = {
AccountListState(
state = accountState,
selected = remember(state) {
@ -118,6 +134,7 @@ internal fun MakeTransferScreen(
)
},
modifier = modifier,
sheetPeekHeight = 200.dp,
) { paddingValues ->
LazyColumn(
modifier = Modifier
@ -129,7 +146,7 @@ internal fun MakeTransferScreen(
) {
item {
MifosTextField(
label = "Amount",
label = stringResource(Res.string.feature_make_transfer_amount),
value = state.amount,
isError = !state.amountIsValid,
onValueChange = {
@ -140,7 +157,7 @@ internal fun MakeTransferScreen(
item {
MifosTextField(
label = "Description",
label = stringResource(Res.string.feature_make_transfer_description_label),
value = state.description,
isError = !state.descriptionIsValid,
onValueChange = {
@ -155,6 +172,15 @@ internal fun MakeTransferScreen(
modifier = Modifier,
)
}
item {
MifosButton(
onClick = { onAction(MakeTransferAction.InitiateTransfer) },
modifier = Modifier.fillMaxWidth(),
) {
Text(text = stringResource(Res.string.feature_make_transfer_continue_button))
}
}
}
}
}
@ -166,70 +192,46 @@ private fun AccountListState(
selected: (Account) -> Boolean,
onAction: (MakeTransferAction) -> Unit,
) {
Surface(
modifier = modifier.fillMaxWidth(),
shadowElevation = 2.dp,
tonalElevation = 2.dp,
shape = RoundedCornerShape(topStart = 12.dp, topEnd = 12.dp),
color = MaterialTheme.colorScheme.background,
Box(
modifier = modifier.fillMaxWidth()
.padding(16.dp),
contentAlignment = Alignment.Center,
) {
Column(
modifier = Modifier
.fillMaxWidth()
.padding(16.dp),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy(16.dp),
) {
Box(
modifier = Modifier.fillMaxWidth(),
contentAlignment = Alignment.Center,
) {
when (state) {
is ViewState.Loading -> {
Box(modifier = Modifier.fillMaxWidth()) {
MifosLoadingWheel(
contentDesc = "Loading",
modifier = Modifier
.align(Alignment.Center)
.padding(16.dp),
)
}
}
is ViewState.Empty -> {
EmptyContentScreen(
title = "Oops!",
subTitle = "No accounts found!",
)
}
is ViewState.Error -> {
EmptyContentScreen(
title = "Oops!",
subTitle = "No accounts found!",
iconTint = MaterialTheme.colorScheme.error,
)
}
is ViewState.Content -> {
AccountList(
accounts = state.data,
selected = selected,
onClick = {
onAction(MakeTransferAction.SelectAccount(it))
},
)
}
when (state) {
is ViewState.Loading -> {
Box(modifier = Modifier.fillMaxWidth()) {
MifosLoadingWheel(
contentDesc = stringResource(Res.string.feature_make_transfer_loading),
modifier = Modifier
.align(Alignment.Center)
.padding(16.dp),
)
}
}
MifosButton(
onClick = {
onAction(MakeTransferAction.InitiateTransfer)
},
modifier = Modifier.fillMaxWidth(),
) {
Text(text = "Continue")
is ViewState.Empty -> {
EmptyContentScreen(
title = stringResource(Res.string.feature_make_transfer_oops_title),
subTitle = stringResource(Res.string.feature_make_transfer_no_accounts_found),
)
}
is ViewState.Error -> {
EmptyContentScreen(
title = stringResource(Res.string.feature_make_transfer_oops_title),
subTitle = stringResource(Res.string.feature_make_transfer_no_accounts_found),
iconTint = MaterialTheme.colorScheme.error,
)
}
is ViewState.Content -> {
AccountList(
accounts = state.data,
selected = selected,
onClick = {
onAction(MakeTransferAction.SelectAccount(it))
},
)
}
}
}
@ -242,16 +244,17 @@ private fun AccountList(
modifier: Modifier = Modifier,
onClick: (Account) -> Unit,
) {
Column(
LazyColumn(
modifier = modifier.fillMaxWidth(),
verticalArrangement = Arrangement.spacedBy(16.dp),
) {
Text(
text = "From Account",
style = MaterialTheme.typography.labelLarge,
)
accounts.forEach { account ->
item {
Text(
text = stringResource(Res.string.feature_make_transfer_from_account),
style = MaterialTheme.typography.labelLarge,
)
}
items(items = accounts, key = { account -> account.id }) { account ->
AccountItem(
account = account,
selected = selected(account),
@ -300,7 +303,7 @@ private fun AccountItem(
} else {
MifosIcons.RadioButtonUnchecked
},
contentDescription = "check",
contentDescription = stringResource(Res.string.feature_make_transfer_check_icon_description),
tint = if (it) {
MaterialTheme.colorScheme.primary
} else {
@ -326,7 +329,7 @@ private fun ClientCard(
verticalArrangement = Arrangement.spacedBy(12.dp),
) {
Text(
text = "To Account",
text = stringResource(Res.string.feature_make_transfer_to_account),
style = MaterialTheme.typography.labelLarge,
)
@ -355,11 +358,11 @@ private fun ClientCard(
verticalAlignment = Alignment.CenterVertically,
) {
AccountBadge(
text = "WALLET",
text = stringResource(Res.string.feature_make_transfer_badge_wallet),
)
AccountBadge(
text = "SAVING",
text = stringResource(Res.string.feature_make_transfer_badge_saving),
borderColor = MaterialTheme.colorScheme.secondary,
)
}

View File

@ -1,4 +1,4 @@
package: name='org.mifospay' versionCode='1' versionName='2025.6.4-beta.0.10' platformBuildVersionName='15' platformBuildVersionCode='35' compileSdkVersion='35' compileSdkVersionCodename='15'
package: name='org.mifospay' versionCode='1' versionName='2025.6.5-beta.0.1' platformBuildVersionName='15' platformBuildVersionCode='35' compileSdkVersion='35' compileSdkVersionCodename='15'
sdkVersion:'26'
targetSdkVersion:'34'
uses-permission: name='android.permission.INTERNET'