First request #5

Closed
student-21892 wants to merge 14 commits from student-21892/NTO-2025-Android-minipigs:main into main
12 changed files with 259 additions and 10 deletions
Showing only changes of commit 011804aa61 - Show all commits

View File

@@ -49,4 +49,5 @@ dependencies {
implementation("io.ktor:ktor-serialization-kotlinx-json:$ktor")
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.9.0")
implementation("androidx.datastore:datastore-preferences:1.2.0")
implementation("androidx.compose.material:material-icons-extended:1.7.8")
}

View File

@@ -7,6 +7,9 @@ object AuthRepository {
suspend fun clearCode() {
LocalDataSource.setCode("")
}
suspend fun getCode(): String {
return LocalDataSource.getCode()
}
val isCodePresentFlow: Flow<Boolean> = LocalDataSource.isCodePresentFlow
suspend fun checkAndSave(text: String): Result<Boolean> {

View File

@@ -0,0 +1,12 @@
package ru.myitschool.work.domain.main
import ru.myitschool.work.data.repo.AuthRepository
import kotlin.mapCatching
class Logout (
private val repository: AuthRepository
) {
suspend operator fun invoke(): Unit {
return repository.clearCode()
}
}

View File

@@ -0,0 +1,6 @@
package ru.myitschool.work.ui.nav
import kotlinx.serialization.Serializable
@Serializable
data object SplashScreenDestination: AppDestination

View File

@@ -10,6 +10,7 @@ import androidx.compose.material3.Scaffold
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.runBlocking
import ru.myitschool.work.data.repo.AuthRepository
@@ -23,7 +24,7 @@ class RootActivity() : ComponentActivity() {
actionBar?.hide()
setContent {
WorkTheme {
val codePresence by AuthRepository.isCodePresentFlow.collectAsState(false)
val codePresence by AuthRepository.isCodePresentFlow.collectAsState(initial = null)
Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
AppNavHost(

View File

@@ -0,0 +1,9 @@
package ru.myitschool.work.ui.root
sealed interface RootState {
object Loading: RootState
object CodePresent: RootState
object CodeAbsent: RootState
}

View File

@@ -6,6 +6,7 @@ import androidx.compose.foundation.layout.Box
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.State
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.navigation.NavHostController
@@ -18,32 +19,38 @@ import ru.myitschool.work.data.repo.AuthRepository
import ru.myitschool.work.ui.nav.AuthScreenDestination
import ru.myitschool.work.ui.nav.BookScreenDestination
import ru.myitschool.work.ui.nav.MainScreenDestination
import ru.myitschool.work.ui.nav.SplashScreenDestination
import ru.myitschool.work.ui.root.RootState
import ru.myitschool.work.ui.screen.auth.AuthIntent
import ru.myitschool.work.ui.screen.auth.AuthScreen
import ru.myitschool.work.ui.screen.main.MainScreen
import ru.myitschool.work.ui.screen.splash.SplashScreen
@Composable
fun AppNavHost(
modifier: Modifier = Modifier,
navController: NavHostController = rememberNavController(),
codePresence: Boolean
codePresence: Boolean?
) {
val startDestination = if (codePresence == null) SplashScreenDestination
else if (codePresence == true) MainScreenDestination
else AuthScreenDestination
NavHost(
modifier = modifier,
enterTransition = { EnterTransition.None },
exitTransition = { ExitTransition.None },
navController = navController,
startDestination = if (codePresence) MainScreenDestination else AuthScreenDestination,
startDestination = startDestination,
) {
composable<AuthScreenDestination> {
AuthScreen(navController = navController)
}
composable<SplashScreenDestination> {
SplashScreen()
}
composable<MainScreenDestination> {
Box(
contentAlignment = Alignment.Center
) {
Text(text = "MAIN")
// LaunchedEffect(Unit) { AuthRepository.clearCode() }
}
MainScreen(navController = navController)
}
composable<BookScreenDestination> {
Box(

View File

@@ -0,0 +1,6 @@
package ru.myitschool.work.ui.screen.main
sealed interface MainIntent {
data object Fetch: MainIntent
data object Logout: MainIntent
}

View File

@@ -0,0 +1,160 @@
package ru.myitschool.work.ui.screen.main
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.aspectRatio
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.outlined.Logout
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedCard
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.draw.shadow
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavController
import coil3.compose.rememberAsyncImagePainter
@Composable
fun MainScreen(
viewModel: MainViewModel = viewModel(),
navController: NavController
) {
val state by viewModel.uiState.collectAsState()
when (val currentState = state) {
is MainState.Error -> {
Text("ИДИ НАХУЙ")
}
is MainState.Loading -> {
Column(
modifier = Modifier.fillMaxSize(),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally,
) {
CircularProgressIndicator()
}
}
is MainState.Data -> {
Column (
modifier = Modifier.fillMaxSize()
) {
Box(
modifier = Modifier
.fillMaxWidth()
.padding(top = 40.dp)
.height(200.dp),
) {
IconButton(
onClick = { viewModel.onIntent(MainIntent.Logout) },
modifier = Modifier
.align(Alignment.TopEnd)
.size(20.dp)
.aspectRatio(1f)
.offset(x = -30.dp, y = 40.dp)
) {
Icon(
Icons.AutoMirrored.Outlined.Logout,
contentDescription = null,
tint = MaterialTheme.colorScheme.error,
modifier = Modifier
.fillMaxSize()
.shadow(7.dp)
) }
Row(
modifier = Modifier.fillMaxSize(),
verticalAlignment = Alignment.CenterVertically
) {
Column (
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier
.padding(start = 30.dp)
.size(120.dp)
.aspectRatio(1f)
.background(MaterialTheme.colorScheme.primaryContainer, CircleShape)
) {
Image(
painter = rememberAsyncImagePainter("https://catalog-cdn.detmir.st/media/2fe02057f9915e72a378795d32c79ea9.jpeg"),
contentDescription = null,
contentScale = ContentScale.Fit,
modifier = Modifier.size(105.dp).clip(CircleShape)
)
}
}
}
// Box(
// modifier = Modifier
// .fillMaxSize()
// .background(MaterialTheme.colorScheme.surfaceContainerLow, RoundedCornerShape(topEnd = 24.dp , topStart = 24.dp))
// ) {
// LazyColumn(
// modifier = Modifier
// .fillMaxWidth()
// .padding(horizontal = 20.dp)
// .padding(top = 8.dp)
// ) {
// items(
// items = [],
// key = { item -> item.id }
// ) { entry ->
// OutlinedCard(
// modifier = Modifier
// .fillMaxWidth()
// .padding(top = 12.dp)
// ) {
// Text(
// text = entry.time,
// style = MaterialTheme.typography.titleMedium,
// fontWeight = FontWeight.Light,
// modifier = Modifier
// .padding(start = 16.dp)
// .padding(top = 12.dp)
// )
// Text(
// text = entry.place.name,
// style = MaterialTheme.typography.titleMedium,
// fontWeight = FontWeight.Normal,
// modifier = Modifier
// .padding(start = 16.dp)
// .padding(bottom = 12.dp)
// )
// }
// }
// }
}
}
}
LaunchedEffect(Unit) {
viewModel.onIntent(MainIntent.Fetch)
}
}

View File

@@ -0,0 +1,9 @@
package ru.myitschool.work.ui.screen.main
sealed interface MainState {
object Loading: MainState
object Data: MainState
object Error: MainState
}

View File

@@ -0,0 +1,27 @@
package ru.myitschool.work.ui.screen.main
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.launch
import ru.myitschool.work.data.repo.AuthRepository
import ru.myitschool.work.domain.main.Logout
class MainViewModel(): ViewModel() {
private val logout by lazy { Logout(AuthRepository) }
private val _uiState = MutableStateFlow<MainState>(MainState.Data)
val uiState: StateFlow<MainState> = _uiState.asStateFlow()
fun onIntent(intent: MainIntent) {
when (intent) {
is MainIntent.Fetch -> Unit
is MainIntent.Logout -> {
viewModelScope.launch {
logout.invoke()
}
}
}
}
}

View File

@@ -0,0 +1,8 @@
package ru.myitschool.work.ui.screen.splash
import androidx.compose.runtime.Composable
@Composable
fun SplashScreen() {
}