feat(collections): add real-time updates via WebSocket (Limitation #4)

Implement real-time collection updates when books are added/removed:

Backend Changes:
- Added connManager to CollectionHandler struct
- Updated constructor to accept ConnectionManager
- Updated all NewCollectionHandler() calls in ebook.go and main.go
- Added WebSocket broadcasts in AddBooks() handler
- Added WebSocket broadcasts in BulkRemoveBooks() handler
- Broadcasts collection_updated events with:
  - collection_id: Which collection changed
  - action: books_added or books_removed
  - book_ids: Array of affected book IDs
  - count: Number of books changed

Frontend Changes:
- Added WebSocket connection in collections UI
- connectWebSocket() establishes connection to /ws/sync
- Listens for collection_updated events
- Shows toast notification on collection change
- Auto-reloads page after 1 second to show updated book list
- Auto-reconnect on disconnect (5s delay)
- Error handling for WebSocket failures

WebSocket Event Format:
{
  "type": "collection_updated",
  "timestamp": "2026-02-01T12:00:00Z",
  "data": {
    "collection_id": "uuid",
    "action": "books_added",
    "book_ids": ["uuid1", "uuid2"],
    "count": 2
  }
}

User Experience:
- When another user adds books to a collection, all connected clients see:
  1. Toast notification: "Collection updated: books_added (2 books)"
  2. Page auto-refreshes after 1 second
  3. Updated book list displays
- Same for book removal
- Works across multiple browser tabs/devices
- No manual refresh needed

Technical Notes:
- Broadcasts to ALL connected WebSocket clients
- Client-side filtering by collection_id
- Existing progress/conflict broadcasts continue to work
- Connection manager handles broadcast distribution

Resolves Limitation #4: Real-time Collection Updates
This commit is contained in:
2026-02-01 00:54:41 -05:00
parent 4bc7a842b5
commit 40c732481a
5 changed files with 78 additions and 6 deletions
+1 -2
View File
@@ -607,7 +607,6 @@ func main() {
return c.HTML(http.StatusOK, buf.String())
})
// Queue Management route (protected) - SSR version
protected.GET("/queue", func(c echo.Context) error {
userID := c.Get("user_id").(string)
@@ -659,7 +658,7 @@ func main() {
})
// Collections management route (protected) - SSR version
collectionHandler := handlers.NewCollectionHandler(queries)
collectionHandler := handlers.NewCollectionHandler(queries, connManager)
collections := protected.Group("/collections")
collections.GET("", func(c echo.Context) error {
user, err := getTemplateUserWithTheme(c, queries)