From 1286860d55c0cbc0123ec4d9552749c1d2a754c6 Mon Sep 17 00:00:00 2001 From: Sweetbread Date: Sat, 26 Apr 2025 02:17:20 +0300 Subject: [PATCH] ref: removing api calls to separate functions --- .../main/java/ru/sweetbread/unn/api/API.kt | 99 ++++++ .../unn/{API.kt => api/endpoints.kt} | 296 +++++------------- .../main/java/ru/sweetbread/unn/api/models.kt | 100 ++++++ app/src/main/java/ru/sweetbread/unn/db/DB.kt | 10 + .../java/ru/sweetbread/unn/db/Schedule.kt | 60 ++-- .../main/java/ru/sweetbread/unn/db/UserDB.kt | 25 +- .../ru/sweetbread/unn/ui/composes/Blogpost.kt | 21 +- .../ru/sweetbread/unn/ui/composes/Schedule.kt | 16 +- .../sweetbread/unn/ui/layout/LoginActivity.kt | 2 +- .../sweetbread/unn/ui/layout/MainActivity.kt | 39 +-- 10 files changed, 352 insertions(+), 316 deletions(-) create mode 100644 app/src/main/java/ru/sweetbread/unn/api/API.kt rename app/src/main/java/ru/sweetbread/unn/{API.kt => api/endpoints.kt} (60%) create mode 100644 app/src/main/java/ru/sweetbread/unn/api/models.kt diff --git a/app/src/main/java/ru/sweetbread/unn/api/API.kt b/app/src/main/java/ru/sweetbread/unn/api/API.kt new file mode 100644 index 0000000..c5b1344 --- /dev/null +++ b/app/src/main/java/ru/sweetbread/unn/api/API.kt @@ -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 { + + 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 { + val schedule = downloadSchedule(type, id, start, finish) + + if ((type == ME.type) and (id == ME.unnId!!)) + cacheSchedule(schedule) + + return schedule +} diff --git a/app/src/main/java/ru/sweetbread/unn/API.kt b/app/src/main/java/ru/sweetbread/unn/api/endpoints.kt similarity index 60% rename from app/src/main/java/ru/sweetbread/unn/API.kt rename to app/src/main/java/ru/sweetbread/unn/api/endpoints.kt index f83a5e5..dc5029d 100644 --- a/app/src/main/java/ru/sweetbread/unn/API.kt +++ b/app/src/main/java/ru/sweetbread/unn/api/endpoints.kt @@ -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, - 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 { - - 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 { 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() 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 { val r = client.get("$prtl2URL/news.php") { header("Cookie", "PHPSESSID=$PHPSESSID") @@ -344,54 +251,3 @@ suspend fun getBlogposts(): ArrayList { } 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"), - ) - } - ) -} \ No newline at end of file diff --git a/app/src/main/java/ru/sweetbread/unn/api/models.kt b/app/src/main/java/ru/sweetbread/unn/api/models.kt new file mode 100644 index 0000000..66727fa --- /dev/null +++ b/app/src/main/java/ru/sweetbread/unn/api/models.kt @@ -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, + 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 +) \ No newline at end of file diff --git a/app/src/main/java/ru/sweetbread/unn/db/DB.kt b/app/src/main/java/ru/sweetbread/unn/db/DB.kt index c71429a..a730a6f 100644 --- a/app/src/main/java/ru/sweetbread/unn/db/DB.kt +++ b/app/src/main/java/ru/sweetbread/unn/db/DB.kt @@ -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(name = "cache") { + fallbackToDestructiveMigration(dropAllTables = true) +} @Database(entities = [ UserDB::class, diff --git a/app/src/main/java/ru/sweetbread/unn/db/Schedule.kt b/app/src/main/java/ru/sweetbread/unn/db/Schedule.kt index 9b40ee1..4bfc282 100644 --- a/app/src/main/java/ru/sweetbread/unn/db/Schedule.kt +++ b/app/src/main/java/ru/sweetbread/unn/db/Schedule.kt @@ -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) { } 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 { - 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) } ) } \ No newline at end of file diff --git a/app/src/main/java/ru/sweetbread/unn/db/UserDB.kt b/app/src/main/java/ru/sweetbread/unn/db/UserDB.kt index ed05347..23a1f12 100644 --- a/app/src/main/java/ru/sweetbread/unn/db/UserDB.kt +++ b/app/src/main/java/ru/sweetbread/unn/db/UserDB.kt @@ -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, diff --git a/app/src/main/java/ru/sweetbread/unn/ui/composes/Blogpost.kt b/app/src/main/java/ru/sweetbread/unn/ui/composes/Blogpost.kt index 3ec3825..886750c 100644 --- a/app/src/main/java/ru/sweetbread/unn/ui/composes/Blogpost.kt +++ b/app/src/main/java/ru/sweetbread/unn/ui/composes/Blogpost.kt @@ -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 ) } } diff --git a/app/src/main/java/ru/sweetbread/unn/ui/composes/Schedule.kt b/app/src/main/java/ru/sweetbread/unn/ui/composes/Schedule.kt index 2e5632d..f041620 100644 --- a/app/src/main/java/ru/sweetbread/unn/ui/composes/Schedule.kt +++ b/app/src/main/java/ru/sweetbread/unn/ui/composes/Schedule.kt @@ -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 diff --git a/app/src/main/java/ru/sweetbread/unn/ui/layout/LoginActivity.kt b/app/src/main/java/ru/sweetbread/unn/ui/layout/LoginActivity.kt index cd2065c..57e854d 100644 --- a/app/src/main/java/ru/sweetbread/unn/ui/layout/LoginActivity.kt +++ b/app/src/main/java/ru/sweetbread/unn/ui/layout/LoginActivity.kt @@ -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 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 49f3149..95bd949 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 @@ -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(name = "database") - class MainActivity : ComponentActivity() { @OptIn(ExperimentalMaterial3Api::class)