diff --git a/app/src/main/java/ru/sweetbread/unn/API.kt b/app/src/main/java/ru/sweetbread/unn/API.kt index 74ed098..f83a5e5 100644 --- a/app/src/main/java/ru/sweetbread/unn/API.kt +++ b/app/src/main/java/ru/sweetbread/unn/API.kt @@ -1,3 +1,8 @@ +/* + * Created by sweetbread + * Copyright (c) 2025. All rights reserved. + */ + package ru.sweetbread.unn import android.util.Log @@ -8,8 +13,6 @@ import io.ktor.client.request.parameter import io.ktor.client.statement.bodyAsText import io.ktor.http.parameters import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.GlobalScope -import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import org.json.JSONArray import org.json.JSONObject @@ -19,6 +22,7 @@ import ru.sweetbread.unn.db.loadSchedule import ru.sweetbread.unn.db.loadUserByBitrixId import ru.sweetbread.unn.ui.layout.LoginData import ru.sweetbread.unn.ui.layout.client +import splitties.resources.appStr import java.time.Instant import java.time.LocalDate import java.time.LocalDateTime @@ -39,11 +43,11 @@ const val restURL = "$portalURL/rest" enum class Type(val s: String) { - Student("student"), + Student(appStr(R.string.student)), Group("group"), - Lecturer("lecturer"), + Lecturer(appStr(R.string.lecturer)), Auditorium("auditorium"), - Employee("employee") + Employee(appStr(R.string.employee)) } enum class LecturerRank(val id: Int) { @@ -166,6 +170,7 @@ suspend fun auth( */ private suspend fun getMyself(login: String) { // WARNING: trailing / is important, 'cuz API devs are eating shit + // TODO: make up another way to get unnId: this is not useful for lectures val studentinfo = JSONObject(client.get("$ruzapiURL/studentinfo/") { header("Cookie", "PHPSESSID=$PHPSESSID") parameter("uns", login.drop(1)) @@ -181,7 +186,7 @@ private suspend fun getMyself(login: String) { ME = User( unnId = studentinfo.getString("id").toInt(), - bitrixId = user.getInt("bitrix_id"), + bitrixId = user.getInt("bitrix_id"), // TODO: remove userId = user.getInt("id"), type = when (studentinfo.getString("type")) { "lecturer" -> Type.Lecturer // ig,,, diff --git a/app/src/main/java/ru/sweetbread/unn/ui/layout/MainActivity.kt b/app/src/main/java/ru/sweetbread/unn/ui/layout/MainActivity.kt index 7723ffc..49f3149 100644 --- a/app/src/main/java/ru/sweetbread/unn/ui/layout/MainActivity.kt +++ b/app/src/main/java/ru/sweetbread/unn/ui/layout/MainActivity.kt @@ -9,29 +9,57 @@ import android.os.Bundle import android.util.Log import androidx.activity.ComponentActivity import androidx.activity.compose.setContent +import androidx.compose.foundation.background import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.systemBarsPadding +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.AccountBox import androidx.compose.material.icons.filled.DateRange import androidx.compose.material.icons.filled.Home +import androidx.compose.material.icons.filled.Menu import androidx.compose.material3.CenterAlignedTopAppBar +import androidx.compose.material3.DrawerValue import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.ModalNavigationDrawer import androidx.compose.material3.NavigationBar import androidx.compose.material3.NavigationBarItem import androidx.compose.material3.Scaffold import androidx.compose.material3.Surface import androidx.compose.material3.Text +import androidx.compose.material3.TextButton +import androidx.compose.material3.rememberDrawerState +import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.RectangleShape +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp import androidx.core.view.WindowCompat +import androidx.navigation.NavController import androidx.navigation.compose.NavHost import androidx.navigation.compose.composable import androidx.navigation.compose.currentBackStackEntryAsState import androidx.navigation.compose.rememberNavController +import coil.compose.AsyncImage import io.ktor.client.HttpClient import io.ktor.client.engine.android.Android import io.ktor.client.plugins.HttpRequestRetry @@ -40,8 +68,11 @@ import io.ktor.client.plugins.cache.HttpCache import io.ktor.client.plugins.logging.LogLevel import io.ktor.client.plugins.logging.Logger import io.ktor.client.plugins.logging.Logging +import kotlinx.coroutines.launch +import ru.sweetbread.unn.ME import ru.sweetbread.unn.R import ru.sweetbread.unn.db.AppDatabase +import ru.sweetbread.unn.portalURL import ru.sweetbread.unn.ui.composes.Blogposts import ru.sweetbread.unn.ui.composes.Schedule import ru.sweetbread.unn.ui.theme.UNNTheme @@ -85,76 +116,106 @@ class MainActivity : ComponentActivity() { UNNTheme { Surface(modifier = Modifier.fillMaxSize(), color = MaterialTheme.colorScheme.background) { val navController = rememberNavController() - val navBackStackEntry by navController.currentBackStackEntryAsState() - val currentRoute = navBackStackEntry?.destination?.route + val drawerState = rememberDrawerState(initialValue = DrawerValue.Closed) + val scope = rememberCoroutineScope() - Scaffold( - topBar = { - CenterAlignedTopAppBar ( - title = { - Text( - when { - currentRoute?.startsWith("portal/") == true -> appStr(R.string.news) - currentRoute?.startsWith("journal/") == true -> appStr(R.string.schedule) - else -> appStr(R.string.app_name) + ModalNavigationDrawer( + drawerState = drawerState, + drawerContent = { + DrawerContent( + Modifier + .fillMaxHeight() + .fillMaxWidth(.75f) + .background(MaterialTheme.colorScheme.surfaceContainer) + .systemBarsPadding(), + navController = navController + ) + } + ) { + val navBackStackEntry by navController.currentBackStackEntryAsState() + val currentRoute = navBackStackEntry?.destination?.route + + Scaffold( + topBar = { + CenterAlignedTopAppBar( + title = { + Text( + when { + currentRoute?.startsWith("portal/") == true -> + appStr(R.string.news) + + currentRoute?.startsWith("journal/") == true -> + appStr(R.string.schedule) + + else -> appStr(R.string.app_name) + }, + Modifier.padding() + ) + }, + navigationIcon = { + IconButton( + onClick = { + scope.launch { drawerState.open() } + } + ) { + Icon(Icons.Filled.Menu, "Меню") + } + } + ) + }, + bottomBar = { + NavigationBar { + NavigationBarItem( + onClick = { + navController.navigate("portal/blogposts") { + launchSingleTop = true + } }, - Modifier.padding() + icon = { + Icon( + Icons.Filled.Home, + contentDescription = "Home" + ) + }, + selected = currentRoute?.startsWith("portal/") == true + ) + + NavigationBarItem( + onClick = { + navController.navigate("journal/schedule") { + launchSingleTop = true + } + }, + icon = { + Icon( + Icons.Filled.DateRange, + contentDescription = "Schedule" + ) + }, + selected = currentRoute?.startsWith("journal/") == true + ) + + NavigationBarItem( + onClick = { toast("Not implemented") }, + icon = { + Icon( + Icons.Filled.AccountBox, + contentDescription = "Account" + ) + }, + selected = false ) } - ) - }, - bottomBar = { - NavigationBar { - NavigationBarItem( - onClick = { - navController.navigate("portal/blogposts") { - launchSingleTop = true - } - }, - icon = { - Icon( - Icons.Filled.Home, - contentDescription = "Home" - ) - }, - selected = currentRoute?.startsWith("portal/") == true - ) - - NavigationBarItem( - onClick = { - navController.navigate("journal/schedule") { - launchSingleTop = true - } - }, - icon = { - Icon( - Icons.Filled.DateRange, - contentDescription = "Schedule" - ) - }, - selected = currentRoute?.startsWith("journal/") == true - ) - - NavigationBarItem( - onClick = { toast("Not implemented") }, - icon = { - Icon( - Icons.Filled.AccountBox, - contentDescription = "Account" - ) - }, - selected = false - ) } - } - ) {innerPadding -> - Box(Modifier.padding(innerPadding)) { - NavHost(navController, startDestination = "portal/blogposts") { - composable("portal/blogposts") { - Blogposts() - } - composable("journal/schedule") { - Schedule() + ) { innerPadding -> + Box(Modifier.padding(innerPadding)) { + NavHost(navController, startDestination = "portal/blogposts") { + composable("portal/blogposts") { + Blogposts() + } + composable("journal/schedule") { + Schedule() + } } } } @@ -163,4 +224,82 @@ class MainActivity : ComponentActivity() { } } } + + @Composable + fun DrawerContent(modifier: Modifier = Modifier, navController: NavController) { + Column(modifier = modifier.padding(0.dp)) { + Row( + Modifier + .padding(8.dp) + .fillMaxWidth() + .background(MaterialTheme.colorScheme.primaryContainer, RoundedCornerShape(16.dp)) + .padding(8.dp), + verticalAlignment = Alignment.CenterVertically + ) { + AsyncImage( + modifier = Modifier.size(64.dp).clip(CircleShape), + model = portalURL + ME.avatar.thumbnail, + contentDescription = ME.nameEn + ) + + Column (Modifier.padding(horizontal = 8.dp)) { + Text( + ME.nameRu.split(" ").dropLast(1).joinToString(" "), + fontWeight = FontWeight.Bold + ) + Text(ME.type.s) + } + } + + Spacer(modifier = Modifier.height(16.dp)) + HorizontalDivider() + + TextButton( + {}, + Modifier.fillMaxWidth(), + shape = RectangleShape + ) { + Text( + appStr(R.string.record_book), + Modifier.fillMaxWidth(), + textAlign = TextAlign.Start + ) + } + + TextButton( + {}, + Modifier.fillMaxWidth(), + shape = RectangleShape + ) { + Text( + appStr(R.string.documents), + Modifier.fillMaxWidth(), + textAlign = TextAlign.Start + ) + } + + TextButton( + {}, + Modifier.fillMaxWidth(), + shape = RectangleShape + ) { + Text( + appStr(R.string.materials), + Modifier.fillMaxWidth(), + textAlign = TextAlign.Start + ) + } + + Spacer(Modifier.weight(1f)) + + HorizontalDivider() + TextButton( + {}, + Modifier.fillMaxWidth(), + shape = RectangleShape + ) { + Text(appStr(R.string.about_app)) + } + } + } } \ 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 d266189..565b385 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -21,6 +21,10 @@ Нет данных Новости Расписание + Зачётная книга + О приложении + Документы + Материалы Пн Вт @@ -29,4 +33,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 90e98ab..6412da8 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -7,7 +7,6 @@ UNN UNN Dev UNN Beta - Email Login Password @@ -23,6 +22,12 @@ No Data News Schedule + Student + Employee + Record book + About app + Documents + Materials Mn Tu @@ -31,5 +36,4 @@ Fr Sa - \ No newline at end of file