@ -18,7 +18,6 @@ import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.utun.chat.data.Channel
@ -112,31 +111,10 @@ fun ChatScreen(
val playbackState by viewModel . playbackState . collectAsState ( )
var initialScrolled by remember { mutableStateOf ( false ) }
/* своё сообщение — подтягиваемся вниз (в reverseLayout индекс 0 — это низ) */
LaunchedEffect ( messages . size ) {
if ( messages . isEmpty ( ) ) return @LaunchedEffect
val lastIndex = messages . size - 1
if ( !in itialScrolled ) {
/* первый вход — мгновенный прыжок вниз без анимации */
listState . scrollToItem ( lastIndex )
initialScrolled = true
return @LaunchedEffect
}
/* новое сообщение: скроллим вниз если своё, либо если были у нижнего края */
val ownMessage = messages . lastOrNull ( ) ?. isOutgoing == true
val wasAtBottom = ! listState . canScrollForward
if ( ownMessage || wasAtBottom ) {
listState . animateScrollToItem ( lastIndex )
}
}
/* при открытии клавиатуры прижимаем список к низу, чтобы последнее сообщение осталось видимым */
val density = LocalDensity . current
val imeVisible = WindowInsets . ime . getBottom ( density ) > 0
LaunchedEffect ( imeVisible ) {
if ( imeVisible && messages . isNotEmpty ( ) ) {
listState . scrollToItem ( messages . size - 1 )
if ( messages . lastOrNull ( ) ?. isOutgoing == true ) {
listState . scrollToItem ( 0 )
}
}
@ -155,10 +133,12 @@ fun ChatScreen(
)
LazyColumn (
modifier = Modifier . weight ( 1f ) . fillMaxWidth ( ) ,
state = listState
state = listState ,
reverseLayout = true
) {
items ( messages . size ) { idx ->
MessageBubble ( messages [ idx ] , playbackState = playbackState ,
item { Spacer ( Modifier . height ( 8. dp ) ) }
items ( messages . reversed ( ) ) { msg ->
MessageBubble ( msg , playbackState = playbackState ,
onPlayVoice = { path , durMs -> viewModel . playVoiceMessage ( path , durMs ) } ,
onSeekVoice = { path , seekMs , durMs -> viewModel . seekVoice ( path , seekMs , durMs ) } ,
onDownloadFile = { msg -> viewModel . downloadAttachment ( msg ) } ,
@ -171,7 +151,6 @@ fun ChatScreen(
onOpenImage = { msg -> onViewPhoto ( msg ) } ,
onPlayVideo = { msg -> onPlayVideo ( msg ) } )
}
item { Spacer ( Modifier . height ( 8. dp ) ) }
}
InputBar (
text = text ,