Skip to content

feat: debounced object list refresh on upload/delete task completion#65

Merged
overtrue merged 2 commits intomainfrom
copilot/debounce-refresh-object-list
Feb 27, 2026
Merged

feat: debounced object list refresh on upload/delete task completion#65
overtrue merged 2 commits intomainfrom
copilot/debounce-refresh-object-list

Conversation

Copy link
Contributor

Copilot AI commented Feb 27, 2026

Object list was refreshing immediately on task enqueue rather than task completion — meaning uploads/deletes hadn't actually happened yet. Additionally, no debounce meant bulk uploads caused continuous list re-renders and flickering.

Changes

contexts/task-context.tsx

  • Added bucketName?: string to AnyTask to enable per-bucket task filtering

app/(dashboard)/browser/content.tsx

  • Subscribe to useTasks() and watch for completed upload/delete tasks scoped to the current bucket
  • Debounce refresh by 1500ms — resets on each new completion, fires once after the burst settles
  • Track seen task IDs in a Set ref; prune stale IDs when tasks are removed from the manager to prevent unbounded growth
  • Remove onSuccess={handleRefresh} from ObjectUploadPicker — refresh is now driven by actual completion, not dialog dismissal

components/object/list.tsx

  • Remove premature (onRefresh ?? fetchObjects)() calls from handleDelete / handleDeleteAllVersions — tasks weren't executed yet at that point
// In BrowserContent: fires once, 1500ms after the last task completes for this bucket
React.useEffect(() => {
  const currentIds = new Set(tasks.map((t) => t.id))
  for (const id of prevCompletedIdsRef.current) {
    if (!currentIds.has(id)) prevCompletedIdsRef.current.delete(id)
  }
  const newCompletions = tasks.filter(
    (t) =>
      (t.kind === "upload" || t.kind === "delete") &&
      t.bucketName === bucketName &&
      t.status === "completed" &&
      !prevCompletedIdsRef.current.has(t.id),
  )
  if (newCompletions.length > 0) {
    newCompletions.forEach((t) => prevCompletedIdsRef.current.add(t.id))
    if (debounceTimerRef.current) clearTimeout(debounceTimerRef.current)
    debounceTimerRef.current = setTimeout(() => setRefreshTrigger((n) => n + 1), 1500)
  }
}, [tasks, bucketName, setRefreshTrigger])

Type of Change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)
  • Documentation update
  • Code refactoring
  • Performance improvement
  • Test improvements
  • Security fix

Testing

  • Unit tests added/updated
  • Manual testing completed
pnpm test:run

Checklist

  • Code follows the project's style guidelines
  • Self-review completed
  • TypeScript types are properly defined
  • All commit messages are in English (Conventional Commits)
  • All existing tests pass
  • No new dependencies added, or they are justified

Screenshots (if applicable)

Additional Notes

The manual refresh button in the object list toolbar remains functional and unaffected.

Original prompt

This section details on the original issue you should resolve

<issue_title>[BUG] When an upload or delete task is completed, the object list should be refreshed.</issue_title>
<issue_description></issue_description>

<agent_instructions>需要 debounce 刷新,不然如果上传大量文件,列表会持续刷新出现闪烁,请按最佳实践修改</agent_instructions>

Comments on the Issue (you are @copilot in this section)


💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.

- Add bucketName to AnyTask type for task filtering
- Watch task completions per bucket in BrowserContent with 1500ms debounce
- Clean up stale IDs from tracking set when tasks are removed
- Remove premature refresh on upload enqueue (upload-picker onSuccess)
- Remove premature refresh on delete enqueue (list.tsx handlers)

Co-authored-by: overtrue <1472352+overtrue@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix object list refresh after upload or delete tasks feat: debounced object list refresh on upload/delete task completion Feb 27, 2026
@overtrue overtrue marked this pull request as ready for review February 27, 2026 08:48
Copilot AI review requested due to automatic review settings February 27, 2026 08:48
@overtrue overtrue merged commit 195314d into main Feb 27, 2026
8 checks passed
@overtrue overtrue deleted the copilot/debounce-refresh-object-list branch February 27, 2026 08:49
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR adjusts the object browser refresh behavior so the object list updates after upload/delete task completion (not enqueue), and reduces UI flicker by debouncing refreshes during bursts of completed tasks.

Changes:

  • Add optional bucketName to AnyTask to support per-bucket filtering.
  • In the browser content view, listen for completed upload/delete tasks for the current bucket and trigger a debounced refresh.
  • Remove immediate refresh calls after delete task creation (since the deletion hasn’t executed yet).

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.

File Description
contexts/task-context.tsx Extends AnyTask with bucketName for bucket-scoped task logic.
app/(dashboard)/browser/content.tsx Adds task-completion-driven, debounced refresh logic and removes upload-picker success refresh wiring.
components/object/list.tsx Stops refreshing immediately after enqueueing delete tasks.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 10 to 31
export type UploadTask = {
id: string
kind: "upload"
status: string
progress: number
error?: string
displayName: string
subInfo: string
actionLabel: string
}

export type AnyTask = {
id: string
kind: string
status: string
progress: number
error?: string
displayName: string
subInfo: string
actionLabel: string
bucketName?: string
}
Copy link

Copilot AI Feb 27, 2026

Choose a reason for hiding this comment

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

AnyTask now includes bucketName, and upload/delete tasks created by the helpers already have a required bucketName. However, the exported UploadTask type in this file does not include bucketName, so it’s now out of sync with the actual shape of upload tasks and the new per-bucket filtering usage. Consider either adding bucketName to UploadTask here or removing this local UploadTask type in favor of importing the canonical UploadTask from lib/upload-task to avoid type drift.

Copilot uses AI. Check for mistakes.
Comment on lines +127 to +133
}, [tasks, bucketName, setRefreshTrigger])

React.useEffect(() => {
return () => {
if (debounceTimerRef.current) clearTimeout(debounceTimerRef.current)
}
}, [])
Copy link

Copilot AI Feb 27, 2026

Choose a reason for hiding this comment

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

The debounce timer is only cleared on unmount (separate effect). If bucketName changes while a timer is pending (e.g., navigation within the same component instance), the scheduled callback can still fire and trigger a refresh unrelated to the new bucket state. Consider clearing any existing debounceTimerRef.current in the main effect cleanup (or whenever bucketName changes) so pending timers don’t leak across bucket switches/rerenders.

Suggested change
}, [tasks, bucketName, setRefreshTrigger])
React.useEffect(() => {
return () => {
if (debounceTimerRef.current) clearTimeout(debounceTimerRef.current)
}
}, [])
return () => {
if (debounceTimerRef.current) {
clearTimeout(debounceTimerRef.current)
debounceTimerRef.current = null
}
}
}, [tasks, bucketName, setRefreshTrigger])

Copilot uses AI. Check for mistakes.
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.

[BUG] When an upload or delete task is completed, the object list should be refreshed.

3 participants