mirror of
https://github.com/basicswap/basicswap.git
synced 2025-11-05 18:38:09 +01:00
918 lines
39 KiB
HTML
918 lines
39 KiB
HTML
<!DOCTYPE html>
|
|
{% from 'style.html' import change_password_svg, notifications_network_offer_svg,
|
|
notifications_bid_accepted_svg, notifications_unknow_event_svg,
|
|
notifications_new_bid_on_offer_svg, notifications_close_svg, swap_in_progress_mobile_svg,
|
|
wallet_svg, page_back_svg, order_book_svg, new_offer_svg, settings_svg, asettings_svg,
|
|
cog_svg, rpc_svg, debug_svg, explorer_svg, tor_svg, smsg_svg, outputs_svg, automation_svg,
|
|
shutdown_svg, notifications_svg, debug_nerd_svg, wallet_locked_svg, mobile_menu_svg,
|
|
wallet_unlocked_svg, tor_purple_svg, sun_svg, moon_svg, swap_in_progress_svg,
|
|
swap_in_progress_green_svg, available_bids_svg, your_offers_svg, bids_received_svg,
|
|
bids_sent_svg, header_arrow_down_svg, love_svg %}
|
|
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
{% if refresh %}
|
|
<meta http-equiv="refresh" content="{{ refresh }}">
|
|
{% endif %}
|
|
|
|
<!-- Scripts -->
|
|
<script src="/static/js/libs/chart.js"></script>
|
|
<script src="/static/js/libs/chartjs-adapter-date-fns.bundle.min.js"></script>
|
|
<script src="/static/js/main.js"></script>
|
|
<script src="/static/js/tabs.js"></script>
|
|
<script src="/static/js/dropdown.js"></script>
|
|
<script src="/static/js/libs/popper.js"></script>
|
|
<script src="/static/js/libs/tippy.js"></script>
|
|
<script src="/static/js/tooltips.js"></script>
|
|
|
|
<!-- Styles -->
|
|
<link type="text/css" media="all" href="/static/css/libs/flowbite.min.css" rel="stylesheet" />
|
|
<link type="text/css" media="all" href="/static/css/libs/tailwind.min.css" rel="stylesheet">
|
|
<link type="text/css" media="all" href="/static/css/style.css" rel="stylesheet">
|
|
|
|
<link rel="icon" sizes="32x32" type="image/png" href="/static/images/favicon/favicon-32.png">
|
|
|
|
<title>(BSX) BasicSwap - v{{ version }}</title>
|
|
|
|
<!-- Initialize tooltips -->
|
|
<script>
|
|
document.addEventListener('DOMContentLoaded', () => {
|
|
const tooltipManager = TooltipManager.initialize();
|
|
tooltipManager.initializeTooltips();
|
|
});
|
|
</script>
|
|
|
|
<!-- Dark mode initialization -->
|
|
<script>
|
|
const isDarkMode = localStorage.getItem('color-theme') === 'dark' ||
|
|
(!localStorage.getItem('color-theme') && window.matchMedia('(prefers-color-scheme: dark)').matches);
|
|
|
|
if (!localStorage.getItem('color-theme')) {
|
|
localStorage.setItem('color-theme', 'dark');
|
|
}
|
|
document.documentElement.classList.toggle('dark', isDarkMode);
|
|
</script>
|
|
|
|
<!-- Shutdown modal functionality -->
|
|
<script>
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
const shutdownButtons = document.querySelectorAll('.shutdown-button');
|
|
const shutdownModal = document.getElementById('shutdownModal');
|
|
const closeModalButton = document.getElementById('closeShutdownModal');
|
|
const confirmShutdownButton = document.getElementById('confirmShutdown');
|
|
const shutdownWarning = document.getElementById('shutdownWarning');
|
|
|
|
function updateShutdownButtons() {
|
|
const activeSwaps = parseInt(shutdownButtons[0].getAttribute('data-active-swaps') || '0');
|
|
shutdownButtons.forEach(button => {
|
|
if (activeSwaps > 0) {
|
|
button.classList.add('shutdown-disabled');
|
|
button.setAttribute('data-disabled', 'true');
|
|
button.setAttribute('title', 'Caution: Swaps in progress');
|
|
} else {
|
|
button.classList.remove('shutdown-disabled');
|
|
button.removeAttribute('data-disabled');
|
|
button.removeAttribute('title');
|
|
}
|
|
});
|
|
}
|
|
|
|
function showShutdownModal() {
|
|
const activeSwaps = parseInt(shutdownButtons[0].getAttribute('data-active-swaps') || '0');
|
|
if (activeSwaps > 0) {
|
|
shutdownWarning.classList.remove('hidden');
|
|
confirmShutdownButton.textContent = 'Yes, Shut Down Anyway';
|
|
} else {
|
|
shutdownWarning.classList.add('hidden');
|
|
confirmShutdownButton.textContent = 'Yes, Shut Down';
|
|
}
|
|
shutdownModal.classList.remove('hidden');
|
|
document.body.style.overflow = 'hidden';
|
|
}
|
|
|
|
function hideShutdownModal() {
|
|
shutdownModal.classList.add('hidden');
|
|
document.body.style.overflow = '';
|
|
}
|
|
|
|
shutdownButtons.forEach(button => {
|
|
button.addEventListener('click', function(e) {
|
|
e.preventDefault();
|
|
showShutdownModal();
|
|
});
|
|
});
|
|
|
|
closeModalButton.addEventListener('click', hideShutdownModal);
|
|
|
|
confirmShutdownButton.addEventListener('click', function() {
|
|
const shutdownToken = document.querySelector('.shutdown-button')
|
|
.getAttribute('href').split('/').pop();
|
|
window.location.href = '/shutdown/' + shutdownToken;
|
|
});
|
|
|
|
shutdownModal.addEventListener('click', function(e) {
|
|
if (e.target === this) {
|
|
hideShutdownModal();
|
|
}
|
|
});
|
|
|
|
updateShutdownButtons();
|
|
});
|
|
</script>
|
|
</head>
|
|
|
|
<body class="dark:bg-gray-700">
|
|
<!-- Shutdown Modal -->
|
|
<div id="shutdownModal" tabindex="-1" class="hidden fixed inset-0 z-50 overflow-y-auto overflow-x-hidden">
|
|
<div class="fixed inset-0 bg-black bg-opacity-60 transition-opacity"></div>
|
|
<div class="flex items-center justify-center min-h-screen p-4 relative z-10">
|
|
<div class="bg-white dark:bg-gray-500 rounded-lg shadow-xl max-w-md w-full">
|
|
<div class="p-6 text-center">
|
|
<svg class="mx-auto mb-4 text-gray-400 w-12 h-12 dark:text-gray-200"
|
|
aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 20 20">
|
|
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"
|
|
stroke-width="2" d="M10 11V6m0 8h.01M19 10a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z" />
|
|
</svg>
|
|
<h3 class="mb-5 text-lg font-normal text-gray-700 dark:text-gray-300">
|
|
Are you sure you want to shut down?
|
|
</h3>
|
|
<p id="shutdownWarning" class="mb-5 text-sm text-red-500 font-bold hidden">
|
|
Warning: Swaps are in progress. Please wait for swaps to complete before shutting down.
|
|
</p>
|
|
<p class="mb-5 text-sm text-gray-500 dark:text-gray-300">
|
|
This action will shut down the application. Are you sure you want to proceed?
|
|
</p>
|
|
<button id="confirmShutdown" type="button" class="text-white bg-red-600 hover:bg-red-800
|
|
focus:ring-0 focus:outline-none focus:ring-red-300 dark:focus:ring-red-800 font-medium
|
|
rounded-lg text-sm inline-flex items-center px-5 py-2.5 text-center mr-2">
|
|
Yes, Shut Down
|
|
</button>
|
|
<button id="closeShutdownModal" type="button" class="text-gray-500 bg-white hover:bg-gray-100
|
|
focus:ring-0 focus:outline-none focus:ring-gray-200 rounded-lg border border-gray-200 text-sm
|
|
font-medium px-5 py-2.5 hover:text-gray-900 focus:z-10 dark:bg-gray-700 dark:text-gray-300
|
|
dark:border-gray-500 dark:hover:text-white dark:hover:bg-gray-600 dark:focus:ring-gray-600">
|
|
Cancel
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Main Navigation -->
|
|
<section>
|
|
<nav class="relative bg-gray-700">
|
|
<div class="p-6 container flex flex-wrap items-center justify-between items-center mx-auto">
|
|
<!-- Logo -->
|
|
<a class="flex-shrink-0 mr-12 text-2xl text-white font-semibold" href="/">
|
|
<img class="h-10" src="/static/images/logos/basicswap-logo.svg" alt="" width="auto">
|
|
</a>
|
|
|
|
<!-- Desktop Navigation -->
|
|
<ul class="hidden xl:flex">
|
|
<!-- Wallets -->
|
|
<li>
|
|
<a class="flex mr-10 items-center py-3 text-gray-50 hover:text-gray-100 text-sm"
|
|
href="/wallets">
|
|
{{ wallet_svg | safe }}
|
|
<span>Wallets</span>
|
|
</a>
|
|
</li>
|
|
|
|
<!-- Network Order Book -->
|
|
<li>
|
|
<a class="flex mr-10 items-center py-2.5 text-gray-50 hover:text-gray-100 text-sm"
|
|
href="/offers">
|
|
{{ order_book_svg | safe }}
|
|
<span>Network Order Book</span>
|
|
<span id="network-offers-counter" class="inline-flex justify-center items-center text-xs
|
|
font-semibold ml-3 mr-2 px-2.5 py-1 font-small text-white bg-blue-500 rounded-full">
|
|
{{ summary.num_network_offers }}
|
|
</span>
|
|
</a>
|
|
</li>
|
|
|
|
<!-- Place New Offer -->
|
|
<li>
|
|
<a class="flex rounded-full flex-wrap justify-center w-full px-4 py-2.5 bg-blue-500
|
|
hover:bg-green-600 hover:border-green-600 font-medium text-sm text-white border
|
|
border-blue-500 rounded-md shadow-button focus:ring-0 focus:outline-none" href="/newoffer">
|
|
{{ new_offer_svg | safe }}
|
|
<span>Place new Offer</span>
|
|
</a>
|
|
</li>
|
|
</ul>
|
|
|
|
<!-- Settings Dropdown -->
|
|
<ul class="hidden xl:flex lg:justify-end lg:items-center lg:space-x-6 ml-auto">
|
|
<div id="dropdownNavbarLink" data-dropdown-toggle="dropdownNavbar" class="flex justify-between
|
|
items-center py-2 pr-4 pl-3 w-full text-gray-50 text-sm md:border-0 md:p-0 md:w-auto
|
|
text-gray-50 hover:text-gray-100">
|
|
{{ settings_svg | safe }}
|
|
Settings & Tools
|
|
{{ header_arrow_down_svg| safe }}
|
|
</div>
|
|
</ul>
|
|
|
|
<!-- Settings Menu -->
|
|
<div id="dropdownNavbar" class="hidden z-50 w-50 font-normal bg-white shadow divide-y
|
|
divide-gray-100 dark:bg-gray-500 dark:divide-gray-400 dark:text-white">
|
|
<ul class="py-0 text-sm text-gray-700" aria-labelledby="dropdownLargeButton">
|
|
<!-- Settings Menu Items -->
|
|
<li>
|
|
<a href="/settings" class="flex items-center block py-4 px-4 hover:bg-gray-100
|
|
dark:hover:bg-gray-700 dark:text-white">
|
|
<span class="sr-only">Settings</span>
|
|
{{ cog_svg | safe }}
|
|
Settings
|
|
</a>
|
|
</li>
|
|
<li>
|
|
<a href="/changepassword" class="flex items-center block py-4 px-4 hover:bg-gray-100
|
|
dark:hover:bg-gray-700 dark:text-white">
|
|
<span class="sr-only">Change/Set Password</span>
|
|
{{ change_password_svg | safe }}
|
|
Change/Set Password
|
|
</a>
|
|
</li>
|
|
{% if debug_mode == true %}
|
|
<li>
|
|
<a href="/rpc" class="flex items-center block py-4 px-4 hover:bg-gray-100
|
|
dark:hover:bg-gray-700 dark:text-white">
|
|
<span class="sr-only">RPC</span>
|
|
{{ rpc_svg | safe }}
|
|
RPC Console
|
|
</a>
|
|
</li>
|
|
<li>
|
|
<a href="/debug" class="flex items-center block py-4 px-4 hover:bg-gray-100
|
|
dark:hover:bg-gray-700 dark:text-white">
|
|
<span class="sr-only">Debug</span>
|
|
{{ debug_svg | safe }}
|
|
Debug
|
|
</a>
|
|
</li>
|
|
<li>
|
|
<a href="/explorers" class="flex items-center block py-4 px-4 hover:bg-gray-100
|
|
dark:hover:bg-gray-700 dark:text-white">
|
|
<span class="sr-only">Explorers</span>
|
|
{{ explorer_svg | safe }}
|
|
Explorers
|
|
</a>
|
|
</li>
|
|
{% endif %}
|
|
{% if use_tor_proxy == true %}
|
|
<li>
|
|
<a href="/tor" class="flex items-center block py-4 px-4 hover:bg-gray-100
|
|
dark:hover:bg-gray-700 dark:text-white">
|
|
<span class="sr-only">Tor</span>
|
|
{{ tor_svg | safe }}
|
|
Tor
|
|
</a>
|
|
</li>
|
|
{% endif %}
|
|
<li>
|
|
<a href="/smsgaddresses" class="flex items-center block py-4 px-4 hover:bg-gray-100
|
|
dark:hover:bg-gray-700 dark:text-white">
|
|
<span class="sr-only">SMSG Addresses</span>
|
|
{{ smsg_svg | safe }}
|
|
SMSG Addresses
|
|
</a>
|
|
</li>
|
|
<li>
|
|
<a href="/watched" class="flex items-center block py-4 px-4 hover:bg-gray-100 dark:hover:bg-gray-700
|
|
dark:text-white">
|
|
<span class="sr-only">Watch Outputs</span>
|
|
{{ outputs_svg | safe }}
|
|
<span>Watch Outputs</span>
|
|
<span id="watched-outputs-counter" class="inline-flex justify-center items-center text-xs font-semibold
|
|
ml-3 mr-2 px-2.5 py-1 text-white {% if summary.num_watched_outputs > 0 %}bg-blue-500
|
|
{% else %}bg-gray-400{% endif %} rounded-full">
|
|
{{ summary.num_watched_outputs }}
|
|
</span>
|
|
</a>
|
|
</li>
|
|
{% if debug_mode == true %}
|
|
<li>
|
|
<a href="/automation" class="flex items-center block py-4 px-4 hover:bg-gray-100
|
|
dark:hover:bg-gray-700 dark:text-white">
|
|
<span class="sr-only">Automation Strategies</span>
|
|
{{ automation_svg | safe }}
|
|
Automation Strategies
|
|
</a>
|
|
</li>
|
|
{% endif %}
|
|
</ul>
|
|
<div class="text-sm text-gray-700">
|
|
<a href="/shutdown/{{ shutdown_token }}" class="shutdown-button flex items-center block py-4
|
|
px-4 text-sm text-gray-700 hover:bg-gray-100 dark:hover:bg-gray-600 dark:text-gray-200
|
|
dark:hover:text-white" data-active-swaps="{{ summary.num_swapping }}">
|
|
{{ shutdown_svg | safe }}
|
|
<span>Shutdown</span>
|
|
</a>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Status Icons -->
|
|
<div class="flex mr-2 items-center text-gray-50 hover:text-gray-100 text-sm ml-5">
|
|
<div class="flex-shrink-0 w-px h-10 bg-gray-400 dark:bg-gray-400 ml-4 mr-5"></div>
|
|
|
|
<!-- Debug Mode Icon -->
|
|
{% if debug_mode == true %}
|
|
<ul class="xl:flex">
|
|
<li>
|
|
<div data-tooltip-target="tooltip-DEV" class="ml-5 flex items-center text-gray-50
|
|
hover:text-gray-100 text-sm">
|
|
{{ debug_nerd_svg | safe }}
|
|
</div>
|
|
<div id="tooltip-DEV" role="tooltip" class="inline-block absolute invisible z-10 py-2 px-3
|
|
text-sm font-medium text-white bg-blue-500 rounded-lg shadow-sm opacity-0
|
|
transition-opacity duration-300 tooltip">
|
|
<p><b>Debug mode:</b> Active</p>
|
|
{% if debug_ui_mode == true %}
|
|
<p><b>Debug UI mode:</b> Active</p>
|
|
{% endif %}
|
|
</div>
|
|
</li>
|
|
</ul>
|
|
{% endif %}
|
|
|
|
<!-- Wallet Status -->
|
|
{% if encrypted == true %}
|
|
<ul class="xl:flex">
|
|
<li>
|
|
{% if locked == true %}
|
|
<div data-tooltip-target="tooltip-locked-wallets" class="ml-5 flex items-center text-gray-50
|
|
hover:text-gray-100 text-sm">
|
|
{{ wallet_locked_svg | safe }}
|
|
</div>
|
|
<div id="tooltip-locked-wallets" role="tooltip" class="inline-block absolute invisible z-10
|
|
py-2 px-3 text-sm font-medium text-white bg-blue-500 rounded-lg shadow-sm opacity-0
|
|
transition-opacity duration-300 tooltip">
|
|
<p><b>Wallets:</b> Locked</p>
|
|
</div>
|
|
{% else %}
|
|
<a href='/lock'>
|
|
<div data-tooltip-target="tooltip-unlocked-wallets" class="ml-5 flex items-center
|
|
text-gray-50 hover:text-gray-100 text-sm">
|
|
{{ wallet_unlocked_svg | safe }}
|
|
</div>
|
|
<div id="tooltip-unlocked-wallets" role="tooltip" class="inline-block absolute invisible
|
|
z-10 py-2 px-3 text-sm font-medium text-white bg-blue-500 rounded-lg shadow-sm opacity-0
|
|
transition-opacity duration-300 tooltip">
|
|
<p><b>Wallets:</b> Unlocked</p>
|
|
</div>
|
|
</a>
|
|
{% endif %}
|
|
</li>
|
|
</ul>
|
|
{% endif %}
|
|
|
|
<!-- Tor Status -->
|
|
{% if use_tor_proxy == true %}
|
|
<ul class="xl:flex ml-5">
|
|
<li>
|
|
<a href="/tor">
|
|
<div data-tooltip-target="tooltip-tor" class="flex items-center text-gray-50
|
|
hover:text-gray-100 text-sm">
|
|
{{ tor_purple_svg | safe }}
|
|
</div>
|
|
<div id="tooltip-tor" role="tooltip" class="inline-block absolute invisible z-10 py-2 px-3
|
|
text-sm font-medium text-white bg-blue-500 rounded-lg shadow-sm opacity-0
|
|
transition-opacity duration-300 tooltip">
|
|
<b>Tor mode:</b> Active
|
|
{% if tor_established == true %}
|
|
<br><b>Tor:</b> Connected
|
|
{% endif %}
|
|
</div>
|
|
</a>
|
|
</li>
|
|
</ul>
|
|
{% endif %}
|
|
|
|
<!-- Theme Toggle -->
|
|
<button data-tooltip-target="tooltip-darkmode" id="theme-toggle" type="button"
|
|
class="text-gray-500 dark:text-gray-400 focus:outline-none rounded-lg text-sm ml-5">
|
|
{{ sun_svg | safe }}
|
|
{{ moon_svg | safe }}
|
|
<div id="tooltip-darkmode" role="tooltip" class="inline-block absolute invisible z-10 py-2
|
|
px-3 text-sm font-medium text-white bg-blue-500 rounded-lg shadow-sm opacity-0
|
|
transition-opacity duration-300 tooltip">
|
|
Dark mode
|
|
</div>
|
|
</button>
|
|
</div>
|
|
|
|
<!-- Mobile Menu Button -->
|
|
<div class="ml-auto flex xl:hidden">
|
|
<button class="navbar-burger flex items-center rounded focus:outline-none">
|
|
{{ mobile_menu_svg | safe }}
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Secondary Navigation Bar -->
|
|
<div class="hidden xl:block py-5 px-6 bg-coolGray-100 border-gray-100 dark:border-gray-500
|
|
dark:bg-body border-b dark:border-b-2">
|
|
<div class="flex items-center justify-center container mx-auto">
|
|
<ul class="flex items-center space-x-8">
|
|
<!-- Your Offers -->
|
|
<li>
|
|
<a data-tooltip-target="tooltip-your-offers" class="flex items-center text-sm text-gray-400
|
|
hover:text-gray-600 dark:text-gray-100 dark:hover:text-gray-100" href="/sentoffers">
|
|
{{ your_offers_svg | safe }}
|
|
<span>Your Offers</span>
|
|
<span id="offers-counter" class="inline-flex justify-center items-center text-xs
|
|
font-semibold ml-3 px-2.5 py-1 text-white {% if summary.num_sent_active_offers > 0 %}
|
|
bg-blue-500{% else %}bg-gray-400{% endif %} rounded-full">
|
|
{{ summary.num_sent_active_offers }}
|
|
</span>
|
|
</a>
|
|
<div id="tooltip-your-offers" role="tooltip" class="inline-block absolute invisible z-10
|
|
py-2 px-3 text-sm font-medium text-white bg-blue-500 rounded-lg shadow-sm opacity-0
|
|
transition-opacity duration-300 tooltip">
|
|
<p><b>Total offers:</b> {{ summary.num_sent_offers }}</p>
|
|
<p><b>Active offers:</b> {{ summary.num_sent_active_offers }}</p>
|
|
</div>
|
|
</li>
|
|
|
|
<li>
|
|
<span class="text-gray-300">|</span>
|
|
</li>
|
|
|
|
<!-- Bid Requests -->
|
|
<li>
|
|
<a class="flex items-center text-sm text-gray-400 hover:text-gray-600 dark:text-gray-100
|
|
dark:hover:text-gray-100" href="/availablebids">
|
|
{{ available_bids_svg | safe }}
|
|
<span>Bid Requests</span>
|
|
<span id="bid-requests-counter" class="inline-flex justify-center items-center text-xs
|
|
font-semibold ml-3 px-2.5 py-1 text-white {% if summary.num_available_bids > 0 %}
|
|
bg-blue-500{% else %}bg-gray-400{% endif %} rounded-full">
|
|
{{ summary.num_available_bids }}
|
|
</span>
|
|
</a>
|
|
</li>
|
|
|
|
<li>
|
|
<span class="text-gray-300">|</span>
|
|
</li>
|
|
|
|
<!-- Bids -->
|
|
<li>
|
|
<a href="/bids" data-tooltip-target="tooltip-bids" class="flex items-center text-sm text-gray-400
|
|
hover:text-gray-600 dark:text-gray-100 dark:hover:text-gray-100">
|
|
<span class="inline-block mr-2">{{ bids_sent_svg | safe }}</span>
|
|
<span>Bids</span>
|
|
<span class="flex items-center ml-2">
|
|
|
|
<!-- Outgoing bids counter arrow -->
|
|
<span id="sent-bids-counter" class="inline-flex items-center text-xs font-semibold px-2.5 py-1
|
|
text-white {% if summary.num_sent_active_bids > 0 %}bg-blue-500{% else %}bg-gray-400{% endif %}
|
|
rounded-full">
|
|
<svg class="w-3 h-3 mr-1" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
<path d="M5 19V5L19 12L5 19Z" fill="currentColor" transform="rotate(-90 12 12)"/>
|
|
</svg>
|
|
{{ summary.num_sent_active_bids }}
|
|
</span>
|
|
|
|
<!-- Incoming bids counter arrow -->
|
|
<span id="recv-bids-counter" class="inline-flex items-center text-xs font-semibold ml-2 px-2.5
|
|
py-1 text-white {% if summary.num_recv_active_bids > 0 %}bg-blue-500{% else %}bg-gray-400
|
|
{% endif %} rounded-full">
|
|
<svg class="w-3 h-3 mr-1" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
<path d="M5 19V5L19 12L5 19Z" fill="currentColor" transform="rotate(90 12 12)"/>
|
|
</svg>
|
|
{{ summary.num_recv_active_bids }}
|
|
</span>
|
|
</span>
|
|
</a>
|
|
<div id="tooltip-bids" role="tooltip" class="inline-block absolute invisible z-10 py-2 px-3 text-sm
|
|
font-medium text-white bg-blue-500 rounded-lg shadow-sm opacity-0 transition-opacity duration-300
|
|
tooltip">
|
|
<p><b>Sent bids:</b> {{ summary.num_sent_bids }} ({{ summary.num_sent_active_bids }} active)</p>
|
|
<p><b>Received bids:</b> {{ summary.num_recv_bids }} ({{ summary.num_recv_active_bids }} active)</p>
|
|
</div>
|
|
</li>
|
|
|
|
<li>
|
|
<span class="text-gray-300">|</span>
|
|
</li>
|
|
|
|
<!-- Swaps in Progress -->
|
|
<li>
|
|
<a class="flex items-center text-sm text-gray-400 hover:text-gray-600 dark:text-gray-100
|
|
dark:hover:text-gray-100" href="/active">
|
|
<div id="swapContainer" class="inline-flex center-spin mr-2"
|
|
{% if summary.num_swapping != 0 %}style="animation: spin 2s linear infinite;"{% endif %}>
|
|
{% if summary.num_swapping != 0 %}
|
|
{{ swap_in_progress_green_svg | safe }}
|
|
{% else %}
|
|
{{ swap_in_progress_svg | safe }}
|
|
{% endif %}
|
|
</div>
|
|
<span>Swaps in Progress</span>
|
|
<span id="swaps-counter" class="inline-flex justify-center items-center text-xs
|
|
font-semibold ml-3 px-2.5 py-1 text-white {% if summary.num_swapping > 0 %}
|
|
bg-blue-500{% else %}bg-gray-400{% endif %} rounded-full">
|
|
{{ summary.num_swapping }}
|
|
</span>
|
|
</a>
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
</nav>
|
|
|
|
<!-- Mobile Navigation Menu -->
|
|
<div class="hidden navbar-menu fixed top-0 left-0 bottom-0 w-3/4 lg:w-80 sm:max-w-xs z-50">
|
|
<div class="navbar-backdrop fixed inset-0 bg-gray-700 dark:bg-gray-600 opacity-10"></div>
|
|
<nav class="relative flex flex-col pt-6 pb-8 h-full w-full bg-gray-700 dark:bg-gray-600 overflow-y-auto">
|
|
<div class="flex w-full items-center px-6 pb-6 mb-6 lg:border-b border-gray-700">
|
|
<a class="text-xl text-white font-semibold" href="/">
|
|
<img class="h-8" src="/static/images/logos/basicswap-logo.svg" alt="" width="auto">
|
|
</a>
|
|
</div>
|
|
<div class="px-4 pb-6">
|
|
<!-- Main Navigation -->
|
|
<ul class="mb-8 text-sm font-medium">
|
|
<li>
|
|
<a class="flex items-center pl-3 py-3 pr-4 text-gray-50 hover:bg-gray-900 rounded" href="/offers">
|
|
{{ order_book_svg | safe }}
|
|
<span>Network Order Book</span>
|
|
<span id="network-offers-counter" class="inline-flex justify-center items-center text-xs
|
|
font-semibold ml-auto px-2.5 py-1 text-white {% if summary.num_network_offers and
|
|
summary.num_network_offers > 0 %}bg-blue-500{% else %}bg-gray-400{% endif %} rounded-full">
|
|
{{ summary.num_network_offers }}
|
|
</span>
|
|
</a>
|
|
</li>
|
|
<li>
|
|
<a class="flex items-center pl-3 py-3 pr-4 text-gray-50 hover:bg-gray-900 rounded"
|
|
href="/newoffer">
|
|
{{ new_offer_svg | safe }}
|
|
<span>Place New Offer</span>
|
|
</a>
|
|
</li>
|
|
<li>
|
|
<a class="flex items-center pl-3 py-3 pr-4 text-gray-50 hover:bg-gray-900 rounded"
|
|
href="/wallets">
|
|
{{ wallet_svg | safe }}
|
|
<span>Wallets</span>
|
|
</a>
|
|
</li>
|
|
</ul>
|
|
|
|
<!-- Trading Section -->
|
|
<h3 class="mb-2 text-xs uppercase text-gray-300 font-medium">Trading</h3>
|
|
<ul class="mb-8 text-sm font-medium">
|
|
<li>
|
|
<a class="flex items-center pl-3 py-3 pr-4 text-gray-50 hover:bg-gray-900 rounded"
|
|
href="/sentoffers">
|
|
{{ your_offers_svg | safe }}
|
|
<span>Your Offers</span>
|
|
<span id="offers-counter" class="inline-flex justify-center items-center text-xs font-semibold
|
|
ml-auto px-2.5 py-1 text-white {% if summary.num_sent_active_offers and
|
|
summary.num_sent_active_offers > 0 %}bg-blue-500{% else %}bg-gray-400{% endif %}
|
|
rounded-full">
|
|
{{ summary.num_sent_active_offers }}
|
|
</span>
|
|
</a>
|
|
</li>
|
|
<li>
|
|
<a class="flex items-center pl-3 py-3 pr-4 text-gray-50 hover:bg-gray-900 rounded"
|
|
href="/availablebids">
|
|
{{ available_bids_svg | safe }}
|
|
<span>Bid Requests</span>
|
|
<span id="bid-requests-counter" class="inline-flex justify-center items-center text-xs
|
|
font-semibold ml-auto px-2.5 py-1 text-white {% if summary.num_available_bids and
|
|
summary.num_available_bids > 0 %}bg-blue-500{% else %}bg-gray-400{% endif %} rounded-full">
|
|
{{ summary.num_available_bids }}
|
|
</span>
|
|
</a>
|
|
</li>
|
|
<li>
|
|
<a class="flex items-center pl-3 py-3 pr-4 text-gray-50 hover:bg-gray-900 rounded" href="/bids">
|
|
{{ bids_received_svg | safe }}
|
|
<span>Bids</span>
|
|
<div class="flex ml-auto">
|
|
<span id="sent-bids-counter" class="inline-flex items-center text-xs font-semibold px-2.5
|
|
py-1 text-white {% if summary.num_sent_active_bids and summary.num_sent_active_bids > 0 %}
|
|
bg-blue-500{% else %}bg-gray-400{% endif %} rounded-full mr-2">
|
|
<svg class="w-3 h-3 mr-1" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
<path d="M5 19V5L19 12L5 19Z" fill="currentColor" transform="rotate(-90 12 12)"/>
|
|
</svg>
|
|
{{ summary.num_sent_active_bids }}
|
|
</span>
|
|
<span id="recv-bids-counter" class="inline-flex items-center text-xs font-semibold px-2.5
|
|
py-1 text-white {% if summary.num_recv_active_bids and summary.num_recv_active_bids > 0 %}
|
|
bg-blue-500{% else %}bg-gray-400{% endif %} rounded-full">
|
|
<svg class="w-3 h-3 mr-1" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
<path d="M5 19V5L19 12L5 19Z" fill="currentColor" transform="rotate(90 12 12)"/>
|
|
</svg>
|
|
{{ summary.num_recv_active_bids }}
|
|
</span>
|
|
</div>
|
|
</a>
|
|
</li>
|
|
<li>
|
|
<a class="flex items-center pl-3 py-3 pr-4 text-gray-50 hover:bg-gray-900 rounded" href="/active">
|
|
<div id="swapContainer" class="inline-flex center-spin mr-2"
|
|
{% if summary.num_swapping and summary.num_swapping != 0 %}
|
|
style="animation: spin 2s linear infinite;"{% endif %}>
|
|
{% if summary.num_swapping and summary.num_swapping != 0 %}
|
|
{{ swap_in_progress_green_svg | safe }}
|
|
{% else %}
|
|
{{ swap_in_progress_svg | safe }}
|
|
{% endif %}
|
|
</div>
|
|
<span>Swaps in Progress</span>
|
|
<span id="swaps-counter" class="inline-flex justify-center items-center text-xs font-semibold
|
|
ml-auto px-2.5 py-1 text-white {% if summary.num_swapping and summary.num_swapping > 0 %}
|
|
bg-blue-500{% else %}bg-gray-400{% endif %} rounded-full">
|
|
{{ summary.num_swapping }}
|
|
</span>
|
|
</a>
|
|
</li>
|
|
<li>
|
|
<a class="flex items-center pl-3 py-3 pr-4 text-gray-50 hover:bg-gray-900 rounded" href="/watched">
|
|
{{ outputs_svg | safe }}
|
|
<span>Watch Outputs</span>
|
|
<span id="watched-outputs-counter" class="inline-flex justify-center items-center text-xs
|
|
font-semibold ml-auto px-2.5 py-1 text-white {% if summary.num_watched_outputs and
|
|
summary.num_watched_outputs > 0 %}bg-blue-500{% else %}bg-gray-400{% endif %} rounded-full">
|
|
{{ summary.num_watched_outputs }}
|
|
</span>
|
|
</a>
|
|
</li>
|
|
</ul>
|
|
|
|
<!-- Settings Section -->
|
|
<h3 class="mb-2 text-xs uppercase text-gray-300 font-medium">Settings & Tools</h3>
|
|
<ul class="text-sm font-medium">
|
|
<li>
|
|
<a class="flex items-center pl-3 py-3 pr-4 text-gray-50 hover:bg-gray-900 rounded"
|
|
href="/settings">
|
|
{{ settings_svg | safe }}
|
|
<span>Settings</span>
|
|
</a>
|
|
</li>
|
|
<li>
|
|
<a class="flex items-center pl-3 py-3 pr-4 text-gray-50 hover:bg-gray-900 rounded"
|
|
href="/changepassword">
|
|
{{ change_password_svg | safe }}
|
|
<span>Change/Set Password</span>
|
|
</a>
|
|
</li>
|
|
{% if debug_mode == true %}
|
|
<li>
|
|
<a class="flex items-center pl-3 py-3 pr-4 text-gray-50 hover:bg-gray-900 rounded" href="/rpc">
|
|
{{ rpc_svg | safe }}
|
|
<span>RPC Console</span>
|
|
</a>
|
|
</li>
|
|
<li>
|
|
<a class="flex items-center pl-3 py-3 pr-4 text-gray-50 hover:bg-gray-900 rounded" href="/debug">
|
|
{{ debug_svg | safe }}
|
|
<span>Debug</span>
|
|
</a>
|
|
</li>
|
|
<li>
|
|
<a class="flex items-center pl-3 py-3 pr-4 text-gray-50 hover:bg-gray-900 rounded"
|
|
href="/explorers">
|
|
{{ explorer_svg | safe }}
|
|
<span>Explorers</span>
|
|
</a>
|
|
</li>
|
|
{% endif %}
|
|
<li>
|
|
<a class="flex items-center pl-3 py-3 pr-4 text-gray-50 hover:bg-gray-900 rounded"
|
|
href="/smsgaddresses">
|
|
{{ smsg_svg | safe }}
|
|
<span>SMSG Addresses</span>
|
|
</a>
|
|
</li>
|
|
{% if debug_mode == true %}
|
|
<li>
|
|
<a class="flex items-center pl-3 py-3 pr-4 text-gray-50 hover:bg-gray-900 rounded"
|
|
href="/automation">
|
|
{{ automation_svg | safe }}
|
|
<span>Automation Strategies</span>
|
|
</a>
|
|
</li>
|
|
{% endif %}
|
|
{% if use_tor_proxy == true %}
|
|
<li>
|
|
<a class="flex items-center pl-3 py-3 pr-4 text-gray-50 hover:bg-gray-900 rounded" href="/tor">
|
|
{{ tor_svg | safe }}
|
|
<span>Tor</span>
|
|
</a>
|
|
</li>
|
|
{% endif %}
|
|
</ul>
|
|
|
|
<!-- Shutdown Button -->
|
|
<div class="pt-8">
|
|
<a href="/shutdown/{{ shutdown_token }}" class="shutdown-button flex items-center pl-3 py-3 pr-4
|
|
text-gray-50 hover:bg-gray-900 rounded" data-active-swaps="{{ summary.num_swapping }}">
|
|
{{ shutdown_svg | safe }}
|
|
<span>Shutdown</span>
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</nav>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- WebSocket -->
|
|
{% if ws_port %}
|
|
<script>
|
|
(function() {
|
|
window.notificationConfig = {
|
|
showNewOffers: false,
|
|
showNewBids: true,
|
|
showBidAccepted: true
|
|
};
|
|
|
|
function ensureToastContainer() {
|
|
let container = document.getElementById('ul_updates');
|
|
if (!container) {
|
|
const floating_div = document.createElement('div');
|
|
floating_div.classList.add('floatright');
|
|
container = document.createElement('ul');
|
|
container.setAttribute('id', 'ul_updates');
|
|
floating_div.appendChild(container);
|
|
document.body.appendChild(floating_div);
|
|
}
|
|
return container;
|
|
}
|
|
|
|
function createToast(title, type = 'success') {
|
|
const messages = ensureToastContainer();
|
|
const message = document.createElement('li');
|
|
message.innerHTML = `
|
|
<div id="hide">
|
|
<div id="toast-${type}" class="flex items-center p-4 mb-4 w-full max-w-xs text-gray-500
|
|
bg-white rounded-lg shadow" role="alert">
|
|
<div class="inline-flex flex-shrink-0 justify-center items-center w-10 h-10
|
|
bg-blue-500 rounded-lg">
|
|
<svg class="w-5 h-5" xmlns="http://www.w3.org/2000/svg" height="18" width="18"
|
|
viewBox="0 0 24 24">
|
|
<g fill="#ffffff">
|
|
<path d="M8.5,20a1.5,1.5,0,0,1-1.061-.439L.379,12.5,2.5,10.379l6,6,13-13L23.621,
|
|
5.5,9.561,19.561A1.5,1.5,0,0,1,8.5,20Z"></path>
|
|
</g>
|
|
</svg>
|
|
</div>
|
|
<div class="uppercase w-40 ml-3 text-sm font-semibold text-gray-900">${title}</div>
|
|
<button type="button" onclick="closeAlert(event)" class="ml-auto -mx-1.5 -my-1.5
|
|
bg-white text-gray-400 hover:text-gray-900 rounded-lg focus:ring-0 focus:outline-none
|
|
focus:ring-gray-300 p-1.5 hover:bg-gray-100 inline-flex h-8 w-8">
|
|
<span class="sr-only">Close</span>
|
|
<svg aria-hidden="true" class="w-5 h-5" fill="currentColor" viewBox="0 0 20 20"
|
|
xmlns="http://www.w3.org/2000/svg">
|
|
<path fill-rule="evenodd" d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1
|
|
1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293
|
|
4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z"
|
|
clip-rule="evenodd"></path>
|
|
</svg>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
`;
|
|
messages.appendChild(message);
|
|
}
|
|
|
|
function updateElement(elementId, value, options = {}) {
|
|
const element = document.getElementById(elementId);
|
|
if (!element) return false;
|
|
|
|
const safeValue = (value !== undefined && value !== null)
|
|
? value
|
|
: (element.dataset.lastValue || 0);
|
|
|
|
element.dataset.lastValue = safeValue;
|
|
|
|
if (elementId === 'sent-bids-counter' || elementId === 'recv-bids-counter') {
|
|
const svg = element.querySelector('svg');
|
|
element.textContent = safeValue;
|
|
if (svg) {
|
|
element.insertBefore(svg, element.firstChild);
|
|
}
|
|
} else {
|
|
element.textContent = safeValue;
|
|
}
|
|
|
|
if (['offers-counter', 'bid-requests-counter', 'sent-bids-counter',
|
|
'recv-bids-counter', 'swaps-counter', 'network-offers-counter',
|
|
'watched-outputs-counter'].includes(elementId)) {
|
|
element.classList.remove('bg-blue-500', 'bg-gray-400');
|
|
element.classList.add(safeValue > 0 ? 'bg-blue-500' : 'bg-gray-400');
|
|
}
|
|
|
|
if (elementId === 'swaps-counter') {
|
|
const swapContainer = document.getElementById('swapContainer');
|
|
if (swapContainer) {
|
|
const isSwapping = safeValue > 0;
|
|
if (isSwapping) {
|
|
swapContainer.innerHTML = `{{ swap_in_progress_green_svg | safe }}`;
|
|
swapContainer.style.animation = 'spin 2s linear infinite';
|
|
} else {
|
|
swapContainer.innerHTML = `{{ swap_in_progress_svg | safe }}`;
|
|
swapContainer.style.animation = 'none';
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
function fetchSummaryData() {
|
|
fetch('/json')
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
updateElement('network-offers-counter', data.num_network_offers);
|
|
updateElement('offers-counter', data.num_sent_active_offers);
|
|
updateElement('sent-bids-counter', data.num_sent_active_bids);
|
|
updateElement('recv-bids-counter', data.num_recv_active_bids);
|
|
updateElement('bid-requests-counter', data.num_available_bids);
|
|
updateElement('swaps-counter', data.num_swapping);
|
|
updateElement('watched-outputs-counter', data.num_watched_outputs);
|
|
})
|
|
.catch(error => console.error('Summary data fetch error:', error));
|
|
}
|
|
|
|
function initWebSocket() {
|
|
const wsUrl = "ws://" + window.location.hostname + ":{{ ws_port }}";
|
|
const ws = new WebSocket(wsUrl);
|
|
|
|
ws.onopen = () => {
|
|
console.log('🟢 WebSocket connection established for Dynamic Counters');
|
|
fetchSummaryData();
|
|
setInterval(fetchSummaryData, 30000); // Refresh every 30 seconds
|
|
};
|
|
|
|
ws.onmessage = (event) => {
|
|
try {
|
|
const data = JSON.parse(event.data);
|
|
if (data.event) {
|
|
let toastTitle;
|
|
let shouldShowToast = false;
|
|
|
|
switch (data.event) {
|
|
case 'new_offer':
|
|
toastTitle = `New network <a class="underline" href=/offer/${data.offer_id}>offer</a>`;
|
|
shouldShowToast = window.notificationConfig.showNewOffers;
|
|
break;
|
|
case 'new_bid':
|
|
toastTitle = `<a class="underline" href=/bid/${data.bid_id}>New bid</a> on
|
|
<a class="underline" href=/offer/${data.offer_id}>offer</a>`;
|
|
shouldShowToast = window.notificationConfig.showNewBids;
|
|
break;
|
|
case 'bid_accepted':
|
|
toastTitle = `<a class="underline" href=/bid/${data.bid_id}>Bid</a> accepted`;
|
|
shouldShowToast = window.notificationConfig.showBidAccepted;
|
|
break;
|
|
}
|
|
|
|
if (toastTitle && shouldShowToast) {
|
|
createToast(toastTitle);
|
|
}
|
|
}
|
|
fetchSummaryData();
|
|
} catch (error) {
|
|
console.error('WebSocket message processing error:', error);
|
|
}
|
|
};
|
|
|
|
ws.onerror = (error) => {
|
|
console.error('WebSocket Error:', error);
|
|
};
|
|
|
|
ws.onclose = (event) => {
|
|
console.log('WebSocket connection closed', event);
|
|
setTimeout(initWebSocket, 5000);
|
|
};
|
|
}
|
|
|
|
window.closeAlert = function(event) {
|
|
let element = event.target;
|
|
while (element.nodeName !== "BUTTON") {
|
|
element = element.parentNode;
|
|
}
|
|
element.parentNode.parentNode.removeChild(element.parentNode);
|
|
};
|
|
|
|
function init() {
|
|
initWebSocket();
|
|
}
|
|
|
|
if (document.readyState === 'loading') {
|
|
document.addEventListener('DOMContentLoaded', init);
|
|
} else {
|
|
init();
|
|
}
|
|
})();
|
|
</script>
|
|
{% endif %}
|