Доработаны GroupSetup и Search: клавиатура, кнопка камеры и попап подсказок

This commit is contained in:
2026-03-09 16:06:49 +05:00
parent 5e908a6d0c
commit ce376d340f
2 changed files with 128 additions and 9 deletions

View File

@@ -68,6 +68,7 @@ import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import androidx.core.view.WindowCompat import androidx.core.view.WindowCompat
import androidx.core.view.WindowInsetsCompat
import coil.compose.AsyncImage import coil.compose.AsyncImage
import coil.request.ImageRequest import coil.request.ImageRequest
import com.rosetta.messenger.R import com.rosetta.messenger.R
@@ -149,6 +150,10 @@ fun GroupSetupScreen(
val primaryTextColor = if (isDarkTheme) Color.White else Color.Black val primaryTextColor = if (isDarkTheme) Color.White else Color.Black
val secondaryTextColor = Color(0xFF8E8E93) val secondaryTextColor = Color(0xFF8E8E93)
val accentColor = if (isDarkTheme) Color(0xFF5AA5FF) else Color(0xFF228BE6) val accentColor = if (isDarkTheme) Color(0xFF5AA5FF) else Color(0xFF228BE6)
val groupAvatarCameraButtonColor =
if (isDarkTheme) sectionColor else Color(0xFF8CC9F6)
val groupAvatarCameraIconColor =
if (isDarkTheme) accentColor else Color.White
androidx.compose.runtime.DisposableEffect(topSurfaceColor, view) { androidx.compose.runtime.DisposableEffect(topSurfaceColor, view) {
val window = (view.context as? Activity)?.window val window = (view.context as? Activity)?.window
@@ -208,12 +213,28 @@ fun GroupSetupScreen(
} }
} }
fun dismissInputUi() {
if (showEmojiKeyboard || coordinator.isEmojiVisible || coordinator.isEmojiBoxVisible) {
coordinator.closeEmoji(hideEmoji = { showEmojiKeyboard = false })
} else {
showEmojiKeyboard = false
}
focusManager.clearFocus(force = true)
keyboardController?.hide()
val imm = context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
imm.hideSoftInputFromWindow(view.windowToken, 0)
(context as? Activity)?.window?.let { window ->
WindowCompat.getInsetsController(window, view).hide(WindowInsetsCompat.Type.ime())
}
}
fun handleBack() { fun handleBack() {
if (isLoading) return if (isLoading) return
errorText = null errorText = null
if (step == GroupSetupStep.DESCRIPTION) { if (step == GroupSetupStep.DESCRIPTION) {
step = GroupSetupStep.DETAILS step = GroupSetupStep.DETAILS
} else { } else {
dismissInputUi()
onBack() onBack()
} }
} }
@@ -402,7 +423,7 @@ fun GroupSetupScreen(
Modifier Modifier
.size(64.dp) .size(64.dp)
.clip(CircleShape) .clip(CircleShape)
.background(sectionColor) .background(groupAvatarCameraButtonColor)
.clickable(enabled = !isLoading) { .clickable(enabled = !isLoading) {
showPhotoPicker = true showPhotoPicker = true
}, },
@@ -423,7 +444,7 @@ fun GroupSetupScreen(
Icon( Icon(
painter = TelegramIcons.Camera, painter = TelegramIcons.Camera,
contentDescription = "Set group avatar", contentDescription = "Set group avatar",
tint = accentColor, tint = groupAvatarCameraIconColor,
modifier = Modifier.size(24.dp) modifier = Modifier.size(24.dp)
) )
} }
@@ -713,6 +734,7 @@ fun GroupSetupScreen(
avatarUriString = selectedAvatarUri avatarUriString = selectedAvatarUri
) )
} }
dismissInputUi()
openGroup( openGroup(
dialogPublicKey = result.dialogPublicKey, dialogPublicKey = result.dialogPublicKey,
groupTitle = result.title.ifBlank { title.trim() } groupTitle = result.title.ifBlank { title.trim() }

View File

@@ -32,9 +32,11 @@ import androidx.compose.ui.draw.drawWithContent
import androidx.compose.ui.focus.FocusRequester import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.focus.focusRequester import androidx.compose.ui.focus.focusRequester
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.hapticfeedback.HapticFeedbackType
import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalFocusManager import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.platform.LocalHapticFeedback
import androidx.compose.ui.platform.LocalView import androidx.compose.ui.platform.LocalView
import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.painterResource
import com.rosetta.messenger.repository.AvatarRepository import com.rosetta.messenger.repository.AvatarRepository
@@ -446,9 +448,22 @@ private fun ChatsTabContent(
val dividerColor = remember(isDarkTheme) { val dividerColor = remember(isDarkTheme) {
if (isDarkTheme) Color(0xFF2A2A2A) else Color(0xFFE8E8E8) if (isDarkTheme) Color(0xFF2A2A2A) else Color(0xFFE8E8E8)
} }
val suggestionsPrefs = remember(currentUserPublicKey) {
context.getSharedPreferences("search_suggestions", Context.MODE_PRIVATE)
}
val hiddenSuggestionsKey = remember(currentUserPublicKey) {
"hidden_frequent_${currentUserPublicKey}"
}
// ─── Загрузка частых контактов из БД (top dialogs) ─── // ─── Загрузка частых контактов из БД (top dialogs) ───
var frequentContacts by remember { mutableStateOf<List<FrequentContact>>(emptyList()) } var frequentContacts by remember { mutableStateOf<List<FrequentContact>>(emptyList()) }
var hiddenSuggestionKeys by remember(currentUserPublicKey) { mutableStateOf<Set<String>>(emptySet()) }
var frequentSuggestionToRemove by remember { mutableStateOf<FrequentContact?>(null) }
LaunchedEffect(currentUserPublicKey) {
hiddenSuggestionKeys =
suggestionsPrefs.getStringSet(hiddenSuggestionsKey, emptySet())?.toSet() ?: emptySet()
}
LaunchedEffect(currentUserPublicKey) { LaunchedEffect(currentUserPublicKey) {
if (currentUserPublicKey.isBlank()) return@LaunchedEffect if (currentUserPublicKey.isBlank()) return@LaunchedEffect
@@ -478,6 +493,14 @@ private fun ChatsTabContent(
} catch (_: Exception) { } } catch (_: Exception) { }
} }
} }
val visibleFrequentContacts = remember(frequentContacts, hiddenSuggestionKeys) {
frequentContacts.filterNot { hiddenSuggestionKeys.contains(it.publicKey) }
}
fun removeFrequentSuggestion(publicKey: String) {
val updated = (hiddenSuggestionKeys + publicKey).toSet()
hiddenSuggestionKeys = updated
suggestionsPrefs.edit().putStringSet(hiddenSuggestionsKey, updated).apply()
}
Box(modifier = Modifier.fillMaxSize()) { Box(modifier = Modifier.fillMaxSize()) {
if (searchQuery.isEmpty()) { if (searchQuery.isEmpty()) {
@@ -486,10 +509,10 @@ private fun ChatsTabContent(
modifier = Modifier.fillMaxSize() modifier = Modifier.fillMaxSize()
) { ) {
// ─── Горизонтальный ряд частых контактов (как в Telegram) ─── // ─── Горизонтальный ряд частых контактов (как в Telegram) ───
if (frequentContacts.isNotEmpty()) { if (visibleFrequentContacts.isNotEmpty()) {
item { item {
FrequentContactsRow( FrequentContactsRow(
contacts = frequentContacts, contacts = visibleFrequentContacts,
avatarRepository = avatarRepository, avatarRepository = avatarRepository,
isDarkTheme = isDarkTheme, isDarkTheme = isDarkTheme,
textColor = textColor, textColor = textColor,
@@ -504,6 +527,9 @@ private fun ChatsTabContent(
) )
RecentSearchesManager.addUser(user) RecentSearchesManager.addUser(user)
onUserSelect(user) onUserSelect(user)
},
onLongPress = { contact ->
frequentSuggestionToRemove = contact
} }
) )
} }
@@ -653,6 +679,69 @@ private fun ChatsTabContent(
} }
) )
} }
frequentSuggestionToRemove?.let { contact ->
val scrimColor = if (isDarkTheme) Color.Black.copy(alpha = 0.42f) else Color.Black.copy(alpha = 0.24f)
val popupColor = if (isDarkTheme) Color(0xFF2C2C2E) else Color.White
val popupSecondaryText = if (isDarkTheme) Color(0xFFAEAEB2) else Color(0xFF6D6D72)
Box(
modifier =
Modifier
.matchParentSize()
.background(scrimColor)
.clickable(
indication = null,
interactionSource = remember { MutableInteractionSource() }
) { frequentSuggestionToRemove = null }
)
Surface(
modifier =
Modifier
.align(Alignment.BottomCenter)
.imePadding()
.padding(start = 20.dp, end = 20.dp, bottom = 14.dp),
color = popupColor,
shape = RoundedCornerShape(18.dp),
tonalElevation = 0.dp,
shadowElevation = 0.dp
) {
Column(
modifier = Modifier.padding(horizontal = 18.dp, vertical = 16.dp)
) {
Text(
text = "Remove suggestion",
color = textColor,
fontWeight = FontWeight.Bold,
fontSize = 17.sp
)
Spacer(modifier = Modifier.height(8.dp))
Text(
text = "Are you sure you want to remove ${contact.name} from suggestions?",
color = popupSecondaryText,
fontSize = 16.sp,
lineHeight = 22.sp
)
Spacer(modifier = Modifier.height(12.dp))
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.End
) {
TextButton(onClick = { frequentSuggestionToRemove = null }) {
Text("Cancel", color = AppPrimaryBlue, fontSize = 17.sp)
}
TextButton(
onClick = {
removeFrequentSuggestion(contact.publicKey)
frequentSuggestionToRemove = null
}
) {
Text("Remove", color = Color(0xFFFF5A5F), fontSize = 17.sp)
}
}
}
}
}
} }
} }
@@ -669,13 +758,16 @@ private data class FrequentContact(
) )
@Composable @Composable
@OptIn(ExperimentalFoundationApi::class)
private fun FrequentContactsRow( private fun FrequentContactsRow(
contacts: List<FrequentContact>, contacts: List<FrequentContact>,
avatarRepository: AvatarRepository?, avatarRepository: AvatarRepository?,
isDarkTheme: Boolean, isDarkTheme: Boolean,
textColor: Color, textColor: Color,
onClick: (FrequentContact) -> Unit onClick: (FrequentContact) -> Unit,
onLongPress: (FrequentContact) -> Unit
) { ) {
val haptic = LocalHapticFeedback.current
LazyRow( LazyRow(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
@@ -687,10 +779,15 @@ private fun FrequentContactsRow(
Column( Column(
modifier = Modifier modifier = Modifier
.width(72.dp) .width(72.dp)
.clickable( .combinedClickable(
indication = null, indication = null,
interactionSource = remember { MutableInteractionSource() } interactionSource = remember { MutableInteractionSource() },
) { onClick(contact) } onClick = { onClick(contact) },
onLongClick = {
haptic.performHapticFeedback(HapticFeedbackType.LongPress)
onLongPress(contact)
}
)
.padding(vertical = 4.dp), .padding(vertical = 4.dp),
horizontalAlignment = Alignment.CenterHorizontally horizontalAlignment = Alignment.CenterHorizontally
) { ) {