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 742d70bb437617758f0f381e26baa51189d4caaf
parent b9362b0f97961c9a97766d2690b6086717819f0e
Author: MTRNord <MTRNord@users.noreply.github.com>
Date:   Mon, 28 Jul 2025 21:05:14 +0200

Improve the content of the details page and fix the topbar title

Diffstat:
Mapp/build.gradle.kts | 3++-
Mapp/src/main/java/space/midnightthoughts/nordiccalendar/components/AppScaffold.kt | 8+++++++-
Mapp/src/main/java/space/midnightthoughts/nordiccalendar/screens/EventDetailsView.kt | 132+++++++++++++++++++++++++++++++++++++++++++++++++++++--------------------------
Mgradle/libs.versions.toml | 2++
4 files changed, 100 insertions(+), 45 deletions(-)

diff --git a/app/build.gradle.kts b/app/build.gradle.kts @@ -46,7 +46,6 @@ android { } dependencies { - implementation(libs.androidx.core.ktx) implementation(libs.androidx.lifecycle.runtime.ktx) implementation(libs.androidx.activity.compose) @@ -58,6 +57,8 @@ dependencies { implementation(libs.accompanist.permissions) implementation(libs.androidx.navigation.compose) implementation(libs.androidx.foundation.foundation) + implementation(libs.autolinktext) + testImplementation(libs.junit) androidTestImplementation(libs.androidx.junit) androidTestImplementation(libs.androidx.espresso.core) diff --git a/app/src/main/java/space/midnightthoughts/nordiccalendar/components/AppScaffold.kt b/app/src/main/java/space/midnightthoughts/nordiccalendar/components/AppScaffold.kt @@ -55,7 +55,13 @@ fun AppScaffold( Scaffold( topBar = { TopAppBar( - title = { title ?: Text(stringResource(R.string.app_name)) }, + title = { + if (title == null) { + Text(stringResource(R.string.app_name)) + } else { + Text(title) + } + }, navigationIcon = { if (isBackButtonVisible) { IconButton(onClick = { diff --git a/app/src/main/java/space/midnightthoughts/nordiccalendar/screens/EventDetailsView.kt b/app/src/main/java/space/midnightthoughts/nordiccalendar/screens/EventDetailsView.kt @@ -1,22 +1,32 @@ package space.midnightthoughts.nordiccalendar.screens -import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.text.selection.SelectionContainer +import androidx.compose.foundation.verticalScroll +import androidx.compose.material3.DividerDefaults +import androidx.compose.material3.HorizontalDivider +import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState -import androidx.compose.ui.Alignment +import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.AnnotatedString import androidx.compose.ui.unit.dp +import androidx.lifecycle.SavedStateHandle +import androidx.lifecycle.ViewModel +import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.viewmodel.compose.LocalViewModelStoreOwner import androidx.lifecycle.viewmodel.compose.viewModel import androidx.navigation.NavBackStackEntry import androidx.navigation.NavHostController +import sh.calvin.autolinktext.rememberAutoLinkText import space.midnightthoughts.nordiccalendar.R import space.midnightthoughts.nordiccalendar.components.AppScaffold import space.midnightthoughts.nordiccalendar.viewmodels.EventDetailsViewModel @@ -36,12 +46,12 @@ fun EventDetailsView( viewModel( viewModelStoreOwner = owner, key = "eventDetails_$eventId", - factory = object : androidx.lifecycle.ViewModelProvider.Factory { - override fun <T : androidx.lifecycle.ViewModel> create(modelClass: Class<T>): T { + factory = object : ViewModelProvider.Factory { + override fun <T : ViewModel> create(modelClass: Class<T>): T { @Suppress("UNCHECKED_CAST") return EventDetailsViewModel( app = context, - savedStateHandle = androidx.lifecycle.SavedStateHandle(mapOf("eventId" to eventId)) + savedStateHandle = SavedStateHandle(mapOf("eventId" to eventId)) ) as T } } @@ -49,50 +59,86 @@ fun EventDetailsView( } else { throw IllegalStateException("ViewModelStoreOwner or eventId is null") } - val event = viewModel.event.collectAsState().value + val event = remember(viewModel) { + viewModel.event + }.collectAsState(initial = null) val dateTimeFormat = SimpleDateFormat("dd.MM.yyyy HH:mm", Locale.getDefault()) + + val scrollState = rememberScrollState() + AppScaffold( - title = stringResource(R.string.event_details_title), + title = event.value?.title + ?: stringResource( + R.string.event_details, + backStackEntry.arguments?.getString("eventId") ?: "" + ), selectedDestination = "eventDetails", navController = navController, ) { innerPadding -> - Column( - modifier = innerPadding - .fillMaxSize() - .padding(16.dp), - verticalArrangement = Arrangement.Center, - horizontalAlignment = Alignment.CenterHorizontally - ) { - if (event != null) { - Text(event.title) - Spacer(modifier = Modifier.height(8.dp)) - if (!event.description.isNullOrBlank()) { - Text(event.description) - Spacer(modifier = Modifier.height(8.dp)) - } - Text( - "Start: " + dateTimeFormat.format(java.util.Date(event.startTime)) - ) - Text( - "Ende: " + dateTimeFormat.format(java.util.Date(event.endTime)) - ) - if (!event.location.isNullOrBlank()) { - Spacer(modifier = Modifier.height(8.dp)) - Text("Ort: ${event.location}") - } - if (!event.organizer.isNullOrBlank()) { - Spacer(modifier = Modifier.height(8.dp)) - Text("Organisator: ${event.organizer}") - } - } else { - Text( - stringResource( - R.string.event_details, - backStackEntry.arguments?.getString("eventId") ?: "" + SelectionContainer(modifier = innerPadding) { + Column( + modifier = Modifier + .verticalScroll(scrollState) + .padding(24.dp) + .fillMaxSize(), + ) { + if (event.value != null) { + Text( + dateTimeFormat.format( + java.util.Date( + event.value?.startTime ?: 0L + ) + ) + " - " + dateTimeFormat.format( + java.util.Date(event.value?.endTime ?: 0L) + ), + style = MaterialTheme.typography.bodyLarge, + color = MaterialTheme.colorScheme.primary, ) - ) - Spacer(modifier = Modifier.height(24.dp)) - Text("Event nicht gefunden oder wird geladen...") + if (!event.value?.location.isNullOrBlank()) { + Spacer(modifier = Modifier.height(16.dp)) + Text( + text = AnnotatedString.rememberAutoLinkText( + event.value?.location ?: "" + ), + style = MaterialTheme.typography.labelLarge.copy(color = MaterialTheme.colorScheme.onSurface), + modifier = Modifier.padding(bottom = 4.dp) + ) + } + if (!event.value?.organizer.isNullOrBlank()) { + Spacer(modifier = Modifier.height(16.dp)) + Text( + event.value?.organizer ?: "", + style = MaterialTheme.typography.labelLarge, + color = MaterialTheme.colorScheme.onSurface, + ) + } + Spacer(modifier = Modifier.height(24.dp)) + HorizontalDivider( + Modifier, + DividerDefaults.Thickness, + color = MaterialTheme.colorScheme.outlineVariant + ) + if (!event.value?.description.isNullOrBlank()) { + Spacer(modifier = Modifier.height(24.dp)) + + Text( + text = AnnotatedString.rememberAutoLinkText( + event.value?.description ?: "" + ), + style = MaterialTheme.typography.bodyMedium.copy(color = MaterialTheme.colorScheme.onSurface), + ) + } + + } else { + Text( + stringResource( + R.string.event_details, + backStackEntry.arguments?.getString("eventId") ?: "" + ) + ) + Spacer(modifier = Modifier.height(24.dp)) + Text("Event nicht gefunden oder wird geladen...") + } } } } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml @@ -11,6 +11,7 @@ composeBom = "2025.07.00" accompanistPermissions = "0.37.3" navigationCompose = "2.9.2" foundation = "1.8.3" +autolinktext = "2.0.2" [libraries] androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" } @@ -30,6 +31,7 @@ androidx-material3 = { group = "androidx.compose.material3", name = "material3" accompanist-permissions = { group = "com.google.accompanist", name = "accompanist-permissions", version.ref = "accompanistPermissions" } androidx-navigation-compose = { group = "androidx.navigation", name = "navigation-compose", version.ref = "navigationCompose" } androidx-foundation-foundation = { group = "androidx.compose.foundation", name = "foundation", version.ref = "foundation" } +autolinktext = { module = "sh.calvin.autolinktext:autolinktext", version.ref = "autolinktext" } [plugins] android-application = { id = "com.android.application", version.ref = "agp" }