fix: scroll to newly added item
This commit is contained in:
@@ -15,7 +15,7 @@ import kotlinx.coroutines.flow.Flow
|
|||||||
interface ExpenseDao {
|
interface ExpenseDao {
|
||||||
|
|
||||||
@Upsert
|
@Upsert
|
||||||
suspend fun insert(expense: Expense)
|
suspend fun insert(expense: Expense): Long
|
||||||
|
|
||||||
|
|
||||||
@Transaction
|
@Transaction
|
||||||
|
|||||||
@@ -25,8 +25,8 @@ class ExpenseRepository @Inject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
@WorkerThread
|
@WorkerThread
|
||||||
suspend fun save(expense: Expense) {
|
suspend fun save(expense: Expense): Long {
|
||||||
expenseDao.insert(expense)
|
return expenseDao.insert(expense)
|
||||||
}
|
}
|
||||||
|
|
||||||
@WorkerThread
|
@WorkerThread
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ import androidx.compose.runtime.Composable
|
|||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.collectAsState
|
import androidx.compose.runtime.collectAsState
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.mutableIntStateOf
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
@@ -99,15 +100,22 @@ fun ListExpenseScreen(
|
|||||||
val expensesFlow =
|
val expensesFlow =
|
||||||
expenseAndCategoryViewModel.getExpensesWithHeadersPaged(currentTripId, search, filter)
|
expenseAndCategoryViewModel.getExpensesWithHeadersPaged(currentTripId, search, filter)
|
||||||
val isRecalculatingRate by tripViewModel.isRecalculating.collectAsState()
|
val isRecalculatingRate by tripViewModel.isRecalculating.collectAsState()
|
||||||
|
var idToScroll by remember { mutableIntStateOf(-1) }
|
||||||
|
|
||||||
ListExpenseScreen(
|
ListExpenseScreen(
|
||||||
currentTrip = currentTrip,
|
currentTrip = currentTrip,
|
||||||
expensesFlow = expensesFlow,
|
expensesFlow = expensesFlow,
|
||||||
onSaveExpense = { expenseAndCategoryViewModel.save(it, currentTrip!!) },
|
onSaveExpense = {
|
||||||
|
expenseAndCategoryViewModel.save(
|
||||||
|
it,
|
||||||
|
currentTrip!!,
|
||||||
|
onComplete = { id -> idToScroll = id })
|
||||||
|
},
|
||||||
onDeleteExpense = { expenseAndCategoryViewModel.delete(it) },
|
onDeleteExpense = { expenseAndCategoryViewModel.delete(it) },
|
||||||
isRecalculatingRate = isRecalculatingRate,
|
isRecalculatingRate = isRecalculatingRate,
|
||||||
initialAutoOpen = initialAutoOpen,
|
initialAutoOpen = initialAutoOpen,
|
||||||
onAutoOpenConsumed = onAutoOpenConsumed
|
onAutoOpenConsumed = onAutoOpenConsumed,
|
||||||
|
idToScroll = idToScroll
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -121,7 +129,8 @@ fun ListExpenseScreen(
|
|||||||
onSaveExpense: (Expense) -> Unit, onDeleteExpense: (Expense) -> Unit,
|
onSaveExpense: (Expense) -> Unit, onDeleteExpense: (Expense) -> Unit,
|
||||||
isRecalculatingRate: Boolean,
|
isRecalculatingRate: Boolean,
|
||||||
initialAutoOpen: Boolean,
|
initialAutoOpen: Boolean,
|
||||||
onAutoOpenConsumed: () -> Unit
|
onAutoOpenConsumed: () -> Unit,
|
||||||
|
idToScroll: Int
|
||||||
) {
|
) {
|
||||||
|
|
||||||
val sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true)
|
val sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true)
|
||||||
@@ -151,7 +160,12 @@ fun ListExpenseScreen(
|
|||||||
{
|
{
|
||||||
Box {
|
Box {
|
||||||
if (items.itemCount == 0) {
|
if (items.itemCount == 0) {
|
||||||
Box(modifier = Modifier.fillMaxSize().padding(10.dp), contentAlignment = Alignment.Center) {
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.padding(10.dp),
|
||||||
|
contentAlignment = Alignment.Center
|
||||||
|
) {
|
||||||
val textToShow = if (currentTrip == null || currentTrip.isDummy()) {
|
val textToShow = if (currentTrip == null || currentTrip.isDummy()) {
|
||||||
stringResource(string.no_trip_picked)
|
stringResource(string.no_trip_picked)
|
||||||
} else {
|
} else {
|
||||||
@@ -167,85 +181,93 @@ fun ListExpenseScreen(
|
|||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
LazyColumn(
|
LaunchedEffect(idToScroll) {
|
||||||
modifier = Modifier
|
if (idToScroll == -1) return@LaunchedEffect
|
||||||
.fillMaxSize()
|
for (index in 0 until items.itemCount) {
|
||||||
.semantics {
|
val item = items.peek(index)
|
||||||
contentDescription = "expensesList"
|
if (item is ExpenseListItemUi.Item && item.expenseDto.expense.id == idToScroll) {
|
||||||
},
|
listState.animateScrollToItem(index)
|
||||||
horizontalAlignment = Alignment.CenterHorizontally,
|
break
|
||||||
state = listState
|
|
||||||
) {
|
|
||||||
items(
|
|
||||||
count = items.itemCount,
|
|
||||||
key = items.itemKey { item ->
|
|
||||||
when (item) {
|
|
||||||
is ExpenseListItemUi.Item -> item.expenseDto.expense.id
|
|
||||||
is ExpenseListItemUi.Header -> "header_${item.date}"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
) { index ->
|
|
||||||
|
|
||||||
when (val item = items[index]) {
|
|
||||||
|
|
||||||
is ExpenseListItemUi.Header -> {
|
|
||||||
CustomDivider(
|
|
||||||
date = item.date,
|
|
||||||
sum = item.sum,
|
|
||||||
currency = item.currency
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
is ExpenseListItemUi.Item -> {
|
|
||||||
SwipeToDeleteExpenseCard(
|
|
||||||
expenseDto = item.expenseDto,
|
|
||||||
onDelete = { expense -> itemToDelete = expense },
|
|
||||||
onClick = { expenseDto ->
|
|
||||||
expenseDtoToEdit = expenseDto
|
|
||||||
showBottomSheet = true
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
null -> {}
|
|
||||||
|
|
||||||
}
|
|
||||||
Spacer(Modifier.height(10.dp))
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
LazyColumn(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.semantics {
|
||||||
|
contentDescription = "expensesList"
|
||||||
|
},
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
|
state = listState
|
||||||
|
) {
|
||||||
|
items(
|
||||||
|
count = items.itemCount,
|
||||||
|
key = items.itemKey { item ->
|
||||||
|
when (item) {
|
||||||
|
is ExpenseListItemUi.Item -> item.expenseDto.expense.id
|
||||||
|
is ExpenseListItemUi.Header -> "header_${item.date}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
) { index ->
|
||||||
|
when (val item = items[index]) {
|
||||||
|
is ExpenseListItemUi.Header -> {
|
||||||
|
CustomDivider(
|
||||||
|
date = item.date,
|
||||||
|
sum = item.sum,
|
||||||
|
currency = item.currency
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
is ExpenseListItemUi.Item -> {
|
||||||
|
SwipeToDeleteExpenseCard(
|
||||||
|
expenseDto = item.expenseDto,
|
||||||
|
onDelete = { expense -> itemToDelete = expense },
|
||||||
|
onClick = { expenseDto ->
|
||||||
|
expenseDtoToEdit = expenseDto
|
||||||
|
showBottomSheet = true
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
null -> {}
|
||||||
|
|
||||||
|
}
|
||||||
|
Spacer(Modifier.height(10.dp))
|
||||||
|
|
||||||
if (itemToDelete != null) {
|
|
||||||
DeleteConfirmationDialog(
|
|
||||||
onConfirm = {
|
|
||||||
onDeleteExpense(itemToDelete!!)
|
|
||||||
itemToDelete = null
|
|
||||||
},
|
|
||||||
onCancel = {
|
|
||||||
itemToDelete = null
|
|
||||||
}
|
}
|
||||||
)
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (showBottomSheet) {
|
}
|
||||||
AddExpenseBottomSheet(
|
|
||||||
onSave = { expense ->
|
if (itemToDelete != null) {
|
||||||
onSaveExpense(expense)
|
DeleteConfirmationDialog(
|
||||||
showBottomSheet = false
|
onConfirm = {
|
||||||
expenseDtoToEdit = null
|
onDeleteExpense(itemToDelete!!)
|
||||||
},
|
itemToDelete = null
|
||||||
onDismiss = {
|
},
|
||||||
expenseDtoToEdit = null
|
onCancel = {
|
||||||
showBottomSheet = false
|
itemToDelete = null
|
||||||
},
|
}
|
||||||
expenseDtoToEdit = expenseDtoToEdit,
|
)
|
||||||
state = sheetState
|
}
|
||||||
)
|
|
||||||
}
|
if (showBottomSheet) {
|
||||||
|
AddExpenseBottomSheet(
|
||||||
|
onSave = { expense ->
|
||||||
|
onSaveExpense(expense)
|
||||||
|
showBottomSheet = false
|
||||||
|
expenseDtoToEdit = null
|
||||||
|
},
|
||||||
|
onDismiss = {
|
||||||
|
expenseDtoToEdit = null
|
||||||
|
showBottomSheet = false
|
||||||
|
},
|
||||||
|
expenseDtoToEdit = expenseDtoToEdit,
|
||||||
|
state = sheetState
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -504,7 +526,8 @@ fun PreviewListExpenseScreen() {
|
|||||||
onDeleteExpense = {},
|
onDeleteExpense = {},
|
||||||
isRecalculatingRate = true,
|
isRecalculatingRate = true,
|
||||||
false,
|
false,
|
||||||
{}
|
{},
|
||||||
|
0
|
||||||
)
|
)
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -529,7 +552,8 @@ fun PreviewListExpenseScreenWithoutExpenses() {
|
|||||||
onDeleteExpense = {},
|
onDeleteExpense = {},
|
||||||
isRecalculatingRate = true,
|
isRecalculatingRate = true,
|
||||||
false,
|
false,
|
||||||
{}
|
{},
|
||||||
|
0
|
||||||
)
|
)
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -548,7 +572,8 @@ fun PreviewListExpenseScreenWithoutTrip() {
|
|||||||
onDeleteExpense = {},
|
onDeleteExpense = {},
|
||||||
isRecalculatingRate = true,
|
isRecalculatingRate = true,
|
||||||
false,
|
false,
|
||||||
{}
|
{},
|
||||||
|
0
|
||||||
)
|
)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -99,14 +99,15 @@ open class ExpenseAndCategoryViewModel @Inject constructor(
|
|||||||
expenseRepo.getExpensesDto(tripId, search, filter)
|
expenseRepo.getExpensesDto(tripId, search, filter)
|
||||||
|
|
||||||
@RequiresApi(Build.VERSION_CODES.O)
|
@RequiresApi(Build.VERSION_CODES.O)
|
||||||
fun save(expense: Expense, trip: Trip) {
|
fun save(expense: Expense, trip: Trip, onComplete: (Int) -> Unit) {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
val rate = exchangeRateRepository.getRate(
|
val rate = exchangeRateRepository.getRate(
|
||||||
Currencies.valueOf(expense.currency),
|
Currencies.valueOf(expense.currency),
|
||||||
Currencies.valueOf(trip.currency),
|
Currencies.valueOf(trip.currency),
|
||||||
expense.datetime.toLocalDate()
|
expense.datetime.toLocalDate()
|
||||||
)
|
)
|
||||||
expenseRepo.save(expense.copy(rate = rate))
|
val id = expenseRepo.save(expense.copy(rate = rate))
|
||||||
|
onComplete(id.toInt())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -210,7 +211,9 @@ open class ExpenseAndCategoryViewModel @Inject constructor(
|
|||||||
}
|
}
|
||||||
.sortedByDescending { it.day }
|
.sortedByDescending { it.day }
|
||||||
|
|
||||||
val highestAmount = summaryPerDayRaw.maxOf { it.amount }
|
|
||||||
|
val highestAmount =
|
||||||
|
if (summaryPerDayRaw.isEmpty()) 1.0 else summaryPerDayRaw.maxOf { it.amount }
|
||||||
summaryPerDayRaw.map {
|
summaryPerDayRaw.map {
|
||||||
it.copy(percent = ((it.amount / highestAmount)).toFloat())
|
it.copy(percent = ((it.amount / highestAmount)).toFloat())
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user