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:
parent
992c23d524
commit
22f06af0bc
4 changed files with 88 additions and 30 deletions
36
claude-progress.txt
Normal file
36
claude-progress.txt
Normal 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
|
||||||
48
dev-init.sh
48
dev-init.sh
|
|
@ -1,33 +1,43 @@
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
# Development environment setup for Open Notebook
|
# Development environment startup for Open Notebook
|
||||||
# This script installs dependencies and starts all required services
|
# Assumes SurrealDB is already running externally (per .env config)
|
||||||
|
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
echo "=== Open Notebook Development Setup ==="
|
echo "=== Open Notebook Dev Startup ==="
|
||||||
|
|
||||||
# 1. Install Python dependencies
|
# Check SurrealDB connectivity
|
||||||
echo "Installing Python dependencies..."
|
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
|
uv sync
|
||||||
|
|
||||||
# 2. Install frontend dependencies
|
echo "Syncing frontend dependencies..."
|
||||||
echo "Installing frontend dependencies..."
|
|
||||||
cd frontend && npm install && cd ..
|
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 ""
|
||||||
echo "=== Starting Services ==="
|
echo "✅ All services starting!"
|
||||||
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 " Frontend: http://localhost:3000"
|
echo " Frontend: http://localhost:3000"
|
||||||
echo " API: http://localhost:5055"
|
echo " API: http://localhost:5055"
|
||||||
echo " API Docs: http://localhost:5055/docs"
|
echo " API Docs: http://localhost:5055/docs"
|
||||||
echo ""
|
echo ""
|
||||||
echo "To check status: make status"
|
cd frontend && npm run dev
|
||||||
echo "To stop all: make stop-all"
|
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@
|
||||||
"Step 4: Verify the toast notification appears",
|
"Step 4: Verify the toast notification appears",
|
||||||
"Step 5: Verify the source list automatically updates to include the new source without manual page refresh"
|
"Step 5: Verify the source list automatically updates to include the new source without manual page refresh"
|
||||||
],
|
],
|
||||||
"passes": false
|
"passes": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"category": "functional",
|
"category": "functional",
|
||||||
|
|
@ -21,7 +21,7 @@
|
||||||
"Step 4: Verify the toast notification appears",
|
"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"
|
"Step 5: Verify the source list automatically updates to include the new uploaded source without manual page refresh"
|
||||||
],
|
],
|
||||||
"passes": false
|
"passes": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"category": "functional",
|
"category": "functional",
|
||||||
|
|
@ -31,7 +31,7 @@
|
||||||
"Step 2: Add a new text source via the add source dialog",
|
"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"
|
"Step 3: Verify the source list automatically updates to include the new text source"
|
||||||
],
|
],
|
||||||
"passes": false
|
"passes": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"category": "functional",
|
"category": "functional",
|
||||||
|
|
@ -41,7 +41,7 @@
|
||||||
"Step 2: Use the batch mode to add multiple URLs at once",
|
"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"
|
"Step 3: Verify the source list updates to show the newly added sources"
|
||||||
],
|
],
|
||||||
"passes": false
|
"passes": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"category": "functional",
|
"category": "functional",
|
||||||
|
|
@ -53,7 +53,7 @@
|
||||||
"Step 4: Wait for processing to complete",
|
"Step 4: Wait for processing to complete",
|
||||||
"Step 5: Verify the source status updates to completed without manual refresh (via useSourceStatus polling)"
|
"Step 5: Verify the source status updates to completed without manual refresh (via useSourceStatus polling)"
|
||||||
],
|
],
|
||||||
"passes": false
|
"passes": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"category": "functional",
|
"category": "functional",
|
||||||
|
|
@ -64,7 +64,7 @@
|
||||||
"Step 3: Add an existing source from another notebook and verify it appears",
|
"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"
|
"Step 4: Remove a source from the notebook and verify it disappears"
|
||||||
],
|
],
|
||||||
"passes": false
|
"passes": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"category": "functional",
|
"category": "functional",
|
||||||
|
|
@ -76,7 +76,7 @@
|
||||||
"Step 4: Navigate to another targeted notebook",
|
"Step 4: Navigate to another targeted notebook",
|
||||||
"Step 5: Verify that notebook's source list also shows the new source"
|
"Step 5: Verify that notebook's source list also shows the new source"
|
||||||
],
|
],
|
||||||
"passes": false
|
"passes": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"category": "style",
|
"category": "style",
|
||||||
|
|
@ -88,6 +88,6 @@
|
||||||
"Step 4: Verify additional sources load correctly",
|
"Step 4: Verify additional sources load correctly",
|
||||||
"Step 5: Add a new source and verify the list updates without layout jumps or visual glitches"
|
"Step 5: Add a new source and verify the list updates without layout jumps or visual glitches"
|
||||||
],
|
],
|
||||||
"passes": false
|
"passes": true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -99,7 +99,11 @@ export function useCreateSource() {
|
||||||
variables.notebooks.forEach(notebookId => {
|
variables.notebooks.forEach(notebookId => {
|
||||||
queryClient.invalidateQueries({
|
queryClient.invalidateQueries({
|
||||||
queryKey: QUERY_KEYS.sources(notebookId),
|
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) {
|
} else if (variables.notebook_id) {
|
||||||
|
|
@ -107,6 +111,10 @@ export function useCreateSource() {
|
||||||
queryKey: QUERY_KEYS.sources(variables.notebook_id),
|
queryKey: QUERY_KEYS.sources(variables.notebook_id),
|
||||||
refetchType: 'active'
|
refetchType: 'active'
|
||||||
})
|
})
|
||||||
|
queryClient.invalidateQueries({
|
||||||
|
queryKey: QUERY_KEYS.sourcesInfinite(variables.notebook_id),
|
||||||
|
refetchType: 'active'
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Invalidate general sources query too with immediate refetch
|
// Invalidate general sources query too with immediate refetch
|
||||||
|
|
@ -201,8 +209,12 @@ export function useFileUpload() {
|
||||||
mutationFn: ({ file, notebookId }: { file: File; notebookId: string }) =>
|
mutationFn: ({ file, notebookId }: { file: File; notebookId: string }) =>
|
||||||
sourcesApi.upload(file, notebookId),
|
sourcesApi.upload(file, notebookId),
|
||||||
onSuccess: (_, variables) => {
|
onSuccess: (_, variables) => {
|
||||||
queryClient.invalidateQueries({
|
queryClient.invalidateQueries({
|
||||||
queryKey: QUERY_KEYS.sources(variables.notebookId)
|
queryKey: QUERY_KEYS.sources(variables.notebookId)
|
||||||
|
})
|
||||||
|
queryClient.invalidateQueries({
|
||||||
|
queryKey: QUERY_KEYS.sourcesInfinite(variables.notebookId),
|
||||||
|
refetchType: 'active'
|
||||||
})
|
})
|
||||||
toast({
|
toast({
|
||||||
title: t.common.success,
|
title: t.common.success,
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue