Skip to content

Commit

Permalink
refactor: migrate from PreCompose to JetBrains ViewModel and Navigati…
Browse files Browse the repository at this point in the history
…on (#333)
  • Loading branch information
cbeyls authored Feb 28, 2025
1 parent da4c73d commit 929146b
Show file tree
Hide file tree
Showing 25 changed files with 181 additions and 197 deletions.
7 changes: 0 additions & 7 deletions androidApp/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -76,10 +76,3 @@ dependencies {
// Used for tags
implementation(project(":shared"))
}

configurations.configureEach {
if (name.endsWith("RuntimeClasspath")) {
// See https://github.com/Tlaster/PreCompose/issues/317
exclude("androidx.compose.ui", "ui-test-junit4-android")
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ class AgendaLayoutViewModelTest {

private val testSubject = AgendaLayoutViewModel(
roomsRepository = fakeStore,
scope = { CoroutineScope(Dispatchers.Unconfined) }
coroutineScope = CoroutineScope(Dispatchers.Unconfined)
)

@Test
Expand Down
11 changes: 7 additions & 4 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ googleid = "1.1.1"
horologist = "0.6.22"
jetbrainsCompose = "1.6.11"
jetbrainsLifecycle = "2.8.4"
jetbrainsNavigation = "2.7.0-alpha07"
junit = "4.13.2"
koin = "4.0.2"
kotlin = "2.1.10"
Expand All @@ -35,7 +36,6 @@ okio = "3.10.2"
openfeedback-m3 = "0.2.3"
playServices-auth = "21.3.0"
playServices-wearable = "19.0.0"
precompose = "1.6.1"
pullrefresh = "1.3.0"

[libraries]
Expand All @@ -60,6 +60,9 @@ firebase-crashlytics-ktx = { module = "com.google.firebase:firebase-crashlytics-
firebase-messaging = { module = "com.google.firebase:firebase-messaging" }
googleid = { group = "com.google.android.libraries.identity.googleid", name = "googleid", version.ref = "googleid" }
jetbrains-lifecycle-runtime = { module = "org.jetbrains.androidx.lifecycle:lifecycle-runtime", version.ref = "jetbrainsLifecycle" }
jetbrains-lifecycle-runtime-compose = { module = "org.jetbrains.androidx.lifecycle:lifecycle-runtime-compose", version.ref = "jetbrainsLifecycle" }
jetbrains-lifecycle-viewmodel-compose = { module = "org.jetbrains.androidx.lifecycle:lifecycle-viewmodel-compose", version.ref = "jetbrainsLifecycle" }
jetbrains-navigation-compose = { module = "org.jetbrains.androidx.navigation:navigation-compose", version.ref = "jetbrainsNavigation" }
junit = { module = "junit:junit", version.ref = "junit" }
kotlinx-coroutines-core = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "kotlinx-coroutines" }
kotlinx-coroutines-android = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-android", version.ref = "kotlinx-coroutines" }
Expand All @@ -74,11 +77,11 @@ okhttp = { group = "com.squareup.okhttp3", name = "okhttp", version.ref = "okhtt
okio = { group = "com.squareup.okio", name = "okio", version.ref = "okio" }
openfeedback-m3 = { module = "io.openfeedback:openfeedback-m3", version.ref = "openfeedback-m3" }
play-services-auth = { module = "com.google.android.gms:play-services-auth", version.ref = "playServices-auth" }
precompose = { module = "moe.tlaster:precompose", version.ref = "precompose" }
precompose-koin = { module = "moe.tlaster:precompose-koin", version.ref = "precompose" }
precompose-viewmodel = { module = "moe.tlaster:precompose-viewmodel", version.ref = "precompose" }

koin-android = { module = "io.insert-koin:koin-android", version.ref = "koin" }
koin-androidx-compose = { module = "io.insert-koin:koin-androidx-compose", version.ref = "koin" }
koin-compose = { module = "io.insert-koin:koin-compose", version.ref = "koin" }
koin-compose-viewmodel = { module = "io.insert-koin:koin-compose-viewmodel", version.ref = "koin" }
koin-core = { module = "io.insert-koin:koin-core", version.ref = "koin" }

android-gradlePlugin = { group = "com.android.tools.build", name = "gradle", version.ref = "androidGradlePlugin" }
Expand Down
6 changes: 2 additions & 4 deletions shared/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,8 @@ kotlin {

sourceSets {
commonMain.dependencies {
api(libs.moko.resources)
implementation(libs.moko.resources)

// Temporary
api(libs.precompose.koin)
api(project(":shared:ui"))
api(project(":shared:domain"))
api(project(":shared:di"))
Expand All @@ -36,7 +34,7 @@ kotlin {
}

configurations.configureEach {
// Remove unnecessary dependency of Precompose and Moko
// Remove unnecessary dependency of Moko
exclude(group = "androidx.appcompat", module = "appcompat")
}

Expand Down
4 changes: 2 additions & 2 deletions shared/di/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ kotlin {

sourceSets {
androidMain.dependencies {
implementation(libs.koin.androidx.compose)
implementation(libs.koin.android)
}

commonMain.dependencies {
Expand All @@ -32,7 +32,7 @@ kotlin {
}

configurations.configureEach {
// Remove unnecessary dependency of Precompose
// Remove unnecessary dependency of Precompose and Koin Android
exclude(group = "androidx.appcompat", module = "appcompat")
}

Expand Down
14 changes: 8 additions & 6 deletions shared/ui/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -21,18 +21,20 @@ kotlin {

sourceSets {
commonMain.dependencies {
api(libs.moko.resources.compose)
implementation(libs.moko.resources.compose)
implementation(compose.runtime)
implementation(compose.foundation)
implementation(compose.material3)
implementation(compose.ui)
implementation(compose.components.resources)
implementation(compose.materialIconsExtended)
implementation(compose.components.uiToolingPreview)
api(libs.coil.compose)
api(libs.precompose)
api(libs.precompose.viewmodel)
api(libs.precompose.koin)
implementation(libs.coil.compose)
implementation(libs.jetbrains.navigation.compose)
implementation(libs.jetbrains.lifecycle.runtime.compose)
implementation(libs.jetbrains.lifecycle.viewmodel.compose)
implementation(libs.koin.compose)
implementation(libs.koin.compose.viewmodel)
api(libs.openfeedback.m3)
implementation(libs.kotlinx.coroutines.core)
implementation(libs.okio) // Used by Openfeedback
Expand All @@ -52,7 +54,7 @@ kotlin {
}

configurations.configureEach {
// Remove unnecessary dependency of Precompose and Moko
// Remove unnecessary dependency of Moko
exclude(group = "androidx.appcompat", module = "appcompat")
// Disable Android Drawable support in Coil
exclude(group = "com.google.accompanist", module = "accompanist-drawablepainter")
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,19 @@
package com.androidmakers.ui

import androidx.compose.animation.EnterTransition
import androidx.compose.animation.ExitTransition
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.animation.scaleIn
import androidx.compose.animation.scaleOut
import androidx.compose.runtime.Composable
import androidx.compose.ui.platform.LocalContext
import fr.androidmakers.domain.PlatformContext
import moe.tlaster.precompose.navigation.transition.NavTransition

actual val defaultTransition: NavTransition = NavTransition()
actual val defaultEnterTransition: EnterTransition = fadeIn() + scaleIn(initialScale = 0.9f)
actual val defaultExitTransition: ExitTransition = fadeOut() + scaleOut(targetScale = 1.1f)
actual val defaultPopEnterTransition: EnterTransition = fadeIn() + scaleIn(initialScale = 1.1f)
actual val defaultPopExitTransition: ExitTransition = fadeOut() + scaleOut(targetScale = 0.9f)

@Composable
actual fun getPlatformContext(): PlatformContext = PlatformContext(LocalContext.current)
95 changes: 50 additions & 45 deletions shared/ui/src/commonMain/kotlin/com/androidmakers/ui/MainLayout.kt
Original file line number Diff line number Diff line change
@@ -1,27 +1,26 @@
package com.androidmakers.ui

import androidx.compose.animation.EnterTransition
import androidx.compose.animation.ExitTransition
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.navigation.NavDeepLink
import androidx.navigation.NavHostController
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController
import coil3.ImageLoader
import coil3.annotation.ExperimentalCoilApi
import coil3.compose.setSingletonImageLoaderFactory
import coil3.request.crossfade
import com.androidmakers.ui.agenda.SessionDetailScreen
import com.androidmakers.ui.agenda.SessionDetailViewModel
import com.androidmakers.ui.common.SigninCallbacks
import com.androidmakers.ui.common.navigation.AVALayout
import com.androidmakers.ui.common.navigation.MainNavigationRoute
import com.androidmakers.ui.speakers.SpeakerDetailsRoute
import com.androidmakers.ui.speakers.SpeakerDetailsViewModel
import fr.androidmakers.domain.PlatformContext
import fr.androidmakers.domain.model.SpeakerId
import moe.tlaster.precompose.koin.koinViewModel
import moe.tlaster.precompose.navigation.NavHost
import moe.tlaster.precompose.navigation.Navigator
import moe.tlaster.precompose.navigation.SwipeProperties
import moe.tlaster.precompose.navigation.path
import moe.tlaster.precompose.navigation.rememberNavigator
import moe.tlaster.precompose.navigation.transition.NavTransition
import org.koin.compose.viewmodel.koinViewModel
import org.koin.core.parameter.parametersOf

/**
Expand All @@ -41,14 +40,14 @@ fun MainLayout(
.build()
}

val navigator = rememberNavigator()
val navController = rememberNavController()
MainNavHost(
mainNavController = navigator,
mainNavController = navController,
onSessionClick = { sessionId ->
navigator.navigate("${MainNavigationRoute.SESSION_DETAIL.name}/$sessionId")
navController.navigate("${MainNavigationRoute.SESSION_DETAIL.name}/$sessionId")
},
navigateToSpeakerDetails = { speakerId ->
navigator.navigate("${MainNavigationRoute.SPEAKER_DETAIL.name}/$speakerId")
navController.navigate("${MainNavigationRoute.SPEAKER_DETAIL.name}/$speakerId")
},
versionCode = versionCode,
versionName = versionName,
Expand All @@ -61,7 +60,7 @@ fun MainLayout(
private fun MainNavHost(
versionCode: String,
versionName: String,
mainNavController: Navigator,
mainNavController: NavHostController,
onSessionClick: (sessionId: String) -> Unit,
navigateToSpeakerDetails: (SpeakerId) -> Unit,
signingCallbacks: SigninCallbacks,
Expand All @@ -78,56 +77,62 @@ private fun MainNavHost(
}

NavHost(
navigator = mainNavController,
initialRoute = MainNavigationRoute.AVA.name
navController = mainNavController,
startDestination = MainNavigationRoute.AVA.name,
enterTransition = { defaultEnterTransition },
exitTransition = { defaultExitTransition },
popEnterTransition = { defaultPopEnterTransition },
popExitTransition = { defaultPopExitTransition }
) {

scene(route = MainNavigationRoute.AVA.name) {

composable(route = MainNavigationRoute.AVA.name) {
AVALayout(
versionCode = versionCode,
versionName = versionName,
onSessionClick = onSessionClick,
navigateToSpeakerDetails = navigateToSpeakerDetails,
signinCallbacks = signingCallbacks,
versionCode = versionCode,
versionName = versionName,
onSessionClick = onSessionClick,
navigateToSpeakerDetails = navigateToSpeakerDetails,
signinCallbacks = signingCallbacks,
)
}

scene(
route = "${MainNavigationRoute.SESSION_DETAIL.name}/{sessionId}",
swipeProperties = SwipeProperties(),
deepLinks = listOf("https://androidmakers.fr/session/{sessionId}"),
navTransition = defaultTransition
) {

val sessionId = it.path<String>("sessionId")
val sessionDetailViewModel = koinViewModel(vmClass = SessionDetailViewModel::class) { parametersOf(sessionId) }
composable(
route = "${MainNavigationRoute.SESSION_DETAIL.name}/{sessionId}",
deepLinks = listOf(
NavDeepLink.Builder()
.setUriPattern("https://androidmakers.fr/session/{sessionId}")
.build()
)
) { backStackEntry ->
val sessionId = backStackEntry.arguments?.getString("sessionId").orEmpty()

SessionDetailScreen(
viewModel = sessionDetailViewModel,
onBackClick = { mainNavController.popBackStack() },
viewModel = koinViewModel { parametersOf(sessionId) },
onBackClick = { mainNavController.popBackStack() },
)
}

scene(
route = "${MainNavigationRoute.SPEAKER_DETAIL.name}/{speakerId}",
deepLinks = listOf("https://androidmakers.fr/speaker/{speakerId}"),
swipeProperties = SwipeProperties(),
navTransition = defaultTransition
composable(
route = "${MainNavigationRoute.SPEAKER_DETAIL.name}/{speakerId}",
deepLinks = listOf(
NavDeepLink.Builder()
.setUriPattern("https://androidmakers.fr/speaker/{speakerId}")
.build()
)
) { backstackEntry ->
val speakerId = backstackEntry.path<String>("speakerId")

val speakerDetailsViewModel = koinViewModel(vmClass = SpeakerDetailsViewModel::class) { parametersOf(speakerId) }
val speakerId = backstackEntry.arguments?.getString("speakerId").orEmpty()

SpeakerDetailsRoute(
speakerDetailsViewModel = speakerDetailsViewModel,
onBackClick = { mainNavController.popBackStack() },
speakerDetailsViewModel = koinViewModel { parametersOf(speakerId) },
onBackClick = { mainNavController.popBackStack() },
)
}
}
}

expect val defaultTransition: NavTransition
expect val defaultEnterTransition: EnterTransition
expect val defaultExitTransition: ExitTransition
expect val defaultPopEnterTransition: EnterTransition
expect val defaultPopExitTransition: ExitTransition

@Composable
expect fun getPlatformContext(): PlatformContext
Original file line number Diff line number Diff line change
Expand Up @@ -31,14 +31,14 @@ import com.androidmakers.ui.getPlatformContext
import dev.icerock.moko.resources.compose.painterResource
import dev.icerock.moko.resources.compose.stringResource
import fr.paug.androidmakers.ui.MR
import moe.tlaster.precompose.koin.koinViewModel
import org.koin.compose.viewmodel.koinViewModel

@Composable
fun AboutScreen(
versionName: String,
versionCode: String,
) {
val viewModel = koinViewModel(vmClass = AboutViewModel::class)
val viewModel = koinViewModel<AboutViewModel>()
Column(
modifier = Modifier
.fillMaxSize()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
package com.androidmakers.ui.about

import androidx.lifecycle.ViewModel
import fr.androidmakers.domain.interactor.OpenCocUseCase
import fr.androidmakers.domain.interactor.OpenFaqUseCase
import fr.androidmakers.domain.interactor.OpenXAccountUseCase
import fr.androidmakers.domain.interactor.OpenXHashtagUseCase
import fr.androidmakers.domain.interactor.OpenYoutubeUseCase
import moe.tlaster.precompose.viewmodel.ViewModel

class AboutViewModel(
val openXAccount: OpenXAccountUseCase,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,15 +37,15 @@ import fr.androidmakers.domain.utils.eventTimeZone
import fr.paug.androidmakers.ui.MR
import kotlinx.datetime.Clock
import kotlinx.datetime.todayIn
import moe.tlaster.precompose.koin.koinViewModel
import org.jetbrains.compose.ui.tooling.preview.Preview
import org.koin.compose.viewmodel.koinViewModel

@Composable
fun AgendaLayout(
agendaFilterDrawerState: DrawerState,
onSessionClick: (sessionId: String) -> Unit,
) {
val agendaLayoutViewModel = koinViewModel(vmClass = AgendaLayoutViewModel::class)
val agendaLayoutViewModel = koinViewModel<AgendaLayoutViewModel>()
val agendaLayoutState by agendaLayoutViewModel.state.collectAsState()

ModalNavigationDrawer(
Expand Down Expand Up @@ -75,7 +75,7 @@ private fun AgendaPagerOrLoading(
sessionFilters: List<SessionFilter>,
onSessionClick: (sessionId: String) -> Unit,
) {
val viewModel = koinViewModel(vmClass = AgendaPagerViewModel::class)
val viewModel = koinViewModel<AgendaPagerViewModel>()
ButtonRefreshableLceLayout(viewModel) { daySchedules ->
AgendaPager(
initialPageIndex = daySchedules.todayPageIndex(),
Expand Down
Loading

0 comments on commit 929146b

Please sign in to comment.