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