Coverage Summary for Class: AccountChooserContentKt (com.georgeci.moneysurfer.feature.account.picker)
| Class |
Class, %
|
Method, %
|
Branch, %
|
Line, %
|
Instruction, %
|
| AccountChooserContentKt |
0%
(0/1)
|
0%
(0/8)
|
0%
(0/46)
|
0%
(0/98)
|
0%
(0/944)
|
package com.georgeci.moneysurfer.feature.account.picker
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.material3.Icon
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.georgeci.moneysurfer.domain.primitives.AccountId
import com.georgeci.moneysurfer.uikit.atom.SurferCard
import com.georgeci.moneysurfer.uikit.atom.SurferIconBubble
import com.georgeci.moneysurfer.uikit.atom.SurferSelectionRadio
import com.georgeci.moneysurfer.uikit.components.SurferAddNewCard
import com.georgeci.moneysurfer.uikit.components.SurferBottomSheetContent
import com.georgeci.moneysurfer.uikit.components.base.SurferSplitAmount
import com.georgeci.moneysurfer.uikit.components.base.SurferSplitAmountTier
import com.georgeci.moneysurfer.uikit.icons.SurferIcons
import com.georgeci.moneysurfer.uikit.preview.SurferBottomSheetPreview
import com.georgeci.moneysurfer.uikit.semantics.SurferSemantics
import com.georgeci.moneysurfer.uikit.theme.AppTheme
internal data class AccountPickerRow(
val id: AccountId,
val name: String,
val subtitle: String,
val formattedBalance: String,
val icon: ImageVector,
)
/**
* Static content of the account-chooser bottom sheet. Renders the drag handle, header, total
* summary, the picker list, the dashed "Add new account" CTA and the transfer footer. The hosting
* [Composable] provides the surrounding `ModalBottomSheet` chrome and wires events through a
* ViewModel.
*
* [transferInsteadLabel] is null when the host has no transfer to offer — picking the second leg
* of a transfer, for instance, is already a transfer.
*/
@Suppress("LongParameterList")
@Composable
internal fun AccountChooserContent(
title: String,
summary: String,
accounts: List<AccountPickerRow>,
selectedId: AccountId?,
addNewLabel: String,
addNewSubtitle: String,
onSelect: (AccountId) -> Unit,
onAddNew: () -> Unit,
onClose: () -> Unit,
transferInsteadLabel: String? = null,
onTransferInstead: () -> Unit = {},
) {
SurferBottomSheetContent(
title = title,
subtitle = summary,
onClose = onClose,
) {
Column(
modifier = Modifier.padding(horizontal = AppTheme.spacing.default),
verticalArrangement = Arrangement.spacedBy(10.dp),
) {
accounts.forEach { row ->
AccountRow(
row = row,
selected = row.id == selectedId,
onClick = { onSelect(row.id) },
)
}
}
SurferAddNewCard(
label = addNewLabel,
subtitle = addNewSubtitle,
onClick = onAddNew,
modifier = Modifier.padding(horizontal = AppTheme.spacing.medium),
)
if (transferInsteadLabel != null) {
Row(
modifier = Modifier
.fillMaxWidth()
.clickable(onClick = onTransferInstead)
.padding(horizontal = 20.dp, vertical = 10.dp),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(AppTheme.spacing.small),
) {
Icon(
imageVector = SurferIcons.SwapHoriz,
contentDescription = SurferSemantics.Decorative,
tint = AppTheme.materialColors.primary,
modifier = Modifier.size(18.dp),
)
Text(
text = transferInsteadLabel,
style = AppTheme.typography.labelLarge,
color = AppTheme.materialColors.primary,
)
}
}
}
}
@Composable
private fun AccountRow(
row: AccountPickerRow,
selected: Boolean,
onClick: () -> Unit,
) {
SurferCard(
modifier = Modifier.fillMaxWidth(),
selected = selected,
onClick = onClick,
) {
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(AppTheme.spacing.medium),
) {
SurferIconBubble(icon = row.icon)
Column(modifier = Modifier.weight(1f)) {
Text(
text = row.name,
style = AppTheme.typography.titleSmall,
color = AppTheme.materialColors.onSurface,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
)
if (row.subtitle.isNotEmpty()) {
Text(
text = row.subtitle,
style = AppTheme.typography.bodySmall,
color = AppTheme.materialColors.onSurfaceVariant,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
)
}
}
SurferSplitAmount(
formattedAmount = row.formattedBalance,
tier = SurferSplitAmountTier.Body,
color = AppTheme.materialColors.onSurface,
signAlpha = 0.6f,
fractionAlpha = 0.5f,
)
Spacer(Modifier.size(4.dp))
SurferSelectionRadio(selected = selected)
}
}
}
private val previewRows = listOf(
AccountPickerRow(
id = AccountId("preview-acc-1"),
name = "Everyday",
subtitle = "EUR",
formattedBalance = "€2,480.32",
icon = SurferIcons.CreditCard,
),
AccountPickerRow(
id = AccountId("preview-acc-2"),
name = "Emergency Fund",
subtitle = "EUR",
formattedBalance = "€8,915.00",
icon = SurferIcons.Savings,
),
AccountPickerRow(
id = AccountId("preview-acc-3"),
name = "Cash wallet",
subtitle = "EUR",
formattedBalance = "€180.00",
icon = SurferIcons.Cash,
),
)
@Preview
@Composable
private fun AccountChooserContentPreview() {
SurferBottomSheetPreview {
AccountChooserContent(
title = "From account",
summary = "Total across 3 accounts · €11,575.32",
accounts = previewRows,
selectedId = AccountId("preview-acc-1"),
addNewLabel = "Add new account",
addNewSubtitle = "Bank, savings, cash or card",
onSelect = {},
onAddNew = {},
onClose = {},
transferInsteadLabel = "Transfer between accounts instead",
)
}
}
@Preview
@Composable
private fun AccountChooserContentEmptyPreview() {
SurferBottomSheetPreview {
AccountChooserContent(
title = "From account",
summary = "No accounts yet",
accounts = emptyList(),
selectedId = null,
addNewLabel = "Add new account",
addNewSubtitle = "Bank, savings, cash or card",
onSelect = {},
onAddNew = {},
onClose = {},
)
}
}