diff --git a/app/src/main/java/com/m57/hermescontrol/ui/chat/ChatScreen.kt b/app/src/main/java/com/m57/hermescontrol/ui/chat/ChatScreen.kt index becb2f82..88018148 100644 --- a/app/src/main/java/com/m57/hermescontrol/ui/chat/ChatScreen.kt +++ b/app/src/main/java/com/m57/hermescontrol/ui/chat/ChatScreen.kt @@ -13,6 +13,8 @@ import androidx.compose.animation.core.rememberInfiniteTransition import androidx.compose.animation.core.tween import androidx.compose.animation.fadeIn import androidx.compose.animation.fadeOut +import androidx.compose.animation.scaleIn +import androidx.compose.animation.scaleOut import androidx.compose.animation.slideInVertically import androidx.compose.animation.slideOutVertically import androidx.compose.foundation.BorderStroke @@ -59,6 +61,7 @@ import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.DropdownMenu import androidx.compose.material3.DropdownMenuItem import androidx.compose.material3.FilledTonalButton +import androidx.compose.material3.FloatingActionButton import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.Icon import androidx.compose.material3.IconButton @@ -73,6 +76,7 @@ import androidx.compose.material3.TextButton import androidx.compose.runtime.Composable import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember @@ -109,6 +113,11 @@ fun ChatScreen( ) { val state by viewModel.uiState.collectAsStateWithLifecycle() val listState = rememberLazyListState() + val showScrollToBottom by remember { + derivedStateOf { + state.messages.isNotEmpty() && listState.canScrollForward + } + } var inputText by rememberSaveable { mutableStateOf("") } val snackbarHostState = remember { SnackbarHostState() } val isDark = isSystemInDarkTheme() @@ -522,6 +531,31 @@ fun ChatScreen( } } } + + // Scroll-to-bottom FAB + AnimatedVisibility( + visible = showScrollToBottom, + enter = fadeIn() + scaleIn(), + exit = fadeOut() + scaleOut(), + modifier = + Modifier + .align(Alignment.BottomEnd) + .padding(end = 16.dp, bottom = 16.dp), + ) { + FloatingActionButton( + onClick = { + listState.animateScrollToItem(state.messages.lastIndex) + }, + modifier = Modifier.size(40.dp), + containerColor = MaterialTheme.colorScheme.surfaceContainer, + contentColor = MaterialTheme.colorScheme.onSurface, + ) { + Icon( + imageVector = Icons.Filled.KeyboardArrowDown, + contentDescription = "Scroll to bottom", + ) + } + } } ChatInputBar(