@@ -40,13 +40,15 @@ import androidx.compose.foundation.lazy.LazyColumn
4040import androidx.compose.foundation.lazy.items
4141import androidx.compose.foundation.lazy.rememberLazyListState
4242import androidx.compose.foundation.shape.CircleShape
43+ import androidx.compose.foundation.text.TextAutoSize
4344import androidx.compose.material.icons.Icons
4445import androidx.compose.material.icons.automirrored.filled.ArrowBack
4546import androidx.compose.material.icons.filled.Add
4647import androidx.compose.material.icons.filled.Close
4748import androidx.compose.material.icons.filled.ExpandMore
4849import androidx.compose.material.icons.filled.KeyboardArrowUp
4950import androidx.compose.material.icons.filled.Menu
51+ import androidx.compose.material.icons.filled.MoreVert
5052import androidx.compose.material.icons.filled.PushPin
5153import androidx.compose.material.icons.outlined.Archive
5254import androidx.compose.material.icons.outlined.Delete
@@ -66,6 +68,8 @@ import androidx.compose.material3.Button
6668import androidx.compose.material3.Card
6769import androidx.compose.material3.CircularProgressIndicator
6870import androidx.compose.material3.DrawerValue
71+ import androidx.compose.material3.DropdownMenu
72+ import androidx.compose.material3.DropdownMenuItem
6973import androidx.compose.material3.ExperimentalMaterial3Api
7074import androidx.compose.material3.FilterChip
7175import androidx.compose.material3.FloatingActionButton
@@ -129,10 +133,12 @@ import androidx.compose.ui.semantics.liveRegion
129133import androidx.compose.ui.semantics.semantics
130134import androidx.compose.ui.semantics.stateDescription
131135import androidx.compose.ui.text.font.FontWeight
136+ import androidx.compose.ui.text.style.TextOverflow
132137import androidx.compose.ui.tooling.preview.Preview
133138import androidx.compose.ui.unit.IntOffset
134139import androidx.compose.ui.unit.Velocity
135140import androidx.compose.ui.unit.dp
141+ import androidx.compose.ui.unit.sp
136142import androidx.core.view.HapticFeedbackConstantsCompat
137143import androidx.core.view.ViewCompat
138144import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
@@ -440,7 +446,17 @@ fun ChatListContent(
440446 ) { mode ->
441447 when (mode) {
442448 ChatListTopBarMode .SELECTION -> {
443- Text (pluralStringResource(R .plurals.chatlist_selected_count, lastSelectedRows.size, lastSelectedRows.size))
449+ // Never wrap: a growing action row must shrink this font instead
450+ // of pushing the count to multiple lines and blowing out the bar's
451+ // height. Ellipsis is only the last-resort floor once even the
452+ // smallest step can't fit (e.g. a huge selected count on a tiny
453+ // screen).
454+ Text (
455+ pluralStringResource(R .plurals.chatlist_selected_count, lastSelectedRows.size, lastSelectedRows.size),
456+ maxLines = 1 ,
457+ overflow = TextOverflow .Ellipsis ,
458+ autoSize = TextAutoSize .StepBased (minFontSize = 14 .sp, maxFontSize = 22 .sp),
459+ )
444460 }
445461
446462 ChatListTopBarMode .INVITATIONS -> {
@@ -524,40 +540,65 @@ fun ChatListContent(
524540 // still apply to the live selection.
525541 val pinTarget = aggregateToggleTarget(lastSelectedRows) { it.pinned }
526542 val muteTarget = aggregateToggleTarget(lastSelectedRows) { it.muted }
543+ var overflowOpen by remember { mutableStateOf(false ) }
544+ // Only the three most-reached-for actions stay inline; pin and
545+ // archive move behind "more" so a growing action set never
546+ // starves the title (the autoSize/ellipsis fallback above is
547+ // the last resort, not the primary fix) or forces icons to
548+ // shrink/wrap on narrower phones.
527549 IconButton (
528550 onClick = {
529- onSetPinned(selectedRows.map(ChatListRow ::bufferId), pinTarget)
530- selectedIds = emptyList()
531- },
532- modifier = Modifier .testTag(" chatlist_selection_pin" ),
533- ) { Icon (Icons .Filled .PushPin , stringResource(if (pinTarget) R .string.chatlist_pin else R .string.chatlist_unpin)) }
534- IconButton (
535- onClick = {
536- onSetMuted(selectedRows.map(ChatListRow ::bufferId), muteTarget)
537- selectedIds = emptyList()
538- },
539- modifier = Modifier .testTag(" chatlist_selection_mute" ),
540- ) { Icon (if (muteTarget) Icons .Outlined .NotificationsOff else Icons .Outlined .Notifications , stringResource(if (muteTarget) R .string.chatlist_mute else R .string.chatlist_unmute)) }
541- IconButton (
542- onClick = {
543- // Explicit per-selection action: unlike mark-all, this
544- // includes muted rows on purpose (hand-picking one is
545- // opting it in).
546551 onMarkSelectedRead(selectedRows.map(ChatListRow ::bufferId))
547552 selectedIds = emptyList()
548553 },
549554 modifier = Modifier .testTag(" chatlist_selection_mark_read" ),
550555 ) { Icon (Icons .Outlined .DoneAll , stringResource(R .string.chatlist_mark_read)) }
551556 IconButton (
552557 onClick = {
553- setArchivedWithReveal (selectedRows.map(ChatListRow ::bufferId), ! archiveMode )
558+ onSetMuted (selectedRows.map(ChatListRow ::bufferId), muteTarget )
554559 selectedIds = emptyList()
555560 },
556- modifier = Modifier .testTag(" chatlist_selection_archive " ),
557- ) { Icon (archiveActionIcon(archiveMode) , stringResource(if (archiveMode ) R .string.chatlist_unarchive else R .string.chatlist_archive )) }
561+ modifier = Modifier .testTag(" chatlist_selection_mute " ),
562+ ) { Icon (if (muteTarget) Icons . Outlined . NotificationsOff else Icons . Outlined . Notifications , stringResource(if (muteTarget ) R .string.chatlist_mute else R .string.chatlist_unmute )) }
558563 IconButton (onClick = { confirmRemoval = true }, modifier = Modifier .testTag(" chatlist_selection_remove" )) {
559564 Icon (Icons .Outlined .Delete , stringResource(R .string.chatlist_remove))
560565 }
566+ Box {
567+ IconButton (
568+ onClick = { overflowOpen = true },
569+ modifier = Modifier .testTag(" chatlist_selection_more" ),
570+ ) {
571+ Icon (Icons .Filled .MoreVert , stringResource(R .string.chatlist_more_actions))
572+ }
573+ DropdownMenu (expanded = overflowOpen, onDismissRequest = { overflowOpen = false }) {
574+ DropdownMenuItem (
575+ text = { Text (stringResource(if (pinTarget) R .string.chatlist_pin else R .string.chatlist_unpin)) },
576+ leadingIcon = { Icon (Icons .Filled .PushPin , contentDescription = null ) },
577+ modifier = Modifier .testTag(" chatlist_selection_pin" ),
578+ onClick = {
579+ onSetPinned(selectedRows.map(ChatListRow ::bufferId), pinTarget)
580+ selectedIds = emptyList()
581+ overflowOpen = false
582+ },
583+ )
584+ DropdownMenuItem (
585+ text = {
586+ Text (
587+ stringResource(
588+ if (archiveMode) R .string.chatlist_unarchive else R .string.chatlist_archive,
589+ ),
590+ )
591+ },
592+ leadingIcon = { Icon (archiveActionIcon(archiveMode), contentDescription = null ) },
593+ modifier = Modifier .testTag(" chatlist_selection_archive" ),
594+ onClick = {
595+ setArchivedWithReveal(selectedRows.map(ChatListRow ::bufferId), ! archiveMode)
596+ selectedIds = emptyList()
597+ overflowOpen = false
598+ },
599+ )
600+ }
601+ }
561602 }
562603
563604 ChatListTopBarMode .DEFAULT , ChatListTopBarMode .SCOPED -> {
0 commit comments