feat: Add reply panel visibility state and optimize dialog confirmation handling
This commit is contained in:
@@ -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 {
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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,74 +440,112 @@ 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
|
||||||
|
|||||||
Reference in New Issue
Block a user