feat: Add VerifiedBadge and online status to user profile header
This commit is contained in:
@@ -613,6 +613,22 @@ class MessageRepository private constructor(private val context: Context) {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Наблюдать за онлайн статусом пользователя
|
||||
*/
|
||||
fun observeUserOnlineStatus(publicKey: String): Flow<Pair<Boolean, Long>> {
|
||||
val account = currentAccount ?: return flowOf(false to 0L)
|
||||
|
||||
return dialogDao.observeOnlineStatus(account, publicKey)
|
||||
.map { info ->
|
||||
if (info != null) {
|
||||
(info.isOnline == 1) to info.lastSeen
|
||||
} else {
|
||||
false to 0L
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Обновить информацию о пользователе в диалоге (имя, username, verified)
|
||||
* Вызывается когда приходит ответ на PacketSearch
|
||||
|
||||
@@ -478,6 +478,22 @@ interface DialogDao {
|
||||
""")
|
||||
suspend fun updateOnlineStatus(account: String, opponentKey: String, isOnline: Int, lastSeen: Long)
|
||||
|
||||
/**
|
||||
* Получить онлайн статус пользователя
|
||||
*/
|
||||
@Query("""
|
||||
SELECT is_online, last_seen
|
||||
FROM dialogs
|
||||
WHERE account = :account AND opponent_key = :opponentKey
|
||||
LIMIT 1
|
||||
""")
|
||||
fun observeOnlineStatus(account: String, opponentKey: String): Flow<OnlineStatusInfo?>
|
||||
|
||||
data class OnlineStatusInfo(
|
||||
@ColumnInfo(name = "is_online") val isOnline: Int,
|
||||
@ColumnInfo(name = "last_seen") val lastSeen: Long
|
||||
)
|
||||
|
||||
/**
|
||||
* Удалить диалог
|
||||
*/
|
||||
|
||||
@@ -15,7 +15,9 @@ import androidx.compose.material.icons.filled.MoreVert
|
||||
import androidx.compose.material.icons.outlined.Block
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.*
|
||||
import com.rosetta.messenger.data.MessageRepository
|
||||
import com.rosetta.messenger.ui.onboarding.PrimaryBlue
|
||||
import com.rosetta.messenger.ui.components.VerifiedBadge
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
@@ -60,6 +62,13 @@ fun OtherProfileScreen(
|
||||
val avatarColors = getAvatarColor(user.publicKey, isDarkTheme)
|
||||
val context = LocalContext.current
|
||||
|
||||
// 🟢 Наблюдаем за онлайн статусом пользователя в реальном времени
|
||||
val messageRepository = remember { MessageRepository.getInstance(context) }
|
||||
val onlineStatus by messageRepository.observeUserOnlineStatus(user.publicKey)
|
||||
.collectAsState(initial = false to 0L)
|
||||
val isOnline = onlineStatus.first
|
||||
val lastSeen = onlineStatus.second
|
||||
|
||||
// Scroll state for collapsing header animation
|
||||
val density = LocalDensity.current
|
||||
val statusBarHeight = WindowInsets.statusBars.asPaddingValues().calculateTopPadding()
|
||||
@@ -153,6 +162,9 @@ fun OtherProfileScreen(
|
||||
name = user.title.ifEmpty { "Unknown User" },
|
||||
username = user.username,
|
||||
publicKey = user.publicKey,
|
||||
verified = user.verified,
|
||||
isOnline = isOnline,
|
||||
lastSeen = lastSeen,
|
||||
avatarColors = avatarColors,
|
||||
collapseProgress = collapseProgress,
|
||||
onBack = onBack,
|
||||
@@ -172,6 +184,9 @@ private fun CollapsingOtherProfileHeader(
|
||||
name: String,
|
||||
username: String,
|
||||
publicKey: String,
|
||||
verified: Int,
|
||||
isOnline: Boolean,
|
||||
lastSeen: Long,
|
||||
avatarColors: AvatarColors,
|
||||
collapseProgress: Float,
|
||||
onBack: () -> Unit,
|
||||
@@ -309,7 +324,7 @@ private fun CollapsingOtherProfileHeader(
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════
|
||||
// 📝 TEXT BLOCK - Name + Online, always centered
|
||||
// 📝 TEXT BLOCK - Name + Verified + Online, always centered
|
||||
// ═══════════════════════════════════════════════════════════
|
||||
Column(
|
||||
modifier = Modifier
|
||||
@@ -323,24 +338,37 @@ private fun CollapsingOtherProfileHeader(
|
||||
},
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
Text(
|
||||
text = name,
|
||||
fontSize = nameFontSize,
|
||||
fontWeight = FontWeight.SemiBold,
|
||||
color = Color.White,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
modifier = Modifier.widthIn(max = 220.dp),
|
||||
textAlign = TextAlign.Center
|
||||
)
|
||||
// Name + Verified Badge
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.Center
|
||||
) {
|
||||
Text(
|
||||
text = name,
|
||||
fontSize = nameFontSize,
|
||||
fontWeight = FontWeight.SemiBold,
|
||||
color = Color.White,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
textAlign = TextAlign.Center
|
||||
)
|
||||
|
||||
if (verified > 0) {
|
||||
Spacer(modifier = Modifier.width(4.dp))
|
||||
VerifiedBadge(
|
||||
verified = verified,
|
||||
size = (nameFontSize.value * 0.8f).toInt()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(2.dp))
|
||||
|
||||
// Online text - always centered
|
||||
// Online/Offline status
|
||||
Text(
|
||||
text = "online",
|
||||
text = if (isOnline) "online" else "offline",
|
||||
fontSize = onlineFontSize,
|
||||
color = Color(0xFF4CAF50)
|
||||
color = if (isOnline) Color(0xFF4CAF50) else Color.White.copy(alpha = 0.6f)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user