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:
@@ -335,11 +335,46 @@ templ CollectionDetail(user User, collection CollectionDetailData, books []BookD
|
||||
},
|
||||
}{ end }
|
||||
];
|
||||
let ws = null;
|
||||
|
||||
function backToCollections() {
|
||||
window.location.href = '/collections';
|
||||
}
|
||||
|
||||
function connectWebSocket() {
|
||||
const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
|
||||
const wsUrl = `${protocol}//${window.location.host}/ws/sync`;
|
||||
|
||||
ws = new WebSocket(wsUrl);
|
||||
|
||||
ws.onopen = function() {
|
||||
console.log('WebSocket connected');
|
||||
};
|
||||
|
||||
ws.onmessage = function(event) {
|
||||
const message = JSON.parse(event.data);
|
||||
|
||||
if (message.type === 'collection_updated' && message.data.collection_id === collectionId) {
|
||||
showToast(`Collection updated: ${message.data.action} (${message.data.count} books)`, 'info');
|
||||
|
||||
setTimeout(() => {
|
||||
location.reload();
|
||||
}, 1000);
|
||||
}
|
||||
};
|
||||
|
||||
ws.onclose = function() {
|
||||
console.log('WebSocket disconnected, reconnecting in 5s...');
|
||||
setTimeout(connectWebSocket, 5000);
|
||||
};
|
||||
|
||||
ws.onerror = function(error) {
|
||||
console.error('WebSocket error:', error);
|
||||
};
|
||||
}
|
||||
|
||||
connectWebSocket();
|
||||
|
||||
function filterCollectionBooks() {
|
||||
const searchTerm = document.getElementById('collection-search').value.toLowerCase();
|
||||
const booksContainer = document.getElementById('books-container');
|
||||
|
||||
Reference in New Issue
Block a user