mirror of
https://github.com/calibrain/shelfmark.git
synced 2026-02-20 07:46:18 -05:00
This PR was coauthored by alexhb1 and davidemarcoli. It builds on the FE rework created by alex, but adds a myriad of additional tweaks and optimizations to make the frontend feel modern, fast, and responsive. The summary of the changes is as follows: ### Architecture Changes React/TypeScript Migration: Refactored frontend from template/JS structure to React/TypeScript application for better maintainability and scalability WebSocket Integration: Implemented real-time updates for download status and progress with automatic fallback to polling Gevent Worker: Configured production WebSocket support ### UI/UX Improvements <img width="1502" height="890" alt="Screenshot 2025-11-10 at 10 02 59 AM" src="https://github.com/user-attachments/assets/86bf8649-623f-413c-b8e5-656e687e55a8" /> Downloads Sidebar: Replaced bottom downloads section with sidebar interface for better organization <img width="201" height="450" alt="Screenshot 2025-11-10 at 10 07 52 AM" src="https://github.com/user-attachments/assets/92b98e7c-c3bc-4b7e-80f1-252c3a760e33" /> Status Badges: Color-coded download status indicators instead of plain text Pinned Header: Fixed header position for consistent navigation Enhanced Book Cards: Improved layout and hover states with info modal button <img width="1474" height="899" alt="Screenshot 2025-11-10 at 10 08 18 AM" src="https://github.com/user-attachments/assets/9216d8a3-f662-434d-80e6-2a69b96abc31" /> Download Progress: Circular progress indicator on download buttons Toast Notifications: Added user feedback for actions Spinner Feedback: Loading indicators on search and download buttons Animations: Smooth transitions and fluid progress updates ### Mobile & Responsive Design Mobile-friendly Layouts: Optimized book cards and search interface for mobile <img width="225" height="450" alt="Screenshot 2025-11-10 at 10 05 49 AM" src="https://github.com/user-attachments/assets/c8236c1c-5837-4309-9577-46db7292a54b" /> Keyboard Handling: Improved mobile keyboard behavior with proper input types PWA Improvements: Enhanced progressive web app functionality Responsive Search: Better search box width and positioning across devices ### Developer Experience Development Mode: Separate frontend dev server that works with existing backend container Makefile: Added build automation and development commands Documentation: Updated README with frontend architecture details ### Bug Fixes Fixed "Clear completed" functionality Fixed dark mode toggle text Fixed sticky header behavior Fixed mobile search box positioning Removed active downloads requirement for initial state view ### Additional Features ESC Key: Close downloads sidebar with ESC key Calibre-Web Button: Direct link to Calibre-Web instance <img width="282" height="83" alt="Screenshot 2025-11-11 at 9 38 05 AM" src="https://github.com/user-attachments/assets/273075be-9743-4e13-9e48-5bf498f6c067" /> Granular Status Tracking: More detailed download progress information obtained via websockets --------- Co-authored-by: Alex <alex.bilbie1@gmail.com> Co-authored-by: Zack Yancey <yanceyz@proton.me> Co-authored-by: davidemarcoli <davide@marcoli.ch>
73 lines
2.7 KiB
Python
73 lines
2.7 KiB
Python
"""WebSocket manager for real-time status updates."""
|
|
|
|
import logging
|
|
from typing import Optional, Dict, Any
|
|
from flask_socketio import SocketIO, emit
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
class WebSocketManager:
|
|
"""Manages WebSocket connections and broadcasts."""
|
|
|
|
def __init__(self):
|
|
self.socketio: Optional[SocketIO] = None
|
|
self._enabled = False
|
|
|
|
def init_app(self, app, socketio: SocketIO):
|
|
"""Initialize the WebSocket manager with Flask-SocketIO instance."""
|
|
self.socketio = socketio
|
|
self._enabled = True
|
|
logger.info("WebSocket manager initialized")
|
|
|
|
def is_enabled(self) -> bool:
|
|
"""Check if WebSocket is enabled and ready."""
|
|
return self._enabled and self.socketio is not None
|
|
|
|
def broadcast_status_update(self, status_data: Dict[str, Any]):
|
|
"""Broadcast status update to all connected clients."""
|
|
if not self.is_enabled():
|
|
return
|
|
|
|
try:
|
|
# When calling socketio.emit() outside event handlers, it broadcasts by default
|
|
self.socketio.emit('status_update', status_data)
|
|
logger.debug(f"Broadcasted status update to all clients")
|
|
except Exception as e:
|
|
logger.error(f"Error broadcasting status update: {e}")
|
|
|
|
def broadcast_download_progress(self, book_id: str, progress: float, status: str):
|
|
"""Broadcast download progress update for a specific book."""
|
|
if not self.is_enabled():
|
|
return
|
|
|
|
try:
|
|
data = {
|
|
'book_id': book_id,
|
|
'progress': progress,
|
|
'status': status
|
|
}
|
|
# When calling socketio.emit() outside event handlers, it broadcasts by default
|
|
self.socketio.emit('download_progress', data)
|
|
logger.debug(f"Broadcasted progress for book {book_id}: {progress}%")
|
|
except Exception as e:
|
|
logger.error(f"Error broadcasting download progress: {e}")
|
|
|
|
def broadcast_notification(self, message: str, notification_type: str = 'info'):
|
|
"""Broadcast a notification message to all clients."""
|
|
if not self.is_enabled():
|
|
return
|
|
|
|
try:
|
|
data = {
|
|
'message': message,
|
|
'type': notification_type
|
|
}
|
|
# When calling socketio.emit() outside event handlers, it broadcasts by default
|
|
self.socketio.emit('notification', data)
|
|
logger.debug(f"Broadcasted notification: {message}")
|
|
except Exception as e:
|
|
logger.error(f"Error broadcasting notification: {e}")
|
|
|
|
# Global WebSocket manager instance
|
|
ws_manager = WebSocketManager()
|