mirror of
https://github.com/Myzel394/NumberHub.git
synced 2025-06-30 22:00:55 +02:00
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:
parent
4668a5cea3
commit
95a401581d
@ -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>
|
||||||
|
@ -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"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -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 = {}
|
||||||
)
|
)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user