fix(ui): clear text selection on tap anywhere in the article

Every block of the dictionary article was wrapped in its own
SelectionContainer, so a tap in one block never cleared a highlight
held in another. A single container now wraps the article list — a tap
anywhere clears the selection and selection can span adjacent blocks.
This commit is contained in:
OneWay authored and Evgenii committed 2026-09-21 13:25:41 +08:00
1 parent 4caf3a534d
commit 4e42a269a7
1 file changed
+51 -47
@@ -123,59 +123,63 @@ fun ArticleContent(
val isHeavyArticle = val isHeavyArticle =
articleState.dslContent.size > HEAVY_ARTICLE_BLOCK_COUNT articleState.dslContent.size > HEAVY_ARTICLE_BLOCK_COUNT
LazyColumn( val articleList: @Composable (Modifier) -> Unit = { listModifier ->
state = listState, LazyColumn(
contentPadding = PaddingValues(top = Spacing.listItemVertical), state = listState,
modifier = modifier contentPadding = PaddingValues(top = Spacing.listItemVertical),
.fillMaxSize() modifier = listModifier
.onSizeChanged { viewportSize = it } .testTag("article_content")
.windowInsetsPadding(AppWindowInsets.horizontalSafeDrawing) .semantics {
.padding(end = 16.dp) testTagsAsResourceId = true
.testTag("article_content") }
.semantics { ) {
testTagsAsResourceId = true itemsIndexed(
} items = articleState.dslContent,
) { key = { index, _ ->
itemsIndexed( // Use stable key combining article save key and index
items = articleState.dslContent, "$articleSaveKey:$index"
key = { index, _ -> },
// Use stable key combining article save key and index contentType = { _, _ -> "article_block" }
"$articleSaveKey:$index" ) { index, block ->
}, val followedByContinuation =
contentType = { _, _ -> "article_block" } articleState.dslContent.getOrNull(index + 1)?.continuesPrevious == true
) { index, block -> val textContent = @Composable {
val followedByContinuation = Box(
articleState.dslContent.getOrNull(index + 1)?.continuesPrevious == true modifier = Modifier
val textContent = @Composable { .padding(
Box( start = Spacing.contentStart + indentStep * block.indent
modifier = Modifier )
.padding( .padding(bottom = if (followedByContinuation) 0.dp else 8.dp)
start = Spacing.contentStart + indentStep * block.indent ) {
ArticleLinkText(
block = block,
linksEnabled = true,
preferDirectHitTesting = isHeavyArticle,
style = baseTextStyle,
onRefClick = refClickHandler,
onTextLayout = {},
modifier = Modifier
.testTag("article_block_$index")
.semantics {
testTagsAsResourceId = true
}
) )
.padding(bottom = if (followedByContinuation) 0.dp else 8.dp) }
) {
ArticleLinkText(
block = block,
linksEnabled = true,
preferDirectHitTesting = isHeavyArticle,
style = baseTextStyle,
onRefClick = refClickHandler,
onTextLayout = {},
modifier = Modifier
.testTag("article_block_$index")
.semantics {
testTagsAsResourceId = true
}
)
} }
}
if (isHeavyArticle) {
textContent() textContent()
} else {
SelectionContainer { textContent() }
} }
} }
} }
val articleListModifier = modifier
.fillMaxSize()
.onSizeChanged { viewportSize = it }
.windowInsetsPadding(AppWindowInsets.horizontalSafeDrawing)
.padding(end = 16.dp)
if (isHeavyArticle) {
articleList(articleListModifier)
} else {
SelectionContainer { articleList(articleListModifier) }
}
} }
is ArticleState.Error -> { is ArticleState.Error -> {
Column( Column(