Files
tripMoney/app/src/main/java/cc/n0th1ng/tripmoney/MainActivity.kt
2026-05-06 12:47:11 +02:00

175 lines
7.1 KiB
Kotlin

package cc.n0th1ng.tripmoney
import android.os.Build
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.ReportDrawnWhen
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.annotation.RequiresApi
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.DrawerValue
import androidx.compose.material3.Scaffold
import androidx.compose.material3.rememberDrawerState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.currentBackStackEntryAsState
import androidx.navigation.compose.rememberNavController
import androidx.navigation.navArgument
import cc.n0th1ng.tripmoney.data.entity.Category
import cc.n0th1ng.tripmoney.data.entity.Trip
import cc.n0th1ng.tripmoney.navigation.BottomNavigation
import cc.n0th1ng.tripmoney.navigation.CustomNavigationDrawer
import cc.n0th1ng.tripmoney.navigation.Screens
import cc.n0th1ng.tripmoney.navigation.TopBar
import cc.n0th1ng.tripmoney.navigation.TopBarSettings
import cc.n0th1ng.tripmoney.screens.listexpense.ListExpenseScreen
import cc.n0th1ng.tripmoney.screens.managecategories.ManageCategoriesScreen
import cc.n0th1ng.tripmoney.screens.settings.SettingsScreen
import cc.n0th1ng.tripmoney.screens.statistics.StatisticsScreen
import cc.n0th1ng.tripmoney.screens.trippicker.TripPickerScreen
import cc.n0th1ng.tripmoney.theme.TripMoneyTheme
import cc.n0th1ng.tripmoney.viewmodel.ExpenseAndCategoryViewModel
import cc.n0th1ng.tripmoney.viewmodel.SettingsViewModel
import cc.n0th1ng.tripmoney.viewmodel.TripViewModel
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.launch
import java.time.LocalDate
@AndroidEntryPoint
class MainActivity : ComponentActivity() {
@RequiresApi(Build.VERSION_CODES.S)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
setContent {
TripMoneyTheme {
NavigationDrawer()
}
}
}
}
@RequiresApi(Build.VERSION_CODES.S)
@Composable
fun NavigationDrawer() {
val settingsViewModel: SettingsViewModel = hiltViewModel()
val tripViewModel: TripViewModel = hiltViewModel()
val expenseAndCategoryViewModel: ExpenseAndCategoryViewModel = hiltViewModel()
val categories by expenseAndCategoryViewModel.getCategories().collectAsState(emptyList())
val currentTripId by settingsViewModel.currentTrip.collectAsState()
val currentTrip by tripViewModel.getTrip(currentTripId).collectAsState(Trip.DUMMY)
val navController = rememberNavController()
val navBackStack by navController.currentBackStackEntryAsState()
val current = navBackStack?.destination?.route
val drawerState = rememberDrawerState(initialValue = DrawerValue.Closed)
val scope = rememberCoroutineScope()
var search by remember { mutableStateOf("") }
var filter by remember { mutableStateOf(Filter()) }
val autoOpenPref by settingsViewModel.autoOpenStartupPref.collectAsState()
var hasHandledStartupOpen by rememberSaveable { mutableStateOf(false) }
val shouldTriggerAutoOpen = autoOpenPref == true && !hasHandledStartupOpen
ReportDrawnWhen { !categories.isEmpty() }
CustomNavigationDrawer(navController, drawerState) {
Scaffold(
topBar = {
if (current == Screens.SETTINGS) TopBarSettings(
navController
) else TopBar(
title = currentTrip?.name ?: "",
onDrawerClick = {
scope.launch {
if (drawerState.isClosed) {
drawerState.open()
} else {
drawerState.close()
}
}
},
isSearchable = current != null && current.contains(Screens.LIST_EXPENSE),
onSearchChange = { newSearch -> search = newSearch },
onFilterChange = { newFilter -> filter = newFilter },
categories = categories,
filter = filter
)
},
bottomBar = { BottomNavigation(navController) }) { innerPadding ->
NavHost(
navController = navController,
startDestination = if (currentTripId == -1) Screens.TRIP_PICKER else Screens.LIST_EXPENSE,
modifier = Modifier.padding(innerPadding)
) {
composable(
Screens.LIST_EXPENSE + "?dateToScroll={dateToScroll}",
arguments = listOf(navArgument("dateToScroll") { defaultValue = "" })
) { backStackEntry ->
ListExpenseScreen(
filter = filter, search = search,
initialAutoOpen = shouldTriggerAutoOpen,
onAutoOpenConsumed = { hasHandledStartupOpen = true },
dateToScroll = backStackEntry.arguments?.getString("dateToScroll") ?: ""
)
}
composable(Screens.TRIP_PICKER) {
TripPickerScreen(navController)
}
composable(Screens.STATISTICS) {
StatisticsScreen(navController)
}
composable(Screens.SETTINGS) {
SettingsScreen(navController)
}
composable(Screens.MANAGE_CATEGORIES) {
ManageCategoriesScreen()
}
}
}
}
}
data class Filter(
val categories: List<Category> = emptyList(), val startAmount: Double = 0.0,
val endAmount: Double = Double.MAX_VALUE,
val startDate: LocalDate = LocalDate.MIN,
val endDate: LocalDate = LocalDate.MAX
) {
fun with(category: Category): Filter {
return this.copy(categories = categories + category)
}
fun withStartAmount(amount: Double): Filter {
return this.copy(startAmount = amount)
}
fun withEndAmount(amount: Double): Filter {
return this.copy(endAmount = amount)
}
fun withStartDate(date: LocalDate): Filter {
return this.copy(startDate = date)
}
fun withEndDate(date: LocalDate): Filter {
return this.copy(endDate = date)
}
fun without(category: Category): Filter {
return this.copy(categories = categories - category)
}
fun isDefault(): Boolean {
return this.categories.isEmpty() && startAmount == 0.0 && endAmount == Double.MAX_VALUE && startDate == LocalDate.MIN && endDate == LocalDate.MAX
}
}