Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
191 changes: 117 additions & 74 deletions app/src/main/java/org/nsh07/wikireader/ui/homeScreen/ArticleFeed.kt

Large diffs are not rendered by default.

194 changes: 91 additions & 103 deletions app/src/main/java/org/nsh07/wikireader/ui/homeScreen/AsyncInfobox.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package org.nsh07.wikireader.ui.homeScreen

import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.ExperimentalSharedTransitionApi
import androidx.compose.animation.SharedTransitionLayout
import androidx.compose.animation.SharedTransitionScope
import androidx.compose.animation.expandVertically
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
Expand Down Expand Up @@ -59,6 +59,7 @@ fun AsyncInfobox(
text: String,
lang: String,
fontSize: Int,
sharedScope: SharedTransitionScope,
darkTheme: Boolean,
background: Boolean,
onImageClick: (String, String) -> Unit,
Expand All @@ -82,92 +83,79 @@ fun AsyncInfobox(

var expanded by rememberSaveable { mutableStateOf(false) }

SharedTransitionLayout {
Card(
colors = cardColors,
shape = shapes.largeIncreased,
modifier = Modifier
.widthIn(max = 512.dp)
.fillMaxWidth()
.padding(start = 16.dp, end = 16.dp, bottom = 16.dp)
Card(
colors = cardColors,
shape = shapes.largeIncreased,
modifier = Modifier
.widthIn(max = 512.dp)
.fillMaxWidth()
.padding(start = 16.dp, end = 16.dp, bottom = 16.dp)
) {
Column(
modifier = Modifier.fillMaxWidth()
) {
Column(
modifier = Modifier.fillMaxWidth()
) {
ListItem(
leadingContent = {
FilledTonalIconButton(
onClick = { expanded = !expanded },
shapes = IconButtonDefaults.shapes()
) {
Icon(
if (expanded) painterResource(R.drawable.keyboard_arrow_up)
else painterResource(R.drawable.keyboard_arrow_down),
contentDescription = stringResource(R.string.expand_section)
ListItem(
leadingContent = {
FilledTonalIconButton(
onClick = { expanded = !expanded },
shapes = IconButtonDefaults.shapes()
) {
Icon(
if (expanded) painterResource(R.drawable.keyboard_arrow_up)
else painterResource(R.drawable.keyboard_arrow_down),
contentDescription = stringResource(R.string.expand_section)
)
}
},
headlineContent = {
Row(verticalAlignment = Alignment.CenterVertically) {
Text(stringResource(R.string.infobox), fontWeight = FontWeight.Medium)
Spacer(Modifier.width(8.dp))
AnimatedVisibility(!expanded) {
Text(
title ?: AnnotatedString(""),
color = colorScheme.outline,
textAlign = TextAlign.Center
)
}
},
headlineContent = {
Row(verticalAlignment = Alignment.CenterVertically) {
Text(stringResource(R.string.infobox), fontWeight = FontWeight.Medium)
Spacer(Modifier.width(8.dp))
AnimatedVisibility(!expanded) {
Text(
title ?: AnnotatedString(""),
color = colorScheme.outline,
textAlign = TextAlign.Center,
modifier = Modifier
.sharedBounds(
sharedContentState = rememberSharedContentState(
title ?: AnnotatedString("")
), animatedVisibilityScope = this
)
)
}
}
},
colors = ListItemDefaults.colors(containerColor = colorScheme.surfaceContainer),
modifier = Modifier
.clip(shapes.largeIncreased)
.padding(vertical = 8.dp)
.clickable(onClick = { expanded = !expanded })
)
}
},
colors = ListItemDefaults.colors(containerColor = colorScheme.surfaceContainer),
modifier = Modifier
.clip(shapes.largeIncreased)
.padding(vertical = 8.dp)
.clickable(onClick = { expanded = !expanded })
)

AnimatedVisibility(
expanded,
enter = expandVertically(expandFrom = Alignment.CenterVertically) + fadeIn(),
exit = shrinkVertically(shrinkTowards = Alignment.CenterVertically) + fadeOut()
) {
Column(horizontalAlignment = Alignment.CenterHorizontally) {
if (title != null)
AnimatedVisibility(
expanded,
enter = expandVertically(expandFrom = Alignment.CenterVertically) + fadeIn(),
exit = shrinkVertically(shrinkTowards = Alignment.CenterVertically) + fadeOut()
) {
Column(horizontalAlignment = Alignment.CenterHorizontally) {
if (title != null)
Text(
title ?: AnnotatedString(""),
style = typography.headlineSmall,
textAlign = TextAlign.Center,
modifier = Modifier.padding(vertical = 8.dp)
)
infobox.fastForEach { item ->
Row(Modifier.fillMaxWidth()) {
Text(
title ?: AnnotatedString(""),
style = typography.headlineSmall,
textAlign = TextAlign.Center,
item.first,
fontSize = fontSize.sp,
fontWeight = FontWeight.Bold,
lineHeight = (24 * (fontSize / 16.0)).toInt().sp,
modifier = Modifier
.sharedBounds(
sharedContentState = rememberSharedContentState(
key = title ?: AnnotatedString("")
),
animatedVisibilityScope = this@AnimatedVisibility
)
.padding(vertical = 8.dp)
.padding(8.dp)
.widthIn(max = 256.dp)
.weight(1f)
)
infobox.fastForEach { item ->
Row(Modifier.fillMaxWidth()) {
Text(
item.first,
fontSize = fontSize.sp,
fontWeight = FontWeight.Bold,
lineHeight = (24 * (fontSize / 16.0)).toInt().sp,
modifier = Modifier
.padding(8.dp)
.widthIn(max = 256.dp)
.weight(1f)
)
if (item.second.matches("\\[\\[.{1,6}:.+]]".toRegex()) ||
extensions.fastAny { item.second.endsWith(it) }
) {
if (item.second.matches("\\[\\[.{1,6}:.+]]".toRegex()) ||
extensions.fastAny { item.second.endsWith(it) }
) {
with(sharedScope) {
ImageWithCaption(
item.second.toString(),
fontSize,
Expand All @@ -180,32 +168,32 @@ fun AsyncInfobox(
showCaption = false,
modifier = Modifier.weight(2f)
)
} else {
Text(
item.second,
fontSize = fontSize.sp,
lineHeight = (24 * (fontSize / 16.0)).toInt().sp,
modifier = Modifier
.padding(8.dp)
.widthIn(max = 256.dp)
.weight(2f)
)
}
} else {
Text(
item.second,
fontSize = fontSize.sp,
lineHeight = (24 * (fontSize / 16.0)).toInt().sp,
modifier = Modifier
.padding(8.dp)
.widthIn(max = 256.dp)
.weight(2f)
)
}
}
FilledTonalIconButton(
onClick = { expanded = false },
shapes = IconButtonDefaults.shapes(),
modifier = Modifier
.align(Alignment.End)
.padding(16.dp)
.width(52.dp)
) {
Icon(
painterResource(R.drawable.keyboard_arrow_up),
contentDescription = stringResource(R.string.collapse_section)
)
}
}
FilledTonalIconButton(
onClick = { expanded = false },
shapes = IconButtonDefaults.shapes(),
modifier = Modifier
.align(Alignment.End)
.padding(16.dp)
.width(52.dp)
) {
Icon(
painterResource(R.drawable.keyboard_arrow_up),
contentDescription = stringResource(R.string.collapse_section)
)
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,9 @@ fun SharedTransitionScope.ImageWithCaption(
background = background,
modifier = modifier
.sharedBounds(
sharedContentState = rememberSharedContentState(uriHigh),
sharedContentState = rememberSharedContentState(
"caption-image-$uriHigh"
),
animatedVisibilityScope = LocalNavAnimatedContentScope.current
)
.padding(horizontal = 16.dp)
Expand All @@ -91,7 +93,9 @@ fun SharedTransitionScope.ImageWithCaption(
color = colorScheme.onSurfaceVariant,
modifier = Modifier
.sharedBounds(
sharedContentState = rememberSharedContentState(description),
sharedContentState = rememberSharedContentState(
"caption-desc-$description"
),
animatedVisibilityScope = LocalNavAnimatedContentScope.current
)
.padding(horizontal = 16.dp)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ fun PageContent(
modifier = Modifier
.sharedBounds(
sharedContentState = rememberSharedContentState(
content.title
"tfa-title-${content.title}"
),
animatedVisibilityScope = LocalNavAnimatedContentScope.current,
zIndexInOverlay = 1f
Expand All @@ -144,7 +144,7 @@ fun PageContent(
modifier = Modifier
.sharedBounds(
sharedContentState = rememberSharedContentState(
photoDesc
"tfa-desc-$photoDesc"
),
animatedVisibilityScope = LocalNavAnimatedContentScope.current,
zIndexInOverlay = 1f
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ fun ParsedBodyText(
text = it.toString(),
lang = lang,
fontSize = fontSize,
sharedScope = sharedScope,
darkTheme = darkTheme,
background = background,
onImageClick = onGalleryImageClick,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,9 @@ fun SharedTransitionScope.FullScreenImageTopBar(
maxLines = 1,
overflow = TextOverflow.Ellipsis,
modifier = Modifier.sharedBounds(
sharedContentState = rememberSharedContentState(description),
sharedContentState = rememberSharedContentState(
"caption-desc-$description"
),
animatedVisibilityScope = LocalNavAnimatedContentScope.current,
zIndexInOverlay = 1f
)
Expand Down
12 changes: 9 additions & 3 deletions app/src/main/java/org/nsh07/wikireader/ui/image/ImageCard.kt
Original file line number Diff line number Diff line change
Expand Up @@ -76,9 +76,15 @@ fun SharedTransitionScope.ImageCard(
contentScale = contentScale,
background = background,
modifier = Modifier
.sharedBounds(
sharedContentState = rememberSharedContentState(photo.source),
animatedVisibilityScope = animatedVisibilityScope
.then(
photo.source?.let { src ->
Modifier.sharedBounds(
sharedContentState = rememberSharedContentState(
"article-image-$src"
Copy link

Copilot AI Jan 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The shared transition key "article-image-$src" in ImageCard.kt (used by PageContent.kt) doesn't match the keys used in ArticleFeed.kt. When navigating from the feed to an article, the shared element transition won't work because ArticleFeed uses keys like "tfa-image-$src" for the Today's Featured Article, "trending-$page-$i-image-$src" for trending articles, and "potd-image-$src" for the Picture of the Day. For shared element transitions to work across navigation, the keys must match exactly on both the source and destination screens.

Copilot uses AI. Check for mistakes.
),
animatedVisibilityScope = animatedVisibilityScope
)
} ?: Modifier
)
.fillMaxWidth()
.clip(cardShape)
Expand Down
16 changes: 12 additions & 4 deletions app/src/main/java/org/nsh07/wikireader/ui/image/PageImage.kt
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,15 @@ fun SharedTransitionScope.PageImage(
contentDescription = photoDesc,
contentScale = contentScale,
modifier = Modifier
.sharedBounds(
sharedContentState = rememberSharedContentState(photo.source),
animatedVisibilityScope = LocalNavAnimatedContentScope.current
.then(
photo.source?.let { src ->
Modifier.sharedBounds(
sharedContentState = rememberSharedContentState(
"article-image-$src"
Copy link

Copilot AI Jan 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The shared transition key "article-image-$src" in PageImage.kt (used by ImageCard.kt which is called from PageContent.kt) doesn't match the keys used in ArticleFeed.kt. When navigating from the feed to an article, the shared element transition won't work because ArticleFeed uses keys like "tfa-image-$src" for the Today's Featured Article, "trending-$page-$i-image-$src" for trending articles, and "potd-image-$src" for the Picture of the Day. For shared element transitions to work across navigation, the keys must match exactly on both the source and destination screens.

Copilot uses AI. Check for mistakes.
),
animatedVisibilityScope = LocalNavAnimatedContentScope.current
)
} ?: Modifier
)
.then(modifier)
.aspectRatio(photo.width.toFloat() / photo.height.toFloat())
Expand Down Expand Up @@ -108,7 +114,9 @@ fun SharedTransitionScope.PageImage(
contentScale = contentScale,
modifier = Modifier
.sharedBounds(
sharedContentState = rememberSharedContentState(uri),
sharedContentState = rememberSharedContentState(
"gallery-image-$uri"
),
animatedVisibilityScope = LocalNavAnimatedContentScope.current
)
.then(modifier)
Expand Down