feat: Implement avatar handling and display across chat and account screens

This commit is contained in:
2026-01-24 01:14:25 +05:00
parent 1367864008
commit 10c78e6231
7 changed files with 135 additions and 87 deletions

View File

@@ -16,11 +16,15 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.rosetta.messenger.database.RosettaDatabase
import com.rosetta.messenger.repository.AvatarRepository
import com.rosetta.messenger.ui.components.AvatarImage
import com.rosetta.messenger.ui.onboarding.PrimaryBlue
data class AccountInfo(
@@ -258,6 +262,12 @@ private fun AccountListItem(
val avatarColor = getAccountColor(account.name)
val context = LocalContext.current
val avatarRepository = remember(account.publicKey) {
val database = RosettaDatabase.getDatabase(context)
AvatarRepository(context, database.avatarDao(), account.publicKey)
}
var visible by remember { mutableStateOf(false) }
LaunchedEffect(Unit) {
@@ -288,20 +298,12 @@ private fun AccountListItem(
verticalAlignment = Alignment.CenterVertically
) {
// Avatar
Box(
modifier = Modifier
.size(48.dp)
.clip(CircleShape)
.background(avatarColor.copy(alpha = 0.2f)),
contentAlignment = Alignment.Center
) {
Text(
text = account.initials,
fontSize = 18.sp,
fontWeight = FontWeight.SemiBold,
color = avatarColor
)
}
AvatarImage(
publicKey = account.publicKey,
avatarRepository = avatarRepository,
size = 48.dp,
isDarkTheme = isDarkTheme
)
Spacer(modifier = Modifier.width(16.dp))

View File

@@ -41,8 +41,11 @@ import com.rosetta.messenger.crypto.CryptoManager
import com.rosetta.messenger.data.AccountManager
import com.rosetta.messenger.data.DecryptedAccount
import com.rosetta.messenger.data.EncryptedAccount
import com.rosetta.messenger.database.RosettaDatabase
import com.rosetta.messenger.network.ProtocolManager
import com.rosetta.messenger.network.ProtocolState
import com.rosetta.messenger.repository.AvatarRepository
import com.rosetta.messenger.ui.components.AvatarImage
import com.rosetta.messenger.ui.chats.getAvatarColor
import com.rosetta.messenger.ui.chats.getAvatarText
import com.rosetta.messenger.ui.chats.utils.getInitials
@@ -387,22 +390,16 @@ fun UnlockScreen(
) {
// Avatar
if (selectedAccount != null) {
val avatarColors =
getAvatarColor(selectedAccount!!.publicKey, isDarkTheme)
Box(
modifier =
Modifier.size(48.dp)
.clip(CircleShape)
.background(avatarColors.backgroundColor),
contentAlignment = Alignment.Center
) {
Text(
text = getInitials(selectedAccount!!.name),
fontSize = 18.sp,
fontWeight = FontWeight.Bold,
color = avatarColors.textColor
)
val database = RosettaDatabase.getDatabase(context)
val avatarRepository = remember(selectedAccount!!.publicKey) {
AvatarRepository(context, database.avatarDao(), selectedAccount!!.publicKey)
}
AvatarImage(
publicKey = selectedAccount!!.publicKey,
avatarRepository = avatarRepository,
size = 48.dp,
isDarkTheme = isDarkTheme
)
}
Spacer(modifier = Modifier.width(12.dp))
@@ -572,25 +569,16 @@ fun UnlockScreen(
verticalAlignment = Alignment.CenterVertically
) {
// Avatar
val avatarColors =
getAvatarColor(account.publicKey, isDarkTheme)
Box(
modifier =
Modifier.size(40.dp)
.clip(CircleShape)
.background(
avatarColors
.backgroundColor
),
contentAlignment = Alignment.Center
) {
Text(
text = getInitials(account.name),
fontSize = 14.sp,
fontWeight = FontWeight.Bold,
color = avatarColors.textColor
)
val database = RosettaDatabase.getDatabase(context)
val avatarRepository = remember(account.publicKey) {
AvatarRepository(context, database.avatarDao(), account.publicKey)
}
AvatarImage(
publicKey = account.publicKey,
avatarRepository = avatarRepository,
size = 40.dp,
isDarkTheme = isDarkTheme
)
Spacer(modifier = Modifier.width(12.dp))

View File

@@ -37,6 +37,7 @@ import com.rosetta.messenger.data.RecentSearchesManager
import com.rosetta.messenger.network.ProtocolManager
import com.rosetta.messenger.network.ProtocolState
import com.rosetta.messenger.ui.components.AppleEmojiText
import com.rosetta.messenger.ui.components.AvatarImage
import com.rosetta.messenger.ui.onboarding.PrimaryBlue
import java.text.SimpleDateFormat
import java.util.*
@@ -147,6 +148,7 @@ fun ChatsListScreen(
onNewChat: () -> Unit,
onUserSelect: (com.rosetta.messenger.network.SearchUser) -> Unit = {},
chatsViewModel: ChatsListViewModel = androidx.lifecycle.viewmodel.compose.viewModel(),
avatarRepository: com.rosetta.messenger.repository.AvatarRepository? = null,
onLogout: () -> Unit
) {
// Theme transition state
@@ -398,7 +400,7 @@ fun ChatsListScreen(
)
) {
Column {
// Avatar with border
// Avatar - используем AvatarImage
Box(
modifier =
Modifier.size(72.dp)
@@ -414,29 +416,15 @@ fun ChatsListScreen(
)
.padding(
3.dp
)
.clip(
CircleShape
)
.background(
avatarColors
.backgroundColor
),
contentAlignment =
Alignment.Center
) {
Text(
text =
getAvatarText(
accountPublicKey
),
fontSize = 26.sp,
fontWeight =
FontWeight
.Bold,
color =
avatarColors
.textColor
AvatarImage(
publicKey = accountPublicKey,
avatarRepository = avatarRepository,
size = 66.dp,
isDarkTheme = isDarkTheme
)
}
@@ -996,6 +984,8 @@ fun ChatsListScreen(
isBlocked,
isSavedMessages =
isSavedMessages,
avatarRepository =
avatarRepository,
onClick = {
val user =
chatsViewModel
@@ -1469,6 +1459,7 @@ fun SwipeableDialogItem(
isTyping: Boolean = false,
isBlocked: Boolean = false,
isSavedMessages: Boolean = false,
avatarRepository: com.rosetta.messenger.repository.AvatarRepository? = null,
onClick: () -> Unit,
onDelete: () -> Unit = {},
onBlock: () -> Unit = {},
@@ -1615,6 +1606,7 @@ fun SwipeableDialogItem(
dialog = dialog,
isDarkTheme = isDarkTheme,
isTyping = isTyping,
avatarRepository = avatarRepository,
onClick = onClick
)
@@ -1635,6 +1627,7 @@ fun DialogItemContent(
dialog: DialogUiModel,
isDarkTheme: Boolean,
isTyping: Boolean = false,
avatarRepository: com.rosetta.messenger.repository.AvatarRepository? = null,
onClick: () -> Unit
) {
// 🔥 ОПТИМИЗАЦИЯ: Кешируем цвета и строки
@@ -1721,31 +1714,28 @@ fun DialogItemContent(
// Avatar container with online indicator
Box(modifier = Modifier.size(56.dp)) {
// Avatar
Box(
modifier =
Modifier.fillMaxSize()
.clip(CircleShape)
.background(
if (dialog.isSavedMessages) PrimaryBlue
else avatarColors.backgroundColor
),
contentAlignment = Alignment.Center
) {
if (dialog.isSavedMessages) {
if (dialog.isSavedMessages) {
Box(
modifier =
Modifier.fillMaxSize()
.clip(CircleShape)
.background(PrimaryBlue),
contentAlignment = Alignment.Center
) {
Icon(
Icons.Default.Bookmark,
contentDescription = null,
tint = Color.White,
modifier = Modifier.size(24.dp)
)
} else {
Text(
text = initials,
color = avatarColors.textColor,
fontWeight = FontWeight.SemiBold,
fontSize = 18.sp
)
}
} else {
com.rosetta.messenger.ui.components.AvatarImage(
publicKey = dialog.opponentKey,
avatarRepository = avatarRepository,
size = 56.dp,
isDarkTheme = isDarkTheme
)
}
// Online indicator - зелёный кружок с белой обводкой

View File

@@ -62,9 +62,15 @@ fun AvatarImage(
// Состояние для bitmap
var bitmap by remember(avatars) { mutableStateOf<Bitmap?>(null) }
// Логируем для отладки
LaunchedEffect(publicKey, avatars) {
android.util.Log.d("AvatarImage", "📸 publicKey=${publicKey.take(16)}... avatars=${avatars.size} repository=${avatarRepository != null}")
}
// Декодируем первый аватар
LaunchedEffect(avatars) {
bitmap = if (avatars.isNotEmpty()) {
android.util.Log.d("AvatarImage", "🔄 Decoding avatar for ${publicKey.take(16)}...")
withContext(Dispatchers.IO) {
AvatarFileManager.base64ToBitmap(avatars.first().base64Data)
}