ref: removing api calls to separate functions

This commit is contained in:
Sweetbread 2025-04-26 02:17:20 +03:00
parent 654acf1b77
commit 1286860d55
Signed by: Sweetbread
GPG Key ID: 17A5CB9A7DD85319
10 changed files with 352 additions and 316 deletions

View File

@ -0,0 +1,99 @@
/*
* Created by sweetbread
* Copyright (c) 2025. All rights reserved.
*/
package ru.sweetbread.unn.api
import android.util.Log
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.withContext
import ru.sweetbread.unn.db.cacheSchedule
import ru.sweetbread.unn.db.loadSchedule
import ru.sweetbread.unn.ui.layout.LoginData
import java.time.LocalDate
lateinit var PHPSESSID: String
lateinit var CSRF: String
lateinit var ME: User
/**
* Authorize user by [login] and [password]
*
* Also defines local vars [PHPSESSID] and [ME]
*/
suspend fun auth(
login: String = LoginData.login,
password: String = LoginData.password,
forced: Boolean = false
): Boolean {
if (!forced) {
if (::PHPSESSID.isInitialized and ::ME.isInitialized)
return true
}
val id = getToken(login, password)
if (id != null) {
PHPSESSID = id
getMyself(login)
getCSRF()
return true
}
return false
}
/**
* Save info about current [User] in memory
*/
private suspend fun getMyself(login: String) = coroutineScope {
val idDeferred = async { getId(login) }
val userDeferred = async { getUser() }
val id = idDeferred.await()
val user = userDeferred.await()
ME = User(
unnId = id,
userId = user.userId,
type = Type.student,
email = user.email,
nameRu = user.nameRu,
nameEn = user.nameEn,
isMale = user.isMale,
birthday = user.birthday,
avatar = user.avatar
)
}
suspend fun getScheduleDay(
type: Type = ME.type,
id: Int = ME.unnId!!,
date: LocalDate
): ArrayList<ScheduleUnit> {
if ((type == ME.type) and (id == ME.unnId!!)) {
val schedule = withContext(Dispatchers.IO) { loadSchedule(date) }
Log.d("Schedule", schedule.joinToString())
if (schedule.isNotEmpty())
return schedule
}
return getSchedule(type, id, date, date)
}
suspend fun getSchedule(
type: Type = ME.type,
id: Int = ME.unnId!!,
start: LocalDate,
finish: LocalDate
): ArrayList<ScheduleUnit> {
val schedule = downloadSchedule(type, id, start, finish)
if ((type == ME.type) and (id == ME.unnId!!))
cacheSchedule(schedule)
return schedule
}

View File

@ -3,9 +3,17 @@
* Copyright (c) 2025. All rights reserved.
*/
package ru.sweetbread.unn
package ru.sweetbread.unn.api
import android.util.Log
import io.ktor.client.HttpClient
import io.ktor.client.engine.android.Android
import io.ktor.client.plugins.HttpRequestRetry
import io.ktor.client.plugins.HttpTimeout
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 io.ktor.client.request.forms.submitForm
import io.ktor.client.request.get
import io.ktor.client.request.header
@ -16,13 +24,8 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import org.json.JSONArray
import org.json.JSONObject
import ru.sweetbread.unn.db.cacheSchedule
import ru.sweetbread.unn.db.cacheUser
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
@ -31,10 +34,6 @@ import java.time.ZoneId
import java.time.format.DateTimeFormatter
private lateinit var PHPSESSID: String
private lateinit var CSRF: String
lateinit var ME: User
const val portalURL = "https://portal.unn.ru"
const val ruzapiURL = "$portalURL/ruzapi"
const val vuzapiURL = "$portalURL/bitrix/vuz/api"
@ -42,110 +41,29 @@ const val prtl2URL = "$portalURL/portal2/api"
const val restURL = "$portalURL/rest"
enum class Type(val s: String) {
Student(appStr(R.string.student)),
Group("group"),
Lecturer(appStr(R.string.lecturer)),
Auditorium("auditorium"),
Employee(appStr(R.string.employee))
}
enum class LecturerRank(val id: Int) {
Assistant(R.string.assistant),
Lecturer(R.string.lecturer),
SLecturer(R.string.slecturer),
AProfessor(R.string.aprofessor)
}
class ScheduleUnit(
val oid: Int,
val auditorium: Auditorium,
val date: LocalDate,
val discipline: Discipline,
val kindOfWork: KindOfWork,
val lecturers: ArrayList<Lecturer>,
val stream: String,
val begin: LocalTime,
val end: LocalTime
)
class Auditorium(
val name: String,
val oid: Int,
val floor: Int,
val building: Building
)
class Building(
val name: String,
val gid: Int,
val oid: Int
)
class Discipline(
val name: String,
val oid: Int,
val type: Int
)
class KindOfWork(
val name: String,
val oid: Int,
val uid: String,
val complexity: Int
)
class Lecturer(
val name: String,
val rank: LecturerRank,
val email: String,
val unnId: Int,
val uid: String
)
class User(
val unnId: Int?,
val bitrixId: Int,
val userId: Int,
val type: Type,
val email: String,
val nameRu: String,
val nameEn: String,
val isMale: Boolean,
val birthday: LocalDate,
val avatar: AvatarSet
)
class Post(
val id: Int,
val authorId: Int,
val enableComments: Boolean,
val numComments: Int,
val date: LocalDateTime,
val content: String
)
class AvatarSet(
val original: String,
val thumbnail: String,
val small: String
)
/**
* Authorize user by [login] and [password]
*
* Also defines local vars [PHPSESSID] and [ME]
*/
suspend fun auth(
login: String = LoginData.login,
password: String = LoginData.password,
forced: Boolean = false
): Boolean {
if (!forced) {
if (::PHPSESSID.isInitialized and ::ME.isInitialized)
return true
val client = HttpClient(Android) {
install(HttpCache)
install(Logging) {
logger = object : Logger {
override fun log(message: String) {
Log.i("Ktor", message)
}
}
level = LogLevel.ALL
}
install(HttpTimeout) {
connectTimeoutMillis = 5000
}
install(HttpRequestRetry) {
retryOnException(maxRetries = 3, retryOnTimeout = true)
exponentialDelay()
modifyRequest { request ->
request.headers.append("x-retry-count", retryCount.toString())
}
}
}
suspend fun getToken(login: String, password: String): String? {
val r = client.submitForm("$portalURL/auth/?login=yes",
formParameters = parameters {
append("AUTH_FORM", "Y")
@ -155,52 +73,43 @@ suspend fun auth(
append("USER_PASSWORD", password)
}
)
if (r.status.value == 302) {
PHPSESSID =
"""PHPSESSID=([\w\d]+)""".toRegex().find(r.headers["Set-Cookie"]!!)!!.groupValues[1]
getMyself(login)
getCSRF()
return true
}
return false
if (r.status.value == 302)
return """PHPSESSID=([\w\d]+)""".toRegex().find(r.headers["Set-Cookie"]!!)!!.groupValues[1]
return null
}
/**
* Save info about current [User] in memory
*/
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/") {
suspend fun getId(login: String): Int {
return JSONObject(client.get("$ruzapiURL/studentinfo/") {
header("Cookie", "PHPSESSID=$PHPSESSID")
parameter("uns", login.drop(1))
}.bodyAsText())
}.bodyAsText()).getString("id").toInt()
}
val user = JSONObject(
client.get("$vuzapiURL/user") {
suspend fun getUser(userId: Int? = null): User {
// 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 json = JSONObject(
client.get("$vuzapiURL/user/${userId ?: ""}") {
header("Cookie", "PHPSESSID=$PHPSESSID")
}.bodyAsText()
)
Log.d("studentInfo", studentinfo.toString(2))
ME = User(
unnId = studentinfo.getString("id").toInt(),
bitrixId = user.getInt("bitrix_id"), // TODO: remove
userId = user.getInt("id"),
type = when (studentinfo.getString("type")) {
"lecturer" -> Type.Lecturer // ig,,,
else -> Type.Student
},
email = user.getString("email"),
nameRu = user.getString("fullname"),
nameEn = user.getString("fullname_en"),
isMale = user.getString("sex") == "M",
return User(
unnId = null,
userId = json.getInt("id"),
type = if (json.getJSONArray("profiles").getJSONObject(0)
.getString("type") == "employee"
) Type.employee else Type.student,
email = json.getString("email"),
nameRu = json.getString("fullname"),
nameEn = json.getString("fullname_en"),
isMale = json.getString("sex") == "M",
birthday = Instant
.parse(user.getString("birthdate"))
.parse(json.getString("birthdate"))
.atZone(ZoneId.of("Europe/Moscow"))
.toLocalDate(),
avatar = user.getJSONObject("photo").let {
avatar = json.getJSONObject("photo").let {
AvatarSet(
it.getString("orig"),
it.getString("thumbnail"),
@ -210,23 +119,7 @@ private suspend fun getMyself(login: String) {
)
}
suspend fun getScheduleDay(
type: Type = ME.type,
id: Int = ME.unnId!!,
date: LocalDate
): ArrayList<ScheduleUnit> {
if ((type == ME.type) and (id == ME.unnId!!)) {
val schedule = withContext(Dispatchers.IO) { loadSchedule(date) }
Log.d("Schedule", schedule.joinToString())
if (schedule.isNotEmpty())
return schedule
}
return getSchedule(type, id, date, date)
}
suspend fun getSchedule(
suspend fun downloadSchedule(
type: Type = ME.type,
id: Int = ME.unnId!!,
start: LocalDate,
@ -234,13 +127,13 @@ suspend fun getSchedule(
): ArrayList<ScheduleUnit> {
val unnDatePattern = DateTimeFormatter.ofPattern("yyyy-MM-dd")
val r = client.get("$ruzapiURL/schedule/${type.s}/$id") {
val r = client.get("$ruzapiURL/schedule/${type.name}/$id") {
parameter("start", start.format(unnDatePattern))
parameter("finish", finish.format(unnDatePattern))
parameter("lng", "1")
}
val json = JSONArray(r.bodyAsText())
val json = JSONArray(r.bodyAsText())
val out = arrayListOf<ScheduleUnit>()
for (i in 0 until json.length()) {
val unit = json.getJSONObject(i)
@ -304,9 +197,6 @@ suspend fun getSchedule(
)
}
if ((type == ME.type) and (id == ME.unnId!!)) {
cacheSchedule(out)
}
return out
}
@ -318,6 +208,23 @@ suspend fun getCSRF() {
CSRF = JSONObject(r.bodyAsText()).getString("sessid")
}
suspend fun getUserByBitrixId(id: Int): User {
withContext(Dispatchers.IO) {
loadUserByBitrixId(id)
}?.let { return it }
val userId = JSONObject(client.get("$vuzapiURL/user/bx/$id") {
header("Cookie", "PHPSESSID=$PHPSESSID")
}.bodyAsText()).getInt("id")
getUser(userId).let { user ->
withContext(Dispatchers.IO) {
cacheUser(user)
}
return user
}
}
suspend fun getBlogposts(): ArrayList<Post> {
val r = client.get("$prtl2URL/news.php") {
header("Cookie", "PHPSESSID=$PHPSESSID")
@ -344,54 +251,3 @@ suspend fun getBlogposts(): ArrayList<Post> {
}
return out
}
suspend fun getUserByBitrixId(id: Int): User {
withContext(Dispatchers.IO) {
loadUserByBitrixId(id)
}?.let { return it }
val userId = JSONObject(client.get("$vuzapiURL/user/bx/$id") {
header("Cookie", "PHPSESSID=$PHPSESSID")
}.bodyAsText()).getInt("id")
getUser(userId).let { user ->
withContext(Dispatchers.IO) {
cacheUser(user)
}
return user
}
}
suspend fun getUser(id: Int): User {
val json = JSONObject(
client.get("$vuzapiURL/user/$id") {
header("Cookie", "PHPSESSID=$PHPSESSID")
}.bodyAsText()
)
Log.d("type", json.getJSONArray("profiles").getJSONObject(0).getString("type"))
return User(
unnId = null,
bitrixId = json.getInt("bitrix_id"),
userId = json.getInt("id"),
type = if (json.getJSONArray("profiles").getJSONObject(0)
.getString("type") == "employee"
) Type.Employee else Type.Student,
email = json.getString("email"),
nameRu = json.getString("fullname"),
nameEn = json.getString("fullname_en"),
isMale = json.getString("sex") == "M",
birthday = Instant
.parse(json.getString("birthdate"))
.atZone(ZoneId.of("Europe/Moscow"))
.toLocalDate(),
avatar = json.getJSONObject("photo").let {
AvatarSet(
it.getString("orig"),
it.getString("thumbnail"),
it.getString("small"),
)
}
)
}

View File

@ -0,0 +1,100 @@
/*
* Created by sweetbread
* Copyright (c) 2025. All rights reserved.
*/
package ru.sweetbread.unn.api
import ru.sweetbread.unn.R
import splitties.resources.appStr
import java.time.LocalDate
import java.time.LocalDateTime
import java.time.LocalTime
enum class Type(val s: String) {
student(appStr(R.string.student)),
group("group"),
lecturer(appStr(R.string.lecturer)),
auditorium("auditorium"),
employee(appStr(R.string.employee))
}
enum class LecturerRank(val id: Int) {
Assistant(R.string.assistant),
Lecturer(R.string.lecturer),
SLecturer(R.string.slecturer),
AProfessor(R.string.aprofessor)
}
class ScheduleUnit(
val oid: Int,
val auditorium: Auditorium,
val date: LocalDate,
val discipline: Discipline,
val kindOfWork: KindOfWork,
val lecturers: ArrayList<Lecturer>,
val stream: String,
val begin: LocalTime,
val end: LocalTime
)
class Auditorium(
val name: String,
val oid: Int,
val floor: Int,
val building: Building
)
class Building(
val name: String,
val gid: Int,
val oid: Int
)
class Discipline(
val name: String,
val oid: Int,
val type: Int
)
class KindOfWork(
val name: String,
val oid: Int,
val uid: String,
val complexity: Int
)
class Lecturer(
val name: String,
val rank: LecturerRank,
val email: String,
val unnId: Int,
val uid: String
)
class User(
val unnId: Int?,
val userId: Int,
val type: Type,
val email: String,
val nameRu: String,
val nameEn: String,
val isMale: Boolean,
val birthday: LocalDate,
val avatar: AvatarSet
)
class Post(
val id: Int,
val authorId: Int,
val enableComments: Boolean,
val numComments: Int,
val date: LocalDateTime,
val content: String
)
class AvatarSet(
val original: String,
val thumbnail: String,
val small: String
)

View File

@ -1,7 +1,17 @@
/*
* Created by sweetbread
* Copyright (c) 2025. All rights reserved.
*/
package ru.sweetbread.unn.db
import androidx.room.Database
import androidx.room.RoomDatabase
import splitties.arch.room.roomDb
val cacheDb = roomDb<AppDatabase>(name = "cache") {
fallbackToDestructiveMigration(dropAllTables = true)
}
@Database(entities = [
UserDB::class,

View File

@ -1,3 +1,8 @@
/*
* Created by sweetbread
* Copyright (c) 2025. All rights reserved.
*/
package ru.sweetbread.unn.db
import android.util.Log
@ -9,14 +14,13 @@ import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.PrimaryKey
import androidx.room.Query
import ru.sweetbread.unn.Auditorium
import ru.sweetbread.unn.Building
import ru.sweetbread.unn.Discipline
import ru.sweetbread.unn.KindOfWork
import ru.sweetbread.unn.Lecturer
import ru.sweetbread.unn.LecturerRank
import ru.sweetbread.unn.ScheduleUnit
import ru.sweetbread.unn.ui.layout.db
import ru.sweetbread.unn.api.Auditorium
import ru.sweetbread.unn.api.Building
import ru.sweetbread.unn.api.Discipline
import ru.sweetbread.unn.api.KindOfWork
import ru.sweetbread.unn.api.Lecturer
import ru.sweetbread.unn.api.LecturerRank
import ru.sweetbread.unn.api.ScheduleUnit
import java.time.LocalDate
import java.time.LocalDateTime
import java.time.LocalTime
@ -43,7 +47,7 @@ interface BuildingDao {
}
fun cacheBuilding(building: Building) {
db.buildingDao().insert(
cacheDb.buildingDao().insert(
BuildingDB(
building.oid,
building.name,
@ -54,13 +58,13 @@ fun cacheBuilding(building: Building) {
}
fun loadBuilding(oid: Int): Building? {
return db.buildingDao().get(oid)?.let {
return cacheDb.buildingDao().get(oid)?.let {
if (LocalDateTime.parse(
it.expiredAt,
DateTimeFormatter.ISO_DATE_TIME
) > LocalDateTime.now()
) {
db.buildingDao().delete(it)
cacheDb.buildingDao().delete(it)
return null
}
Building(
@ -94,7 +98,7 @@ interface AuditoriumDao {
fun cacheAuditorium(auditorium: Auditorium) {
cacheBuilding(auditorium.building)
db.auditoriumDao().insert(
cacheDb.auditoriumDao().insert(
AuditoriumDB(
auditorium.oid,
auditorium.name,
@ -106,13 +110,13 @@ fun cacheAuditorium(auditorium: Auditorium) {
}
fun loadAuditorium(oid: Int): Auditorium? {
return db.auditoriumDao().get(oid)?.let {
return cacheDb.auditoriumDao().get(oid)?.let {
if (LocalDateTime.parse(
it.expiredAt,
DateTimeFormatter.ISO_DATE_TIME
) > LocalDateTime.now()
) {
db.auditoriumDao().delete(it)
cacheDb.auditoriumDao().delete(it)
return null
}
val building = loadBuilding(it.buildingOid) ?: return null
@ -146,7 +150,7 @@ interface DisciplineDao {
}
fun cacheDiscipline(discipline: Discipline) {
db.disciplineDao().insert(
cacheDb.disciplineDao().insert(
DisciplineDB(
discipline.oid,
discipline.name,
@ -157,13 +161,13 @@ fun cacheDiscipline(discipline: Discipline) {
}
fun loadDiscipline(oid: Int): Discipline? {
return db.disciplineDao().get(oid)?.let {
return cacheDb.disciplineDao().get(oid)?.let {
if (LocalDateTime.parse(
it.expiredAt,
DateTimeFormatter.ISO_DATE_TIME
) > LocalDateTime.now()
) {
db.disciplineDao().delete(it)
cacheDb.disciplineDao().delete(it)
return null
}
@ -197,7 +201,7 @@ interface KindOfWorkDao {
}
fun cacheKindOfWork(kindOfWork: KindOfWork) {
db.kindOfWorkDao().insert(
cacheDb.kindOfWorkDao().insert(
KindOfWorkDB(
kindOfWork.oid,
kindOfWork.name,
@ -209,13 +213,13 @@ fun cacheKindOfWork(kindOfWork: KindOfWork) {
}
fun loadKindOfWork(oid: Int): KindOfWork? {
return db.kindOfWorkDao().get(oid)?.let {
return cacheDb.kindOfWorkDao().get(oid)?.let {
if (LocalDateTime.parse(
it.expiredAt,
DateTimeFormatter.ISO_DATE_TIME
) > LocalDateTime.now()
) {
db.kindOfWorkDao().delete(it)
cacheDb.kindOfWorkDao().delete(it)
return null
}
@ -251,7 +255,7 @@ interface LecturerDao {
}
fun cacheLecturer(lecturer: Lecturer) {
db.lecturerDao().insert(
cacheDb.lecturerDao().insert(
LecturerDB(
lecturer.unnId,
lecturer.name,
@ -264,13 +268,13 @@ fun cacheLecturer(lecturer: Lecturer) {
}
fun loadLecturer(unnId: Int): Lecturer? {
return db.lecturerDao().get(unnId)?.let {
return cacheDb.lecturerDao().get(unnId)?.let {
if (LocalDateTime.parse(
it.expiredAt,
DateTimeFormatter.ISO_DATE_TIME
) > LocalDateTime.now()
) {
db.lecturerDao().delete(it)
cacheDb.lecturerDao().delete(it)
return null
}
@ -320,7 +324,7 @@ fun cacheSchedule(item: ScheduleUnit) {
cacheKindOfWork(item.kindOfWork)
cacheLecturer(item.lecturers[0])
db.scheduleDao().insert(
cacheDb.scheduleDao().insert(
ScheduleUnitDB(
item.oid,
item.date.format(DateTimeFormatter.ISO_DATE),
@ -347,7 +351,7 @@ fun cacheSchedule(items: ArrayList<ScheduleUnit>) {
}
fun loadSchedule(oid: Int): ScheduleUnit? {
db.scheduleDao().getSchedule(oid)?.let {
cacheDb.scheduleDao().getSchedule(oid)?.let {
Log.d("load", it.oid.toString())
if (LocalDateTime.parse(
it.expiredAt,
@ -355,7 +359,7 @@ fun loadSchedule(oid: Int): ScheduleUnit? {
) < LocalDateTime.now()
) {
Log.d("delete", it.oid.toString())
db.scheduleDao().delete(it)
cacheDb.scheduleDao().delete(it)
return null
}
@ -375,10 +379,10 @@ fun loadSchedule(oid: Int): ScheduleUnit? {
}
fun loadSchedule(date: LocalDate): ArrayList<ScheduleUnit> {
db.scheduleDao().getSchedule(date.format(DateTimeFormatter.ISO_DATE))
cacheDb.scheduleDao().getSchedule(date.format(DateTimeFormatter.ISO_DATE))
.map { Log.d("meow", "${it.oid}: ${loadSchedule(it.oid)}") }
return ArrayList(
db.scheduleDao().getSchedule(date.format(DateTimeFormatter.ISO_DATE))
cacheDb.scheduleDao().getSchedule(date.format(DateTimeFormatter.ISO_DATE))
.mapNotNull { loadSchedule(it.oid) }
)
}

View File

@ -1,3 +1,8 @@
/*
* Created by sweetbread
* Copyright (c) 2025. All rights reserved.
*/
package ru.sweetbread.unn.db
import android.util.Log
@ -8,10 +13,9 @@ import androidx.room.Entity
import androidx.room.Insert
import androidx.room.PrimaryKey
import androidx.room.Query
import ru.sweetbread.unn.AvatarSet
import ru.sweetbread.unn.Type
import ru.sweetbread.unn.User
import ru.sweetbread.unn.ui.layout.db
import ru.sweetbread.unn.api.AvatarSet
import ru.sweetbread.unn.api.Type
import ru.sweetbread.unn.api.User
import java.time.LocalDate
import java.time.LocalDateTime
import java.time.format.DateTimeFormatter
@ -20,7 +24,6 @@ import java.time.format.DateTimeFormatter
data class UserDB(
@PrimaryKey val userId: Int,
@ColumnInfo val unnId: Int?,
@ColumnInfo val bitrixId: Int,
@ColumnInfo val type: Type,
@ColumnInfo val email: String,
@ColumnInfo val nameRu: String,
@ -35,8 +38,8 @@ data class UserDB(
@Dao
interface UserDao {
@Query("SELECT * FROM userDB WHERE bitrixId = :bitrixId LIMIT 1")
fun getUserByBitrix(bitrixId: Int): UserDB?
@Query("SELECT * FROM userDB WHERE userId = :userId LIMIT 1")
fun getUserById(userId: Int): UserDB?
@Insert
fun insert(user: UserDB)
@ -48,11 +51,10 @@ interface UserDao {
fun cacheUser(user: User) {
try {
db.userDao().insert(
cacheDb.userDao().insert(
UserDB(
user.userId,
user.unnId,
user.bitrixId,
user.type,
user.email,
user.nameRu,
@ -70,7 +72,7 @@ fun cacheUser(user: User) {
}
fun loadUserByBitrixId(bitrixId: Int): User? {
val user = db.userDao().getUserByBitrix(bitrixId)
val user = cacheDb.userDao().getUserById(bitrixId)
Log.d("UserDB", user?.nameEn ?: "None")
if (user == null) return null
if (LocalDateTime.parse(
@ -78,12 +80,11 @@ fun loadUserByBitrixId(bitrixId: Int): User? {
DateTimeFormatter.ISO_LOCAL_DATE_TIME
) < LocalDateTime.now()
) {
db.userDao().delete(user)
cacheDb.userDao().delete(user)
return null
} else {
return User(
user.unnId,
user.bitrixId,
user.userId,
user.type,
user.email,

View File

@ -55,14 +55,14 @@ import coil.request.ImageRequest
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import ru.sweetbread.unn.AvatarSet
import ru.sweetbread.unn.Post
import ru.sweetbread.unn.R
import ru.sweetbread.unn.Type
import ru.sweetbread.unn.User
import ru.sweetbread.unn.getBlogposts
import ru.sweetbread.unn.getUserByBitrixId
import ru.sweetbread.unn.portalURL
import ru.sweetbread.unn.api.AvatarSet
import ru.sweetbread.unn.api.Post
import ru.sweetbread.unn.api.Type
import ru.sweetbread.unn.api.User
import ru.sweetbread.unn.api.getBlogposts
import ru.sweetbread.unn.api.getUserByBitrixId
import ru.sweetbread.unn.api.portalURL
import ru.sweetbread.unn.ui.theme.UNNTheme
import java.time.LocalDate
import java.time.LocalDateTime
@ -73,8 +73,7 @@ import java.time.format.FormatStyle
val defUser = User(
null,
123,
123,
Type.Student,
Type.student,
"cool.email@domain.com",
"Джон Сигма Омегович",
"Jon Sigma Omega",
@ -241,7 +240,9 @@ fun UserItemPreview() {
Modifier
.width(300.dp)
.clip(RoundedCornerShape(8.dp))
.background(MaterialTheme.colorScheme.primaryContainer), defUser, Type.Student.s
.background(MaterialTheme.colorScheme.primaryContainer),
defUser,
Type.student.s
)
}
}

View File

@ -55,15 +55,15 @@ import com.kizitonwose.calendar.core.WeekDay
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import ru.sweetbread.unn.Auditorium
import ru.sweetbread.unn.Building
import ru.sweetbread.unn.Discipline
import ru.sweetbread.unn.KindOfWork
import ru.sweetbread.unn.Lecturer
import ru.sweetbread.unn.LecturerRank
import ru.sweetbread.unn.R
import ru.sweetbread.unn.ScheduleUnit
import ru.sweetbread.unn.getScheduleDay
import ru.sweetbread.unn.api.Auditorium
import ru.sweetbread.unn.api.Building
import ru.sweetbread.unn.api.Discipline
import ru.sweetbread.unn.api.KindOfWork
import ru.sweetbread.unn.api.Lecturer
import ru.sweetbread.unn.api.LecturerRank
import ru.sweetbread.unn.api.ScheduleUnit
import ru.sweetbread.unn.api.getScheduleDay
import ru.sweetbread.unn.ui.theme.UNNTheme
import splitties.resources.appStr
import splitties.resources.appStrArray

View File

@ -44,7 +44,7 @@ import androidx.compose.ui.unit.dp
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import ru.sweetbread.unn.R
import ru.sweetbread.unn.auth
import ru.sweetbread.unn.api.auth
import ru.sweetbread.unn.ui.theme.UNNTheme
import splitties.activities.start
import splitties.preferences.Preferences

View File

@ -6,7 +6,6 @@
package ru.sweetbread.unn.ui.layout
import android.os.Bundle
import android.util.Log
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.background
@ -60,50 +59,16 @@ 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
import io.ktor.client.plugins.HttpTimeout
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.api.ME
import ru.sweetbread.unn.api.portalURL
import ru.sweetbread.unn.ui.composes.Blogposts
import ru.sweetbread.unn.ui.composes.Schedule
import ru.sweetbread.unn.ui.theme.UNNTheme
import splitties.arch.room.roomDb
import splitties.resources.appStr
import splitties.toast.toast
val client = HttpClient(Android) {
install(HttpCache)
install(Logging) {
logger = object : Logger {
override fun log(message: String) {
Log.i("Ktor", message)
}
}
level = LogLevel.ALL
}
install(HttpTimeout) {
connectTimeoutMillis = 5000
}
install(HttpRequestRetry) {
retryOnException(maxRetries = 3, retryOnTimeout = true)
exponentialDelay()
modifyRequest { request ->
request.headers.append("x-retry-count", retryCount.toString())
}
}
}
val db = roomDb<AppDatabase>(name = "database")
class MainActivity : ComponentActivity() {
@OptIn(ExperimentalMaterial3Api::class)