Skip to content

Commit

Permalink
add home screen and basic auth
Browse files Browse the repository at this point in the history
  • Loading branch information
stslex committed Aug 1, 2023
1 parent 854587e commit 165d75b
Show file tree
Hide file tree
Showing 27 changed files with 479 additions and 198 deletions.
1 change: 0 additions & 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 Down
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,45 +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
}
}
8 changes: 7 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 @@ -9,7 +10,12 @@ import com.stslex.aproselection.navigation.NavigationHost
@Composable
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,49 @@
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()

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
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 Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ enum class AppDestination(
vararg val argsNames: String
) {
AUTH,
HOME;
HOME,
SPLASH;

val route: String
get() = StringBuilder()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,29 +1,34 @@
package com.stslex.aproselection.core.ui.navigation

sealed class NavigationScreen {
sealed interface NavigationScreen {

abstract val screen: AppDestination
val screen: AppDestination

val screenRoute: String
get() = "${screen.route}${appArgs.argumentsForRoute}"

open val isSingleTop: Boolean
val isSingleTop: Boolean
get() = false

open val appArgs: AppArguments
val appArgs: AppArguments
get() = AppArguments.Empty

object Auth : NavigationScreen() {
data object Auth : NavigationScreen {
override val screen: AppDestination = AppDestination.AUTH
override val isSingleTop: Boolean = true
}

object Home : NavigationScreen() {
data object Home : NavigationScreen {
override val screen: AppDestination = AppDestination.HOME
override val isSingleTop: Boolean = true
}

object PopBackStack : NavigationScreen() {
data object Splash: NavigationScreen{
override val screen: AppDestination = AppDestination.SPLASH
override val isSingleTop: Boolean = true
}

data object PopBackStack : NavigationScreen {
override val screen: AppDestination = throw Exception("PopBackStack")
override val appArgs: AppArguments = throw Exception("PopBackStack")
}
Expand Down
2 changes: 2 additions & 0 deletions feature/auth/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile

plugins {
id("aproselection.android.library")
id("aproselection.android.library.compose")
Expand Down
Loading

0 comments on commit 165d75b

Please sign in to comment.