Implemented show floating action menu with options Add account and New money transfer
This commit is contained in:
parent
18ea0e35f1
commit
7275d60f82
|
@ -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))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -56,7 +56,9 @@ private val VerticalSpacing = 8.dp
|
||||||
fun SideMenuContent() {
|
fun SideMenuContent() {
|
||||||
val drawerState = uiState.drawerState.collectAsState().value
|
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()
|
val coroutineScope = rememberCoroutineScope()
|
||||||
|
|
||||||
|
@ -97,7 +99,7 @@ fun SideMenuContent() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (accounts.isNotEmpty()) {
|
if (accountsThatSupportMoneyTransfer.isNotEmpty()) {
|
||||||
Spacer(Modifier.height(VerticalSpacing))
|
Spacer(Modifier.height(VerticalSpacing))
|
||||||
|
|
||||||
NavigationMenuItem(itemModifier, "Neue Überweisung", textColor, horizontalPadding = ItemHorizontalPadding,
|
NavigationMenuItem(itemModifier, "Neue Überweisung", textColor, horizontalPadding = ItemHorizontalPadding,
|
||||||
|
|
|
@ -19,6 +19,15 @@ object Style {
|
||||||
val ListItemHeaderWeight = FontWeight.Medium // couldn't believe it, the FontWeights look different on Desktop and Android
|
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
|
val DividerThickness = 1.dp
|
||||||
|
|
||||||
}
|
}
|
|
@ -42,8 +42,7 @@ fun TransferMoneyDialog(
|
||||||
val accountsToBank = banks.sortedBy { it.displayIndex }
|
val accountsToBank = banks.sortedBy { it.displayIndex }
|
||||||
.flatMap { bank -> bank.accountsSorted.map { it to bank } }.toMap()
|
.flatMap { bank -> bank.accountsSorted.map { it to bank } }.toMap()
|
||||||
|
|
||||||
val accountsSupportingTransferringMoney = banks.flatMap { it.accounts }
|
val accountsSupportingTransferringMoney = uiState.accountsThatSupportMoneyTransfer.collectAsState().value
|
||||||
.filter { it.supportsMoneyTransfer }
|
|
||||||
|
|
||||||
if (accountsSupportingTransferringMoney.isEmpty()) {
|
if (accountsSupportingTransferringMoney.isEmpty()) {
|
||||||
uiState.applicationErrorOccurred(ErroneousAction.TransferMoney, "Keines Ihrer Konten unterstützt das Überweisen von Geld")
|
uiState.applicationErrorOccurred(ErroneousAction.TransferMoney, "Keines Ihrer Konten unterstützt das Überweisen von Geld")
|
||||||
|
|
|
@ -6,6 +6,7 @@ import androidx.compose.foundation.shape.CircleShape
|
||||||
import androidx.compose.material.*
|
import androidx.compose.material.*
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.filled.Add
|
import androidx.compose.material.icons.filled.Add
|
||||||
|
import androidx.compose.material.icons.filled.Close
|
||||||
import androidx.compose.runtime.*
|
import androidx.compose.runtime.*
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.unit.dp
|
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
|
val fabPositionAdjustment = if (isMobile) 44.dp // FabSpacing = 16.dp + FAB height (= 56.dp) / 2
|
||||||
else (-10).dp
|
else (-10).dp
|
||||||
|
|
||||||
|
var showFloatingActionMenu by remember { mutableStateOf(false) }
|
||||||
|
|
||||||
val desktopDrawerWidth = 350.dp
|
val desktopDrawerWidth = 350.dp
|
||||||
|
|
||||||
val uiState = DI.uiState
|
val uiState = DI.uiState
|
||||||
|
@ -51,9 +54,13 @@ fun MainScreen() {
|
||||||
FloatingActionButton(
|
FloatingActionButton(
|
||||||
shape = CircleShape,
|
shape = CircleShape,
|
||||||
modifier = Modifier.offset(x = 4.dp, y = fabPositionAdjustment),
|
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 },
|
drawerContent = { if (isMobile) SideMenuContent() else null },
|
||||||
|
@ -85,6 +92,9 @@ fun MainScreen() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
FloatingActionMenu(showFloatingActionMenu) { showFloatingActionMenu = false }
|
||||||
|
|
||||||
if (showFilterBar.value) {
|
if (showFilterBar.value) {
|
||||||
FilterBar()
|
FilterBar()
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,8 +3,8 @@ package net.codinux.banking.ui.state
|
||||||
import androidx.compose.material.DrawerState
|
import androidx.compose.material.DrawerState
|
||||||
import androidx.compose.material.DrawerValue
|
import androidx.compose.material.DrawerValue
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
import androidx.lifecycle.viewModelScope
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.*
|
||||||
import net.codinux.banking.client.model.tan.EnterTanResult
|
import net.codinux.banking.client.model.tan.EnterTanResult
|
||||||
import net.codinux.banking.client.model.tan.TanChallenge
|
import net.codinux.banking.client.model.tan.TanChallenge
|
||||||
import net.codinux.banking.persistence.entities.HoldingEntity
|
import net.codinux.banking.persistence.entities.HoldingEntity
|
||||||
|
@ -25,6 +25,18 @@ class UiState : ViewModel() {
|
||||||
|
|
||||||
val banks = MutableStateFlow<List<BankAccessEntity>>(emptyList())
|
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 transactions = MutableStateFlow<List<AccountTransactionViewModel>>(emptyList())
|
||||||
|
|
||||||
val holdings = MutableStateFlow<List<HoldingEntity>>(emptyList())
|
val holdings = MutableStateFlow<List<HoldingEntity>>(emptyList())
|
||||||
|
|
Loading…
Reference in New Issue