feat: Implement asynchronous update check on startup with UI integration

This commit is contained in:
claudi 2026-01-29 08:44:03 +01:00
parent 5b28c931d8
commit 41549848ed
3 changed files with 246 additions and 2 deletions

View file

@ -53,6 +53,9 @@ def main() -> int:
window.show()
logger.info("Main window opened successfully")
# Check for updates on startup (non-blocking, async)
window.check_for_updates_startup()
# Run event loop
return app.exec()

View file

@ -1,16 +1,20 @@
"""Main application window with web engine integration."""
import asyncio
import logging
from pathlib import Path
from typing import Optional
from PySide6.QtCore import QSize, Qt, QUrl, Signal
from PySide6.QtWidgets import QMainWindow, QToolBar, QVBoxLayout, QWidget, QLabel, QStatusBar
from PySide6.QtCore import QSize, Qt, QThread, QUrl, Signal
from PySide6.QtWidgets import QLabel, QMainWindow, QStatusBar, QToolBar, QVBoxLayout, QWidget
from webdrop_bridge.config import Config
from webdrop_bridge.core.drag_interceptor import DragInterceptor
from webdrop_bridge.core.validator import PathValidator
from webdrop_bridge.ui.restricted_web_view import RestrictedWebEngineView
logger = logging.getLogger(__name__)
# Default welcome page HTML when no webapp is configured
DEFAULT_WELCOME_PAGE = """
<!DOCTYPE html>
@ -175,6 +179,7 @@ class MainWindow(QMainWindow):
# Signals
check_for_updates = Signal()
update_available = Signal(object) # Emits Release object
def __init__(
self,
@ -424,3 +429,111 @@ class MainWindow(QMainWindow):
True if drag was initiated successfully
"""
return self.drag_interceptor.initiate_drag(file_paths)
def check_for_updates_startup(self) -> None:
"""Check for updates on application startup.
Runs asynchronously in background without blocking UI.
Uses 24h cache so won't hammer the API.
"""
from webdrop_bridge.core.updater import UpdateManager
try:
# Create update manager
cache_dir = Path.home() / ".webdrop-bridge"
manager = UpdateManager(
current_version=self.config.app_version,
config_dir=cache_dir
)
# Run async check in background
self._run_async_check(manager)
except Exception as e:
logger.error(f"Failed to initialize update check: {e}")
def _run_async_check(self, manager) -> None:
"""Run update check in background thread.
Args:
manager: UpdateManager instance
"""
# Create and start background thread
thread = QThread()
worker = UpdateCheckWorker(manager, self.config.app_version)
# Connect signals
worker.update_available.connect(self._on_update_available)
worker.update_status.connect(self._on_update_status)
worker.finished.connect(thread.quit)
# Start thread
worker.moveToThread(thread)
thread.started.connect(worker.run)
thread.start()
def _on_update_status(self, status: str, emoji: str) -> None:
"""Handle update status changes.
Args:
status: Status text
emoji: Status emoji
"""
self.set_update_status(status, emoji)
def _on_update_available(self, release) -> None:
"""Handle update available notification.
Args:
release: Release object with update info
"""
# Update status to show update available
self.set_update_status(f"Update available: v{release.version}", emoji="")
# Emit signal for main app to show dialog
self.update_available.emit(release)
class UpdateCheckWorker:
"""Worker for running update check asynchronously."""
def __init__(self, manager, current_version: str):
"""Initialize worker.
Args:
manager: UpdateManager instance
current_version: Current app version
"""
self.manager = manager
self.current_version = current_version
# Create signals
from PySide6.QtCore import Signal
self.update_available = Signal(object)
self.update_status = Signal(str, str)
self.finished = Signal()
def run(self) -> None:
"""Run the update check."""
try:
# Notify checking status
self.update_status.emit("Checking for updates", "🔄")
# Run async check
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
release = loop.run_until_complete(self.manager.check_for_updates())
loop.close()
# Emit result
if release:
self.update_available.emit(release)
else:
# No update available - show ready status
self.update_status.emit("Ready", "")
except Exception as e:
logger.error(f"Update check failed: {e}")
self.update_status.emit("Update check failed", "⚠️")
finally:
self.finished.emit()