diff --git a/basicswap/basicswap.py b/basicswap/basicswap.py index fa27225..cc565c1 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}") @@ -11260,6 +11269,16 @@ class BasicSwap(BaseApp, BSXNetwork, UIApp): settings_copy["notifications_outgoing_transactions"] = new_value settings_changed = True + if "notifications_swap_completed" in data: + new_value = data["notifications_swap_completed"] + ensure( + isinstance(new_value, bool), + "New notifications_swap_completed value not boolean", + ) + if settings_copy.get("notifications_swap_completed", True) != new_value: + settings_copy["notifications_swap_completed"] = new_value + settings_changed = True + if "notifications_duration" in data: new_value = data["notifications_duration"] ensure( @@ -11274,6 +11293,15 @@ class BasicSwap(BaseApp, BSXNetwork, UIApp): settings_copy["notifications_duration"] = new_value settings_changed = True + if "check_updates" in data: + new_value = data["check_updates"] + ensure( + isinstance(new_value, bool), "New check_updates value not boolean" + ) + if settings_copy.get("check_updates", True) != new_value: + settings_copy["check_updates"] = new_value + settings_changed = True + if settings_changed: settings_path = os.path.join(self.data_dir, cfg.CONFIG_FILENAME) settings_path_new = settings_path + ".new" 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; diff --git a/basicswap/templates/settings.html b/basicswap/templates/settings.html index ce8b7a7..716dad0 100644 --- a/basicswap/templates/settings.html +++ b/basicswap/templates/settings.html @@ -492,7 +492,7 @@