Skip to content

Commit

Permalink
fix di and auth password fields
Browse files Browse the repository at this point in the history
  • Loading branch information
stslex committed Aug 2, 2023
1 parent 8191b9b commit 22e3096
Show file tree
Hide file tree
Showing 18 changed files with 185 additions and 122 deletions.
Original file line number Diff line number Diff line change
@@ -1,29 +1,20 @@
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() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,5 @@ interface AuthController {

val isAuth: StateFlow<Boolean?>

fun init()
suspend fun init()
}

Original file line number Diff line number Diff line change
Expand Up @@ -2,45 +2,25 @@ 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 com.stslex.aproselection.utils.mapState
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()
}
get() = dataStore.uuid
.mapState { value ->
value.isNotBlank()
}
.launchIn(scope)

dataStore.uuid
.catch { error ->
Logger.exception(error)
}
.onEach { uuid ->
_isAuth.emit(uuid.isNotBlank())
override suspend fun init() {
dataStore.token.collect { token ->
if (token.isBlank()) {
networkClient.regenerateToken()
}
.launchIn(scope)
}
}
}
17 changes: 16 additions & 1 deletion app/src/main/java/com/stslex/aproselection/ui/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,25 @@ import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.runtime.Composable
import androidx.lifecycle.lifecycleScope
import androidx.navigation.NavHostController
import androidx.navigation.compose.rememberNavController
import com.stslex.aproselection.controller.AuthController
import com.stslex.aproselection.core.datastore.AppDataStore
import com.stslex.aproselection.core.ui.theme.AppTheme
import com.stslex.aproselection.di.navigationModule
import kotlinx.coroutines.launch
import org.koin.android.ext.android.inject
import org.koin.androidx.compose.getKoin

class MainActivity : ComponentActivity() {

private val appController: AuthController by inject()
private val appDataStore: AppDataStore by inject()

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

initObservers()
setContent {
val navHostController = rememberNavController()
SetupComposeDependencies(navHostController)
Expand All @@ -26,6 +34,13 @@ class MainActivity : ComponentActivity() {
}
}

private fun initObservers() {
lifecycleScope.launch {
appController.init()
appDataStore.init()
}
}

@Composable
private fun SetupComposeDependencies(
navController: NavHostController
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package com.stslex.aproselection.utils

import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.FlowCollector
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn

class TransformableStateFlow<out T>(
val getValue: () -> T,
val flow: Flow<T>,
) : StateFlow<T> {

override val replayCache: List<T>
get() = listOf(value)

override val value: T
get() = getValue()

override suspend fun collect(collector: FlowCollector<T>): Nothing {
coroutineScope {
flow.distinctUntilChanged()
.stateIn(this)
.collect(collector)
}
}
}

fun <Flow1, Result> StateFlow<Flow1>.mapState(
transform: (a: Flow1) -> Result,
): StateFlow<Result> = TransformableStateFlow(
getValue = { transform(this.value) },
flow = map(transform = transform),
)
12 changes: 4 additions & 8 deletions app/src/test/java/com/stslex/aproselection/DiKoinModuleTest.kt
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
package com.stslex.aproselection

import android.content.Context
import androidx.navigation.NavHostController
import com.stslex.aproselection.core.datastore.coreDataStoreModule
import com.stslex.aproselection.core.network.di.ModuleCoreNetwork.moduleCoreNetwork
import com.stslex.aproselection.core.ui.navigation.destination.NavigationScreen
import com.stslex.aproselection.di.appModule
import com.stslex.aproselection.di.navigationModule
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 @@ -19,17 +19,13 @@ class DiKoinModuleTest : KoinTest {

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

val navHostController = Mockito.mock(NavHostController::class.java)
koinApplication {

androidContext(Mockito.mock(Context::class.java))
modules(
module {
single {
navigator
}
},
navigationModule(navHostController),
appModule,
moduleCoreNetwork,
coreDataStoreModule,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
package com.stslex.aproselection.core.datastore

import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.StateFlow

interface AppDataStore {

val uuid: Flow<String>
val token: Flow<String>
val uuid: StateFlow<String>
val token: StateFlow<String>

suspend fun setUuid(uuid: String)

suspend fun setToken(token: String)

suspend fun init()
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@ import androidx.datastore.core.DataStore
import androidx.datastore.preferences.core.Preferences
import androidx.datastore.preferences.core.stringPreferencesKey
import androidx.datastore.preferences.preferencesDataStore
import com.stslex.aproselection.core.datastore.core.Logger
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map

Expand All @@ -21,19 +23,8 @@ class AppDataStoreImpl(
private val Context.dataStore: DataStore<Preferences> by preferencesDataStore(DATA_STORE_KEY)
}

override val uuid: Flow<String>
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()
}
.flowOn(Dispatchers.IO)
override val uuid = MutableStateFlow("")
override val token = MutableStateFlow("")

override suspend fun setUuid(uuid: String) {
context.dataStore.updateData { prefs ->
Expand All @@ -50,4 +41,31 @@ class AppDataStoreImpl(
}
}
}

override suspend fun init() {
context.dataStore
.data
.map { prefs ->
prefs[TOKEN_KEY].orEmpty()
}
.catch { error ->
Logger.exception(error)
}
.flowOn(Dispatchers.IO)
.collect { token ->
this.token.value = token
}

context.dataStore.data
.map { prefs ->
prefs[UUID_KEY].orEmpty()
}
.catch { error ->
Logger.exception(error)
}
.flowOn(Dispatchers.IO)
.collect { token ->
this.token.value = token
}
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.stslex.aproselection.core.ui.core
package com.stslex.aproselection.core.datastore.core

import android.util.Log

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,39 +23,17 @@ import io.ktor.http.appendPathSegments
import io.ktor.http.contentType
import io.ktor.http.encodedPath
import io.ktor.serialization.kotlinx.json.json
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.json.Json

class NetworkClientImpl(
private val dataStore: AppDataStore
private val dataStore: AppDataStore,
) : NetworkClient {

private val scope = CoroutineScope(SupervisorJob())
private val _token = MutableStateFlow("")
private val token = _token.asStateFlow()
private val _uuid = MutableStateFlow("")
private val uuid = _uuid.asStateFlow()

// TODO (вынести из init блока)
init {
dataStore.token
.onEach {
_token.emit(it)
}
.launchIn(scope)

dataStore.uuid
.onEach {
_uuid.emit(it)
}
.launchIn(scope)
}
private val token: String
get() = dataStore.token.value
private val uuid: String
get() = dataStore.uuid.value

@OptIn(ExperimentalSerializationApi::class)
override val client: HttpClient
Expand Down Expand Up @@ -100,10 +78,10 @@ class NetworkClientImpl(
protocol = URLProtocol.HTTP
headers.append("API_KEY", BuildConfig.API_KEY)
headers.append("DEVICE_ID", "test")
headers.append("uuid", uuid.value)
headers.append("uuid", uuid)
contentType(ContentType.Application.Json)
}
bearerAuth(token = token.value)
bearerAuth(token = token)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ import kotlinx.coroutines.flow.update

class AuthViewModel(
private val interactor: AuthInteractor,
private val navigator: Navigator
//private val navigator: Navigator
) : BaseViewModel() {

private val _screenState = MutableStateFlow(ScreenState())
Expand Down Expand Up @@ -123,7 +123,7 @@ class AuthViewModel(
}
.onEach {
setLoadingState(ScreenLoadingState.Content)
navigator.navigate(NavigationScreen.Home)
//navigator.navigate(NavigationScreen.Home)
}
.launchIn(viewModelScope)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.tooling.preview.Preview
import com.stslex.aproselection.core.ui.theme.AppTheme
import com.stslex.aproselection.feature.auth.ui.model.screen.text_field.PasswordTextFieldState
import com.stslex.aproselection.feature.auth.ui.model.screen.text_field.base.PasswordTextFieldState

@Composable
fun AuthPasswordTextField(
Expand Down
Loading

0 comments on commit 22e3096

Please sign in to comment.