Skip to content

Commit

Permalink
Chore: Re-update to serialization based navigation (#118)
Browse files Browse the repository at this point in the history
Co-authored-by: Tim Ortel <100865202+TimOrtel@users.noreply.github.com>
  • Loading branch information
FelberMartin and TimOrtel authored Nov 28, 2024
1 parent f4f2d94 commit 8ceddfd
Show file tree
Hide file tree
Showing 17 changed files with 247 additions and 246 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,9 @@ import androidx.compose.ui.res.stringResource
import androidx.core.net.toUri
import androidx.lifecycle.lifecycleScope
import androidx.navigation.NavHostController
import androidx.navigation.NavOptions
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.rememberNavController
import androidx.navigation.navOptions
import com.google.android.gms.oss.licenses.OssLicensesMenuActivity
import de.tum.informatics.www1.artemis.native_app.android.BuildConfig
import de.tum.informatics.www1.artemis.native_app.android.R
Expand All @@ -40,16 +40,17 @@ import de.tum.informatics.www1.artemis.native_app.feature.courseregistration.cou
import de.tum.informatics.www1.artemis.native_app.feature.courseregistration.navigateToCourseRegistration
import de.tum.informatics.www1.artemis.native_app.feature.courseview.ui.course_overview.course
import de.tum.informatics.www1.artemis.native_app.feature.courseview.ui.course_overview.navigateToCourse
import de.tum.informatics.www1.artemis.native_app.feature.dashboard.DASHBOARD_DESTINATION
import de.tum.informatics.www1.artemis.native_app.feature.dashboard.DashboardScreen
import de.tum.informatics.www1.artemis.native_app.feature.dashboard.dashboard
import de.tum.informatics.www1.artemis.native_app.feature.dashboard.navigateToDashboard
import de.tum.informatics.www1.artemis.native_app.feature.exerciseview.ExerciseViewDestination
import de.tum.informatics.www1.artemis.native_app.feature.exerciseview.ExerciseViewMode
import de.tum.informatics.www1.artemis.native_app.feature.exerciseview.ExerciseViewUi
import de.tum.informatics.www1.artemis.native_app.feature.exerciseview.exercise
import de.tum.informatics.www1.artemis.native_app.feature.exerciseview.navigateToExercise
import de.tum.informatics.www1.artemis.native_app.feature.lectureview.lecture
import de.tum.informatics.www1.artemis.native_app.feature.lectureview.navigateToLecture
import de.tum.informatics.www1.artemis.native_app.feature.login.LOGIN_DESTINATION
import de.tum.informatics.www1.artemis.native_app.feature.login.LoginScreen
import de.tum.informatics.www1.artemis.native_app.feature.login.loginScreen
import de.tum.informatics.www1.artemis.native_app.feature.login.navigateToLogin
import de.tum.informatics.www1.artemis.native_app.feature.metis.shared.visiblemetiscontextreporter.ProvideLocalVisibleMetisContextManager
Expand Down Expand Up @@ -96,8 +97,8 @@ class MainActivity : AppCompatActivity(),
// When the user is logged in, immediately display the course overview.
val startDestination = runBlocking {
when (accountService.authenticationData.first()) {
is AccountService.AuthenticationData.LoggedIn -> DASHBOARD_DESTINATION
AccountService.AuthenticationData.NotLoggedIn -> LOGIN_DESTINATION
is AccountService.AuthenticationData.LoggedIn -> DashboardScreen
AccountService.AuthenticationData.NotLoggedIn -> LoginScreen(null)
}
}

Expand Down Expand Up @@ -190,7 +191,7 @@ class MainActivity : AppCompatActivity(),
}

@Composable
private fun MainActivityComposeUi(startDestination: String, navController: NavHostController) {
private fun MainActivityComposeUi(startDestination: Any, navController: NavHostController) {
// Listen for when the user get logged out (e.g. because their token has expired)
// This only happens when the user has the app running for multiple days or the user logged out manually
LaunchedEffect(Unit) {
Expand All @@ -199,7 +200,7 @@ class MainActivity : AppCompatActivity(),
.collect { (wasLoggedIn, isLoggedIn) ->
if (wasLoggedIn == true && !isLoggedIn) {
navController.navigateToLogin {
popUpTo(DASHBOARD_DESTINATION) {
popUpTo(DashboardScreen) {
inclusive = true
}
}
Expand Down Expand Up @@ -259,15 +260,17 @@ class MainActivity : AppCompatActivity(),
if (deepLink == null) {
// Navigate to the course overview and remove the login screen from the navigation stack.
navController.navigateToDashboard {
popUpTo(LOGIN_DESTINATION) {
popUpTo<LoginScreen> {
inclusive = true
}
}
} else {
try {
navController.navigate(
Uri.parse(deepLink),
NavOptions.Builder().setPopUpTo(LOGIN_DESTINATION, true).build()
navOptions {
popUpTo<LoginScreen>()
}
)
} catch (_: IllegalArgumentException) {
navController.navigateToDashboard {
Expand Down Expand Up @@ -347,7 +350,7 @@ class MainActivity : AppCompatActivity(),
quizParticipation(
onLeaveQuiz = {
val previousBackStackEntry = navController.previousBackStackEntry
if (previousBackStackEntry?.destination?.route == ExerciseViewDestination.EXERCISE_VIEW_ROUTE) {
if (previousBackStackEntry?.destination?.route == ExerciseViewUi::class.qualifiedName.orEmpty()) {
previousBackStackEntry.savedStateHandle[ExerciseViewDestination.REQUIRE_RELOAD_KEY] =
true
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import org.gradle.api.artifacts.VersionCatalogsExtension
import org.gradle.kotlin.dsl.configure
import org.gradle.kotlin.dsl.dependencies
import org.gradle.kotlin.dsl.getByType
import org.gradle.kotlin.dsl.kotlin
import kotlin.Suppress
import kotlin.apply
import kotlin.with
Expand All @@ -18,6 +19,7 @@ class AndroidFeatureConventionPlugin : Plugin<Project> {
pluginManager.apply {
apply("artemis.android.library")
apply("org.gradle.jacoco")
apply("org.jetbrains.kotlin.plugin.serialization")
}

extensions.configure<LibraryExtension> {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package de.tum.informatics.www1.artemis.native_app.core.ui.navigation

import android.os.Bundle
import androidx.navigation.NavType
import kotlinx.serialization.KSerializer
import kotlinx.serialization.json.Json

class KSerializableNavType<T>(
isNullableAllowed: Boolean,
private val serializer: KSerializer<T>
) : NavType<T>(isNullableAllowed) {

companion object {
private val json = Json {
coerceInputValues = true
}
}

override fun get(bundle: Bundle, key: String): T? {
return parseValue(bundle.getString(key) ?: return null)
}

override fun parseValue(value: String): T {
return json.decodeFromString(serializer, value)
}

override fun put(bundle: Bundle, key: String, value: T) {
bundle.putString(key, json.encodeToString(serializer, value))
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -64,23 +64,25 @@ import de.tum.informatics.www1.artemis.native_app.core.ui.common.course.computeC
import de.tum.informatics.www1.artemis.native_app.core.ui.getWindowSizeClass
import de.tum.informatics.www1.artemis.native_app.core.ui.markdown.MarkdownText
import kotlinx.coroutines.Deferred
import kotlinx.serialization.Serializable
import org.koin.androidx.compose.getViewModel

internal const val TEST_TAG_REGISTRABLE_COURSE_LIST = "registrable course list"

internal fun testTagForRegistrableCourse(courseId: Long) = "registrableCourse$courseId"

private const val COURSE_REGISTRATION_DESTINATION = "courseRegistration"
@Serializable
private data object CourseRegistrationScreen

fun NavController.navigateToCourseRegistration(builder: NavOptionsBuilder.() -> Unit) {
navigate(COURSE_REGISTRATION_DESTINATION, builder)
navigate(CourseRegistrationScreen, builder)
}

fun NavGraphBuilder.courseRegistration(
onNavigateUp: () -> Unit,
onRegisteredInCourse: (courseId: Long) -> Unit
) {
composable(COURSE_REGISTRATION_DESTINATION) {
composable<CourseRegistrationScreen> {
RegisterForCourseScreen(
modifier = Modifier.fillMaxSize(),
viewModel = getViewModel(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import androidx.navigation.NavType
import androidx.navigation.compose.composable
import androidx.navigation.navArgument
import androidx.navigation.navDeepLink
import androidx.navigation.toRoute
import de.tum.informatics.www1.artemis.native_app.core.data.DataState
import de.tum.informatics.www1.artemis.native_app.core.model.Course
import de.tum.informatics.www1.artemis.native_app.core.model.exercise.Exercise
Expand All @@ -56,6 +57,7 @@ import de.tum.informatics.www1.artemis.native_app.feature.metis.ui.NavigateToUse
import de.tum.informatics.www1.artemis.native_app.feature.metis.ui.NothingOpened
import de.tum.informatics.www1.artemis.native_app.feature.metis.ui.OpenedConversation
import de.tum.informatics.www1.artemis.native_app.feature.metis.ui.OpenedThread
import kotlinx.serialization.Serializable
import org.koin.androidx.compose.koinViewModel
import org.koin.core.parameter.parametersOf

Expand All @@ -66,8 +68,16 @@ internal const val TAB_COMMUNICATION = 2
internal const val DEFAULT_CONVERSATION_ID = -1L
internal const val DEFAULT_POST_ID = -1L

@Serializable
private data class CourseUiScreen(
val courseId: Long,
val conversationId: Long = DEFAULT_CONVERSATION_ID,
val postId: Long = DEFAULT_POST_ID,
val username: String = ""
)

fun NavController.navigateToCourse(courseId: Long, builder: NavOptionsBuilder.() -> Unit) {
navigate("course/$courseId", builder)
navigate(CourseUiScreen(courseId), builder)
}

fun NavGraphBuilder.course(
Expand All @@ -88,25 +98,15 @@ fun NavGraphBuilder.course(
generateLinks("courses/{courseId}/exercises") +
generateLinks("courses/{courseId}/messages?conversationId={conversationId}") +
generateLinks("courses/{courseId}/messages?username={username}")
composable(
route = "course/{courseId}",
arguments = listOf(
navArgument("courseId") { type = NavType.LongType; nullable = false },
navArgument("conversationId") {
type = NavType.LongType; defaultValue = DEFAULT_CONVERSATION_ID
},
navArgument("postId") { type = NavType.LongType; defaultValue = DEFAULT_POST_ID },
navArgument("username") { type = NavType.StringType; defaultValue = "" }
),
composable<CourseUiScreen>(
deepLinks = deepLinks
) { backStackEntry ->
val courseId = backStackEntry.arguments?.getLong("courseId")
checkNotNull(courseId)
val route: CourseUiScreen = backStackEntry.toRoute()
val courseId = route.courseId

val conversationId =
backStackEntry.arguments?.getLong("conversationId") ?: DEFAULT_CONVERSATION_ID
val postId = backStackEntry.arguments?.getLong("postId") ?: DEFAULT_POST_ID
val username = backStackEntry.arguments?.getString("username").orEmpty()
val conversationId = route.conversationId
val postId = route.postId
val username = route.username

CourseUiScreen(
modifier = Modifier.fillMaxSize(),
Expand Down
1 change: 0 additions & 1 deletion feature/dashboard/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
plugins {
id("artemis.android.feature")
id("artemis.android.library.compose")
kotlin("plugin.serialization")
}

android {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,25 +75,28 @@ import de.tum.informatics.www1.artemis.native_app.core.ui.common.course.Expanded
import de.tum.informatics.www1.artemis.native_app.core.ui.exercise.CoursePointsDecimalFormat
import de.tum.informatics.www1.artemis.native_app.feature.dashboard.service.BetaHintService
import kotlinx.coroutines.launch
import kotlinx.serialization.Serializable
import org.koin.androidx.compose.getViewModel
import org.koin.compose.koinInject
import java.text.DecimalFormat

const val DASHBOARD_DESTINATION = "dashboard"
internal const val TEST_TAG_COURSE_LIST = "TEST_TAG_COURSE_LIST"

internal fun testTagForCourse(courseId: Long) = "Course$courseId"

@Serializable
data object DashboardScreen

fun NavController.navigateToDashboard(builder: NavOptionsBuilder.() -> Unit) {
navigate(DASHBOARD_DESTINATION, builder)
navigate(DashboardScreen, builder)
}

fun NavGraphBuilder.dashboard(
onOpenSettings: () -> Unit,
onClickRegisterForCourse: () -> Unit,
onViewCourse: (courseId: Long) -> Unit
) {
composable(DASHBOARD_DESTINATION) {
composable<DashboardScreen> {
CoursesOverview(
modifier = Modifier.fillMaxSize(),
viewModel = getViewModel(),
Expand Down
Loading

0 comments on commit 8ceddfd

Please sign in to comment.