nordiccalendar

A simple Material 3 native calendar app fitting my personal requirements
git clone git://archive.git.mtrnord.blog/MTRNord/nordiccalendar.git
Log | Files | Refs | README | LICENSE

commit 7367b8d7cb2928e52c1814d0a629dc4933ed5899
parent 63e6fbad8de2b80cdbb7a029cad4f1dea050962e
Author: MTRNord <MTRNord@users.noreply.github.com>
Date:   Tue, 29 Jul 2025 22:03:50 +0200

Use modern tabs instead and clean up some code

Diffstat:
Mapp/src/main/java/space/midnightthoughts/nordiccalendar/MainActivity.kt | 20--------------------
Mapp/src/main/java/space/midnightthoughts/nordiccalendar/data/CalendarRepository.kt | 9+++++++++
Mapp/src/main/java/space/midnightthoughts/nordiccalendar/screens/CalendarScreen.kt | 36+++++++++++++++++++-----------------
Mapp/src/main/java/space/midnightthoughts/nordiccalendar/viewmodels/CalendarViewModel.kt | 17++++++++++++++---
4 files changed, 42 insertions(+), 40 deletions(-)

diff --git a/app/src/main/java/space/midnightthoughts/nordiccalendar/MainActivity.kt b/app/src/main/java/space/midnightthoughts/nordiccalendar/MainActivity.kt @@ -31,7 +31,6 @@ import androidx.compose.material3.Scaffold import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.collectAsState import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope @@ -43,8 +42,6 @@ import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp -import androidx.hilt.navigation.compose.hiltViewModel -import androidx.navigation.NavBackStackEntry import androidx.navigation.NavHostController import androidx.navigation.NavType import androidx.navigation.compose.NavHost @@ -63,7 +60,6 @@ import space.midnightthoughts.nordiccalendar.screens.EventDetailsView import space.midnightthoughts.nordiccalendar.screens.SettingsView import space.midnightthoughts.nordiccalendar.ui.theme.NordicCalendarTheme import space.midnightthoughts.nordiccalendar.util.OnboardingPrefs -import space.midnightthoughts.nordiccalendar.viewmodels.CalendarViewModel sealed class Destinations(val route: String) { object Intro : Destinations("intro") @@ -130,7 +126,6 @@ class MainActivity : ComponentActivity() { ) ) { backStackEntry -> CalendarView( - backStackEntry = backStackEntry, navController = navController ) } @@ -270,21 +265,8 @@ fun IntroScreen(navController: NavHostController, onFinish: (() -> Unit)? = null @Composable fun CalendarView( - backStackEntry: NavBackStackEntry, navController: NavHostController ) { - val calendarViewModel: CalendarViewModel = hiltViewModel() - - val selectedTab = remember(calendarViewModel) { - calendarViewModel.selectedTab - }.collectAsState(initial = 0) - val tabArg = backStackEntry.arguments?.getInt("tab") - - LaunchedEffect(tabArg) { - if (tabArg != null) { - calendarViewModel.setTab(tabArg) - } - } AppScaffold( title = stringResource(R.string.app_name), @@ -308,8 +290,6 @@ fun CalendarView( } ) { innerPadding -> CalendarScreen( - selectedTab = selectedTab.value, - onTabSelected = { calendarViewModel.setTab(it) }, modifier = innerPadding, navController = navController, ) diff --git a/app/src/main/java/space/midnightthoughts/nordiccalendar/data/CalendarRepository.kt b/app/src/main/java/space/midnightthoughts/nordiccalendar/data/CalendarRepository.kt @@ -96,4 +96,13 @@ class CalendarRepository @Inject constructor(@ApplicationContext context: Contex fun getEventById(context: Context, eventId: Long): Event? { return calendarData.getEventById(context.contentResolver, eventId) } + + fun refreshEvents(context: Context) { + repoScope.launch { + val calendars = getCalendars(context) + val selectedIds = calendars.filter { it.selected }.map { it.id } + _eventsFlow.value = + getEventsForCalendars(context, selectedIds, _startMillis.value, _endMillis.value) + } + } } diff --git a/app/src/main/java/space/midnightthoughts/nordiccalendar/screens/CalendarScreen.kt b/app/src/main/java/space/midnightthoughts/nordiccalendar/screens/CalendarScreen.kt @@ -34,8 +34,8 @@ import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.PrimaryTabRow import androidx.compose.material3.Tab -import androidx.compose.material3.TabRow import androidx.compose.material3.Text import androidx.compose.material3.TextButton import androidx.compose.material3.pulltorefresh.PullToRefreshBox @@ -66,7 +66,7 @@ import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.IntOffset import androidx.compose.ui.unit.dp -import androidx.lifecycle.viewmodel.compose.viewModel +import androidx.hilt.navigation.compose.hiltViewModel import androidx.navigation.NavController import kotlinx.coroutines.delay import space.midnightthoughts.nordiccalendar.R @@ -83,29 +83,29 @@ import java.util.Date @Composable fun CalendarScreen( modifier: Modifier = Modifier, - calendarViewModel: CalendarViewModel = viewModel(), - selectedTab: Int, - onTabSelected: (Int) -> Unit, navController: NavController, ) { + val calendarViewModel: CalendarViewModel = hiltViewModel() val events = remember(calendarViewModel) { calendarViewModel.events }.collectAsState(initial = emptyList()) val isRefreshing = remember(calendarViewModel) { calendarViewModel.isRefreshing }.collectAsState(initial = false) + val selectedTab = remember(calendarViewModel) { + calendarViewModel.selectedTab + }.collectAsState(initial = 0) Log.d( "CalendarScreen", - "Rendering with selectedTab: $selectedTab, events count: ${events.value.size}, isRefreshing: $isRefreshing" + "Rendering with selectedTab: $selectedTab.value, events count: ${events.value.size}, isRefreshing: $isRefreshing" ) val pullToRefreshState = rememberPullToRefreshState() PullToRefreshBox( state = pullToRefreshState, modifier = modifier, onRefresh = { - // updateEvents gibt es nicht mehr im ViewModel, stattdessen Tab neu setzen, um Zeitraum zu triggern - calendarViewModel.setTab(selectedTab) + calendarViewModel.refreshEvents() }, isRefreshing = isRefreshing.value, indicator = { @@ -119,27 +119,28 @@ fun CalendarScreen( }, ) { Column(modifier = Modifier.fillMaxSize()) { - CalendarTabBar(selectedTab = selectedTab, onTabSelected = onTabSelected) - // DateRangeHeader sollte direkt unter den Tabs stehen, IMMER sichtbar + CalendarTabBar( + selectedTab = selectedTab.value, + onTabSelected = { calendarViewModel.setTab(it) }) DateRangeHeader( - selectedTab = selectedTab, + selectedTab = selectedTab.value, calendarViewModel = calendarViewModel, onPrev = { - when (selectedTab) { + when (selectedTab.value) { 0 -> calendarViewModel.prevMonth() 1 -> calendarViewModel.prevWeek() 2 -> calendarViewModel.prevDay() } }, onNext = { - when (selectedTab) { + when (selectedTab.value) { 0 -> calendarViewModel.nextMonth() 1 -> calendarViewModel.nextWeek() 2 -> calendarViewModel.nextDay() } }, onToday = { - when (selectedTab) { + when (selectedTab.value) { 0 -> calendarViewModel.setTodayMonth() 1 -> calendarViewModel.setTodayWeek() 2 -> calendarViewModel.setTodayDay() @@ -149,7 +150,7 @@ fun CalendarScreen( Spacer(Modifier.height(8.dp)) if (events.value.isEmpty()) { Text(stringResource(R.string.no_events_found), modifier = Modifier.padding(16.dp)) - } else if (selectedTab == 2) { + } else if (selectedTab.value == 2) { DayView( navController = navController, calendarViewModel = calendarViewModel, @@ -158,7 +159,7 @@ fun CalendarScreen( EventList( navController = navController, calendarViewModel = calendarViewModel, - selectedTab = selectedTab + selectedTab = selectedTab.value ) } } @@ -352,6 +353,7 @@ fun DayView( } } +@OptIn(ExperimentalMaterial3Api::class) @Composable fun CalendarTabBar(selectedTab: Int, onTabSelected: (Int) -> Unit) { val tabTitles = listOf( @@ -359,7 +361,7 @@ fun CalendarTabBar(selectedTab: Int, onTabSelected: (Int) -> Unit) { stringResource(R.string.tab_week), stringResource(R.string.tab_day) ) - TabRow( + PrimaryTabRow( selectedTabIndex = selectedTab, modifier = Modifier.semantics { collectionInfo = CollectionInfo( diff --git a/app/src/main/java/space/midnightthoughts/nordiccalendar/viewmodels/CalendarViewModel.kt b/app/src/main/java/space/midnightthoughts/nordiccalendar/viewmodels/CalendarViewModel.kt @@ -1,6 +1,7 @@ package space.midnightthoughts.nordiccalendar.viewmodels import android.content.Context +import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import dagger.hilt.android.lifecycle.HiltViewModel @@ -16,7 +17,8 @@ import javax.inject.Inject @HiltViewModel class CalendarViewModel @Inject constructor( @param:ApplicationContext private val context: Context, - private val repository: CalendarRepository + private val repository: CalendarRepository, + savedStateHandle: SavedStateHandle ) : ViewModel() { val events = repository.eventsFlow.stateIn( viewModelScope, @@ -31,8 +33,10 @@ class CalendarViewModel @Inject constructor( val selectedTab: StateFlow<Int> = _selectedTab.asStateFlow() init { - // Repository initialisieren (Context benötigt) - setTab(0) + val tab = savedStateHandle.get<Int?>("tab") + if (tab != null) { + setTab(tab) + } } fun setTab(tab: Int) { @@ -244,4 +248,11 @@ class CalendarViewModel @Inject constructor( }.timeInMillis repository.setTimeRange(start, end) } + + fun refreshEvents() { + isRefreshing.value = true + repository.refreshEvents(context).apply { + isRefreshing.value = false + } + } }