Refactor code structure for improved readability and maintainability

This commit is contained in:
k1ngsterr1
2026-01-08 21:48:22 +05:00
parent 5b298a37b3
commit b877d6fa73
8 changed files with 51542 additions and 380 deletions

View File

@@ -6,24 +6,35 @@ import androidx.compose.foundation.*
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.pager.HorizontalPager
import androidx.compose.foundation.pager.rememberPagerState
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.*
import androidx.compose.material.icons.outlined.*
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.runtime.saveable.rememberSaveable
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.graphics.vector.ImageVector
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.airbnb.lottie.compose.*
import com.rosetta.messenger.R
import com.rosetta.messenger.ui.onboarding.PrimaryBlue
import kotlinx.coroutines.launch
import java.text.SimpleDateFormat
import java.util.*
import androidx.compose.runtime.Immutable
@Immutable
data class Chat(
val id: String,
val name: String,
@@ -32,50 +43,82 @@ data class Chat(
val unreadCount: Int = 0,
val isOnline: Boolean = false,
val publicKey: String,
val isSavedMessages: Boolean = false
val isSavedMessages: Boolean = false,
val isPinned: Boolean = false
)
// Beautiful avatar colors
// Avatar colors like in React Native app
private val avatarColors = listOf(
Color(0xFF5E9FFF) to Color(0xFFE8F1FF), // Blue
Color(0xFFFF7EB3) to Color(0xFFFFEEF4), // Pink
Color(0xFF7B68EE) to Color(0xFFF0EDFF), // Purple
Color(0xFF50C878) to Color(0xFFE8F8EE), // Green
Color(0xFFFF6B6B) to Color(0xFFFFEEEE), // Red
Color(0xFF4ECDC4) to Color(0xFFE8F8F7), // Teal
Color(0xFFFFB347) to Color(0xFFFFF5E8), // Orange
Color(0xFFBA55D3) to Color(0xFFF8EEFF) // Orchid
Color(0xFF5E9FFF), // Blue
Color(0xFFFF7EB3), // Pink
Color(0xFF7B68EE), // Purple
Color(0xFF50C878), // Green
Color(0xFFFF6B6B), // Red
Color(0xFF4ECDC4), // Teal
Color(0xFFFFB347), // Orange
Color(0xFFBA55D3) // Orchid
)
fun getAvatarColor(name: String, isDark: Boolean): Pair<Color, Color> {
val index = name.hashCode().mod(avatarColors.size).let { if (it < 0) it + avatarColors.size else it }
val (primary, light) = avatarColors[index]
return if (isDark) primary to primary.copy(alpha = 0.2f) else primary to light
}
// Cache для цветов аватаров - избегаем вычисления каждый раз
private val avatarColorCache = mutableMapOf<String, Color>()
fun getInitials(name: String): String {
val words = name.trim().split(Regex("\\s+")).filter { it.isNotEmpty() }
return when {
words.isEmpty() -> "??"
words.size == 1 -> words[0].take(2).uppercase()
else -> "${words[0].first()}${words[1].first()}".uppercase()
fun getAvatarColor(name: String): Color {
return avatarColorCache.getOrPut(name) {
val index = name.hashCode().mod(avatarColors.size).let {
if (it < 0) it + avatarColors.size else it
}
avatarColors[index]
}
}
// Cache для инициалов
private val initialsCache = mutableMapOf<String, String>()
fun getInitials(name: String): String {
return initialsCache.getOrPut(name) {
val words = name.trim().split(Regex("\\s+")).filter { it.isNotEmpty() }
when {
words.isEmpty() -> "??"
words.size == 1 -> words[0].take(2).uppercase()
else -> "${words[0].first()}${words[1].first()}".uppercase()
}
}
}
// Drawer menu item
data class DrawerMenuItem(
val icon: ImageVector,
val title: String,
val onClick: () -> Unit,
val badge: Int? = null
)
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun ChatsListScreen(
isDarkTheme: Boolean,
chats: List<Chat>,
onChatClick: (Chat) -> Unit,
onNewChat: () -> Unit,
accountName: String,
accountPhone: String,
onToggleTheme: () -> Unit,
onProfileClick: () -> Unit,
onSavedMessagesClick: () -> Unit
onNewGroupClick: () -> Unit,
onContactsClick: () -> Unit,
onCallsClick: () -> Unit,
onSavedMessagesClick: () -> Unit,
onSettingsClick: () -> Unit,
onInviteFriendsClick: () -> Unit,
onSearchClick: () -> Unit,
onNewChat: () -> Unit,
onLogout: () -> Unit
) {
val backgroundColor = if (isDarkTheme) Color(0xFF1E1E1E) else Color(0xFFFFFFFF)
val backgroundColor = if (isDarkTheme) Color(0xFF1A1A1A) else Color(0xFFFFFFFF)
val drawerBackgroundColor = if (isDarkTheme) Color(0xFF212121) else Color(0xFFFFFFFF)
val textColor = if (isDarkTheme) Color.White else Color.Black
val secondaryTextColor = if (isDarkTheme) Color(0xFF8E8E93) else Color(0xFF666666)
val surfaceColor = if (isDarkTheme) Color(0xFF2A2A2A) else Color(0xFFF5F5F5)
val dividerColor = if (isDarkTheme) Color(0xFF3A3A3A) else Color(0xFFE8E8E8)
val drawerState = rememberDrawerState(initialValue = DrawerValue.Closed)
val scope = rememberCoroutineScope()
var visible by remember { mutableStateOf(false) }
@@ -83,378 +126,494 @@ fun ChatsListScreen(
visible = true
}
Scaffold(
topBar = {
AnimatedVisibility(
visible = visible,
enter = fadeIn(tween(400)) + slideInVertically(
initialOffsetY = { -it },
animationSpec = tween(400)
)
// Drawer menu items
val menuItems = listOf(
DrawerMenuItem(
icon = Icons.Outlined.Person,
title = "My Profile",
onClick = onProfileClick
),
DrawerMenuItem(
icon = Icons.Outlined.Group,
title = "New Group",
onClick = onNewGroupClick
),
DrawerMenuItem(
icon = Icons.Outlined.Settings,
title = "Settings",
onClick = onSettingsClick
)
)
ModalNavigationDrawer(
drawerState = drawerState,
drawerContent = {
ModalDrawerSheet(
modifier = Modifier.width(300.dp),
drawerContainerColor = drawerBackgroundColor
) {
TopAppBar(
title = {
// Header with logo and theme toggle
Box(
modifier = Modifier
.fillMaxWidth()
.background(if (isDarkTheme) Color(0xFF2A2A2A) else Color(0xFFF5F5F5))
.padding(16.dp)
) {
Column {
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.Top
) {
// Avatar with initials
Box(
modifier = Modifier
.size(64.dp)
.clip(CircleShape)
.background(PrimaryBlue),
contentAlignment = Alignment.Center
) {
Text(
text = getInitials(accountName),
fontSize = 24.sp,
fontWeight = FontWeight.Bold,
color = Color.White
)
}
// Theme toggle
IconButton(
onClick = onToggleTheme
) {
Icon(
if (isDarkTheme) Icons.Default.LightMode else Icons.Default.DarkMode,
contentDescription = "Toggle theme",
tint = textColor
)
}
}
Spacer(modifier = Modifier.height(16.dp))
// Account name
Text(
"Chats",
fontWeight = FontWeight.Bold,
fontSize = 28.sp
)
},
actions = {
IconButton(onClick = onNewChat) {
Icon(
Icons.Default.Edit,
contentDescription = "New Chat",
tint = PrimaryBlue
)
}
IconButton(onClick = onProfileClick) {
Icon(
Icons.Default.Person,
contentDescription = "Profile",
tint = textColor
)
}
},
colors = TopAppBarDefaults.topAppBarColors(
containerColor = backgroundColor,
titleContentColor = textColor
)
)
}
},
containerColor = backgroundColor
) { paddingValues ->
LazyColumn(
modifier = Modifier
.fillMaxSize()
.padding(paddingValues),
contentPadding = PaddingValues(vertical = 8.dp)
) {
// Saved Messages section
item {
AnimatedVisibility(
visible = visible,
enter = fadeIn(tween(400, delayMillis = 100)) + slideInHorizontally(
initialOffsetX = { -50 },
animationSpec = tween(400, delayMillis = 100)
)
) {
SavedMessagesItem(
isDarkTheme = isDarkTheme,
onClick = onSavedMessagesClick
)
}
}
// Chat items
items(chats, key = { it.id }) { chat ->
val index = chats.indexOf(chat)
AnimatedVisibility(
visible = visible,
enter = fadeIn(tween(400, delayMillis = 150 + (index * 50))) + slideInHorizontally(
initialOffsetX = { -50 },
animationSpec = tween(400, delayMillis = 150 + (index * 50))
)
) {
ChatItem(
chat = chat,
isDarkTheme = isDarkTheme,
onClick = { onChatClick(chat) }
)
}
}
// Empty state
if (chats.isEmpty()) {
item {
AnimatedVisibility(
visible = visible,
enter = fadeIn(tween(400, delayMillis = 200)) + scaleIn(
initialScale = 0.9f,
animationSpec = tween(400, delayMillis = 200)
)
) {
EmptyChatsState(
isDarkTheme = isDarkTheme,
onNewChat = onNewChat
text = accountName,
fontSize = 18.sp,
fontWeight = FontWeight.SemiBold,
color = textColor
)
}
}
// Menu items
menuItems.forEach { item ->
DrawerItem(
icon = item.icon,
title = item.title,
onClick = {
scope.launch { drawerState.close() }
item.onClick()
},
isDarkTheme = isDarkTheme
)
}
}
}
) {
Scaffold(
topBar = {
AnimatedVisibility(
visible = visible,
enter = fadeIn(tween(400)) + slideInVertically(
initialOffsetY = { -it },
animationSpec = tween(400)
)
) {
TopAppBar(
navigationIcon = {
IconButton(
onClick = { scope.launch { drawerState.open() } }
) {
Icon(
Icons.Default.Menu,
contentDescription = "Menu",
tint = textColor
)
}
},
title = {
// Stories / Title area
Row(
verticalAlignment = Alignment.CenterVertically
) {
// User story avatar placeholder
Box(
modifier = Modifier
.size(36.dp)
.clip(CircleShape)
.background(
brush = androidx.compose.ui.graphics.Brush.linearGradient(
colors = listOf(
Color(0xFF405DE6),
Color(0xFFC13584),
Color(0xFFFD1D1D)
)
)
)
.padding(2.dp)
.clip(CircleShape)
.background(backgroundColor),
contentAlignment = Alignment.Center
) {
Box(
modifier = Modifier
.size(30.dp)
.clip(CircleShape)
.background(getAvatarColor(accountName)),
contentAlignment = Alignment.Center
) {
Text(
text = getInitials(accountName),
fontSize = 12.sp,
fontWeight = FontWeight.Bold,
color = Color.White
)
}
}
Spacer(modifier = Modifier.width(12.dp))
Text(
"Rosetta",
fontWeight = FontWeight.Bold,
fontSize = 20.sp
)
}
},
actions = {
IconButton(onClick = onSearchClick) {
Icon(
Icons.Default.Search,
contentDescription = "Search",
tint = textColor
)
}
},
colors = TopAppBarDefaults.topAppBarColors(
containerColor = backgroundColor,
titleContentColor = textColor
)
)
}
},
floatingActionButton = {
AnimatedVisibility(
visible = visible,
enter = fadeIn(tween(500, delayMillis = 300)) + scaleIn(
initialScale = 0.5f,
animationSpec = tween(500, delayMillis = 300)
)
) {
FloatingActionButton(
onClick = onNewChat,
containerColor = PrimaryBlue,
contentColor = Color.White,
shape = CircleShape
) {
Icon(
Icons.Default.Edit,
contentDescription = "New Chat"
)
}
}
},
containerColor = backgroundColor
) { paddingValues ->
// Empty state with Lottie animation
EmptyChatsState(
isDarkTheme = isDarkTheme,
modifier = Modifier
.fillMaxSize()
.padding(paddingValues)
)
}
}
}
@Composable
private fun SavedMessagesItem(
private fun DrawerItem(
icon: ImageVector,
title: String,
onClick: () -> Unit,
isDarkTheme: Boolean,
onClick: () -> Unit
badge: Int? = null
) {
val textColor = if (isDarkTheme) Color.White else Color.Black
val secondaryTextColor = if (isDarkTheme) Color(0xFF8E8E93) else Color(0xFF666666)
Row(
modifier = Modifier
.fillMaxWidth()
.clickable(onClick = onClick)
.padding(horizontal = 16.dp, vertical = 12.dp),
.padding(horizontal = 20.dp, vertical = 14.dp),
verticalAlignment = Alignment.CenterVertically
) {
// Saved Messages icon
Box(
modifier = Modifier
.size(56.dp)
.clip(CircleShape)
.background(PrimaryBlue),
contentAlignment = Alignment.Center
) {
Icon(
Icons.Default.Bookmark,
contentDescription = null,
tint = Color.White,
modifier = Modifier.size(28.dp)
)
}
Icon(
icon,
contentDescription = title,
tint = if (isDarkTheme) Color(0xFF8E8E93) else Color(0xFF666666),
modifier = Modifier.size(24.dp)
)
Spacer(modifier = Modifier.width(12.dp))
Spacer(modifier = Modifier.width(20.dp))
Column(modifier = Modifier.weight(1f)) {
Text(
text = "Saved Messages",
fontSize = 17.sp,
fontWeight = FontWeight.SemiBold,
color = textColor
)
Text(
text = "Your personal cloud storage",
fontSize = 14.sp,
color = secondaryTextColor,
maxLines = 1,
overflow = TextOverflow.Ellipsis
)
Text(
text = title,
fontSize = 16.sp,
color = textColor,
modifier = Modifier.weight(1f)
)
badge?.let {
Box(
modifier = Modifier
.clip(CircleShape)
.background(PrimaryBlue)
.padding(horizontal = 8.dp, vertical = 2.dp),
contentAlignment = Alignment.Center
) {
Text(
text = it.toString(),
fontSize = 12.sp,
fontWeight = FontWeight.SemiBold,
color = Color.White
)
}
}
}
Divider(
modifier = Modifier.padding(start = 84.dp),
color = if (isDarkTheme) Color(0xFF3A3A3A) else Color(0xFFE8E8E8),
thickness = 0.5.dp
)
}
@Composable
private fun ChatItem(
private fun EmptyChatsState(
isDarkTheme: Boolean,
modifier: Modifier = Modifier
) {
val secondaryTextColor = if (isDarkTheme) Color(0xFF8E8E93) else Color(0xFF666666)
// Lottie animation
val composition by rememberLottieComposition(
LottieCompositionSpec.RawRes(R.raw.letter)
)
val progress by animateLottieCompositionAsState(
composition = composition,
iterations = 1
)
Column(
modifier = modifier
.fillMaxSize(),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
) {
// Lottie animation
LottieAnimation(
composition = composition,
progress = { progress },
modifier = Modifier.size(150.dp)
)
Spacer(modifier = Modifier.height(24.dp))
Text(
text = "No conversations yet",
fontSize = 18.sp,
fontWeight = FontWeight.SemiBold,
color = secondaryTextColor,
textAlign = TextAlign.Center
)
Spacer(modifier = Modifier.height(8.dp))
Text(
text = "Start a new conversation to get started",
fontSize = 15.sp,
color = secondaryTextColor,
textAlign = TextAlign.Center
)
}
}
// Chat item for list
@Composable
fun ChatItem(
chat: Chat,
isDarkTheme: Boolean,
onClick: () -> Unit
) {
val textColor = if (isDarkTheme) Color.White else Color.Black
val secondaryTextColor = if (isDarkTheme) Color(0xFF8E8E93) else Color(0xFF666666)
val dividerColor = if (isDarkTheme) Color(0xFF3A3A3A) else Color(0xFFE8E8E8)
val (avatarTextColor, avatarBgColor) = getAvatarColor(chat.name, isDarkTheme)
val avatarColor = getAvatarColor(chat.name)
val initials = getInitials(chat.name)
Row(
modifier = Modifier
.fillMaxWidth()
.clickable(onClick = onClick)
.padding(horizontal = 16.dp, vertical = 12.dp),
verticalAlignment = Alignment.CenterVertically
) {
// Avatar
Box(
Column {
Row(
modifier = Modifier
.size(56.dp)
.clip(CircleShape)
.background(avatarBgColor),
contentAlignment = Alignment.Center
.fillMaxWidth()
.clickable(onClick = onClick)
.padding(horizontal = 16.dp, vertical = 12.dp),
verticalAlignment = Alignment.CenterVertically
) {
Text(
text = initials,
fontSize = 20.sp,
fontWeight = FontWeight.SemiBold,
color = avatarTextColor
)
// Online indicator
if (chat.isOnline) {
Box(
modifier = Modifier
.align(Alignment.BottomEnd)
.size(14.dp)
.clip(CircleShape)
.background(if (isDarkTheme) Color(0xFF1E1E1E) else Color.White)
.padding(2.dp)
.clip(CircleShape)
.background(Color(0xFF4CAF50))
)
}
}
Spacer(modifier = Modifier.width(12.dp))
Column(modifier = Modifier.weight(1f)) {
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
// Avatar
Box(
modifier = Modifier
.size(56.dp)
.clip(CircleShape)
.background(avatarColor),
contentAlignment = Alignment.Center
) {
Text(
text = chat.name,
fontSize = 17.sp,
text = initials,
fontSize = 20.sp,
fontWeight = FontWeight.SemiBold,
color = textColor,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
modifier = Modifier.weight(1f)
color = Color.White
)
Text(
text = formatTime(chat.lastMessageTime),
fontSize = 13.sp,
color = if (chat.unreadCount > 0) PrimaryBlue else secondaryTextColor
)
}
Spacer(modifier = Modifier.height(4.dp))
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
Text(
text = chat.lastMessage,
fontSize = 15.sp,
color = secondaryTextColor,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
modifier = Modifier.weight(1f)
)
if (chat.unreadCount > 0) {
// Online indicator
if (chat.isOnline) {
Box(
modifier = Modifier
.padding(start = 8.dp)
.align(Alignment.BottomEnd)
.offset(x = 2.dp, y = 2.dp)
.size(16.dp)
.clip(CircleShape)
.background(PrimaryBlue)
.padding(horizontal = 8.dp, vertical = 2.dp),
contentAlignment = Alignment.Center
) {
Text(
text = if (chat.unreadCount > 99) "99+" else chat.unreadCount.toString(),
fontSize = 12.sp,
fontWeight = FontWeight.SemiBold,
color = Color.White
.background(if (isDarkTheme) Color(0xFF1A1A1A) else Color.White)
.padding(2.dp)
.clip(CircleShape)
.background(Color(0xFF4CAF50))
)
}
}
Spacer(modifier = Modifier.width(12.dp))
Column(modifier = Modifier.weight(1f)) {
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
Text(
text = chat.name,
fontSize = 16.sp,
fontWeight = FontWeight.SemiBold,
color = textColor,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
modifier = Modifier.weight(1f)
)
Row(verticalAlignment = Alignment.CenterVertically) {
// Read status
Icon(
Icons.Default.DoneAll,
contentDescription = null,
tint = PrimaryBlue,
modifier = Modifier.size(16.dp)
)
Spacer(modifier = Modifier.width(4.dp))
Text(
text = formatTime(chat.lastMessageTime),
fontSize = 13.sp,
color = secondaryTextColor
)
}
}
Spacer(modifier = Modifier.height(4.dp))
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
Text(
text = chat.lastMessage,
fontSize = 14.sp,
color = secondaryTextColor,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
modifier = Modifier.weight(1f)
)
Row(verticalAlignment = Alignment.CenterVertically) {
// Pin icon
if (chat.isPinned) {
Icon(
Icons.Default.PushPin,
contentDescription = "Pinned",
tint = secondaryTextColor,
modifier = Modifier
.size(16.dp)
.padding(end = 4.dp)
)
}
// Unread badge
if (chat.unreadCount > 0) {
Box(
modifier = Modifier
.clip(CircleShape)
.background(PrimaryBlue)
.padding(horizontal = 8.dp, vertical = 2.dp),
contentAlignment = Alignment.Center
) {
Text(
text = if (chat.unreadCount > 99) "99+" else chat.unreadCount.toString(),
fontSize = 12.sp,
fontWeight = FontWeight.SemiBold,
color = Color.White
)
}
}
}
}
}
}
Divider(
modifier = Modifier.padding(start = 84.dp),
color = dividerColor,
thickness = 0.5.dp
)
}
Divider(
modifier = Modifier.padding(start = 84.dp),
color = if (isDarkTheme) Color(0xFF3A3A3A) else Color(0xFFE8E8E8),
thickness = 0.5.dp
)
}
@Composable
private fun EmptyChatsState(
isDarkTheme: Boolean,
onNewChat: () -> Unit
) {
val textColor = if (isDarkTheme) Color.White else Color.Black
val secondaryTextColor = if (isDarkTheme) Color(0xFF8E8E93) else Color(0xFF666666)
Column(
modifier = Modifier
.fillMaxWidth()
.padding(32.dp),
horizontalAlignment = Alignment.CenterHorizontally
) {
Spacer(modifier = Modifier.height(60.dp))
Box(
modifier = Modifier
.size(80.dp)
.clip(CircleShape)
.background(PrimaryBlue.copy(alpha = 0.1f)),
contentAlignment = Alignment.Center
) {
Icon(
Icons.Default.ChatBubbleOutline,
contentDescription = null,
tint = PrimaryBlue,
modifier = Modifier.size(40.dp)
)
}
Spacer(modifier = Modifier.height(24.dp))
Text(
text = "No chats yet",
fontSize = 20.sp,
fontWeight = FontWeight.SemiBold,
color = textColor
)
Spacer(modifier = Modifier.height(8.dp))
Text(
text = "Start a conversation with\nsomeone new",
fontSize = 15.sp,
color = secondaryTextColor,
textAlign = androidx.compose.ui.text.style.TextAlign.Center
)
Spacer(modifier = Modifier.height(24.dp))
Button(
onClick = onNewChat,
colors = ButtonDefaults.buttonColors(
containerColor = PrimaryBlue,
contentColor = Color.White
),
shape = RoundedCornerShape(12.dp)
) {
Icon(
Icons.Default.Add,
contentDescription = null,
modifier = Modifier.size(20.dp)
)
Spacer(modifier = Modifier.width(8.dp))
Text("Start Chat", fontSize = 16.sp)
}
}
}
// Cache для SimpleDateFormat - создание дорогостоящее
private val timeFormatCache = java.lang.ThreadLocal.withInitial { SimpleDateFormat("HH:mm", Locale.getDefault()) }
private val weekFormatCache = java.lang.ThreadLocal.withInitial { SimpleDateFormat("EEE", Locale.getDefault()) }
private val monthFormatCache = java.lang.ThreadLocal.withInitial { SimpleDateFormat("MMM d", Locale.getDefault()) }
private val yearFormatCache = java.lang.ThreadLocal.withInitial { SimpleDateFormat("dd.MM.yy", Locale.getDefault()) }
private fun formatTime(date: Date): String {
val now = Calendar.getInstance()
val messageTime = Calendar.getInstance().apply { time = date }
return when {
// Today
now.get(Calendar.DATE) == messageTime.get(Calendar.DATE) -> {
SimpleDateFormat("HH:mm", Locale.getDefault()).format(date)
timeFormatCache.get()?.format(date) ?: ""
}
// Yesterday
now.get(Calendar.DATE) - messageTime.get(Calendar.DATE) == 1 -> {
"Yesterday"
}
// This week
now.get(Calendar.WEEK_OF_YEAR) == messageTime.get(Calendar.WEEK_OF_YEAR) -> {
SimpleDateFormat("EEE", Locale.getDefault()).format(date)
weekFormatCache.get()?.format(date) ?: ""
}
// This year
now.get(Calendar.YEAR) == messageTime.get(Calendar.YEAR) -> {
SimpleDateFormat("MMM d", Locale.getDefault()).format(date)
monthFormatCache.get()?.format(date) ?: ""
}
// Other
else -> {
SimpleDateFormat("dd.MM.yy", Locale.getDefault()).format(date)
yearFormatCache.get()?.format(date) ?: ""
}
}
}