Clear history UX/UI improved

- Show clear history button only when history view is fully expanded
- History view placeholder
- AlertDialog closes after clearing history
- List item height callback is called only once
This commit is contained in:
Sad Ellie 2023-02-21 18:59:06 +04:00
parent 4668a5cea3
commit 95a401581d
3 changed files with 124 additions and 44 deletions

View File

@ -1028,6 +1028,7 @@
<string name="calculator_clear_history_label">Clear</string>
<string name="calculator_clear_history_title">Clear history</string>
<string name="calculator_clear_history_support">All expressions from history will be deleted forever. This action can\'t be undone!</string>
<string name="calculator_no_history">No history</string>
<!--Precision-->
<string name="precision_setting_support">Number of decimal places</string>

View File

@ -18,7 +18,10 @@
package com.sadellie.unitto.feature.calculator
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.foundation.background
import androidx.compose.foundation.gestures.Orientation
import androidx.compose.foundation.gestures.draggable
@ -101,6 +104,7 @@ private fun CalculatorScreen(
evaluate: () -> Unit,
clearHistory: () -> Unit
) {
var showClearHistoryButton by rememberSaveable { mutableStateOf(false) }
var showClearHistoryDialog by rememberSaveable { mutableStateOf(false) }
var draggedAmount by remember { mutableStateOf(0f) }
val dragAmountAnimated by animateFloatAsState(draggedAmount)
@ -113,6 +117,11 @@ private fun CalculatorScreen(
navigateUpAction = navigateUpAction,
colors = TopAppBarDefaults.topAppBarColors(MaterialTheme.colorScheme.surfaceVariant),
actions = {
AnimatedVisibility(
visible = showClearHistoryButton,
enter = fadeIn(),
exit = fadeOut()
) {
IconButton(
onClick = { showClearHistoryDialog = true },
content = {
@ -123,6 +132,7 @@ private fun CalculatorScreen(
}
)
}
}
) { paddingValues ->
DragDownView(
modifier = Modifier.padding(paddingValues),
@ -153,10 +163,14 @@ private fun CalculatorScreen(
state = rememberDraggableState { delta ->
draggedAmount = (draggedAmount + delta).coerceAtLeast(0f)
},
onDragStopped = {
onDragStopped = { _ ->
// Snap to closest anchor (0, one history item, all history items)
draggedAmount = listOf(0, historyItemHeight, maxDragAmount)
.minBy { abs(draggedAmount.roundToInt() - it) }
.also {
// Show button only when fully history view is fully expanded
showClearHistoryButton = it == maxDragAmount
}
.toFloat()
}
),
@ -218,12 +232,21 @@ private fun CalculatorScreen(
Text(stringResource(R.string.calculator_clear_history_support))
},
confirmButton = {
TextButton(onClick = clearHistory) { Text(stringResource(R.string.calculator_clear_history_label)) }
TextButton(
onClick = {
clearHistory()
showClearHistoryDialog = false
}
) {
Text(stringResource(R.string.calculator_clear_history_label))
}
},
dismissButton = {
TextButton(onClick = {
showClearHistoryDialog = false
}) { Text(stringResource(R.string.cancel_label)) }
TextButton(
onClick = { showClearHistoryDialog = false }
) {
Text(stringResource(R.string.cancel_label))
}
},
onDismissRequest = { showClearHistoryDialog = false }
)
@ -247,7 +270,7 @@ private fun PreviewCalculatorScreen() {
).map {
HistoryItem(
date = dtf.parse(it)!!,
expression = "12345123451234512345123451234512345123451234512345123451234512345",
expression = "12345".repeat(10),
result = "67890"
)
}

View File

@ -18,21 +18,35 @@
package com.sadellie.unitto.feature.calculator.components
import androidx.compose.foundation.background
import androidx.compose.foundation.horizontalScroll
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
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.items
import androidx.compose.foundation.rememberScrollState
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.History
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.onPlaced
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.sadellie.unitto.core.ui.theme.NumbersTextStyleDisplayMedium
import com.sadellie.unitto.data.model.HistoryItem
import com.sadellie.unitto.feature.calculator.R
import java.text.SimpleDateFormat
import java.util.*
@ -42,14 +56,56 @@ internal fun HistoryList(
historyItems: List<HistoryItem>,
historyItemHeightCallback: (Int) -> Unit
) {
val verticalArrangement by remember(historyItems) {
derivedStateOf {
if (historyItems.isEmpty()) {
Arrangement.Center
} else {
Arrangement.spacedBy(16.dp, Alignment.Bottom)
}
}
}
LazyColumn(
modifier = modifier,
reverseLayout = true
reverseLayout = true,
verticalArrangement = verticalArrangement
) {
items(historyItems) { historyItem ->
if (historyItems.isEmpty()) {
item {
Column(
Modifier.onPlaced { historyItemHeightCallback(it.size.height) }
modifier = Modifier
.onPlaced { historyItemHeightCallback(it.size.height) }
.fillParentMaxWidth()
.padding(vertical = 32.dp),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
Icon(Icons.Default.History, null)
Text(stringResource(R.string.calculator_no_history))
}
}
} else {
// We do this so that callback for items height is called only once
item {
HistoryListItem(
modifier = Modifier.onPlaced { historyItemHeightCallback(it.size.height) },
historyItem = historyItems.first()
)
}
items(historyItems.drop(1)) { historyItem ->
HistoryListItem(historyItem = historyItem)
}
}
}
}
@Composable
private fun HistoryListItem(
modifier: Modifier = Modifier,
historyItem: HistoryItem
) {
Column(modifier = modifier) {
Text(
text = historyItem.expression,
maxLines = 1,
@ -71,8 +127,6 @@ internal fun HistoryList(
textAlign = TextAlign.End
)
}
}
}
}
@Preview
@ -98,7 +152,9 @@ private fun PreviewHistoryList() {
}
HistoryList(
modifier = Modifier.fillMaxWidth(),
modifier = Modifier
.background(MaterialTheme.colorScheme.surfaceVariant.copy(alpha = 0.5f))
.fillMaxSize(),
historyItems = historyItems,
historyItemHeightCallback = {}
)