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_label">Clear</string>
<string name="calculator_clear_history_title">Clear history</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_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--> <!--Precision-->
<string name="precision_setting_support">Number of decimal places</string> <string name="precision_setting_support">Number of decimal places</string>

View File

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

View File

@ -18,21 +18,35 @@
package com.sadellie.unitto.feature.calculator.components package com.sadellie.unitto.feature.calculator.components
import androidx.compose.foundation.background
import androidx.compose.foundation.horizontalScroll import androidx.compose.foundation.horizontalScroll
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.rememberScrollState 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.MaterialTheme
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable 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.Modifier
import androidx.compose.ui.layout.onPlaced import androidx.compose.ui.layout.onPlaced
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview 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.core.ui.theme.NumbersTextStyleDisplayMedium
import com.sadellie.unitto.data.model.HistoryItem import com.sadellie.unitto.data.model.HistoryItem
import com.sadellie.unitto.feature.calculator.R
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.* import java.util.*
@ -42,37 +56,77 @@ internal fun HistoryList(
historyItems: List<HistoryItem>, historyItems: List<HistoryItem>,
historyItemHeightCallback: (Int) -> Unit historyItemHeightCallback: (Int) -> Unit
) { ) {
LazyColumn( val verticalArrangement by remember(historyItems) {
modifier = modifier, derivedStateOf {
reverseLayout = true if (historyItems.isEmpty()) {
) { Arrangement.Center
items(historyItems) { historyItem -> } else {
Column( Arrangement.spacedBy(16.dp, Alignment.Bottom)
Modifier.onPlaced { historyItemHeightCallback(it.size.height) }
) {
Text(
text = historyItem.expression,
maxLines = 1,
modifier = Modifier
.fillMaxWidth()
.horizontalScroll(rememberScrollState(), reverseScrolling = true),
style = NumbersTextStyleDisplayMedium,
color = MaterialTheme.colorScheme.onSurfaceVariant,
textAlign = TextAlign.End
)
Text(
text = historyItem.result,
maxLines = 1,
modifier = Modifier
.fillMaxWidth()
.horizontalScroll(rememberScrollState(), reverseScrolling = true),
style = NumbersTextStyleDisplayMedium,
color = MaterialTheme.colorScheme.onSurfaceVariant.copy(alpha = 0.5f),
textAlign = TextAlign.End
)
} }
} }
} }
LazyColumn(
modifier = modifier,
reverseLayout = true,
verticalArrangement = verticalArrangement
) {
if (historyItems.isEmpty()) {
item {
Column(
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,
modifier = Modifier
.fillMaxWidth()
.horizontalScroll(rememberScrollState(), reverseScrolling = true),
style = NumbersTextStyleDisplayMedium,
color = MaterialTheme.colorScheme.onSurfaceVariant,
textAlign = TextAlign.End
)
Text(
text = historyItem.result,
maxLines = 1,
modifier = Modifier
.fillMaxWidth()
.horizontalScroll(rememberScrollState(), reverseScrolling = true),
style = NumbersTextStyleDisplayMedium,
color = MaterialTheme.colorScheme.onSurfaceVariant.copy(alpha = 0.5f),
textAlign = TextAlign.End
)
}
} }
@Preview @Preview
@ -98,7 +152,9 @@ private fun PreviewHistoryList() {
} }
HistoryList( HistoryList(
modifier = Modifier.fillMaxWidth(), modifier = Modifier
.background(MaterialTheme.colorScheme.surfaceVariant.copy(alpha = 0.5f))
.fillMaxSize(),
historyItems = historyItems, historyItems = historyItems,
historyItemHeightCallback = {} historyItemHeightCallback = {}
) )