mirror of
https://github.com/basicswap/basicswap.git
synced 2025-11-05 10:28:10 +01:00
AMM (#310)
* AMM * LINT + Fixes * Remove unused global variables. * BLACK * BLACK * AMM - Various Fixes/Features/Bug Fixes. * FLAKE * FLAKE * BLACK * Small fix * Fix * Auto-start option AMM + Various fixes/bugs/styling. * Updated createoffers.py * BLACK * AMM Styling * Update bid_xmr template confirm model. * Fixed bug with Create Default Configuration + Added confirm modal. * Fix: Better redirect. * Fixed adjust_rates_based_on_market + Removed debug / extra logging + Various fixes. * GUI v3.2.2 * Fix sub-header your-offers count when created offers by AMM. * Fix math. * Added USD prices + Add offers/bids checkbox enabled always checked. * Donation page. * Updated header.html + Typo. * Update on createoffer.py + BLACK * AMM: html updates. * AMM: Add all, minrate, and static options. * AMM: Amount step default 0.001 * Fix global settings. * Update createoffers.py * Fixed bug with autostart when save global settings + Various layout fixes. * Fixed bug with autostart with add/edit + Added new option Orderbook (Auto-Accept) * Fixed debug + New feature attempt bids first. * Fix: Orderbook (Auto-Accept) * Added bidding strategy: Only bid on auto-accept offers (best rates from auto-accept only) * Fix: with_extra_info * Small fix automation_strat_id * Various fixes. * Final fixes
This commit is contained in:
@@ -529,6 +529,20 @@ class BasicSwap(BaseApp):
|
||||
def finalise(self):
|
||||
self.log.info("Finalising")
|
||||
|
||||
try:
|
||||
from basicswap.ui.page_amm import stop_amm_process, get_amm_status
|
||||
|
||||
amm_status = get_amm_status()
|
||||
if amm_status == "running":
|
||||
self.log.info("Stopping AMM process...")
|
||||
success, msg = stop_amm_process(self)
|
||||
if success:
|
||||
self.log.info(f"AMM shutdown: {msg}")
|
||||
else:
|
||||
self.log.warning(f"AMM shutdown warning: {msg}")
|
||||
except Exception as e:
|
||||
self.log.error(f"Error stopping AMM during shutdown: {e}")
|
||||
|
||||
self.delay_event.set()
|
||||
self.chainstate_delay_event.set()
|
||||
|
||||
@@ -1151,6 +1165,57 @@ class BasicSwap(BaseApp):
|
||||
nm += 1
|
||||
self.log.info(f"Scanned {nm} unread messages.")
|
||||
|
||||
autostart_setting = self.settings.get("amm_autostart", False)
|
||||
self.log.info(f"Checking AMM autostart setting: {autostart_setting}")
|
||||
|
||||
if autostart_setting:
|
||||
self.log.info("AMM autostart is enabled, starting AMM process...")
|
||||
try:
|
||||
from basicswap.ui.page_amm import (
|
||||
start_amm_process,
|
||||
start_amm_process_force,
|
||||
check_existing_amm_processes,
|
||||
)
|
||||
|
||||
self.log.info("Waiting 2 seconds for BasicSwap to fully initialize...")
|
||||
time.sleep(2)
|
||||
|
||||
amm_host = self.settings.get("htmlhost", "127.0.0.1")
|
||||
amm_port = self.settings.get("htmlport", 12700)
|
||||
amm_debug = False
|
||||
|
||||
self.log.info(
|
||||
f"Starting AMM with host={amm_host}, port={amm_port}, debug={amm_debug}"
|
||||
)
|
||||
|
||||
existing_pids = check_existing_amm_processes()
|
||||
if existing_pids:
|
||||
self.log.warning(
|
||||
f"Found existing AMM processes: {existing_pids}. Using force start to clean up..."
|
||||
)
|
||||
success, msg = start_amm_process_force(
|
||||
self, amm_host, amm_port, debug=amm_debug
|
||||
)
|
||||
if success:
|
||||
self.log.info(f"AMM autostart force successful: {msg}")
|
||||
else:
|
||||
self.log.warning(f"AMM autostart force failed: {msg}")
|
||||
else:
|
||||
success, msg = start_amm_process(
|
||||
self, amm_host, amm_port, debug=amm_debug
|
||||
)
|
||||
if success:
|
||||
self.log.info(f"AMM autostart successful: {msg}")
|
||||
else:
|
||||
self.log.warning(f"AMM autostart failed: {msg}")
|
||||
except Exception as e:
|
||||
self.log.error(f"AMM autostart error: {str(e)}")
|
||||
import traceback
|
||||
|
||||
self.log.error(traceback.format_exc())
|
||||
else:
|
||||
self.log.info("AMM autostart is disabled")
|
||||
|
||||
def stopDaemon(self, coin) -> None:
|
||||
if coin in (Coins.XMR, Coins.DCR, Coins.WOW):
|
||||
return
|
||||
@@ -2343,6 +2408,10 @@ class BasicSwap(BaseApp):
|
||||
finally:
|
||||
self.closeDB(cursor)
|
||||
self.log.info(f"Sent OFFER {self.log.id(offer_id)}")
|
||||
|
||||
if self.ws_server:
|
||||
self.ws_server.send_message_to_all('{"event": "offer_created"}')
|
||||
|
||||
return offer_id
|
||||
|
||||
def revokeOffer(self, offer_id, security_token=None) -> None:
|
||||
@@ -11247,7 +11316,15 @@ class BasicSwap(BaseApp):
|
||||
return "bitcoin-cash"
|
||||
if coin_id == Coins.FIRO:
|
||||
return "zcoin"
|
||||
return chainparams[coin_id]["name"]
|
||||
|
||||
# Handle coin variants that use base coin chainparams
|
||||
use_coinid = coin_id
|
||||
if coin_id == Coins.PART_ANON or coin_id == Coins.PART_BLIND:
|
||||
use_coinid = Coins.PART
|
||||
elif coin_id == Coins.LTC_MWEB:
|
||||
use_coinid = Coins.LTC
|
||||
|
||||
return chainparams[use_coinid]["name"]
|
||||
|
||||
def lookupFiatRates(
|
||||
self,
|
||||
@@ -11432,17 +11509,18 @@ class BasicSwap(BaseApp):
|
||||
js = self.lookupFiatRates([int(coin_from), int(coin_to)])
|
||||
rate = float(js[int(coin_from)]) / float(js[int(coin_to)])
|
||||
js["rate_inferred"] = ci_to.format_amount(rate, conv_int=True, r=1)
|
||||
|
||||
js[name_from] = {"usd": js[int(coin_from)]}
|
||||
js.pop(int(coin_from))
|
||||
js[name_to] = {"usd": js[int(coin_to)]}
|
||||
js.pop(int(coin_to))
|
||||
|
||||
rv["coingecko"] = js
|
||||
except Exception as e:
|
||||
rv["coingecko_error"] = str(e)
|
||||
if self.debug:
|
||||
self.log.error(traceback.format_exc())
|
||||
|
||||
js[name_from] = {"usd": js[int(coin_from)]}
|
||||
js.pop(int(coin_from))
|
||||
js[name_to] = {"usd": js[int(coin_to)]}
|
||||
js.pop(int(coin_to))
|
||||
|
||||
if output_array:
|
||||
|
||||
def format_float(f):
|
||||
@@ -11451,7 +11529,7 @@ class BasicSwap(BaseApp):
|
||||
rv_array = []
|
||||
if "coingecko_error" in rv:
|
||||
rv_array.append(("coingecko.com", "error", rv["coingecko_error"]))
|
||||
if "coingecko" in rv:
|
||||
elif "coingecko" in rv:
|
||||
js = rv["coingecko"]
|
||||
rv_array.append(
|
||||
(
|
||||
|
||||
@@ -48,6 +48,20 @@ def signal_handler(sig, frame):
|
||||
sys.stdout.fileno(), f"Signal {sig} detected, ending program.\n".encode("utf-8")
|
||||
)
|
||||
if swap_client is not None and not swap_client.chainstate_delay_event.is_set():
|
||||
try:
|
||||
from basicswap.ui.page_amm import stop_amm_process, get_amm_status
|
||||
|
||||
amm_status = get_amm_status()
|
||||
if amm_status == "running":
|
||||
logger.info("Signal handler stopping AMM process...")
|
||||
success, msg = stop_amm_process(swap_client)
|
||||
if success:
|
||||
logger.info(f"AMM signal shutdown: {msg}")
|
||||
else:
|
||||
logger.warning(f"AMM signal shutdown warning: {msg}")
|
||||
except Exception as e:
|
||||
logger.error(f"Error stopping AMM in signal handler: {e}")
|
||||
|
||||
swap_client.stopRunning()
|
||||
|
||||
|
||||
|
||||
@@ -25,7 +25,6 @@ from . import __version__
|
||||
from .util import (
|
||||
dumpj,
|
||||
toBool,
|
||||
LockedCoinError,
|
||||
format_timestamp,
|
||||
)
|
||||
from .chainparams import (
|
||||
@@ -54,6 +53,7 @@ from .ui.page_automation import (
|
||||
page_automation_strategy_new,
|
||||
)
|
||||
|
||||
from .ui.page_amm import page_amm, amm_status_api, amm_autostart_api, amm_debug_api
|
||||
from .ui.page_bids import page_bids, page_bid
|
||||
from .ui.page_offers import page_offers, page_offer, page_newoffer
|
||||
from .ui.page_tor import page_tor, get_tor_established_state
|
||||
@@ -227,6 +227,18 @@ class HttpHandler(BaseHTTPRequestHandler):
|
||||
swap_client.log.error(f"Error getting Tor state: {str(e)}")
|
||||
swap_client.log.error(traceback.format_exc())
|
||||
|
||||
from .ui.page_amm import get_amm_status, get_amm_active_count
|
||||
|
||||
try:
|
||||
args_dict["current_status"] = get_amm_status()
|
||||
args_dict["amm_active_count"] = get_amm_active_count(swap_client)
|
||||
except Exception as e:
|
||||
args_dict["current_status"] = "stopped"
|
||||
args_dict["amm_active_count"] = 0
|
||||
if swap_client.debug:
|
||||
swap_client.log.error(f"Error getting AMM state: {str(e)}")
|
||||
swap_client.log.error(traceback.format_exc())
|
||||
|
||||
if swap_client._show_notifications:
|
||||
args_dict["notifications"] = swap_client.getNotifications()
|
||||
|
||||
@@ -626,10 +638,37 @@ class HttpHandler(BaseHTTPRequestHandler):
|
||||
clear_cookie_header = self._clear_session_cookie()
|
||||
extra_headers.append(clear_cookie_header)
|
||||
|
||||
try:
|
||||
from basicswap.ui.page_amm import stop_amm_process, get_amm_status
|
||||
|
||||
amm_status = get_amm_status()
|
||||
if amm_status == "running":
|
||||
swap_client.log.info("Web shutdown stopping AMM process...")
|
||||
success, msg = stop_amm_process(swap_client)
|
||||
if success:
|
||||
swap_client.log.info(f"AMM web shutdown: {msg}")
|
||||
else:
|
||||
swap_client.log.warning(f"AMM web shutdown warning: {msg}")
|
||||
except Exception as e:
|
||||
swap_client.log.error(f"Error stopping AMM in web shutdown: {e}")
|
||||
|
||||
swap_client.stopRunning()
|
||||
|
||||
return self.page_info("Shutting down", extra_headers=extra_headers)
|
||||
|
||||
def page_donation(self, url_split, post_string):
|
||||
swap_client = self.server.swap_client
|
||||
swap_client.checkSystemStatus()
|
||||
summary = swap_client.getSummary()
|
||||
|
||||
template = env.get_template("donation.html")
|
||||
return self.render_template(
|
||||
template,
|
||||
{
|
||||
"summary": summary,
|
||||
},
|
||||
)
|
||||
|
||||
def page_index(self, url_split):
|
||||
swap_client = self.server.swap_client
|
||||
swap_client.checkSystemStatus()
|
||||
@@ -660,6 +699,9 @@ class HttpHandler(BaseHTTPRequestHandler):
|
||||
self.end_headers()
|
||||
|
||||
def handle_http(self, status_code, path, post_string="", is_json=False):
|
||||
from basicswap.util import LockedCoinError
|
||||
from basicswap.ui.util import getCoinName
|
||||
|
||||
swap_client = self.server.swap_client
|
||||
parsed = parse.urlparse(self.path)
|
||||
url_split = parsed.path.split("/")
|
||||
@@ -727,7 +769,11 @@ class HttpHandler(BaseHTTPRequestHandler):
|
||||
func = js_url_to_function(url_split)
|
||||
return func(self, url_split, post_string, is_json)
|
||||
except Exception as ex:
|
||||
if swap_client.debug is True:
|
||||
if isinstance(ex, LockedCoinError):
|
||||
clean_msg = f"Wallet locked: {getCoinName(ex.coinid)} wallet must be unlocked"
|
||||
swap_client.log.warning(clean_msg)
|
||||
return js_error(self, clean_msg)
|
||||
elif swap_client.debug is True:
|
||||
swap_client.log.error(traceback.format_exc())
|
||||
return js_error(self, str(ex))
|
||||
|
||||
@@ -821,6 +867,8 @@ class HttpHandler(BaseHTTPRequestHandler):
|
||||
return page_bids(self, url_split, post_string, available=True)
|
||||
if page == "watched":
|
||||
return self.page_watched(url_split, post_string)
|
||||
if page == "donation":
|
||||
return self.page_donation(url_split, post_string)
|
||||
if page == "smsgaddresses":
|
||||
return page_smsgaddresses(self, url_split, post_string)
|
||||
if page == "identity":
|
||||
@@ -833,6 +881,41 @@ class HttpHandler(BaseHTTPRequestHandler):
|
||||
return page_automation_strategy(self, url_split, post_string)
|
||||
if page == "newautomationstrategy":
|
||||
return page_automation_strategy_new(self, url_split, post_string)
|
||||
if page == "amm":
|
||||
if len(url_split) > 2 and url_split[2] == "status":
|
||||
query_params = {}
|
||||
if parsed.query:
|
||||
query_params = {
|
||||
k: v[0] for k, v in parse.parse_qs(parsed.query).items()
|
||||
}
|
||||
status_data = amm_status_api(
|
||||
swap_client, self.path, query_params
|
||||
)
|
||||
self.putHeaders(200, "application/json")
|
||||
return json.dumps(status_data).encode("utf-8")
|
||||
elif len(url_split) > 2 and url_split[2] == "autostart":
|
||||
query_params = {}
|
||||
if parsed.query:
|
||||
query_params = {
|
||||
k: v[0] for k, v in parse.parse_qs(parsed.query).items()
|
||||
}
|
||||
autostart_data = amm_autostart_api(
|
||||
swap_client, post_string, query_params
|
||||
)
|
||||
self.putHeaders(200, "application/json")
|
||||
return json.dumps(autostart_data).encode("utf-8")
|
||||
elif len(url_split) > 2 and url_split[2] == "debug":
|
||||
query_params = {}
|
||||
if parsed.query:
|
||||
query_params = {
|
||||
k: v[0] for k, v in parse.parse_qs(parsed.query).items()
|
||||
}
|
||||
debug_data = amm_debug_api(
|
||||
swap_client, post_string, query_params
|
||||
)
|
||||
self.putHeaders(200, "application/json")
|
||||
return json.dumps(debug_data).encode("utf-8")
|
||||
return page_amm(self, url_split, post_string)
|
||||
if page == "shutdown":
|
||||
return self.page_shutdown(url_split, post_string)
|
||||
if page == "changepassword":
|
||||
|
||||
@@ -42,6 +42,7 @@ from .ui.util import (
|
||||
)
|
||||
from .ui.page_offers import postNewOffer
|
||||
from .protocols.xmr_swap_1 import recoverNoScriptTxnWithKey, getChainBSplitKey
|
||||
from .db import Concepts
|
||||
|
||||
|
||||
def getFormData(post_string: str, is_json: bool):
|
||||
@@ -183,7 +184,19 @@ def js_wallets(self, url_split, post_string, is_json):
|
||||
|
||||
def js_offers(self, url_split, post_string, is_json, sent=False) -> bytes:
|
||||
swap_client = self.server.swap_client
|
||||
swap_client.checkSystemStatus()
|
||||
|
||||
try:
|
||||
swap_client.checkSystemStatus()
|
||||
except Exception as e:
|
||||
from basicswap.util import LockedCoinError
|
||||
from basicswap.ui.util import getCoinName
|
||||
|
||||
if isinstance(e, LockedCoinError):
|
||||
error_msg = f"Wallet must be unlocked to view offers. Please unlock your {getCoinName(e.coinid)} wallet."
|
||||
return bytes(json.dumps({"error": error_msg, "locked": True}), "UTF-8")
|
||||
else:
|
||||
return bytes(json.dumps({"error": str(e)}), "UTF-8")
|
||||
|
||||
offer_id = None
|
||||
if len(url_split) > 3:
|
||||
if url_split[3] == "new":
|
||||
@@ -206,6 +219,12 @@ def js_offers(self, url_split, post_string, is_json, sent=False) -> bytes:
|
||||
if offer_id:
|
||||
filters["offer_id"] = offer_id
|
||||
|
||||
parsed_url = urllib.parse.urlparse(self.path)
|
||||
query_params = urllib.parse.parse_qs(parsed_url.query) if parsed_url.query else {}
|
||||
|
||||
if "with_extra_info" in query_params:
|
||||
with_extra_info = toBool(query_params["with_extra_info"][0])
|
||||
|
||||
if post_string != "":
|
||||
post_data = getFormData(post_string, is_json)
|
||||
filters["coin_from"] = setCoinFilter(post_data, "coin_from")
|
||||
@@ -273,6 +292,24 @@ def js_offers(self, url_split, post_string, is_json, sent=False) -> bytes:
|
||||
offer_data["feerate_from"] = o.from_feerate
|
||||
offer_data["feerate_to"] = o.to_feerate
|
||||
|
||||
offer_data["automation_strat_id"] = getattr(o, "auto_accept_type", 0)
|
||||
|
||||
if o.was_sent:
|
||||
try:
|
||||
strategy = swap_client.getLinkedStrategy(Concepts.OFFER, o.offer_id)
|
||||
if strategy:
|
||||
offer_data["local_automation_strat_id"] = strategy[0]
|
||||
swap_client.log.debug(
|
||||
f"Found local automation strategy for own offer {o.offer_id.hex()}: {strategy[0]}"
|
||||
)
|
||||
else:
|
||||
offer_data["local_automation_strat_id"] = 0
|
||||
except Exception as e:
|
||||
swap_client.log.debug(
|
||||
f"Error getting local automation strategy for offer {o.offer_id.hex()}: {e}"
|
||||
)
|
||||
offer_data["local_automation_strat_id"] = 0
|
||||
|
||||
rv.append(offer_data)
|
||||
return bytes(json.dumps(rv), "UTF-8")
|
||||
|
||||
@@ -514,7 +551,19 @@ def js_bids(self, url_split, post_string: str, is_json: bool) -> bytes:
|
||||
|
||||
def js_sentbids(self, url_split, post_string, is_json) -> bytes:
|
||||
swap_client = self.server.swap_client
|
||||
swap_client.checkSystemStatus()
|
||||
|
||||
try:
|
||||
swap_client.checkSystemStatus()
|
||||
except Exception as e:
|
||||
from basicswap.util import LockedCoinError
|
||||
from basicswap.ui.util import getCoinName
|
||||
|
||||
if isinstance(e, LockedCoinError):
|
||||
error_msg = f"Wallet must be unlocked to view bids. Please unlock your {getCoinName(e.coinid)} wallet."
|
||||
return bytes(json.dumps({"error": error_msg, "locked": True}), "UTF-8")
|
||||
else:
|
||||
return bytes(json.dumps({"error": str(e)}), "UTF-8")
|
||||
|
||||
post_data = getFormData(post_string, is_json)
|
||||
offer_id, filters = parseBidFilters(post_data)
|
||||
|
||||
@@ -631,7 +680,19 @@ def js_rate(self, url_split, post_string, is_json) -> bytes:
|
||||
|
||||
def js_index(self, url_split, post_string, is_json) -> bytes:
|
||||
swap_client = self.server.swap_client
|
||||
swap_client.checkSystemStatus()
|
||||
|
||||
try:
|
||||
swap_client.checkSystemStatus()
|
||||
except Exception as e:
|
||||
from basicswap.util import LockedCoinError
|
||||
from basicswap.ui.util import getCoinName
|
||||
|
||||
if isinstance(e, LockedCoinError):
|
||||
error_msg = f"Wallet must be unlocked to view summary. Please unlock your {getCoinName(e.coinid)} wallet."
|
||||
return bytes(json.dumps({"error": error_msg, "locked": True}), "UTF-8")
|
||||
else:
|
||||
return bytes(json.dumps({"error": str(e)}), "UTF-8")
|
||||
|
||||
return bytes(json.dumps(swap_client.getSummary()), "UTF-8")
|
||||
|
||||
|
||||
|
||||
255
basicswap/static/js/amm_counter.js
Normal file
255
basicswap/static/js/amm_counter.js
Normal file
@@ -0,0 +1,255 @@
|
||||
const AmmCounterManager = (function() {
|
||||
const config = {
|
||||
refreshInterval: 10000,
|
||||
ammStatusEndpoint: '/amm/status',
|
||||
retryDelay: 5000,
|
||||
maxRetries: 3,
|
||||
debug: false
|
||||
};
|
||||
|
||||
let refreshTimer = null;
|
||||
let fetchRetryCount = 0;
|
||||
let lastAmmStatus = null;
|
||||
|
||||
function isDebugEnabled() {
|
||||
return localStorage.getItem('amm_debug_enabled') === 'true' || config.debug;
|
||||
}
|
||||
|
||||
function debugLog(message, data) {
|
||||
// if (isDebugEnabled()) {
|
||||
// if (data) {
|
||||
// console.log(`[AmmCounter] ${message}`, data);
|
||||
// } else {
|
||||
// console.log(`[AmmCounter] ${message}`);
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
function updateAmmCounter(count, status) {
|
||||
const ammCounter = document.getElementById('amm-counter');
|
||||
const ammCounterMobile = document.getElementById('amm-counter-mobile');
|
||||
|
||||
debugLog(`Updating AMM counter: count=${count}, status=${status}`);
|
||||
|
||||
if (ammCounter) {
|
||||
ammCounter.textContent = count;
|
||||
ammCounter.classList.remove('bg-blue-500', 'bg-gray-400');
|
||||
ammCounter.classList.add(status === 'running' && count > 0 ? 'bg-blue-500' : 'bg-gray-400');
|
||||
}
|
||||
|
||||
if (ammCounterMobile) {
|
||||
ammCounterMobile.textContent = count;
|
||||
ammCounterMobile.classList.remove('bg-blue-500', 'bg-gray-400');
|
||||
ammCounterMobile.classList.add(status === 'running' && count > 0 ? 'bg-blue-500' : 'bg-gray-400');
|
||||
}
|
||||
|
||||
updateAmmTooltips(count, status);
|
||||
}
|
||||
|
||||
function updateAmmTooltips(count, status) {
|
||||
debugLog(`updateAmmTooltips called with count=${count}, status=${status}`);
|
||||
|
||||
const subheaderTooltip = document.getElementById('tooltip-amm-subheader');
|
||||
debugLog('Looking for tooltip-amm-subheader element:', subheaderTooltip);
|
||||
|
||||
if (subheaderTooltip) {
|
||||
const statusText = status === 'running' ? 'Active' : 'Inactive';
|
||||
|
||||
const newContent = `
|
||||
<p><b>Status:</b> ${statusText}</p>
|
||||
<p><b>Currently active offers/bids:</b> ${count}</p>
|
||||
`;
|
||||
|
||||
const statusParagraph = subheaderTooltip.querySelector('p:first-child');
|
||||
const countParagraph = subheaderTooltip.querySelector('p:last-child');
|
||||
|
||||
if (statusParagraph && countParagraph) {
|
||||
statusParagraph.innerHTML = `<b>Status:</b> ${statusText}`;
|
||||
countParagraph.innerHTML = `<b>Currently active offers/bids:</b> ${count}`;
|
||||
debugLog(`Updated AMM subheader tooltip paragraphs: status=${statusText}, count=${count}`);
|
||||
} else {
|
||||
subheaderTooltip.innerHTML = newContent;
|
||||
debugLog(`Replaced AMM subheader tooltip content: status=${statusText}, count=${count}`);
|
||||
}
|
||||
|
||||
refreshTooltipInstances('tooltip-amm-subheader', newContent);
|
||||
} else {
|
||||
debugLog('AMM subheader tooltip element not found - checking all tooltip elements');
|
||||
const allTooltips = document.querySelectorAll('[id*="tooltip"]');
|
||||
debugLog('All tooltip elements found:', Array.from(allTooltips).map(el => el.id));
|
||||
}
|
||||
}
|
||||
|
||||
function refreshTooltipInstances(tooltipId, newContent) {
|
||||
const triggers = document.querySelectorAll(`[data-tooltip-target="${tooltipId}"]`);
|
||||
|
||||
triggers.forEach(trigger => {
|
||||
if (trigger._tippy) {
|
||||
trigger._tippy.setContent(newContent);
|
||||
debugLog(`Updated Tippy instance content for ${tooltipId}`);
|
||||
} else {
|
||||
if (window.TooltipManager && typeof window.TooltipManager.create === 'function') {
|
||||
window.TooltipManager.create(trigger, newContent, {
|
||||
placement: trigger.getAttribute('data-tooltip-placement') || 'top'
|
||||
});
|
||||
debugLog(`Created new Tippy instance for ${tooltipId}`);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (window.TooltipManager && typeof window.TooltipManager.refreshTooltip === 'function') {
|
||||
window.TooltipManager.refreshTooltip(tooltipId, newContent);
|
||||
debugLog(`Refreshed tooltip via TooltipManager for ${tooltipId}`);
|
||||
}
|
||||
|
||||
if (window.TooltipManager && typeof window.TooltipManager.initializeTooltips === 'function') {
|
||||
setTimeout(() => {
|
||||
window.TooltipManager.initializeTooltips(`[data-tooltip-target="${tooltipId}"]`);
|
||||
debugLog(`Re-initialized tooltips for ${tooltipId}`);
|
||||
}, 50);
|
||||
}
|
||||
}
|
||||
|
||||
function fetchAmmStatus() {
|
||||
debugLog('Fetching AMM status...');
|
||||
|
||||
let url = config.ammStatusEndpoint;
|
||||
if (isDebugEnabled()) {
|
||||
url += '?debug=true';
|
||||
}
|
||||
|
||||
return fetch(url, {
|
||||
headers: {
|
||||
'Accept': 'application/json',
|
||||
'Cache-Control': 'no-cache',
|
||||
'Pragma': 'no-cache'
|
||||
}
|
||||
})
|
||||
.then(response => {
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! Status: ${response.status}`);
|
||||
}
|
||||
return response.json();
|
||||
})
|
||||
.then(data => {
|
||||
lastAmmStatus = data;
|
||||
debugLog('AMM status data received:', data);
|
||||
updateAmmCounter(data.amm_active_count, data.status);
|
||||
fetchRetryCount = 0;
|
||||
return data;
|
||||
})
|
||||
.catch(error => {
|
||||
if (isDebugEnabled()) {
|
||||
console.error('[AmmCounter] AMM status fetch error:', error);
|
||||
}
|
||||
|
||||
if (fetchRetryCount < config.maxRetries) {
|
||||
fetchRetryCount++;
|
||||
debugLog(`Retrying AMM status fetch (${fetchRetryCount}/${config.maxRetries}) in ${config.retryDelay/1000}s`);
|
||||
|
||||
return new Promise(resolve => {
|
||||
setTimeout(() => {
|
||||
resolve(fetchAmmStatus());
|
||||
}, config.retryDelay);
|
||||
});
|
||||
} else {
|
||||
fetchRetryCount = 0;
|
||||
throw error;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function startRefreshTimer() {
|
||||
stopRefreshTimer();
|
||||
|
||||
debugLog('Starting AMM status refresh timer');
|
||||
|
||||
fetchAmmStatus()
|
||||
.then(() => {})
|
||||
.catch(() => {});
|
||||
|
||||
refreshTimer = setInterval(() => {
|
||||
fetchAmmStatus()
|
||||
.then(() => {})
|
||||
.catch(() => {});
|
||||
}, config.refreshInterval);
|
||||
}
|
||||
|
||||
function stopRefreshTimer() {
|
||||
if (refreshTimer) {
|
||||
debugLog('Stopping AMM status refresh timer');
|
||||
clearInterval(refreshTimer);
|
||||
refreshTimer = null;
|
||||
}
|
||||
}
|
||||
|
||||
function setupWebSocketHandler() {
|
||||
if (window.WebSocketManager && typeof window.WebSocketManager.addMessageHandler === 'function') {
|
||||
debugLog('Setting up WebSocket handler for AMM status updates');
|
||||
window.WebSocketManager.addMessageHandler('message', (data) => {
|
||||
if (data && data.event) {
|
||||
debugLog('WebSocket event received, refreshing AMM status');
|
||||
fetchAmmStatus()
|
||||
.then(() => {})
|
||||
.catch(() => {});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function setupDebugListener() {
|
||||
const debugCheckbox = document.getElementById('amm_debug');
|
||||
if (debugCheckbox) {
|
||||
debugLog('Found AMM debug checkbox, setting up listener');
|
||||
|
||||
localStorage.setItem('amm_debug_enabled', debugCheckbox.checked ? 'true' : 'false');
|
||||
|
||||
debugCheckbox.addEventListener('change', function() {
|
||||
localStorage.setItem('amm_debug_enabled', this.checked ? 'true' : 'false');
|
||||
debugLog(`Debug mode ${this.checked ? 'enabled' : 'disabled'}`);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const publicAPI = {
|
||||
initialize: function(options = {}) {
|
||||
Object.assign(config, options);
|
||||
|
||||
setupWebSocketHandler();
|
||||
setupDebugListener();
|
||||
startRefreshTimer();
|
||||
|
||||
debugLog('AMM Counter Manager initialized');
|
||||
|
||||
if (window.CleanupManager) {
|
||||
window.CleanupManager.registerResource('ammCounterManager', this, (mgr) => mgr.dispose());
|
||||
}
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
fetchAmmStatus: fetchAmmStatus,
|
||||
|
||||
updateCounter: updateAmmCounter,
|
||||
|
||||
updateTooltips: updateAmmTooltips,
|
||||
|
||||
startRefreshTimer: startRefreshTimer,
|
||||
|
||||
stopRefreshTimer: stopRefreshTimer,
|
||||
|
||||
dispose: function() {
|
||||
debugLog('Disposing AMM Counter Manager');
|
||||
stopRefreshTimer();
|
||||
}
|
||||
};
|
||||
|
||||
return publicAPI;
|
||||
})();
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
if (!window.ammCounterManagerInitialized) {
|
||||
window.AmmCounterManager = AmmCounterManager.initialize();
|
||||
window.ammCounterManagerInitialized = true;
|
||||
}
|
||||
});
|
||||
2367
basicswap/static/js/amm_tables.js
Normal file
2367
basicswap/static/js/amm_tables.js
Normal file
File diff suppressed because it is too large
Load Diff
@@ -30,7 +30,7 @@ const state = {
|
||||
|
||||
document.addEventListener('tabactivated', function(event) {
|
||||
if (event.detail && event.detail.tabId) {
|
||||
const tabType = event.detail.type || (event.detail.tabId === '#all' ? 'all' :
|
||||
const tabType = event.detail.type || (event.detail.tabId === '#all' ? 'all' :
|
||||
(event.detail.tabId === '#sent' ? 'sent' : 'received'));
|
||||
//console.log('Tab activation event received for:', tabType);
|
||||
state.currentTab = tabType;
|
||||
@@ -555,7 +555,7 @@ function filterAndSortData(bids) {
|
||||
const coinName = selectedOption?.textContent.trim();
|
||||
|
||||
if (coinName) {
|
||||
const coinToMatch = state.currentTab === 'all'
|
||||
const coinToMatch = state.currentTab === 'all'
|
||||
? (bid.source === 'sent' ? bid.coin_to : bid.coin_from)
|
||||
: (state.currentTab === 'sent' ? bid.coin_to : bid.coin_from);
|
||||
if (!coinMatches(coinToMatch, coinName)) {
|
||||
@@ -614,7 +614,7 @@ function filterAndSortData(bids) {
|
||||
let matchesDisplayedLabel = false;
|
||||
if (!matchesLabel && document) {
|
||||
try {
|
||||
const tableId = state.currentTab === 'sent' ? 'sent' :
|
||||
const tableId = state.currentTab === 'sent' ? 'sent' :
|
||||
(state.currentTab === 'received' ? 'received' : 'all');
|
||||
const cells = document.querySelectorAll(`#${tableId} a[href^="/identity/"]`);
|
||||
|
||||
@@ -1056,6 +1056,24 @@ async function fetchAllBids() {
|
||||
receivedResponse.json()
|
||||
]);
|
||||
|
||||
if (sentData.error || receivedData.error) {
|
||||
const errorData = sentData.error ? sentData : receivedData;
|
||||
if (errorData.locked) {
|
||||
const tbody = elements.allBidsBody;
|
||||
if (tbody) {
|
||||
tbody.innerHTML = `
|
||||
<tr>
|
||||
<td colspan="8" class="text-center py-4 text-yellow-600 dark:text-yellow-400">
|
||||
${errorData.error}
|
||||
</td>
|
||||
</tr>`;
|
||||
}
|
||||
return [];
|
||||
} else {
|
||||
throw new Error(errorData.error);
|
||||
}
|
||||
}
|
||||
|
||||
const filteredSentData = filterAndSortData(sentData).map(bid => ({ ...bid, source: 'sent' }));
|
||||
const filteredReceivedData = filterAndSortData(receivedData).map(bid => ({ ...bid, source: 'received' }));
|
||||
|
||||
@@ -1090,7 +1108,7 @@ const createTableRow = async (bid) => {
|
||||
const timeColor = getTimeStrokeColor(bid.expire_at);
|
||||
const currentTabIsAll = state.currentTab === 'all';
|
||||
const isSent = currentTabIsAll ? (bid.source === 'sent') : (state.currentTab === 'sent');
|
||||
const sourceIndicator = currentTabIsAll ?
|
||||
const sourceIndicator = currentTabIsAll ?
|
||||
`<span class="ml-1 px-1.5 py-0.5 text-xs font-medium ${isSent ? 'bg-blue-100 text-blue-800 dark:bg-blue-900 dark:text-blue-300' : 'bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-300'} rounded">
|
||||
${isSent ? 'Sent' : 'Received'}
|
||||
</span>` : '';
|
||||
@@ -1321,6 +1339,24 @@ async function fetchBids(type = state.currentTab) {
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
if (data.error) {
|
||||
if (data.locked) {
|
||||
const tbody = elements[`${type}BidsBody`];
|
||||
if (tbody) {
|
||||
tbody.innerHTML = `
|
||||
<tr>
|
||||
<td colspan="8" class="text-center py-4 text-yellow-600 dark:text-yellow-400">
|
||||
${data.error}
|
||||
</td>
|
||||
</tr>`;
|
||||
}
|
||||
return [];
|
||||
} else {
|
||||
throw new Error(data.error);
|
||||
}
|
||||
}
|
||||
|
||||
//console.log(`Received raw ${type} data:`, data.length, 'bids');
|
||||
|
||||
state.filters.with_expired = includeExpired;
|
||||
@@ -1509,14 +1545,13 @@ const updateBidsTable = async () => {
|
||||
updateLoadingState(true);
|
||||
|
||||
let bids;
|
||||
|
||||
|
||||
if (state.currentTab === 'all') {
|
||||
bids = await fetchAllBids();
|
||||
} else {
|
||||
bids = await fetchBids();
|
||||
}
|
||||
|
||||
// Add identity preloading if we're searching
|
||||
if (state.filters.searchQuery && state.filters.searchQuery.length > 0) {
|
||||
await preloadIdentitiesForSearch(bids);
|
||||
}
|
||||
@@ -1774,7 +1809,7 @@ const setupRefreshButtons = () => {
|
||||
|
||||
state.data[lowerType] = data;
|
||||
}
|
||||
|
||||
|
||||
await updateTableContent(lowerType);
|
||||
updatePaginationControls(lowerType);
|
||||
|
||||
@@ -1802,7 +1837,7 @@ const switchTab = (tabId) => {
|
||||
|
||||
tooltipIdsToCleanup.clear();
|
||||
|
||||
state.currentTab = tabId === '#all' ? 'all' :
|
||||
state.currentTab = tabId === '#all' ? 'all' :
|
||||
(tabId === '#sent' ? 'sent' : 'received');
|
||||
|
||||
elements.allContent.classList.add('hidden');
|
||||
@@ -1888,7 +1923,7 @@ const setupEventListeners = () => {
|
||||
elements.sentContent.classList.toggle('hidden', targetId !== '#sent');
|
||||
elements.receivedContent.classList.toggle('hidden', targetId !== '#received');
|
||||
|
||||
state.currentTab = targetId === '#all' ? 'all' :
|
||||
state.currentTab = targetId === '#all' ? 'all' :
|
||||
(targetId === '#sent' ? 'sent' : 'received');
|
||||
state.currentPage[state.currentTab] = 1;
|
||||
|
||||
@@ -2101,7 +2136,7 @@ function initialize() {
|
||||
}, 100);
|
||||
|
||||
setupMemoryMonitoring();
|
||||
|
||||
|
||||
window.cleanupBidsTable = cleanup;
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,8 @@ const SummaryManager = (function() {
|
||||
summaryEndpoint: '/json',
|
||||
retryDelay: 5000,
|
||||
maxRetries: 3,
|
||||
requestTimeout: 15000
|
||||
requestTimeout: 15000,
|
||||
debug: false
|
||||
};
|
||||
|
||||
let refreshTimer = null;
|
||||
@@ -60,12 +61,15 @@ const SummaryManager = (function() {
|
||||
|
||||
updateElement('network-offers-counter', data.num_network_offers);
|
||||
updateElement('offers-counter', data.num_sent_active_offers);
|
||||
updateElement('offers-counter-mobile', 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);
|
||||
|
||||
updateTooltips(data);
|
||||
|
||||
const shutdownButtons = document.querySelectorAll('.shutdown-button');
|
||||
shutdownButtons.forEach(button => {
|
||||
button.setAttribute('data-active-swaps', data.num_swapping);
|
||||
@@ -81,6 +85,100 @@ const SummaryManager = (function() {
|
||||
});
|
||||
}
|
||||
|
||||
function updateTooltips(data) {
|
||||
debugLog(`updateTooltips called with data:`, data);
|
||||
|
||||
const yourOffersTooltip = document.getElementById('tooltip-your-offers');
|
||||
debugLog('Looking for tooltip-your-offers element:', yourOffersTooltip);
|
||||
|
||||
if (yourOffersTooltip) {
|
||||
const newContent = `
|
||||
<p><b>Total offers:</b> ${data.num_sent_offers || 0}</p>
|
||||
<p><b>Active offers:</b> ${data.num_sent_active_offers || 0}</p>
|
||||
`;
|
||||
|
||||
const totalParagraph = yourOffersTooltip.querySelector('p:first-child');
|
||||
const activeParagraph = yourOffersTooltip.querySelector('p:last-child');
|
||||
|
||||
debugLog('Found paragraphs:', { totalParagraph, activeParagraph });
|
||||
|
||||
if (totalParagraph && activeParagraph) {
|
||||
totalParagraph.innerHTML = `<b>Total offers:</b> ${data.num_sent_offers || 0}`;
|
||||
activeParagraph.innerHTML = `<b>Active offers:</b> ${data.num_sent_active_offers || 0}`;
|
||||
debugLog(`Updated Your Offers tooltip paragraphs: total=${data.num_sent_offers}, active=${data.num_sent_active_offers}`);
|
||||
} else {
|
||||
yourOffersTooltip.innerHTML = newContent;
|
||||
debugLog(`Replaced Your Offers tooltip content: total=${data.num_sent_offers}, active=${data.num_sent_active_offers}`);
|
||||
}
|
||||
|
||||
refreshTooltipInstances('tooltip-your-offers', newContent);
|
||||
} else {
|
||||
debugLog('Your Offers tooltip element not found - checking all tooltip elements');
|
||||
const allTooltips = document.querySelectorAll('[id*="tooltip"]');
|
||||
debugLog('All tooltip elements found:', Array.from(allTooltips).map(el => el.id));
|
||||
}
|
||||
|
||||
const bidsTooltip = document.getElementById('tooltip-bids');
|
||||
if (bidsTooltip) {
|
||||
const newBidsContent = `
|
||||
<p><b>Sent bids:</b> ${data.num_sent_bids || 0} (${data.num_sent_active_bids || 0} active)</p>
|
||||
<p><b>Received bids:</b> ${data.num_recv_bids || 0} (${data.num_recv_active_bids || 0} active)</p>
|
||||
`;
|
||||
|
||||
const sentParagraph = bidsTooltip.querySelector('p:first-child');
|
||||
const recvParagraph = bidsTooltip.querySelector('p:last-child');
|
||||
|
||||
if (sentParagraph && recvParagraph) {
|
||||
sentParagraph.innerHTML = `<b>Sent bids:</b> ${data.num_sent_bids || 0} (${data.num_sent_active_bids || 0} active)`;
|
||||
recvParagraph.innerHTML = `<b>Received bids:</b> ${data.num_recv_bids || 0} (${data.num_recv_active_bids || 0} active)`;
|
||||
debugLog(`Updated Bids tooltip: sent=${data.num_sent_bids}(${data.num_sent_active_bids}), recv=${data.num_recv_bids}(${data.num_recv_active_bids})`);
|
||||
} else {
|
||||
bidsTooltip.innerHTML = newBidsContent;
|
||||
debugLog(`Replaced Bids tooltip content: sent=${data.num_sent_bids}(${data.num_sent_active_bids}), recv=${data.num_recv_bids}(${data.num_recv_active_bids})`);
|
||||
}
|
||||
|
||||
refreshTooltipInstances('tooltip-bids', newBidsContent);
|
||||
} else {
|
||||
debugLog('Bids tooltip element not found');
|
||||
}
|
||||
}
|
||||
|
||||
function refreshTooltipInstances(tooltipId, newContent) {
|
||||
const triggers = document.querySelectorAll(`[data-tooltip-target="${tooltipId}"]`);
|
||||
|
||||
triggers.forEach(trigger => {
|
||||
if (trigger._tippy) {
|
||||
trigger._tippy.setContent(newContent);
|
||||
debugLog(`Updated Tippy instance content for ${tooltipId}`);
|
||||
} else {
|
||||
if (window.TooltipManager && typeof window.TooltipManager.create === 'function') {
|
||||
window.TooltipManager.create(trigger, newContent, {
|
||||
placement: trigger.getAttribute('data-tooltip-placement') || 'top'
|
||||
});
|
||||
debugLog(`Created new Tippy instance for ${tooltipId}`);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (window.TooltipManager && typeof window.TooltipManager.refreshTooltip === 'function') {
|
||||
window.TooltipManager.refreshTooltip(tooltipId, newContent);
|
||||
debugLog(`Refreshed tooltip via TooltipManager for ${tooltipId}`);
|
||||
}
|
||||
|
||||
if (window.TooltipManager && typeof window.TooltipManager.initializeTooltips === 'function') {
|
||||
setTimeout(() => {
|
||||
window.TooltipManager.initializeTooltips(`[data-tooltip-target="${tooltipId}"]`);
|
||||
debugLog(`Re-initialized tooltips for ${tooltipId}`);
|
||||
}, 50);
|
||||
}
|
||||
}
|
||||
|
||||
function debugLog(message) {
|
||||
if (config.debug && console && console.log) {
|
||||
console.log(`[SummaryManager] ${message}`);
|
||||
}
|
||||
}
|
||||
|
||||
function cacheSummaryData(data) {
|
||||
if (!data) return;
|
||||
|
||||
@@ -303,6 +401,14 @@ const SummaryManager = (function() {
|
||||
});
|
||||
},
|
||||
|
||||
updateTooltips: function(data) {
|
||||
updateTooltips(data || lastSuccessfulData);
|
||||
},
|
||||
|
||||
updateUI: function(data) {
|
||||
updateUIFromData(data || lastSuccessfulData);
|
||||
},
|
||||
|
||||
startRefreshTimer: function() {
|
||||
startRefreshTimer();
|
||||
},
|
||||
|
||||
@@ -217,8 +217,8 @@ function filterAndSortData() {
|
||||
|
||||
const sentFromFilter = filters.sent_from || 'any';
|
||||
filteredData = filteredData.filter(offer => {
|
||||
const isMatch = sentFromFilter === 'public' ? offer.is_public :
|
||||
sentFromFilter === 'private' ? !offer.is_public :
|
||||
const isMatch = sentFromFilter === 'public' ? offer.is_public :
|
||||
sentFromFilter === 'private' ? !offer.is_public :
|
||||
true;
|
||||
return isMatch;
|
||||
});
|
||||
@@ -232,7 +232,7 @@ function filterAndSortData() {
|
||||
const coinToSelect = document.getElementById('coin_to');
|
||||
const selectedOption = coinToSelect?.querySelector(`option[value="${filters.coin_to}"]`);
|
||||
const coinName = selectedOption?.textContent.trim();
|
||||
|
||||
|
||||
if (coinName && !coinMatches(offer.coin_to, coinName)) {
|
||||
return false;
|
||||
}
|
||||
@@ -254,13 +254,13 @@ function filterAndSortData() {
|
||||
|
||||
let statusMatch = false;
|
||||
switch (filters.status) {
|
||||
case 'active':
|
||||
case 'active':
|
||||
statusMatch = !isExpired && !isRevoked;
|
||||
break;
|
||||
case 'expired':
|
||||
case 'expired':
|
||||
statusMatch = isExpired && !isRevoked;
|
||||
break;
|
||||
case 'revoked':
|
||||
case 'revoked':
|
||||
statusMatch = isRevoked;
|
||||
break;
|
||||
}
|
||||
@@ -275,7 +275,7 @@ function filterAndSortData() {
|
||||
|
||||
if (currentSortColumn === 7) {
|
||||
const offersWithPercentages = [];
|
||||
|
||||
|
||||
for (const offer of filteredData) {
|
||||
const fromAmount = parseFloat(offer.amount_from) || 0;
|
||||
const toAmount = parseFloat(offer.amount_to) || 0;
|
||||
@@ -293,7 +293,7 @@ function filterAndSortData() {
|
||||
if (fromPriceUSD && toPriceUSD && !isNaN(fromPriceUSD) && !isNaN(toPriceUSD)) {
|
||||
const fromValueUSD = fromAmount * fromPriceUSD;
|
||||
const toValueUSD = toAmount * toPriceUSD;
|
||||
|
||||
|
||||
if (fromValueUSD && toValueUSD) {
|
||||
if (offer.is_own_offer || isSentOffers) {
|
||||
percentDiff = ((toValueUSD / fromValueUSD) - 1) * 100;
|
||||
@@ -302,7 +302,7 @@ function filterAndSortData() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
offersWithPercentages.push({
|
||||
offer: offer,
|
||||
percentDiff: percentDiff
|
||||
@@ -353,7 +353,7 @@ function filterAndSortData() {
|
||||
const bRate = parseFloat(b.rate) || 0;
|
||||
const aPriceUSD = latestPrices && aSymbol ? latestPrices[aSymbol]?.usd : null;
|
||||
const bPriceUSD = latestPrices && bSymbol ? latestPrices[bSymbol]?.usd : null;
|
||||
|
||||
|
||||
aValue = aPriceUSD && !isNaN(aPriceUSD) ? aRate * aPriceUSD : 0;
|
||||
bValue = bPriceUSD && !isNaN(bPriceUSD) ? bRate * bPriceUSD : 0;
|
||||
break;
|
||||
@@ -367,8 +367,8 @@ function filterAndSortData() {
|
||||
}
|
||||
|
||||
if (typeof aValue === 'string' && typeof bValue === 'string') {
|
||||
return currentSortDirection === 'asc'
|
||||
? aValue.localeCompare(bValue)
|
||||
return currentSortDirection === 'asc'
|
||||
? aValue.localeCompare(bValue)
|
||||
: bValue.localeCompare(aValue);
|
||||
}
|
||||
|
||||
@@ -395,7 +395,7 @@ async function calculateProfitLoss(fromCoin, toCoin, fromAmount, toAmount, isOwn
|
||||
normalizedCoin = window.CoinManager.getPriceKey(coin) || normalizedCoin;
|
||||
} else {
|
||||
if (normalizedCoin === 'zcoin') normalizedCoin = 'firo';
|
||||
if (normalizedCoin === 'bitcoincash' || normalizedCoin === 'bitcoin cash')
|
||||
if (normalizedCoin === 'bitcoincash' || normalizedCoin === 'bitcoin cash')
|
||||
normalizedCoin = 'bitcoin-cash';
|
||||
if (normalizedCoin.includes('particl')) normalizedCoin = 'particl';
|
||||
}
|
||||
@@ -481,6 +481,29 @@ async function fetchOffers() {
|
||||
}
|
||||
|
||||
const data = await offersResponse.json();
|
||||
|
||||
if (data.error) {
|
||||
if (data.locked) {
|
||||
if (typeof ui !== 'undefined' && ui.displayErrorMessage) {
|
||||
ui.displayErrorMessage(data.error);
|
||||
} else {
|
||||
offersBody.innerHTML = `
|
||||
<tr>
|
||||
<td colspan="9" class="text-center py-8">
|
||||
<div class="flex flex-col items-center justify-center text-yellow-600 dark:text-yellow-400">
|
||||
<svg class="w-8 h-8 mb-2" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-2.5L13.732 4c-.77-.833-1.964-.833-2.732 0L4.082 16.5c-.77.833.192 2.5 1.732 2.5z"></path>
|
||||
</svg>
|
||||
<span class="font-medium">${data.error}</span>
|
||||
</div>
|
||||
</td>
|
||||
</tr>`;
|
||||
}
|
||||
return;
|
||||
} else {
|
||||
throw new Error(data.error);
|
||||
}
|
||||
}
|
||||
const processedData = Array.isArray(data) ? data : Object.values(data);
|
||||
|
||||
jsonData = formatInitialData(processedData);
|
||||
@@ -980,7 +1003,7 @@ function createTableRow(offer, identity = null) {
|
||||
} = offer;
|
||||
|
||||
let coinFromSymbol, coinToSymbol;
|
||||
|
||||
|
||||
if (window.CoinManager) {
|
||||
coinFromSymbol = window.CoinManager.getSymbol(coinFrom) || coinFrom.toLowerCase();
|
||||
coinToSymbol = window.CoinManager.getSymbol(coinTo) || coinTo.toLowerCase();
|
||||
@@ -1572,7 +1595,7 @@ function createCombinedRateTooltip(offer, coinFrom, coinTo, treatAsSentOffer) {
|
||||
|
||||
const getPriceKey = (coin) => {
|
||||
if (!coin) return null;
|
||||
|
||||
|
||||
const lowerCoin = coin.toLowerCase();
|
||||
|
||||
if (lowerCoin === 'zcoin') return 'firo';
|
||||
@@ -1807,7 +1830,7 @@ function getPriceKey(coin) {
|
||||
}
|
||||
|
||||
if (!coin) return null;
|
||||
|
||||
|
||||
const lowerCoin = coin.toLowerCase();
|
||||
|
||||
if (lowerCoin === 'zcoin') {
|
||||
@@ -1818,7 +1841,7 @@ function getPriceKey(coin) {
|
||||
return 'bitcoin-cash';
|
||||
}
|
||||
|
||||
if (lowerCoin === 'part' || lowerCoin === 'particl' ||
|
||||
if (lowerCoin === 'part' || lowerCoin === 'particl' ||
|
||||
lowerCoin.includes('particl')) {
|
||||
return 'particl';
|
||||
}
|
||||
@@ -2221,7 +2244,7 @@ document.addEventListener('DOMContentLoaded', async function() {
|
||||
}
|
||||
|
||||
await updateOffersTable();
|
||||
|
||||
|
||||
updateProfitLossDisplays();
|
||||
|
||||
document.querySelectorAll('.usd-value').forEach(usdValue => {
|
||||
|
||||
2019
basicswap/templates/amm.html
Normal file
2019
basicswap/templates/amm.html
Normal file
File diff suppressed because it is too large
Load Diff
@@ -833,6 +833,27 @@
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<div id="confirmModal" class="fixed inset-0 z-50 hidden overflow-y-auto">
|
||||
<div class="fixed inset-0 bg-black bg-opacity-50 transition-opacity duration-300 ease-out"></div>
|
||||
<div class="relative z-50 min-h-screen px-4 flex items-center justify-center">
|
||||
<div class="bg-white dark:bg-gray-500 rounded-lg max-w-md w-full p-6 shadow-lg transition-opacity duration-300 ease-out">
|
||||
<div class="text-center">
|
||||
<h2 class="text-xl font-semibold text-gray-900 dark:text-white mb-4" id="confirmTitle">Confirm Action</h2>
|
||||
<p class="text-gray-600 dark:text-gray-200 mb-6 whitespace-pre-line" id="confirmMessage">Are you sure?</p>
|
||||
<div class="flex justify-center gap-4">
|
||||
<button type="button" id="confirmYes"
|
||||
class="px-4 py-2.5 bg-blue-500 hover:bg-blue-600 font-medium text-sm text-white border border-blue-500 rounded-md shadow-button focus:ring-0 focus:outline-none">
|
||||
Confirm
|
||||
</button>
|
||||
<button type="button" id="confirmNo"
|
||||
class="px-4 py-2.5 font-medium text-sm text-white hover:text-red border border-red-500 hover:border-red-500 hover:bg-red-600 bg-red-500 rounded-md shadow-button focus:ring-0 focus:outline-none">
|
||||
Cancel
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<input type="hidden" name="formid" value="{{ form_id }}">
|
||||
</div>
|
||||
</div>
|
||||
@@ -840,9 +861,74 @@
|
||||
</div>
|
||||
</form>
|
||||
<script>
|
||||
function confirmPopup(name) {
|
||||
return confirm(name + " Bid - Are you sure?");
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
let confirmCallback = null;
|
||||
let triggerElement = null;
|
||||
|
||||
document.getElementById('confirmYes').addEventListener('click', function() {
|
||||
if (typeof confirmCallback === 'function') {
|
||||
confirmCallback();
|
||||
}
|
||||
hideConfirmDialog();
|
||||
});
|
||||
|
||||
document.getElementById('confirmNo').addEventListener('click', hideConfirmDialog);
|
||||
|
||||
function showConfirmDialog(title, message, callback) {
|
||||
confirmCallback = callback;
|
||||
document.getElementById('confirmTitle').textContent = title;
|
||||
document.getElementById('confirmMessage').textContent = message;
|
||||
const modal = document.getElementById('confirmModal');
|
||||
if (modal) {
|
||||
modal.classList.remove('hidden');
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function hideConfirmDialog() {
|
||||
const modal = document.getElementById('confirmModal');
|
||||
if (modal) {
|
||||
modal.classList.add('hidden');
|
||||
}
|
||||
confirmCallback = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
window.confirmPopup = function(action = 'Abandon') {
|
||||
triggerElement = document.activeElement;
|
||||
const title = `Confirm ${action} Bid`;
|
||||
const message = `Are you sure you want to ${action.toLowerCase()} this bid?`;
|
||||
|
||||
return showConfirmDialog(title, message, function() {
|
||||
if (triggerElement) {
|
||||
const form = triggerElement.form;
|
||||
const hiddenInput = document.createElement('input');
|
||||
hiddenInput.type = 'hidden';
|
||||
hiddenInput.name = triggerElement.name;
|
||||
hiddenInput.value = triggerElement.value;
|
||||
form.appendChild(hiddenInput);
|
||||
form.submit();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const overrideButtonConfirm = function(button, action) {
|
||||
if (button) {
|
||||
button.removeAttribute('onclick');
|
||||
button.addEventListener('click', function(e) {
|
||||
e.preventDefault();
|
||||
triggerElement = this;
|
||||
return confirmPopup(action);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const abandonBidBtn = document.querySelector('button[name="abandon_bid"]');
|
||||
overrideButtonConfirm(abandonBidBtn, 'Abandon');
|
||||
|
||||
const acceptBidBtn = document.querySelector('button[name="accept_bid"]');
|
||||
overrideButtonConfirm(acceptBidBtn, 'Accept');
|
||||
});
|
||||
</script>
|
||||
</div>
|
||||
{% include 'footer.html' %}
|
||||
|
||||
334
basicswap/templates/donation.html
Normal file
334
basicswap/templates/donation.html
Normal file
@@ -0,0 +1,334 @@
|
||||
{% include 'header.html' %}
|
||||
{% from 'style.html' import breadcrumb_line_svg, love_svg %}
|
||||
|
||||
<section class="py-3 px-4 mt-6">
|
||||
<div class="lg:container mx-auto">
|
||||
<div class="relative py-8 px-8 bg-coolGray-900 dark:bg-blue-500 rounded-md overflow-hidden">
|
||||
<img class="absolute z-10 left-4 top-4 right-4 bottom-4" src="/static/images/elements/dots-red.svg" alt="dots-red">
|
||||
<img class="absolute h-64 left-1/2 top-1/2 transform -translate-x-1/2 -translate-y-1/2 object-cover" src="/static/images/elements/wave.svg" alt="wave">
|
||||
<div class="relative z-20 flex flex-wrap items-center justify-center text-center">
|
||||
<div class="w-full">
|
||||
<h2 class="text-3xl font-bold text-white mb-4">
|
||||
Support BasicSwap Development
|
||||
</h2>
|
||||
<p class="text-lg text-white max-w-3xl mx-auto">
|
||||
Help keep BasicSwap free and open-source. Your donations directly fund development, security audits, and community growth.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<div class="xl:container mx-auto">
|
||||
|
||||
<section class="p-6">
|
||||
<div class="lg:container mx-auto">
|
||||
<div class="text-center mb-8">
|
||||
<h3 class="font-semibold text-2xl text-black dark:text-white mb-4">Why Your Support Matters</h3>
|
||||
<div class="flex justify-center mb-6">
|
||||
{{ love_svg | safe }}
|
||||
</div>
|
||||
<div class="max-w-4xl mx-auto">
|
||||
<p class="text-lg text-coolGray-500 dark:text-gray-300 mb-6">
|
||||
BasicSwap is completely free and open-source software that charges no fees for its use. The project is entirely funded by generous community donations from users who believe in decentralized, censorship-resistant trading.
|
||||
</p>
|
||||
<p class="text-lg text-coolGray-500 dark:text-gray-300 mb-8">
|
||||
Your donations are vital to keeping this project alive, accelerating development, and expanding our reach to more users who value financial freedom and privacy.
|
||||
</p>
|
||||
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6 mb-8">
|
||||
<div class="bg-coolGray-100 dark:bg-gray-500 rounded-lg p-6">
|
||||
<div class="text-green-500 mb-3">
|
||||
<svg class="w-8 h-8 mx-auto" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path fill-rule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clip-rule="evenodd"></path>
|
||||
</svg>
|
||||
</div>
|
||||
<h4 class="font-semibold text-coolGray-900 dark:text-white mb-2">Core Development</h4>
|
||||
<p class="text-sm text-coolGray-500 dark:text-gray-300">New features and improvements</p>
|
||||
</div>
|
||||
<div class="bg-coolGray-100 dark:bg-gray-500 rounded-lg p-6">
|
||||
<div class="text-green-500 mb-3">
|
||||
<svg class="w-8 h-8 mx-auto" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path fill-rule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clip-rule="evenodd"></path>
|
||||
</svg>
|
||||
</div>
|
||||
<h4 class="font-semibold text-coolGray-900 dark:text-white mb-2">Security Audits</h4>
|
||||
<p class="text-sm text-coolGray-500 dark:text-gray-300">Testing and security infrastructure</p>
|
||||
</div>
|
||||
<div class="bg-coolGray-100 dark:bg-gray-500 rounded-lg p-6">
|
||||
<div class="text-green-500 mb-3">
|
||||
<svg class="w-8 h-8 mx-auto" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path fill-rule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clip-rule="evenodd"></path>
|
||||
</svg>
|
||||
</div>
|
||||
<h4 class="font-semibold text-coolGray-900 dark:text-white mb-2">Documentation</h4>
|
||||
<p class="text-sm text-coolGray-500 dark:text-gray-300">Educational resources and guides</p>
|
||||
</div>
|
||||
<div class="bg-coolGray-100 dark:bg-gray-500 rounded-lg p-6">
|
||||
<div class="text-green-500 mb-3">
|
||||
<svg class="w-8 h-8 mx-auto" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path fill-rule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clip-rule="evenodd"></path>
|
||||
</svg>
|
||||
</div>
|
||||
<h4 class="font-semibold text-coolGray-900 dark:text-white mb-2">Community Growth</h4>
|
||||
<p class="text-sm text-coolGray-500 dark:text-gray-300">Outreach and adoption initiatives</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p class="text-lg text-coolGray-500 dark:text-gray-300">
|
||||
Together, we're building financial tools that empower individuals and resist censorship. Thank you for being part of this movement toward true financial freedom.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<div class="px-6 py-0 h-full overflow-hidden">
|
||||
<div class="pb-6 border-coolGray-100">
|
||||
<div class="flex flex-wrap items-center justify-between -m-2">
|
||||
<div class="w-full pt-2">
|
||||
<div class="lg:container mt-5 mx-auto">
|
||||
<div class="pt-6 pb-8 bg-coolGray-100 dark:bg-gray-500 rounded-xl">
|
||||
<div class="px-6">
|
||||
<h3 class="mb-6 text-2xl text-coolGray-900 dark:text-white font-bold text-center">
|
||||
Donation Addresses
|
||||
</h3>
|
||||
<div class="w-full pb-6 overflow-x-auto">
|
||||
<table class="w-full text-lg lg:text-sm">
|
||||
<thead class="uppercase">
|
||||
<tr class="text-left">
|
||||
<th class="p-0 w-1/6">
|
||||
<div class="py-3 px-6 rounded-tl-xl bg-coolGray-200 dark:bg-gray-600">
|
||||
<span class="text-md lg:text-xs text-gray-600 dark:text-gray-300 font-semibold">Cryptocurrency</span>
|
||||
</div>
|
||||
</th>
|
||||
<th class="p-0 w-5/6">
|
||||
<div class="py-3 px-6 rounded-tr-xl bg-coolGray-200 dark:bg-gray-600">
|
||||
<span class="text-md lg:text-xs text-gray-600 dark:text-gray-300 font-semibold">Donation Address</span>
|
||||
</div>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<!-- Monero -->
|
||||
<tr class="opacity-100 text-gray-500 dark:text-gray-100 hover:bg-coolGray-200 dark:hover:bg-gray-600">
|
||||
<td class="py-3 px-6 bold">
|
||||
<span class="inline-flex align-middle items-center justify-center w-9 h-10 bg-white-50 rounded">
|
||||
<img class="h-7" src="/static/images/coins/Monero.png" alt="Monero">
|
||||
</span>
|
||||
Monero (XMR)
|
||||
</td>
|
||||
<td class="py-3 px-6">
|
||||
<input type="text" readonly
|
||||
class="donation-address cursor-pointer hover:border-blue-500 bg-gray-50 text-gray-900 appearance-none dark:bg-gray-500 dark:text-white border border-gray-300 dark:border-gray-400 dark:text-gray-50 text-sm rounded-lg outline-none focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 focus:ring-0 font-mono"
|
||||
value="8BuQsYBNdfhfoWsvVR1unE7YuZEoTkC4hANaPm2fD6VR5VM2DzQoJhq2CHHXUN1UCWQfH3dctJgorSRxksVa5U4RNTJkcAc"
|
||||
data-address="8BuQsYBNdfhfoWsvVR1unE7YuZEoTkC4hANaPm2fD6VR5VM2DzQoJhq2CHHXUN1UCWQfH3dctJgorSRxksVa5U4RNTJkcAc">
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<!-- Litecoin -->
|
||||
<tr class="opacity-100 text-gray-500 dark:text-gray-100 hover:bg-coolGray-200 dark:hover:bg-gray-600">
|
||||
<td class="py-3 px-6 bold">
|
||||
<span class="inline-flex align-middle items-center justify-center w-9 h-10 bg-white-50 rounded">
|
||||
<img class="h-7" src="/static/images/coins/Litecoin.png" alt="Litecoin">
|
||||
</span>
|
||||
Litecoin (LTC)
|
||||
</td>
|
||||
<td class="py-3 px-6">
|
||||
<input type="text" readonly
|
||||
class="donation-address cursor-pointer hover:border-blue-500 bg-gray-50 text-gray-900 appearance-none dark:bg-gray-500 dark:text-white border border-gray-300 dark:border-gray-400 dark:text-gray-50 text-sm rounded-lg outline-none focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 focus:ring-0 font-mono"
|
||||
value="ltc1qevlumv48nz2afl0re9ml4tdewc56svxq3egkyt"
|
||||
data-address="ltc1qevlumv48nz2afl0re9ml4tdewc56svxq3egkyt">
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<!-- Litecoin MWEB -->
|
||||
<tr class="opacity-100 text-gray-500 dark:text-gray-100 hover:bg-coolGray-200 dark:hover:bg-gray-600">
|
||||
<td class="py-3 px-6 bold">
|
||||
<span class="inline-flex align-middle items-center justify-center w-9 h-10 bg-white-50 rounded">
|
||||
<img class="h-7" src="/static/images/coins/Litecoin-MWEB.png" alt="Litecoin MWEB">
|
||||
</span>
|
||||
Litecoin MWEB
|
||||
</td>
|
||||
<td class="py-3 px-6">
|
||||
<input type="text" readonly
|
||||
class="donation-address cursor-pointer hover:border-blue-500 bg-gray-50 text-gray-900 appearance-none dark:bg-gray-500 dark:text-white border border-gray-300 dark:border-gray-400 dark:text-gray-50 text-sm rounded-lg outline-none focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 focus:ring-0 font-mono"
|
||||
value="ltcmweb1qqt9rwznnxzkghv4s5wgtwxs0m0ry6n3atp95f47slppapxljde3xyqmdlnrc8ag7y2k354jzdc4pc4ks0kr43jehr77lngdecgh6689nn5mgv5yn"
|
||||
data-address="ltcmweb1qqt9rwznnxzkghv4s5wgtwxs0m0ry6n3atp95f47slppapxljde3xyqmdlnrc8ag7y2k354jzdc4pc4ks0kr43jehr77lngdecgh6689nn5mgv5yn">
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<!-- Bitcoin -->
|
||||
<tr class="opacity-100 text-gray-500 dark:text-gray-100 hover:bg-coolGray-200 dark:hover:bg-gray-600">
|
||||
<td class="py-3 px-6 bold">
|
||||
<span class="inline-flex align-middle items-center justify-center w-9 h-10 bg-white-50 rounded">
|
||||
<img class="h-7" src="/static/images/coins/Bitcoin.png" alt="Bitcoin">
|
||||
</span>
|
||||
Bitcoin (BTC)
|
||||
</td>
|
||||
<td class="py-3 px-6">
|
||||
<input type="text" readonly
|
||||
class="donation-address cursor-pointer hover:border-blue-500 bg-gray-50 text-gray-900 appearance-none dark:bg-gray-500 dark:text-white border border-gray-300 dark:border-gray-400 dark:text-gray-50 text-sm rounded-lg outline-none focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 focus:ring-0 font-mono"
|
||||
value="bc1q72j07vkn059xnmsrkk8x9up9lgvd9h9xjf8cq8"
|
||||
data-address="bc1q72j07vkn059xnmsrkk8x9up9lgvd9h9xjf8cq8">
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<!-- Particl -->
|
||||
<tr class="opacity-100 text-gray-500 dark:text-gray-100 hover:bg-coolGray-200 dark:hover:bg-gray-600">
|
||||
<td class="py-3 px-6 bold">
|
||||
<span class="inline-flex align-middle items-center justify-center w-9 h-10 bg-white-50 rounded">
|
||||
<img class="h-7" src="/static/images/coins/Particl.png" alt="Particl">
|
||||
</span>
|
||||
Particl (PART)
|
||||
</td>
|
||||
<td class="py-3 px-6">
|
||||
<input type="text" readonly
|
||||
class="donation-address cursor-pointer hover:border-blue-500 bg-gray-50 text-gray-900 appearance-none dark:bg-gray-500 dark:text-white border border-gray-300 dark:border-gray-400 dark:text-gray-50 text-sm rounded-lg outline-none focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 focus:ring-0 font-mono"
|
||||
value="pw1qf59ef0zjdckldjs8smfhv4j04gsjv302w7pdpz"
|
||||
data-address="pw1qf59ef0zjdckldjs8smfhv4j04gsjv302w7pdpz">
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="text-center mt-6">
|
||||
<p class="text-lg text-coolGray-500 dark:text-gray-300">
|
||||
Every contribution helps make decentralized trading more accessible to everyone.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
setupDonationAddressCopy();
|
||||
});
|
||||
|
||||
function setupDonationAddressCopy() {
|
||||
const donationAddresses = document.querySelectorAll('.donation-address');
|
||||
|
||||
donationAddresses.forEach(element => {
|
||||
element.addEventListener('click', function(e) {
|
||||
const address = this.value;
|
||||
|
||||
copyToClipboard(address);
|
||||
|
||||
this.select();
|
||||
this.setSelectionRange(0, 99999);
|
||||
|
||||
this.classList.add('bg-blue-50', 'dark:bg-blue-900');
|
||||
|
||||
showCopyFeedback(this);
|
||||
|
||||
setTimeout(() => {
|
||||
this.classList.remove('bg-blue-50', 'dark:bg-blue-900');
|
||||
this.blur(); // Remove focus
|
||||
}, 1000);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
let activeTooltip = null;
|
||||
|
||||
function showCopyFeedback(element) {
|
||||
if (activeTooltip && activeTooltip.parentNode) {
|
||||
activeTooltip.parentNode.removeChild(activeTooltip);
|
||||
}
|
||||
|
||||
const popup = document.createElement('div');
|
||||
popup.className = 'copy-feedback-popup fixed z-50 bg-blue-600 text-white text-sm py-2 px-3 rounded-md shadow-lg';
|
||||
popup.innerText = 'Address copied!';
|
||||
document.body.appendChild(popup);
|
||||
|
||||
activeTooltip = popup;
|
||||
|
||||
updateTooltipPosition(popup, element);
|
||||
|
||||
const scrollHandler = () => {
|
||||
if (popup.parentNode) {
|
||||
updateTooltipPosition(popup, element);
|
||||
}
|
||||
};
|
||||
|
||||
window.addEventListener('scroll', scrollHandler, { passive: true });
|
||||
|
||||
popup.style.opacity = '0';
|
||||
popup.style.transition = 'opacity 0.2s ease-in-out';
|
||||
|
||||
setTimeout(() => {
|
||||
popup.style.opacity = '1';
|
||||
}, 10);
|
||||
|
||||
setTimeout(() => {
|
||||
window.removeEventListener('scroll', scrollHandler);
|
||||
popup.style.opacity = '0';
|
||||
|
||||
setTimeout(() => {
|
||||
if (popup.parentNode) {
|
||||
popup.parentNode.removeChild(popup);
|
||||
}
|
||||
if (activeTooltip === popup) {
|
||||
activeTooltip = null;
|
||||
}
|
||||
}, 200);
|
||||
}, 1500);
|
||||
}
|
||||
|
||||
function updateTooltipPosition(tooltip, element) {
|
||||
const rect = element.getBoundingClientRect();
|
||||
|
||||
let top = rect.top - tooltip.offsetHeight - 8;
|
||||
const left = rect.left + rect.width / 2;
|
||||
|
||||
if (top < 10) {
|
||||
top = rect.bottom + 8;
|
||||
}
|
||||
|
||||
tooltip.style.top = `${top}px`;
|
||||
tooltip.style.left = `${left}px`;
|
||||
tooltip.style.transform = 'translateX(-50%)';
|
||||
}
|
||||
|
||||
function copyToClipboard(text) {
|
||||
if (navigator.clipboard && navigator.clipboard.writeText) {
|
||||
navigator.clipboard.writeText(text).catch(err => {
|
||||
console.error('Failed to copy: ', err);
|
||||
copyToClipboardFallback(text);
|
||||
});
|
||||
} else {
|
||||
copyToClipboardFallback(text);
|
||||
}
|
||||
}
|
||||
|
||||
function copyToClipboardFallback(text) {
|
||||
const textArea = document.createElement('textarea');
|
||||
textArea.value = text;
|
||||
|
||||
textArea.style.position = 'fixed';
|
||||
textArea.style.left = '-999999px';
|
||||
textArea.style.top = '-999999px';
|
||||
|
||||
document.body.appendChild(textArea);
|
||||
textArea.focus();
|
||||
textArea.select();
|
||||
|
||||
try {
|
||||
document.execCommand('copy');
|
||||
} catch (err) {
|
||||
console.error('Failed to copy text: ', err);
|
||||
}
|
||||
|
||||
document.body.removeChild(textArea);
|
||||
}
|
||||
</script>
|
||||
|
||||
{% include 'footer.html' %}
|
||||
@@ -8,6 +8,7 @@
|
||||
<img src="/static/images/logos/basicswap-logo-dark.svg" class="h-8 imageshow light-image">
|
||||
</a>
|
||||
<div class="mb-12 md:mb-0 flex flex-wrap -mx-3 md:-mx-6">
|
||||
<div class="w-full md:w-auto p-3 md:py-0 md:px-6"><a class="inline-block text-coolGray-500 dark:text-gray-300 font-medium" href="/donation">Donate</a></div>
|
||||
<div class="w-full md:w-auto p-3 md:py-0 md:px-6"><a class="inline-block text-coolGray-500 dark:text-gray-300 font-medium" href="https://academy.particl.io/en/latest/basicswap-dex/basicswap_explained.html" target="_blank">BasicSwap Explained</a></div>
|
||||
<div class="w-full md:w-auto p-3 md:py-0 md:px-6"><a class="inline-block text-coolGray-500 dark:text-gray-300 font-medium" href="https://academy.particl.io/en/latest/basicswap-guides/basicswapguides_installation.html" target="_blank">Tutorials and Guides</a></div>
|
||||
<div class="w-full md:w-auto p-3 md:py-0 md:px-6"><a class="inline-block text-coolGray-500 dark:text-gray-300 font-medium" href="https://academy.particl.io/en/latest/faq/get_support.html" target="_blank">Get Support</a></div>
|
||||
@@ -25,7 +26,7 @@
|
||||
<div class="flex items-center">
|
||||
<p class="text-sm text-gray-90 dark:text-white font-medium">© 2025~ (BSX) BasicSwap</p> <span class="w-1 h-1 mx-1.5 bg-gray-500 dark:bg-white rounded-full"></span>
|
||||
<p class="text-sm text-coolGray-400 font-medium">BSX: v{{ version }}</p> <span class="w-1 h-1 mx-1.5 bg-gray-500 dark:bg-white rounded-full"></span>
|
||||
<p class="text-sm text-coolGray-400 font-medium">GUI: v3.2.1</p> <span class="w-1 h-1 mx-1.5 bg-gray-500 dark:bg-white rounded-full"></span>
|
||||
<p class="text-sm text-coolGray-400 font-medium">GUI: v3.2.2</p> <span class="w-1 h-1 mx-1.5 bg-gray-500 dark:bg-white rounded-full"></span>
|
||||
<p class="mr-2 text-sm font-bold dark:text-white text-gray-90 ">Made with </p>
|
||||
{{ love_svg | safe }}
|
||||
</div>
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
<!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 %}
|
||||
{% 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, mobile_love_svg, amm_active_svg, amm_inactive_svg %}
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
@@ -47,7 +46,7 @@
|
||||
})();
|
||||
|
||||
(function() {
|
||||
const isDarkMode = localStorage.getItem('color-theme') === 'dark' ||
|
||||
const isDarkMode = localStorage.getItem('color-theme') === 'dark' ||
|
||||
(!localStorage.getItem('color-theme') && window.matchMedia('(prefers-color-scheme: dark)').matches);
|
||||
|
||||
if (!localStorage.getItem('color-theme')) {
|
||||
@@ -78,6 +77,7 @@
|
||||
<script src="/static/js/modules/notification-manager.js"></script>
|
||||
<script src="/static/js/modules/identity-manager.js"></script>
|
||||
<script src="/static/js/modules/summary-manager.js"></script>
|
||||
<script src="/static/js/amm_counter.js"></script>
|
||||
{% if current_page == 'wallets' or current_page == 'wallet' %}
|
||||
<script src="/static/js/modules/wallet-manager.js"></script>
|
||||
{% endif %}
|
||||
@@ -93,9 +93,9 @@
|
||||
<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"
|
||||
<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"
|
||||
<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">
|
||||
@@ -107,14 +107,14 @@
|
||||
<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
|
||||
<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
|
||||
<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>
|
||||
@@ -136,7 +136,7 @@
|
||||
<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"
|
||||
<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>
|
||||
@@ -145,11 +145,11 @@
|
||||
|
||||
<!-- Network Order Book -->
|
||||
<li>
|
||||
<a class="flex mr-10 items-center py-2.5 text-gray-50 hover:text-gray-100 text-sm"
|
||||
<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
|
||||
<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>
|
||||
@@ -158,8 +158,8 @@
|
||||
|
||||
<!-- 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
|
||||
<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>
|
||||
@@ -167,10 +167,20 @@
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<!-- Settings Dropdown -->
|
||||
<!-- Donation Link -->
|
||||
<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
|
||||
<li>
|
||||
<a href="/donation" class="flex items-center py-2 pr-4 pl-3 text-gray-50 text-sm hover:text-gray-100">
|
||||
{{ love_svg | safe }}
|
||||
<span class="ml-2">Donate</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<!-- Settings Dropdown -->
|
||||
<ul class="hidden xl:flex lg:justify-end lg:items-center lg:space-x-6">
|
||||
<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
|
||||
@@ -179,12 +189,12 @@
|
||||
</ul>
|
||||
|
||||
<!-- Settings Menu -->
|
||||
<div id="dropdownNavbar" class="hidden z-50 w-50 font-normal bg-white shadow divide-y
|
||||
<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
|
||||
<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 }}
|
||||
@@ -192,7 +202,7 @@
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/changepassword" class="flex items-center block py-4 px-4 hover:bg-gray-100
|
||||
<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 }}
|
||||
@@ -201,7 +211,7 @@
|
||||
</li>
|
||||
{% if debug_mode == true %}
|
||||
<li>
|
||||
<a href="/rpc" class="flex items-center block py-4 px-4 hover:bg-gray-100
|
||||
<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 }}
|
||||
@@ -209,7 +219,7 @@
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/debug" class="flex items-center block py-4 px-4 hover:bg-gray-100
|
||||
<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 }}
|
||||
@@ -217,7 +227,7 @@
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/explorers" class="flex items-center block py-4 px-4 hover:bg-gray-100
|
||||
<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 }}
|
||||
@@ -227,7 +237,7 @@
|
||||
{% endif %}
|
||||
{% if use_tor_proxy == true %}
|
||||
<li>
|
||||
<a href="/tor" class="flex items-center block py-4 px-4 hover:bg-gray-100
|
||||
<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 }}
|
||||
@@ -236,7 +246,7 @@
|
||||
</li>
|
||||
{% endif %}
|
||||
<li>
|
||||
<a href="/smsgaddresses" class="flex items-center block py-4 px-4 hover:bg-gray-100
|
||||
<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 }}
|
||||
@@ -244,21 +254,22 @@
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/watched" class="flex items-center block py-4 px-4 hover:bg-gray-100 dark:hover:bg-gray-700
|
||||
<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
|
||||
<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
|
||||
<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 }}
|
||||
@@ -268,8 +279,8 @@
|
||||
{% 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
|
||||
<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>
|
||||
@@ -285,12 +296,12 @@
|
||||
{% if debug_mode == true %}
|
||||
<ul class="xl:flex">
|
||||
<li>
|
||||
<div data-tooltip-target="tooltip-DEV" class="ml-5 flex items-center text-gray-50
|
||||
<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
|
||||
<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 %}
|
||||
@@ -301,28 +312,60 @@
|
||||
</ul>
|
||||
{% endif %}
|
||||
|
||||
<!-- AMM Status -->
|
||||
<ul class="xl:flex">
|
||||
<li>
|
||||
{% if current_status == 'running' %}
|
||||
<a href='/amm'>
|
||||
<div data-tooltip-target="tooltip-amm-active" class="ml-5 flex items-center text-gray-50
|
||||
hover:text-gray-100 text-sm">
|
||||
{{ amm_active_svg | safe }}
|
||||
</div>
|
||||
<div id="tooltip-amm-active" 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>AMM:</b> Active</p>
|
||||
<p><b>Currently offers/bids:</b> {{ amm_active_count }}</p>
|
||||
</div>
|
||||
</a>
|
||||
{% else %}
|
||||
<a href='/amm'>
|
||||
<div data-tooltip-target="tooltip-amm-inactive" class="ml-5 flex items-center
|
||||
text-gray-50 hover:text-gray-100 text-sm">
|
||||
{{ amm_inactive_svg | safe }}
|
||||
</div>
|
||||
<div id="tooltip-amm-inactive" 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>AMM:</b> Inactive</p>
|
||||
</div>
|
||||
</a>
|
||||
{% endif %}
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<!-- 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
|
||||
<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
|
||||
<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
|
||||
<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
|
||||
<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>
|
||||
@@ -337,12 +380,12 @@
|
||||
<ul class="xl:flex ml-5">
|
||||
<li>
|
||||
<a href="/tor">
|
||||
<div data-tooltip-target="tooltip-tor" class="flex items-center text-gray-50
|
||||
<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
|
||||
<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 %}
|
||||
@@ -355,12 +398,12 @@
|
||||
{% endif %}
|
||||
|
||||
<!-- Theme Toggle -->
|
||||
<button data-tooltip-target="tooltip-darkmode" id="theme-toggle" type="button"
|
||||
<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
|
||||
<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>
|
||||
@@ -376,24 +419,48 @@
|
||||
</div>
|
||||
|
||||
<!-- Secondary Navigation Bar -->
|
||||
<div class="hidden xl:block py-5 px-6 bg-coolGray-100 border-gray-100 dark:border-gray-500
|
||||
<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">
|
||||
<!-- AMM -->
|
||||
<li>
|
||||
<a data-tooltip-target="tooltip-amm-subheader" class="flex items-center text-sm text-gray-400
|
||||
hover:text-gray-600 dark:text-gray-100 dark:hover:text-gray-100" href="/amm">
|
||||
{{ automation_svg | safe }}
|
||||
<span>AMM</span>
|
||||
<span id="amm-counter" class="inline-flex justify-center items-center text-xs
|
||||
font-semibold ml-3 px-2.5 py-1 text-white {% if current_status == 'running' and amm_active_count > 0 %}
|
||||
bg-blue-500{% else %}bg-gray-400{% endif %} rounded-full">
|
||||
{{ amm_active_count }}
|
||||
</span>
|
||||
</a>
|
||||
<div id="tooltip-amm-subheader" 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>Status:</b> {% if current_status == 'running' %}Active{% else %}Inactive{% endif %}</p>
|
||||
<p><b>Currently active offers/bids:</b> {{ amm_active_count }}</p>
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<span class="text-gray-300">|</span>
|
||||
</li>
|
||||
|
||||
<!-- Your Offers -->
|
||||
<li>
|
||||
<a data-tooltip-target="tooltip-your-offers" class="flex items-center text-sm text-gray-400
|
||||
<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
|
||||
<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
|
||||
<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>
|
||||
@@ -406,11 +473,11 @@
|
||||
|
||||
<!-- Bid Requests -->
|
||||
<li>
|
||||
<a class="flex items-center text-sm text-gray-400 hover:text-gray-600 dark:text-gray-100
|
||||
<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
|
||||
<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 }}
|
||||
@@ -453,9 +520,9 @@
|
||||
|
||||
<!-- Swaps in Progress -->
|
||||
<li>
|
||||
<a class="flex items-center text-sm text-gray-400 hover:text-gray-600 dark:text-gray-100
|
||||
<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"
|
||||
<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 }}
|
||||
@@ -464,7 +531,7 @@
|
||||
{% endif %}
|
||||
</div>
|
||||
<span>Swaps in Progress</span>
|
||||
<span id="swaps-counter" class="inline-flex justify-center items-center text-xs
|
||||
<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 }}
|
||||
@@ -492,52 +559,72 @@
|
||||
<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
|
||||
<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"
|
||||
<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"
|
||||
<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>
|
||||
<li>
|
||||
<a class="flex items-center pl-3 py-3 pr-4 text-gray-50 hover:bg-gray-900 rounded"
|
||||
href="/donation">
|
||||
{{ mobile_love_svg | safe }}
|
||||
<span>Donate</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"
|
||||
<a class="flex items-center pl-3 py-3 pr-4 text-gray-50 hover:bg-gray-900 rounded"
|
||||
href="/amm">
|
||||
{{ automation_svg | safe }}
|
||||
<span>AMM</span>
|
||||
<span id="amm-counter-mobile" class="inline-flex justify-center items-center text-xs font-semibold
|
||||
ml-auto px-2.5 py-1 text-white {% if current_status == 'running' and amm_active_count > 0 %}
|
||||
bg-blue-500{% else %}bg-gray-400{% endif %} rounded-full">
|
||||
{{ amm_active_count }}
|
||||
</span>
|
||||
</a>
|
||||
</li>
|
||||
<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 %}
|
||||
<span id="offers-counter-mobile" 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"
|
||||
<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
|
||||
<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>
|
||||
@@ -548,16 +635,16 @@
|
||||
{{ bids_received_svg | safe }}
|
||||
<span class="my-auto">Bids</span>
|
||||
<div class="flex items-center ml-auto my-auto">
|
||||
<span id="sent-bids-counter" class="inline-flex items-center text-xs font-semibold px-2.5
|
||||
<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 bids-tab-link cursor-pointer"
|
||||
bg-blue-500{% else %}bg-gray-400{% endif %} rounded-full mr-2 bids-tab-link cursor-pointer"
|
||||
data-tab-target="#sent">
|
||||
<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
|
||||
<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 bids-tab-link cursor-pointer"
|
||||
data-tab-target="#received">
|
||||
@@ -568,9 +655,10 @@
|
||||
</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"
|
||||
<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 %}
|
||||
@@ -580,7 +668,7 @@
|
||||
{% endif %}
|
||||
</div>
|
||||
<span>Swaps in Progress</span>
|
||||
<span id="swaps-counter" class="inline-flex justify-center items-center text-xs font-semibold
|
||||
<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 }}
|
||||
@@ -591,8 +679,8 @@
|
||||
<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
|
||||
<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>
|
||||
@@ -604,14 +692,14 @@
|
||||
<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"
|
||||
<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"
|
||||
<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>
|
||||
@@ -631,7 +719,7 @@
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a class="flex items-center pl-3 py-3 pr-4 text-gray-50 hover:bg-gray-900 rounded"
|
||||
<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>
|
||||
@@ -639,15 +727,16 @@
|
||||
</li>
|
||||
{% endif %}
|
||||
<li>
|
||||
<a class="flex items-center pl-3 py-3 pr-4 text-gray-50 hover:bg-gray-900 rounded"
|
||||
<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"
|
||||
<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>
|
||||
@@ -666,7 +755,7 @@
|
||||
|
||||
<!-- Shutdown Button -->
|
||||
<div class="pt-8">
|
||||
<a href="/shutdown/{{ shutdown_token }}" class="shutdown-button flex items-center pl-3 py-3 pr-4
|
||||
<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>
|
||||
|
||||
@@ -14,10 +14,10 @@
|
||||
<li class="font-semibold text-sm text-green-500 error_msg"><span class="bold">INFO:</span></li>
|
||||
<li class="font-medium text-sm text-green-500 infomsg">{{ m[1] }}</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="w-auto p-2">
|
||||
<button type="button" class="ms-auto bg-green-50 text-green-500 rounded-lg focus:ring-0 focus:ring-green-400 p-1.5 hover:bg-green-200 inline-flex items-center justify-center h-8 w-8 focus:outline-none dark:bg-gray-800 dark:text-green-400 dark:hover:bg-gray-700" data-dismiss-target="#messages_{{ m[0] }}" aria-label="Close"><span class="sr-only">Close</span>
|
||||
<button type="button" class="ms-auto bg-green-50 text-green-500 rounded-lg focus:ring-0 focus:ring-green-400 p-1.5 hover:bg-green-200 inline-flex items-center justify-center h-8 w-8 focus:outline-none dark:bg-gray-800 dark:text-green-400 dark:hover:bg-gray-700" onclick="document.getElementById('messages_{{ m[0] }}').style.display='none';" aria-label="Close"><span class="sr-only">Close</span>
|
||||
{{ green_cross_close_svg | safe }}
|
||||
</button>
|
||||
</div>
|
||||
@@ -47,7 +47,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="w-auto p-2">
|
||||
<button type="button" class="ml-auto bg-red-100 text-red-500 rounded-lg focus:ring-0 focus:ring-red-400 p-1.5 hover:bg-red-200 inline-flex h-8 w-8 focus:outline-none inline-flex items-center justify-center h-8 w-8 dark:bg-gray-800 dark:text-red-400 dark:hover:bg-gray-700" data-dismiss-target="#err_messages_{{ err_messages[0][0] }}" aria-label="Close">
|
||||
<button type="button" class="ml-auto bg-red-100 text-red-500 rounded-lg focus:ring-0 focus:ring-red-400 p-1.5 hover:bg-red-200 inline-flex h-8 w-8 focus:outline-none inline-flex items-center justify-center h-8 w-8 dark:bg-gray-800 dark:text-red-400 dark:hover:bg-gray-700" onclick="document.getElementById('err_messages_{{ err_messages[0][0] }}').style.display='none';" aria-label="Close">
|
||||
<span class="sr-only">Close</span>
|
||||
{{ red_cross_close_svg | safe }}
|
||||
</button>
|
||||
|
||||
@@ -471,14 +471,48 @@
|
||||
<svg class="text-gray-500 w-5 h-5 mr-2" xmlns="http://www.w3.org/2000/svg" height="20" width="20" viewBox="0 0 24 24">
|
||||
<g stroke-linecap="round" stroke-width="2" fill="none" stroke="#ffffff" stroke-linejoin="round">
|
||||
<line data-cap="butt" x1="5" y1="1" x2="5" y2="6" stroke="#ffffff"></line>
|
||||
<line x1="3" y1="1" x2="7" y2="1" stroke="#fffffwww"></line>
|
||||
<line x1="3" y1="1" x2="7" y2="1" stroke="#ffffff"></line>
|
||||
<line data-cap="butt" x1="19" y1="1" x2="19" y2="6" stroke="#ffffff"></line>
|
||||
<line x1="17" y1="1" x2="21" y2="1" stroke="#ffffff"></line>
|
||||
<rect x="6" y="15" width="12" height="4" stroke="#ffffff"></rect>
|
||||
<line data-cap="butt" x1="10" y1="19" x2="10" y2="15" stroke="#ffffff"></line>
|
||||
<line data-cap="butt" x1="14" y1="19" x2="14" y2="15" stroke="#ffffff"></line>
|
||||
<line x1="6" y1="11" x2="8" y2="11" stroke="#ffffff"></line>
|
||||
<line x1="16" y1="11" x2="18" y2="11" stroke="#fff"></line>
|
||||
<line x1="16" y1="11" x2="18" y2="11" stroke="#ffffff"></line>
|
||||
<polygon points="23 6 5 6 1 6 1 23 23 23 23 6"></polygon>
|
||||
</g>
|
||||
</svg>
|
||||
' %}
|
||||
|
||||
{% set amm_active_svg = '
|
||||
<svg class="text-gray-500 w-5 h-5 mr-3" xmlns="http://www.w3.org/2000/svg" height="20" width="20" viewBox="0 0 24 24">
|
||||
<g stroke-linecap="round" stroke-width="2" fill="none" stroke="#2ad167" stroke-linejoin="round">
|
||||
<line data-cap="butt" x1="5" y1="1" x2="5" y2="6" stroke="#2ad167"></line>
|
||||
<line x1="3" y1="1" x2="7" y2="1" stroke="#2ad167"></line>
|
||||
<line data-cap="butt" x1="19" y1="1" x2="19" y2="6" stroke="#2ad167"></line>
|
||||
<line x1="17" y1="1" x2="21" y2="1" stroke="#2ad167"></line>
|
||||
<rect x="6" y="15" width="12" height="4" stroke="#2ad167"></rect>
|
||||
<line data-cap="butt" x1="10" y1="19" x2="10" y2="15" stroke="#2ad167"></line>
|
||||
<line data-cap="butt" x1="14" y1="19" x2="14" y2="15" stroke="#2ad167"></line>
|
||||
<line x1="6" y1="11" x2="8" y2="11" stroke="#2ad167"></line>
|
||||
<line x1="16" y1="11" x2="18" y2="11" stroke="#2ad167"></line>
|
||||
<polygon points="23 6 5 6 1 6 1 23 23 23 23 6"></polygon>
|
||||
</g>
|
||||
</svg>
|
||||
' %}
|
||||
|
||||
{% set amm_inactive_svg = '
|
||||
<svg class="text-gray-500 w-5 h-5 mr-3" xmlns="http://www.w3.org/2000/svg" height="20" width="20" viewBox="0 0 24 24">
|
||||
<g stroke-linecap="round" stroke-width="2" fill="none" stroke="#f80b0b" stroke-linejoin="round">
|
||||
<line data-cap="butt" x1="5" y1="1" x2="5" y2="6" stroke="#f80b0b"></line>
|
||||
<line x1="3" y1="1" x2="7" y2="1" stroke="#f80b0b"></line>
|
||||
<line data-cap="butt" x1="19" y1="1" x2="19" y2="6" stroke="#f80b0b"></line>
|
||||
<line x1="17" y1="1" x2="21" y2="1" stroke="#f80b0b"></line>
|
||||
<rect x="6" y="15" width="12" height="4" stroke="#f80b0b"></rect>
|
||||
<line data-cap="butt" x1="10" y1="19" x2="10" y2="15" stroke="#f80b0b"></line>
|
||||
<line data-cap="butt" x1="14" y1="19" x2="14" y2="15" stroke="#f80b0b"></line>
|
||||
<line x1="6" y1="11" x2="8" y2="11" stroke="#f80b0b"></line>
|
||||
<line x1="16" y1="11" x2="18" y2="11" stroke="#f80b0b"></line>
|
||||
<polygon points="23 6 5 6 1 6 1 23 23 23 23 6"></polygon>
|
||||
</g>
|
||||
</svg>
|
||||
@@ -502,6 +536,20 @@
|
||||
</g>
|
||||
</svg>
|
||||
' %}
|
||||
{% set mobile_love_svg = '
|
||||
<svg class="w-5 h-5 mr-3" xmlns="http://www.w3.org/2000/svg" height="20" width="20" viewBox="0 0 24 24">
|
||||
<g stroke-linecap="round" stroke-width="2" fill="none" stroke="#f80b0b" stroke-linejoin="round">
|
||||
<path d="M21.243,3.757 c-2.343-2.343-6.142-2.343-8.485,0c-0.289,0.289-0.54,0.6-0.757,0.927c-0.217-0.327-0.469-0.639-0.757-0.927 c-2.343-2.343-6.142-2.343-8.485,0c-2.343,2.343-2.343,6.142,0,8.485L12,21.485l9.243-9.243C23.586,9.899,23.586,6.1,21.243,3.757z"></path>
|
||||
</g>
|
||||
</svg>
|
||||
' %}
|
||||
{% set donation_svg = '
|
||||
<svg class="text-gray-500 w-5 h-5 mr-3" xmlns="http://www.w3.org/2000/svg" height="20" width="20" viewBox="0 0 24 24">
|
||||
<g stroke-linecap="round" stroke-width="2" fill="none" stroke="#6b7280" stroke-linejoin="round">
|
||||
<path d="M21.243,3.757 c-2.343-2.343-6.142-2.343-8.485,0c-0.289,0.289-0.54,0.6-0.757,0.927c-0.217-0.327-0.469-0.639-0.757-0.927 c-2.343-2.343-6.142-2.343-8.485,0c-2.343,2.343-2.343,6.142,0,8.485L12,21.485l9.243-9.243C23.586,9.899,23.586,6.1,21.243,3.757z"></path>
|
||||
</g>
|
||||
</svg>
|
||||
' %}
|
||||
{% set github_svg = '
|
||||
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M9 0C4.0275 0 0 4.13211 0 9.22838C0 13.3065 2.5785 16.7648 6.15375 17.9841C6.60375 18.0709 6.76875 17.7853 6.76875 17.5403C6.76875 17.3212 6.76125 16.7405 6.7575 15.9712C4.254 16.5277 3.726 14.7332 3.726 14.7332C3.3165 13.6681 2.72475 13.3832 2.72475 13.3832C1.9095 12.8111 2.78775 12.8229 2.78775 12.8229C3.6915 12.887 4.16625 13.7737 4.16625 13.7737C4.96875 15.1847 6.273 14.777 6.7875 14.5414C6.8685 13.9443 7.10025 13.5381 7.3575 13.3073C5.35875 13.0764 3.258 12.2829 3.258 8.74709C3.258 7.73988 3.60675 6.91659 4.18425 6.27095C4.083 6.03774 3.77925 5.0994 4.263 3.82846C4.263 3.82846 5.01675 3.58116 6.738 4.77462C7.458 4.56958 8.223 4.46785 8.988 4.46315C9.753 4.46785 10.518 4.56958 11.238 4.77462C12.948 3.58116 13.7017 3.82846 13.7017 3.82846C14.1855 5.0994 13.8818 6.03774 13.7917 6.27095C14.3655 6.91659 14.7142 7.73988 14.7142 8.74709C14.7142 12.2923 12.6105 13.0725 10.608 13.2995C10.923 13.5765 11.2155 14.1423 11.2155 15.0071C11.2155 16.242 11.2043 17.2344 11.2043 17.5341C11.2043 17.7759 11.3617 18.0647 11.823 17.9723C15.4237 16.7609 18 13.3002 18 9.22838C18 4.13211 13.9703 0 9 0Z" fill="currentColor"></path>
|
||||
@@ -607,7 +655,7 @@
|
||||
<line data-cap="butt" data-color="color-2" fill="none" stroke="#3b82f6" stroke-width="2" x1="7" y1="17" x2="22" y2="17" stroke-linecap="butt"></line>
|
||||
<polyline data-color="color-2" fill="none" stroke="#3b82f6" stroke-width="2" points=" 18,21 22,17 18,13 "></polyline>
|
||||
<line data-cap="butt" fill="none" stroke="#3b82f6" stroke-width="2" x1="18" y1="7" x2="2" y2="7" stroke-linecap="butt"></line>
|
||||
<polyline fill="none" stroke="#3b82f6" stroke-width="2" points="6,11 2,7 6,3 ">
|
||||
<polyline fill="none" stroke="#3b82f6" stroke-width="2" points="6,11 2,7 6,3 ">
|
||||
</polyline>
|
||||
</g>
|
||||
</svg>
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
{% endif %}
|
||||
<title>(BSX) BasicSwap - v{{ version }}</title>
|
||||
<link rel="icon" sizes="32x32" type="image/png" href="/static/images/favicon/favicon-32.png">
|
||||
<!-- CSS Stylesheets -->>
|
||||
<!-- CSS Stylesheets -->
|
||||
<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">
|
||||
<!-- Custom styles -->
|
||||
@@ -37,7 +37,7 @@
|
||||
})();
|
||||
|
||||
(function() {
|
||||
const isDarkMode = localStorage.getItem('color-theme') === 'dark' ||
|
||||
const isDarkMode = localStorage.getItem('color-theme') === 'dark' ||
|
||||
(!localStorage.getItem('color-theme') && window.matchMedia('(prefers-color-scheme: dark)').matches);
|
||||
|
||||
if (!localStorage.getItem('color-theme')) {
|
||||
@@ -77,9 +77,9 @@
|
||||
</head>
|
||||
<script>
|
||||
(function() {
|
||||
const isDarkMode = localStorage.getItem('color-theme') === 'dark' ||
|
||||
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');
|
||||
}
|
||||
@@ -169,7 +169,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const passwordToggle = document.querySelector('.js-password-toggle');
|
||||
|
||||
1405
basicswap/ui/page_amm.py
Normal file
1405
basicswap/ui/page_amm.py
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user