ref: reformat caching data in Rooms
This commit is contained in:
@@ -6,15 +6,12 @@
|
||||
package ru.risdeveau.pixeldragon
|
||||
|
||||
import android.util.Log
|
||||
import androidx.room.Room
|
||||
import io.ktor.client.HttpClient
|
||||
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 ru.risdeveau.pixeldragon.api.getMe
|
||||
import ru.risdeveau.pixeldragon.db.AppDatabase
|
||||
import splitties.init.appCtx
|
||||
import splitties.preferences.Preferences
|
||||
|
||||
val client = HttpClient {
|
||||
@@ -50,9 +47,4 @@ suspend fun initCheck(): Boolean {
|
||||
baseUrl = "$homeserver/_matrix/client/v3"
|
||||
|
||||
return getMe() != null
|
||||
}
|
||||
|
||||
val db = Room.databaseBuilder(
|
||||
appCtx,
|
||||
AppDatabase::class.java, "database"
|
||||
).build()
|
||||
}
|
||||
@@ -12,8 +12,8 @@ import io.ktor.http.HttpStatusCode
|
||||
import org.json.JSONObject
|
||||
import ru.risdeveau.pixeldragon.baseUrl
|
||||
import ru.risdeveau.pixeldragon.client
|
||||
import ru.risdeveau.pixeldragon.db
|
||||
import ru.risdeveau.pixeldragon.db.Room
|
||||
import ru.risdeveau.pixeldragon.repo.Room
|
||||
import ru.risdeveau.pixeldragon.repo.User
|
||||
import ru.risdeveau.pixeldragon.token
|
||||
|
||||
//fun getRooms(): List<Room> {
|
||||
@@ -34,8 +34,6 @@ suspend fun getRooms(): List<String> {
|
||||
}
|
||||
|
||||
suspend fun getRoom(rid: String): Room {
|
||||
var room = db.roomDoa().getById(rid)
|
||||
|
||||
val direct = getAccountData(getMe()!!.userId, "m.direct")
|
||||
var directWith = ""
|
||||
direct?.let {
|
||||
@@ -51,16 +49,21 @@ suspend fun getRoom(rid: String): Room {
|
||||
}
|
||||
}
|
||||
|
||||
if (room == null) {
|
||||
val name = getState(rid, "m.room.name", "name")
|
||||
val type = getState(rid, "m.room.create", "type") ?: "m.room"
|
||||
val creator = getState(rid, "m.room.create", "creator")
|
||||
val avatar = getState(rid, "m.room.avatar", "url")
|
||||
room = Room(rid, name, type, creator, null, avatar, null, true, if (directWith.isNotEmpty()) directWith else null)
|
||||
db.roomDoa().insert(room)
|
||||
}
|
||||
|
||||
return room
|
||||
val name = getState(rid, "m.room.name", "name")
|
||||
val type = getState(rid, "m.room.create", "type") ?: "m.room"
|
||||
val creator = getState(rid, "m.room.create", "creator")
|
||||
val avatar = getState(rid, "m.room.avatar", "url")
|
||||
return Room(
|
||||
rid,
|
||||
name,
|
||||
type,
|
||||
creator,
|
||||
null,
|
||||
avatar,
|
||||
null,
|
||||
true, // TODO: insert actual value
|
||||
if (directWith.isNotEmpty()) User.getById(directWith) else null
|
||||
)
|
||||
}
|
||||
|
||||
private suspend fun getState(rid: String, state: String, key: String): String? {
|
||||
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Created by sweetbread
|
||||
* Copyright (c) 2025. All rights reserved.
|
||||
*/
|
||||
|
||||
package ru.risdeveau.pixeldragon.db
|
||||
|
||||
import androidx.room.Database
|
||||
import androidx.room.Room
|
||||
import androidx.room.RoomDatabase
|
||||
import androidx.room.TypeConverter
|
||||
import splitties.init.appCtx
|
||||
import java.time.Instant
|
||||
|
||||
@Database(entities = [RoomDB::class, SpaceToRoom::class, UserDB::class], version = 1)
|
||||
abstract class AppDatabase : RoomDatabase() {
|
||||
abstract fun roomDoa(): RoomDao
|
||||
abstract fun userDoa(): UserDao
|
||||
}
|
||||
|
||||
class Converters {
|
||||
@TypeConverter
|
||||
fun fromInstant(value: Instant?): String? {
|
||||
return value?.toString()
|
||||
}
|
||||
|
||||
@TypeConverter
|
||||
fun toInstant(value: String?): Instant? {
|
||||
return value?.let { Instant.parse(it) }
|
||||
}
|
||||
}
|
||||
|
||||
val cacheDb = Room.databaseBuilder(
|
||||
appCtx,
|
||||
AppDatabase::class.java, "cache"
|
||||
)
|
||||
.fallbackToDestructiveMigration()
|
||||
.build()
|
||||
@@ -6,7 +6,6 @@
|
||||
package ru.risdeveau.pixeldragon.db
|
||||
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Database
|
||||
import androidx.room.Delete
|
||||
import androidx.room.Embedded
|
||||
import androidx.room.Entity
|
||||
@@ -15,11 +14,16 @@ import androidx.room.Junction
|
||||
import androidx.room.PrimaryKey
|
||||
import androidx.room.Query
|
||||
import androidx.room.Relation
|
||||
import androidx.room.RoomDatabase
|
||||
import androidx.room.TypeConverters
|
||||
import ru.risdeveau.pixeldragon.repo.Room
|
||||
import ru.risdeveau.pixeldragon.repo.User
|
||||
import java.time.Instant
|
||||
|
||||
@Entity
|
||||
data class Room(
|
||||
@PrimaryKey val roomId: String,
|
||||
@Entity(tableName = "room")
|
||||
@TypeConverters(Converters::class)
|
||||
data class RoomDB (
|
||||
@PrimaryKey val id: String,
|
||||
val updatedAt: Instant,
|
||||
val name: String?,
|
||||
val type: String,
|
||||
val creatorId: String?,
|
||||
@@ -27,36 +31,56 @@ data class Room(
|
||||
val avatarUrl: String?,
|
||||
val members: Int?,
|
||||
val joined: Boolean,
|
||||
val direct: String?,
|
||||
val direct: String?
|
||||
)
|
||||
|
||||
fun RoomDB.isExpired(cacheDuration: Long = 60 * 60): Boolean {
|
||||
return Instant.now().minusSeconds(cacheDuration) > updatedAt
|
||||
}
|
||||
|
||||
suspend fun RoomDB.toDomain(): Room = Room(
|
||||
id = id,
|
||||
name = name,
|
||||
type = type,
|
||||
creatorId = creatorId,
|
||||
createTime = createTime,
|
||||
avatarUrl = avatarUrl,
|
||||
members = members,
|
||||
joined = joined,
|
||||
direct = direct?.let { User.getById(it) }
|
||||
)
|
||||
|
||||
fun Room.toEntity(): RoomDB = RoomDB(
|
||||
id = id,
|
||||
updatedAt = Instant.now(),
|
||||
name = name,
|
||||
type = type,
|
||||
creatorId = creatorId,
|
||||
createTime = createTime,
|
||||
avatarUrl = avatarUrl,
|
||||
members = members,
|
||||
joined = joined,
|
||||
direct = direct?.id
|
||||
)
|
||||
|
||||
|
||||
@Dao
|
||||
interface RoomDao {
|
||||
// @Query("SELECT * FROM room")
|
||||
// fun getAll(): List<User>
|
||||
|
||||
// @Query("SELECT * FROM user WHERE uid IN (:userIds)")
|
||||
// fun loadAllByIds(userIds: IntArray): List<User>
|
||||
|
||||
// @Query("SELECT * FROM user WHERE first_name LIKE :first AND " +
|
||||
// "last_name LIKE :last LIMIT 1")
|
||||
// fun findByName(first: String, last: String): User
|
||||
|
||||
@Query("SELECT * FROM room WHERE roomId LIKE :rid LIMIT 1")
|
||||
fun getById(rid: String): Room?
|
||||
@Query("SELECT * FROM room WHERE id LIKE :id LIMIT 1")
|
||||
fun getById(id: String): RoomDB?
|
||||
|
||||
// @Transaction
|
||||
// @Query("SELECT * FROM room WHERE ")
|
||||
// fun getSpace(rid: String): Space
|
||||
|
||||
@Query("SELECT * FROM room WHERE joined = 1 AND roomId NOT IN (SELECT roomId FROM SpaceToRoom)")
|
||||
fun getAllJoined(): List<Room>
|
||||
@Query("SELECT * FROM room WHERE joined = 1 AND id NOT IN (SELECT id FROM SpaceToRoom)")
|
||||
fun getAllJoined(): List<RoomDB>
|
||||
|
||||
@Insert
|
||||
fun insert(vararg rooms: Room)
|
||||
fun insert(vararg rooms: RoomDB)
|
||||
|
||||
@Delete
|
||||
fun delete(room: Room)
|
||||
fun delete(room: RoomDB)
|
||||
}
|
||||
|
||||
@Entity(primaryKeys = ["spaceId", "roomId"])
|
||||
@@ -66,16 +90,11 @@ data class SpaceToRoom(
|
||||
)
|
||||
|
||||
data class Space(
|
||||
@Embedded val space: Room,
|
||||
@Embedded val space: RoomDB,
|
||||
@Relation(
|
||||
parentColumn = "spaceId",
|
||||
entityColumn = "roomId",
|
||||
associateBy = Junction(SpaceToRoom::class)
|
||||
)
|
||||
val children: List<Room>
|
||||
)
|
||||
|
||||
@Database(entities = [Room::class, SpaceToRoom::class, User::class], version = 1)
|
||||
abstract class AppDatabase : RoomDatabase() {
|
||||
abstract fun roomDoa(): RoomDao
|
||||
}
|
||||
val children: List<RoomDB>
|
||||
)
|
||||
@@ -1,18 +1,59 @@
|
||||
/*
|
||||
* Created by sweetbread on 22.02.2025, 15:45
|
||||
* Created by sweetbread
|
||||
* Copyright (c) 2025. All rights reserved.
|
||||
* Last modified 21.02.2025, 13:38
|
||||
*/
|
||||
|
||||
package ru.risdeveau.pixeldragon.db
|
||||
|
||||
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 androidx.room.TypeConverters
|
||||
import org.json.JSONObject
|
||||
import ru.risdeveau.pixeldragon.repo.User
|
||||
import java.time.Instant
|
||||
|
||||
@Entity
|
||||
data class User(
|
||||
@PrimaryKey val uid: String,
|
||||
@ColumnInfo(name = "name") val name: String?,
|
||||
@ColumnInfo(name = "avatar") val avatar: String?
|
||||
)
|
||||
@Entity(tableName = "user")
|
||||
@TypeConverters(Converters::class)
|
||||
data class UserDB (
|
||||
@PrimaryKey val id: String,
|
||||
val updatedAt: Instant,
|
||||
val name: String?,
|
||||
val avatar: String?,
|
||||
val attrs: String,
|
||||
)
|
||||
|
||||
fun UserDB.isExpired(cacheDuration: Long = 60 * 60): Boolean {
|
||||
return Instant.now().minusSeconds(cacheDuration) > updatedAt
|
||||
}
|
||||
|
||||
fun UserDB.toDomain(): User = User(
|
||||
id = id,
|
||||
name = name,
|
||||
avatarUrl = avatar,
|
||||
attrs = JSONObject(attrs),
|
||||
)
|
||||
|
||||
fun User.toEntity(): UserDB = UserDB(
|
||||
id = id,
|
||||
updatedAt = Instant.now(),
|
||||
name = name,
|
||||
avatar = avatarUrl,
|
||||
attrs = attrs.toString().trim(),
|
||||
)
|
||||
|
||||
|
||||
@Dao
|
||||
interface UserDao {
|
||||
@Query("SELECT * FROM user WHERE id LIKE :id LIMIT 1")
|
||||
fun getById(id: String): UserDB?
|
||||
|
||||
@Insert
|
||||
fun insert(vararg users: UserDB)
|
||||
|
||||
@Delete
|
||||
fun delete(user: UserDB)
|
||||
}
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Created by sweetbread
|
||||
* Copyright (c) 2025. All rights reserved.
|
||||
*/
|
||||
|
||||
package ru.risdeveau.pixeldragon.repo
|
||||
|
||||
import ru.risdeveau.pixeldragon.api.getRoom
|
||||
import ru.risdeveau.pixeldragon.db.cacheDb
|
||||
import ru.risdeveau.pixeldragon.db.isExpired
|
||||
import ru.risdeveau.pixeldragon.db.toDomain
|
||||
import ru.risdeveau.pixeldragon.db.toEntity
|
||||
|
||||
class Room (
|
||||
val id: String,
|
||||
val name: String?,
|
||||
val type: String,
|
||||
val creatorId: String?,
|
||||
val createTime: Long?,
|
||||
val avatarUrl: String?,
|
||||
val members: Int?,
|
||||
val joined: Boolean,
|
||||
val direct: User?,
|
||||
|
||||
) {
|
||||
companion object {
|
||||
suspend fun getById(id: String, cached: Boolean = true): Room {
|
||||
val cachedRoom = cacheDb.roomDoa().getById(id)
|
||||
if ((cachedRoom == null) or (cachedRoom?.isExpired() == true)) {
|
||||
val room = getRoom(id)
|
||||
val cacheRoom = room.toEntity()
|
||||
cacheDb.roomDoa().insert(cacheRoom)
|
||||
return room
|
||||
}
|
||||
return cachedRoom!!.toDomain()
|
||||
}
|
||||
}
|
||||
}
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Created by sweetbread
|
||||
* Copyright (c) 2025. All rights reserved.
|
||||
*/
|
||||
|
||||
package ru.risdeveau.pixeldragon.repo
|
||||
|
||||
import android.util.Log
|
||||
import org.json.JSONObject
|
||||
import ru.risdeveau.pixeldragon.api.getUserProfile
|
||||
import ru.risdeveau.pixeldragon.db.cacheDb
|
||||
import ru.risdeveau.pixeldragon.db.isExpired
|
||||
import ru.risdeveau.pixeldragon.db.toDomain
|
||||
import ru.risdeveau.pixeldragon.db.toEntity
|
||||
|
||||
class User (
|
||||
val id: String,
|
||||
val name: String?,
|
||||
val avatarUrl: String?,
|
||||
val attrs: JSONObject
|
||||
) {
|
||||
companion object {
|
||||
suspend fun getById(id: String): User? {
|
||||
val cachedUser = cacheDb.userDoa().getById(id)
|
||||
if ((cachedUser == null) or (cachedUser?.isExpired() == true)) {
|
||||
val userProfile = getUserProfile(id)
|
||||
if (userProfile == null) {
|
||||
Log.i("User.getById", "User $id not found")
|
||||
return null
|
||||
}
|
||||
val user = User(
|
||||
id,
|
||||
userProfile.displayName,
|
||||
userProfile.avatarUrl,
|
||||
userProfile.other
|
||||
)
|
||||
cacheDb.userDoa().insert(user.toEntity())
|
||||
return user
|
||||
}
|
||||
return cachedUser!!.toDomain()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -35,11 +35,8 @@ import androidx.navigation.NavController
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import ru.risdeveau.pixeldragon.api.UserProfile
|
||||
import ru.risdeveau.pixeldragon.api.getRoom
|
||||
import ru.risdeveau.pixeldragon.api.getRooms
|
||||
import ru.risdeveau.pixeldragon.api.getUserProfile
|
||||
import ru.risdeveau.pixeldragon.db.Room
|
||||
import ru.risdeveau.pixeldragon.repo.Room
|
||||
import ru.risdeveau.pixeldragon.ui.item.MXCImage
|
||||
|
||||
@Composable
|
||||
@@ -79,31 +76,19 @@ fun RoomList(modifier: Modifier = Modifier, navController: NavController) {
|
||||
@Composable
|
||||
fun RoomItem(modifier: Modifier = Modifier, rid: String, navController: NavController) {
|
||||
var room by remember { mutableStateOf<Room?>(null) }
|
||||
var directUser by remember { mutableStateOf<UserProfile?>(null) }
|
||||
val scope = rememberCoroutineScope()
|
||||
|
||||
LaunchedEffect(true) {
|
||||
scope.launch {
|
||||
withContext(Dispatchers.IO) {
|
||||
room = getRoom(rid)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LaunchedEffect(room?.direct) {
|
||||
room?.let {
|
||||
it.direct?.let { uid ->
|
||||
scope.launch {
|
||||
withContext(Dispatchers.IO) {
|
||||
directUser = getUserProfile(uid)
|
||||
}
|
||||
}
|
||||
room = Room.getById(rid)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (room != null) {
|
||||
val avatarUrl = room!!.avatarUrl ?: (directUser?.avatarUrl ?: "")
|
||||
val room = room!!
|
||||
val avatarUrl = room.avatarUrl ?: (room.direct?.avatarUrl ?: "")
|
||||
|
||||
Row(
|
||||
modifier
|
||||
@@ -112,14 +97,14 @@ fun RoomItem(modifier: Modifier = Modifier, rid: String, navController: NavContr
|
||||
.fillMaxWidth()
|
||||
.background(
|
||||
color =
|
||||
if (room!!.type == "m.space")
|
||||
if (room.type == "m.space")
|
||||
MaterialTheme.colorScheme.tertiary
|
||||
else
|
||||
MaterialTheme.colorScheme.background
|
||||
)
|
||||
.clip(RoundedCornerShape(12.dp))
|
||||
.clickable {
|
||||
if (room!!.type == "m.space")
|
||||
if (room.type == "m.space")
|
||||
navController.navigate("space/$rid")
|
||||
else
|
||||
navController.navigate("room/$rid")
|
||||
@@ -133,16 +118,16 @@ fun RoomItem(modifier: Modifier = Modifier, rid: String, navController: NavContr
|
||||
.height(52.dp)
|
||||
.width(52.dp)
|
||||
.let {
|
||||
if (room!!.type == "m.space")
|
||||
if (room.type == "m.space")
|
||||
it.clip(RoundedCornerShape(12.dp))
|
||||
else
|
||||
it.clip(CircleShape)
|
||||
}
|
||||
)
|
||||
Column {
|
||||
Text(room!!.type)
|
||||
Text(room.type)
|
||||
Text(
|
||||
room!!.name ?: "Unnamed",
|
||||
room.name ?: "Unnamed",
|
||||
maxLines = 1,
|
||||
color = MaterialTheme.colorScheme.primary,
|
||||
fontSize = MaterialTheme.typography.titleLarge.fontSize
|
||||
|
||||
Reference in New Issue
Block a user