diff --git a/basicswap/basicswap.py b/basicswap/basicswap.py index fa27225..7f132d3 100644 --- a/basicswap/basicswap.py +++ b/basicswap/basicswap.py @@ -1509,6 +1509,7 @@ class BasicSwap(BaseApp, BSXNetwork, UIApp): try: if version_tuple(latest_tag) > version_tuple(current_version): + self._latest_version = latest_tag if not self._update_available: self._update_available = True self.log.info( @@ -1528,6 +1529,7 @@ class BasicSwap(BaseApp, BSXNetwork, UIApp): self.log.info(f"Update v{latest_tag} already notified") else: self._update_available = False + self._latest_version = None self.log.info(f"BasicSwap is up to date (v{current_version})") except ValueError as e: self.log.warning(f"Error comparing versions: {e}") @@ -2026,6 +2028,13 @@ class BasicSwap(BaseApp, BSXNetwork, UIApp): if self.ws_server and show_event: self.ws_server.send_message_to_all(json.dumps(event_data)) + elif event_type == NT.UPDATE_AVAILABLE: + self.log.info( + f"Update available: v{event_data.get('latest_version', 'unknown')}" + ) + if self.ws_server and show_event: + event_data["event"] = "update_available" + self.ws_server.send_message_to_all(json.dumps(event_data)) else: self.log.warning(f"Unknown notification {event_type}") diff --git a/basicswap/js_server.py b/basicswap/js_server.py index a29d64e..1ecd597 100644 --- a/basicswap/js_server.py +++ b/basicswap/js_server.py @@ -952,6 +952,32 @@ def js_notifications(self, url_split, post_string, is_json) -> bytes: return bytes(json.dumps(swap_client.getNotifications()), "UTF-8") +def js_updatestatus(self, url_split, post_string, is_json) -> bytes: + swap_client = self.server.swap_client + from basicswap import __version__ + + return bytes( + json.dumps( + { + "update_available": swap_client._update_available, + "current_version": __version__, + "latest_version": swap_client._latest_version, + "release_url": ( + f"https://github.com/basicswap/basicswap/releases/tag/v{swap_client._latest_version}" + if swap_client._latest_version + else None + ), + "release_notes": ( + f"New version v{swap_client._latest_version} is available. Click to view details on GitHub." + if swap_client._latest_version + else None + ), + } + ), + "UTF-8", + ) + + def js_identities(self, url_split, post_string: str, is_json: bool) -> bytes: swap_client = self.server.swap_client swap_client.checkSystemStatus() @@ -1559,6 +1585,7 @@ endpoints = { "rateslist": js_rates_list, "generatenotification": js_generatenotification, "checkupdates": js_checkupdates, + "updatestatus": js_updatestatus, "notifications": js_notifications, "identities": js_identities, "automationstrategies": js_automationstrategies, diff --git a/basicswap/static/js/modules/notification-manager.js b/basicswap/static/js/modules/notification-manager.js index fcea181..183568b 100644 --- a/basicswap/static/js/modules/notification-manager.js +++ b/basicswap/static/js/modules/notification-manager.js @@ -321,6 +321,7 @@ function ensureToastContainer() { updateHistoryDropdown(); this.initializeBalanceTracking(); + this.checkForPendingUpdateNotification(); if (window.CleanupManager) { window.CleanupManager.registerResource('notificationManager', this, (mgr) => { @@ -331,6 +332,29 @@ function ensureToastContainer() { return this; }, + checkForPendingUpdateNotification: function() { + CleanupManager.setTimeout(async () => { + try { + const response = await fetch('/json/updatestatus'); + const updateStatus = await response.json(); + + if (updateStatus.update_available && config.showUpdateNotifications) { + this.createToast( + `Update Available: v${updateStatus.latest_version}`, + 'update_available', + { + subtitle: `Current: v${updateStatus.current_version} • Click to view release`, + releaseUrl: updateStatus.release_url, + releaseNotes: updateStatus.release_notes + } + ); + } + } catch (error) { + console.error('Error checking for pending update notification:', error); + } + }, 2000); + }, + updateSettings: function(newSettings) { saveConfig(newSettings); return this;