fix: invalidate sourcesInfinite query key on source creation (#526)

useCreateSource and useFileUpload only invalidated QUERY_KEYS.sources()
which doesn't match the infinite scroll query key used by the notebook
page (QUERY_KEYS.sourcesInfinite()). Added sourcesInfinite invalidation
to both hooks so the source list auto-refreshes after adding sources.
This commit is contained in:
Luis Novo 2026-04-06 07:05:02 -03:00
parent 992c23d524
commit 22f06af0bc
4 changed files with 88 additions and 30 deletions

36
claude-progress.txt Normal file
View file

@ -0,0 +1,36 @@
Session 1 - Initializer Agent - 2026-04-06
## Completed
- Read and analyzed GitHub issue #526
- Researched the full source creation and listing flow (frontend + backend)
- Identified root cause: useCreateSource and useFileUpload don't invalidate QUERY_KEYS.sourcesInfinite
- Created claude-goal.md with detailed analysis
- Created feature_list.json with 8 test cases
- Created dev-init.sh
- Created branch: fix/source-list-auto-refresh-526
- Made initial commit
## Root Cause Summary
- useNotebookSources queries with key ['sources', 'infinite', notebookId]
- useCreateSource invalidates ['sources', notebookId] - doesn't match infinite key
- useFileUpload invalidates ['sources', notebookId] - doesn't match infinite key
- React Query prefix matching: ['sources', notebookId] does NOT match ['sources', 'infinite', notebookId]
## Fix Required
File: frontend/src/lib/hooks/use-sources.ts
1. In useCreateSource onSuccess (~line 96-129):
- After each QUERY_KEYS.sources(notebookId) invalidation, add QUERY_KEYS.sourcesInfinite(notebookId) invalidation
2. In useFileUpload onSuccess (~line 203-210):
- After QUERY_KEYS.sources(variables.notebookId) invalidation, add QUERY_KEYS.sourcesInfinite(variables.notebookId) invalidation
## Branch
fix/source-list-auto-refresh-526
## Next Steps for Coding Agent
1. Apply the fix to use-sources.ts (add sourcesInfinite invalidation)
2. Run frontend linting/type checks
3. Test manually or write unit tests
4. Mark feature_list.json tests as passing
5. Create PR

View file

@ -1,33 +1,43 @@
#!/bin/bash
# Development environment setup for Open Notebook
# This script installs dependencies and starts all required services
# Development environment startup for Open Notebook
# Assumes SurrealDB is already running externally (per .env config)
set -e
echo "=== Open Notebook Development Setup ==="
echo "=== Open Notebook Dev Startup ==="
# 1. Install Python dependencies
echo "Installing Python dependencies..."
# Check SurrealDB connectivity
SURREAL_PORT=${SURREAL_PORT:-8018}
echo "Checking SurrealDB on port $SURREAL_PORT..."
if ! nc -z localhost "$SURREAL_PORT" 2>/dev/null; then
echo "❌ SurrealDB not reachable on port $SURREAL_PORT. Please start it first."
exit 1
fi
echo "✅ SurrealDB is running"
# Install dependencies if needed
echo "Syncing Python dependencies..."
uv sync
# 2. Install frontend dependencies
echo "Installing frontend dependencies..."
echo "Syncing frontend dependencies..."
cd frontend && npm install && cd ..
# 3. Start services using Makefile
# Start API backend in background
echo "Starting API backend (port 5055)..."
uv run --env-file .env run_api.py &
sleep 3
# Start background worker in background
echo "Starting background worker..."
uv run --env-file .env surreal-commands-worker --import-modules commands &
sleep 2
# Start frontend (foreground)
echo "Starting Next.js frontend (port 3000)..."
echo ""
echo "=== Starting Services ==="
echo "Use 'make start-all' to start all services (DB + API + Worker + Frontend)"
echo "Or start individually:"
echo " make database - Start SurrealDB"
echo " make api - Start FastAPI backend (port 5055)"
echo " make worker-start - Start background worker"
echo " make frontend - Start Next.js frontend (port 3000)"
echo ""
echo "Access points:"
echo "✅ All services starting!"
echo " Frontend: http://localhost:3000"
echo " API: http://localhost:5055"
echo " API Docs: http://localhost:5055/docs"
echo ""
echo "To check status: make status"
echo "To stop all: make stop-all"
cd frontend && npm run dev

View file

@ -9,7 +9,7 @@
"Step 4: Verify the toast notification appears",
"Step 5: Verify the source list automatically updates to include the new source without manual page refresh"
],
"passes": false
"passes": true
},
{
"category": "functional",
@ -21,7 +21,7 @@
"Step 4: Verify the toast notification appears",
"Step 5: Verify the source list automatically updates to include the new uploaded source without manual page refresh"
],
"passes": false
"passes": true
},
{
"category": "functional",
@ -31,7 +31,7 @@
"Step 2: Add a new text source via the add source dialog",
"Step 3: Verify the source list automatically updates to include the new text source"
],
"passes": false
"passes": true
},
{
"category": "functional",
@ -41,7 +41,7 @@
"Step 2: Use the batch mode to add multiple URLs at once",
"Step 3: Verify the source list updates to show the newly added sources"
],
"passes": false
"passes": true
},
{
"category": "functional",
@ -53,7 +53,7 @@
"Step 4: Wait for processing to complete",
"Step 5: Verify the source status updates to completed without manual refresh (via useSourceStatus polling)"
],
"passes": false
"passes": true
},
{
"category": "functional",
@ -64,7 +64,7 @@
"Step 3: Add an existing source from another notebook and verify it appears",
"Step 4: Remove a source from the notebook and verify it disappears"
],
"passes": false
"passes": true
},
{
"category": "functional",
@ -76,7 +76,7 @@
"Step 4: Navigate to another targeted notebook",
"Step 5: Verify that notebook's source list also shows the new source"
],
"passes": false
"passes": true
},
{
"category": "style",
@ -88,6 +88,6 @@
"Step 4: Verify additional sources load correctly",
"Step 5: Add a new source and verify the list updates without layout jumps or visual glitches"
],
"passes": false
"passes": true
}
]

View file

@ -99,7 +99,11 @@ export function useCreateSource() {
variables.notebooks.forEach(notebookId => {
queryClient.invalidateQueries({
queryKey: QUERY_KEYS.sources(notebookId),
refetchType: 'active' // Refetch active queries immediately
refetchType: 'active'
})
queryClient.invalidateQueries({
queryKey: QUERY_KEYS.sourcesInfinite(notebookId),
refetchType: 'active'
})
})
} else if (variables.notebook_id) {
@ -107,6 +111,10 @@ export function useCreateSource() {
queryKey: QUERY_KEYS.sources(variables.notebook_id),
refetchType: 'active'
})
queryClient.invalidateQueries({
queryKey: QUERY_KEYS.sourcesInfinite(variables.notebook_id),
refetchType: 'active'
})
}
// Invalidate general sources query too with immediate refetch
@ -201,8 +209,12 @@ export function useFileUpload() {
mutationFn: ({ file, notebookId }: { file: File; notebookId: string }) =>
sourcesApi.upload(file, notebookId),
onSuccess: (_, variables) => {
queryClient.invalidateQueries({
queryKey: QUERY_KEYS.sources(variables.notebookId)
queryClient.invalidateQueries({
queryKey: QUERY_KEYS.sources(variables.notebookId)
})
queryClient.invalidateQueries({
queryKey: QUERY_KEYS.sourcesInfinite(variables.notebookId),
refetchType: 'active'
})
toast({
title: t.common.success,