Skip to content

# Fix: Download notification does not scroll to the currently downloading gallery修复点击下载通知不会滚动到当前正在下载的画廊 - #2994

Open
SauronSkywalker wants to merge 1 commit into
FooIbar:mainfrom
SauronSkywalker:source-20260703-142148
Open

Conversation

@SauronSkywalker

@SauronSkywalker SauronSkywalker commented Jul 3, 2026

Copy link
Copy Markdown

Fix: Download notification does not scroll to the currently downloading gallery / 修复:下载通知不滚动到正在下载的画廊


Summary / 概述

EN: When tapping the download notification in the system notification bar while multiple galleries are being downloaded sequentially, the app opens the Downloads screen but stays at the very top instead of scrolling to the position of the currently downloading gallery. This PR fixes the issue by passing the gallery ID (gid) as a route argument to DownloadsScreen, which then auto-scrolls to that item on load.

中: 当多个画廊按顺序下载时,点击系统通知栏中的下载通知,App 会打开下载页面但停留在列表顶部,不会自动滚动到当前正在下载的画廊位置。本 PR 通过将画廊 ID(gid)作为 route 参数传递给 DownloadsScreen,使其在加载后自动滚动到对应条目。


Root cause / 根因分析

EN: The notification handler in MainActivity received the gallery ID (KEY_GID) inside the notification's Bundle, but it was completely ignored — the code just called navigator.navigate(DownloadsScreenDestination) without passing any data. DownloadsScreen had no mechanism to know which item to scroll to.

中: MainActivity 中的通知处理器在通知的 Bundle 中收到了画廊 ID(KEY_GID),但完全忽略了它——代码只是调用了 navigator.navigate(DownloadsScreenDestination) 而没有传递任何数据。DownloadsScreen 没有任何方式知道要滚动到哪个条目。


Changes / 代码改动

1. MainActivity.kt — Pass gid as route arg + popUpTo / 传递 gid 作为路由参数 + 清除旧页面

Line / 行 Before / 修改前 After / 修改后
180 Triple(DownloadsScreenDestination, ...) Triple(DownloadsScreenDestination(), ...)
323-328 navigator.navigate(DownloadsScreenDestination) Extract gid + route arg + popUpTo
477 else -> DownloadsScreenDestination else -> DownloadsScreenDestination()

EN: The navigation drawer entry and start destination now use DownloadsScreenDestination() with parentheses because the added scrollToGid: Long = -1L parameter changes the generated class from object to data class.

中: 导航抽屉条目和启动目标现在使用带括号的 DownloadsScreenDestination(),因为新增的 scrollToGid: Long = -1L 参数将生成的类从 object 变为 data class

// EN: Before / 修改前
navigator.navigate(DownloadsScreenDestination)

// EN: After — extract gid, pass as route arg, popUpTo clearing stale instances
// 中:修改后——提取 gid,作为 route 参数传递,popUpTo 清空旧实例
val gid = args.getLong(DownloadService.KEY_GID, -1L)
navigator.navigate(DownloadsScreenDestination(gid)) {
    popUpTo(navItems.first().first) {
        inclusive = false
    }
}

EN: popUpTo(navItems.first().first) pops all destinations above the home page before navigating, ensuring old DownloadsScreen instances don't pile up. Since pop and navigate are within a single navigate() call, the transition is seamless with no flicker.

中: popUpTo(navItems.first().first) 在导航前弹出首页之上的所有目的地,确保旧的 DownloadsScreen 实例不会堆积。由于弹出和导航在同一个 navigate() 调用中,过渡动画无缝无闪烁。


2. DownloadsScreen.kt — Accept gid via route arg + auto-scroll / 通过路由参数接收 gid + 自动滚动

Function signature / 函数签名

// EN: Add scrollToGid route parameter with default -1L (no scroll)
// 中:添加 scrollToGid 路由参数,默认值 -1L(不滚动)
fun AnimatedVisibilityScope.DownloadsScreen(
    navigator: DestinationsNavigator,
    scrollToGid: Long = -1L
) = Screen(navigator) {

New imports / 新增导入

import androidx.compose.foundation.lazy.staggeredgrid.LazyStaggeredGridState
import androidx.compose.foundation.lazy.staggeredgrid.rememberLazyStaggeredGridState
import androidx.compose.runtime.snapshotFlow
import kotlinx.coroutines.flow.first

EN: LazyStaggeredGridState and rememberLazyStaggeredGridState are required for controlling the grid view's scroll state. snapshotFlow converts Compose state into a Flow for reactive waiting.

中: LazyStaggeredGridStaterememberLazyStaggeredGridState 用于控制网格视图的滚动状态。snapshotFlow 将 Compose 状态转换为 Flow 进行响应式等待。

Scroll states / 滚动状态

val listState = rememberLazyListState()
val gridState = rememberLazyStaggeredGridState()

EN: Two separate state objects are created because the list and grid views use different state types. They are passed to the respective views so scrolling can be controlled programmatically.

中: 创建两个独立的状态对象,因为列表和网格视图使用不同的状态类型。它们被传递给各自的视图,以便以编程方式控制滚动。

// EN: Pass to grid view / 中:传递给网格视图
FastScrollLazyVerticalStaggeredGrid(state = gridState, ...)

// EN: Pass to list view / 中:传递给列表视图
FastScrollLazyColumn(state = listState, ...)

Auto-scroll logic / 自动滚动逻辑

LaunchedEffect(scrollToGid) {
    if (scrollToGid == -1L) return@LaunchedEffect
    // EN: Reactively wait for download list to finish loading
    // 中:响应式等待下载列表加载完成
    snapshotFlow { isLoading }.first { !it }
    // EN: Let the layout settle after data load / 中:数据加载后让布局稳定
    delay(300)
    // EN: Find target gallery in the filtered list / 中:在过滤后的列表中找到目标画廊
    val index = list.indexOfFirst { it.gid == scrollToGid }
    if (index >= 0) {
        // EN: Animate scroll, supporting both list and grid views
        // 中:动画滚动,同时支持列表和网格视图
        if (gridView) {
            gridState.animateScrollToItem(index)
        } else {
            listState.animateScrollToItem(index)
        }
    }
}

Files changed / 改动文件统计

File / 文件 Insertions / 新增 Deletions / 删除 Key changes / 关键改动
MainActivity.kt 10 3 Extract gid + route arg navigation + popUpTo + () refs
DownloadsScreen.kt 25 1 scrollToGid param + scroll states + auto-scroll LaunchedEffect

EN: No changes to DownloadManager.kt, ci.yml, or any other files. The fix is fully contained in two files and follows standard compose-destinations route args pattern, consistent with GalleryCommentsScreen(gid: Long), ProgressScreen(gid, token, page), etc.

中: DownloadManager.ktci.yml 及其他文件均无改动。修复完全集中在两个文件中,遵循标准的 compose-destinations route args 模式,与 GalleryCommentsScreen(gid: Long)ProgressScreen(gid, token, page) 等一致。


Testing / 测试方案

  1. EN: Start downloading 2+ galleries sequentially. While one is active, pull down the notification shade and tap the download notification. Verify the Downloads screen scrolls to the currently downloading gallery.
    中: 开始顺序下载 2 个以上画廊。在正在下载时下拉通知栏并点击下载通知。确认下载页面滚动到当前正在下载的画廊。

  2. EN: Switch to grid view and repeat test 1.
    中: 切换到网格视图后重复测试 1。

  3. EN: Tap notification again while already on the Downloads screen. Verify it scrolls again.
    中: 已在下载页面时再次点击通知。确认再次滚动。

  4. EN: After tapping notification, press back. Verify you return directly to the home page (not through an old Downloads screen instance).
    中: 点击通知后按返回键。确认直接返回首页(不会经过旧的下载页面)。

Comment thread app/src/main/kotlin/com/hippo/ehviewer/ui/MainActivity.kt Outdated
@revonateB0T
revonateB0T requested a review from FooIbar July 5, 2026 17:27
Comment thread app/src/main/kotlin/com/hippo/ehviewer/ui/screen/DownloadsScreen.kt Outdated
@SauronSkywalker
SauronSkywalker force-pushed the source-20260703-142148 branch from 5ba71ba to 5ebfb07 Compare July 9, 2026 14:51
navigator.navigate(DownloadsScreenDestination)
val gid = args.getLong(DownloadService.KEY_GID, -1L)
navigator.navigate(DownloadsScreenDestination(gid)) {
popUpTo(navItems.first().first) {

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

没什么必要

val gridState = rememberLazyStaggeredGridState()

LaunchedEffect(scrollToGid) {
if (scrollToGid == -1L) return@LaunchedEffect

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

提到 LaunchedEffect 外面

LaunchedEffect(scrollToGid) {
if (scrollToGid == -1L) return@LaunchedEffect
snapshotFlow { isLoading }.first { !it }
delay(300)

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

没什么必要

snapshotFlow { isLoading }.first { !it }
delay(300)
val index = list.indexOfFirst { it.gid == scrollToGid }
if (index >= 0) {

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

index != -1

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants