Show snackbar with undo option. Don't jump back to calculator on history click.
All checks were successful
Build Android / build (push) Has been cancelled

This commit is contained in:
2023-08-20 22:20:45 +02:00
parent c8098fc904
commit 55a2293b6c
5 changed files with 114 additions and 34 deletions

View File

@@ -23,6 +23,8 @@ import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.ModalNavigationDrawer
import androidx.compose.material3.Scaffold
import androidx.compose.material3.SnackbarHost
import androidx.compose.material3.SnackbarHostState
import androidx.compose.material3.rememberDrawerState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
@@ -180,8 +182,11 @@ class MainActivity : AppCompatActivity(), ISettingsChangeListener {
) {
var topBarState by remember { mutableStateOf(TopBarState()) }
var snackbarHostState by remember { mutableStateOf(SnackbarHostState()) }
val scope = rememberCoroutineScope()
Scaffold(
snackbarHost = { SnackbarHost(snackbarHostState) },
floatingActionButton = {
if (showFab) {
FloatingActionButton(
@@ -245,7 +250,10 @@ class MainActivity : AppCompatActivity(), ISettingsChangeListener {
topBarState =
TopBarState(title = stringResource(R.string.menu_history)) { scope.launch { drawerState.open() } }
HistoryList(historyViewModel) { navController.navigate(Route.COUNTER) }
HistoryList(
historyViewModel,
snackbarHostState
) { navController.navigate(Route.COUNTER) }
}
composable(Route.SETTINGS) {
topBarState =

View File

@@ -3,6 +3,7 @@ package me.zobrist.tichucounter.ui.history
import androidx.compose.animation.animateColorAsState
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
@@ -13,7 +14,9 @@ import androidx.compose.foundation.layout.fillMaxSize
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.material.icons.Icons
import androidx.compose.material.icons.outlined.Delete
import androidx.compose.material.icons.outlined.DeleteForever
@@ -27,6 +30,9 @@ import androidx.compose.material3.DismissValue
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.SnackbarDuration
import androidx.compose.material3.SnackbarHostState
import androidx.compose.material3.SnackbarResult
import androidx.compose.material3.SwipeToDismiss
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
@@ -35,6 +41,7 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Alignment.Companion.CenterVertically
@@ -46,6 +53,7 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import kotlinx.coroutines.launch
import me.zobrist.tichucounter.R
import me.zobrist.tichucounter.data.GameWithScores
import me.zobrist.tichucounter.data.entity.Game
@@ -58,11 +66,15 @@ import java.util.Locale
@Composable
fun HistoryList(
viewModel: HistoryViewModel, navigateToCalculator: () -> Unit
viewModel: HistoryViewModel,
snackbarHostState: SnackbarHostState,
navigateToCalculator: () -> Unit,
) {
val scope = rememberCoroutineScope()
val lazyListState = rememberLazyListState()
var showDeleteDialog by remember { mutableStateOf(false) }
DeleteConfirmDialog(showDeleteDialog) {
showDeleteDialog = false
if (it) {
@@ -70,14 +82,48 @@ fun HistoryList(
}
}
val deletedMessage = stringResource(id = R.string.delete_success)
val deletedActionLabel = stringResource(id = R.string.undo_question)
val activatedMessage = stringResource(id = R.string.activated_success)
val activatedActionLabel = stringResource(id = R.string.to_calculator_question)
HistoryList(
viewModel.gameAndHistory,
{
viewModel.activateGame(it)
navigateToCalculator()
games = viewModel.gameAndHistory,
onOpenClicked = {
scope.launch {
viewModel.activateGame(it)
lazyListState.animateScrollToItem(0)
val result = snackbarHostState.showSnackbar(
message = activatedMessage,
actionLabel = activatedActionLabel,
duration = SnackbarDuration.Short
)
if (result == SnackbarResult.ActionPerformed) {
navigateToCalculator()
}
}
},
{ viewModel.deleteGame(it) },
{ showDeleteDialog = true },
onDeleteClicked = {
scope.launch {
viewModel.markToDelete(it)
val result = snackbarHostState.showSnackbar(
message = deletedMessage,
actionLabel = deletedActionLabel,
duration = SnackbarDuration.Short
)
if (result == SnackbarResult.Dismissed) {
viewModel.deleteGame(it)
} else {
viewModel.unmarkToDelete(it)
}
}
},
onDeleteAllClicked = { showDeleteDialog = true },
lazyListState = lazyListState
)
}
@@ -104,16 +150,17 @@ fun DeleteConfirmDialog(show: Boolean = true, onExecuted: (Boolean) -> Unit = {}
}
}
@OptIn(ExperimentalFoundationApi::class)
@Composable
fun HistoryList(
games: List<GameWithScores>,
onOpenClicked: (gameId: Long) -> Unit,
onDeleteClicked: (gameId: Long) -> Unit,
onDeleteAllClicked: () -> Unit
onDeleteAllClicked: () -> Unit,
lazyListState: LazyListState = LazyListState(),
) {
Row {
LazyColumn {
LazyColumn(state = lazyListState) {
item {
Text(
modifier = Modifier.padding(start = 10.dp, end = 10.dp),
@@ -122,7 +169,7 @@ fun HistoryList(
)
}
items(games.filter { it.game.active }) {
HistoryListItem(it)
HistoryListItem(it, Modifier.animateItemPlacement())
}
if (games.count() > 1) {
@@ -137,7 +184,9 @@ fun HistoryList(
items(items = games.filter { !it.game.active }, key = {
it.hashCode()
}) {
DismissibleHistoryListItem(it, onOpenClicked, onDeleteClicked)
DismissibleHistoryListItem(
it, onOpenClicked, onDeleteClicked, Modifier.animateItemPlacement()
)
}
item {
@@ -145,7 +194,8 @@ fun HistoryList(
modifier = Modifier
.padding(start = 4.dp, end = 4.dp, top = 10.dp)
.align(CenterVertically)
.fillMaxWidth(),
.fillMaxWidth()
.animateItemPlacement(),
onClick = { onDeleteAllClicked() }) {
Icon(imageVector = Icons.Outlined.DeleteForever, contentDescription = null)
Text(text = stringResource(id = R.string.deleteAll))
@@ -161,23 +211,24 @@ fun HistoryList(
fun DismissibleHistoryListItem(
game: GameWithScores,
onOpenClicked: (gameId: Long) -> Unit,
onDeleteClicked: (gameId: Long) -> Unit
onDeleteClicked: (gameId: Long) -> Unit,
modifier: Modifier = Modifier,
) {
val density = LocalDensity.current
val dismissState = rememberDismissState(
positionalThreshold = { with(density) { 100.dp.toPx() } },
confirmValueChange = {
if (it == DismissValue.DismissedToStart) {
onDeleteClicked(game.game.uid)
}
if (it == DismissValue.DismissedToEnd) {
onOpenClicked(game.game.uid)
}
true
})
val dismissState =
rememberDismissState(positionalThreshold = { with(density) { 100.dp.toPx() } },
confirmValueChange = {
if (it == DismissValue.DismissedToStart) {
onDeleteClicked(game.game.uid)
}
if (it == DismissValue.DismissedToEnd) {
onOpenClicked(game.game.uid)
}
true
})
SwipeToDismiss(state = dismissState, background = {
SwipeToDismiss(modifier = modifier, state = dismissState, background = {
val direction = dismissState.dismissDirection ?: return@SwipeToDismiss
val color by animateColorAsState(
when (dismissState.targetValue) {
@@ -209,8 +260,10 @@ fun DismissibleHistoryListItem(
.padding(horizontal = 20.dp),
contentAlignment = alignment
) {
Column(verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally) {
Column(
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
Icon(
icon, contentDescription = null, modifier = Modifier.scale(scale)
)
@@ -224,7 +277,7 @@ fun DismissibleHistoryListItem(
@Composable
fun HistoryListItem(
game: GameWithScores
game: GameWithScores, modifier: Modifier = Modifier
) {
val format =
DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.SHORT, Locale.getDefault())
@@ -240,7 +293,7 @@ fun HistoryListItem(
val totalScores = game.getTotalPoints()
Card(
modifier = Modifier
modifier = modifier
.fillMaxWidth()
.padding(all = 4.dp), colors = cardColor
) {
@@ -284,5 +337,5 @@ private fun HistoryListPreview() {
Game(false, "TeamA5", "TeamB5", Date(), Date()), listOf(Round(5, 50, 90))
)
)
HistoryList(tempData, {}, {}) {}
HistoryList(tempData, {}, {}, {})
}

View File

@@ -20,16 +20,27 @@ class HistoryViewModel @Inject constructor(
var gameAndHistory by mutableStateOf(emptyList<GameWithScores>())
private set
private var fullList: List<GameWithScores> = emptyList()
init {
viewModelScope.launch {
gameRepository.getAllWithRoundFlow().collect { games ->
gameAndHistory =
fullList =
games.sortedBy { it.game.modified }.sortedBy { it.game.active }.reversed()
gameAndHistory = fullList
}
}
}
fun markToDelete(gameId: Long) {
gameAndHistory = fullList.filter { it.game.uid != gameId }
}
fun unmarkToDelete(gameId: Long) {
gameAndHistory = fullList
}
fun deleteGame(gameId: Long) {
viewModelScope.launch {
gameRepository.deleteGame(gameId)

View File

@@ -25,5 +25,9 @@
<string name="menu_about">About</string>
<string name="contact_us">Schreib uns</string>
<string name="continue_play">Weiterspielen</string>
<string name="delete_success">Spiel gelöscht.</string>
<string name="undo_question">RÜCKGÄNGIG</string>
<string name="activated_success">Spiel aktiviert.</string>
<string name="to_calculator_question">WEITERSPIELEN</string>
</resources>

View File

@@ -29,4 +29,8 @@
<string name="contact_us">Contact us</string>
<string name="play_store" translatable="false">Play Store</string>
<string name="continue_play">Continue</string>
<string name="delete_success">Game deleted.</string>
<string name="undo_question">UNDO</string>
<string name="activated_success">Game activated.</string>
<string name="to_calculator_question">CONTINUE PLAYING</string>
</resources>