diff --git a/app/build.gradle b/app/build.gradle index b9ae6e0..1950abb 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -10,6 +10,13 @@ apply from: "${rootProject.projectDir}/gradle/compose-android-config.gradle" android { namespace 'com.moove' + + defaultConfig { + buildConfigField "String", "FIREBASE_DYNAMIC_LINK_HOST", "\"$firebase_dynamic_link_host\"" + manifestPlaceholders = [ + firebaseDynamicLinkHost: "\"$firebase_dynamic_link_host\"" + ] + } } dependencies { @@ -26,6 +33,7 @@ dependencies { implementation libs.androidx.fragment implementation libs.bundles.androidx.navigation implementation libs.bundles.androidx.lifecycle + implementation libs.bundles.androidx.compose // endregion // region Kotlin diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 19bcbe9..c1ef8b9 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -30,6 +30,52 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/java/com/moove/app/MooveApp.kt b/app/src/main/java/com/moove/app/MooveApp.kt index f151a58..01ca86c 100644 --- a/app/src/main/java/com/moove/app/MooveApp.kt +++ b/app/src/main/java/com/moove/app/MooveApp.kt @@ -2,10 +2,11 @@ package com.moove.app import android.app.Application import com.moove.app.di.coroutineModule +import com.moove.app.di.deepLinkModule import com.moove.app.di.exceptionsModule import com.moove.app.di.mainModule import com.moove.app.di.netModule -import com.moove.app.di.ticketsModule +import com.moove.tickets.di.ticketsModule import org.koin.android.ext.koin.androidContext import org.koin.android.ext.koin.androidLogger import org.koin.core.context.startKoin @@ -28,6 +29,7 @@ open class MooveApp : Application() { exceptionsModule, ticketsModule, netModule, + deepLinkModule, ) } } diff --git a/app/src/main/java/com/moove/app/di/DeepLinkModule.kt b/app/src/main/java/com/moove/app/di/DeepLinkModule.kt new file mode 100644 index 0000000..160c45f --- /dev/null +++ b/app/src/main/java/com/moove/app/di/DeepLinkModule.kt @@ -0,0 +1,42 @@ +package com.moove.app.di + +import com.google.firebase.Firebase +import com.google.firebase.dynamiclinks.dynamicLinks +import com.moove.BuildConfig +import com.moove.app.feature.deeplink.data.DeeplinkDataRepository +import com.moove.app.feature.deeplink.data.DynamicLinkDataRepository +import com.moove.app.feature.deeplink.data.local.AppDeepLinkLocalDataSource +import com.moove.app.feature.deeplink.data.remote.FirebaseDynamicLinkDataSource +import com.moove.app.feature.deeplink.presentation.DeepLinkAppNavigator +import com.moove.shared.feature.deeplink.domain.DeeplinkRepository +import com.moove.shared.feature.deeplink.domain.DynamicLinkRepository +import com.moove.shared.feature.deeplink.domain.GetDeeplinkUseCase +import com.moove.shared.feature.deeplink.domain.GetDynamicLinkUseCase +import com.moove.shared.feature.deeplink.presentation.DeepLinkNavigator +import org.koin.dsl.module + +val deepLinkModule = module { + + factory { DeeplinkDataRepository(get()) } + factory { DynamicLinkDataRepository(get()) } + factory { + DeepLinkAppNavigator( + ticketsNavigator = get(), + globalAppNavigator = get(), + ) + } + factory { + FirebaseDynamicLinkDataSource( + host = BuildConfig.FIREBASE_DYNAMIC_LINK_HOST, +// firebaseDynamicLinks = Firebase.dynamicLinks + ) + } + factory { AppDeepLinkLocalDataSource() } + factory { + GetDeeplinkUseCase( + deeplinkRepository = get(), + getDynamicLinkUseCase = get(), + ) + } + factory { GetDynamicLinkUseCase(get()) } +} diff --git a/app/src/main/java/com/moove/app/di/MainModule.kt b/app/src/main/java/com/moove/app/di/MainModule.kt index 6e462e5..5c0fc65 100644 --- a/app/src/main/java/com/moove/app/di/MainModule.kt +++ b/app/src/main/java/com/moove/app/di/MainModule.kt @@ -1,20 +1,38 @@ package com.moove.app.di -import android.app.Activity -import com.moove.app.main.AppNavigator +import com.moove.app.feature.home.HomeNavigator +import com.moove.app.feature.home.HomeViewModel import com.moove.app.main.MainActivityViewModel +import com.moove.app.main.MainNavigator +import com.moove.app.navigation.AppNavigator +import com.moove.shared.navigation.GlobalAppNavigator +import com.moove.shared.navigation.ScreenNavigator +import com.moove.shared.navigation.TicketsNavigator import org.koin.androidx.viewmodel.dsl.viewModelOf +import org.koin.core.module.dsl.factoryOf +import org.koin.dsl.binds import org.koin.dsl.module val mainModule = module { - viewModelOf(::MainActivityViewModel) - factory { AppNavigator( navController = get(), - coroutineScope = get(), - context = get(), + ) + } binds arrayOf( + ScreenNavigator::class, + GlobalAppNavigator::class, + TicketsNavigator::class, + ) + + factory { + HomeNavigator( + navController = get(), + screenNavigator = get(), ) } + viewModelOf(::HomeViewModel) + + viewModelOf(::MainActivityViewModel) + factoryOf(::MainNavigator) } diff --git a/app/src/main/java/com/moove/app/feature/deeplink/data/DeeplinkDataRepository.kt b/app/src/main/java/com/moove/app/feature/deeplink/data/DeeplinkDataRepository.kt new file mode 100644 index 0000000..bcfe4c8 --- /dev/null +++ b/app/src/main/java/com/moove/app/feature/deeplink/data/DeeplinkDataRepository.kt @@ -0,0 +1,14 @@ +package com.moove.app.feature.deeplink.data + +import com.moove.app.feature.deeplink.data.local.AppDeepLinkLocalDataSource +import com.moove.shared.feature.deeplink.domain.DeepLink +import com.moove.shared.feature.deeplink.domain.DeeplinkRepository + +class DeeplinkDataRepository( + private val localDataSource: AppDeepLinkLocalDataSource, +) : DeeplinkRepository { + + override suspend fun getDeepLink(uri: String): DeepLink { + return localDataSource.getDeepLinkData(uri) + } +} diff --git a/app/src/main/java/com/moove/app/feature/deeplink/data/DynamicLinkDataRepository.kt b/app/src/main/java/com/moove/app/feature/deeplink/data/DynamicLinkDataRepository.kt new file mode 100644 index 0000000..7dfcd0a --- /dev/null +++ b/app/src/main/java/com/moove/app/feature/deeplink/data/DynamicLinkDataRepository.kt @@ -0,0 +1,13 @@ +package com.moove.app.feature.deeplink.data + +import com.moove.app.feature.deeplink.data.remote.FirebaseDynamicLinkDataSource +import com.moove.shared.feature.deeplink.domain.DynamicLinkRepository + +class DynamicLinkDataRepository( + private val dataSource: FirebaseDynamicLinkDataSource, +) : DynamicLinkRepository { + + override suspend fun parseLink(uri: String): String? { + return dataSource.parseLink(uri) + } +} diff --git a/app/src/main/java/com/moove/app/feature/deeplink/data/local/AppDeepLinkLocalDataSource.kt b/app/src/main/java/com/moove/app/feature/deeplink/data/local/AppDeepLinkLocalDataSource.kt new file mode 100644 index 0000000..2567502 --- /dev/null +++ b/app/src/main/java/com/moove/app/feature/deeplink/data/local/AppDeepLinkLocalDataSource.kt @@ -0,0 +1,93 @@ +package com.moove.app.feature.deeplink.data.local + +import com.moove.app.feature.deeplink.domain.AppDeepLink +import com.moove.core.kotlin.text.matchesPattern +import com.moove.shared.feature.deeplink.domain.DeepLink +import com.moove.tickets.domain.model.Fare +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext +import java.net.URI +import java.net.URLDecoder +import java.nio.charset.StandardCharsets + +class AppDeepLinkLocalDataSource( + private val backgroundDispatcher: CoroutineDispatcher = Dispatchers.IO, +) { + + companion object { + private const val RYDER_ID = "ryderId" + private const val PRICE = "price" + + const val HOME = "moove://app/home" + const val FARE_LIST = "moove://app/fare_list" + const val CONFIRM_CONFIRMATION = "/ticket/confirmation" + const val MOOVE_CONFIRM_CONFIRMATION = "moove://app/confirmation" + } + + suspend fun getDeepLinkData(uri: String): DeepLink = withContext(backgroundDispatcher) { + when { + uri.matchesPattern(CONFIRM_CONFIRMATION) -> { + val innerUri = URI.create(uri) + val params = getQueryParams(innerUri) + AppDeepLink.Confirmation( + ryderId = params[RYDER_ID]!!, + fare = Fare( + description = "", + price = params[PRICE]?.toFloat()!! + ), + ) + } + + uri.matchesPattern(MOOVE_CONFIRM_CONFIRMATION) -> { + val innerUri = URI.create(uri) + val params = getQueryParams(innerUri) + AppDeepLink.Confirmation( + ryderId = params[RYDER_ID]!!, + fare = Fare( + description = "", + price = params[PRICE]?.toFloat()!! + ), + ) + } + + uri.isThat(FARE_LIST) -> { + val innerUri = URI.create(uri) + val params = getQueryParams(innerUri) + AppDeepLink.FareList(ryderId = params[RYDER_ID]!!) + } + + uri.isThat(HOME) || uri.matchesPattern(HOME) -> AppDeepLink.Home + else -> AppDeepLink.Unknown + } + } + + private fun String.isThat(type: String): Boolean { + /** + * Handle two cases + * app/profile/ and app/profile + */ + return contains(type, ignoreCase = true) + } + + private fun getQueryParams(url: URI): Map { + val query = url.query ?: return emptyMap() + return query + .split("&".toRegex()) + .filter { it.isNotEmpty() } + .map(::mapQueryParameter) + .associateBy(keySelector = { it.first }, valueTransform = { it.second }) + } + + private fun mapQueryParameter(query: String): Pair { + val index = query.indexOf("=") + val key = if (index > 0) query.substring(0, index) else query + val value = if (index > 0 && query.length > index + 1) { + query.substring(index + 1) + } else null + return Pair( + URLDecoder.decode(key, StandardCharsets.UTF_8.name()), + URLDecoder.decode(value, StandardCharsets.UTF_8.name()) + ) + } +} diff --git a/app/src/main/java/com/moove/app/feature/deeplink/data/remote/FirebaseDynamicLinkDataSource.kt b/app/src/main/java/com/moove/app/feature/deeplink/data/remote/FirebaseDynamicLinkDataSource.kt new file mode 100644 index 0000000..8e5041a --- /dev/null +++ b/app/src/main/java/com/moove/app/feature/deeplink/data/remote/FirebaseDynamicLinkDataSource.kt @@ -0,0 +1,27 @@ +package com.moove.app.feature.deeplink.data.remote + +import android.net.Uri +import com.google.firebase.dynamiclinks.FirebaseDynamicLinks +import com.moove.core.kotlin.text.matchesPattern +import com.moove.shared.feature.deeplink.domain.exceptions.DynamicLinkParseException +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.tasks.await +import kotlinx.coroutines.withContext + +class FirebaseDynamicLinkDataSource( + private val host: String, +// private val firebaseDynamicLinks: FirebaseDynamicLinks, + private val backgroundDispatcher: CoroutineDispatcher = Dispatchers.IO, +) { + + suspend fun parseLink(uri: String): String? = withContext(backgroundDispatcher) { + if (uri.matchesPattern(host).not()) return@withContext null + try { +// firebaseDynamicLinks.getDynamicLink(Uri.parse(uri)).await().link?.toString() + "https://moove.page.link/45hj45j" + } catch (e: Exception) { + throw DynamicLinkParseException(cause = e) + } + } +} diff --git a/app/src/main/java/com/moove/app/feature/deeplink/domain/AppDeepLink.kt b/app/src/main/java/com/moove/app/feature/deeplink/domain/AppDeepLink.kt new file mode 100644 index 0000000..5bc693e --- /dev/null +++ b/app/src/main/java/com/moove/app/feature/deeplink/domain/AppDeepLink.kt @@ -0,0 +1,15 @@ +package com.moove.app.feature.deeplink.domain + +import com.moove.shared.feature.deeplink.domain.DeepLink +import com.moove.tickets.domain.model.Fare + +sealed class AppDeepLink: DeepLink { + data object Unknown : AppDeepLink() + data object Home : AppDeepLink() + data class FareList(val ryderId: String) : AppDeepLink() + + data class Confirmation( + val ryderId: String, + val fare: Fare, + ) : AppDeepLink() +} diff --git a/app/src/main/java/com/moove/app/feature/deeplink/presentation/DeepLinkAppNavigator.kt b/app/src/main/java/com/moove/app/feature/deeplink/presentation/DeepLinkAppNavigator.kt new file mode 100644 index 0000000..294cfc4 --- /dev/null +++ b/app/src/main/java/com/moove/app/feature/deeplink/presentation/DeepLinkAppNavigator.kt @@ -0,0 +1,28 @@ +package com.moove.app.feature.deeplink.presentation + +import com.moove.app.feature.deeplink.domain.AppDeepLink +import com.moove.shared.feature.deeplink.domain.DeepLink +import com.moove.shared.feature.deeplink.presentation.DeepLinkNavigator +import com.moove.shared.navigation.GlobalAppNavigator +import com.moove.shared.navigation.TicketsNavigator + +class DeepLinkAppNavigator( + private val globalAppNavigator: GlobalAppNavigator, + private val ticketsNavigator: TicketsNavigator, +) : DeepLinkNavigator { + override fun navigateTo(link: DeepLink) { + when (link) { + is AppDeepLink.FareList -> ticketsNavigator.goFares(link.ryderId) + is AppDeepLink.Confirmation -> { + ticketsNavigator.goFares(link.ryderId) + ticketsNavigator.goToConfirmation( + ryderId = link.ryderId, + fareDescription = link.fare.description, + farePrice = link.fare.price + ) + } + + is AppDeepLink.Home, AppDeepLink.Unknown -> globalAppNavigator.goHome() + } + } +} diff --git a/app/src/main/java/com/moove/app/feature/home/HomeFragment.kt b/app/src/main/java/com/moove/app/feature/home/HomeFragment.kt new file mode 100644 index 0000000..98a063b --- /dev/null +++ b/app/src/main/java/com/moove/app/feature/home/HomeFragment.kt @@ -0,0 +1,28 @@ +package com.moove.app.feature.home + +import android.os.Bundle +import android.view.View +import androidx.fragment.app.Fragment +import androidx.lifecycle.lifecycleScope +import androidx.navigation.fragment.findNavController +import com.moove.shared.R +import com.moove.shared.databinding.ComposeFragmentBinding +import com.moove.shared.presentation.compose.platform.setAppComposeContent +import com.moove.shared.presentation.fragment.delegate.viewBinding +import org.koin.android.ext.android.inject +import org.koin.core.parameter.parametersOf + +class HomeFragment : Fragment(R.layout.compose_fragment) { + + private val viewBinding by viewBinding(ComposeFragmentBinding::bind) + private val navigator: HomeNavigator by inject { + parametersOf(findNavController(), lifecycleScope, requireContext()) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + setAppComposeContent(viewBinding.compose) { + HomeRoute(navigator = navigator) + } + } +} diff --git a/app/src/main/java/com/moove/app/feature/home/HomeNavigator.kt b/app/src/main/java/com/moove/app/feature/home/HomeNavigator.kt new file mode 100644 index 0000000..ea8324c --- /dev/null +++ b/app/src/main/java/com/moove/app/feature/home/HomeNavigator.kt @@ -0,0 +1,19 @@ +package com.moove.app.feature.home + +import androidx.navigation.NavController +import com.moove.shared.navigation.ScreenNavigator +import com.moove.shared.navigation.navigateSafely + +class HomeNavigator( + private val navController: NavController, + private val screenNavigator: ScreenNavigator, +) : ScreenNavigator { + + fun goRyderList() { + navController.navigateSafely(HomeFragmentDirections.actionHomeFragmentToTicketsFlow()) + } + + override fun goBack() { + screenNavigator.goBack() + } +} diff --git a/app/src/main/java/com/moove/app/feature/home/HomeRoute.kt b/app/src/main/java/com/moove/app/feature/home/HomeRoute.kt new file mode 100644 index 0000000..272ce22 --- /dev/null +++ b/app/src/main/java/com/moove/app/feature/home/HomeRoute.kt @@ -0,0 +1,39 @@ +package com.moove.app.feature.home + +import androidx.compose.material.ScaffoldState +import androidx.compose.material.rememberScaffoldState +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import com.moove.shared.presentation.viewmodel.composableEffect +import com.moove.shared.presentation.viewmodel.composableState +import org.koin.androidx.compose.koinViewModel + +@Composable +fun HomeRoute( + navigator: HomeNavigator, + viewModel: HomeViewModel = koinViewModel(), + scaffoldState: ScaffoldState = rememberScaffoldState(), +) { + val state by viewModel.composableState() + + HomeScreen( + uiState = state, + scaffoldState = scaffoldState, + onRyderClick = viewModel::onRyderClick, + ) + + viewModel.RenderEffect(navigator = navigator) +} + +@Composable +private fun HomeViewModel.RenderEffect( + navigator: HomeNavigator, +) { + composableEffect { effect -> + when (effect) { + is HomeEffect.GoToRyderList -> { + navigator.goRyderList() + } + } + } +} diff --git a/app/src/main/java/com/moove/app/feature/home/HomeScreen.kt b/app/src/main/java/com/moove/app/feature/home/HomeScreen.kt new file mode 100644 index 0000000..ff6f07d --- /dev/null +++ b/app/src/main/java/com/moove/app/feature/home/HomeScreen.kt @@ -0,0 +1,60 @@ +package com.moove.app.feature.home + +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.statusBarsPadding +import androidx.compose.material.ScaffoldState +import androidx.compose.material.Text +import androidx.compose.material.rememberScaffoldState +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.tooling.preview.Preview +import com.moove.design_system.compose.AppTheme +import com.moove.design_system.compose.Button +import com.moove.design_system.compose.Scaffold +import com.moove.design_system.compose.Spacing + +@Composable +fun HomeScreen( + uiState: HomeState, + scaffoldState: ScaffoldState = rememberScaffoldState(), + onRyderClick: () -> Unit, +) { + Scaffold( + modifier = Modifier.statusBarsPadding(), + scaffoldState = scaffoldState, + content = { + Box( + modifier = Modifier.fillMaxSize(), + contentAlignment = Alignment.Center + ) { + Button( + modifier = Modifier + .clickable(onClick = { onRyderClick() }) + .padding(horizontal = Spacing.S, vertical = Spacing.S), + onClick = onRyderClick + ) { + Text( + text = "Go to Ryders list", + style = AppTheme.typography.material.h1, + maxLines = 1, + ) + } + } + } + ) +} + +@Preview(name = "Home Content", showBackground = true) +@Composable +fun PreviewHomeContent() { + AppTheme { + HomeScreen( + uiState = HomeState(), + onRyderClick = {} + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/moove/app/feature/home/HomeState.kt b/app/src/main/java/com/moove/app/feature/home/HomeState.kt new file mode 100644 index 0000000..577ee45 --- /dev/null +++ b/app/src/main/java/com/moove/app/feature/home/HomeState.kt @@ -0,0 +1,11 @@ +package com.moove.app.feature.home + +import android.os.Parcelable +import kotlinx.parcelize.Parcelize + +@Parcelize +class HomeState : Parcelable + +sealed class HomeEffect { + data object GoToRyderList : HomeEffect() +} diff --git a/app/src/main/java/com/moove/app/feature/home/HomeViewModel.kt b/app/src/main/java/com/moove/app/feature/home/HomeViewModel.kt new file mode 100644 index 0000000..91275f6 --- /dev/null +++ b/app/src/main/java/com/moove/app/feature/home/HomeViewModel.kt @@ -0,0 +1,26 @@ +package com.moove.app.feature.home + +import androidx.lifecycle.ViewModel +import com.moove.core.exception.ExceptionHandler +import com.moove.core.exception.asCoroutineExceptionHandler +import org.orbitmvi.orbit.Container +import org.orbitmvi.orbit.ContainerHost +import org.orbitmvi.orbit.syntax.simple.intent +import org.orbitmvi.orbit.syntax.simple.postSideEffect +import org.orbitmvi.orbit.viewmodel.container + +class HomeViewModel( + exceptionHandler: ExceptionHandler, +) : ViewModel(), ContainerHost { + + override val container: Container = container( + initialState = HomeState(), + buildSettings = { + this.exceptionHandler = exceptionHandler.asCoroutineExceptionHandler() + }, + ) + + fun onRyderClick() = intent { + postSideEffect(HomeEffect.GoToRyderList) + } +} diff --git a/app/src/main/java/com/moove/app/main/AppNavigator.kt b/app/src/main/java/com/moove/app/main/AppNavigator.kt deleted file mode 100644 index d3d8be3..0000000 --- a/app/src/main/java/com/moove/app/main/AppNavigator.kt +++ /dev/null @@ -1,11 +0,0 @@ -package com.moove.app.main - -import android.content.Context -import androidx.navigation.NavController -import kotlinx.coroutines.CoroutineScope - -class AppNavigator( - private val navController: NavController, - private val coroutineScope: CoroutineScope, - private val context: Context, -) \ No newline at end of file diff --git a/app/src/main/java/com/moove/app/main/MainActivity.kt b/app/src/main/java/com/moove/app/main/MainActivity.kt index 24299c7..c98c79f 100644 --- a/app/src/main/java/com/moove/app/main/MainActivity.kt +++ b/app/src/main/java/com/moove/app/main/MainActivity.kt @@ -1,31 +1,78 @@ package com.moove.app.main +import android.content.Intent import android.os.Build import android.os.Bundle +import android.widget.Toast +import androidx.activity.addCallback import androidx.appcompat.app.AppCompatActivity import androidx.core.view.WindowCompat +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.lifecycleScope +import androidx.lifecycle.repeatOnLifecycle +import androidx.navigation.fragment.NavHostFragment +import com.moove.R import com.moove.databinding.ActivityMainBinding +import kotlinx.coroutines.launch +import org.koin.android.ext.android.inject import org.koin.androidx.viewmodel.ext.android.viewModel +import org.koin.core.parameter.parametersOf class MainActivity : AppCompatActivity() { - private lateinit var viewBinding: ActivityMainBinding private val mainViewModel: MainActivityViewModel by viewModel() + private val navigator: MainNavigator by inject { + parametersOf( + (supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as NavHostFragment) + .navController, + lifecycleScope, + this + ) + } + private lateinit var viewBinding: ActivityMainBinding override fun onCreate(savedInstanceState: Bundle?) { WindowCompat.setDecorFitsSystemWindows(window, false) super.onCreate(savedInstanceState) viewBinding = ActivityMainBinding.inflate(layoutInflater) setContentView(viewBinding.root) + + mainViewModel.handleIntent(intent) + + observeSideEffect() + } + + override fun onNewIntent(intent: Intent) { + super.onNewIntent(intent) + mainViewModel.handleIntent(intent) + } + + private fun observeSideEffect() { + lifecycleScope.launch { + val sideEffectFlow = mainViewModel.container.sideEffectFlow + lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) { + sideEffectFlow.collect { effect -> + when (effect) { + is MainActivityEffect.NavigateDeepLink -> navigator.navigateDeepLink(effect.deepLink) + MainActivityEffect.ShowGenericError -> Toast.makeText( + this@MainActivity, + "Generic Error", + Toast.LENGTH_SHORT + ) + } + } + } + } } - @Suppress("OVERRIDE_DEPRECATION", "DEPRECATION") override fun onBackPressed() { + val backStackEntryCount = supportFragmentManager.primaryNavigationFragment + ?.childFragmentManager + ?.backStackEntryCount ?: 0 + if (Build.VERSION.SDK_INT == Build.VERSION_CODES.Q && isTaskRoot && - (supportFragmentManager.primaryNavigationFragment?.childFragmentManager?.backStackEntryCount - ?: 0) == 0 && - supportFragmentManager.backStackEntryCount == 0 + backStackEntryCount == 0 && supportFragmentManager.backStackEntryCount == 0 ) { finishAfterTransition() } else { diff --git a/app/src/main/java/com/moove/app/main/MainActivityState.kt b/app/src/main/java/com/moove/app/main/MainActivityState.kt index 4d49096..b94a814 100644 --- a/app/src/main/java/com/moove/app/main/MainActivityState.kt +++ b/app/src/main/java/com/moove/app/main/MainActivityState.kt @@ -1,9 +1,13 @@ package com.moove.app.main import android.os.Parcelable +import com.moove.shared.feature.deeplink.domain.DeepLink import kotlinx.parcelize.Parcelize @Parcelize class MainActivityState : Parcelable -sealed class MainActivityEffect +sealed class MainActivityEffect { + data object ShowGenericError : MainActivityEffect() + data class NavigateDeepLink(val deepLink: DeepLink) : MainActivityEffect() +} diff --git a/app/src/main/java/com/moove/app/main/MainActivityViewModel.kt b/app/src/main/java/com/moove/app/main/MainActivityViewModel.kt index 48cba3d..cd9d06a 100644 --- a/app/src/main/java/com/moove/app/main/MainActivityViewModel.kt +++ b/app/src/main/java/com/moove/app/main/MainActivityViewModel.kt @@ -1,24 +1,43 @@ package com.moove.app.main -import androidx.lifecycle.SavedStateHandle +import android.content.Intent import androidx.lifecycle.ViewModel import com.moove.core.exception.ExceptionHandler import com.moove.core.exception.asCoroutineExceptionHandler +import com.moove.shared.feature.deeplink.domain.GetDeeplinkUseCase +import com.moove.shared.presentation.viewmodel.executeUseCase import org.orbitmvi.orbit.Container import org.orbitmvi.orbit.ContainerHost +import org.orbitmvi.orbit.syntax.simple.intent +import org.orbitmvi.orbit.syntax.simple.postSideEffect import org.orbitmvi.orbit.viewmodel.container class MainActivityViewModel( exceptionHandler: ExceptionHandler, - savedStateHandle: SavedStateHandle, -) : ViewModel(), - ContainerHost { + private val getDeeplinkUseCase: GetDeeplinkUseCase, +) : ViewModel(), ContainerHost { override val container: Container = container( initialState = MainActivityState(), - savedStateHandle = savedStateHandle, buildSettings = { this.exceptionHandler = exceptionHandler.asCoroutineExceptionHandler() }, ) + + fun handleIntent(intent: Intent?) = intent { + if (intent == null) return@intent + + val uri = getDeepLinkFromIntent(intent) + if (uri.isNullOrEmpty()) return@intent + + executeUseCase { getDeeplinkUseCase(uri) } + .onSuccess { deeplink -> postSideEffect(MainActivityEffect.NavigateDeepLink(deeplink)) } + .onFailure { postSideEffect(MainActivityEffect.ShowGenericError) } + } + + private fun getDeepLinkFromIntent(intent: Intent): String? { + return intent.takeIf { + Intent.ACTION_VIEW == it.action || Intent.ACTION_MAIN == it.action + }?.dataString + } } diff --git a/app/src/main/java/com/moove/app/main/MainNavigator.kt b/app/src/main/java/com/moove/app/main/MainNavigator.kt new file mode 100644 index 0000000..455819a --- /dev/null +++ b/app/src/main/java/com/moove/app/main/MainNavigator.kt @@ -0,0 +1,13 @@ +package com.moove.app.main + +import com.moove.shared.feature.deeplink.domain.DeepLink +import com.moove.shared.feature.deeplink.presentation.DeepLinkNavigator + +class MainNavigator( + private val deepLinkNavigator: DeepLinkNavigator, +) { + + fun navigateDeepLink(link: DeepLink) { + deepLinkNavigator.navigateTo(link) + } +} diff --git a/app/src/main/java/com/moove/app/navigation/AppNavigator.kt b/app/src/main/java/com/moove/app/navigation/AppNavigator.kt new file mode 100644 index 0000000..adecd21 --- /dev/null +++ b/app/src/main/java/com/moove/app/navigation/AppNavigator.kt @@ -0,0 +1,44 @@ +package com.moove.app.navigation + +import androidx.navigation.NavController +import com.moove.MobileNavigationDirections +import com.moove.app.feature.home.HomeFragmentDirections +import com.moove.shared.navigation.GlobalAppNavigator +import com.moove.shared.navigation.navigateSafely +import com.moove.tickets.presentation.fare.FareListFragmentDirections +import com.moove.tickets.presentation.fare.model.FareModel +import com.moove.tickets.presentation.list.RyderListFragmentDirections + +class AppNavigator( + private val navController: NavController, +) : GlobalAppNavigator { + + override fun goFares(ryderId: String) { + navController.navigateSafely(HomeFragmentDirections.actionHomeFragmentToTicketsFlow()) + navController.navigateSafely( + RyderListFragmentDirections.actionRydersFragmentToFareListFragment(ryderId = ryderId) + ) + } + + override fun goToConfirmation(ryderId: String, fareDescription: String, farePrice: Float) { + navController.navigateSafely( + FareListFragmentDirections.actionFareListFragmentToConfirmationFragment( + ryderId = ryderId, + fare = FareModel( + description = fareDescription, + price = farePrice + ) + ) + ) + } + + override fun goBack() { + navController.navigateUp() + } + + override fun goHome() { + navController.navigateSafely( + MobileNavigationDirections.actionGlobalGoHome() + ) + } +} \ No newline at end of file diff --git a/app/src/main/res/navigation/app_main_navigation.xml b/app/src/main/res/navigation/app_main_navigation.xml index f46ecaf..f55f7b6 100644 --- a/app/src/main/res/navigation/app_main_navigation.xml +++ b/app/src/main/res/navigation/app_main_navigation.xml @@ -3,7 +3,21 @@ xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:id="@+id/mobile_navigation" - app:startDestination="@id/tickets_flow"> + app:startDestination="@id/homeFragment"> + + + + + + + diff --git a/build.gradle b/build.gradle index 21b9d61..7107c50 100644 --- a/build.gradle +++ b/build.gradle @@ -6,6 +6,10 @@ buildscript { 'targetSdkVersion' : 34 ] + ext { + firebase_dynamic_link_host = 'moove.page.link' + } + repositories { google() mavenCentral() diff --git a/core/src/main/java/com/moove/core/kotlin/text/StringExtensions.kt b/core/src/main/java/com/moove/core/kotlin/text/StringExtensions.kt new file mode 100644 index 0000000..96c8891 --- /dev/null +++ b/core/src/main/java/com/moove/core/kotlin/text/StringExtensions.kt @@ -0,0 +1,10 @@ +package com.moove.core.kotlin.text + +import java.util.regex.Pattern + +fun String.matchesPattern(pattern: String): Boolean { + return Pattern + .compile("($pattern)") + .matcher(this) + .find() +} diff --git a/gradle/base-android-config.gradle b/gradle/base-android-config.gradle index 1582d19..ae8f5a8 100644 --- a/gradle/base-android-config.gradle +++ b/gradle/base-android-config.gradle @@ -42,6 +42,7 @@ android { } buildFeatures { + buildConfig true dataBinding false viewBinding true } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 44fe4cb..63ab323 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,50 +1,51 @@ [versions] # kotlin gradle = "8.4.1" -kotlin = "1.9.23" -ksp = "1.9.22-1.0.17" +kotlin = "1.9.24" +ksp = "1.9.24-1.0.20" coroutines = "1.8.1" # androidx -lifecycle = "2.8.0" +lifecycle = "2.7.0" navigation = "2.7.7" # di -koin = "3.5.3" -koin_android = "3.5.3" -koin_compose = "3.5.3" +koin = "3.5.6" +koin_android = "3.5.6" +koin_compose = "3.5.6" # presentation -orbit = "7.1.0" +orbit = "7.1.1" # ui compose = "1.6.7" -composeCompiler = "1.5.11" +composeCompiler = "1.5.14" composeFoundation = "1.6.7" composeMaterial = "1.6.7" accompanist = "0.34.0" paging = "3.3.0" # io -retrofit = "2.9.0" +retrofit = "2.11.0" moshi = "1.15.1" -moshi_sealed = "0.25.1" +moshi_sealed = "0.27.1" room = "2.6.1" # services -firebase = "33.0.0" +firebase = "33.1.0" # checks lint = "31.4.1" core-ktx = "1.13.1" junit = "4.13.2" androidx-test-ext-junit = "1.1.5" espresso-core = "3.5.1" -appcompat = "1.6.1" +appcompat = "1.7.0" material = "1.12.0" [libraries] # core androidx-core = "androidx.core:core-ktx:1.13.1" -androidx-appcompat = "androidx.appcompat:appcompat:1.6.1" +androidx-appcompat = "androidx.appcompat:appcompat:1.7.0" androidx-fragment = "androidx.fragment:fragment-ktx:1.7.1" androidx-navigation-fragment = { module = "androidx.navigation:navigation-fragment-ktx", version.ref = "navigation" } androidx-navigation-ui = { module = "androidx.navigation:navigation-ui-ktx", version.ref = "navigation" } androidx-lifecycle-common = { module = "androidx.lifecycle:lifecycle-common-java8", version.ref = "lifecycle" } androidx-lifecycle-runtime = { module = "androidx.lifecycle:lifecycle-runtime-ktx", version.ref = "lifecycle" } +androidx-lifecycle-runtime-compose = { module = "androidx.lifecycle:lifecycle-runtime-compose", version.ref = "lifecycle" } androidx-lifecycle-viewmodel = { module = "androidx.lifecycle:lifecycle-viewmodel-ktx", version.ref = "lifecycle" } androidx-lifecycle-livedata = { module = "androidx.lifecycle:lifecycle-livedata-ktx", version.ref = "lifecycle" } androidx-browser = "androidx.browser:browser:1.8.0" @@ -125,12 +126,12 @@ firebase-perf = { module = "com.google.firebase:firebase-perf-ktx" } firebase-messaging = { module = "com.google.firebase:firebase-messaging-ktx" } firebase-inappmessaging = { module = "com.google.firebase:firebase-inappmessaging-display-ktx" } firebase-installations = { module = "com.google.firebase:firebase-installations" } -playservices-base = "com.google.android.gms:play-services-base:18.3.0" -playservices-tasks = "com.google.android.gms:play-services-tasks:18.1.0" -playservices-location = "com.google.android.gms:play-services-location:21.1.0" +playservices-base = "com.google.android.gms:play-services-base:18.5.0" +playservices-tasks = "com.google.android.gms:play-services-tasks:18.2.0" +playservices-location = "com.google.android.gms:play-services-location:21.3.0" playservices-maps = "com.google.android.gms:play-services-maps:18.2.0" playservices-review = "com.google.android.play:review-ktx:2.0.1" -playservices-ads-identifier = "com.google.android.gms:play-services-ads-identifier:18.0.1" +playservices-ads-identifier = "com.google.android.gms:play-services-ads-identifier:18.1.0" install-referrer = "com.android.installreferrer:installreferrer:2.2" # tools timber = "com.jakewharton.timber:timber:5.0.1" @@ -162,12 +163,16 @@ androidx-navigation = ["androidx-navigation-fragment", "androidx-navigation-ui"] androidx-lifecycle = [ "androidx-lifecycle-common", "androidx-lifecycle-runtime", "androidx-lifecycle-viewmodel", "androidx-lifecycle-livedata", + "androidx-lifecycle-runtime-compose" ] androidx-compose = [ - "androidx-compose-ui-core", "androidx-compose-ui-tooling", + "androidx-compose-ui-core", + "androidx-compose-ui-tooling", "androidx-compose-foundation", "androidx-compose-livedata", - "androidx-compose-material-core", "androidx-compose-material-icons-core", "androidx-compose-material-icons-ext", + "androidx-compose-material-core", + "androidx-compose-material-icons-core", + "androidx-compose-material-icons-ext", "androidx-compose-activity", "androidx-compose-constraintlayout", "androidx-compose-animation-graphics", diff --git a/shared/build.gradle b/shared/build.gradle index 6035be5..f2c7ac5 100644 --- a/shared/build.gradle +++ b/shared/build.gradle @@ -64,6 +64,9 @@ dependencies { ksp libs.moshi.compiler // endregion + api platform(libs.firebase.platform) + api libs.firebase.dynamic.links + // region Tests api libs.javafaker diff --git a/shared/src/main/java/com/moove/shared/feature/deeplink/domain/DeepLink.kt b/shared/src/main/java/com/moove/shared/feature/deeplink/domain/DeepLink.kt new file mode 100644 index 0000000..f029249 --- /dev/null +++ b/shared/src/main/java/com/moove/shared/feature/deeplink/domain/DeepLink.kt @@ -0,0 +1,3 @@ +package com.moove.shared.feature.deeplink.domain + +interface DeepLink \ No newline at end of file diff --git a/shared/src/main/java/com/moove/shared/feature/deeplink/domain/DeeplinkRepository.kt b/shared/src/main/java/com/moove/shared/feature/deeplink/domain/DeeplinkRepository.kt new file mode 100644 index 0000000..f18cf3d --- /dev/null +++ b/shared/src/main/java/com/moove/shared/feature/deeplink/domain/DeeplinkRepository.kt @@ -0,0 +1,6 @@ +package com.moove.shared.feature.deeplink.domain + +interface DeeplinkRepository { + + suspend fun getDeepLink(uri: String): DeepLink +} diff --git a/shared/src/main/java/com/moove/shared/feature/deeplink/domain/DynamicLinkRepository.kt b/shared/src/main/java/com/moove/shared/feature/deeplink/domain/DynamicLinkRepository.kt new file mode 100644 index 0000000..b70e3c0 --- /dev/null +++ b/shared/src/main/java/com/moove/shared/feature/deeplink/domain/DynamicLinkRepository.kt @@ -0,0 +1,6 @@ +package com.moove.shared.feature.deeplink.domain + +interface DynamicLinkRepository { + + suspend fun parseLink(uri: String): String? +} diff --git a/shared/src/main/java/com/moove/shared/feature/deeplink/domain/GetDeeplinkUseCase.kt b/shared/src/main/java/com/moove/shared/feature/deeplink/domain/GetDeeplinkUseCase.kt new file mode 100644 index 0000000..e191444 --- /dev/null +++ b/shared/src/main/java/com/moove/shared/feature/deeplink/domain/GetDeeplinkUseCase.kt @@ -0,0 +1,12 @@ +package com.moove.shared.feature.deeplink.domain + +class GetDeeplinkUseCase( + private val deeplinkRepository: DeeplinkRepository, + private val getDynamicLinkUseCase: GetDynamicLinkUseCase, +) { + suspend operator fun invoke(uri: String): DeepLink { + val parsedUri = getDynamicLinkUseCase(uri) ?: uri + val deepLink = deeplinkRepository.getDeepLink(parsedUri) + return deepLink + } +} diff --git a/shared/src/main/java/com/moove/shared/feature/deeplink/domain/GetDynamicLinkUseCase.kt b/shared/src/main/java/com/moove/shared/feature/deeplink/domain/GetDynamicLinkUseCase.kt new file mode 100644 index 0000000..82cdc81 --- /dev/null +++ b/shared/src/main/java/com/moove/shared/feature/deeplink/domain/GetDynamicLinkUseCase.kt @@ -0,0 +1,10 @@ +package com.moove.shared.feature.deeplink.domain + +class GetDynamicLinkUseCase( + private val dynamicLinkRepository: DynamicLinkRepository, +) { + + suspend operator fun invoke(uri: String): String? { + return dynamicLinkRepository.parseLink(uri) + } +} diff --git a/shared/src/main/java/com/moove/shared/feature/deeplink/domain/exceptions/DynamicLinkParseException.kt b/shared/src/main/java/com/moove/shared/feature/deeplink/domain/exceptions/DynamicLinkParseException.kt new file mode 100644 index 0000000..1e2b75a --- /dev/null +++ b/shared/src/main/java/com/moove/shared/feature/deeplink/domain/exceptions/DynamicLinkParseException.kt @@ -0,0 +1,6 @@ +package com.moove.shared.feature.deeplink.domain.exceptions + +class DynamicLinkParseException( + message: String? = null, + cause: Throwable? = null, +) : Exception(message, cause) diff --git a/shared/src/main/java/com/moove/shared/feature/deeplink/presentation/DeepLinkNavigator.kt b/shared/src/main/java/com/moove/shared/feature/deeplink/presentation/DeepLinkNavigator.kt new file mode 100644 index 0000000..6f4a4a6 --- /dev/null +++ b/shared/src/main/java/com/moove/shared/feature/deeplink/presentation/DeepLinkNavigator.kt @@ -0,0 +1,7 @@ +package com.moove.shared.feature.deeplink.presentation + +import com.moove.shared.feature.deeplink.domain.DeepLink + +interface DeepLinkNavigator { + fun navigateTo(link: DeepLink) +} diff --git a/shared/src/main/java/com/moove/shared/navigation/GlobalAppNavigator.kt b/shared/src/main/java/com/moove/shared/navigation/GlobalAppNavigator.kt new file mode 100644 index 0000000..6e5e390 --- /dev/null +++ b/shared/src/main/java/com/moove/shared/navigation/GlobalAppNavigator.kt @@ -0,0 +1,6 @@ +package com.moove.shared.navigation + +interface GlobalAppNavigator : ScreenNavigator, TicketsNavigator { + + fun goHome() +} diff --git a/shared/src/main/java/com/moove/shared/navigation/ScreenNavigator.kt b/shared/src/main/java/com/moove/shared/navigation/ScreenNavigator.kt index 87f6d76..0bdf268 100644 --- a/shared/src/main/java/com/moove/shared/navigation/ScreenNavigator.kt +++ b/shared/src/main/java/com/moove/shared/navigation/ScreenNavigator.kt @@ -1,11 +1,5 @@ package com.moove.shared.navigation -import androidx.navigation.NavController - -abstract class ScreenNavigator( - private val navController: NavController -) { - open fun goBack() { - navController.navigateUp() - } +interface ScreenNavigator { + fun goBack() } diff --git a/shared/src/main/java/com/moove/shared/navigation/TicketsNavigator.kt b/shared/src/main/java/com/moove/shared/navigation/TicketsNavigator.kt new file mode 100644 index 0000000..cc0d424 --- /dev/null +++ b/shared/src/main/java/com/moove/shared/navigation/TicketsNavigator.kt @@ -0,0 +1,7 @@ +package com.moove.shared.navigation + +interface TicketsNavigator { + + fun goFares(ryderId: String) + fun goToConfirmation(ryderId: String, fareDescription: String, farePrice: Float) +} \ No newline at end of file diff --git a/app/src/main/java/com/moove/app/di/TicketsModule.kt b/tickets/src/main/java/com/moove/tickets/di/TicketsModule.kt similarity index 93% rename from app/src/main/java/com/moove/app/di/TicketsModule.kt rename to tickets/src/main/java/com/moove/tickets/di/TicketsModule.kt index fb98cb0..4d538e3 100644 --- a/app/src/main/java/com/moove/app/di/TicketsModule.kt +++ b/tickets/src/main/java/com/moove/tickets/di/TicketsModule.kt @@ -1,4 +1,4 @@ -package com.moove.app.di +package com.moove.tickets.di import com.moove.tickets.data.TicketsDataRepository import com.moove.tickets.data.local.TicketsLocalDataSource @@ -28,7 +28,7 @@ val ticketsModule = module { single { TicketsDataRepository(get()) } // endregion - factory { RyderListNavigator(get()) } + factory { RyderListNavigator(get(), get()) } viewModel { RyderListViewModel( exceptionHandler = get(), @@ -36,7 +36,7 @@ val ticketsModule = module { ) } - factory { FareListNavigator(get()) } + factory { FareListNavigator(get(), get()) } viewModel { FareListViewModel( exceptionHandler = get(), diff --git a/tickets/src/main/java/com/moove/tickets/presentation/confirmation/ConfirmationNavigator.kt b/tickets/src/main/java/com/moove/tickets/presentation/confirmation/ConfirmationNavigator.kt index 3e43d18..f945999 100644 --- a/tickets/src/main/java/com/moove/tickets/presentation/confirmation/ConfirmationNavigator.kt +++ b/tickets/src/main/java/com/moove/tickets/presentation/confirmation/ConfirmationNavigator.kt @@ -1,11 +1,12 @@ package com.moove.tickets.presentation.confirmation -import androidx.navigation.NavController import com.moove.shared.navigation.ScreenNavigator -import com.moove.tickets.presentation.fare.model.FareModel class ConfirmationNavigator( - navController: NavController -) : ScreenNavigator(navController) { + private val screenNavigator: ScreenNavigator, +) : ScreenNavigator { + override fun goBack() { + screenNavigator.goBack() + } } diff --git a/tickets/src/main/java/com/moove/tickets/presentation/confirmation/ConfirmationRoute.kt b/tickets/src/main/java/com/moove/tickets/presentation/confirmation/ConfirmationRoute.kt index 67bcdf1..3beb39c 100644 --- a/tickets/src/main/java/com/moove/tickets/presentation/confirmation/ConfirmationRoute.kt +++ b/tickets/src/main/java/com/moove/tickets/presentation/confirmation/ConfirmationRoute.kt @@ -12,6 +12,7 @@ import com.moove.shared.presentation.viewmodel.composableEffect import com.moove.shared.presentation.viewmodel.composableState import com.moove.tickets.presentation.fare.model.FareModel import org.koin.androidx.compose.getViewModel +import org.koin.androidx.compose.koinViewModel import org.koin.core.parameter.parametersOf @Composable @@ -19,7 +20,7 @@ fun FareListRoute( navigator: ConfirmationNavigator, ryderId: String, fare: FareModel, - viewModel: ConfirmationViewModel = getViewModel { parametersOf(ryderId, fare) }, + viewModel: ConfirmationViewModel = koinViewModel { parametersOf(ryderId, fare) }, scaffoldState: ScaffoldState = rememberScaffoldState(), ) { val state by viewModel.composableState() diff --git a/tickets/src/main/java/com/moove/tickets/presentation/fare/FareListNavigator.kt b/tickets/src/main/java/com/moove/tickets/presentation/fare/FareListNavigator.kt index 0fb4fad..75071df 100644 --- a/tickets/src/main/java/com/moove/tickets/presentation/fare/FareListNavigator.kt +++ b/tickets/src/main/java/com/moove/tickets/presentation/fare/FareListNavigator.kt @@ -1,20 +1,19 @@ package com.moove.tickets.presentation.fare -import androidx.navigation.NavController import com.moove.shared.navigation.ScreenNavigator -import com.moove.shared.navigation.navigateSafely +import com.moove.shared.navigation.TicketsNavigator import com.moove.tickets.presentation.fare.model.FareModel class FareListNavigator( - private val navController: NavController -) : ScreenNavigator(navController) { + private val ticketsNavigator: TicketsNavigator, + private val screenNavigator: ScreenNavigator, +) : ScreenNavigator { fun goToConfirmation(ryderId: String, fare: FareModel) { - navController.navigateSafely( - FareListFragmentDirections.actionFareListFragmentToConfirmationFragment( - ryderId = ryderId, - fare = fare - ) - ) + ticketsNavigator.goToConfirmation(ryderId, fare.description, fare.price) + } + + override fun goBack() { + screenNavigator.goBack() } } diff --git a/tickets/src/main/java/com/moove/tickets/presentation/fare/FareListRoute.kt b/tickets/src/main/java/com/moove/tickets/presentation/fare/FareListRoute.kt index 1662772..ae6abac 100644 --- a/tickets/src/main/java/com/moove/tickets/presentation/fare/FareListRoute.kt +++ b/tickets/src/main/java/com/moove/tickets/presentation/fare/FareListRoute.kt @@ -9,13 +9,14 @@ import com.moove.shared.presentation.compose.component.showGenericError import com.moove.shared.presentation.viewmodel.composableEffect import com.moove.shared.presentation.viewmodel.composableState import org.koin.androidx.compose.getViewModel +import org.koin.androidx.compose.koinViewModel import org.koin.core.parameter.parametersOf @Composable fun FareListRoute( navigator: FareListNavigator, ryderId: String, - viewModel: FareListViewModel = getViewModel { parametersOf(ryderId) }, + viewModel: FareListViewModel = koinViewModel { parametersOf(ryderId) }, scaffoldState: ScaffoldState = rememberScaffoldState(), ) { val state by viewModel.composableState() diff --git a/tickets/src/main/java/com/moove/tickets/presentation/list/RyderListNavigator.kt b/tickets/src/main/java/com/moove/tickets/presentation/list/RyderListNavigator.kt index c8141d9..983dc6f 100644 --- a/tickets/src/main/java/com/moove/tickets/presentation/list/RyderListNavigator.kt +++ b/tickets/src/main/java/com/moove/tickets/presentation/list/RyderListNavigator.kt @@ -1,16 +1,18 @@ package com.moove.tickets.presentation.list -import androidx.navigation.NavController import com.moove.shared.navigation.ScreenNavigator -import com.moove.shared.navigation.navigateSafely +import com.moove.shared.navigation.TicketsNavigator class RyderListNavigator( - private val navController: NavController -) : ScreenNavigator(navController) { + private val ticketsNavigator: TicketsNavigator, + private val screenNavigator: ScreenNavigator, +) : ScreenNavigator { fun goFares(id: String) { - navController.navigateSafely( - RyderListFragmentDirections.actionRydersFragmentToFareListFragment(ryderId = id) - ) + ticketsNavigator.goFares(id) + } + + override fun goBack() { + screenNavigator.goBack() } }