feat: Enhance group chat functionality and UI improvements
- Added support for group action system messages in MessageBubble. - Implemented group invite handling with inline cards for joining groups. - Updated MessageBubble to display group sender labels and admin badges. - Enhanced image decryption logic for group attachments. - Modified BlurredAvatarBackground to load system avatars based on public keys. - Improved SwipeBackContainer with layer management for better swipe effects. - Updated VerifiedBadge to use dynamic icons based on user verification status. - Added new drawable resource for admin badge icon.
This commit is contained in:
@@ -42,6 +42,8 @@ import com.rosetta.messenger.ui.auth.DeviceConfirmScreen
|
||||
import com.rosetta.messenger.ui.chats.ChatDetailScreen
|
||||
import com.rosetta.messenger.ui.chats.ChatsListScreen
|
||||
import com.rosetta.messenger.ui.chats.ConnectionLogsScreen
|
||||
import com.rosetta.messenger.ui.chats.GroupInfoScreen
|
||||
import com.rosetta.messenger.ui.chats.GroupSetupScreen
|
||||
import com.rosetta.messenger.ui.chats.RequestsListScreen
|
||||
import com.rosetta.messenger.ui.chats.SearchScreen
|
||||
import com.rosetta.messenger.ui.components.OptimizedEmojiCache
|
||||
@@ -498,6 +500,8 @@ sealed class Screen {
|
||||
data object Profile : Screen()
|
||||
data object Requests : Screen()
|
||||
data object Search : Screen()
|
||||
data object GroupSetup : Screen()
|
||||
data class GroupInfo(val group: SearchUser) : Screen()
|
||||
data class ChatDetail(val user: SearchUser) : Screen()
|
||||
data class OtherProfile(val user: SearchUser) : Screen()
|
||||
data object Updates : Screen()
|
||||
@@ -600,10 +604,15 @@ fun MainScreen(
|
||||
val isProfileVisible by remember { derivedStateOf { navStack.any { it is Screen.Profile } } }
|
||||
val isRequestsVisible by remember { derivedStateOf { navStack.any { it is Screen.Requests } } }
|
||||
val isSearchVisible by remember { derivedStateOf { navStack.any { it is Screen.Search } } }
|
||||
val isGroupSetupVisible by remember { derivedStateOf { navStack.any { it is Screen.GroupSetup } } }
|
||||
val chatDetailScreen by remember {
|
||||
derivedStateOf { navStack.filterIsInstance<Screen.ChatDetail>().lastOrNull() }
|
||||
}
|
||||
val selectedUser = chatDetailScreen?.user
|
||||
val groupInfoScreen by remember {
|
||||
derivedStateOf { navStack.filterIsInstance<Screen.GroupInfo>().lastOrNull() }
|
||||
}
|
||||
val selectedGroup = groupInfoScreen?.group
|
||||
val otherProfileScreen by remember {
|
||||
derivedStateOf { navStack.filterIsInstance<Screen.OtherProfile>().lastOrNull() }
|
||||
}
|
||||
@@ -650,7 +659,10 @@ fun MainScreen(
|
||||
}
|
||||
}
|
||||
fun popChatAndChildren() {
|
||||
navStack = navStack.filterNot { it is Screen.ChatDetail || it is Screen.OtherProfile }
|
||||
navStack =
|
||||
navStack.filterNot {
|
||||
it is Screen.ChatDetail || it is Screen.OtherProfile || it is Screen.GroupInfo
|
||||
}
|
||||
}
|
||||
|
||||
// ProfileViewModel для логов
|
||||
@@ -689,7 +701,7 @@ fun MainScreen(
|
||||
// 🔥 Простая навигация с swipe back
|
||||
Box(modifier = Modifier.fillMaxSize()) {
|
||||
// Base layer - chats list (всегда видимый, чтобы его было видно при свайпе)
|
||||
SwipeBackBackgroundEffect(modifier = Modifier.fillMaxSize()) {
|
||||
SwipeBackBackgroundEffect(modifier = Modifier.fillMaxSize(), layer = 0) {
|
||||
ChatsListScreen(
|
||||
isDarkTheme = isDarkTheme,
|
||||
accountName = accountName,
|
||||
@@ -702,7 +714,7 @@ fun MainScreen(
|
||||
onToggleTheme = onToggleTheme,
|
||||
onProfileClick = { pushScreen(Screen.Profile) },
|
||||
onNewGroupClick = {
|
||||
// TODO: Navigate to new group
|
||||
pushScreen(Screen.GroupSetup)
|
||||
},
|
||||
onContactsClick = {
|
||||
// TODO: Navigate to contacts
|
||||
@@ -754,7 +766,8 @@ fun MainScreen(
|
||||
SwipeBackContainer(
|
||||
isVisible = isRequestsVisible,
|
||||
onBack = { navStack = navStack.filterNot { it is Screen.Requests } },
|
||||
isDarkTheme = isDarkTheme
|
||||
isDarkTheme = isDarkTheme,
|
||||
layer = 1
|
||||
) {
|
||||
RequestsListScreen(
|
||||
isDarkTheme = isDarkTheme,
|
||||
@@ -783,7 +796,9 @@ fun MainScreen(
|
||||
SwipeBackContainer(
|
||||
isVisible = isProfileVisible,
|
||||
onBack = { popProfileAndChildren() },
|
||||
isDarkTheme = isDarkTheme
|
||||
isDarkTheme = isDarkTheme,
|
||||
layer = 1,
|
||||
propagateBackgroundProgress = false
|
||||
) {
|
||||
// Экран профиля
|
||||
ProfileScreen(
|
||||
@@ -816,7 +831,8 @@ fun MainScreen(
|
||||
SwipeBackContainer(
|
||||
isVisible = isSafetyVisible,
|
||||
onBack = { navStack = navStack.filterNot { it is Screen.Safety } },
|
||||
isDarkTheme = isDarkTheme
|
||||
isDarkTheme = isDarkTheme,
|
||||
layer = 2
|
||||
) {
|
||||
SafetyScreen(
|
||||
isDarkTheme = isDarkTheme,
|
||||
@@ -833,7 +849,8 @@ fun MainScreen(
|
||||
SwipeBackContainer(
|
||||
isVisible = isBackupVisible,
|
||||
onBack = { navStack = navStack.filterNot { it is Screen.Backup } },
|
||||
isDarkTheme = isDarkTheme
|
||||
isDarkTheme = isDarkTheme,
|
||||
layer = 3
|
||||
) {
|
||||
BackupScreen(
|
||||
isDarkTheme = isDarkTheme,
|
||||
@@ -878,7 +895,8 @@ fun MainScreen(
|
||||
SwipeBackContainer(
|
||||
isVisible = isThemeVisible,
|
||||
onBack = { navStack = navStack.filterNot { it is Screen.Theme } },
|
||||
isDarkTheme = isDarkTheme
|
||||
isDarkTheme = isDarkTheme,
|
||||
layer = 2
|
||||
) {
|
||||
ThemeScreen(
|
||||
isDarkTheme = isDarkTheme,
|
||||
@@ -891,7 +909,8 @@ fun MainScreen(
|
||||
SwipeBackContainer(
|
||||
isVisible = isAppearanceVisible,
|
||||
onBack = { navStack = navStack.filterNot { it is Screen.Appearance } },
|
||||
isDarkTheme = isDarkTheme
|
||||
isDarkTheme = isDarkTheme,
|
||||
layer = 2
|
||||
) {
|
||||
com.rosetta.messenger.ui.settings.AppearanceScreen(
|
||||
isDarkTheme = isDarkTheme,
|
||||
@@ -912,7 +931,8 @@ fun MainScreen(
|
||||
SwipeBackContainer(
|
||||
isVisible = isUpdatesVisible,
|
||||
onBack = { navStack = navStack.filterNot { it is Screen.Updates } },
|
||||
isDarkTheme = isDarkTheme
|
||||
isDarkTheme = isDarkTheme,
|
||||
layer = 2
|
||||
) {
|
||||
UpdatesScreen(
|
||||
isDarkTheme = isDarkTheme,
|
||||
@@ -938,6 +958,7 @@ fun MainScreen(
|
||||
isVisible = selectedUser != null,
|
||||
onBack = { popChatAndChildren() },
|
||||
isDarkTheme = isDarkTheme,
|
||||
layer = 1,
|
||||
swipeEnabled = !isChatSwipeLocked,
|
||||
propagateBackgroundProgress = false
|
||||
) {
|
||||
@@ -959,6 +980,9 @@ fun MainScreen(
|
||||
pushScreen(Screen.OtherProfile(user))
|
||||
}
|
||||
},
|
||||
onGroupInfoClick = { groupUser ->
|
||||
pushScreen(Screen.GroupInfo(groupUser))
|
||||
},
|
||||
onNavigateToChat = { forwardUser ->
|
||||
// 📨 Forward: переход в выбранный чат с полными данными
|
||||
navStack =
|
||||
@@ -973,10 +997,54 @@ fun MainScreen(
|
||||
}
|
||||
}
|
||||
|
||||
var isGroupInfoSwipeEnabled by remember { mutableStateOf(true) }
|
||||
LaunchedEffect(selectedGroup?.publicKey) {
|
||||
isGroupInfoSwipeEnabled = true
|
||||
}
|
||||
|
||||
SwipeBackContainer(
|
||||
isVisible = selectedGroup != null,
|
||||
onBack = { navStack = navStack.filterNot { it is Screen.GroupInfo } },
|
||||
isDarkTheme = isDarkTheme,
|
||||
layer = 2,
|
||||
swipeEnabled = isGroupInfoSwipeEnabled,
|
||||
propagateBackgroundProgress = false
|
||||
) {
|
||||
selectedGroup?.let { groupUser ->
|
||||
GroupInfoScreen(
|
||||
groupUser = groupUser,
|
||||
currentUserPublicKey = accountPublicKey,
|
||||
currentUserPrivateKey = accountPrivateKey,
|
||||
isDarkTheme = isDarkTheme,
|
||||
avatarRepository = avatarRepository,
|
||||
onBack = { navStack = navStack.filterNot { it is Screen.GroupInfo } },
|
||||
onMemberClick = { member ->
|
||||
if (member.publicKey == accountPublicKey) {
|
||||
pushScreen(Screen.Profile)
|
||||
} else {
|
||||
pushScreen(Screen.OtherProfile(member))
|
||||
}
|
||||
},
|
||||
onSwipeBackEnabledChanged = { enabled ->
|
||||
isGroupInfoSwipeEnabled = enabled
|
||||
},
|
||||
onGroupLeft = {
|
||||
navStack =
|
||||
navStack.filterNot {
|
||||
it is Screen.GroupInfo ||
|
||||
(it is Screen.ChatDetail &&
|
||||
it.user.publicKey == groupUser.publicKey)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
SwipeBackContainer(
|
||||
isVisible = isSearchVisible,
|
||||
onBack = { navStack = navStack.filterNot { it is Screen.Search } },
|
||||
isDarkTheme = isDarkTheme
|
||||
isDarkTheme = isDarkTheme,
|
||||
layer = 1
|
||||
) {
|
||||
// Экран поиска
|
||||
SearchScreen(
|
||||
@@ -996,10 +1064,30 @@ fun MainScreen(
|
||||
)
|
||||
}
|
||||
|
||||
SwipeBackContainer(
|
||||
isVisible = isGroupSetupVisible,
|
||||
onBack = { navStack = navStack.filterNot { it is Screen.GroupSetup } },
|
||||
isDarkTheme = isDarkTheme,
|
||||
layer = 1
|
||||
) {
|
||||
GroupSetupScreen(
|
||||
isDarkTheme = isDarkTheme,
|
||||
accountPublicKey = accountPublicKey,
|
||||
accountPrivateKey = accountPrivateKey,
|
||||
onBack = { navStack = navStack.filterNot { it is Screen.GroupSetup } },
|
||||
onGroupOpened = { groupUser ->
|
||||
navStack =
|
||||
navStack.filterNot { it is Screen.GroupSetup } +
|
||||
Screen.ChatDetail(groupUser)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
SwipeBackContainer(
|
||||
isVisible = isLogsVisible,
|
||||
onBack = { navStack = navStack.filterNot { it is Screen.Logs } },
|
||||
isDarkTheme = isDarkTheme
|
||||
isDarkTheme = isDarkTheme,
|
||||
layer = 2
|
||||
) {
|
||||
com.rosetta.messenger.ui.settings.ProfileLogsScreen(
|
||||
isDarkTheme = isDarkTheme,
|
||||
@@ -1012,7 +1100,8 @@ fun MainScreen(
|
||||
SwipeBackContainer(
|
||||
isVisible = isCrashLogsVisible,
|
||||
onBack = { navStack = navStack.filterNot { it is Screen.CrashLogs } },
|
||||
isDarkTheme = isDarkTheme
|
||||
isDarkTheme = isDarkTheme,
|
||||
layer = 1
|
||||
) {
|
||||
CrashLogsScreen(
|
||||
onBackClick = { navStack = navStack.filterNot { it is Screen.CrashLogs } }
|
||||
@@ -1022,7 +1111,8 @@ fun MainScreen(
|
||||
SwipeBackContainer(
|
||||
isVisible = isConnectionLogsVisible,
|
||||
onBack = { navStack = navStack.filterNot { it is Screen.ConnectionLogs } },
|
||||
isDarkTheme = isDarkTheme
|
||||
isDarkTheme = isDarkTheme,
|
||||
layer = 1
|
||||
) {
|
||||
ConnectionLogsScreen(
|
||||
isDarkTheme = isDarkTheme,
|
||||
@@ -1039,7 +1129,9 @@ fun MainScreen(
|
||||
isVisible = selectedOtherUser != null,
|
||||
onBack = { navStack = navStack.filterNot { it is Screen.OtherProfile } },
|
||||
isDarkTheme = isDarkTheme,
|
||||
swipeEnabled = isOtherProfileSwipeEnabled
|
||||
layer = 2,
|
||||
swipeEnabled = isOtherProfileSwipeEnabled,
|
||||
propagateBackgroundProgress = false
|
||||
) {
|
||||
selectedOtherUser?.let { currentOtherUser ->
|
||||
OtherProfileScreen(
|
||||
@@ -1066,7 +1158,8 @@ fun MainScreen(
|
||||
SwipeBackContainer(
|
||||
isVisible = isBiometricVisible,
|
||||
onBack = { navStack = navStack.filterNot { it is Screen.Biometric } },
|
||||
isDarkTheme = isDarkTheme
|
||||
isDarkTheme = isDarkTheme,
|
||||
layer = 2
|
||||
) {
|
||||
val biometricManager = remember {
|
||||
com.rosetta.messenger.biometric.BiometricAuthManager(context)
|
||||
|
||||
Reference in New Issue
Block a user