From 44c1f0154171ae6f05959ddcf3cb35f960847eb7 Mon Sep 17 00:00:00 2001 From: N08I40K Date: Fri, 20 Dec 2024 23:44:19 +0400 Subject: [PATCH] 2.3.0 --- .idea/.name | 1 + .idea/dictionaries/n08i40k.xml | 3 + app/build.gradle.kts | 4 +- .../polytechnic/next/ui/auth/SignUpForm.kt | 35 ++++-- .../polytechnic/next/ui/main/Constants.kt | 18 ++- .../polytechnic/next/ui/main/MainScreen.kt | 53 ++++++--- .../next/ui/main/profile/ChangeGroupDialog.kt | 7 +- .../next/ui/main/schedule/DayPager.kt | 4 +- .../teacher/main/TeacherMainScheduleScreen.kt | 112 ++++++++++++++++++ .../schedule/teacher/{ => user}/SearchBox.kt | 2 +- .../teacher/{ => user}/TeacherSearchBox.kt | 2 +- .../TeacherUserScheduleScreen.kt} | 4 +- .../next/ui/widgets/GroupSelector.kt | 9 +- .../next/ui/widgets/TeacherNameSelector.kt | 109 +++++++++++++++++ app/src/main/res/values-ru/strings.xml | 2 + app/src/main/res/values/strings.xml | 2 + 16 files changed, 323 insertions(+), 44 deletions(-) create mode 100644 .idea/.name create mode 100644 .idea/dictionaries/n08i40k.xml create mode 100644 app/src/main/java/ru/n08i40k/polytechnic/next/ui/main/schedule/teacher/main/TeacherMainScheduleScreen.kt rename app/src/main/java/ru/n08i40k/polytechnic/next/ui/main/schedule/teacher/{ => user}/SearchBox.kt (97%) rename app/src/main/java/ru/n08i40k/polytechnic/next/ui/main/schedule/teacher/{ => user}/TeacherSearchBox.kt (95%) rename app/src/main/java/ru/n08i40k/polytechnic/next/ui/main/schedule/teacher/{TeacherScheduleScreen.kt => user/TeacherUserScheduleScreen.kt} (97%) create mode 100644 app/src/main/java/ru/n08i40k/polytechnic/next/ui/widgets/TeacherNameSelector.kt diff --git a/.idea/.name b/.idea/.name new file mode 100644 index 0000000..f866a66 --- /dev/null +++ b/.idea/.name @@ -0,0 +1 @@ +PolytecnicNext \ No newline at end of file diff --git a/.idea/dictionaries/n08i40k.xml b/.idea/dictionaries/n08i40k.xml new file mode 100644 index 0000000..24d5649 --- /dev/null +++ b/.idea/dictionaries/n08i40k.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 5072a76..414f30a 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -33,8 +33,8 @@ android { applicationId = "ru.n08i40k.polytechnic.next" minSdk = 26 targetSdk = 35 - versionCode = 22 - versionName = "2.2.1" + versionCode = 23 + versionName = "2.3.0" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" vectorDrawables { diff --git a/app/src/main/java/ru/n08i40k/polytechnic/next/ui/auth/SignUpForm.kt b/app/src/main/java/ru/n08i40k/polytechnic/next/ui/auth/SignUpForm.kt index 76ccc02..f1c84b1 100644 --- a/app/src/main/java/ru/n08i40k/polytechnic/next/ui/auth/SignUpForm.kt +++ b/app/src/main/java/ru/n08i40k/polytechnic/next/ui/auth/SignUpForm.kt @@ -29,6 +29,7 @@ import ru.n08i40k.polytechnic.next.R import ru.n08i40k.polytechnic.next.model.UserRole import ru.n08i40k.polytechnic.next.ui.widgets.GroupSelector import ru.n08i40k.polytechnic.next.ui.widgets.RoleSelector +import ru.n08i40k.polytechnic.next.ui.widgets.TeacherNameSelector @Preview(showBackground = true) @@ -105,17 +106,26 @@ internal fun RegisterForm( Spacer(modifier = Modifier.size(10.dp)) - OutlinedTextField( - value = username, - singleLine = true, - onValueChange = { - username = it - usernameError = false - }, - label = { Text(stringResource(R.string.username)) }, - isError = usernameError, - readOnly = loading - ) + if (role != UserRole.TEACHER) { + OutlinedTextField( + value = username, + singleLine = true, + onValueChange = { + username = it + usernameError = false + }, + label = { Text(stringResource(R.string.username)) }, + isError = usernameError, + readOnly = loading + ) + } else { + TeacherNameSelector( + value = username, + isError = usernameError, + readOnly = loading, + onValueChange = { username = it ?: "" } + ) + } OutlinedTextField( value = password, @@ -135,7 +145,8 @@ internal fun RegisterForm( GroupSelector( value = group, isError = groupError, - readOnly = loading + readOnly = loading, + teacher = role == UserRole.TEACHER ) { groupError = false group = it diff --git a/app/src/main/java/ru/n08i40k/polytechnic/next/ui/main/Constants.kt b/app/src/main/java/ru/n08i40k/polytechnic/next/ui/main/Constants.kt index 1911aa9..8e06b81 100644 --- a/app/src/main/java/ru/n08i40k/polytechnic/next/ui/main/Constants.kt +++ b/app/src/main/java/ru/n08i40k/polytechnic/next/ui/main/Constants.kt @@ -8,19 +8,31 @@ import androidx.compose.material.icons.filled.DateRange import androidx.compose.material.icons.filled.Person import androidx.compose.ui.graphics.vector.ImageVector import ru.n08i40k.polytechnic.next.R +import ru.n08i40k.polytechnic.next.model.UserRole data class BottomNavItem( @StringRes val label: Int, val icon: ImageVector, val route: String, - val isAdmin: Boolean = false + val requiredRole: UserRole? = null ) object Constants { val bottomNavItem = listOf( BottomNavItem(R.string.profile, Icons.Filled.AccountCircle, "profile"), - BottomNavItem(R.string.replacer, Icons.Filled.Create, "replacer", true), + BottomNavItem(R.string.replacer, Icons.Filled.Create, "replacer", UserRole.ADMIN), + BottomNavItem( + R.string.teacher_schedule, + Icons.Filled.Person, + "teacher-main-schedule", + UserRole.TEACHER + ), BottomNavItem(R.string.schedule, Icons.Filled.DateRange, "schedule"), - BottomNavItem(R.string.teachers, Icons.Filled.Person, "teacher-schedule") + BottomNavItem( + R.string.teachers, + Icons.Filled.Person, + "teacher-user-schedule", + UserRole.STUDENT + ) ) } \ No newline at end of file diff --git a/app/src/main/java/ru/n08i40k/polytechnic/next/ui/main/MainScreen.kt b/app/src/main/java/ru/n08i40k/polytechnic/next/ui/main/MainScreen.kt index a7b68d0..4db7d5c 100644 --- a/app/src/main/java/ru/n08i40k/polytechnic/next/ui/main/MainScreen.kt +++ b/app/src/main/java/ru/n08i40k/polytechnic/next/ui/main/MainScreen.kt @@ -61,6 +61,7 @@ import kotlinx.coroutines.runBlocking import ru.n08i40k.polytechnic.next.MainViewModel import ru.n08i40k.polytechnic.next.PolytechnicApplication import ru.n08i40k.polytechnic.next.R +import ru.n08i40k.polytechnic.next.model.Profile import ru.n08i40k.polytechnic.next.model.UserRole import ru.n08i40k.polytechnic.next.settings.settingsDataStore import ru.n08i40k.polytechnic.next.ui.icons.AppIcons @@ -70,7 +71,8 @@ import ru.n08i40k.polytechnic.next.ui.icons.appicons.filled.Telegram import ru.n08i40k.polytechnic.next.ui.main.profile.ProfileScreen import ru.n08i40k.polytechnic.next.ui.main.replacer.ReplacerScreen import ru.n08i40k.polytechnic.next.ui.main.schedule.group.GroupScheduleScreen -import ru.n08i40k.polytechnic.next.ui.main.schedule.teacher.TeacherScheduleScreen +import ru.n08i40k.polytechnic.next.ui.main.schedule.teacher.main.TeacherMainScheduleScreen +import ru.n08i40k.polytechnic.next.ui.main.schedule.teacher.user.TeacherUserScheduleScreen import ru.n08i40k.polytechnic.next.ui.model.GroupScheduleViewModel import ru.n08i40k.polytechnic.next.ui.model.ProfileUiState import ru.n08i40k.polytechnic.next.ui.model.ProfileViewModel @@ -84,15 +86,27 @@ import ru.n08i40k.polytechnic.next.ui.model.profileViewModel private fun NavHostContainer( navController: NavHostController, padding: PaddingValues, + profileViewModel: ProfileViewModel, groupScheduleViewModel: GroupScheduleViewModel, teacherScheduleViewModel: TeacherScheduleViewModel, scheduleReplacerViewModel: ScheduleReplacerViewModel? ) { val context = LocalContext.current + val profileUiState by profileViewModel.uiState.collectAsStateWithLifecycle() + + val profile: Profile? = when (profileUiState) { + is ProfileUiState.NoProfile -> null + is ProfileUiState.HasProfile -> + (profileUiState as ProfileUiState.HasProfile).profile + } + + if (profile == null) + return + NavHost( navController = navController, - startDestination = "schedule", + startDestination = if (profile.role == UserRole.TEACHER) "teacher-main-schedule" else "schedule", modifier = Modifier.padding(paddingValues = padding), enterTransition = { slideIn( @@ -126,8 +140,16 @@ private fun NavHostContainer( GroupScheduleScreen(groupScheduleViewModel) { groupScheduleViewModel.refresh() } } - composable("teacher-schedule") { - TeacherScheduleScreen(teacherScheduleViewModel) { + composable("teacher-user-schedule") { + TeacherUserScheduleScreen(teacherScheduleViewModel) { + if (it.isNotEmpty()) teacherScheduleViewModel.fetch( + it + ) + } + } + + composable("teacher-main-schedule") { + TeacherMainScheduleScreen(teacherScheduleViewModel) { if (it.isNotEmpty()) teacherScheduleViewModel.fetch( it ) @@ -231,14 +253,14 @@ private fun TopNavBar( } @Composable -private fun BottomNavBar(navController: NavHostController, isAdmin: Boolean) { +private fun BottomNavBar(navController: NavHostController, userRole: UserRole) { NavigationBar { val navBackStackEntry by navController.currentBackStackEntryAsState() val currentRoute = navBackStackEntry?.destination?.route Constants.bottomNavItem.forEach { - if (it.isAdmin && !isAdmin) + if (it.requiredRole != null && it.requiredRole != userRole && userRole != UserRole.ADMIN) return@forEach NavigationBarItem( @@ -303,17 +325,17 @@ fun MainScreen( // schedule replacer view model val profileUiState by profileViewModel.uiState.collectAsStateWithLifecycle() - val isAdmin = when (profileUiState) { - is ProfileUiState.NoProfile -> false - is ProfileUiState.HasProfile -> { - val profile = (profileUiState as ProfileUiState.HasProfile).profile - - profile.role == UserRole.ADMIN - } + val profile: Profile? = when (profileUiState) { + is ProfileUiState.NoProfile -> null + is ProfileUiState.HasProfile -> + (profileUiState as ProfileUiState.HasProfile).profile } + if (profile == null) + return + val scheduleReplacerViewModel: ScheduleReplacerViewModel? = - if (isAdmin) hiltViewModel(LocalContext.current as ComponentActivity) + if (profile.role == UserRole.ADMIN) hiltViewModel(LocalContext.current as ComponentActivity) else null // nav controller @@ -321,11 +343,12 @@ fun MainScreen( val navController = rememberNavController() Scaffold( topBar = { TopNavBar(remoteConfigViewModel) }, - bottomBar = { BottomNavBar(navController, isAdmin) } + bottomBar = { BottomNavBar(navController, profile.role) } ) { paddingValues -> NavHostContainer( navController, paddingValues, + profileViewModel, groupScheduleViewModel, teacherScheduleViewModel, scheduleReplacerViewModel diff --git a/app/src/main/java/ru/n08i40k/polytechnic/next/ui/main/profile/ChangeGroupDialog.kt b/app/src/main/java/ru/n08i40k/polytechnic/next/ui/main/profile/ChangeGroupDialog.kt index a8cbcbb..ada7752 100644 --- a/app/src/main/java/ru/n08i40k/polytechnic/next/ui/main/profile/ChangeGroupDialog.kt +++ b/app/src/main/java/ru/n08i40k/polytechnic/next/ui/main/profile/ChangeGroupDialog.kt @@ -23,6 +23,7 @@ import com.android.volley.ClientError import ru.n08i40k.polytechnic.next.R import ru.n08i40k.polytechnic.next.data.users.impl.FakeProfileRepository import ru.n08i40k.polytechnic.next.model.Profile +import ru.n08i40k.polytechnic.next.model.UserRole import ru.n08i40k.polytechnic.next.network.request.profile.ProfileChangeGroup import ru.n08i40k.polytechnic.next.ui.widgets.GroupSelector @@ -65,10 +66,10 @@ internal fun ChangeGroupDialog( GroupSelector( value = group, - onValueChange = { group = it }, isError = groupError, - readOnly = processing - ) + readOnly = processing, + teacher = profile.role == UserRole.TEACHER + ) { group = it } val focusManager = LocalFocusManager.current Button( diff --git a/app/src/main/java/ru/n08i40k/polytechnic/next/ui/main/schedule/DayPager.kt b/app/src/main/java/ru/n08i40k/polytechnic/next/ui/main/schedule/DayPager.kt index 419f770..8a3ea80 100644 --- a/app/src/main/java/ru/n08i40k/polytechnic/next/ui/main/schedule/DayPager.kt +++ b/app/src/main/java/ru/n08i40k/polytechnic/next/ui/main/schedule/DayPager.kt @@ -61,7 +61,9 @@ fun DayPager(groupOrTeacher: GroupOrTeacher = FakeScheduleRepository.exampleGrou ) { page -> DayCard( modifier = Modifier.graphicsLayer { - val offset = pagerState.getOffsetDistanceInPages(page).absoluteValue + val offset = pagerState.getOffsetDistanceInPages( + page.coerceIn(0, pagerState.pageCount - 1) + ).absoluteValue lerp( start = 1f, stop = 0.95f, fraction = 1f - offset.coerceIn(0f, 1f) diff --git a/app/src/main/java/ru/n08i40k/polytechnic/next/ui/main/schedule/teacher/main/TeacherMainScheduleScreen.kt b/app/src/main/java/ru/n08i40k/polytechnic/next/ui/main/schedule/teacher/main/TeacherMainScheduleScreen.kt new file mode 100644 index 0000000..1bed1ff --- /dev/null +++ b/app/src/main/java/ru/n08i40k/polytechnic/next/ui/main/schedule/teacher/main/TeacherMainScheduleScreen.kt @@ -0,0 +1,112 @@ +package ru.n08i40k.polytechnic.next.ui.main.schedule.teacher.main + +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.DisposableEffect +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.remember +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.tooling.preview.Preview +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.LifecycleEventObserver +import androidx.lifecycle.LifecycleOwner +import androidx.lifecycle.compose.LocalLifecycleOwner +import androidx.lifecycle.compose.collectAsStateWithLifecycle +import kotlinx.coroutines.delay +import ru.n08i40k.polytechnic.next.R +import ru.n08i40k.polytechnic.next.data.MockAppContainer +import ru.n08i40k.polytechnic.next.ui.main.schedule.DayPager +import ru.n08i40k.polytechnic.next.ui.model.ProfileUiState +import ru.n08i40k.polytechnic.next.ui.model.TeacherScheduleUiState +import ru.n08i40k.polytechnic.next.ui.model.TeacherScheduleViewModel +import ru.n08i40k.polytechnic.next.ui.model.profileViewModel +import ru.n08i40k.polytechnic.next.ui.widgets.LoadingContent + +@Composable +private fun rememberUpdatedLifecycleOwner(): LifecycleOwner { + val lifecycleOwner = LocalLifecycleOwner.current + return remember { lifecycleOwner } +} + +@Preview(showBackground = true, showSystemUi = true) +@Composable +fun TeacherMainScheduleScreen( + teacherScheduleViewModel: TeacherScheduleViewModel = TeacherScheduleViewModel( + MockAppContainer( + LocalContext.current + ) + ), + fetch: (String) -> Unit = {} +) { + val profileViewModel = LocalContext.current.profileViewModel!! + val profileUiState by profileViewModel.uiState.collectAsStateWithLifecycle() + + if (profileUiState is ProfileUiState.NoProfile) + return + + val profile = (profileUiState as ProfileUiState.HasProfile).profile + + var teacherName = profile.username + + val uiState by teacherScheduleViewModel.uiState.collectAsStateWithLifecycle() + LaunchedEffect(uiState) { + delay(120_000) + fetch(teacherName) + } + + val lifecycleOwner = rememberUpdatedLifecycleOwner() + + DisposableEffect(lifecycleOwner) { + val observer = LifecycleEventObserver { _, event -> + when (event) { + Lifecycle.Event.ON_RESUME -> { + fetch(teacherName) + } + + else -> Unit + } + } + + lifecycleOwner.lifecycle.addObserver(observer) + + onDispose { + lifecycleOwner.lifecycle.removeObserver(observer) + } + } + + Column(Modifier.fillMaxSize()) { + LoadingContent( + empty = when (uiState) { + is TeacherScheduleUiState.NoData -> uiState.isLoading + is TeacherScheduleUiState.HasData -> false + }, + loading = uiState.isLoading, + ) { + when (uiState) { + is TeacherScheduleUiState.HasData -> { + Column { + val hasData = uiState as TeacherScheduleUiState.HasData + + DayPager(hasData.teacher) + } + } + + is TeacherScheduleUiState.NoData -> { + if (!uiState.isLoading) { + Text( + modifier = Modifier.fillMaxSize(), + text = stringResource(R.string.teacher_not_selected), + textAlign = TextAlign.Center + ) + } + } + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/ru/n08i40k/polytechnic/next/ui/main/schedule/teacher/SearchBox.kt b/app/src/main/java/ru/n08i40k/polytechnic/next/ui/main/schedule/teacher/user/SearchBox.kt similarity index 97% rename from app/src/main/java/ru/n08i40k/polytechnic/next/ui/main/schedule/teacher/SearchBox.kt rename to app/src/main/java/ru/n08i40k/polytechnic/next/ui/main/schedule/teacher/user/SearchBox.kt index 4eeaad7..84814d2 100644 --- a/app/src/main/java/ru/n08i40k/polytechnic/next/ui/main/schedule/teacher/SearchBox.kt +++ b/app/src/main/java/ru/n08i40k/polytechnic/next/ui/main/schedule/teacher/user/SearchBox.kt @@ -1,4 +1,4 @@ -package ru.n08i40k.polytechnic.next.ui.main.schedule.teacher +package ru.n08i40k.polytechnic.next.ui.main.schedule.teacher.user import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxWidth diff --git a/app/src/main/java/ru/n08i40k/polytechnic/next/ui/main/schedule/teacher/TeacherSearchBox.kt b/app/src/main/java/ru/n08i40k/polytechnic/next/ui/main/schedule/teacher/user/TeacherSearchBox.kt similarity index 95% rename from app/src/main/java/ru/n08i40k/polytechnic/next/ui/main/schedule/teacher/TeacherSearchBox.kt rename to app/src/main/java/ru/n08i40k/polytechnic/next/ui/main/schedule/teacher/user/TeacherSearchBox.kt index 0c34c91..5638c9c 100644 --- a/app/src/main/java/ru/n08i40k/polytechnic/next/ui/main/schedule/teacher/TeacherSearchBox.kt +++ b/app/src/main/java/ru/n08i40k/polytechnic/next/ui/main/schedule/teacher/user/TeacherSearchBox.kt @@ -1,4 +1,4 @@ -package ru.n08i40k.polytechnic.next.ui.main.schedule.teacher +package ru.n08i40k.polytechnic.next.ui.main.schedule.teacher.user import android.content.Context import androidx.compose.runtime.Composable diff --git a/app/src/main/java/ru/n08i40k/polytechnic/next/ui/main/schedule/teacher/TeacherScheduleScreen.kt b/app/src/main/java/ru/n08i40k/polytechnic/next/ui/main/schedule/teacher/user/TeacherUserScheduleScreen.kt similarity index 97% rename from app/src/main/java/ru/n08i40k/polytechnic/next/ui/main/schedule/teacher/TeacherScheduleScreen.kt rename to app/src/main/java/ru/n08i40k/polytechnic/next/ui/main/schedule/teacher/user/TeacherUserScheduleScreen.kt index 6419ac9..dda37b2 100644 --- a/app/src/main/java/ru/n08i40k/polytechnic/next/ui/main/schedule/teacher/TeacherScheduleScreen.kt +++ b/app/src/main/java/ru/n08i40k/polytechnic/next/ui/main/schedule/teacher/user/TeacherUserScheduleScreen.kt @@ -1,4 +1,4 @@ -package ru.n08i40k.polytechnic.next.ui.main.schedule.teacher +package ru.n08i40k.polytechnic.next.ui.main.schedule.teacher.user import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer @@ -39,7 +39,7 @@ private fun rememberUpdatedLifecycleOwner(): LifecycleOwner { @Preview(showBackground = true, showSystemUi = true) @Composable -fun TeacherScheduleScreen( +fun TeacherUserScheduleScreen( teacherScheduleViewModel: TeacherScheduleViewModel = TeacherScheduleViewModel( MockAppContainer( LocalContext.current diff --git a/app/src/main/java/ru/n08i40k/polytechnic/next/ui/widgets/GroupSelector.kt b/app/src/main/java/ru/n08i40k/polytechnic/next/ui/widgets/GroupSelector.kt index 3f06d4c..abd409e 100644 --- a/app/src/main/java/ru/n08i40k/polytechnic/next/ui/widgets/GroupSelector.kt +++ b/app/src/main/java/ru/n08i40k/polytechnic/next/ui/widgets/GroupSelector.kt @@ -27,7 +27,7 @@ import ru.n08i40k.polytechnic.next.R import ru.n08i40k.polytechnic.next.network.request.schedule.ScheduleGetGroupNames @Composable -private fun getGroups(context: Context, onUpdated: (String?) -> Unit): ArrayList { +private fun getTeacherNames(context: Context, onUpdated: (String?) -> Unit): ArrayList { val groupPlaceholder = stringResource(R.string.loading) val groups = remember { arrayListOf(null, groupPlaceholder) } @@ -55,6 +55,7 @@ fun GroupSelector( value: String? = "ИС-214/24", isError: Boolean = false, readOnly: Boolean = false, + teacher: Boolean = false, onValueChange: (String?) -> Unit = {}, ) { var expanded by remember { mutableStateOf(false) } @@ -68,10 +69,10 @@ fun GroupSelector( expanded = !readOnly && !expanded } ) { - val groups = getGroups(LocalContext.current, onValueChange) + val groups = getTeacherNames(LocalContext.current, onValueChange) TextField( - label = { Text(stringResource(R.string.group)) }, + label = { Text(stringResource(if (teacher) R.string.supervised_group else R.string.group)) }, modifier = Modifier.menuAnchor(MenuAnchorType.PrimaryEditable), value = value ?: groups.getOrElse(1) { "TODO" }!!, leadingIcon = { @@ -97,7 +98,7 @@ fun GroupSelector( DropdownMenuItem( text = { Text(it) }, onClick = { - if (groups.size > 0 && groups[0] != null) + if (groups.isNotEmpty() && groups[0] != null) onValueChange(it) expanded = false } diff --git a/app/src/main/java/ru/n08i40k/polytechnic/next/ui/widgets/TeacherNameSelector.kt b/app/src/main/java/ru/n08i40k/polytechnic/next/ui/widgets/TeacherNameSelector.kt new file mode 100644 index 0000000..62a7c35 --- /dev/null +++ b/app/src/main/java/ru/n08i40k/polytechnic/next/ui/widgets/TeacherNameSelector.kt @@ -0,0 +1,109 @@ +package ru.n08i40k.polytechnic.next.ui.widgets + +import android.content.Context +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.wrapContentSize +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Person +import androidx.compose.material3.DropdownMenuItem +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.ExposedDropdownMenuBox +import androidx.compose.material3.ExposedDropdownMenuDefaults +import androidx.compose.material3.Icon +import androidx.compose.material3.MenuAnchorType +import androidx.compose.material3.Text +import androidx.compose.material3.TextField +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.tooling.preview.Preview +import ru.n08i40k.polytechnic.next.R +import ru.n08i40k.polytechnic.next.network.request.schedule.ScheduleGetTeacherNames + +@Composable +private fun getTeacherNames(context: Context, onUpdated: (String?) -> Unit): ArrayList { + val groupPlaceholder = stringResource(R.string.loading) + + val names = remember { arrayListOf(null, groupPlaceholder) } + + LaunchedEffect(names) { + ScheduleGetTeacherNames(context, { + names.clear() + names.addAll(it.names) + onUpdated(names.getOrElse(0) { "TODO" }!!) + }, { + names.clear() + names.add(null) + names.add(context.getString(R.string.failed_to_fetch_teacher_names)) + onUpdated(names[1]!!) + }).send() + } + + return names +} + +@OptIn(ExperimentalMaterial3Api::class) +@Preview(showBackground = true) +@Composable +fun TeacherNameSelector( + value: String? = "Фамилия И.О.", + isError: Boolean = false, + readOnly: Boolean = false, + onValueChange: (String?) -> Unit = {}, +) { + var expanded by remember { mutableStateOf(false) } + + Box( + modifier = Modifier.wrapContentSize() + ) { + ExposedDropdownMenuBox( + expanded = expanded, + onExpandedChange = { + expanded = !readOnly && !expanded + } + ) { + val names = getTeacherNames(LocalContext.current, onValueChange) + + TextField( + label = { Text(stringResource(R.string.username)) }, + modifier = Modifier.menuAnchor(MenuAnchorType.PrimaryEditable), + value = value ?: names.getOrElse(1) { "TODO" }!!, + leadingIcon = { + Icon( + Icons.Filled.Person, + contentDescription = "username" + ) + }, + onValueChange = {}, + isError = isError, + readOnly = true, + trailingIcon = { ExposedDropdownMenuDefaults.TrailingIcon(expanded = expanded) } + ) + + ExposedDropdownMenu( + expanded = expanded, + onDismissRequest = { expanded = false } + ) { + names.forEach { + if (it == null) + return@forEach + + DropdownMenuItem( + text = { Text(it) }, + onClick = { + if (names.isNotEmpty() && names[0] != null) + onValueChange(it) + expanded = false + } + ) + } + } + } + } +} \ No newline at end of file diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index b23dfe2..d287c2a 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -85,4 +85,6 @@ ЗАЧЁТ ЗАЧЁТ С ОЦЕНКОЙ ЭКЗАМЕН + Курируемая группа + Преподаватель \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 06c22d3..24b4c3d 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -85,4 +85,6 @@ EXAM* EXAM* WITH GRADE EXAM + Supervised group + Teacher \ No newline at end of file