Доработаны GroupSetup и Search: клавиатура, кнопка камеры и попап подсказок
This commit is contained in:
@@ -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() }
|
||||||
|
|||||||
@@ -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
|
||||||
) {
|
) {
|
||||||
|
|||||||
Reference in New Issue
Block a user