feat: Implement Recent Searches functionality in SearchScreen for improved user experience
This commit is contained in:
@@ -494,6 +494,8 @@ fun SetPasswordScreen(
|
||||
|
||||
accountManager.saveAccount(account)
|
||||
accountManager.setCurrentAccount(keyPair.publicKey)
|
||||
// Save as last logged account for next time
|
||||
accountManager.setLastLoggedPublicKey(keyPair.publicKey)
|
||||
|
||||
// 🔌 Connect to server and authenticate
|
||||
val privateKeyHash = CryptoManager.generatePrivateKeyHash(keyPair.privateKey)
|
||||
|
||||
@@ -587,6 +587,8 @@ fun UnlockScreen(
|
||||
ProtocolManager.authenticate(account.publicKey, privateKeyHash)
|
||||
|
||||
accountManager.setCurrentAccount(account.publicKey)
|
||||
// Save as last logged account for next time
|
||||
accountManager.setLastLoggedPublicKey(account.publicKey)
|
||||
onUnlocked(decryptedAccount)
|
||||
|
||||
} catch (e: Exception) {
|
||||
|
||||
@@ -4,6 +4,9 @@ import androidx.compose.animation.*
|
||||
import androidx.compose.animation.core.*
|
||||
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.shape.CircleShape
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.*
|
||||
@@ -11,12 +14,15 @@ import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.focus.FocusRequester
|
||||
import androidx.compose.ui.focus.focusRequester
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import com.rosetta.messenger.data.RecentSearchesManager
|
||||
import com.rosetta.messenger.network.ProtocolState
|
||||
import com.rosetta.messenger.network.SearchUser
|
||||
|
||||
@@ -37,7 +43,8 @@ fun SearchScreen(
|
||||
onBackClick: () -> Unit,
|
||||
onUserSelect: (SearchUser) -> Unit
|
||||
) {
|
||||
val backgroundColor = if (isDarkTheme) Color(0xFF121212) else Color(0xFFF8F9FA)
|
||||
// Цвета ТОЧНО как в ChatsListScreen
|
||||
val backgroundColor = if (isDarkTheme) Color(0xFF1A1A1A) else Color(0xFFFFFFFF)
|
||||
val textColor = if (isDarkTheme) Color.White else Color(0xFF1a1a1a)
|
||||
val secondaryTextColor = if (isDarkTheme) Color(0xFFB0B0B0) else Color(0xFF6c757d)
|
||||
val surfaceColor = if (isDarkTheme) Color(0xFF1E1E1E) else Color.White
|
||||
@@ -48,6 +55,9 @@ fun SearchScreen(
|
||||
val searchResults by searchViewModel.searchResults.collectAsState()
|
||||
val isSearching by searchViewModel.isSearching.collectAsState()
|
||||
|
||||
// Recent users (не текстовые запросы, а пользователи)
|
||||
val recentUsers by RecentSearchesManager.recentUsers.collectAsState()
|
||||
|
||||
// Устанавливаем privateKeyHash
|
||||
LaunchedEffect(privateKeyHash) {
|
||||
if (privateKeyHash.isNotEmpty()) {
|
||||
@@ -69,8 +79,7 @@ fun SearchScreen(
|
||||
// Кастомный header с полем ввода на всю ширину
|
||||
Surface(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
color = backgroundColor,
|
||||
shadowElevation = 4.dp
|
||||
color = backgroundColor
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
@@ -157,20 +166,163 @@ fun SearchScreen(
|
||||
},
|
||||
containerColor = backgroundColor
|
||||
) { paddingValues ->
|
||||
// Контент - результаты поиска
|
||||
// Контент - показываем recent users если поле пустое, иначе результаты
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.padding(paddingValues)
|
||||
) {
|
||||
SearchResultsList(
|
||||
searchResults = searchResults,
|
||||
isSearching = isSearching,
|
||||
currentUserPublicKey = currentUserPublicKey,
|
||||
isDarkTheme = isDarkTheme,
|
||||
onUserClick = { user ->
|
||||
onUserSelect(user)
|
||||
if (searchQuery.isEmpty() && recentUsers.isNotEmpty()) {
|
||||
// Recent Users с аватарками
|
||||
LazyColumn(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
contentPadding = PaddingValues(vertical = 8.dp)
|
||||
) {
|
||||
item {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp, vertical = 12.dp),
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Text(
|
||||
"Recent",
|
||||
fontSize = 14.sp,
|
||||
fontWeight = FontWeight.SemiBold,
|
||||
color = secondaryTextColor
|
||||
)
|
||||
TextButton(onClick = { RecentSearchesManager.clearAll() }) {
|
||||
Text(
|
||||
"Clear All",
|
||||
fontSize = 13.sp,
|
||||
color = PrimaryBlue
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
items(recentUsers, key = { it.publicKey }) { user ->
|
||||
RecentUserItem(
|
||||
user = user,
|
||||
isDarkTheme = isDarkTheme,
|
||||
textColor = textColor,
|
||||
secondaryTextColor = secondaryTextColor,
|
||||
onClick = {
|
||||
RecentSearchesManager.addUser(user)
|
||||
onUserSelect(user)
|
||||
},
|
||||
onRemove = {
|
||||
RecentSearchesManager.removeUser(user.publicKey)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Search Results
|
||||
SearchResultsList(
|
||||
searchResults = searchResults,
|
||||
isSearching = isSearching,
|
||||
currentUserPublicKey = currentUserPublicKey,
|
||||
isDarkTheme = isDarkTheme,
|
||||
onUserClick = { user ->
|
||||
// Сохраняем пользователя в историю
|
||||
RecentSearchesManager.addUser(user)
|
||||
onUserSelect(user)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun RecentUserItem(
|
||||
user: SearchUser,
|
||||
isDarkTheme: Boolean,
|
||||
textColor: Color,
|
||||
secondaryTextColor: Color,
|
||||
onClick: () -> Unit,
|
||||
onRemove: () -> Unit
|
||||
) {
|
||||
val displayName = user.title.ifEmpty {
|
||||
user.username.ifEmpty {
|
||||
user.publicKey.take(8) + "..."
|
||||
}
|
||||
}
|
||||
// Используем getInitials из ChatsListScreen
|
||||
val initials = getInitials(displayName)
|
||||
|
||||
// Используем getAvatarColor из ChatsListScreen для правильных цветов
|
||||
val avatarColors = getAvatarColor(user.publicKey, isDarkTheme)
|
||||
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.clickable(onClick = onClick)
|
||||
.padding(horizontal = 16.dp, vertical = 10.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
// Avatar
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.size(48.dp)
|
||||
.clip(CircleShape)
|
||||
.background(avatarColors.backgroundColor),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
Text(
|
||||
text = initials,
|
||||
fontSize = 16.sp,
|
||||
fontWeight = FontWeight.SemiBold,
|
||||
color = avatarColors.textColor
|
||||
)
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.width(12.dp))
|
||||
|
||||
// Name and username
|
||||
Column(modifier = Modifier.weight(1f)) {
|
||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||
Text(
|
||||
text = displayName,
|
||||
fontSize = 16.sp,
|
||||
fontWeight = FontWeight.Medium,
|
||||
color = textColor,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis
|
||||
)
|
||||
if (user.verified != 0) {
|
||||
Spacer(modifier = Modifier.width(4.dp))
|
||||
Icon(
|
||||
Icons.Default.Verified,
|
||||
contentDescription = "Verified",
|
||||
tint = PrimaryBlue,
|
||||
modifier = Modifier.size(16.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
if (user.username.isNotEmpty()) {
|
||||
Text(
|
||||
text = "@${user.username}",
|
||||
fontSize = 14.sp,
|
||||
color = secondaryTextColor,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Remove button
|
||||
IconButton(
|
||||
onClick = onRemove,
|
||||
modifier = Modifier.size(40.dp)
|
||||
) {
|
||||
Icon(
|
||||
Icons.Default.Close,
|
||||
contentDescription = "Remove",
|
||||
tint = secondaryTextColor,
|
||||
modifier = Modifier.size(20.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user