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 b0c82bca8e1a7e3fcd07256fb4c0f9b9b89c18f1
parent 1ed51dcac6b1607aecdaf0ebf5c8c43f9f015fc8
Author: MTRNord <MTRNord@users.noreply.github.com>
Date:   Wed, 30 Jul 2025 00:15:36 +0200

Revert "Use more derived state"

This reverts commit 62c27a7176f0eeefe7eb476085a508ac0319f0a5.

Diffstat:
Mapp/src/main/java/space/midnightthoughts/nordiccalendar/MainActivity.kt | 3++-
Mapp/src/main/java/space/midnightthoughts/nordiccalendar/screens/CalendarScreen.kt | 40++++++++++++++++++----------------------
Mapp/src/main/java/space/midnightthoughts/nordiccalendar/screens/EventDetailsView.kt | 66++++++++++++++++++++++++++----------------------------------------
3 files changed, 46 insertions(+), 63 deletions(-)

diff --git a/app/src/main/java/space/midnightthoughts/nordiccalendar/MainActivity.kt b/app/src/main/java/space/midnightthoughts/nordiccalendar/MainActivity.kt @@ -196,7 +196,7 @@ fun IntroScreen(navController: NavHostController, onFinish: (() -> Unit)? = null val pagerState = rememberPagerState(initialPage = 0, pageCount = { onBoardingData.size }) val coroutineScope = rememberCoroutineScope() - // Keep state if the user has allowed permissions to hide the finish button using mutable state + // Keep state if the user has allowed permmissions to hide the finish button using mutable state val hasPermissions = remember { mutableStateOf(false) } NordicCalendarTheme { @@ -314,6 +314,7 @@ fun CalendarView( @Composable fun AboutView(navController: NavHostController) { + AppScaffold( title = stringResource(R.string.about) + " Nordic Calendar", selectedDestination = "about", diff --git a/app/src/main/java/space/midnightthoughts/nordiccalendar/screens/CalendarScreen.kt b/app/src/main/java/space/midnightthoughts/nordiccalendar/screens/CalendarScreen.kt @@ -44,7 +44,6 @@ import androidx.compose.material3.pulltorefresh.rememberPullToRefreshState import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.collectAsState -import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableLongStateOf import androidx.compose.runtime.remember @@ -89,13 +88,13 @@ fun CalendarScreen( val calendarViewModel: CalendarViewModel = hiltViewModel() val events = remember(calendarViewModel) { calendarViewModel.events - }.collectAsState() + }.collectAsState(initial = emptyList()) val isRefreshing = remember(calendarViewModel) { calendarViewModel.isRefreshing - }.collectAsState() + }.collectAsState(initial = false) val selectedTab = remember(calendarViewModel) { calendarViewModel.selectedTab - }.collectAsState() + }.collectAsState(initial = 0) Log.d( "CalendarScreen", @@ -399,30 +398,29 @@ fun DateRangeHeader( ) { val startMillis = remember(calendarViewModel) { calendarViewModel.startMillis - }.collectAsState() + }.collectAsState(initial = System.currentTimeMillis()) val endMillis = remember(calendarViewModel) { calendarViewModel.endMillis - }.collectAsState() + }.collectAsState(initial = System.currentTimeMillis()) val appLocale = getCurrentAppLocale(LocalContext.current) val dateFormat = DateTimeFormatter.ofPattern("dd.MM.yyyy", appLocale) - val startDate = remember { - derivedStateOf { Date(startMillis.value) } + val startDate = remember(startMillis.value) { + Date(startMillis.value) } - val endDate = remember { - derivedStateOf { Date(endMillis.value) } + val endDate = remember(endMillis.value) { + Date(endMillis.value) } val rangeText = when (selectedTab) { 0, 1 -> "${ - startDate.value.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime() + startDate.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime() .format(dateFormat) } – ${ - endDate.value.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime() - .format(dateFormat) + endDate.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime().format(dateFormat) }" - 2 -> startDate.value.toInstant() + 2 -> startDate.toInstant() .atZone(ZoneId.systemDefault()).toLocalDate() .format(dateFormat) @@ -509,11 +507,11 @@ fun EventCard( ) { val appLocale = getCurrentAppLocale(LocalContext.current) val hourFormat = DateTimeFormatter.ofPattern("HH:mm", appLocale) - val startDate = remember { - derivedStateOf { Date(eventStartOverride.takeIf { it > 0L } ?: event.startTime) } + val startDate = remember(eventStartOverride.takeIf { it > 0L } ?: event.startTime) { + Date(eventStartOverride.takeIf { it > 0L } ?: event.startTime) } - val endDate = remember { - derivedStateOf { Date(eventEndOverride.takeIf { it > 0L } ?: event.endTime) } + val endDate = remember(eventEndOverride.takeIf { it > 0L } ?: event.endTime) { + Date(eventEndOverride.takeIf { it > 0L } ?: event.endTime) } val shape = if (noTopCorners && noBottomCorners) { MaterialTheme.shapes.medium.copy( @@ -571,14 +569,12 @@ fun EventCard( Spacer(modifier = Modifier.height(4.dp)) val startTimeText = when { showStartTimeAsMidnight -> "00:00" - else -> startDate.value.toInstant().atZone(ZoneId.systemDefault()) - .toLocalDateTime() + else -> startDate.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime() .format(hourFormat) } val endTimeText = when { showEndTimeAsMidnight -> "24:00" - else -> endDate.value.toInstant().atZone(ZoneId.systemDefault()) - .toLocalDateTime() + else -> endDate.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime() .format(hourFormat) } Text( diff --git a/app/src/main/java/space/midnightthoughts/nordiccalendar/screens/EventDetailsView.kt b/app/src/main/java/space/midnightthoughts/nordiccalendar/screens/EventDetailsView.kt @@ -28,7 +28,6 @@ import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.collectAsState -import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.getValue import androidx.compose.runtime.remember import androidx.compose.ui.Alignment @@ -77,30 +76,22 @@ fun EventDetailsView( val viewModel: EventDetailsViewModel = hiltViewModel() val event = remember(viewModel) { viewModel.event - }.collectAsState() - val startDate = remember { - derivedStateOf { - Date(event.value?.startTime ?: 0L) - } + }.collectAsState(initial = null) + val startDate = remember(event.value?.startTime) { + Date(event.value?.startTime ?: 0L) } - val endDate = remember { - derivedStateOf { - Date(event.value?.endTime ?: 0L) - } + val endDate = remember(event.value?.endTime) { + Date(event.value?.endTime ?: 0L) } - val startCalendar = remember { - derivedStateOf { - val calendar = Calendar.getInstance() - calendar.time = startDate.value - calendar - } + val startCalendar = remember(startDate) { + val calendar = Calendar.getInstance() + calendar.time = startDate + calendar } - val endCalendar = remember { - derivedStateOf { - val calendar = Calendar.getInstance() - calendar.time = endDate.value - calendar - } + val endCalendar = remember(endDate) { + val calendar = Calendar.getInstance() + calendar.time = endDate + calendar } val appLocale = getCurrentAppLocale( @@ -111,8 +102,8 @@ fun EventDetailsView( val duration = remember(startDate, endDate) { // Make DateTimePeriod from start and end date val duration = Duration.between( - startDate.value.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime(), - endDate.value.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime() + startDate.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime(), + endDate.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime() ) // Format the duration as a string language agnostic using android.text.format.formatElapsedTime @@ -130,15 +121,13 @@ fun EventDetailsView( val locationText = event.value?.location?.trim() // Prevent leaking the url to third party services - val isValidUrlInLocation = remember { - derivedStateOf { - Patterns.WEB_URL.matcher(locationText ?: "").matches() - } + val isValidUrlInLocation = remember(locationText) { + Patterns.WEB_URL.matcher(locationText ?: "").matches() } val locationPosition by viewModel.locationPosition.collectAsState() val appLocaleString = appLocale.language LaunchedEffect(locationText, appLocaleString) { - if (!locationText.isNullOrEmpty() && !isValidUrlInLocation.value) { + if (!locationText.isNullOrEmpty() && !isValidUrlInLocation) { viewModel.resolveLocation(locationText, appLocaleString) } } @@ -190,23 +179,21 @@ fun EventDetailsView( ) { Text( // Check if the event is starting and ending on the same day or not - text = if (startCalendar.value.get(Calendar.DAY_OF_YEAR) == endCalendar.value.get( + text = if (startCalendar.get(Calendar.DAY_OF_YEAR) == endCalendar.get( Calendar.DAY_OF_YEAR ) - && startCalendar.value.get(Calendar.YEAR) == endCalendar.value.get( - Calendar.YEAR - ) + && startCalendar.get(Calendar.YEAR) == endCalendar.get(Calendar.YEAR) ) { - startDate.value.toInstant().atZone(ZoneId.systemDefault()) + startDate.toInstant().atZone(ZoneId.systemDefault()) .toLocalDate() .format(dateFormat) } else { stringResource( R.string.event_date_range, - startDate.value.toInstant().atZone(ZoneId.systemDefault()) + startDate.toInstant().atZone(ZoneId.systemDefault()) .toLocalDate() .format(dateFormat), - endDate.value.toInstant().atZone(ZoneId.systemDefault()) + endDate.toInstant().atZone(ZoneId.systemDefault()) .toLocalDate() .format(dateFormat) ) @@ -217,11 +204,10 @@ fun EventDetailsView( Text( text = stringResource( R.string.event_time_range, - startDate.value.toInstant().atZone(ZoneId.systemDefault()) + startDate.toInstant().atZone(ZoneId.systemDefault()) .toLocalTime() .format(timeFormat), - endDate.value.toInstant().atZone(ZoneId.systemDefault()) - .toLocalTime() + endDate.toInstant().atZone(ZoneId.systemDefault()).toLocalTime() .format(timeFormat) ), style = MaterialTheme.typography.titleSmall, @@ -310,7 +296,7 @@ fun EventDetailsView( DividerDefaults.Thickness, color = MaterialTheme.colorScheme.outlineVariant ) - if (locationPosition != null && !isValidUrlInLocation.value) { + if (locationPosition != null && !isValidUrlInLocation) { Column { Text( text = stringResource(R.string.location),