feat: Add reply panel visibility state and optimize dialog confirmation handling

This commit is contained in:
k1ngsterr1
2026-01-15 21:46:51 +05:00
parent 22a17e5fec
commit e7e6d23631
3 changed files with 170 additions and 225 deletions

View File

@@ -17,6 +17,9 @@ android {
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
vectorDrawables { useSupportLibrary = true } vectorDrawables { useSupportLibrary = true }
// Optimize Lottie animations
manifestPlaceholders["enableLottieOptimizations"] = "true"
} }
signingConfigs { signingConfigs {

View File

@@ -573,74 +573,38 @@ fun ChatsListScreen(
dialogToDelete?.let { dialog -> dialogToDelete?.let { dialog ->
AlertDialog( AlertDialog(
onDismissRequest = { dialogToDelete = null }, onDismissRequest = { dialogToDelete = null },
icon = { containerColor = if (isDarkTheme) Color(0xFF2C2C2E) else Color.White,
Icon(
imageVector = Icons.Default.Delete,
contentDescription = null,
tint = PrimaryBlue,
modifier = Modifier.size(32.dp)
)
},
title = { title = {
Text( Text(
text = "Delete conversation?", "Delete Chat",
fontWeight = FontWeight.SemiBold, fontWeight = FontWeight.Bold,
fontSize = 18.sp, color = textColor
textAlign = TextAlign.Center,
modifier = Modifier.fillMaxWidth()
) )
}, },
text = { text = {
Column( Text(
modifier = Modifier.fillMaxWidth(), "Are you sure you want to delete this chat? This action cannot be undone.",
horizontalAlignment = Alignment.CenterHorizontally color = secondaryTextColor
) { )
val displayName = dialog.opponentTitle.ifEmpty { dialog.opponentKey.take(8) } },
Text( confirmButton = {
text = "All messages with $displayName will be permanently deleted. This action cannot be undone.", TextButton(
fontSize = 15.sp, onClick = {
color = if (isDarkTheme) Color(0xFFAAAAAA) else Color(0xFF666666), val opponentKey = dialog.opponentKey
textAlign = TextAlign.Center, dialogToDelete = null
modifier = Modifier.fillMaxWidth() scope.launch {
) chatsViewModel.deleteDialog(opponentKey)
Spacer(modifier = Modifier.height(16.dp))
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.Center,
verticalAlignment = Alignment.CenterVertically
) {
TextButton(
onClick = { dialogToDelete = null },
modifier = Modifier.weight(1f)
) {
Text("Cancel", fontWeight = FontWeight.Medium)
}
Spacer(modifier = Modifier.width(8.dp))
TextButton(
onClick = {
scope.launch {
chatsViewModel.deleteDialog(dialog.opponentKey)
dialogToDelete = null
}
},
colors = ButtonDefaults.textButtonColors(
contentColor = Color(0xFFFF3B30)
),
modifier = Modifier.weight(1f)
) {
Text("Delete", fontWeight = FontWeight.SemiBold)
} }
} }
) {
Text("Delete", color = Color(0xFFFF3B30))
} }
}, },
confirmButton = {}, dismissButton = {
dismissButton = {}, TextButton(onClick = { dialogToDelete = null }) {
containerColor = if (isDarkTheme) Color(0xFF2C2C2E) else Color.White, Text("Cancel", color = PrimaryBlue)
shape = RoundedCornerShape(16.dp) }
}
) )
} }
@@ -648,75 +612,39 @@ fun ChatsListScreen(
dialogToBlock?.let { dialog -> dialogToBlock?.let { dialog ->
AlertDialog( AlertDialog(
onDismissRequest = { dialogToBlock = null }, onDismissRequest = { dialogToBlock = null },
icon = { containerColor = if (isDarkTheme) Color(0xFF2C2C2E) else Color.White,
Icon(
imageVector = Icons.Default.Block,
contentDescription = null,
tint = Color(0xFFFF6B6B),
modifier = Modifier.size(32.dp)
)
},
title = { title = {
Text( Text(
text = "Block user?", "Block ${dialog.opponentTitle.ifEmpty { "User" }}",
fontWeight = FontWeight.SemiBold, fontWeight = FontWeight.Bold,
fontSize = 18.sp, color = textColor
textAlign = TextAlign.Center,
modifier = Modifier.fillMaxWidth()
) )
}, },
text = { text = {
Column( Text(
modifier = Modifier.fillMaxWidth(), "Are you sure you want to block this user? They won't be able to send you messages.",
horizontalAlignment = Alignment.CenterHorizontally color = secondaryTextColor
) { )
val displayName = dialog.opponentTitle.ifEmpty { dialog.opponentKey.take(8) } },
Text( confirmButton = {
text = "$displayName will no longer be able to send you messages. You can unblock them later.", TextButton(
fontSize = 15.sp, onClick = {
color = if (isDarkTheme) Color(0xFFAAAAAA) else Color(0xFF666666), val opponentKey = dialog.opponentKey
textAlign = TextAlign.Center, dialogToBlock = null
modifier = Modifier.fillMaxWidth() scope.launch {
) chatsViewModel.blockUser(opponentKey)
blocklistUpdateTrigger++
Spacer(modifier = Modifier.height(16.dp))
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.Center,
verticalAlignment = Alignment.CenterVertically
) {
TextButton(
onClick = { dialogToBlock = null },
modifier = Modifier.weight(1f)
) {
Text("Cancel", fontWeight = FontWeight.Medium)
}
Spacer(modifier = Modifier.width(8.dp))
TextButton(
onClick = {
scope.launch {
chatsViewModel.blockUser(dialog.opponentKey)
dialogToBlock = null
blocklistUpdateTrigger++
}
},
colors = ButtonDefaults.textButtonColors(
contentColor = Color(0xFFFF3B30)
),
modifier = Modifier.weight(1f)
) {
Text("Block", fontWeight = FontWeight.SemiBold)
} }
} }
) {
Text("Block", color = Color(0xFFFF3B30))
} }
}, },
confirmButton = {}, dismissButton = {
dismissButton = {}, TextButton(onClick = { dialogToBlock = null }) {
containerColor = if (isDarkTheme) Color(0xFF2C2C2E) else Color.White, Text("Cancel", color = PrimaryBlue)
shape = RoundedCornerShape(16.dp) }
}
) )
} }
@@ -724,75 +652,39 @@ fun ChatsListScreen(
dialogToUnblock?.let { dialog -> dialogToUnblock?.let { dialog ->
AlertDialog( AlertDialog(
onDismissRequest = { dialogToUnblock = null }, onDismissRequest = { dialogToUnblock = null },
icon = { containerColor = if (isDarkTheme) Color(0xFF2C2C2E) else Color.White,
Icon(
imageVector = Icons.Default.LockOpen,
contentDescription = null,
tint = Color(0xFF4CAF50),
modifier = Modifier.size(32.dp)
)
},
title = { title = {
Text( Text(
text = "Unblock user?", "Unblock ${dialog.opponentTitle.ifEmpty { "User" }}",
fontWeight = FontWeight.SemiBold, fontWeight = FontWeight.Bold,
fontSize = 18.sp, color = textColor
textAlign = TextAlign.Center,
modifier = Modifier.fillMaxWidth()
) )
}, },
text = { text = {
Column( Text(
modifier = Modifier.fillMaxWidth(), "Are you sure you want to unblock this user? They will be able to send you messages again.",
horizontalAlignment = Alignment.CenterHorizontally color = secondaryTextColor
) { )
val displayName = dialog.opponentTitle.ifEmpty { dialog.opponentKey.take(8) } },
Text( confirmButton = {
text = "$displayName will be able to send you messages again.", TextButton(
fontSize = 15.sp, onClick = {
color = if (isDarkTheme) Color(0xFFAAAAAA) else Color(0xFF666666), val opponentKey = dialog.opponentKey
textAlign = TextAlign.Center, dialogToUnblock = null
modifier = Modifier.fillMaxWidth() scope.launch {
) chatsViewModel.unblockUser(opponentKey)
blocklistUpdateTrigger++
Spacer(modifier = Modifier.height(16.dp))
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.Center,
verticalAlignment = Alignment.CenterVertically
) {
TextButton(
onClick = { dialogToUnblock = null },
modifier = Modifier.weight(1f)
) {
Text("Cancel", fontWeight = FontWeight.Medium)
}
Spacer(modifier = Modifier.width(8.dp))
TextButton(
onClick = {
scope.launch {
chatsViewModel.unblockUser(dialog.opponentKey)
dialogToUnblock = null
blocklistUpdateTrigger++
}
},
colors = ButtonDefaults.textButtonColors(
contentColor = Color(0xFF4CAF50)
),
modifier = Modifier.weight(1f)
) {
Text("Unblock", fontWeight = FontWeight.SemiBold)
} }
} }
) {
Text("Unblock", color = PrimaryBlue)
} }
}, },
confirmButton = {}, dismissButton = {
dismissButton = {}, TextButton(onClick = { dialogToUnblock = null }) {
containerColor = if (isDarkTheme) Color(0xFF2C2C2E) else Color.White, Text("Cancel", color = Color(0xFF8E8E93))
shape = RoundedCornerShape(16.dp) }
}
) )
} }
} // Close Box } // Close Box

View File

@@ -389,11 +389,23 @@ fun AnimatedRosettaLogo(
) )
Box( Box(
modifier = modifier, modifier = modifier
.graphicsLayer {
// Enable hardware acceleration
compositingStrategy = androidx.compose.ui.graphics.CompositingStrategy.Offscreen
},
contentAlignment = Alignment.Center contentAlignment = Alignment.Center
) { ) {
// Pre-render all animations to avoid lag // Pre-render all animations to avoid lag
Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) { Box(
modifier = Modifier
.fillMaxSize()
.graphicsLayer {
// Hardware layer for better performance
renderEffect = null
},
contentAlignment = Alignment.Center
) {
// Rosetta icon (page 0) with pulse animation like splash screen // Rosetta icon (page 0) with pulse animation like splash screen
if (currentPage == 0) { if (currentPage == 0) {
val pulseScale by rememberInfiniteTransition(label = "pulse").animateFloat( val pulseScale by rememberInfiniteTransition(label = "pulse").animateFloat(
@@ -428,70 +440,108 @@ fun AnimatedRosettaLogo(
} }
// Fast page - idea animation (page 1) // Fast page - idea animation (page 1)
ideaComposition?.let { comp -> // Load adjacent pages for smooth swiping
val lottieComp = comp as? com.airbnb.lottie.LottieComposition val shouldLoadIdea = currentPage in 0..2
val progress by animateLottieCompositionAsState( if (shouldLoadIdea) {
composition = lottieComp, ideaComposition?.let { comp ->
iterations = 1, val lottieComp = comp as? com.airbnb.lottie.LottieComposition
isPlaying = currentPage == 1 val progress by animateLottieCompositionAsState(
)
if (currentPage == 1) {
LottieAnimation(
composition = lottieComp, composition = lottieComp,
progress = { progress }, iterations = 1,
modifier = Modifier.fillMaxSize() isPlaying = currentPage == 1,
speed = 1.2f // Faster for smoother perception
) )
if (currentPage == 1) {
LottieAnimation(
composition = lottieComp,
progress = { progress },
modifier = Modifier
.fillMaxSize()
.graphicsLayer {
// Hardware acceleration for smooth rendering
compositingStrategy = androidx.compose.ui.graphics.CompositingStrategy.Offscreen
},
maintainOriginalImageBounds = true
)
}
} }
} }
// Free page - money animation (page 2) // Free page - money animation (page 2)
moneyComposition?.let { comp -> val shouldLoadMoney = currentPage in 1..3
val lottieComp = comp as? com.airbnb.lottie.LottieComposition if (shouldLoadMoney) {
val progress by animateLottieCompositionAsState( moneyComposition?.let { comp ->
composition = lottieComp, val lottieComp = comp as? com.airbnb.lottie.LottieComposition
iterations = 1, val progress by animateLottieCompositionAsState(
isPlaying = currentPage == 2
)
if (currentPage == 2) {
LottieAnimation(
composition = lottieComp, composition = lottieComp,
progress = { progress }, iterations = 1,
modifier = Modifier.fillMaxSize() isPlaying = currentPage == 2,
speed = 1.2f
) )
if (currentPage == 2) {
LottieAnimation(
composition = lottieComp,
progress = { progress },
modifier = Modifier
.fillMaxSize()
.graphicsLayer {
compositingStrategy = androidx.compose.ui.graphics.CompositingStrategy.Offscreen
},
maintainOriginalImageBounds = true
)
}
} }
} }
// Secure page - lock animation (page 3) // Secure page - lock animation (page 3)
lockComposition?.let { comp -> val shouldLoadLock = currentPage in 2..4
val lottieComp = comp as? com.airbnb.lottie.LottieComposition if (shouldLoadLock) {
val progress by animateLottieCompositionAsState( lockComposition?.let { comp ->
composition = lottieComp, val lottieComp = comp as? com.airbnb.lottie.LottieComposition
iterations = 1, val progress by animateLottieCompositionAsState(
isPlaying = currentPage == 3
)
if (currentPage == 3) {
LottieAnimation(
composition = lottieComp, composition = lottieComp,
progress = { progress }, iterations = 1,
modifier = Modifier.fillMaxSize() isPlaying = currentPage == 3,
speed = 1.2f
) )
if (currentPage == 3) {
LottieAnimation(
composition = lottieComp,
progress = { progress },
modifier = Modifier
.fillMaxSize()
.graphicsLayer {
compositingStrategy = androidx.compose.ui.graphics.CompositingStrategy.Offscreen
},
maintainOriginalImageBounds = true
)
}
} }
} }
// Private page - book animation (page 4) // Private page - book animation (page 4)
bookComposition?.let { comp -> val shouldLoadBook = currentPage in 3..4
val lottieComp = comp as? com.airbnb.lottie.LottieComposition if (shouldLoadBook) {
val progress by animateLottieCompositionAsState( bookComposition?.let { comp ->
composition = lottieComp, val lottieComp = comp as? com.airbnb.lottie.LottieComposition
iterations = 1, val progress by animateLottieCompositionAsState(
isPlaying = currentPage == 4
)
if (currentPage == 4) {
LottieAnimation(
composition = lottieComp, composition = lottieComp,
progress = { progress }, iterations = 1,
modifier = Modifier.fillMaxSize() isPlaying = currentPage == 4,
speed = 1.2f
) )
if (currentPage == 4) {
LottieAnimation(
composition = lottieComp,
progress = { progress },
modifier = Modifier
.fillMaxSize()
.graphicsLayer {
compositingStrategy = androidx.compose.ui.graphics.CompositingStrategy.Offscreen
},
maintainOriginalImageBounds = true
)
}
} }
} }
} }