feat(cache): Cache users

This commit is contained in:
sweetbread 2024-03-28 00:03:48 +03:00 committed by Sweetbread
parent dd88d4a81a
commit 172f997af6
8 changed files with 161 additions and 13 deletions

View File

@ -5,7 +5,7 @@ plugins {
alias(libs.plugins.androidApplication) alias(libs.plugins.androidApplication)
alias(libs.plugins.jetbrainsKotlinAndroid) alias(libs.plugins.jetbrainsKotlinAndroid)
id("com.google.android.libraries.mapsplatform.secrets-gradle-plugin") id("com.google.android.libraries.mapsplatform.secrets-gradle-plugin")
id("com.google.devtools.ksp")
} }
secrets { secrets {
@ -110,4 +110,8 @@ dependencies {
implementation(libs.kefirbb) implementation(libs.kefirbb)
implementation(libs.acra.http) implementation(libs.acra.http)
implementation(libs.androidx.room.runtime)
implementation(libs.androidx.room.ktx)
ksp(libs.androidx.room.compiler)
} }

View File

@ -1,13 +1,18 @@
package ru.sweetbread.unn package ru.sweetbread.unn
import android.util.Log
import io.ktor.client.request.forms.submitForm import io.ktor.client.request.forms.submitForm
import io.ktor.client.request.get import io.ktor.client.request.get
import io.ktor.client.request.header import io.ktor.client.request.header
import io.ktor.client.request.parameter 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.withContext
import org.json.JSONArray import org.json.JSONArray
import org.json.JSONObject import org.json.JSONObject
import ru.sweetbread.unn.db.cacheUser
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 java.time.LocalDate import java.time.LocalDate
@ -28,7 +33,8 @@ enum class Type(val s: String) {
Student("student"), Student("student"),
Group("group"), Group("group"),
Lecturer("lecturer"), Lecturer("lecturer"),
Auditorium("auditorium") Auditorium("auditorium"),
Employee("employee")
} }
enum class LecturerRank(val id: Int) { enum class LecturerRank(val id: Int) {
@ -94,7 +100,7 @@ class User(
val nameEn: String, val nameEn: String,
val isMale: Boolean, val isMale: Boolean,
val birthday: LocalDate, val birthday: LocalDate,
val avatar: ImageSet val avatar: AvatarSet
) )
class Post( class Post(
@ -107,7 +113,7 @@ class Post(
val content: String val content: String
) )
class ImageSet( class AvatarSet(
val original: String, val original: String,
val thumbnail: String, val thumbnail: String,
val small: String val small: String
@ -177,7 +183,7 @@ private suspend fun getMyself(login: String) {
DateTimeFormatter.ofPattern("yyyy-MM-dd") DateTimeFormatter.ofPattern("yyyy-MM-dd")
), ),
avatar = user.getJSONObject("photo").let { avatar = user.getJSONObject("photo").let {
ImageSet( AvatarSet(
it.getString("orig"), it.getString("orig"),
it.getString("thumbnail"), it.getString("thumbnail"),
it.getString("small"), it.getString("small"),
@ -304,10 +310,22 @@ suspend fun getBlogposts(): ArrayList<Post> {
} }
suspend fun getUserByBitrixId(id: Int): User { suspend fun getUserByBitrixId(id: Int): User {
var user: User?
withContext(Dispatchers.IO) {
user = loadUserByBitrixId(id)
}
user?.let { return user as User }
val userId = JSONObject(client.get("$vuzapiURL/user/bx/$id") { val userId = JSONObject(client.get("$vuzapiURL/user/bx/$id") {
header("Cookie", "PHPSESSID=$PHPSESSID") header("Cookie", "PHPSESSID=$PHPSESSID")
}.bodyAsText()).getInt("id") }.bodyAsText()).getInt("id")
return getUser(userId)
getUser(userId).let { user ->
withContext(Dispatchers.IO) {
cacheUser(user)
}
return user
}
} }
suspend fun getUser(id: Int): User { suspend fun getUser(id: Int): User {
@ -317,14 +335,15 @@ suspend fun getUser(id: Int): User {
}.bodyAsText() }.bodyAsText()
) )
Log.d("type", json.getJSONArray("profiles").getJSONObject(0).getString("type"))
return User( return User(
unnId = null, unnId = null,
bitrixId = json.getInt("bitrix_id"), bitrixId = json.getInt("bitrix_id"),
userId = json.getInt("id"), userId = json.getInt("id"),
type = when (json.getJSONArray("profiles").getJSONObject(0).getString("type")) { type = if (json.getJSONArray("profiles").getJSONObject(0)
"lecturer" -> Type.Lecturer // ig,,, .getString("type") == "employee"
else -> Type.Student ) Type.Employee else Type.Student,
},
email = json.getString("email"), email = json.getString("email"),
nameRu = json.getString("fullname"), nameRu = json.getString("fullname"),
nameEn = json.getString("fullname_en"), nameEn = json.getString("fullname_en"),
@ -334,7 +353,7 @@ suspend fun getUser(id: Int): User {
DateTimeFormatter.ofPattern("yyyy-MM-dd") DateTimeFormatter.ofPattern("yyyy-MM-dd")
), ),
avatar = json.getJSONObject("photo").let { avatar = json.getJSONObject("photo").let {
ImageSet( AvatarSet(
it.getString("orig"), it.getString("orig"),
it.getString("thumbnail"), it.getString("thumbnail"),
it.getString("small"), it.getString("small"),

View File

@ -0,0 +1,9 @@
package ru.sweetbread.unn.db
import androidx.room.Database
import androidx.room.RoomDatabase
@Database(entities = [UserDB::class], version = 1)
abstract class AppDatabase : RoomDatabase() {
abstract fun userDao(): UserDao
}

View File

@ -0,0 +1,101 @@
package ru.sweetbread.unn.db
import android.util.Log
import androidx.room.ColumnInfo
import androidx.room.Dao
import androidx.room.Delete
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 java.time.LocalDate
import java.time.LocalDateTime
import java.time.format.DateTimeFormatter
@Entity
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,
@ColumnInfo val nameEn: String,
@ColumnInfo val isMale: Boolean,
@ColumnInfo val birthday: String,
@ColumnInfo val origAvatar: String,
@ColumnInfo val thumbAvatar: String,
@ColumnInfo val smallAvatar: String,
@ColumnInfo val expiredAt: String
)
@Dao
interface UserDao {
@Query("SELECT * FROM userDB WHERE bitrixId = :bitrixId LIMIT 1")
fun getUserByBitrix(bitrixId: Int): UserDB?
@Insert
fun insert(user: UserDB)
@Delete
fun delete(user: UserDB)
}
fun cacheUser(user: User) {
try {
db.userDao().insert(
UserDB(
user.userId,
user.unnId,
user.bitrixId,
user.type,
user.email,
user.nameRu,
user.nameEn,
user.isMale,
user.birthday.format(DateTimeFormatter.ISO_LOCAL_DATE),
user.avatar.original,
user.avatar.thumbnail,
user.avatar.small,
LocalDateTime.now().plusDays(1).format(DateTimeFormatter.ISO_LOCAL_DATE_TIME)
)
)
} catch (_: android.database.sqlite.SQLiteConstraintException) {
}
}
fun loadUserByBitrixId(bitrixId: Int): User? {
val user = db.userDao().getUserByBitrix(bitrixId)
Log.d("UserDB", user?.nameEn ?: "None")
if (user == null) return null
if (LocalDateTime.parse(
user.expiredAt,
DateTimeFormatter.ISO_LOCAL_DATE_TIME
) < LocalDateTime.now()
) {
db.userDao().delete(user)
return null
} else {
return User(
user.unnId,
user.bitrixId,
user.userId,
user.type,
user.email,
user.nameRu,
user.nameEn,
user.isMale,
LocalDate.parse(user.birthday, DateTimeFormatter.ISO_LOCAL_DATE),
AvatarSet(
user.origAvatar,
user.thumbAvatar,
user.smallAvatar
)
)
}
}

View File

@ -48,7 +48,7 @@ import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.asStateFlow
import org.kefirsf.bb.BBProcessorFactory import org.kefirsf.bb.BBProcessorFactory
import org.kefirsf.bb.TextProcessor import org.kefirsf.bb.TextProcessor
import ru.sweetbread.unn.ImageSet import ru.sweetbread.unn.AvatarSet
import ru.sweetbread.unn.Post import ru.sweetbread.unn.Post
import ru.sweetbread.unn.R import ru.sweetbread.unn.R
import ru.sweetbread.unn.Type import ru.sweetbread.unn.Type
@ -73,7 +73,7 @@ val defUser = User(
"Jon Sigma Omega", "Jon Sigma Omega",
true, true,
LocalDate.now(), LocalDate.now(),
ImageSet( AvatarSet(
"https://upload.wikimedia.org/wikipedia/ru/thumb/9/94/%D0%93%D0%B8%D0%B3%D0%B0%D1%87%D0%B0%D0%B4.jpg/500px-%D0%93%D0%B8%D0%B3%D0%B0%D1%87%D0%B0%D0%B4.jpg", "https://upload.wikimedia.org/wikipedia/ru/thumb/9/94/%D0%93%D0%B8%D0%B3%D0%B0%D1%87%D0%B0%D0%B4.jpg/500px-%D0%93%D0%B8%D0%B3%D0%B0%D1%87%D0%B0%D0%B4.jpg",
"https://upload.wikimedia.org/wikipedia/ru/thumb/9/94/%D0%93%D0%B8%D0%B3%D0%B0%D1%87%D0%B0%D0%B4.jpg/500px-%D0%93%D0%B8%D0%B3%D0%B0%D1%87%D0%B0%D0%B4.jpg", "https://upload.wikimedia.org/wikipedia/ru/thumb/9/94/%D0%93%D0%B8%D0%B3%D0%B0%D1%87%D0%B0%D0%B4.jpg/500px-%D0%93%D0%B8%D0%B3%D0%B0%D1%87%D0%B0%D0%B4.jpg",
"https://upload.wikimedia.org/wikipedia/ru/thumb/9/94/%D0%93%D0%B8%D0%B3%D0%B0%D1%87%D0%B0%D0%B4.jpg/500px-%D0%93%D0%B8%D0%B3%D0%B0%D1%87%D0%B0%D0%B4.jpg" "https://upload.wikimedia.org/wikipedia/ru/thumb/9/94/%D0%93%D0%B8%D0%B3%D0%B0%D1%87%D0%B0%D0%B4.jpg/500px-%D0%93%D0%B8%D0%B3%D0%B0%D1%87%D0%B0%D0%B4.jpg"

View File

@ -25,6 +25,7 @@ import androidx.compose.ui.Modifier
import androidx.navigation.compose.NavHost import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController import androidx.navigation.compose.rememberNavController
import androidx.room.Room
import io.ktor.client.HttpClient import io.ktor.client.HttpClient
import io.ktor.client.plugins.HttpRequestRetry import io.ktor.client.plugins.HttpRequestRetry
import io.ktor.client.plugins.HttpTimeout import io.ktor.client.plugins.HttpTimeout
@ -32,6 +33,7 @@ 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 ru.sweetbread.unn.db.AppDatabase
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
@ -59,10 +61,18 @@ val client = HttpClient {
} }
} }
lateinit var db: AppDatabase
class MainActivity : ComponentActivity() { class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
db = Room.databaseBuilder(
applicationContext,
AppDatabase::class.java, "database"
).build()
setContent { setContent {
UNNTheme { UNNTheme {
Surface(modifier = Modifier.fillMaxSize(), color = MaterialTheme.colorScheme.background) { Surface(modifier = Modifier.fillMaxSize(), color = MaterialTheme.colorScheme.background) {

View File

@ -2,6 +2,7 @@
plugins { plugins {
alias(libs.plugins.androidApplication) apply false alias(libs.plugins.androidApplication) apply false
alias(libs.plugins.jetbrainsKotlinAndroid) apply false alias(libs.plugins.jetbrainsKotlinAndroid) apply false
id("com.google.devtools.ksp") version "1.9.0-1.0.13" apply false
} }
buildscript { buildscript {

View File

@ -24,6 +24,7 @@ lifecycleLivedataKtx = "2.7.0"
lifecycleViewmodelKtx = "2.7.0" lifecycleViewmodelKtx = "2.7.0"
activity = "1.8.2" activity = "1.8.2"
navigationCompose = "2.7.7" navigationCompose = "2.7.7"
roomRuntime = "2.6.1"
secretsGradlePlugin = "2.0.1" secretsGradlePlugin = "2.0.1"
splittiesFunPackAndroidBaseWithViewsDsl = "3.0.0" splittiesFunPackAndroidBaseWithViewsDsl = "3.0.0"
kefirbb = "1.5" kefirbb = "1.5"
@ -34,6 +35,9 @@ androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref =
androidx-core-splashscreen = { module = "androidx.core:core-splashscreen", version.ref = "coreSplashscreen" } androidx-core-splashscreen = { module = "androidx.core:core-splashscreen", version.ref = "coreSplashscreen" }
androidx-datastore-preferences = { module = "androidx.datastore:datastore-preferences", version.ref = "datastorePreferences" } androidx-datastore-preferences = { module = "androidx.datastore:datastore-preferences", version.ref = "datastorePreferences" }
androidx-navigation-compose = { module = "androidx.navigation:navigation-compose", version.ref = "navigationCompose" } androidx-navigation-compose = { module = "androidx.navigation:navigation-compose", version.ref = "navigationCompose" }
androidx-room-compiler = { module = "androidx.room:room-compiler", version.ref = "roomRuntime" }
androidx-room-ktx = { module = "androidx.room:room-ktx", version.ref = "roomRuntime" }
androidx-room-runtime = { module = "androidx.room:room-runtime", version.ref = "roomRuntime" }
coil-compose = { module = "io.coil-kt:coil-compose", version.ref = "coilCompose" } coil-compose = { module = "io.coil-kt:coil-compose", version.ref = "coilCompose" }
compose = { module = "com.kizitonwose.calendar:compose", version.ref = "compose" } compose = { module = "com.kizitonwose.calendar:compose", version.ref = "compose" }
desugar_jdk_libs = { module = "com.android.tools:desugar_jdk_libs", version.ref = "desugar_jdk_libs" } desugar_jdk_libs = { module = "com.android.tools:desugar_jdk_libs", version.ref = "desugar_jdk_libs" }