From af99c5a6b0f8968be20b5581f09b2a0713867870 Mon Sep 17 00:00:00 2001 From: AE <98699388+A-EDev@users.noreply.github.com> Date: Sat, 7 Nov 2026 18:18:09 +0200 Subject: [PATCH 1/2] fix(appearance): show the theme gradient on placeholders until the thumbnail is read --- .../ui/components/shared/SkeletonGradient.kt | 44 +++++++++++-------- 1 file changed, 26 insertions(+), 18 deletions(-) diff --git a/app/src/main/java/io/github/aedev/flow/ui/components/shared/SkeletonGradient.kt b/app/src/main/java/io/github/aedev/flow/ui/components/shared/SkeletonGradient.kt index f0dc49f3c..7381bb662 100644 --- a/app/src/main/java/io/github/aedev/flow/ui/components/shared/SkeletonGradient.kt +++ b/app/src/main/java/io/github/aedev/flow/ui/components/shared/SkeletonGradient.kt @@ -1,10 +1,12 @@ package io.github.aedev.flow.ui.components.shared +import androidx.compose.animation.animateColorAsState import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.runtime.Immutable import androidx.compose.runtime.compositionLocalOf +import androidx.compose.runtime.getValue import androidx.compose.runtime.key import androidx.compose.runtime.remember import androidx.compose.runtime.staticCompositionLocalOf @@ -56,7 +58,8 @@ internal fun themeSkeletonGradient(): SkeletonGradient { /** * Placeholders in [content] take their gradient from the colours of [thumbnailUrl]. Hue comes from * the picture and tone from the app's surface, so a loud thumbnail can't make the page glare. They - * stay neutral until the picture has been read, so the theme's accent never flashes first. + * show the theme's gradient until the picture has been read, which on a slow connection can be the + * whole time the page loads, and ease across once it has. */ @Composable fun ThumbnailSkeletonGradient( @@ -70,26 +73,31 @@ fun ThumbnailSkeletonGradient( cacheOnly = LocalThumbnailQuality.current == ThumbnailQuality.OFF, ) val scheme = MaterialTheme.colorScheme + val theme = themeSkeletonGradient() val dark = scheme.surface.tone() < MID_TONE - val gradient = - remember(palette.base, palette.accent, dark, scheme.surfaceContainerHigh, scheme.surfaceContainerHighest) { + val target = + remember(palette.base, palette.accent, dark, theme) { if (palette.accent == scheme.primary && palette.base == scheme.surface) { - return@remember SkeletonGradient( - dimStart = scheme.surfaceContainerHigh, - dimEnd = scheme.surfaceContainerHigh, - brightStart = scheme.surfaceContainerHighest, - brightEnd = scheme.surfaceContainerHighest, + theme + } else { + val towardsSurface = if (dark) PULSE_TONE_STEP else -PULSE_TONE_STEP + val startTone = if (dark) START_TONE_DARK else START_TONE_LIGHT + val endTone = if (dark) END_TONE_DARK else END_TONE_LIGHT + SkeletonGradient( + dimStart = palette.base.withTone(startTone), + dimEnd = palette.accent.withTone(endTone), + brightStart = palette.base.withTone(startTone + towardsSurface), + brightEnd = palette.accent.withTone(endTone + towardsSurface), ) } - val towardsSurface = if (dark) PULSE_TONE_STEP else -PULSE_TONE_STEP - val startTone = if (dark) START_TONE_DARK else START_TONE_LIGHT - val endTone = if (dark) END_TONE_DARK else END_TONE_LIGHT - SkeletonGradient( - dimStart = palette.base.withTone(startTone), - dimEnd = palette.accent.withTone(endTone), - brightStart = palette.base.withTone(startTone + towardsSurface), - brightEnd = palette.accent.withTone(endTone + towardsSurface), - ) } - CompositionLocalProvider(LocalSkeletonGradient provides gradient, content = content) + val spec = MaterialTheme.motionScheme.slowEffectsSpec() + val dimStart by animateColorAsState(target.dimStart, spec, label = "skeletonDimStart") + val dimEnd by animateColorAsState(target.dimEnd, spec, label = "skeletonDimEnd") + val brightStart by animateColorAsState(target.brightStart, spec, label = "skeletonBrightStart") + val brightEnd by animateColorAsState(target.brightEnd, spec, label = "skeletonBrightEnd") + CompositionLocalProvider( + LocalSkeletonGradient provides SkeletonGradient(dimStart, dimEnd, brightStart, brightEnd), + content = content, + ) } From 7940850b59661535eca5ae97503c5625f9b721df Mon Sep 17 00:00:00 2001 From: AE <98699388+A-EDev@users.noreply.github.com> Date: Sat, 7 Nov 2026 18:18:09 +0200 Subject: [PATCH 2/2] docs(nightly): note the placeholder gradient fix --- .../2026-11-07-fix-skeleton-gradient-before-thumbnail.md | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 .github/nightly-notes/2026-11-07-fix-skeleton-gradient-before-thumbnail.md diff --git a/.github/nightly-notes/2026-11-07-fix-skeleton-gradient-before-thumbnail.md b/.github/nightly-notes/2026-11-07-fix-skeleton-gradient-before-thumbnail.md new file mode 100644 index 000000000..d277161ca --- /dev/null +++ b/.github/nightly-notes/2026-11-07-fix-skeleton-gradient-before-thumbnail.md @@ -0,0 +1,2 @@ +## Fixed +- Gradient loading placeholders no longer stay grey on the video page while the thumbnail loads (#1815)