diff --git a/app/src/main/java/com/rosetta/messenger/ui/chats/GroupSetupScreen.kt b/app/src/main/java/com/rosetta/messenger/ui/chats/GroupSetupScreen.kt index 0a02a5e..0e7973e 100644 --- a/app/src/main/java/com/rosetta/messenger/ui/chats/GroupSetupScreen.kt +++ b/app/src/main/java/com/rosetta/messenger/ui/chats/GroupSetupScreen.kt @@ -68,6 +68,7 @@ import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.core.view.WindowCompat +import androidx.core.view.WindowInsetsCompat import coil.compose.AsyncImage import coil.request.ImageRequest import com.rosetta.messenger.R @@ -149,6 +150,10 @@ fun GroupSetupScreen( val primaryTextColor = if (isDarkTheme) Color.White else Color.Black val secondaryTextColor = Color(0xFF8E8E93) 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) { 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() { if (isLoading) return errorText = null if (step == GroupSetupStep.DESCRIPTION) { step = GroupSetupStep.DETAILS } else { + dismissInputUi() onBack() } } @@ -402,7 +423,7 @@ fun GroupSetupScreen( Modifier .size(64.dp) .clip(CircleShape) - .background(sectionColor) + .background(groupAvatarCameraButtonColor) .clickable(enabled = !isLoading) { showPhotoPicker = true }, @@ -423,7 +444,7 @@ fun GroupSetupScreen( Icon( painter = TelegramIcons.Camera, contentDescription = "Set group avatar", - tint = accentColor, + tint = groupAvatarCameraIconColor, modifier = Modifier.size(24.dp) ) } @@ -713,6 +734,7 @@ fun GroupSetupScreen( avatarUriString = selectedAvatarUri ) } + dismissInputUi() openGroup( dialogPublicKey = result.dialogPublicKey, groupTitle = result.title.ifBlank { title.trim() } diff --git a/app/src/main/java/com/rosetta/messenger/ui/chats/SearchScreen.kt b/app/src/main/java/com/rosetta/messenger/ui/chats/SearchScreen.kt index 2fc23b4..2be8c01 100644 --- a/app/src/main/java/com/rosetta/messenger/ui/chats/SearchScreen.kt +++ b/app/src/main/java/com/rosetta/messenger/ui/chats/SearchScreen.kt @@ -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.graphics.Color +import androidx.compose.ui.hapticfeedback.HapticFeedbackType import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalFocusManager +import androidx.compose.ui.platform.LocalHapticFeedback import androidx.compose.ui.platform.LocalView import androidx.compose.ui.res.painterResource import com.rosetta.messenger.repository.AvatarRepository @@ -446,9 +448,22 @@ private fun ChatsTabContent( val dividerColor = remember(isDarkTheme) { 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) ─── var frequentContacts by remember { mutableStateOf>(emptyList()) } + var hiddenSuggestionKeys by remember(currentUserPublicKey) { mutableStateOf>(emptySet()) } + var frequentSuggestionToRemove by remember { mutableStateOf(null) } + + LaunchedEffect(currentUserPublicKey) { + hiddenSuggestionKeys = + suggestionsPrefs.getStringSet(hiddenSuggestionsKey, emptySet())?.toSet() ?: emptySet() + } LaunchedEffect(currentUserPublicKey) { if (currentUserPublicKey.isBlank()) return@LaunchedEffect @@ -478,6 +493,14 @@ private fun ChatsTabContent( } 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()) { if (searchQuery.isEmpty()) { @@ -486,10 +509,10 @@ private fun ChatsTabContent( modifier = Modifier.fillMaxSize() ) { // ─── Горизонтальный ряд частых контактов (как в Telegram) ─── - if (frequentContacts.isNotEmpty()) { + if (visibleFrequentContacts.isNotEmpty()) { item { FrequentContactsRow( - contacts = frequentContacts, + contacts = visibleFrequentContacts, avatarRepository = avatarRepository, isDarkTheme = isDarkTheme, textColor = textColor, @@ -504,6 +527,9 @@ private fun ChatsTabContent( ) RecentSearchesManager.addUser(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 +@OptIn(ExperimentalFoundationApi::class) private fun FrequentContactsRow( contacts: List, avatarRepository: AvatarRepository?, isDarkTheme: Boolean, textColor: Color, - onClick: (FrequentContact) -> Unit + onClick: (FrequentContact) -> Unit, + onLongPress: (FrequentContact) -> Unit ) { + val haptic = LocalHapticFeedback.current LazyRow( modifier = Modifier .fillMaxWidth() @@ -687,10 +779,15 @@ private fun FrequentContactsRow( Column( modifier = Modifier .width(72.dp) - .clickable( - indication = null, - interactionSource = remember { MutableInteractionSource() } - ) { onClick(contact) } + .combinedClickable( + indication = null, + interactionSource = remember { MutableInteractionSource() }, + onClick = { onClick(contact) }, + onLongClick = { + haptic.performHapticFeedback(HapticFeedbackType.LongPress) + onLongPress(contact) + } + ) .padding(vertical = 4.dp), horizontalAlignment = Alignment.CenterHorizontally ) {