Skip to content

Commit

Permalink
Merge pull request #6 from stslex/dev
Browse files Browse the repository at this point in the history
Dev
  • Loading branch information
stslex authored Aug 1, 2023
2 parents aadd026 + 34bebce commit 0b5bdd5
Show file tree
Hide file tree
Showing 41 changed files with 851 additions and 135 deletions.
2 changes: 1 addition & 1 deletion app/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
@Suppress("DSL_SCOPE_VIOLATION") // TODO: Remove once KTIJ-19369 is fixed
plugins {
id("aproselection.android.application")
id("aproselection.android.application.compose")
Expand All @@ -9,4 +8,5 @@ dependencies {
implementation(project(":core:datastore"))
implementation(project(":core:network"))
implementation(project(":feature:auth"))
implementation(project(":feature:home"))
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ package com.stslex.aproselection
import android.app.Application
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.koin.androidContext
import org.koin.android.ext.koin.androidLogger
import org.koin.core.context.startKoin
Expand All @@ -16,9 +18,11 @@ class SelectApplication : Application() {
androidLogger()
androidContext(applicationContext)
modules(
moduleFeatureAuth,
appModule,
moduleCoreNetwork,
coreDataStoreModule
coreDataStoreModule,
moduleFeatureAuth,
moduleFeatureHome,
)
}
}
Expand Down
9 changes: 9 additions & 0 deletions app/src/main/java/com/stslex/aproselection/di/AppModule.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.stslex.aproselection.di

import com.stslex.aproselection.ui.InitialAppViewModel
import org.koin.androidx.viewmodel.dsl.viewModelOf
import org.koin.dsl.module

val appModule = module {
viewModelOf(::InitialAppViewModel)
}
Original file line number Diff line number Diff line change
@@ -1,43 +1,78 @@
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.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,
modifier: Modifier = Modifier,
startDestination: AppDestination = AppDestination.AUTH
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)
}
}

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

launchSingleTop = true
restoreState = false
}
}
7 changes: 6 additions & 1 deletion app/src/main/java/com/stslex/aproselection/ui/InitialApp.kt
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
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
Expand All @@ -10,7 +11,11 @@ import com.stslex.aproselection.navigation.NavigationHost
fun InitialApp() {
val navController = rememberNavController()

NavigationHost(navController = navController)
Box {
NavigationHost(
navController = navController,
)
}
}

@Preview(showBackground = true)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
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.core.ui.base.BaseViewModel
import com.stslex.aproselection.core.ui.navigation.NavigationScreen
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,
) : BaseViewModel() {

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

// TODO (вынести из init блока)
init {
dataStore.token
.catch { error ->
handleError(error)
}
.onEach { token ->
if (token.isBlank()) {
networkClient.regenerateToken()
}
}
.launchIn(viewModelScope)

dataStore.uuid
.catch { error ->
handleError(error)
}
.onEach { uuid ->
_isAuth.emit(uuid.isNotBlank())
if (uuid.isBlank()) {
navigate(NavigationScreen.Auth)
}
}
.launchIn(viewModelScope)
}
}
15 changes: 3 additions & 12 deletions app/src/main/java/com/stslex/aproselection/ui/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -15,21 +15,12 @@ import org.koin.android.ext.android.inject

class MainActivity : ComponentActivity() {

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

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
dataStore.token
.catch {
Log.e(it.message, javaClass.simpleName, it)
}
.onEach {
if (it.isBlank()) {
networkClient.regenerateToken()
}
}
.launchIn(lifecycleScope)

setContent {
AppTheme {
InitialApp()
Expand Down
19 changes: 16 additions & 3 deletions app/src/test/java/com/stslex/aproselection/DiKoinModuleTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,15 @@ package com.stslex.aproselection

import android.content.Context
import com.stslex.aproselection.core.datastore.coreDataStoreModule
import com.stslex.aproselection.core.network.di.ModuleCoreNetwork
import com.stslex.aproselection.core.network.di.ModuleCoreNetwork.moduleCoreNetwork
import com.stslex.aproselection.core.ui.navigation.NavigationScreen
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.junit.Test
import org.koin.android.ext.koin.androidContext
import org.koin.dsl.koinApplication
import org.koin.dsl.module
import org.koin.test.KoinTest
import org.koin.test.check.checkModules
import org.mockito.Mockito
Expand All @@ -16,12 +19,22 @@ class DiKoinModuleTest : KoinTest {

@Test
fun checkKoinModules() {
val navigator: (screen: NavigationScreen) -> Unit = {}

koinApplication {

androidContext(Mockito.mock(Context::class.java))
modules(
moduleFeatureAuth,
module {
single {
navigator
}
},
appModule,
moduleCoreNetwork,
coreDataStoreModule
coreDataStoreModule,
moduleFeatureAuth,
moduleFeatureHome,
)
checkModules()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,11 @@ import org.gradle.api.JavaVersion
import org.gradle.api.Project
import org.gradle.api.artifacts.VersionCatalogsExtension
import org.gradle.api.plugins.ExtensionAware
import org.gradle.api.plugins.JavaPluginExtension
import org.gradle.kotlin.dsl.configure
import org.gradle.kotlin.dsl.dependencies
import org.gradle.kotlin.dsl.getByType
import org.gradle.kotlin.dsl.getValue
import org.gradle.kotlin.dsl.provideDelegate
import org.gradle.kotlin.dsl.withType
import org.jetbrains.kotlin.gradle.dsl.KotlinJvmOptions
Expand Down Expand Up @@ -34,9 +37,9 @@ internal fun Project.configureKotlinAndroid(
targetCompatibility = JavaVersion.VERSION_11
isCoreLibraryDesugaringEnabled = true
}
}

configureKotlin()
configureKotlinJvm()
}

val libs = extensions.getByType<VersionCatalogsExtension>().named("libs")

Expand Down Expand Up @@ -65,20 +68,20 @@ private fun CommonExtension<*, *, *, *>.kotlinOptions(block: KotlinJvmOptions.()
(this as ExtensionAware).extensions.configure("kotlinOptions", block)
}

// TODO check
///**
// * Configure base Kotlin options for JVM (non-Android)
// */
//internal fun Project.configureKotlinJvm() {
// extensions.configure<JavaPluginExtension> {
// // Up to Java 11 APIs are available through desugaring
// // https://developer.android.com/studio/write/java11-minimal-support-table
// sourceCompatibility = JavaVersion.VERSION_11
// targetCompatibility = JavaVersion.VERSION_11
// }
//
// configureKotlin()
//}
/**
* Configure base Kotlin options for JVM (non-Android)
*/
internal fun Project.configureKotlinJvm() {
extensions.configure<JavaPluginExtension> {
// Up to Java 11 APIs are available through desugaring
// https://developer.android.com/studio/write/java11-minimal-support-table
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}

configureKotlin()
}


/**
* Configure base Kotlin options
Expand All @@ -89,6 +92,7 @@ private fun Project.configureKotlin() {
kotlinOptions {
// Set JVM target to 11
jvmTarget = JavaVersion.VERSION_11.toString()
languageVersion = "1.9"
// Treat all Kotlin warnings as errors (disabled by default)
// Override by setting warningsAsErrors=true in your ~/.gradle/gradle.properties
val warningsAsErrors: String? by project
Expand Down
2 changes: 2 additions & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
@file:Suppress("UNUSED_EXPRESSION")

// Top-level build file where you can add configuration options common to all sub-projects/modules.
@Suppress("DSL_SCOPE_VIOLATION") // TODO: Remove once KTIJ-19369 is fixed
plugins {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ import androidx.datastore.core.DataStore
import androidx.datastore.preferences.core.Preferences
import androidx.datastore.preferences.core.stringPreferencesKey
import androidx.datastore.preferences.preferencesDataStore
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map

class AppDataStoreImpl(
Expand All @@ -20,14 +22,18 @@ class AppDataStoreImpl(
}

override val uuid: Flow<String>
get() = context.dataStore.data.map { prefs ->
prefs[UUID_KEY].orEmpty()
}
get() = context.dataStore.data
.map { prefs ->
prefs[UUID_KEY].orEmpty()
}
.flowOn(Dispatchers.IO)

override val token: Flow<String>
get() = context.dataStore.data.map { prefs ->
prefs[TOKEN_KEY].orEmpty()
}
get() = context.dataStore.data
.map { prefs ->
prefs[TOKEN_KEY].orEmpty()
}
.flowOn(Dispatchers.IO)

override suspend fun setUuid(uuid: String) {
context.dataStore.updateData { prefs ->
Expand All @@ -37,7 +43,6 @@ class AppDataStoreImpl(
}
}


override suspend fun setToken(token: String) {
context.dataStore.updateData { prefs ->
prefs.toMutablePreferences().apply {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ class NetworkClientImpl(
private val _uuid = MutableStateFlow("")
private val uuid = _uuid.asStateFlow()

// TODO (вынести из init блока)
init {
dataStore.token
.onEach {
Expand Down
Loading

0 comments on commit 0b5bdd5

Please sign in to comment.