Implemented show floating action menu with options Add account and New money transfer

This commit is contained in:
dankito 2024-09-24 02:48:35 +02:00
parent 18ea0e35f1
commit 7275d60f82
6 changed files with 139 additions and 8 deletions

View File

@ -0,0 +1,99 @@
package net.codinux.banking.ui.appskeleton
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material.*
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Add
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.shadow
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import net.codinux.banking.ui.config.DI
import net.codinux.banking.ui.config.Style.FabMenuSpacing
import net.codinux.banking.ui.config.Style.FabSize
import net.codinux.banking.ui.config.Style.SmallFabSize
import net.codinux.banking.ui.model.ShowTransferMoneyDialogData
private val uiState = DI.uiState
@Composable
fun FloatingActionMenu(
showFloatingActionMenu: Boolean,
menuItemClicked: () -> Unit
) {
val fabVisibilityAnimation = animateFloatAsState(targetValue = if (showFloatingActionMenu) 1f else 0f)
val bottomPadding = FabSize + FabSize / 2
val accountsThatSupportMoneyTransfer = uiState.accountsThatSupportMoneyTransfer.collectAsState().value
val coroutineScope = rememberCoroutineScope()
fun handleClick(action: () -> Unit) {
menuItemClicked()
coroutineScope.launch {
delay(50)
action()
}
}
if (fabVisibilityAnimation.value > 0) {
Box(Modifier.fillMaxSize().padding(bottom = bottomPadding, end = 12.dp), contentAlignment = Alignment.BottomEnd) {
Column(Modifier, horizontalAlignment = Alignment.End) {
FloatingActionMenuItem("Überweisung", "Neue Überweisung", enabled = accountsThatSupportMoneyTransfer.isNotEmpty()) {
handleClick {
uiState.showTransferMoneyDialogData.value = ShowTransferMoneyDialogData()
}
}
FloatingActionMenuItem("Konto", "Neues Konto hinzufügen") {
handleClick {
uiState.showAddAccountDialog.value = true
}
}
}
}
}
}
@Composable
fun FloatingActionMenuItem(
label: String,
contentDescription: String,
enabled: Boolean = true,
onClick: () -> Unit
) {
Row(verticalAlignment = Alignment.CenterVertically, modifier = Modifier.padding(bottom = FabMenuSpacing).clickable(enabled) { onClick() }) {
Text(label, fontSize = 16.sp, color = contentColorFor(MaterialTheme.colors.secondary).copy(if (enabled) 1f else ContentAlpha.disabled), modifier = Modifier.padding(end = 8.dp).background(MaterialTheme.colors.secondary).shadow(20.dp).padding(horizontal = 20.dp, vertical = 4.dp)) // the same background color as the FAB
FloatingActionButton(
shape = CircleShape,
elevation = FloatingActionButtonDefaults.elevation(defaultElevation = 20.dp),
modifier = Modifier.padding(end = (FabSize - SmallFabSize) / 2).size(SmallFabSize),
onClick = {
if (enabled) {
onClick()
}
}
) {
Icon(Icons.Filled.Add, contentDescription = contentDescription, tint = LocalContentColor.current.copy(if (enabled) LocalContentAlpha.current else ContentAlpha.disabled))
}
}
}

View File

@ -56,7 +56,9 @@ private val VerticalSpacing = 8.dp
fun SideMenuContent() {
val drawerState = uiState.drawerState.collectAsState().value
val accounts = uiState.banks.collectAsState().value
val accounts = uiState.accounts.collectAsState().value
val accountsThatSupportMoneyTransfer = uiState.accountsThatSupportMoneyTransfer.collectAsState().value
val coroutineScope = rememberCoroutineScope()
@ -97,7 +99,7 @@ fun SideMenuContent() {
}
}
if (accounts.isNotEmpty()) {
if (accountsThatSupportMoneyTransfer.isNotEmpty()) {
Spacer(Modifier.height(VerticalSpacing))
NavigationMenuItem(itemModifier, "Neue Überweisung", textColor, horizontalPadding = ItemHorizontalPadding,

View File

@ -19,6 +19,15 @@ object Style {
val ListItemHeaderWeight = FontWeight.Medium // couldn't believe it, the FontWeights look different on Desktop and Android
val FabSize = 56.dp
val SmallFabSize = 46.dp
val FabSpacing = 16.dp
val FabMenuSpacing = FabSpacing / 2
val DividerThickness = 1.dp
}

View File

@ -42,8 +42,7 @@ fun TransferMoneyDialog(
val accountsToBank = banks.sortedBy { it.displayIndex }
.flatMap { bank -> bank.accountsSorted.map { it to bank } }.toMap()
val accountsSupportingTransferringMoney = banks.flatMap { it.accounts }
.filter { it.supportsMoneyTransfer }
val accountsSupportingTransferringMoney = uiState.accountsThatSupportMoneyTransfer.collectAsState().value
if (accountsSupportingTransferringMoney.isEmpty()) {
uiState.applicationErrorOccurred(ErroneousAction.TransferMoney, "Keines Ihrer Konten unterstützt das Überweisen von Geld")

View File

@ -6,6 +6,7 @@ import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material.*
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Add
import androidx.compose.material.icons.filled.Close
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
@ -30,6 +31,8 @@ fun MainScreen() {
val fabPositionAdjustment = if (isMobile) 44.dp // FabSpacing = 16.dp + FAB height (= 56.dp) / 2
else (-10).dp
var showFloatingActionMenu by remember { mutableStateOf(false) }
val desktopDrawerWidth = 350.dp
val uiState = DI.uiState
@ -51,9 +54,13 @@ fun MainScreen() {
FloatingActionButton(
shape = CircleShape,
modifier = Modifier.offset(x = 4.dp, y = fabPositionAdjustment),
onClick = { uiState.showAddAccountDialog.value = true }
onClick = { showFloatingActionMenu = !showFloatingActionMenu }
) {
Icon(Icons.Filled.Add, contentDescription = "Add a bank account")
if (showFloatingActionMenu) {
Icon(Icons.Filled.Close, contentDescription = "Menü zum Hinzufügen eines Kontos, für eine neue Überweisung, ... verstecken")
} else {
Icon(Icons.Filled.Add, contentDescription = "Zeigt ein Menü zum Hinzufügen eines Kontos, für eine neue Überweisung, ... an")
}
}
},
drawerContent = { if (isMobile) SideMenuContent() else null },
@ -85,6 +92,9 @@ fun MainScreen() {
}
}
FloatingActionMenu(showFloatingActionMenu) { showFloatingActionMenu = false }
if (showFilterBar.value) {
FilterBar()
}

View File

@ -3,8 +3,8 @@ package net.codinux.banking.ui.state
import androidx.compose.material.DrawerState
import androidx.compose.material.DrawerValue
import androidx.lifecycle.ViewModel
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.flow.*
import net.codinux.banking.client.model.tan.EnterTanResult
import net.codinux.banking.client.model.tan.TanChallenge
import net.codinux.banking.persistence.entities.HoldingEntity
@ -25,6 +25,18 @@ class UiState : ViewModel() {
val banks = MutableStateFlow<List<BankAccessEntity>>(emptyList())
val accounts = banks.map { it.flatMap { it.accounts } }.stateIn(
scope = this.viewModelScope,
started = SharingStarted.Eagerly,
initialValue = emptyList()
)
val accountsThatSupportMoneyTransfer = accounts.map { it.filter { it.supportsMoneyTransfer } }.stateIn(
scope = this.viewModelScope,
started = SharingStarted.Eagerly,
initialValue = emptyList()
)
val transactions = MutableStateFlow<List<AccountTransactionViewModel>>(emptyList())
val holdings = MutableStateFlow<List<HoldingEntity>>(emptyList())