feat: add side menu

This commit is contained in:
Sweetbread 2025-04-25 22:15:38 +03:00
parent 0cad16ac74
commit 654acf1b77
Signed by: Sweetbread
GPG Key ID: 17A5CB9A7DD85319
4 changed files with 227 additions and 73 deletions

View File

@ -1,3 +1,8 @@
/*
* Created by sweetbread
* Copyright (c) 2025. All rights reserved.
*/
package ru.sweetbread.unn package ru.sweetbread.unn
import android.util.Log import android.util.Log
@ -8,8 +13,6 @@ import io.ktor.client.request.parameter
import io.ktor.client.statement.bodyAsText import io.ktor.client.statement.bodyAsText
import io.ktor.http.parameters import io.ktor.http.parameters
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import org.json.JSONArray import org.json.JSONArray
import org.json.JSONObject import org.json.JSONObject
@ -19,6 +22,7 @@ import ru.sweetbread.unn.db.loadSchedule
import ru.sweetbread.unn.db.loadUserByBitrixId import ru.sweetbread.unn.db.loadUserByBitrixId
import ru.sweetbread.unn.ui.layout.LoginData import ru.sweetbread.unn.ui.layout.LoginData
import ru.sweetbread.unn.ui.layout.client import ru.sweetbread.unn.ui.layout.client
import splitties.resources.appStr
import java.time.Instant import java.time.Instant
import java.time.LocalDate import java.time.LocalDate
import java.time.LocalDateTime import java.time.LocalDateTime
@ -39,11 +43,11 @@ const val restURL = "$portalURL/rest"
enum class Type(val s: String) { enum class Type(val s: String) {
Student("student"), Student(appStr(R.string.student)),
Group("group"), Group("group"),
Lecturer("lecturer"), Lecturer(appStr(R.string.lecturer)),
Auditorium("auditorium"), Auditorium("auditorium"),
Employee("employee") Employee(appStr(R.string.employee))
} }
enum class LecturerRank(val id: Int) { enum class LecturerRank(val id: Int) {
@ -166,6 +170,7 @@ suspend fun auth(
*/ */
private suspend fun getMyself(login: String) { private suspend fun getMyself(login: String) {
// WARNING: trailing / is important, 'cuz API devs are eating shit // 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/") { val studentinfo = JSONObject(client.get("$ruzapiURL/studentinfo/") {
header("Cookie", "PHPSESSID=$PHPSESSID") header("Cookie", "PHPSESSID=$PHPSESSID")
parameter("uns", login.drop(1)) parameter("uns", login.drop(1))
@ -181,7 +186,7 @@ private suspend fun getMyself(login: String) {
ME = User( ME = User(
unnId = studentinfo.getString("id").toInt(), unnId = studentinfo.getString("id").toInt(),
bitrixId = user.getInt("bitrix_id"), bitrixId = user.getInt("bitrix_id"), // TODO: remove
userId = user.getInt("id"), userId = user.getInt("id"),
type = when (studentinfo.getString("type")) { type = when (studentinfo.getString("type")) {
"lecturer" -> Type.Lecturer // ig,,, "lecturer" -> Type.Lecturer // ig,,,

View File

@ -9,29 +9,57 @@ import android.os.Bundle
import android.util.Log import android.util.Log
import androidx.activity.ComponentActivity import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent import androidx.activity.compose.setContent
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box 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.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding 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.Icons
import androidx.compose.material.icons.filled.AccountBox import androidx.compose.material.icons.filled.AccountBox
import androidx.compose.material.icons.filled.DateRange import androidx.compose.material.icons.filled.DateRange
import androidx.compose.material.icons.filled.Home import androidx.compose.material.icons.filled.Home
import androidx.compose.material.icons.filled.Menu
import androidx.compose.material3.CenterAlignedTopAppBar import androidx.compose.material3.CenterAlignedTopAppBar
import androidx.compose.material3.DrawerValue
import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.ModalNavigationDrawer
import androidx.compose.material3.NavigationBar import androidx.compose.material3.NavigationBar
import androidx.compose.material3.NavigationBarItem import androidx.compose.material3.NavigationBarItem
import androidx.compose.material3.Scaffold import androidx.compose.material3.Scaffold
import androidx.compose.material3.Surface import androidx.compose.material3.Surface
import androidx.compose.material3.Text 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.getValue
import androidx.compose.runtime.rememberCoroutineScope
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.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.core.view.WindowCompat
import androidx.navigation.NavController
import androidx.navigation.compose.NavHost import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable import androidx.navigation.compose.composable
import androidx.navigation.compose.currentBackStackEntryAsState import androidx.navigation.compose.currentBackStackEntryAsState
import androidx.navigation.compose.rememberNavController import androidx.navigation.compose.rememberNavController
import coil.compose.AsyncImage
import io.ktor.client.HttpClient import io.ktor.client.HttpClient
import io.ktor.client.engine.android.Android import io.ktor.client.engine.android.Android
import io.ktor.client.plugins.HttpRequestRetry 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.LogLevel
import io.ktor.client.plugins.logging.Logger import io.ktor.client.plugins.logging.Logger
import io.ktor.client.plugins.logging.Logging 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.R
import ru.sweetbread.unn.db.AppDatabase import ru.sweetbread.unn.db.AppDatabase
import ru.sweetbread.unn.portalURL
import ru.sweetbread.unn.ui.composes.Blogposts import ru.sweetbread.unn.ui.composes.Blogposts
import ru.sweetbread.unn.ui.composes.Schedule import ru.sweetbread.unn.ui.composes.Schedule
import ru.sweetbread.unn.ui.theme.UNNTheme import ru.sweetbread.unn.ui.theme.UNNTheme
@ -85,21 +116,50 @@ class MainActivity : ComponentActivity() {
UNNTheme { UNNTheme {
Surface(modifier = Modifier.fillMaxSize(), color = MaterialTheme.colorScheme.background) { Surface(modifier = Modifier.fillMaxSize(), color = MaterialTheme.colorScheme.background) {
val navController = rememberNavController() val navController = rememberNavController()
val drawerState = rememberDrawerState(initialValue = DrawerValue.Closed)
val scope = rememberCoroutineScope()
ModalNavigationDrawer(
drawerState = drawerState,
drawerContent = {
DrawerContent(
Modifier
.fillMaxHeight()
.fillMaxWidth(.75f)
.background(MaterialTheme.colorScheme.surfaceContainer)
.systemBarsPadding(),
navController = navController
)
}
) {
val navBackStackEntry by navController.currentBackStackEntryAsState() val navBackStackEntry by navController.currentBackStackEntryAsState()
val currentRoute = navBackStackEntry?.destination?.route val currentRoute = navBackStackEntry?.destination?.route
Scaffold( Scaffold(
topBar = { topBar = {
CenterAlignedTopAppBar ( CenterAlignedTopAppBar(
title = { title = {
Text( Text(
when { when {
currentRoute?.startsWith("portal/") == true -> appStr(R.string.news) currentRoute?.startsWith("portal/") == true ->
currentRoute?.startsWith("journal/") == true -> appStr(R.string.schedule) appStr(R.string.news)
currentRoute?.startsWith("journal/") == true ->
appStr(R.string.schedule)
else -> appStr(R.string.app_name) else -> appStr(R.string.app_name)
}, },
Modifier.padding() Modifier.padding()
) )
},
navigationIcon = {
IconButton(
onClick = {
scope.launch { drawerState.open() }
}
) {
Icon(Icons.Filled.Menu, "Меню")
}
} }
) )
}, },
@ -147,7 +207,7 @@ class MainActivity : ComponentActivity() {
) )
} }
} }
) {innerPadding -> ) { innerPadding ->
Box(Modifier.padding(innerPadding)) { Box(Modifier.padding(innerPadding)) {
NavHost(navController, startDestination = "portal/blogposts") { NavHost(navController, startDestination = "portal/blogposts") {
composable("portal/blogposts") { composable("portal/blogposts") {
@ -163,4 +223,83 @@ 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))
}
}
}
} }

View File

@ -21,6 +21,10 @@
<string name="noData">Нет данных</string> <string name="noData">Нет данных</string>
<string name="news">Новости</string> <string name="news">Новости</string>
<string name="schedule">Расписание</string> <string name="schedule">Расписание</string>
<string name="record_book">Зачётная книга</string>
<string name="about_app">О приложении</string>
<string name="documents">Документы</string>
<string name="materials">Материалы</string>
<string-array name="short_weekdays"> <string-array name="short_weekdays">
<item>Пн</item> <item>Пн</item>
<item>Вт</item> <item>Вт</item>
@ -29,4 +33,6 @@
<item>Пт</item> <item>Пт</item>
<item>Сб</item> <item>Сб</item>
</string-array> </string-array>
<string name="student">Студент</string>
<string name="employee">Сотрудник</string>
</resources> </resources>

View File

@ -7,7 +7,6 @@
<string name="app_name_reg">UNN</string> <string name="app_name_reg">UNN</string>
<string name="app_name_dev">UNN Dev</string> <string name="app_name_dev">UNN Dev</string>
<string name="app_name_beta">UNN Beta</string> <string name="app_name_beta">UNN Beta</string>
<!-- <string name="title_activity_login">LoginActivity</string>-->
<string name="prompt_email" translatable="false">Email</string> <string name="prompt_email" translatable="false">Email</string>
<string name="prompt_login">Login</string> <string name="prompt_login">Login</string>
<string name="prompt_password">Password</string> <string name="prompt_password">Password</string>
@ -23,6 +22,12 @@
<string name="noData">No Data</string> <string name="noData">No Data</string>
<string name="news">News</string> <string name="news">News</string>
<string name="schedule">Schedule</string> <string name="schedule">Schedule</string>
<string name="student">Student</string>
<string name="employee">Employee</string>
<string name="record_book">Record book</string>
<string name="about_app">About app</string>
<string name="documents">Documents</string>
<string name="materials">Materials</string>
<string-array name="short_weekdays"> <string-array name="short_weekdays">
<item>Mn</item> <item>Mn</item>
<item>Tu</item> <item>Tu</item>
@ -31,5 +36,4 @@
<item>Fr</item> <item>Fr</item>
<item>Sa</item> <item>Sa</item>
</string-array> </string-array>
<!-- <string name="login_failed">"Login failed"</string>-->
</resources> </resources>