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(
modifier = Modifier.fillMaxWidth(),
horizontalAlignment = Alignment.CenterHorizontally
) {
val displayName = dialog.opponentTitle.ifEmpty { dialog.opponentKey.take(8) }
Text( Text(
text = "All messages with $displayName will be permanently deleted. This action cannot be undone.", "Are you sure you want to delete this chat? This action cannot be undone.",
fontSize = 15.sp, color = secondaryTextColor
color = if (isDarkTheme) Color(0xFFAAAAAA) else Color(0xFF666666),
textAlign = TextAlign.Center,
modifier = Modifier.fillMaxWidth()
) )
},
Spacer(modifier = Modifier.height(16.dp)) confirmButton = {
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( TextButton(
onClick = { onClick = {
scope.launch { val opponentKey = dialog.opponentKey
chatsViewModel.deleteDialog(dialog.opponentKey)
dialogToDelete = null dialogToDelete = null
scope.launch {
chatsViewModel.deleteDialog(opponentKey)
}
} }
},
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(
modifier = Modifier.fillMaxWidth(),
horizontalAlignment = Alignment.CenterHorizontally
) {
val displayName = dialog.opponentTitle.ifEmpty { dialog.opponentKey.take(8) }
Text( Text(
text = "$displayName will no longer be able to send you messages. You can unblock them later.", "Are you sure you want to block this user? They won't be able to send you messages.",
fontSize = 15.sp, color = secondaryTextColor
color = if (isDarkTheme) Color(0xFFAAAAAA) else Color(0xFF666666),
textAlign = TextAlign.Center,
modifier = Modifier.fillMaxWidth()
) )
},
Spacer(modifier = Modifier.height(16.dp)) confirmButton = {
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( TextButton(
onClick = { onClick = {
scope.launch { val opponentKey = dialog.opponentKey
chatsViewModel.blockUser(dialog.opponentKey)
dialogToBlock = null dialogToBlock = null
scope.launch {
chatsViewModel.blockUser(opponentKey)
blocklistUpdateTrigger++ 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(
modifier = Modifier.fillMaxWidth(),
horizontalAlignment = Alignment.CenterHorizontally
) {
val displayName = dialog.opponentTitle.ifEmpty { dialog.opponentKey.take(8) }
Text( Text(
text = "$displayName will be able to send you messages again.", "Are you sure you want to unblock this user? They will be able to send you messages again.",
fontSize = 15.sp, color = secondaryTextColor
color = if (isDarkTheme) Color(0xFFAAAAAA) else Color(0xFF666666),
textAlign = TextAlign.Center,
modifier = Modifier.fillMaxWidth()
) )
},
Spacer(modifier = Modifier.height(16.dp)) confirmButton = {
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( TextButton(
onClick = { onClick = {
scope.launch { val opponentKey = dialog.opponentKey
chatsViewModel.unblockUser(dialog.opponentKey)
dialogToUnblock = null dialogToUnblock = null
scope.launch {
chatsViewModel.unblockUser(opponentKey)
blocklistUpdateTrigger++ 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,75 +440,113 @@ fun AnimatedRosettaLogo(
} }
// Fast page - idea animation (page 1) // Fast page - idea animation (page 1)
// Load adjacent pages for smooth swiping
val shouldLoadIdea = currentPage in 0..2
if (shouldLoadIdea) {
ideaComposition?.let { comp -> ideaComposition?.let { comp ->
val lottieComp = comp as? com.airbnb.lottie.LottieComposition val lottieComp = comp as? com.airbnb.lottie.LottieComposition
val progress by animateLottieCompositionAsState( val progress by animateLottieCompositionAsState(
composition = lottieComp, composition = lottieComp,
iterations = 1, iterations = 1,
isPlaying = currentPage == 1 isPlaying = currentPage == 1,
speed = 1.2f // Faster for smoother perception
) )
if (currentPage == 1) { if (currentPage == 1) {
LottieAnimation( LottieAnimation(
composition = lottieComp, composition = lottieComp,
progress = { progress }, progress = { progress },
modifier = Modifier.fillMaxSize() 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)
val shouldLoadMoney = currentPage in 1..3
if (shouldLoadMoney) {
moneyComposition?.let { comp -> moneyComposition?.let { comp ->
val lottieComp = comp as? com.airbnb.lottie.LottieComposition val lottieComp = comp as? com.airbnb.lottie.LottieComposition
val progress by animateLottieCompositionAsState( val progress by animateLottieCompositionAsState(
composition = lottieComp, composition = lottieComp,
iterations = 1, iterations = 1,
isPlaying = currentPage == 2 isPlaying = currentPage == 2,
speed = 1.2f
) )
if (currentPage == 2) { if (currentPage == 2) {
LottieAnimation( LottieAnimation(
composition = lottieComp, composition = lottieComp,
progress = { progress }, progress = { progress },
modifier = Modifier.fillMaxSize() 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)
val shouldLoadLock = currentPage in 2..4
if (shouldLoadLock) {
lockComposition?.let { comp -> lockComposition?.let { comp ->
val lottieComp = comp as? com.airbnb.lottie.LottieComposition val lottieComp = comp as? com.airbnb.lottie.LottieComposition
val progress by animateLottieCompositionAsState( val progress by animateLottieCompositionAsState(
composition = lottieComp, composition = lottieComp,
iterations = 1, iterations = 1,
isPlaying = currentPage == 3 isPlaying = currentPage == 3,
speed = 1.2f
) )
if (currentPage == 3) { if (currentPage == 3) {
LottieAnimation( LottieAnimation(
composition = lottieComp, composition = lottieComp,
progress = { progress }, progress = { progress },
modifier = Modifier.fillMaxSize() 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)
val shouldLoadBook = currentPage in 3..4
if (shouldLoadBook) {
bookComposition?.let { comp -> bookComposition?.let { comp ->
val lottieComp = comp as? com.airbnb.lottie.LottieComposition val lottieComp = comp as? com.airbnb.lottie.LottieComposition
val progress by animateLottieCompositionAsState( val progress by animateLottieCompositionAsState(
composition = lottieComp, composition = lottieComp,
iterations = 1, iterations = 1,
isPlaying = currentPage == 4 isPlaying = currentPage == 4,
speed = 1.2f
) )
if (currentPage == 4) { if (currentPage == 4) {
LottieAnimation( LottieAnimation(
composition = lottieComp, composition = lottieComp,
progress = { progress }, progress = { progress },
modifier = Modifier.fillMaxSize() modifier = Modifier
.fillMaxSize()
.graphicsLayer {
compositingStrategy = androidx.compose.ui.graphics.CompositingStrategy.Offscreen
},
maintainOriginalImageBounds = true
) )
} }
} }
} }
} }
} }
}
@Composable @Composable
fun OnboardingPageContent( fun OnboardingPageContent(