feat: Optimize emoji picker with chunk loading and improve typing indicator in chat list
This commit is contained in:
@@ -266,7 +266,9 @@ fun AppleEmojiText(
|
||||
modifier: Modifier = Modifier,
|
||||
color: androidx.compose.ui.graphics.Color = androidx.compose.ui.graphics.Color.White,
|
||||
fontSize: androidx.compose.ui.unit.TextUnit = androidx.compose.ui.unit.TextUnit.Unspecified,
|
||||
fontWeight: androidx.compose.ui.text.font.FontWeight? = null
|
||||
fontWeight: androidx.compose.ui.text.font.FontWeight? = null,
|
||||
maxLines: Int = Int.MAX_VALUE,
|
||||
overflow: android.text.TextUtils.TruncateAt? = null
|
||||
) {
|
||||
val fontSizeValue = if (fontSize == androidx.compose.ui.unit.TextUnit.Unspecified) 15f
|
||||
else fontSize.value
|
||||
@@ -291,12 +293,22 @@ fun AppleEmojiText(
|
||||
setTextSize(fontSizeValue)
|
||||
minimumHeight = (minHeight * ctx.resources.displayMetrics.density).toInt()
|
||||
setTypeface(typeface, typefaceStyle)
|
||||
// 🔥 Поддержка maxLines и ellipsize
|
||||
setMaxLines(maxLines)
|
||||
if (overflow != null) {
|
||||
ellipsize = overflow
|
||||
}
|
||||
}
|
||||
},
|
||||
update = { view ->
|
||||
view.setTextWithEmojis(text)
|
||||
view.setTextColor(color.toArgb())
|
||||
view.setTypeface(view.typeface, typefaceStyle)
|
||||
// 🔥 Обновляем maxLines и ellipsize
|
||||
view.maxLines = maxLines
|
||||
if (overflow != null) {
|
||||
view.ellipsize = overflow
|
||||
}
|
||||
},
|
||||
modifier = modifier
|
||||
)
|
||||
|
||||
@@ -35,6 +35,7 @@ import coil.request.CachePolicy
|
||||
import coil.request.ImageRequest
|
||||
import com.rosetta.messenger.ui.onboarding.PrimaryBlue
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
@@ -554,18 +555,7 @@ fun CategoryButton(
|
||||
isDarkTheme: Boolean = true,
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
val interactionSource = remember { MutableInteractionSource() }
|
||||
val isPressed by interactionSource.collectIsPressedAsState()
|
||||
|
||||
val scale by animateFloatAsState(
|
||||
targetValue = if (isPressed) 0.9f else 1f,
|
||||
animationSpec = spring(
|
||||
dampingRatio = Spring.DampingRatioMediumBouncy,
|
||||
stiffness = Spring.StiffnessHigh
|
||||
),
|
||||
label = "categoryScale"
|
||||
)
|
||||
|
||||
// 🚀 Убрали анимацию scale для производительности
|
||||
val backgroundColor = if (isSelected) PrimaryBlue.copy(alpha = 0.2f) else Color.Transparent
|
||||
val iconTint = if (isSelected) PrimaryBlue
|
||||
else if (isDarkTheme) Color.White.copy(alpha = 0.6f)
|
||||
@@ -574,14 +564,9 @@ fun CategoryButton(
|
||||
Box(
|
||||
modifier = modifier
|
||||
.size(40.dp)
|
||||
.scale(scale)
|
||||
.clip(CircleShape)
|
||||
.background(backgroundColor)
|
||||
.clickable(
|
||||
interactionSource = interactionSource,
|
||||
indication = null,
|
||||
onClick = onClick
|
||||
),
|
||||
.clickable(onClick = onClick),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
Icon(
|
||||
@@ -614,20 +599,32 @@ fun AppleEmojiPickerPanel(
|
||||
}
|
||||
}
|
||||
|
||||
// Текущие эмодзи для выбранной категории - используем derivedStateOf для оптимизации
|
||||
val currentEmojis by remember {
|
||||
derivedStateOf {
|
||||
if (EmojiCache.isLoaded) {
|
||||
EmojiCache.getEmojisForCategory(selectedCategory.key)
|
||||
} else {
|
||||
emptyList()
|
||||
}
|
||||
// 🚀 Chunk loading: показываем эмодзи порциями для плавности
|
||||
var loadedCount by remember { mutableStateOf(40) } // Начинаем с 40 (5 рядов)
|
||||
|
||||
// Текущие эмодзи для выбранной категории
|
||||
val allEmojis = remember(selectedCategory, EmojiCache.isLoaded) {
|
||||
if (EmojiCache.isLoaded) {
|
||||
EmojiCache.getEmojisForCategory(selectedCategory.key)
|
||||
} else {
|
||||
emptyList()
|
||||
}
|
||||
}
|
||||
|
||||
// 🚀 Убираем анимацию скролла для мгновенного переключения категорий
|
||||
// 🚀 При смене категории сбрасываем чанки и постепенно догружаем
|
||||
LaunchedEffect(selectedCategory) {
|
||||
loadedCount = 40 // Сразу показываем 40 эмодзи
|
||||
gridState.scrollToItem(0)
|
||||
// Догружаем остальные чанками
|
||||
while (loadedCount < allEmojis.size) {
|
||||
delay(32) // 2 фрейма
|
||||
loadedCount = minOf(loadedCount + 24, allEmojis.size)
|
||||
}
|
||||
}
|
||||
|
||||
// Отображаемые эмодзи (с chunk loading)
|
||||
val displayedEmojis = remember(allEmojis, loadedCount) {
|
||||
allEmojis.take(loadedCount)
|
||||
}
|
||||
|
||||
val panelBackground = if (isDarkTheme) Color(0xFF1C1C1E) else Color(0xFFF2F2F7)
|
||||
@@ -680,7 +677,7 @@ fun AppleEmojiPickerPanel(
|
||||
strokeWidth = 2.dp
|
||||
)
|
||||
}
|
||||
} else if (currentEmojis.isEmpty()) {
|
||||
} else if (displayedEmojis.isEmpty()) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
@@ -694,7 +691,7 @@ fun AppleEmojiPickerPanel(
|
||||
)
|
||||
}
|
||||
} else {
|
||||
// 🚀 Оптимизированная LazyVerticalGrid для быстрого рендеринга
|
||||
// 🚀 Оптимизированная LazyVerticalGrid с chunk loading
|
||||
LazyVerticalGrid(
|
||||
state = gridState,
|
||||
columns = GridCells.Fixed(8),
|
||||
@@ -706,7 +703,7 @@ fun AppleEmojiPickerPanel(
|
||||
contentPadding = PaddingValues(start = 12.dp, end = 12.dp, top = 4.dp, bottom = 16.dp)
|
||||
) {
|
||||
items(
|
||||
items = currentEmojis,
|
||||
items = displayedEmojis,
|
||||
key = { emoji -> emoji },
|
||||
contentType = { "emoji" }
|
||||
) { unified ->
|
||||
|
||||
Reference in New Issue
Block a user