Checkpoint 3.ui.1

This commit is contained in:
student-a-suhoruchkin 2026-02-26 14:18:58 +03:00
parent 2ef8b4a307
commit 3c2d359a01
24 changed files with 337 additions and 54 deletions

View File

@ -0,0 +1,9 @@
package ru.myitschool.work.data.repo
object MeetingsRepository {
private var roomCache: String? = null
fun getRoom(): String? {
return roomCache
}
}

View File

@ -1,12 +0,0 @@
package ru.myitschool.work.domain.auth
class CheckCodeFormatUseCase {
operator fun invoke(
text: String
): Boolean {
return text.length == 4 && text.all { char ->
char.isLetterOrDigit() &&
((char in 'A'..'Z') || (char >= 'a' && char <= 'z') || char.isDigit())
}
}
}

View File

@ -0,0 +1,11 @@
package ru.myitschool.work.domain.meetings
import ru.myitschool.work.data.repo.MeetingsRepository
class GetRoomUseCase (
private val repository: MeetingsRepository
) {
suspend operator fun invoke(): String? {
return repository.getRoom()
}
}

View File

@ -0,0 +1,64 @@
package ru.myitschool.work.ui.custom.component
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.RowScope
import androidx.compose.foundation.layout.defaultMinSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.RoundRect
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import ru.myitschool.work.ui.theme.gray_gradient
import ru.myitschool.work.ui.theme.orange_gradient
@Composable
fun CustomButon(
onClick: () -> Unit,
modifier: Modifier = Modifier,
enabled: Boolean = true,
round : Dp = 10.dp,
content: @Composable RowScope.() -> Unit,
) {
Box(
modifier = modifier
.clickable { onClick() }
.background(if (enabled) orange_gradient else gray_gradient, RoundedCornerShape(round))
) {
Row(
modifier = Modifier
.defaultMinSize(
minWidth = ButtonDefaults.MinWidth,
minHeight = ButtonDefaults.MinHeight,
)
.fillMaxWidth(),
horizontalArrangement = Arrangement.Center,
verticalAlignment = Alignment.CenterVertically,
content = content,
)
}
}
@Composable
fun RoundCustomButton(
onClick: () -> Unit,
modifier: Modifier = Modifier,
enabled: Boolean = true,
content: @Composable RowScope.() -> Unit,
) {
CustomButon(
onClick, modifier, enabled, content = content,
round = 50.dp
)
}

View File

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

View File

@ -20,9 +20,11 @@ import ru.myitschool.work.ui.nav.AppDestination
import ru.myitschool.work.ui.nav.AuthScreenDestination import ru.myitschool.work.ui.nav.AuthScreenDestination
import ru.myitschool.work.ui.nav.BookScreenDestination import ru.myitschool.work.ui.nav.BookScreenDestination
import ru.myitschool.work.ui.nav.MainScreenDestination import ru.myitschool.work.ui.nav.MainScreenDestination
import ru.myitschool.work.ui.nav.MeetingsScreenDestination
import ru.myitschool.work.ui.screen.auth.AuthScreen import ru.myitschool.work.ui.screen.auth.AuthScreen
import ru.myitschool.work.ui.screen.book.BookScreen import ru.myitschool.work.ui.screen.book.BookScreen
import ru.myitschool.work.ui.screen.main.MainScreen import ru.myitschool.work.ui.screen.main.MainScreen
import ru.myitschool.work.ui.screen.meetings.MeetingsScreen
@Composable @Composable
fun AppNavHost( fun AppNavHost(
@ -37,6 +39,7 @@ fun AppNavHost(
} else { } else {
MainScreenDestination MainScreenDestination
} }
// destination = MainScreenDestination
} }
if (destination != null) { if (destination != null) {
NavHost( NavHost(
@ -55,6 +58,9 @@ fun AppNavHost(
composable<BookScreenDestination> { composable<BookScreenDestination> {
BookScreen(navController = navController) BookScreen(navController = navController)
} }
composable<MeetingsScreenDestination> {
MeetingsScreen()
}
} }
} }
} }

View File

@ -31,6 +31,7 @@ import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavController import androidx.navigation.NavController
import ru.myitschool.work.R import ru.myitschool.work.R
import ru.myitschool.work.core.TestIds import ru.myitschool.work.core.TestIds
import ru.myitschool.work.ui.custom.component.CustomButon
@Composable @Composable
fun AuthScreen( fun AuthScreen(
@ -102,7 +103,7 @@ private fun Content(
label = { Text(stringResource(R.string.auth_password)) } label = { Text(stringResource(R.string.auth_password)) }
) )
Spacer(modifier = Modifier.size(16.dp)) Spacer(modifier = Modifier.size(16.dp))
Button( CustomButon(
modifier = Modifier.testTag(TestIds.Auth.SIGN_BUTTON).fillMaxWidth(), modifier = Modifier.testTag(TestIds.Auth.SIGN_BUTTON).fillMaxWidth(),
onClick = { onClick = {
viewModel.onIntent(AuthIntent.Send(inputTextLogin, inputTextPassword)) viewModel.onIntent(AuthIntent.Send(inputTextLogin, inputTextPassword))

View File

@ -141,6 +141,22 @@ private fun ErrorState(
horizontalAlignment = Alignment.CenterHorizontally, horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center verticalArrangement = Arrangement.Center
) { ) {
when (state.error) {
"No auth" -> {
Image(
painter = painterResource(R.drawable.no_accounts),
null,
Modifier.size(100.dp)
)
}
"Not internet" -> {
Image(
painter = painterResource(R.drawable.not_wifi),
null,
Modifier.size(100.dp)
)
}
}
Text( Text(
modifier = Modifier.testTag(TestIds.Book.ERROR), modifier = Modifier.testTag(TestIds.Book.ERROR),
text = state.error, text = state.error,

View File

@ -14,7 +14,6 @@ import androidx.compose.foundation.layout.size
import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.itemsIndexed import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material3.Button
import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.FloatingActionButton import androidx.compose.material3.FloatingActionButton
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
@ -30,9 +29,13 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.ImageBitmap
import androidx.compose.ui.graphics.painter.Painter
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.testTag import androidx.compose.ui.platform.testTag
import androidx.compose.ui.res.imageResource
import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
@ -43,6 +46,8 @@ import coil3.compose.AsyncImage
import coil3.request.ImageRequest import coil3.request.ImageRequest
import ru.myitschool.work.R import ru.myitschool.work.R
import ru.myitschool.work.core.TestIds import ru.myitschool.work.core.TestIds
import ru.myitschool.work.ui.custom.component.CustomButon
import ru.myitschool.work.ui.custom.component.RoundCustomButton
@Composable @Composable
fun MainScreen( fun MainScreen(
@ -113,14 +118,30 @@ private fun ErrorState(
horizontalAlignment = Alignment.CenterHorizontally, horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center verticalArrangement = Arrangement.Center
) { ) {
when (state.error) {
"No auth" -> {
Image(
painter = painterResource(R.drawable.no_accounts),
null,
Modifier.size(100.dp)
)
}
"Not internet" -> {
Image(
painter = painterResource(R.drawable.not_wifi),
null,
Modifier.size(100.dp)
)
}
}
Text( Text(
modifier = Modifier.testTag(TestIds.Main.ERROR), modifier = Modifier.testTag(TestIds.Main.ERROR),
text = state.error, text = state.error,
style = MaterialTheme.typography.headlineSmall, style = MaterialTheme.typography.headlineSmall,
color = Color.Black, color = Color.Red
) )
Spacer(modifier = Modifier.size(16.dp)) Spacer(modifier = Modifier.size(16.dp))
Button( RoundCustomButton(
modifier = Modifier.testTag(TestIds.Main.REFRESH_BUTTON).fillMaxWidth(), modifier = Modifier.testTag(TestIds.Main.REFRESH_BUTTON).fillMaxWidth(),
onClick = { onClick = {
println("!!!!!!!! refresh on click error") println("!!!!!!!! refresh on click error")

View File

@ -1,8 +1,17 @@
package ru.myitschool.work.ui.screen.meetings package ru.myitschool.work.ui.screen.meetings
sealed interface MeetingIntent { sealed interface MeetingIntent {
/**
* Обнавление страницы
*/
data object Refresh: MeetingIntent data object Refresh: MeetingIntent
/**
* Выход
*/
data object Logout: MeetingIntent data object Logout: MeetingIntent
/**
* Бронирование
*/
data class Add( data class Add(
val date: String, val date: String,
val placeId: String val placeId: String

View File

@ -1,4 +1,62 @@
package ru.myitschool.work.ui.screen.meetings package ru.myitschool.work.ui.screen.meetings
class MeetingsScreen { import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
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.Modifier
import androidx.lifecycle.viewmodel.compose.viewModel
import ru.myitschool.work.ui.screen.book.BookAction
import ru.myitschool.work.ui.screen.book.BookViewModel
import ru.myitschool.work.ui.screen.main.MainResult
@Composable
fun MeetingsScreen(
viewModel: MeetingsViewModel = viewModel()
) {
val state by viewModel.uiState.collectAsState()
Column(
Modifier
.fillMaxSize()
) {
when (val currentState = state) {
is MeetingsState.Data -> MeetingsData(currentState)
is MeetingsState.Empty -> MeetingsEmpty(currentState)
is MeetingsState.Error -> TODO()
is MeetingsState.Loading -> TODO()
}
}
}
@Composable
fun MeetingsData(
state: MeetingsState.Data
) {
}
@Composable
fun MeetingsEmpty(
state: MeetingsState.Empty
) {
}
@Composable
fun MeetingsError(
state: MeetingsState.Error
) {
}
@Composable
fun MeetingsLoading(
state: MeetingsState.Loading
) {
} }

View File

@ -9,22 +9,11 @@ sealed interface MeetingsState {
val error: String val error: String
): MeetingsState ): MeetingsState
data class Data( data class Data(
val name: String,
val photoUrl: String,
val books: PersistentList<Book>, val books: PersistentList<Book>,
val items: PersistentList<Item>
): MeetingsState { ): MeetingsState {
data class Book( data class Book(
val date: String, val date: String,
val place: String,
)
data class Item(
val date: String,
val places: PersistentList<Place>,
)
data class Place(
val id: String,
val name: String,
) )
} }
} }

View File

@ -1,4 +1,18 @@
package ru.myitschool.work.ui.screen.meetings package ru.myitschool.work.ui.screen.meetings
class MeetingsViewModel { import androidx.lifecycle.ViewModel
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
class MeetingsViewModel : ViewModel() {
private val _uiState = MutableStateFlow<MeetingsState>(MeetingsState.Loading)
val uiState: StateFlow<MeetingsState> = _uiState.asStateFlow()
private val _actionFlow: MutableSharedFlow<MeetingsAction> = MutableSharedFlow()
val actionFlow: SharedFlow<MeetingsAction> = _actionFlow
} }

View File

@ -1,11 +1,20 @@
package ru.myitschool.work.ui.theme package ru.myitschool.work.ui.theme
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
val Purple80 = Color(0xFFD0BCFF) val day_primary = Color(0xFFffa500)
val PurpleGrey80 = Color(0xFFCCC2DC) val day_secondary = Color(0xFFffd700)
val Pink80 = Color(0xFFEFB8C8) val day_tertiary = Color(0xFFffbe00)
val day_text = Color(0xFF003366)
val day_background = Color(0xFFf0f0f0)
val Purple40 = Color(0xFF6650a4) val night_primary = Color(0xFFff8c00)
val PurpleGrey40 = Color(0xFF625b71) val night_secondary = Color(0xFFff7300)
val Pink40 = Color(0xFF7D5260) val night_tertiary = Color(0xFFff5a00)
val night_text = Color(0xFFffe4b5)
val night_background = Color(0xFF121212)
val orange_gradient = Brush.linearGradient(listOf(day_primary, night_primary))
val gray_gradient = Brush.linearGradient(listOf(Color(0xFFc4c7cf), Color(0xFF828897)))

View File

@ -0,0 +1,26 @@
package ru.myitschool.work.ui.theme
import androidx.compose.ui.text.font.Font
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.font.FontWeight
import ru.myitschool.work.R
val subtitle = FontFamily(
Font(
R.font.merriweather_120pt_medium,
weight = FontWeight.Medium,
)
)
val title = FontFamily(
Font(
R.font.merriweather_120pt_bold,
weight = FontWeight.Bold
)
)
val normal = FontFamily(
Font(
R.font.roboto_serif_120pt_regular,
)
)

View File

@ -11,15 +11,21 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
private val DarkColorScheme = darkColorScheme( private val DarkColorScheme = darkColorScheme(
primary = Purple80, primary = night_primary,
secondary = PurpleGrey80, secondary = night_secondary,
tertiary = Pink80 tertiary = night_tertiary,
background = night_background,
onBackground = night_text,
onSurface = night_text,
) )
private val LightColorScheme = lightColorScheme( private val LightColorScheme = lightColorScheme(
primary = Purple40, primary = day_primary,
secondary = PurpleGrey40, secondary = day_secondary,
tertiary = Pink40 tertiary = day_tertiary,
background = day_background,
onBackground = day_text,
onSurface = day_text
/* Other default colors to override /* Other default colors to override
background = Color(0xFFFFFBFE), background = Color(0xFFFFFBFE),
@ -36,7 +42,7 @@ private val LightColorScheme = lightColorScheme(
fun WorkTheme( fun WorkTheme(
darkTheme: Boolean = isSystemInDarkTheme(), darkTheme: Boolean = isSystemInDarkTheme(),
// Dynamic color is available on Android 12+ // Dynamic color is available on Android 12+
dynamicColor: Boolean = true, dynamicColor: Boolean = false,
content: @Composable () -> Unit content: @Composable () -> Unit
) { ) {
val colorScheme = when { val colorScheme = when {

View File

@ -9,26 +9,24 @@ import androidx.compose.ui.unit.sp
// Set of Material typography styles to start with // Set of Material typography styles to start with
val Typography = Typography( val Typography = Typography(
bodyLarge = TextStyle( bodyLarge = TextStyle(
fontFamily = FontFamily.Default, fontFamily = subtitle,
fontWeight = FontWeight.Normal, fontWeight = FontWeight.Medium,
fontSize = 16.sp, fontSize = 16.sp,
lineHeight = 24.sp, lineHeight = 24.sp,
letterSpacing = 0.5.sp letterSpacing = 0.5.sp
) ),
/* Other default text styles to override
titleLarge = TextStyle( titleLarge = TextStyle(
fontFamily = FontFamily.Default, fontFamily = title,
fontWeight = FontWeight.Normal, fontWeight = FontWeight.Bold,
fontSize = 22.sp, fontSize = 22.sp,
lineHeight = 28.sp, lineHeight = 28.sp,
letterSpacing = 0.sp letterSpacing = 0.sp
), ),
labelSmall = TextStyle( labelSmall = TextStyle(
fontFamily = FontFamily.Default, fontFamily = normal,
fontWeight = FontWeight.Medium, fontWeight = FontWeight.Normal,
fontSize = 11.sp, fontSize = 11.sp,
lineHeight = 16.sp, lineHeight = 16.sp,
letterSpacing = 0.5.sp letterSpacing = 0.5.sp
) )
*/
) )

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -4,5 +4,5 @@
# Location of the SDK. This is only used by Gradle. # Location of the SDK. This is only used by Gradle.
# For customization when using a Version Control System, please read the # For customization when using a Version Control System, please read the
# header note. # header note.
#Tue Feb 24 18:06:36 MSK 2026 #Wed Feb 25 10:24:12 MSK 2026
sdk.dir=C\:\\Users\\Samsung\\AppData\\Local\\Android\\Sdk sdk.dir=C\:\\Users\\Samsung\\AppData\\Local\\Android\\Sdk