Skip to content

Commit

Permalink
Merge pull request #7 from stslex/dev
Browse files Browse the repository at this point in the history
Dev
  • Loading branch information
stslex authored Aug 3, 2023
2 parents 0b5bdd5 + 4ca24dd commit ce504d0
Show file tree
Hide file tree
Showing 40 changed files with 1,054 additions and 300 deletions.
13 changes: 13 additions & 0 deletions app/src/main/java/com/stslex/aproselection/SelectApplication.kt
Original file line number Diff line number Diff line change
@@ -1,19 +1,32 @@
package com.stslex.aproselection

import android.app.Application
import com.stslex.aproselection.controller.AuthController
import com.stslex.aproselection.core.datastore.coreDataStoreModule
import com.stslex.aproselection.core.network.di.ModuleCoreNetwork.moduleCoreNetwork
import com.stslex.aproselection.di.appModule
import com.stslex.aproselection.feature.auth.di.ModuleFeatureAuth.moduleFeatureAuth
import com.stslex.aproselection.feature.home.di.moduleFeatureHome
import org.koin.android.ext.android.inject
import org.koin.android.ext.koin.androidContext
import org.koin.android.ext.koin.androidLogger
import org.koin.core.context.startKoin

class SelectApplication : Application() {

private val appController: AuthController by inject()

override fun onCreate() {
super.onCreate()
setupDependencies()
initControllers()
}

private fun initControllers() {
appController.init()
}

private fun setupDependencies() {
startKoin {
androidLogger()
androidContext(applicationContext)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.stslex.aproselection.controller

import kotlinx.coroutines.flow.StateFlow

interface AuthController {

val isAuth: StateFlow<Boolean?>

fun init()
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package com.stslex.aproselection.controller

import com.stslex.aproselection.core.datastore.AppDataStore
import com.stslex.aproselection.core.network.client.NetworkClient
import com.stslex.aproselection.core.ui.core.Logger
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach

class AuthControllerImpl(
private val dataStore: AppDataStore,
private val networkClient: NetworkClient,
) : AuthController {

private val scope = CoroutineScope(SupervisorJob())
private val _isAuth = MutableStateFlow<Boolean?>(null)
override val isAuth: StateFlow<Boolean?>
get() = _isAuth.asStateFlow()

override fun init() {
dataStore.token
.catch { error ->
Logger.exception(error)
}
.onEach { token ->
if (token.isBlank()) {
networkClient.regenerateToken()
}
}
.launchIn(scope)

dataStore.uuid
.catch { error ->
Logger.exception(error)
}
.onEach { uuid ->
_isAuth.emit(uuid.isNotBlank())
}
.launchIn(scope)
}
}
14 changes: 14 additions & 0 deletions app/src/main/java/com/stslex/aproselection/di/AppModule.kt
Original file line number Diff line number Diff line change
@@ -1,9 +1,23 @@
package com.stslex.aproselection.di

import androidx.navigation.NavHostController
import com.stslex.aproselection.controller.AuthController
import com.stslex.aproselection.controller.AuthControllerImpl
import com.stslex.aproselection.core.ui.navigation.navigator.Navigator
import com.stslex.aproselection.core.ui.navigation.navigator.NavigatorImpl
import com.stslex.aproselection.ui.InitialAppViewModel
import org.koin.androidx.viewmodel.dsl.viewModelOf
import org.koin.core.module.dsl.bind
import org.koin.core.module.dsl.singleOf
import org.koin.dsl.module

val appModule = module {
singleOf(::AuthControllerImpl) { bind<AuthController>() }
}

fun navigationModule(
navHostController: NavHostController
) = module {
single<Navigator> { NavigatorImpl(navHostController) }
viewModelOf(::InitialAppViewModel)
}
Original file line number Diff line number Diff line change
@@ -1,78 +1,24 @@
package com.stslex.aproselection.navigation

import androidx.compose.foundation.layout.Box
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.navigation.NavHostController
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import com.stslex.aproselection.core.ui.navigation.AppDestination
import com.stslex.aproselection.core.ui.navigation.NavigationScreen
import com.stslex.aproselection.core.ui.navigation.destination.AppDestination
import com.stslex.aproselection.feature.auth.ui.navigation.authRouter
import com.stslex.aproselection.feature.home.ui.navigation.homeRouter
import com.stslex.aproselection.ui.InitialAppViewModel
import org.koin.androidx.compose.koinViewModel
import org.koin.core.parameter.parametersOf

@Composable
fun NavigationHost(
navController: NavHostController,
startDestination: AppDestination,
modifier: Modifier = Modifier,
startDestination: AppDestination = AppDestination.SPLASH
) {
val navigator: (NavigationScreen) -> Unit = { screen ->
when (screen) {
is NavigationScreen.PopBackStack -> navController.popBackStack()
else -> navController.navigateScreen(screen)
}
}

val viewModel = koinViewModel<InitialAppViewModel>(
parameters = { parametersOf(navigator) }
)
val isAuth by remember {
viewModel.isAuth
}.collectAsState()

NavHost(
navController = navController,
startDestination = startDestination.route
) {
composable(NavigationScreen.Splash.screenRoute) {
Box(
contentAlignment = Alignment.Center
) {
Text(text = "splash") // TODO replace with norm navigator
}
val screen = when (isAuth) {
true -> NavigationScreen.Home
false -> NavigationScreen.Auth
null -> null
}
screen?.let(navigator)
}
authRouter(modifier, navigator)
homeRouter(modifier, navigator)
authRouter(modifier)
homeRouter(modifier)
}
}

fun NavHostController.navigateScreen(screen: NavigationScreen) {
val currentRoute = this.currentDestination?.route
if (currentRoute == screen.screenRoute) return
navigate(screen.screenRoute) {
if (screen.isSingleTop.not()) return@navigate
currentRoute?.let { route ->
popUpTo(route) {
inclusive = true
saveState = true
}
}

launchSingleTop = true
}
}
43 changes: 25 additions & 18 deletions app/src/main/java/com/stslex/aproselection/ui/InitialApp.kt
Original file line number Diff line number Diff line change
@@ -1,27 +1,34 @@
package com.stslex.aproselection.ui

import androidx.compose.foundation.layout.Box
import androidx.compose.runtime.Composable
import androidx.compose.ui.tooling.preview.Preview
import androidx.navigation.compose.rememberNavController
import com.stslex.aproselection.core.ui.theme.AppTheme
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.navigation.NavHostController
import com.stslex.aproselection.core.ui.navigation.destination.AppDestination
import com.stslex.aproselection.navigation.NavigationHost
import org.koin.androidx.compose.koinViewModel

@Composable
fun InitialApp() {
val navController = rememberNavController()
fun InitialApp(
navController: NavHostController
) {
val viewModel: InitialAppViewModel = koinViewModel()
val isInitialAuth by remember {
viewModel.isInitialAuth
}.collectAsState()

Box {
NavigationHost(
navController = navController,
)
LaunchedEffect(Unit) {
viewModel.init()
}
}

@Preview(showBackground = true)
@Composable
fun InitialAppPreview() {
AppTheme {
InitialApp()
}
}
AppDestination
.getStartDestination(isInitialAuth)
?.let { destination ->
NavigationHost(
navController = navController,
startDestination = destination
)
}
}
Original file line number Diff line number Diff line change
@@ -1,48 +1,33 @@
package com.stslex.aproselection.ui

import androidx.lifecycle.viewModelScope
import com.stslex.aproselection.core.datastore.AppDataStore
import com.stslex.aproselection.core.network.client.NetworkClient
import com.stslex.aproselection.controller.AuthController
import com.stslex.aproselection.core.ui.base.BaseViewModel
import com.stslex.aproselection.core.ui.navigation.NavigationScreen
import com.stslex.aproselection.core.ui.navigation.destination.NavigationScreen
import com.stslex.aproselection.core.ui.navigation.navigator.Navigator
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach

class InitialAppViewModel(
dataStore: AppDataStore,
navigate: (NavigationScreen) -> Unit,
networkClient: NetworkClient,
private val navigator: Navigator,
private val controller: AuthController
) : BaseViewModel() {

private val _isAuth = MutableStateFlow<Boolean?>(null)
val isAuth: StateFlow<Boolean?>
get() = _isAuth.asStateFlow()
private val _isInitialAuth = MutableStateFlow<Boolean?>(null)
val isInitialAuth: StateFlow<Boolean?>
get() = _isInitialAuth.asStateFlow()

// TODO (вынести из init блока)
init {
dataStore.token
.catch { error ->
handleError(error)
}
.onEach { token ->
if (token.isBlank()) {
networkClient.regenerateToken()
fun init() {
controller.isAuth
.onEach { isAuth ->
if (isInitialAuth.value == null) {
_isInitialAuth.emit(isAuth)
}
}
.launchIn(viewModelScope)

dataStore.uuid
.catch { error ->
handleError(error)
}
.onEach { uuid ->
_isAuth.emit(uuid.isNotBlank())
if (uuid.isBlank()) {
navigate(NavigationScreen.Auth)
if (isAuth == false) {
navigator.navigate(NavigationScreen.Auth)
}
}
.launchIn(viewModelScope)
Expand Down
32 changes: 20 additions & 12 deletions app/src/main/java/com/stslex/aproselection/ui/MainActivity.kt
Original file line number Diff line number Diff line change
@@ -1,30 +1,38 @@
package com.stslex.aproselection.ui

import android.os.Bundle
import android.util.Log
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.lifecycle.lifecycleScope
import com.stslex.aproselection.core.datastore.AppDataStore
import com.stslex.aproselection.core.network.client.NetworkClient
import androidx.compose.runtime.Composable
import androidx.navigation.NavHostController
import androidx.navigation.compose.rememberNavController
import com.stslex.aproselection.core.ui.theme.AppTheme
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import org.koin.android.ext.android.inject
import com.stslex.aproselection.di.navigationModule
import org.koin.androidx.compose.getKoin

class MainActivity : ComponentActivity() {

private val dataStore by inject<AppDataStore>()
private val networkClient by inject<NetworkClient>()

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

setContent {
val navHostController = rememberNavController()
SetupComposeDependencies(navHostController)
AppTheme {
InitialApp()
InitialApp(
navController = navHostController
)
}
}
}

@Composable
private fun SetupComposeDependencies(
navController: NavHostController
) {
val navModule = navigationModule(navController)
getKoin().loadModules(
listOf(navModule)
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package com.stslex.aproselection
import android.content.Context
import com.stslex.aproselection.core.datastore.coreDataStoreModule
import com.stslex.aproselection.core.network.di.ModuleCoreNetwork.moduleCoreNetwork
import com.stslex.aproselection.core.ui.navigation.NavigationScreen
import com.stslex.aproselection.core.ui.navigation.destination.NavigationScreen
import com.stslex.aproselection.di.appModule
import com.stslex.aproselection.feature.auth.di.ModuleFeatureAuth.moduleFeatureAuth
import com.stslex.aproselection.feature.home.di.moduleFeatureHome
Expand Down
Loading

0 comments on commit ce504d0

Please sign in to comment.