From 05fbfbac07daecb741537b7d84f6e766b6c3968f Mon Sep 17 00:00:00 2001 From: Sweetbread Date: Tue, 4 Nov 2025 16:01:14 +0300 Subject: [PATCH] feat: show room avatar when is DM --- .../java/ru/risdeveau/pixeldragon/api/Room.kt | 19 +++++- .../ru/risdeveau/pixeldragon/api/Server.kt | 7 +- .../java/ru/risdeveau/pixeldragon/api/User.kt | 16 +++++ .../java/ru/risdeveau/pixeldragon/db/Room.kt | 4 +- .../ru/risdeveau/pixeldragon/ui/item/Image.kt | 66 ++++++++++--------- .../risdeveau/pixeldragon/ui/layout/Rooms.kt | 30 +++++++-- 6 files changed, 98 insertions(+), 44 deletions(-) diff --git a/app/src/main/java/ru/risdeveau/pixeldragon/api/Room.kt b/app/src/main/java/ru/risdeveau/pixeldragon/api/Room.kt index 60bb0ca..74ee808 100755 --- a/app/src/main/java/ru/risdeveau/pixeldragon/api/Room.kt +++ b/app/src/main/java/ru/risdeveau/pixeldragon/api/Room.kt @@ -1,7 +1,6 @@ /* * Created by sweetbread * Copyright (c) 2025. All rights reserved. - * Last modified 03.03.2025, 18:28 */ package ru.risdeveau.pixeldragon.api @@ -36,12 +35,28 @@ suspend fun getRooms(): List { suspend fun getRoom(rid: String): Room { var room = db.roomDoa().getById(rid) + + val direct = getAccountData(getMe()!!.userId, "m.direct") + var directWith = "" + direct?.let { + for (user in direct.keys()) { + val roomsWithUser = direct.getJSONArray(user) + for (i in 0 until roomsWithUser.length()) { + if (rid == roomsWithUser.getString(i)) { + directWith = user + break + } + } + if (directWith.isNotEmpty()) break + } + } + 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) + room = Room(rid, name, type, creator, null, avatar, null, true, if (directWith.isNotEmpty()) directWith else null) db.roomDoa().insert(room) } diff --git a/app/src/main/java/ru/risdeveau/pixeldragon/api/Server.kt b/app/src/main/java/ru/risdeveau/pixeldragon/api/Server.kt index 75b09f1..4ca9304 100755 --- a/app/src/main/java/ru/risdeveau/pixeldragon/api/Server.kt +++ b/app/src/main/java/ru/risdeveau/pixeldragon/api/Server.kt @@ -1,7 +1,6 @@ /* * Created by sweetbread * Copyright (c) 2025. All rights reserved. - * Last modified 03.03.2025, 15:40 */ package ru.risdeveau.pixeldragon.api @@ -28,9 +27,11 @@ suspend fun getHomeserver(url: String): String? { return homeserver } -fun mxcToUrl(mxc: String): String { - val pattern = Regex("mxc://([-a-zA-Z0-9@:%._+~#=]{1,256}\\.[a-zA-Z0-9()]{1,6})/([a-zA-Z]+)") +fun mxcToUrl(mxc: String): String? { + val pattern = Regex("mxc://([-a-zA-Z0-9@:%._+~#=]{1,256}\\.[a-zA-Z0-9()]{1,6})/([a-zA-Z0-9]+)") val match = pattern.find(mxc) + if ((match?.groupValues[1] == null) or (match?.groupValues[2] == null)) return null + return "$homeserver/_matrix/client/v1/media/download/${match?.groupValues[1]}/${match?.groupValues[2]}" } \ No newline at end of file diff --git a/app/src/main/java/ru/risdeveau/pixeldragon/api/User.kt b/app/src/main/java/ru/risdeveau/pixeldragon/api/User.kt index 6d784a0..e83bf2b 100755 --- a/app/src/main/java/ru/risdeveau/pixeldragon/api/User.kt +++ b/app/src/main/java/ru/risdeveau/pixeldragon/api/User.kt @@ -25,6 +25,7 @@ import splitties.init.appCtx import splitties.preferences.edit data class Me (val userId: String, val deviceId: String) +data class UserProfile (val displayName: String, val avatarUrl: String, val other: JSONObject) /** * This func is to validate the token @@ -82,3 +83,18 @@ suspend fun login(server: String, login: String, pass: String): Boolean { return initCheck() } + +suspend fun getAccountData(user: String, state: String): JSONObject? { + val r = client.get("$baseUrl/user/$user/account_data/$state") { bearerAuth(token) } + if (r.status != HttpStatusCode.OK) return null + return JSONObject(r.bodyAsText()) +} + +suspend fun getUserProfile(userId: String): UserProfile? { + val r = client.get("$baseUrl/profile/$userId") { bearerAuth(token) } + if (r.status != HttpStatusCode.OK) return null + val json = JSONObject(r.bodyAsText()) + val name = json.optString("displayname", ""); json.remove("displayname") + val avatar = json.optString("avatar_url", ""); json.remove("avatar_url") + return UserProfile(name, avatar, json) +} \ No newline at end of file diff --git a/app/src/main/java/ru/risdeveau/pixeldragon/db/Room.kt b/app/src/main/java/ru/risdeveau/pixeldragon/db/Room.kt index 4cffd13..a951e90 100755 --- a/app/src/main/java/ru/risdeveau/pixeldragon/db/Room.kt +++ b/app/src/main/java/ru/risdeveau/pixeldragon/db/Room.kt @@ -1,7 +1,6 @@ /* * Created by sweetbread * Copyright (c) 2025. All rights reserved. - * Last modified 22.02.2025, 19:49 */ package ru.risdeveau.pixeldragon.db @@ -27,7 +26,8 @@ data class Room( val createTime: Long?, val avatarUrl: String?, val members: Int?, - val joined: Boolean + val joined: Boolean, + val direct: String?, ) @Dao diff --git a/app/src/main/java/ru/risdeveau/pixeldragon/ui/item/Image.kt b/app/src/main/java/ru/risdeveau/pixeldragon/ui/item/Image.kt index a19a85b..d32e3a8 100755 --- a/app/src/main/java/ru/risdeveau/pixeldragon/ui/item/Image.kt +++ b/app/src/main/java/ru/risdeveau/pixeldragon/ui/item/Image.kt @@ -15,6 +15,7 @@ import androidx.compose.material3.Icon import androidx.compose.runtime.Composable import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.layout.ContentScale import coil3.compose.AsyncImagePainter @@ -38,40 +39,43 @@ fun MXCImage( contentScale: ContentScale = ContentScale.Fit, contentDescription: String = "" ) { - val loadState = remember { mutableStateOf(ImageLoadState.Loading) } - val painter = rememberAsyncImagePainter( - model = ImageRequest.Builder(appCtx) - .data(mxcToUrl(mxcUrl)) - .httpHeaders( - NetworkHeaders.Builder() - .set("Authorization", "Bearer $token") - .set("Cache-Control", "max-age=86400") - .build() - ) - .build(), - onState = { state -> - loadState.value = when (state) { - is AsyncImagePainter.State.Loading -> ImageLoadState.Loading - is AsyncImagePainter.State.Success -> ImageLoadState.Success - else -> ImageLoadState.Error + mxcToUrl(mxcUrl)?.let { url -> + val loadState = remember { mutableStateOf(ImageLoadState.Loading) } + val painter = rememberAsyncImagePainter( + model = ImageRequest.Builder(appCtx) + .data(url) + .httpHeaders( + NetworkHeaders.Builder() + .set("Authorization", "Bearer $token") + .set("Cache-Control", "max-age=86400") + .build() + ) + .build(), + onState = { state -> + loadState.value = when (state) { + is AsyncImagePainter.State.Loading -> ImageLoadState.Loading + is AsyncImagePainter.State.Success -> ImageLoadState.Success + else -> ImageLoadState.Error + } } - } - ) - - Box( - modifier = modifier.fillMaxSize() - ) { - Image( - painter = painter, - contentDescription = contentDescription, - contentScale = contentScale, - modifier = Modifier.fillMaxSize() ) - when (loadState.value) { - ImageLoadState.Loading -> CircularProgressIndicator() - ImageLoadState.Error -> Icon(Icons.Outlined.Warning, "Error") - ImageLoadState.Success -> Unit + Box( + modifier = modifier.fillMaxSize(), + contentAlignment = Alignment.Center + ) { + Image( + painter = painter, + contentDescription = contentDescription, + contentScale = contentScale, + modifier = Modifier.fillMaxSize() + ) + + when (loadState.value) { + ImageLoadState.Loading -> CircularProgressIndicator() + ImageLoadState.Error -> Icon(Icons.Outlined.Warning, "Error") + ImageLoadState.Success -> Unit + } } } } \ No newline at end of file diff --git a/app/src/main/java/ru/risdeveau/pixeldragon/ui/layout/Rooms.kt b/app/src/main/java/ru/risdeveau/pixeldragon/ui/layout/Rooms.kt index 770d060..c7bfc4a 100755 --- a/app/src/main/java/ru/risdeveau/pixeldragon/ui/layout/Rooms.kt +++ b/app/src/main/java/ru/risdeveau/pixeldragon/ui/layout/Rooms.kt @@ -35,8 +35,10 @@ 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.ui.item.MXCImage @@ -77,6 +79,7 @@ fun RoomList(modifier: Modifier = Modifier, navController: NavController) { @Composable fun RoomItem(modifier: Modifier = Modifier, rid: String, navController: NavController) { var room by remember { mutableStateOf(null) } + var directUser by remember { mutableStateOf(null) } val scope = rememberCoroutineScope() LaunchedEffect(true) { @@ -87,17 +90,32 @@ fun RoomItem(modifier: Modifier = Modifier, rid: String, navController: NavContr } } + LaunchedEffect(room?.direct) { + room?.let { + it.direct?.let { uid -> + scope.launch { + withContext(Dispatchers.IO) { + directUser = getUserProfile(uid) + } + } + } + } + } + if (room != null) { + val avatarUrl = room!!.avatarUrl ?: (directUser?.avatarUrl ?: "") + Row( modifier .padding(8.dp) .height((52 + 8 * 2).dp) .fillMaxWidth() - .background(color = - if (room!!.type == "m.space") - MaterialTheme.colorScheme.tertiary - else - MaterialTheme.colorScheme.background + .background( + color = + if (room!!.type == "m.space") + MaterialTheme.colorScheme.tertiary + else + MaterialTheme.colorScheme.background ) .clip(RoundedCornerShape(12.dp)) .clickable { @@ -109,7 +127,7 @@ fun RoomItem(modifier: Modifier = Modifier, rid: String, navController: NavContr .padding(8.dp) ) { MXCImage( - room!!.avatarUrl ?: "", + avatarUrl, modifier = Modifier .padding(end = 4.dp) .height(52.dp)