mirror of
https://github.com/basicswap/basicswap.git
synced 2025-11-06 02:38:11 +01:00
Show notification when new release of BSX
This commit is contained in:
@@ -382,6 +382,13 @@ class BasicSwap(BaseApp, BSXNetwork, UIApp):
|
|||||||
self._expiring_offers = [] # List of offers expiring soon
|
self._expiring_offers = [] # List of offers expiring soon
|
||||||
self._updating_wallets_info = {}
|
self._updating_wallets_info = {}
|
||||||
self._last_updated_wallets_info = 0
|
self._last_updated_wallets_info = 0
|
||||||
|
|
||||||
|
self.check_updates_seconds = self.get_int_setting(
|
||||||
|
"check_updates_seconds", 24 * 60 * 60, 60 * 60, 7 * 24 * 60 * 60
|
||||||
|
)
|
||||||
|
self._last_checked_updates = 0
|
||||||
|
self._latest_version = None
|
||||||
|
self._update_available = False
|
||||||
self._notifications_enabled = self.settings.get("notifications_enabled", True)
|
self._notifications_enabled = self.settings.get("notifications_enabled", True)
|
||||||
self._disabled_notification_types = self.settings.get(
|
self._disabled_notification_types = self.settings.get(
|
||||||
"disabled_notification_types", []
|
"disabled_notification_types", []
|
||||||
@@ -1325,6 +1332,65 @@ class BasicSwap(BaseApp, BSXNetwork, UIApp):
|
|||||||
if ci.isWalletLocked():
|
if ci.isWalletLocked():
|
||||||
raise LockedCoinError(Coins.PART)
|
raise LockedCoinError(Coins.PART)
|
||||||
|
|
||||||
|
def checkForUpdates(self) -> None:
|
||||||
|
if not self.settings.get("check_updates", True):
|
||||||
|
return
|
||||||
|
|
||||||
|
now = time.time()
|
||||||
|
if now - self._last_checked_updates < self.check_updates_seconds:
|
||||||
|
return
|
||||||
|
|
||||||
|
self._last_checked_updates = now
|
||||||
|
self.log.info("Checking for BasicSwap updates...")
|
||||||
|
|
||||||
|
try:
|
||||||
|
url = "https://api.github.com/repos/basicswap/basicswap/tags"
|
||||||
|
response_data = self.readURL(url, timeout=30)
|
||||||
|
tags_data = json.loads(response_data.decode("utf-8"))
|
||||||
|
|
||||||
|
if not tags_data or not isinstance(tags_data, list) or len(tags_data) == 0:
|
||||||
|
self.log.warning("Could not determine latest version from GitHub tags")
|
||||||
|
return
|
||||||
|
|
||||||
|
latest_tag = tags_data[0].get("name", "").lstrip("v")
|
||||||
|
if not latest_tag:
|
||||||
|
self.log.warning("Could not determine latest version from GitHub tags")
|
||||||
|
return
|
||||||
|
|
||||||
|
self._latest_version = latest_tag
|
||||||
|
current_version = __version__
|
||||||
|
|
||||||
|
def version_tuple(v):
|
||||||
|
return tuple(map(int, v.split(".")))
|
||||||
|
|
||||||
|
try:
|
||||||
|
if version_tuple(latest_tag) > version_tuple(current_version):
|
||||||
|
if not self._update_available:
|
||||||
|
self._update_available = True
|
||||||
|
self.log.info(
|
||||||
|
f"Update available: v{latest_tag} (current: v{current_version})"
|
||||||
|
)
|
||||||
|
|
||||||
|
self.notify(
|
||||||
|
NT.UPDATE_AVAILABLE,
|
||||||
|
{
|
||||||
|
"current_version": current_version,
|
||||||
|
"latest_version": latest_tag,
|
||||||
|
"release_url": f"https://github.com/basicswap/basicswap/releases/tag/v{latest_tag}",
|
||||||
|
"release_notes": f"New version v{latest_tag} is available. Click to view details on GitHub.",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
self.log.info(f"Update v{latest_tag} already notified")
|
||||||
|
else:
|
||||||
|
self._update_available = False
|
||||||
|
self.log.info(f"BasicSwap is up to date (v{current_version})")
|
||||||
|
except ValueError as e:
|
||||||
|
self.log.warning(f"Error comparing versions: {e}")
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
self.log.warning(f"Failed to check for updates: {e}")
|
||||||
|
|
||||||
def isBaseCoinActive(self, c) -> bool:
|
def isBaseCoinActive(self, c) -> bool:
|
||||||
if c not in chainparams:
|
if c not in chainparams:
|
||||||
return False
|
return False
|
||||||
@@ -10798,6 +10864,9 @@ class BasicSwap(BaseApp, BSXNetwork, UIApp):
|
|||||||
self.checkDelayedAutoAccept()
|
self.checkDelayedAutoAccept()
|
||||||
self._last_checked_delayed_auto_accept = now
|
self._last_checked_delayed_auto_accept = now
|
||||||
|
|
||||||
|
if now - self._last_checked_updates >= self.check_updates_seconds:
|
||||||
|
self.checkForUpdates()
|
||||||
|
|
||||||
except Exception as ex:
|
except Exception as ex:
|
||||||
self.logException(f"update {ex}")
|
self.logException(f"update {ex}")
|
||||||
|
|
||||||
|
|||||||
@@ -244,6 +244,7 @@ class NotificationTypes(IntEnum):
|
|||||||
BID_RECEIVED = auto()
|
BID_RECEIVED = auto()
|
||||||
BID_ACCEPTED = auto()
|
BID_ACCEPTED = auto()
|
||||||
SWAP_COMPLETED = auto()
|
SWAP_COMPLETED = auto()
|
||||||
|
UPDATE_AVAILABLE = auto()
|
||||||
|
|
||||||
|
|
||||||
class ConnectionRequestTypes(IntEnum):
|
class ConnectionRequestTypes(IntEnum):
|
||||||
|
|||||||
@@ -842,9 +842,19 @@ def js_generatenotification(self, url_split, post_string, is_json) -> bytes:
|
|||||||
if not swap_client.debug:
|
if not swap_client.debug:
|
||||||
raise ValueError("Debug mode not active.")
|
raise ValueError("Debug mode not active.")
|
||||||
|
|
||||||
r = random.randint(0, 3)
|
r = random.randint(0, 4)
|
||||||
if r == 0:
|
if r == 0:
|
||||||
swap_client.notify(NT.OFFER_RECEIVED, {"offer_id": random.randbytes(28).hex()})
|
swap_client.notify(
|
||||||
|
NT.OFFER_RECEIVED,
|
||||||
|
{
|
||||||
|
"offer_id": random.randbytes(28).hex(),
|
||||||
|
"coin_from": 2,
|
||||||
|
"coin_to": 6,
|
||||||
|
"amount_from": 100000000,
|
||||||
|
"amount_to": 15500000000000,
|
||||||
|
"rate": 15500000000000,
|
||||||
|
},
|
||||||
|
)
|
||||||
elif r == 1:
|
elif r == 1:
|
||||||
swap_client.notify(
|
swap_client.notify(
|
||||||
NT.BID_RECEIVED,
|
NT.BID_RECEIVED,
|
||||||
@@ -852,6 +862,13 @@ def js_generatenotification(self, url_split, post_string, is_json) -> bytes:
|
|||||||
"type": "atomic",
|
"type": "atomic",
|
||||||
"bid_id": random.randbytes(28).hex(),
|
"bid_id": random.randbytes(28).hex(),
|
||||||
"offer_id": random.randbytes(28).hex(),
|
"offer_id": random.randbytes(28).hex(),
|
||||||
|
"coin_from": 2,
|
||||||
|
"coin_to": 6,
|
||||||
|
"amount_from": 100000000,
|
||||||
|
"amount_to": 15500000000000,
|
||||||
|
"bid_amount": 50000000,
|
||||||
|
"bid_amount_to": 7750000000000,
|
||||||
|
"rate": 15500000000000,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
elif r == 2:
|
elif r == 2:
|
||||||
@@ -863,12 +880,71 @@ def js_generatenotification(self, url_split, post_string, is_json) -> bytes:
|
|||||||
"type": "ads",
|
"type": "ads",
|
||||||
"bid_id": random.randbytes(28).hex(),
|
"bid_id": random.randbytes(28).hex(),
|
||||||
"offer_id": random.randbytes(28).hex(),
|
"offer_id": random.randbytes(28).hex(),
|
||||||
|
"coin_from": 1,
|
||||||
|
"coin_to": 3,
|
||||||
|
"amount_from": 500000000,
|
||||||
|
"amount_to": 100000000,
|
||||||
|
"bid_amount": 250000000,
|
||||||
|
"bid_amount_to": 50000000,
|
||||||
|
"rate": 20000000,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
elif r == 4:
|
||||||
|
swap_client.notify(NT.SWAP_COMPLETED, {"bid_id": random.randbytes(28).hex()})
|
||||||
|
|
||||||
return bytes(json.dumps({"type": r}), "UTF-8")
|
return bytes(json.dumps({"type": r}), "UTF-8")
|
||||||
|
|
||||||
|
|
||||||
|
def js_checkupdates(self, url_split, post_string, is_json) -> bytes:
|
||||||
|
swap_client = self.server.swap_client
|
||||||
|
from basicswap import __version__
|
||||||
|
|
||||||
|
if not swap_client.settings.get("check_updates", True):
|
||||||
|
return bytes(
|
||||||
|
json.dumps({"error": "Update checking is disabled in settings"}), "UTF-8"
|
||||||
|
)
|
||||||
|
|
||||||
|
import time
|
||||||
|
|
||||||
|
now = time.time()
|
||||||
|
last_manual_check = getattr(swap_client, "_last_manual_update_check", 0)
|
||||||
|
|
||||||
|
if not swap_client.debug and (now - last_manual_check) < 3600:
|
||||||
|
remaining = int(3600 - (now - last_manual_check))
|
||||||
|
return bytes(
|
||||||
|
json.dumps(
|
||||||
|
{
|
||||||
|
"error": f"Please wait {remaining // 60} minutes before checking again"
|
||||||
|
}
|
||||||
|
),
|
||||||
|
"UTF-8",
|
||||||
|
)
|
||||||
|
|
||||||
|
swap_client._last_manual_update_check = now
|
||||||
|
swap_client.log.info("Manual update check requested via web interface")
|
||||||
|
|
||||||
|
swap_client.checkForUpdates()
|
||||||
|
|
||||||
|
if swap_client._update_available:
|
||||||
|
swap_client.log.info(
|
||||||
|
f"Manual check result: Update available v{swap_client._latest_version} (current: v{__version__})"
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
swap_client.log.info(f"Manual check result: Up to date (v{__version__})")
|
||||||
|
|
||||||
|
return bytes(
|
||||||
|
json.dumps(
|
||||||
|
{
|
||||||
|
"message": "Update check completed",
|
||||||
|
"current_version": __version__,
|
||||||
|
"latest_version": swap_client._latest_version,
|
||||||
|
"update_available": swap_client._update_available,
|
||||||
|
}
|
||||||
|
),
|
||||||
|
"UTF-8",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def js_notifications(self, url_split, post_string, is_json) -> bytes:
|
def js_notifications(self, url_split, post_string, is_json) -> bytes:
|
||||||
swap_client = self.server.swap_client
|
swap_client = self.server.swap_client
|
||||||
swap_client.checkSystemStatus()
|
swap_client.checkSystemStatus()
|
||||||
@@ -1377,6 +1453,7 @@ endpoints = {
|
|||||||
"rates": js_rates,
|
"rates": js_rates,
|
||||||
"rateslist": js_rates_list,
|
"rateslist": js_rates_list,
|
||||||
"generatenotification": js_generatenotification,
|
"generatenotification": js_generatenotification,
|
||||||
|
"checkupdates": js_checkupdates,
|
||||||
"notifications": js_notifications,
|
"notifications": js_notifications,
|
||||||
"identities": js_identities,
|
"identities": js_identities,
|
||||||
"automationstrategies": js_automationstrategies,
|
"automationstrategies": js_automationstrategies,
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ const NotificationManager = (function() {
|
|||||||
showBalanceChanges: true,
|
showBalanceChanges: true,
|
||||||
showOutgoingTransactions: true,
|
showOutgoingTransactions: true,
|
||||||
showSwapCompleted: true,
|
showSwapCompleted: true,
|
||||||
|
showUpdateNotifications: true,
|
||||||
notificationDuration: 20000
|
notificationDuration: 20000
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -67,6 +68,7 @@ const NotificationManager = (function() {
|
|||||||
coinSymbol: options.coinSymbol || '',
|
coinSymbol: options.coinSymbol || '',
|
||||||
coinFrom: options.coinFrom || null,
|
coinFrom: options.coinFrom || null,
|
||||||
coinTo: options.coinTo || null,
|
coinTo: options.coinTo || null,
|
||||||
|
releaseUrl: options.releaseUrl || null,
|
||||||
timestamp: new Date().toLocaleString(),
|
timestamp: new Date().toLocaleString(),
|
||||||
timestampMs: Date.now()
|
timestampMs: Date.now()
|
||||||
};
|
};
|
||||||
@@ -183,6 +185,10 @@ const NotificationManager = (function() {
|
|||||||
return `window.location.href='/bids'`;
|
return `window.location.href='/bids'`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (item.type === 'update_available' && item.releaseUrl) {
|
||||||
|
return `window.open('${item.releaseUrl}', '_blank')`;
|
||||||
|
}
|
||||||
|
|
||||||
if (item.title.includes('offer') || item.title.includes('Offer')) {
|
if (item.title.includes('offer') || item.title.includes('Offer')) {
|
||||||
return `window.location.href='/offers'`;
|
return `window.location.href='/offers'`;
|
||||||
}
|
}
|
||||||
@@ -231,6 +237,9 @@ function ensureToastContainer() {
|
|||||||
'balance_change': `<svg class="w-5 h-5" fill="currentColor" viewBox="0 0 20 20">
|
'balance_change': `<svg class="w-5 h-5" fill="currentColor" viewBox="0 0 20 20">
|
||||||
<path fill-rule="evenodd" d="M4 4a2 2 0 00-2 2v4a2 2 0 002 2V6h10a2 2 0 00-2-2H4zm2 6a2 2 0 012-2h8a2 2 0 012 2v4a2 2 0 01-2 2H8a2 2 0 01-2-2v-4zm6 4a2 2 0 100-4 2 2 0 000 4z" clip-rule="evenodd"></path>
|
<path fill-rule="evenodd" d="M4 4a2 2 0 00-2 2v4a2 2 0 002 2V6h10a2 2 0 00-2-2H4zm2 6a2 2 0 012-2h8a2 2 0 012 2v4a2 2 0 01-2 2H8a2 2 0 01-2-2v-4zm6 4a2 2 0 100-4 2 2 0 000 4z" clip-rule="evenodd"></path>
|
||||||
</svg>`,
|
</svg>`,
|
||||||
|
'update_available': `<svg class="w-5 h-5" fill="currentColor" viewBox="0 0 20 20">
|
||||||
|
<path fill-rule="evenodd" d="M4 2a1 1 0 011 1v2.101a7.002 7.002 0 0111.601 2.566 1 1 0 11-1.885.666A5.002 5.002 0 005.999 7H9a1 1 0 010 2H4a1 1 0 01-1-1V3a1 1 0 011-1zm.008 9.057a1 1 0 011.276.61A5.002 5.002 0 0014.001 13H11a1 1 0 110-2h5a1 1 0 011 1v5a1 1 0 11-2 0v-2.101a7.002 7.002 0 01-11.601-2.566 1 1 0 01.61-1.276z" clip-rule="evenodd"></path>
|
||||||
|
</svg>`,
|
||||||
'success': `<svg class="w-5 h-5" fill="currentColor" viewBox="0 0 20 20">
|
'success': `<svg class="w-5 h-5" fill="currentColor" viewBox="0 0 20 20">
|
||||||
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clip-rule="evenodd"></path>
|
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clip-rule="evenodd"></path>
|
||||||
</svg>`
|
</svg>`
|
||||||
@@ -245,6 +254,7 @@ function ensureToastContainer() {
|
|||||||
'bid_accepted': 'bg-purple-500',
|
'bid_accepted': 'bg-purple-500',
|
||||||
'swap_completed': 'bg-green-600',
|
'swap_completed': 'bg-green-600',
|
||||||
'balance_change': 'bg-yellow-500',
|
'balance_change': 'bg-yellow-500',
|
||||||
|
'update_available': 'bg-blue-600',
|
||||||
'success': 'bg-blue-500'
|
'success': 'bg-blue-500'
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -425,6 +435,18 @@ function ensureToastContainer() {
|
|||||||
);
|
);
|
||||||
}, 4000);
|
}, 4000);
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
this.createToast(
|
||||||
|
'Update Available: v0.15.0',
|
||||||
|
'update_available',
|
||||||
|
{
|
||||||
|
subtitle: 'Current: v0.14.6 • Click to view release',
|
||||||
|
releaseUrl: 'https://github.com/basicswap/basicswap/releases/tag/v0.15.0',
|
||||||
|
releaseNotes: 'New version v0.15.0 is available. Click to view details on GitHub.'
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}, 4500);
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
initializeBalanceTracking: function() {
|
initializeBalanceTracking: function() {
|
||||||
@@ -466,7 +488,6 @@ function ensureToastContainer() {
|
|||||||
const staleThreshold = 10 * 60 * 1000;
|
const staleThreshold = 10 * 60 * 1000;
|
||||||
|
|
||||||
if (!lastFetch || (now - parseInt(lastFetch)) > staleThreshold) {
|
if (!lastFetch || (now - parseInt(lastFetch)) > staleThreshold) {
|
||||||
console.log('Resetting stale balance tracking to prevent false notifications');
|
|
||||||
this.resetBalanceTracking();
|
this.resetBalanceTracking();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -504,6 +525,8 @@ function ensureToastContainer() {
|
|||||||
const iconColor = getToastColor(type, options);
|
const iconColor = getToastColor(type, options);
|
||||||
const icon = getToastIcon(type);
|
const icon = getToastIcon(type);
|
||||||
|
|
||||||
|
const isPersistent = type === 'update_available';
|
||||||
|
|
||||||
let coinIconHtml = '';
|
let coinIconHtml = '';
|
||||||
if (options.coinSymbol) {
|
if (options.coinSymbol) {
|
||||||
const coinIcon = getCoinIcon(options.coinSymbol);
|
const coinIcon = getCoinIcon(options.coinSymbol);
|
||||||
@@ -523,6 +546,9 @@ function ensureToastContainer() {
|
|||||||
} else if (options.coinSymbol) {
|
} else if (options.coinSymbol) {
|
||||||
clickAction = `onclick="window.location.href='/wallet/${options.coinSymbol}'"`;
|
clickAction = `onclick="window.location.href='/wallet/${options.coinSymbol}'"`;
|
||||||
cursorStyle = 'cursor-pointer';
|
cursorStyle = 'cursor-pointer';
|
||||||
|
} else if (options.releaseUrl) {
|
||||||
|
clickAction = `onclick="window.open('${options.releaseUrl}', '_blank')"`;
|
||||||
|
cursorStyle = 'cursor-pointer';
|
||||||
}
|
}
|
||||||
|
|
||||||
message.innerHTML = `
|
message.innerHTML = `
|
||||||
@@ -558,6 +584,7 @@ function ensureToastContainer() {
|
|||||||
`;
|
`;
|
||||||
messages.appendChild(message);
|
messages.appendChild(message);
|
||||||
|
|
||||||
|
if (!isPersistent) {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
if (message.parentNode) {
|
if (message.parentNode) {
|
||||||
message.classList.add('toast-slide-out');
|
message.classList.add('toast-slide-out');
|
||||||
@@ -569,6 +596,7 @@ function ensureToastContainer() {
|
|||||||
}, 300);
|
}, 300);
|
||||||
}
|
}
|
||||||
}, config.notificationDuration);
|
}, config.notificationDuration);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
handleWebSocketEvent: function(data) {
|
handleWebSocketEvent: function(data) {
|
||||||
@@ -633,6 +661,15 @@ function ensureToastContainer() {
|
|||||||
shouldShowToast = config.showSwapCompleted;
|
shouldShowToast = config.showSwapCompleted;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case 'update_available':
|
||||||
|
toastTitle = `Update Available: v${data.latest_version}`;
|
||||||
|
toastOptions.subtitle = `Current: v${data.current_version} • Click to view release`;
|
||||||
|
toastOptions.releaseUrl = data.release_url;
|
||||||
|
toastOptions.releaseNotes = data.release_notes;
|
||||||
|
toastType = 'update_available';
|
||||||
|
shouldShowToast = config.showUpdateNotifications;
|
||||||
|
break;
|
||||||
|
|
||||||
case 'coin_balance_updated':
|
case 'coin_balance_updated':
|
||||||
if (data.coin && config.showBalanceChanges) {
|
if (data.coin && config.showBalanceChanges) {
|
||||||
this.handleBalanceUpdate(data);
|
this.handleBalanceUpdate(data);
|
||||||
|
|||||||
@@ -503,6 +503,17 @@
|
|||||||
<p class="text-xs text-gray-500 dark:text-gray-400 ml-7 mt-1">Show notifications when swaps complete successfully</p>
|
<p class="text-xs text-gray-500 dark:text-gray-400 ml-7 mt-1">Show notifications when swaps complete successfully</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="py-2">
|
||||||
|
<div class="flex items-center">
|
||||||
|
<input type="checkbox" id="check_updates" name="check_updates" value="true" class="w-4 h-4 text-blue-600 bg-gray-100 border-gray-300 rounded focus:ring-blue-500 dark:focus:ring-blue-600 dark:ring-offset-gray-800 focus:ring-2 dark:bg-gray-600 dark:border-gray-500"{% if general_settings.check_updates %} checked{% endif %}>
|
||||||
|
<label for="check_updates" class="ml-3 text-sm font-medium text-gray-700 dark:text-gray-300">Update Notifications</label>
|
||||||
|
<button type="button" onclick="checkForUpdatesNow()" class="ml-3 text-xs bg-gray-600 hover:bg-gray-700 text-white font-medium py-1 px-3 rounded transition-colors focus:outline-none">
|
||||||
|
Check Now
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<p class="text-xs text-gray-500 dark:text-gray-400 ml-7 mt-1">Check for BasicSwap updates and show notifications when available</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
@@ -537,10 +548,16 @@
|
|||||||
<div>
|
<div>
|
||||||
<h4 class="text-sm font-medium text-gray-900 dark:text-white mb-4">Test Notifications</h4>
|
<h4 class="text-sm font-medium text-gray-900 dark:text-white mb-4">Test Notifications</h4>
|
||||||
<div class="bg-gray-50 dark:bg-gray-700 rounded-lg p-4">
|
<div class="bg-gray-50 dark:bg-gray-700 rounded-lg p-4">
|
||||||
<div>
|
<div class="space-y-3">
|
||||||
<button type="button" onclick="window.NotificationManager && window.NotificationManager.testToasts()" class="bg-blue-600 hover:bg-blue-700 text-white font-medium py-2 px-4 rounded-lg transition-colors focus:outline-none">
|
<button type="button" onclick="window.NotificationManager && window.NotificationManager.testToasts()" class="bg-blue-600 hover:bg-blue-700 text-white font-medium py-2 px-4 rounded-lg transition-colors focus:outline-none">
|
||||||
Test All Notification Types
|
Test All Notification Types
|
||||||
</button>
|
</button>
|
||||||
|
<button type="button" onclick="testUpdateNotification()" class="bg-blue-600 hover:bg-blue-700 text-white font-medium py-2 px-4 rounded-lg transition-colors focus:outline-none">
|
||||||
|
Test Update Notification
|
||||||
|
</button>
|
||||||
|
<button type="button" onclick="testLiveUpdateCheck()" class="bg-green-600 hover:bg-green-700 text-white font-medium py-2 px-4 rounded-lg transition-colors focus:outline-none">
|
||||||
|
Test Live Update Check
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -570,6 +587,7 @@
|
|||||||
showBalanceChanges: document.getElementById('notifications_balance_changes').checked,
|
showBalanceChanges: document.getElementById('notifications_balance_changes').checked,
|
||||||
showOutgoingTransactions: document.getElementById('notifications_outgoing_transactions').checked,
|
showOutgoingTransactions: document.getElementById('notifications_outgoing_transactions').checked,
|
||||||
showSwapCompleted: document.getElementById('notifications_swap_completed').checked,
|
showSwapCompleted: document.getElementById('notifications_swap_completed').checked,
|
||||||
|
showUpdateNotifications: document.getElementById('check_updates').checked,
|
||||||
notificationDuration: parseInt(document.getElementById('notifications_duration').value) * 1000
|
notificationDuration: parseInt(document.getElementById('notifications_duration').value) * 1000
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -581,6 +599,141 @@
|
|||||||
setTimeout(syncNotificationSettings, 100);
|
setTimeout(syncNotificationSettings, 100);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function testUpdateNotification() {
|
||||||
|
if (window.NotificationManager) {
|
||||||
|
window.NotificationManager.createToast(
|
||||||
|
'Update Available: v0.15.0',
|
||||||
|
'update_available',
|
||||||
|
{
|
||||||
|
subtitle: 'Current: v{{ version }} • Click to view release',
|
||||||
|
releaseUrl: 'https://github.com/basicswap/basicswap/releases/tag/v0.15.0',
|
||||||
|
releaseNotes: 'New version v0.15.0 is available. Click to view details on GitHub.'
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function testLiveUpdateCheck() {
|
||||||
|
const button = event.target;
|
||||||
|
const originalText = button.textContent;
|
||||||
|
button.textContent = 'Checking...';
|
||||||
|
button.disabled = true;
|
||||||
|
|
||||||
|
fetch('/json/checkupdates', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(data => {
|
||||||
|
if (window.NotificationManager) {
|
||||||
|
if (data.update_available) {
|
||||||
|
window.NotificationManager.createToast(
|
||||||
|
`Live Update Available: v${data.latest_version}`,
|
||||||
|
'update_available',
|
||||||
|
{
|
||||||
|
subtitle: `Current: v${data.current_version} • Click to view release`,
|
||||||
|
releaseUrl: `https://github.com/basicswap/basicswap/releases/tag/v${data.latest_version}`,
|
||||||
|
releaseNotes: 'This is a real update check from GitHub API.'
|
||||||
|
}
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
window.NotificationManager.createToast(
|
||||||
|
'No Updates Available',
|
||||||
|
'success',
|
||||||
|
{
|
||||||
|
subtitle: `Current version v${data.current_version} is up to date`
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.error('Update check failed:', error);
|
||||||
|
if (window.NotificationManager) {
|
||||||
|
window.NotificationManager.createToast(
|
||||||
|
'Update Check Failed',
|
||||||
|
'error',
|
||||||
|
{
|
||||||
|
subtitle: 'Could not check for updates. See console for details.'
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
button.textContent = originalText;
|
||||||
|
button.disabled = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkForUpdatesNow() {
|
||||||
|
const button = event.target;
|
||||||
|
const originalText = button.textContent;
|
||||||
|
button.textContent = 'Checking...';
|
||||||
|
button.disabled = true;
|
||||||
|
|
||||||
|
fetch('/json/checkupdates', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(data => {
|
||||||
|
if (data.error) {
|
||||||
|
if (window.NotificationManager) {
|
||||||
|
window.NotificationManager.createToast(
|
||||||
|
'Update Check Failed',
|
||||||
|
'error',
|
||||||
|
{
|
||||||
|
subtitle: data.error
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (window.NotificationManager) {
|
||||||
|
if (data.update_available) {
|
||||||
|
window.NotificationManager.createToast(
|
||||||
|
`Update Available: v${data.latest_version}`,
|
||||||
|
'update_available',
|
||||||
|
{
|
||||||
|
subtitle: `Current: v${data.current_version} • Click to view release`,
|
||||||
|
releaseUrl: `https://github.com/basicswap/basicswap/releases/tag/v${data.latest_version}`,
|
||||||
|
releaseNotes: `New version v${data.latest_version} is available. Click to view details on GitHub.`
|
||||||
|
}
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
window.NotificationManager.createToast(
|
||||||
|
'You\'re Up to Date!',
|
||||||
|
'success',
|
||||||
|
{
|
||||||
|
subtitle: `Current version v${data.current_version} is the latest`
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.error('Update check failed:', error);
|
||||||
|
if (window.NotificationManager) {
|
||||||
|
window.NotificationManager.createToast(
|
||||||
|
'Update Check Failed',
|
||||||
|
'error',
|
||||||
|
{
|
||||||
|
subtitle: 'Network error. Please try again later.'
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
button.textContent = originalText;
|
||||||
|
button.disabled = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
document.addEventListener('DOMContentLoaded', function() {
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
syncNotificationSettings();
|
syncNotificationSettings();
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -665,7 +665,7 @@ function fillDonationAddress(address, type) {
|
|||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if donation_info %}
|
{% if debug_ui and donation_info %}
|
||||||
<tr>
|
<tr>
|
||||||
<td colspan="2" class="py-3 px-6">
|
<td colspan="2" class="py-3 px-6">
|
||||||
<div class="p-4 bg-coolGray-100 dark:bg-gray-500 border border-coolGray-200 dark:border-gray-400 rounded-lg">
|
<div class="p-4 bg-coolGray-100 dark:bg-gray-500 border border-coolGray-200 dark:border-gray-400 rounded-lg">
|
||||||
|
|||||||
@@ -94,6 +94,9 @@ def page_settings(self, url_split, post_string):
|
|||||||
"notifications_duration": int(
|
"notifications_duration": int(
|
||||||
get_data_entry_or(form_data, "notifications_duration", "20")
|
get_data_entry_or(form_data, "notifications_duration", "20")
|
||||||
),
|
),
|
||||||
|
"check_updates": toBool(
|
||||||
|
get_data_entry_or(form_data, "check_updates", "true")
|
||||||
|
),
|
||||||
}
|
}
|
||||||
swap_client.editGeneralSettings(data)
|
swap_client.editGeneralSettings(data)
|
||||||
messages.append("Notification settings applied.")
|
messages.append("Notification settings applied.")
|
||||||
@@ -207,6 +210,7 @@ def page_settings(self, url_split, post_string):
|
|||||||
"debug": swap_client.debug,
|
"debug": swap_client.debug,
|
||||||
"debug_ui": swap_client.debug_ui,
|
"debug_ui": swap_client.debug_ui,
|
||||||
"expire_db_records": swap_client._expire_db_records,
|
"expire_db_records": swap_client._expire_db_records,
|
||||||
|
"check_updates": swap_client.settings.get("check_updates", True),
|
||||||
}
|
}
|
||||||
|
|
||||||
chart_api_key = get_api_key_setting(
|
chart_api_key = get_api_key_setting(
|
||||||
|
|||||||
@@ -410,5 +410,6 @@ def page_wallet(self, url_split, post_string):
|
|||||||
"summary": summary,
|
"summary": summary,
|
||||||
"block_unknown_seeds": swap_client._restrict_unknown_seed_wallets,
|
"block_unknown_seeds": swap_client._restrict_unknown_seed_wallets,
|
||||||
"donation_info": donation_info,
|
"donation_info": donation_info,
|
||||||
|
"debug_ui": swap_client.debug_ui,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user