diff --git a/app/src/main/java/cc/n0th1ng/tripmoney/data/dao/ExpenseDao.kt b/app/src/main/java/cc/n0th1ng/tripmoney/data/dao/ExpenseDao.kt index b475462..4948ae4 100644 --- a/app/src/main/java/cc/n0th1ng/tripmoney/data/dao/ExpenseDao.kt +++ b/app/src/main/java/cc/n0th1ng/tripmoney/data/dao/ExpenseDao.kt @@ -15,7 +15,7 @@ import kotlinx.coroutines.flow.Flow interface ExpenseDao { @Upsert - suspend fun insert(expense: Expense) + suspend fun insert(expense: Expense): Long @Transaction diff --git a/app/src/main/java/cc/n0th1ng/tripmoney/data/repository/ExpenseRepository.kt b/app/src/main/java/cc/n0th1ng/tripmoney/data/repository/ExpenseRepository.kt index bacf5ab..b3ddbd1 100644 --- a/app/src/main/java/cc/n0th1ng/tripmoney/data/repository/ExpenseRepository.kt +++ b/app/src/main/java/cc/n0th1ng/tripmoney/data/repository/ExpenseRepository.kt @@ -25,8 +25,8 @@ class ExpenseRepository @Inject constructor( } @WorkerThread - suspend fun save(expense: Expense) { - expenseDao.insert(expense) + suspend fun save(expense: Expense): Long { + return expenseDao.insert(expense) } @WorkerThread diff --git a/app/src/main/java/cc/n0th1ng/tripmoney/screens/listexpense/ListExpenseScreen.kt b/app/src/main/java/cc/n0th1ng/tripmoney/screens/listexpense/ListExpenseScreen.kt index 16adc81..a3732f3 100644 --- a/app/src/main/java/cc/n0th1ng/tripmoney/screens/listexpense/ListExpenseScreen.kt +++ b/app/src/main/java/cc/n0th1ng/tripmoney/screens/listexpense/ListExpenseScreen.kt @@ -40,6 +40,7 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableIntStateOf import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue @@ -99,15 +100,22 @@ fun ListExpenseScreen( val expensesFlow = expenseAndCategoryViewModel.getExpensesWithHeadersPaged(currentTripId, search, filter) val isRecalculatingRate by tripViewModel.isRecalculating.collectAsState() + var idToScroll by remember { mutableIntStateOf(-1) } ListExpenseScreen( currentTrip = currentTrip, expensesFlow = expensesFlow, - onSaveExpense = { expenseAndCategoryViewModel.save(it, currentTrip!!) }, + onSaveExpense = { + expenseAndCategoryViewModel.save( + it, + currentTrip!!, + onComplete = { id -> idToScroll = id }) + }, onDeleteExpense = { expenseAndCategoryViewModel.delete(it) }, isRecalculatingRate = isRecalculatingRate, initialAutoOpen = initialAutoOpen, - onAutoOpenConsumed = onAutoOpenConsumed + onAutoOpenConsumed = onAutoOpenConsumed, + idToScroll = idToScroll ) } @@ -121,7 +129,8 @@ fun ListExpenseScreen( onSaveExpense: (Expense) -> Unit, onDeleteExpense: (Expense) -> Unit, isRecalculatingRate: Boolean, initialAutoOpen: Boolean, - onAutoOpenConsumed: () -> Unit + onAutoOpenConsumed: () -> Unit, + idToScroll: Int ) { val sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true) @@ -151,7 +160,12 @@ fun ListExpenseScreen( { Box { 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()) { stringResource(string.no_trip_picked) } else { @@ -167,85 +181,93 @@ fun ListExpenseScreen( } } else { - 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}" - } + LaunchedEffect(idToScroll) { + if (idToScroll == -1) return@LaunchedEffect + for (index in 0 until items.itemCount) { + val item = items.peek(index) + if (item is ExpenseListItemUi.Item && item.expenseDto.expense.id == idToScroll) { + listState.animateScrollToItem(index) + break } - ) { 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 -> - onSaveExpense(expense) - showBottomSheet = false - expenseDtoToEdit = null - }, - onDismiss = { - expenseDtoToEdit = null - showBottomSheet = false - }, - expenseDtoToEdit = expenseDtoToEdit, - state = sheetState - ) - } + } + + if (itemToDelete != null) { + DeleteConfirmationDialog( + onConfirm = { + onDeleteExpense(itemToDelete!!) + itemToDelete = null + }, + onCancel = { + itemToDelete = null + } + ) + } + + 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 = {}, isRecalculatingRate = true, false, - {} + {}, + 0 ) } @@ -529,7 +552,8 @@ fun PreviewListExpenseScreenWithoutExpenses() { onDeleteExpense = {}, isRecalculatingRate = true, false, - {} + {}, + 0 ) } @@ -548,7 +572,8 @@ fun PreviewListExpenseScreenWithoutTrip() { onDeleteExpense = {}, isRecalculatingRate = true, false, - {} + {}, + 0 ) } diff --git a/app/src/main/java/cc/n0th1ng/tripmoney/viewmodel/ExpenseAndCategoryViewModel.kt b/app/src/main/java/cc/n0th1ng/tripmoney/viewmodel/ExpenseAndCategoryViewModel.kt index 0bdeae8..9a680e6 100644 --- a/app/src/main/java/cc/n0th1ng/tripmoney/viewmodel/ExpenseAndCategoryViewModel.kt +++ b/app/src/main/java/cc/n0th1ng/tripmoney/viewmodel/ExpenseAndCategoryViewModel.kt @@ -99,14 +99,15 @@ open class ExpenseAndCategoryViewModel @Inject constructor( expenseRepo.getExpensesDto(tripId, search, filter) @RequiresApi(Build.VERSION_CODES.O) - fun save(expense: Expense, trip: Trip) { + fun save(expense: Expense, trip: Trip, onComplete: (Int) -> Unit) { viewModelScope.launch { val rate = exchangeRateRepository.getRate( Currencies.valueOf(expense.currency), Currencies.valueOf(trip.currency), 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 } - val highestAmount = summaryPerDayRaw.maxOf { it.amount } + + val highestAmount = + if (summaryPerDayRaw.isEmpty()) 1.0 else summaryPerDayRaw.maxOf { it.amount } summaryPerDayRaw.map { it.copy(percent = ((it.amount / highestAmount)).toFloat()) }