-
Notifications
You must be signed in to change notification settings - Fork 108
[CLX-3537][Horizon] Inbox a11y #3443
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
942cfe9
ae0811c
a4adfc0
6d75469
fbca632
a8d48da
1677a00
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -21,6 +21,7 @@ import androidx.annotation.DrawableRes | |
| import androidx.compose.animation.AnimatedContent | ||
| import androidx.compose.foundation.ExperimentalFoundationApi | ||
| import androidx.compose.foundation.background | ||
| import androidx.compose.foundation.layout.Arrangement | ||
| import androidx.compose.foundation.layout.Box | ||
| import androidx.compose.foundation.layout.Column | ||
| import androidx.compose.foundation.layout.PaddingValues | ||
|
|
@@ -31,7 +32,8 @@ import androidx.compose.foundation.layout.fillMaxWidth | |
| import androidx.compose.foundation.layout.padding | ||
| import androidx.compose.foundation.layout.size | ||
| import androidx.compose.foundation.lazy.LazyColumn | ||
| import androidx.compose.foundation.lazy.items | ||
| import androidx.compose.foundation.lazy.itemsIndexed | ||
| import androidx.compose.foundation.lazy.rememberLazyListState | ||
| import androidx.compose.material3.CenterAlignedTopAppBar | ||
| import androidx.compose.material3.ExperimentalMaterial3Api | ||
| import androidx.compose.material3.Icon | ||
|
|
@@ -50,6 +52,8 @@ import androidx.compose.ui.draw.clip | |
| import androidx.compose.ui.platform.LocalContext | ||
| import androidx.compose.ui.res.painterResource | ||
| import androidx.compose.ui.res.stringResource | ||
| import androidx.compose.ui.semantics.isTraversalGroup | ||
| import androidx.compose.ui.semantics.semantics | ||
| import androidx.compose.ui.text.input.TextFieldValue | ||
| import androidx.compose.ui.tooling.preview.Preview | ||
| import androidx.compose.ui.unit.dp | ||
|
|
@@ -198,25 +202,39 @@ private fun HorizonInboxDetailsContent( | |
| .clip(HorizonCornerRadius.level4Top) | ||
| .background(HorizonColors.Surface.pageSecondary()) | ||
| ) { | ||
| val scrollState = rememberLazyListState( | ||
| if (state.bottomLayout) { | ||
| state.items.lastIndex | ||
| } else { | ||
| 0 | ||
| } | ||
| ) | ||
| LazyColumn( | ||
| verticalArrangement = if (state.bottomLayout) Arrangement.Bottom else Arrangement.Top, | ||
| state = scrollState, | ||
| modifier = Modifier | ||
| .fillMaxSize() | ||
| .background(HorizonColors.Surface.pageSecondary()), | ||
| reverseLayout = state.bottomLayout, | ||
| .weight(1f) | ||
| .background(HorizonColors.Surface.pageSecondary()) | ||
| .semantics { | ||
| isTraversalGroup = true | ||
| }, | ||
| contentPadding = PaddingValues(top = 16.dp) | ||
| ) { | ||
| if (state.replyState != null) { | ||
| stickyHeader { HorizonInboxReplyContent(state.replyState) } | ||
| } | ||
| items(state.items) { | ||
| itemsIndexed(state.items) { index, item -> | ||
| Column { | ||
| HorizonInboxDetailsItem(it) | ||
| if ((state.bottomLayout && it != state.items.firstOrNull()) || (!state.bottomLayout && it != state.items.lastOrNull())) { | ||
| HorizonInboxDetailsItem( | ||
| item, | ||
| Modifier.semantics(true) {} | ||
| ) | ||
| if (item != state.items.lastOrNull()) { | ||
| HorizonDivider() | ||
| } | ||
| } | ||
| } | ||
| } | ||
| if (state.replyState != null) { | ||
| HorizonInboxReplyContent(state.replyState) | ||
| } | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Moving the reply content from Previous behavior: Reply UI stayed visible at the top/bottom while scrolling through messages (sticky header behavior) New behavior: Reply UI is now a separate component after the scrollable list Questions:
If the sticky header was causing problems, consider using a |
||
| } | ||
| } | ||
|
|
||
|
|
@@ -448,7 +466,6 @@ private fun HorizonInboxDetailsScreenPreview() { | |
| onReplyTextValueChange = {}, | ||
| onSendReply = {} | ||
| ), | ||
| bottomLayout = true | ||
| ) | ||
| ) | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -134,7 +134,7 @@ class HorizonInboxDetailsViewModel @Inject constructor( | |
| attachment.toAttachmentUiState() | ||
| } | ||
| ) | ||
| }, | ||
| }.reversed(), | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This reversal combined with the layout changes needs careful verification. Question: With the new Testing needed:
The interaction between |
||
| replyState = getReplyState(), | ||
| bottomLayout = true | ||
| ) | ||
|
|
@@ -382,7 +382,7 @@ class HorizonInboxDetailsViewModel @Inject constructor( | |
|
|
||
| _uiState.update { | ||
| it.copy( | ||
| items = conversation.messages.map { message -> | ||
| items = it.items + conversation.messages.map { message -> | ||
| HorizonInboxDetailsItem( | ||
| author = conversation.participants.firstOrNull { it.id == message.authorId }?.name.orEmpty(), | ||
| date = message.createdAt.toDate() ?: Date(), | ||
|
|
@@ -392,7 +392,7 @@ class HorizonInboxDetailsViewModel @Inject constructor( | |
| attachment.toAttachmentUiState() | ||
| } | ||
| ) | ||
| } + it.items, | ||
| }, | ||
| replyState = it.replyState?.copy( | ||
| replyTextValue = TextFieldValue(""), | ||
| isLoading = false, | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a significant behavioral change from the previous
reverseLayoutapproach.Concerns:
reverseLayout = truewithLazyColumnis the standard pattern for bottom-anchored lists (chat-style UIs) because it naturally handles scroll position when items are added/removedArrangement.Bottomwith manual scroll state initialization may not handle dynamic content changes as gracefullyRecommendation:
If the goal is to fix accessibility traversal order, consider using
reverseLayoutwithModifier.semantics { isTraversalGroup = true; traversalIndex = ... }instead of completely changing the layout approach. This would maintain the proven scroll behavior while fixing accessibility.Please ensure this has been thoroughly tested with: