mirror of
https://github.com/basicswap/basicswap.git
synced 2025-11-05 18:38:09 +01:00
Compare commits
50 Commits
v0.14.3
...
release-v0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c945e267e7 | ||
|
|
ef65420978 | ||
|
|
6da4bf6aaf | ||
|
|
6e56b7f421 | ||
|
|
f084c6f538 | ||
|
|
443bd6917f | ||
|
|
b55d126a0a | ||
|
|
586ff3288f | ||
|
|
0398fce5a8 | ||
|
|
ef082ff7be | ||
|
|
168284ce25 | ||
|
|
e797e23625 | ||
|
|
d3fcdc8052 | ||
|
|
2c176a8c86 | ||
|
|
e92d5560af | ||
|
|
0171ad6889 | ||
|
|
5d381d4b73 | ||
|
|
9e24d9a12a | ||
|
|
21ef6f3129 | ||
|
|
67d808cbe4 | ||
|
|
5d1bed6423 | ||
|
|
edc11b4c96 | ||
|
|
5daf591985 | ||
|
|
aee66712b8 | ||
|
|
8de365f9d3 | ||
|
|
765ef9571a | ||
|
|
c575625097 | ||
|
|
fe02441619 | ||
|
|
c992ef571a | ||
|
|
5f275132de | ||
|
|
64151f4203 | ||
|
|
734214af53 | ||
|
|
1cb8ffb632 | ||
|
|
40d06df325 | ||
|
|
62031173f5 | ||
|
|
f473d66de5 | ||
|
|
e548cf2b3b | ||
|
|
d1baf4bc10 | ||
|
|
3b8e084b2e | ||
|
|
0a697c61e8 | ||
|
|
5af59dd8da | ||
|
|
a75cd28995 | ||
|
|
f40d98ef23 | ||
|
|
b14fba0e1f | ||
|
|
4d928dc98e | ||
|
|
1845f802a2 | ||
|
|
7ec9dfa35a | ||
|
|
b70e46ffc1 | ||
|
|
07de2d61af | ||
|
|
b87e034719 |
@@ -1303,16 +1303,16 @@ class BasicSwap(BaseApp):
|
||||
legacy_root_hash = ci.getSeedHash(root_key, 20)
|
||||
self.setStringKV(key_str, legacy_root_hash.hex(), cursor)
|
||||
|
||||
def initialiseWallet(self, coin_type, raise_errors: bool = False) -> None:
|
||||
if coin_type == Coins.PART:
|
||||
def initialiseWallet(self, interface_type, raise_errors: bool = False) -> None:
|
||||
if interface_type == Coins.PART:
|
||||
return
|
||||
ci = self.ci(coin_type)
|
||||
ci = self.ci(interface_type)
|
||||
db_key_coin_name = ci.coin_name().lower()
|
||||
self.log.info("Initialising {} wallet.".format(ci.coin_name()))
|
||||
|
||||
if coin_type in (Coins.XMR, Coins.WOW):
|
||||
key_view = self.getWalletKey(coin_type, 1, for_ed25519=True)
|
||||
key_spend = self.getWalletKey(coin_type, 2, for_ed25519=True)
|
||||
if interface_type in (Coins.XMR, Coins.WOW):
|
||||
key_view = self.getWalletKey(interface_type, 1, for_ed25519=True)
|
||||
key_spend = self.getWalletKey(interface_type, 2, for_ed25519=True)
|
||||
ci.initialiseWallet(key_view, key_spend)
|
||||
root_address = ci.getAddressFromKeys(key_view, key_spend)
|
||||
|
||||
@@ -1320,7 +1320,7 @@ class BasicSwap(BaseApp):
|
||||
self.setStringKV(key_str, root_address)
|
||||
return
|
||||
|
||||
root_key = self.getWalletKey(coin_type, 1)
|
||||
root_key = self.getWalletKey(interface_type, 1)
|
||||
try:
|
||||
ci.initialiseWallet(root_key)
|
||||
except Exception as e:
|
||||
@@ -1334,13 +1334,13 @@ class BasicSwap(BaseApp):
|
||||
|
||||
try:
|
||||
cursor = self.openDB()
|
||||
self.storeSeedIDForCoin(root_key, coin_type, cursor)
|
||||
self.storeSeedIDForCoin(root_key, interface_type, cursor)
|
||||
|
||||
# Clear any saved addresses
|
||||
self.clearStringKV("receive_addr_" + db_key_coin_name, cursor)
|
||||
self.clearStringKV("stealth_addr_" + db_key_coin_name, cursor)
|
||||
|
||||
coin_id = int(coin_type)
|
||||
coin_id = int(interface_type)
|
||||
info_type = 1 # wallet
|
||||
query_str = "DELETE FROM wallets WHERE coin_id = ? AND balance_type = ?"
|
||||
cursor.execute(query_str, (coin_id, info_type))
|
||||
|
||||
@@ -84,7 +84,7 @@ NAV_VERSION_TAG = os.getenv("NAV_VERSION_TAG", "")
|
||||
DCR_VERSION = os.getenv("DCR_VERSION", "1.8.1")
|
||||
DCR_VERSION_TAG = os.getenv("DCR_VERSION_TAG", "")
|
||||
|
||||
BITCOINCASH_VERSION = os.getenv("BITCOINCASH_VERSION", "27.1.0")
|
||||
BITCOINCASH_VERSION = os.getenv("BITCOINCASH_VERSION", "28.0.1")
|
||||
BITCOINCASH_VERSION_TAG = os.getenv("BITCOINCASH_VERSION_TAG", "")
|
||||
|
||||
DOGECOIN_VERSION = os.getenv("DOGECOIN_VERSION", "23.2.1")
|
||||
@@ -268,10 +268,18 @@ TOR_PROXY_HOST = os.getenv("TOR_PROXY_HOST", "127.0.0.1")
|
||||
TOR_PROXY_PORT = int(os.getenv("TOR_PROXY_PORT", 9050))
|
||||
TOR_CONTROL_PORT = int(os.getenv("TOR_CONTROL_PORT", 9051))
|
||||
TOR_DNS_PORT = int(os.getenv("TOR_DNS_PORT", 5353))
|
||||
TOR_CONTROL_LISTEN_INTERFACE = os.getenv("TOR_CONTROL_LISTEN_INTERFACE", "127.0.0.1" if BSX_LOCAL_TOR else "0.0.0.0")
|
||||
TORRC_PROXY_HOST = os.getenv("TORRC_PROXY_HOST", "127.0.0.1" if BSX_LOCAL_TOR else "0.0.0.0")
|
||||
TORRC_CONTROL_HOST = os.getenv("TORRC_CONTROL_HOST", "127.0.0.1" if BSX_LOCAL_TOR else "0.0.0.0")
|
||||
TORRC_DNS_HOST = os.getenv("TORRC_DNS_HOST", "127.0.0.1" if BSX_LOCAL_TOR else "0.0.0.0")
|
||||
TOR_CONTROL_LISTEN_INTERFACE = os.getenv(
|
||||
"TOR_CONTROL_LISTEN_INTERFACE", "127.0.0.1" if BSX_LOCAL_TOR else "0.0.0.0"
|
||||
)
|
||||
TORRC_PROXY_HOST = os.getenv(
|
||||
"TORRC_PROXY_HOST", "127.0.0.1" if BSX_LOCAL_TOR else "0.0.0.0"
|
||||
)
|
||||
TORRC_CONTROL_HOST = os.getenv(
|
||||
"TORRC_CONTROL_HOST", "127.0.0.1" if BSX_LOCAL_TOR else "0.0.0.0"
|
||||
)
|
||||
TORRC_DNS_HOST = os.getenv(
|
||||
"TORRC_DNS_HOST", "127.0.0.1" if BSX_LOCAL_TOR else "0.0.0.0"
|
||||
)
|
||||
|
||||
TEST_TOR_PROXY = toBool(
|
||||
os.getenv("TEST_TOR_PROXY", "true")
|
||||
@@ -1593,6 +1601,9 @@ def printHelp():
|
||||
print("--withoutcoin= Do not prepare system to run daemon for coin.")
|
||||
print("--addcoin= Add coin to existing setup.")
|
||||
print("--disablecoin= Make coin inactive.")
|
||||
print(
|
||||
"--upgradecores Upgrade all coin cores present in basicswap.json. Optionally use alongside --withcoin= or --withoutcoin="
|
||||
)
|
||||
print("--preparebinonly Don't prepare settings or datadirs.")
|
||||
print("--nocores Don't download and extract any coin clients.")
|
||||
print("--usecontainers Expect each core to run in a unique container.")
|
||||
|
||||
@@ -137,7 +137,7 @@ class LTCInterfaceMWEB(LTCInterface):
|
||||
self.rpc_wallet("walletpassphrase", [password, 100000000])
|
||||
|
||||
if self.getWalletSeedID() == "Not found":
|
||||
self._sc.initialiseWallet(self.coin_type())
|
||||
self._sc.initialiseWallet(self.interface_type())
|
||||
|
||||
# Workaround to trigger mweb_spk_man->LoadMWEBKeychain()
|
||||
self.rpc("unloadwallet", ["mweb"])
|
||||
|
||||
190
basicswap/static/js/dropdown.js
Normal file
190
basicswap/static/js/dropdown.js
Normal file
@@ -0,0 +1,190 @@
|
||||
(function(window) {
|
||||
'use strict';
|
||||
|
||||
function positionElement(targetEl, triggerEl, placement = 'bottom', offsetDistance = 8) {
|
||||
targetEl.style.visibility = 'hidden';
|
||||
targetEl.style.display = 'block';
|
||||
|
||||
const triggerRect = triggerEl.getBoundingClientRect();
|
||||
const targetRect = targetEl.getBoundingClientRect();
|
||||
const scrollLeft = window.pageXOffset || document.documentElement.scrollLeft;
|
||||
const scrollTop = window.pageYOffset || document.documentElement.scrollTop;
|
||||
|
||||
let top, left;
|
||||
|
||||
top = triggerRect.bottom + offsetDistance;
|
||||
left = triggerRect.left + (triggerRect.width - targetRect.width) / 2;
|
||||
|
||||
switch (placement) {
|
||||
case 'bottom-start':
|
||||
left = triggerRect.left;
|
||||
break;
|
||||
case 'bottom-end':
|
||||
left = triggerRect.right - targetRect.width;
|
||||
break;
|
||||
}
|
||||
|
||||
const viewport = {
|
||||
width: window.innerWidth,
|
||||
height: window.innerHeight
|
||||
};
|
||||
|
||||
if (left < 10) left = 10;
|
||||
if (left + targetRect.width > viewport.width - 10) {
|
||||
left = viewport.width - targetRect.width - 10;
|
||||
}
|
||||
|
||||
targetEl.style.position = 'fixed';
|
||||
targetEl.style.top = `${Math.round(top)}px`;
|
||||
targetEl.style.left = `${Math.round(left)}px`;
|
||||
targetEl.style.margin = '0';
|
||||
targetEl.style.maxHeight = `${viewport.height - top - 10}px`;
|
||||
targetEl.style.overflow = 'auto';
|
||||
targetEl.style.visibility = 'visible';
|
||||
}
|
||||
|
||||
class Dropdown {
|
||||
constructor(targetEl, triggerEl, options = {}) {
|
||||
this._targetEl = targetEl;
|
||||
this._triggerEl = triggerEl;
|
||||
this._options = {
|
||||
placement: options.placement || 'bottom',
|
||||
offset: options.offset || 5,
|
||||
onShow: options.onShow || function() {},
|
||||
onHide: options.onHide || function() {}
|
||||
};
|
||||
this._visible = false;
|
||||
this._initialized = false;
|
||||
this._handleScroll = this._handleScroll.bind(this);
|
||||
this._handleResize = this._handleResize.bind(this);
|
||||
this._handleOutsideClick = this._handleOutsideClick.bind(this);
|
||||
this.init();
|
||||
}
|
||||
|
||||
init() {
|
||||
if (!this._initialized) {
|
||||
this._targetEl.style.margin = '0';
|
||||
this._targetEl.style.display = 'none';
|
||||
this._targetEl.style.position = 'fixed';
|
||||
this._targetEl.style.zIndex = '50';
|
||||
|
||||
this._setupEventListeners();
|
||||
this._initialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
_setupEventListeners() {
|
||||
this._triggerEl.addEventListener('click', (e) => {
|
||||
e.stopPropagation();
|
||||
this.toggle();
|
||||
});
|
||||
|
||||
document.addEventListener('click', this._handleOutsideClick);
|
||||
document.addEventListener('keydown', (e) => {
|
||||
if (e.key === 'Escape') this.hide();
|
||||
});
|
||||
window.addEventListener('scroll', this._handleScroll, true);
|
||||
window.addEventListener('resize', this._handleResize);
|
||||
}
|
||||
|
||||
_handleScroll() {
|
||||
if (this._visible) {
|
||||
requestAnimationFrame(() => {
|
||||
positionElement(
|
||||
this._targetEl,
|
||||
this._triggerEl,
|
||||
this._options.placement,
|
||||
this._options.offset
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
_handleResize() {
|
||||
if (this._visible) {
|
||||
requestAnimationFrame(() => {
|
||||
positionElement(
|
||||
this._targetEl,
|
||||
this._triggerEl,
|
||||
this._options.placement,
|
||||
this._options.offset
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
_handleOutsideClick(e) {
|
||||
if (this._visible &&
|
||||
!this._targetEl.contains(e.target) &&
|
||||
!this._triggerEl.contains(e.target)) {
|
||||
this.hide();
|
||||
}
|
||||
}
|
||||
|
||||
show() {
|
||||
if (!this._visible) {
|
||||
this._targetEl.style.display = 'block';
|
||||
this._targetEl.style.visibility = 'hidden';
|
||||
|
||||
requestAnimationFrame(() => {
|
||||
positionElement(
|
||||
this._targetEl,
|
||||
this._triggerEl,
|
||||
this._options.placement,
|
||||
this._options.offset
|
||||
);
|
||||
|
||||
this._visible = true;
|
||||
this._options.onShow();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
hide() {
|
||||
if (this._visible) {
|
||||
this._targetEl.style.display = 'none';
|
||||
this._visible = false;
|
||||
this._options.onHide();
|
||||
}
|
||||
}
|
||||
|
||||
toggle() {
|
||||
if (this._visible) {
|
||||
this.hide();
|
||||
} else {
|
||||
this.show();
|
||||
}
|
||||
}
|
||||
|
||||
destroy() {
|
||||
document.removeEventListener('click', this._handleOutsideClick);
|
||||
window.removeEventListener('scroll', this._handleScroll, true);
|
||||
window.removeEventListener('resize', this._handleResize);
|
||||
this._initialized = false;
|
||||
}
|
||||
}
|
||||
|
||||
function initDropdowns() {
|
||||
document.querySelectorAll('[data-dropdown-toggle]').forEach(triggerEl => {
|
||||
const targetId = triggerEl.getAttribute('data-dropdown-toggle');
|
||||
const targetEl = document.getElementById(targetId);
|
||||
|
||||
if (targetEl) {
|
||||
const placement = triggerEl.getAttribute('data-dropdown-placement');
|
||||
new Dropdown(targetEl, triggerEl, {
|
||||
placement: placement || 'bottom-start'
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (document.readyState === 'loading') {
|
||||
document.addEventListener('DOMContentLoaded', initDropdowns);
|
||||
} else {
|
||||
initDropdowns();
|
||||
}
|
||||
|
||||
window.Dropdown = Dropdown;
|
||||
window.initDropdowns = initDropdowns;
|
||||
|
||||
})(window);
|
||||
File diff suppressed because one or more lines are too long
@@ -19,8 +19,8 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
const backdrop = document.querySelectorAll('.navbar-backdrop');
|
||||
|
||||
if (close.length) {
|
||||
for (var i = 0; i < close.length; i++) {
|
||||
close[i].addEventListener('click', function() {
|
||||
for (var k = 0; k < close.length; k++) {
|
||||
close[k].addEventListener('click', function() {
|
||||
for (var j = 0; j < menu.length; j++) {
|
||||
menu[j].classList.toggle('hidden');
|
||||
}
|
||||
@@ -29,8 +29,8 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
}
|
||||
|
||||
if (backdrop.length) {
|
||||
for (var i = 0; i < backdrop.length; i++) {
|
||||
backdrop[i].addEventListener('click', function() {
|
||||
for (var l = 0; l < backdrop.length; l++) {
|
||||
backdrop[l].addEventListener('click', function() {
|
||||
for (var j = 0; j < menu.length; j++) {
|
||||
menu[j].classList.toggle('hidden');
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
window.addEventListener('DOMContentLoaded', (event) => {
|
||||
let err_msgs = document.querySelectorAll('p.error_msg');
|
||||
window.addEventListener('DOMContentLoaded', () => {
|
||||
const err_msgs = document.querySelectorAll('p.error_msg');
|
||||
for (let i = 0; i < err_msgs.length; i++) {
|
||||
err_msg = err_msgs[i].innerText;
|
||||
if (err_msg.indexOf('coin_to') >= 0 || err_msg.indexOf('Coin To') >= 0) {
|
||||
@@ -29,9 +29,9 @@ window.addEventListener('DOMContentLoaded', (event) => {
|
||||
}
|
||||
|
||||
// remove error class on input or select focus
|
||||
let inputs = document.querySelectorAll('input.error');
|
||||
let selects = document.querySelectorAll('select.error');
|
||||
let elements = [...inputs, ...selects];
|
||||
const inputs = document.querySelectorAll('input.error');
|
||||
const selects = document.querySelectorAll('select.error');
|
||||
const elements = [...inputs, ...selects];
|
||||
elements.forEach((element) => {
|
||||
element.addEventListener('focus', (event) => {
|
||||
event.target.classList.remove('error');
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -28,21 +28,32 @@ const config = {
|
||||
}
|
||||
},
|
||||
showVolume: false,
|
||||
cacheTTL: 5 * 60 * 1000, // 5 minutes
|
||||
cacheTTL: 10 * 60 * 1000,
|
||||
specialCoins: [''],
|
||||
resolutions: {
|
||||
year: { days: 365, interval: 'month' },
|
||||
sixMonths: { days: 180, interval: 'daily' },
|
||||
day: { days: 1, interval: 'hourly' }
|
||||
},
|
||||
currentResolution: 'year'
|
||||
currentResolution: 'year',
|
||||
requestTimeout: 60000, // 60 sec
|
||||
retryDelays: [5000, 15000, 30000],
|
||||
rateLimits: {
|
||||
coingecko: {
|
||||
requestsPerMinute: 50,
|
||||
minInterval: 1200 // 1.2 sec
|
||||
},
|
||||
cryptocompare: {
|
||||
requestsPerMinute: 30,
|
||||
minInterval: 2000 // 2 sec
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// UTILS
|
||||
const utils = {
|
||||
formatNumber: (number, decimals = 2) =>
|
||||
number.toFixed(decimals).replace(/\B(?=(\d{3})+(?!\d))/g, ','),
|
||||
|
||||
formatDate: (timestamp, resolution) => {
|
||||
const date = new Date(timestamp);
|
||||
const options = {
|
||||
@@ -77,16 +88,19 @@ const logger = {
|
||||
error: (message) => console.error(`[AppError] ${new Date().toISOString()}: ${message}`)
|
||||
};
|
||||
|
||||
// API
|
||||
const api = {
|
||||
makePostRequest: (url, headers = {}) => {
|
||||
const apiKeys = getAPIKeys();
|
||||
return new Promise((resolve, reject) => {
|
||||
const xhr = new XMLHttpRequest();
|
||||
xhr.open('POST', '/json/readurl');
|
||||
xhr.setRequestHeader('Content-Type', 'application/json');
|
||||
xhr.timeout = 30000;
|
||||
xhr.ontimeout = () => reject(new AppError('Request timed out'));
|
||||
xhr.timeout = config.requestTimeout;
|
||||
|
||||
xhr.ontimeout = () => {
|
||||
logger.warn(`Request timed out for ${url}`);
|
||||
reject(new AppError('Request timed out'));
|
||||
};
|
||||
|
||||
xhr.onload = () => {
|
||||
logger.log(`Response for ${url}:`, xhr.responseText);
|
||||
if (xhr.status === 200) {
|
||||
@@ -107,7 +121,12 @@ const api = {
|
||||
reject(new AppError(`HTTP Error: ${xhr.status} ${xhr.statusText}`, 'HTTPError'));
|
||||
}
|
||||
};
|
||||
xhr.onerror = () => reject(new AppError('Network error occurred', 'NetworkError'));
|
||||
|
||||
xhr.onerror = () => {
|
||||
logger.error(`Network error occurred for ${url}`);
|
||||
reject(new AppError('Network error occurred', 'NetworkError'));
|
||||
};
|
||||
|
||||
xhr.send(JSON.stringify({
|
||||
url: url,
|
||||
headers: headers
|
||||
@@ -116,44 +135,81 @@ const api = {
|
||||
},
|
||||
|
||||
fetchCryptoCompareDataXHR: (coin) => {
|
||||
return rateLimiter.queueRequest('cryptocompare', async () => {
|
||||
const url = `${config.apiEndpoints.cryptoCompare}?fsyms=${coin}&tsyms=USD,BTC&api_key=${config.apiKeys.cryptoCompare}`;
|
||||
const headers = {
|
||||
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36',
|
||||
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
|
||||
'Accept-Language': 'en-US,en;q=0.5',
|
||||
};
|
||||
return api.makePostRequest(url, headers).catch(error => ({
|
||||
error: error.message
|
||||
}));
|
||||
try {
|
||||
return await api.makePostRequest(url, headers);
|
||||
} catch (error) {
|
||||
logger.error(`CryptoCompare request failed for ${coin}:`, error);
|
||||
const cachedData = cache.get(`coinData_${coin}`);
|
||||
if (cachedData) {
|
||||
logger.info(`Using cached data for ${coin}`);
|
||||
return cachedData.value;
|
||||
}
|
||||
return { error: error.message };
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
fetchCoinGeckoDataXHR: async () => {
|
||||
const cacheKey = 'coinGeckoOneLiner';
|
||||
let cachedData = cache.get(cacheKey);
|
||||
const cachedData = cache.get(cacheKey);
|
||||
|
||||
if (cachedData) {
|
||||
console.log('Using cached CoinGecko data');
|
||||
return cachedData.value;
|
||||
}
|
||||
|
||||
return rateLimiter.queueRequest('coingecko', async () => {
|
||||
try {
|
||||
const existingCache = localStorage.getItem(cacheKey);
|
||||
let fallbackData = null;
|
||||
|
||||
if (existingCache) {
|
||||
try {
|
||||
const parsed = JSON.parse(existingCache);
|
||||
fallbackData = parsed.value;
|
||||
} catch (e) {
|
||||
console.warn('Failed to parse existing cache:', e);
|
||||
}
|
||||
}
|
||||
|
||||
const coinIds = config.coins
|
||||
.filter(coin => coin.usesCoinGecko)
|
||||
.map(coin => coin.name)
|
||||
.join(',');
|
||||
|
||||
const url = `${config.apiEndpoints.coinGecko}/simple/price?ids=${coinIds}&vs_currencies=usd,btc&include_24hr_vol=true&include_24hr_change=true&api_key=${config.apiKeys.coinGecko}`;
|
||||
|
||||
//console.log(`Fetching data for multiple coins from CoinGecko: ${url}`);
|
||||
const response = await api.makePostRequest(url, {
|
||||
'User-Agent': 'Mozilla/5.0',
|
||||
'Accept': 'application/json',
|
||||
'Accept-Language': 'en-US,en;q=0.5'
|
||||
});
|
||||
|
||||
try {
|
||||
const data = await api.makePostRequest(url);
|
||||
//console.log(`Raw CoinGecko data:`, data);
|
||||
if (typeof response !== 'object' || response === null) {
|
||||
if (fallbackData) {
|
||||
console.log('Using fallback data due to invalid response');
|
||||
return fallbackData;
|
||||
}
|
||||
throw new AppError('Invalid data structure received from CoinGecko');
|
||||
}
|
||||
|
||||
if (typeof data !== 'object' || data === null) {
|
||||
throw new AppError(`Invalid data structure received from CoinGecko`);
|
||||
if (response.error || response.Error) {
|
||||
if (fallbackData) {
|
||||
console.log('Using fallback data due to API error');
|
||||
return fallbackData;
|
||||
}
|
||||
throw new AppError(response.error || response.Error);
|
||||
}
|
||||
|
||||
const transformedData = {};
|
||||
Object.entries(data).forEach(([id, values]) => {
|
||||
Object.entries(response).forEach(([id, values]) => {
|
||||
const coinConfig = config.coins.find(coin => coin.name === id);
|
||||
const symbol = coinConfig?.symbol.toLowerCase() || id;
|
||||
transformedData[symbol] = {
|
||||
@@ -165,13 +221,21 @@ const api = {
|
||||
};
|
||||
});
|
||||
|
||||
//console.log(`Transformed CoinGecko data:`, transformedData);
|
||||
cache.set(cacheKey, transformedData);
|
||||
return transformedData;
|
||||
|
||||
} catch (error) {
|
||||
//console.error(`Error fetching CoinGecko data:`, error);
|
||||
return { error: error.message };
|
||||
console.error('Error fetching CoinGecko data:', error);
|
||||
|
||||
const cachedData = cache.get(cacheKey);
|
||||
if (cachedData) {
|
||||
console.log('Using expired cache data due to error');
|
||||
return cachedData.value;
|
||||
}
|
||||
|
||||
throw error;
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
fetchHistoricalDataXHR: async (coinSymbols) => {
|
||||
@@ -179,32 +243,36 @@ const api = {
|
||||
coinSymbols = [coinSymbols];
|
||||
}
|
||||
|
||||
//console.log(`Fetching historical data for coins: ${coinSymbols.join(', ')}`);
|
||||
|
||||
const results = {};
|
||||
|
||||
const fetchPromises = coinSymbols.map(async coin => {
|
||||
const coinConfig = config.coins.find(c => c.symbol === coin);
|
||||
if (!coinConfig) {
|
||||
//console.error(`Coin configuration not found for ${coin}`);
|
||||
if (!coinConfig) return;
|
||||
|
||||
const cacheKey = `historical_${coin}_${config.currentResolution}`;
|
||||
const cachedData = cache.get(cacheKey);
|
||||
if (cachedData) {
|
||||
results[coin] = cachedData.value;
|
||||
return;
|
||||
}
|
||||
|
||||
if (coin === 'WOW') {
|
||||
return rateLimiter.queueRequest('coingecko', async () => {
|
||||
const url = `${config.apiEndpoints.coinGecko}/coins/wownero/market_chart?vs_currency=usd&days=1&api_key=${config.apiKeys.coinGecko}`;
|
||||
//console.log(`CoinGecko URL for WOW: ${url}`);
|
||||
|
||||
try {
|
||||
const response = await api.makePostRequest(url);
|
||||
if (response && response.prices) {
|
||||
results[coin] = response.prices;
|
||||
} else {
|
||||
//console.error(`Unexpected data structure for WOW:`, response);
|
||||
cache.set(cacheKey, response.prices);
|
||||
}
|
||||
} catch (error) {
|
||||
//console.error(`Error fetching CoinGecko data for WOW:`, error);
|
||||
console.error(`Error fetching CoinGecko data for WOW:`, error);
|
||||
if (cachedData) {
|
||||
results[coin] = cachedData.value;
|
||||
}
|
||||
}
|
||||
});
|
||||
} else {
|
||||
return rateLimiter.queueRequest('cryptocompare', async () => {
|
||||
const resolution = config.resolutions[config.currentResolution];
|
||||
let url;
|
||||
if (resolution.interval === 'hourly') {
|
||||
@@ -213,30 +281,116 @@ const api = {
|
||||
url = `${config.apiEndpoints.cryptoCompareHistorical}?fsym=${coin}&tsym=USD&limit=${resolution.days}&api_key=${config.apiKeys.cryptoCompare}`;
|
||||
}
|
||||
|
||||
//console.log(`CryptoCompare URL for ${coin}: ${url}`);
|
||||
|
||||
try {
|
||||
const response = await api.makePostRequest(url);
|
||||
if (response.Response === "Error") {
|
||||
//console.error(`API Error for ${coin}:`, response.Message);
|
||||
console.error(`API Error for ${coin}:`, response.Message);
|
||||
if (cachedData) {
|
||||
results[coin] = cachedData.value;
|
||||
}
|
||||
} else if (response.Data && response.Data.Data) {
|
||||
results[coin] = response.Data;
|
||||
} else {
|
||||
//console.error(`Unexpected data structure for ${coin}:`, response);
|
||||
cache.set(cacheKey, response.Data);
|
||||
}
|
||||
} catch (error) {
|
||||
//console.error(`Error fetching CryptoCompare data for ${coin}:`, error);
|
||||
console.error(`Error fetching CryptoCompare data for ${coin}:`, error);
|
||||
if (cachedData) {
|
||||
results[coin] = cachedData.value;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
await Promise.all(fetchPromises);
|
||||
|
||||
//console.log('Final results object:', JSON.stringify(results, null, 2));
|
||||
return results;
|
||||
}
|
||||
};
|
||||
|
||||
const rateLimiter = {
|
||||
lastRequestTime: {},
|
||||
minRequestInterval: {
|
||||
coingecko: config.rateLimits.coingecko.minInterval,
|
||||
cryptocompare: config.rateLimits.cryptocompare.minInterval
|
||||
},
|
||||
requestQueue: {},
|
||||
retryDelays: config.retryDelays,
|
||||
|
||||
canMakeRequest: function(apiName) {
|
||||
const now = Date.now();
|
||||
const lastRequest = this.lastRequestTime[apiName] || 0;
|
||||
return (now - lastRequest) >= this.minRequestInterval[apiName];
|
||||
},
|
||||
|
||||
updateLastRequestTime: function(apiName) {
|
||||
this.lastRequestTime[apiName] = Date.now();
|
||||
},
|
||||
|
||||
getWaitTime: function(apiName) {
|
||||
const now = Date.now();
|
||||
const lastRequest = this.lastRequestTime[apiName] || 0;
|
||||
return Math.max(0, this.minRequestInterval[apiName] - (now - lastRequest));
|
||||
},
|
||||
|
||||
queueRequest: async function(apiName, requestFn, retryCount = 0) {
|
||||
if (!this.requestQueue[apiName]) {
|
||||
this.requestQueue[apiName] = Promise.resolve();
|
||||
}
|
||||
|
||||
try {
|
||||
await this.requestQueue[apiName];
|
||||
|
||||
const executeRequest = async () => {
|
||||
const waitTime = this.getWaitTime(apiName);
|
||||
if (waitTime > 0) {
|
||||
await new Promise(resolve => setTimeout(resolve, waitTime));
|
||||
}
|
||||
|
||||
try {
|
||||
this.updateLastRequestTime(apiName);
|
||||
return await requestFn();
|
||||
} catch (error) {
|
||||
if (error.message.includes('429') && retryCount < this.retryDelays.length) {
|
||||
const delay = this.retryDelays[retryCount];
|
||||
console.log(`Rate limit hit, retrying in ${delay/1000} seconds...`);
|
||||
await new Promise(resolve => setTimeout(resolve, delay));
|
||||
return this.queueRequest(apiName, requestFn, retryCount + 1);
|
||||
}
|
||||
|
||||
if ((error.message.includes('timeout') || error.name === 'NetworkError') &&
|
||||
retryCount < this.retryDelays.length) {
|
||||
const delay = this.retryDelays[retryCount];
|
||||
logger.warn(`Request failed, retrying in ${delay/1000} seconds...`, {
|
||||
apiName,
|
||||
retryCount,
|
||||
error: error.message
|
||||
});
|
||||
await new Promise(resolve => setTimeout(resolve, delay));
|
||||
return this.queueRequest(apiName, requestFn, retryCount + 1);
|
||||
}
|
||||
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
this.requestQueue[apiName] = executeRequest();
|
||||
return await this.requestQueue[apiName];
|
||||
|
||||
} catch (error) {
|
||||
if (error.message.includes('429') ||
|
||||
error.message.includes('timeout') ||
|
||||
error.name === 'NetworkError') {
|
||||
const cachedData = cache.get(`coinData_${apiName}`);
|
||||
if (cachedData) {
|
||||
console.log('Using cached data due to request failure');
|
||||
return cachedData.value;
|
||||
}
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// CACHE
|
||||
const cache = {
|
||||
set: (key, value, customTtl = null) => {
|
||||
@@ -266,8 +420,8 @@ const cache = {
|
||||
//console.log(`Cache expired for ${key}`);
|
||||
localStorage.removeItem(key);
|
||||
}
|
||||
} catch (e) {
|
||||
//console.error('Error parsing cache item:', e);
|
||||
} catch (error) {
|
||||
console.error('Error parsing cache item:', error.message);
|
||||
localStorage.removeItem(key);
|
||||
}
|
||||
return null;
|
||||
@@ -288,7 +442,6 @@ const cache = {
|
||||
// UI
|
||||
const ui = {
|
||||
displayCoinData: (coin, data) => {
|
||||
const coinConfig = config.coins.find(c => c.symbol === coin);
|
||||
let priceUSD, priceBTC, priceChange1d, volume24h;
|
||||
const updateUI = (isError = false) => {
|
||||
const priceUsdElement = document.querySelector(`#${coin.toLowerCase()}-price-usd`);
|
||||
@@ -296,28 +449,23 @@ displayCoinData: (coin, data) => {
|
||||
const volumeElement = document.querySelector(`#${coin.toLowerCase()}-volume-24h`);
|
||||
const btcPriceDiv = document.querySelector(`#${coin.toLowerCase()}-btc-price-div`);
|
||||
const priceBtcElement = document.querySelector(`#${coin.toLowerCase()}-price-btc`);
|
||||
|
||||
if (priceUsdElement) {
|
||||
priceUsdElement.textContent = isError ? 'N/A' : `$ ${ui.formatPrice(coin, priceUSD)}`;
|
||||
}
|
||||
|
||||
if (volumeDiv && volumeElement) {
|
||||
volumeElement.textContent = isError ? 'N/A' : `${utils.formatNumber(volume24h, 0)} USD`;
|
||||
volumeDiv.style.display = volumeToggle.isVisible ? 'flex' : 'none';
|
||||
}
|
||||
|
||||
if (btcPriceDiv && priceBtcElement) {
|
||||
if (coin === 'BTC') {
|
||||
btcPriceDiv.style.display = 'none';
|
||||
} else {
|
||||
priceBtcElement.textContent = isError ? 'N/A' : `${priceBTC.toFixed(8)} BTC`;
|
||||
priceBtcElement.textContent = isError ? 'N/A' : `${priceBTC.toFixed(8)}`;
|
||||
btcPriceDiv.style.display = 'flex';
|
||||
}
|
||||
}
|
||||
|
||||
ui.updatePriceChangeContainer(coin, isError ? null : priceChange1d);
|
||||
};
|
||||
|
||||
try {
|
||||
if (data.error) {
|
||||
throw new Error(data.error);
|
||||
@@ -325,20 +473,17 @@ displayCoinData: (coin, data) => {
|
||||
if (!data || !data.current_price) {
|
||||
throw new Error(`Invalid CoinGecko data structure for ${coin}`);
|
||||
}
|
||||
|
||||
priceUSD = data.current_price;
|
||||
priceBTC = coin === 'BTC' ? 1 : data.price_btc || (data.current_price / app.btcPriceUSD);
|
||||
priceChange1d = data.price_change_percentage_24h;
|
||||
volume24h = data.total_volume;
|
||||
|
||||
if (isNaN(priceUSD) || isNaN(priceBTC) || isNaN(volume24h)) {
|
||||
throw new Error(`Invalid numeric values in data for ${coin}`);
|
||||
}
|
||||
|
||||
updateUI(false);
|
||||
} catch (error) {
|
||||
//console.error(`Error displaying data for ${coin}:`, error.message);
|
||||
updateUI(true);
|
||||
logger.error(`Failed to display data for ${coin}:`, error.message);
|
||||
updateUI(true); // Show error state in UI
|
||||
}
|
||||
},
|
||||
|
||||
@@ -382,11 +527,9 @@ displayCoinData: (coin, data) => {
|
||||
updateLoadTimeAndCache: (loadTime, cachedData) => {
|
||||
const loadTimeElement = document.getElementById('load-time');
|
||||
const cacheStatusElement = document.getElementById('cache-status');
|
||||
|
||||
if (loadTimeElement) {
|
||||
loadTimeElement.textContent = `Load time: ${loadTime}ms`;
|
||||
}
|
||||
|
||||
if (cacheStatusElement) {
|
||||
if (cachedData && cachedData.remainingTime) {
|
||||
const remainingMinutes = Math.ceil(cachedData.remainingTime / 60000);
|
||||
@@ -399,7 +542,6 @@ displayCoinData: (coin, data) => {
|
||||
cacheStatusElement.classList.remove('text-green-500');
|
||||
}
|
||||
}
|
||||
|
||||
ui.updateLastRefreshedTime();
|
||||
},
|
||||
|
||||
@@ -446,8 +588,10 @@ displayCoinData: (coin, data) => {
|
||||
if (price < 0.000001) return price.toExponential(2);
|
||||
if (price < 0.001) return price.toFixed(8);
|
||||
if (price < 1) return price.toFixed(4);
|
||||
if (price < 10) return price.toFixed(3);
|
||||
if (price < 1000) return price.toFixed(2);
|
||||
return price.toFixed(1);
|
||||
if (price < 100000) return price.toFixed(1);
|
||||
return price.toFixed(0);
|
||||
},
|
||||
|
||||
setActiveContainer: (containerId) => {
|
||||
@@ -509,16 +653,14 @@ const chartModule = {
|
||||
},
|
||||
|
||||
initChart: () => {
|
||||
const ctx = document.getElementById('coin-chart').getContext('2d');
|
||||
const ctx = document.getElementById('coin-chart')?.getContext('2d');
|
||||
if (!ctx) {
|
||||
logger.error('Failed to get chart context. Make sure the canvas element exists.');
|
||||
return;
|
||||
}
|
||||
|
||||
const gradient = ctx.createLinearGradient(0, 0, 0, 400);
|
||||
gradient.addColorStop(0, 'rgba(77, 132, 240, 0.2)');
|
||||
gradient.addColorStop(1, 'rgba(77, 132, 240, 0)');
|
||||
|
||||
chartModule.chart = new Chart(ctx, {
|
||||
type: 'line',
|
||||
data: {
|
||||
@@ -565,10 +707,11 @@ const chartModule = {
|
||||
family: "'Inter', sans-serif"
|
||||
},
|
||||
color: 'rgba(156, 163, 175, 1)',
|
||||
maxRotation: 0,
|
||||
minRotation: 0,
|
||||
callback: function(value) {
|
||||
const date = new Date(value);
|
||||
if (config.currentResolution === 'day') {
|
||||
// Convert to AM/PM format
|
||||
return date.toLocaleTimeString('en-US', {
|
||||
hour: 'numeric',
|
||||
minute: '2-digit',
|
||||
@@ -668,16 +811,11 @@ const chartModule = {
|
||||
},
|
||||
plugins: [chartModule.verticalLinePlugin]
|
||||
});
|
||||
|
||||
//console.log('Chart initialized:', chartModule.chart);
|
||||
},
|
||||
|
||||
prepareChartData: (coinSymbol, data) => {
|
||||
if (!data) {
|
||||
//console.error(`No data received for ${coinSymbol}`);
|
||||
return [];
|
||||
}
|
||||
|
||||
try {
|
||||
let preparedData;
|
||||
|
||||
@@ -686,13 +824,10 @@ const chartModule = {
|
||||
endTime.setUTCMinutes(0, 0, 0);
|
||||
const endUnix = endTime.getTime();
|
||||
const startUnix = endUnix - (24 * 3600000);
|
||||
|
||||
const hourlyPoints = [];
|
||||
|
||||
for (let hourUnix = startUnix; hourUnix <= endUnix; hourUnix += 3600000) {
|
||||
const targetHour = new Date(hourUnix);
|
||||
targetHour.setUTCMinutes(0, 0, 0);
|
||||
|
||||
const closestPoint = data.reduce((prev, curr) => {
|
||||
const prevTime = new Date(prev[0]);
|
||||
const currTime = new Date(curr[0]);
|
||||
@@ -733,16 +868,14 @@ const chartModule = {
|
||||
y: price
|
||||
}));
|
||||
} else {
|
||||
//console.error(`Unexpected data structure for ${coinSymbol}:`, data);
|
||||
return [];
|
||||
}
|
||||
|
||||
return preparedData.map(point => ({
|
||||
x: new Date(point.x).getTime(),
|
||||
y: point.y
|
||||
}));
|
||||
} catch (error) {
|
||||
//console.error(`Error preparing chart data for ${coinSymbol}:`, error);
|
||||
console.error(`Error preparing chart data for ${coinSymbol}:`, error);
|
||||
return [];
|
||||
}
|
||||
},
|
||||
@@ -759,84 +892,77 @@ const chartModule = {
|
||||
Math.abs(new Date(curr.x).getTime() - targetTime.getTime()) <
|
||||
Math.abs(new Date(prev.x).getTime() - targetTime.getTime()) ? curr : prev
|
||||
);
|
||||
|
||||
hourlyData.push({
|
||||
x: targetTime.getTime(),
|
||||
y: closestDataPoint.y
|
||||
});
|
||||
}
|
||||
|
||||
return hourlyData;
|
||||
},
|
||||
|
||||
updateChart: async (coinSymbol, forceRefresh = false) => {
|
||||
try {
|
||||
const currentChartData = chartModule.chart?.data.datasets[0].data || [];
|
||||
if (currentChartData.length === 0) {
|
||||
chartModule.showChartLoader();
|
||||
}
|
||||
chartModule.loadStartTime = Date.now();
|
||||
|
||||
const cacheKey = `chartData_${coinSymbol}_${config.currentResolution}`;
|
||||
let cachedData = !forceRefresh ? cache.get(cacheKey) : null;
|
||||
let data;
|
||||
|
||||
if (cachedData && Object.keys(cachedData.value).length > 0) {
|
||||
data = cachedData.value;
|
||||
//console.log(`Using cached data for ${coinSymbol} (${config.currentResolution})`);
|
||||
console.log(`Using cached data for ${coinSymbol}`);
|
||||
} else {
|
||||
//console.log(`Fetching fresh data for ${coinSymbol} (${config.currentResolution})`);
|
||||
try {
|
||||
const allData = await api.fetchHistoricalDataXHR([coinSymbol]);
|
||||
data = allData[coinSymbol];
|
||||
if (!data || Object.keys(data).length === 0) {
|
||||
throw new Error(`No data returned for ${coinSymbol}`);
|
||||
}
|
||||
//console.log(`Caching new data for ${cacheKey}`);
|
||||
cache.set(cacheKey, data, config.cacheTTL);
|
||||
cachedData = null;
|
||||
} catch (error) {
|
||||
if (error.message.includes('429') && currentChartData.length > 0) {
|
||||
console.warn(`Rate limit hit for ${coinSymbol}, maintaining current chart`);
|
||||
return;
|
||||
}
|
||||
const expiredCache = localStorage.getItem(cacheKey);
|
||||
if (expiredCache) {
|
||||
try {
|
||||
const parsedCache = JSON.parse(expiredCache);
|
||||
data = parsedCache.value;
|
||||
console.log(`Using expired cache data for ${coinSymbol}`);
|
||||
} catch (cacheError) {
|
||||
throw error;
|
||||
}
|
||||
} else {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const chartData = chartModule.prepareChartData(coinSymbol, data);
|
||||
//console.log(`Prepared chart data for ${coinSymbol}:`, chartData.slice(0, 5));
|
||||
|
||||
if (chartData.length === 0) {
|
||||
throw new Error(`No valid chart data for ${coinSymbol}`);
|
||||
}
|
||||
|
||||
if (chartModule.chart) {
|
||||
if (chartData.length > 0 && chartModule.chart) {
|
||||
chartModule.chart.data.datasets[0].data = chartData;
|
||||
chartModule.chart.data.datasets[0].label = `${coinSymbol} Price (USD)`;
|
||||
|
||||
if (coinSymbol === 'WOW') {
|
||||
chartModule.chart.options.scales.x.time.unit = 'hour';
|
||||
chartModule.chart.options.scales.x.ticks.maxTicksLimit = 24;
|
||||
} else {
|
||||
const resolution = config.resolutions[config.currentResolution];
|
||||
chartModule.chart.options.scales.x.time.unit = resolution.interval === 'hourly' ? 'hour' : 'day';
|
||||
|
||||
if (config.currentResolution === 'year' || config.currentResolution === 'sixMonths') {
|
||||
chartModule.chart.options.scales.x.time.unit = 'month';
|
||||
chartModule.chart.options.scales.x.time.unit =
|
||||
resolution.interval === 'hourly' ? 'hour' :
|
||||
config.currentResolution === 'year' ? 'month' : 'day';
|
||||
}
|
||||
|
||||
if (config.currentResolution === 'year') {
|
||||
chartModule.chart.options.scales.x.ticks.maxTicksLimit = 12;
|
||||
} else if (config.currentResolution === 'sixMonths') {
|
||||
chartModule.chart.options.scales.x.ticks.maxTicksLimit = 6;
|
||||
} else if (config.currentResolution === 'day') {
|
||||
chartModule.chart.options.scales.x.ticks.maxTicksLimit = 24;
|
||||
}
|
||||
}
|
||||
|
||||
chartModule.chart.update('active');
|
||||
} else {
|
||||
//console.error('Chart object not initialized');
|
||||
throw new Error('Chart object not initialized');
|
||||
}
|
||||
|
||||
chartModule.currentCoin = coinSymbol;
|
||||
const loadTime = Date.now() - chartModule.loadStartTime;
|
||||
ui.updateLoadTimeAndCache(loadTime, cachedData);
|
||||
|
||||
}
|
||||
} catch (error) {
|
||||
//console.error(`Error updating chart for ${coinSymbol}:`, error);
|
||||
ui.displayErrorMessage(`Failed to update chart for ${coinSymbol}: ${error.message}`);
|
||||
console.error(`Error updating chart for ${coinSymbol}:`, error);
|
||||
if (!(chartModule.chart?.data.datasets[0].data.length > 0)) {
|
||||
chartModule.chart.data.datasets[0].data = [];
|
||||
chartModule.chart.update('active');
|
||||
}
|
||||
} finally {
|
||||
chartModule.hideChartLoader();
|
||||
}
|
||||
@@ -845,12 +971,10 @@ const chartModule = {
|
||||
showChartLoader: () => {
|
||||
const loader = document.getElementById('chart-loader');
|
||||
const chart = document.getElementById('coin-chart');
|
||||
|
||||
if (!loader || !chart) {
|
||||
//console.warn('Chart loader or chart container elements not found');
|
||||
return;
|
||||
}
|
||||
|
||||
loader.classList.remove('hidden');
|
||||
chart.classList.add('hidden');
|
||||
},
|
||||
@@ -858,12 +982,10 @@ const chartModule = {
|
||||
hideChartLoader: () => {
|
||||
const loader = document.getElementById('chart-loader');
|
||||
const chart = document.getElementById('coin-chart');
|
||||
|
||||
if (!loader || !chart) {
|
||||
//console.warn('Chart loader or chart container elements not found');
|
||||
return;
|
||||
}
|
||||
|
||||
loader.classList.add('hidden');
|
||||
chart.classList.remove('hidden');
|
||||
},
|
||||
@@ -916,8 +1038,8 @@ const app = {
|
||||
disabled: 'Auto-refresh: disabled',
|
||||
justRefreshed: 'Just refreshed',
|
||||
},
|
||||
cacheTTL: 5 * 60 * 1000, // 5 minutes
|
||||
minimumRefreshInterval: 60 * 1000, // 1 minute
|
||||
cacheTTL: 5 * 60 * 1000, // 5 min
|
||||
minimumRefreshInterval: 60 * 1000, // 1 min
|
||||
|
||||
init: () => {
|
||||
console.log('Initializing app...');
|
||||
@@ -937,8 +1059,6 @@ const app = {
|
||||
if (chartContainer) {
|
||||
chartModule.initChart();
|
||||
chartModule.showChartLoader();
|
||||
} else {
|
||||
//console.warn('Chart container not found, skipping chart initialization');
|
||||
}
|
||||
|
||||
console.log('Loading all coin data...');
|
||||
@@ -948,16 +1068,19 @@ const app = {
|
||||
config.currentResolution = 'day';
|
||||
await chartModule.updateChart('BTC');
|
||||
app.updateResolutionButtons('BTC');
|
||||
|
||||
const chartTitle = document.getElementById('chart-title');
|
||||
if (chartTitle) {
|
||||
chartTitle.textContent = 'Price Chart (BTC)';
|
||||
}
|
||||
}
|
||||
ui.setActiveContainer('btc-container');
|
||||
|
||||
//console.log('Setting up event listeners and initializations...');
|
||||
app.setupEventListeners();
|
||||
app.initializeSelectImages();
|
||||
app.initAutoRefresh();
|
||||
|
||||
} catch (error) {
|
||||
//console.error('Error during initialization:', error);
|
||||
ui.displayErrorMessage('Failed to initialize the dashboard. Please try refreshing the page.');
|
||||
} finally {
|
||||
ui.hideLoader();
|
||||
@@ -967,7 +1090,6 @@ const app = {
|
||||
console.log('App onLoad completed');
|
||||
}
|
||||
},
|
||||
|
||||
loadAllCoinData: async () => {
|
||||
//console.log('Loading data for all coins...');
|
||||
try {
|
||||
@@ -1032,12 +1154,14 @@ const app = {
|
||||
},
|
||||
|
||||
setupEventListeners: () => {
|
||||
//console.log('Setting up event listeners...');
|
||||
config.coins.forEach(coin => {
|
||||
const container = document.getElementById(`${coin.symbol.toLowerCase()}-container`);
|
||||
if (container) {
|
||||
container.addEventListener('click', () => {
|
||||
//console.log(`${coin.symbol} container clicked`);
|
||||
const chartTitle = document.getElementById('chart-title');
|
||||
if (chartTitle) {
|
||||
chartTitle.textContent = `Price Chart (${coin.symbol})`;
|
||||
}
|
||||
ui.setActiveContainer(`${coin.symbol.toLowerCase()}-container`);
|
||||
if (chartModule.chart) {
|
||||
if (coin.symbol === 'WOW') {
|
||||
@@ -1112,74 +1236,144 @@ const app = {
|
||||
} else {
|
||||
nextRefreshTime = now + config.cacheTTL;
|
||||
}
|
||||
|
||||
const timeUntilRefresh = nextRefreshTime - now;
|
||||
console.log(`Next refresh scheduled in ${timeUntilRefresh / 1000} seconds`);
|
||||
|
||||
app.nextRefreshTime = nextRefreshTime;
|
||||
app.autoRefreshInterval = setTimeout(() => {
|
||||
console.log('Auto-refresh triggered');
|
||||
app.refreshAllData();
|
||||
}, timeUntilRefresh);
|
||||
|
||||
localStorage.setItem('nextRefreshTime', app.nextRefreshTime.toString());
|
||||
app.updateNextRefreshTime();
|
||||
},
|
||||
|
||||
refreshAllData: async () => {
|
||||
if (app.isRefreshing) {
|
||||
console.log('Refresh already in progress, skipping...');
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('Refreshing all data...');
|
||||
const lastGeckoRequest = rateLimiter.lastRequestTime['coingecko'] || 0;
|
||||
const timeSinceLastRequest = Date.now() - lastGeckoRequest;
|
||||
const waitTime = Math.max(0, rateLimiter.minRequestInterval.coingecko - timeSinceLastRequest);
|
||||
|
||||
if (waitTime > 0) {
|
||||
const seconds = Math.ceil(waitTime / 1000);
|
||||
ui.displayErrorMessage(`Rate limit: Please wait ${seconds} seconds before refreshing`);
|
||||
|
||||
let remainingTime = seconds;
|
||||
const countdownInterval = setInterval(() => {
|
||||
remainingTime--;
|
||||
if (remainingTime > 0) {
|
||||
ui.displayErrorMessage(`Rate limit: Please wait ${remainingTime} seconds before refreshing`);
|
||||
} else {
|
||||
clearInterval(countdownInterval);
|
||||
ui.hideErrorMessage();
|
||||
}
|
||||
}, 1000);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('Starting refresh of all data...');
|
||||
app.isRefreshing = true;
|
||||
ui.showLoader();
|
||||
chartModule.showChartLoader();
|
||||
|
||||
try {
|
||||
|
||||
ui.hideErrorMessage();
|
||||
cache.clear();
|
||||
|
||||
await app.updateBTCPrice();
|
||||
const btcUpdateSuccess = await app.updateBTCPrice();
|
||||
if (!btcUpdateSuccess) {
|
||||
console.warn('BTC price update failed, continuing with cached or default value');
|
||||
}
|
||||
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
|
||||
const allCoinData = await api.fetchCoinGeckoDataXHR();
|
||||
if (allCoinData.error) {
|
||||
throw new Error(allCoinData.error);
|
||||
throw new Error(`CoinGecko API Error: ${allCoinData.error}`);
|
||||
}
|
||||
|
||||
const failedCoins = [];
|
||||
|
||||
for (const coin of config.coins) {
|
||||
const symbol = coin.symbol.toLowerCase();
|
||||
const coinData = allCoinData[symbol];
|
||||
if (coinData) {
|
||||
coinData.displayName = coin.displayName || coin.symbol;
|
||||
|
||||
try {
|
||||
if (!coinData) {
|
||||
throw new Error(`No data received`);
|
||||
}
|
||||
|
||||
coinData.displayName = coin.displayName || coin.symbol;
|
||||
ui.displayCoinData(coin.symbol, coinData);
|
||||
|
||||
const cacheKey = `coinData_${coin.symbol}`;
|
||||
cache.set(cacheKey, coinData);
|
||||
} else {
|
||||
//console.error(`No data found for ${coin.symbol}`);
|
||||
|
||||
} catch (coinError) {
|
||||
console.warn(`Failed to update ${coin.symbol}: ${coinError.message}`);
|
||||
failedCoins.push(coin.symbol);
|
||||
}
|
||||
}
|
||||
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
|
||||
if (chartModule.currentCoin) {
|
||||
try {
|
||||
await chartModule.updateChart(chartModule.currentCoin, true);
|
||||
} catch (chartError) {
|
||||
console.error('Chart update failed:', chartError);
|
||||
}
|
||||
}
|
||||
|
||||
app.lastRefreshedTime = new Date();
|
||||
localStorage.setItem('lastRefreshedTime', app.lastRefreshedTime.getTime().toString());
|
||||
ui.updateLastRefreshedTime();
|
||||
|
||||
console.log('All data refreshed successfully');
|
||||
if (failedCoins.length > 0) {
|
||||
const failureMessage = failedCoins.length === config.coins.length
|
||||
? 'Failed to update any coin data'
|
||||
: `Failed to update some coins: ${failedCoins.join(', ')}`;
|
||||
|
||||
let countdown = 5;
|
||||
ui.displayErrorMessage(`${failureMessage} (${countdown}s)`);
|
||||
|
||||
const countdownInterval = setInterval(() => {
|
||||
countdown--;
|
||||
if (countdown > 0) {
|
||||
ui.displayErrorMessage(`${failureMessage} (${countdown}s)`);
|
||||
} else {
|
||||
clearInterval(countdownInterval);
|
||||
ui.hideErrorMessage();
|
||||
}
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
console.log(`Refresh completed. Failed coins: ${failedCoins.length}`);
|
||||
|
||||
} catch (error) {
|
||||
//console.error('Error refreshing all data:', error);
|
||||
ui.displayErrorMessage('Failed to refresh all data. Please try again.');
|
||||
console.error('Critical error during refresh:', error);
|
||||
|
||||
let countdown = 10;
|
||||
ui.displayErrorMessage(`Refresh failed: ${error.message}. Please try again later. (${countdown}s)`);
|
||||
|
||||
const countdownInterval = setInterval(() => {
|
||||
countdown--;
|
||||
if (countdown > 0) {
|
||||
ui.displayErrorMessage(`Refresh failed: ${error.message}. Please try again later. (${countdown}s)`);
|
||||
} else {
|
||||
clearInterval(countdownInterval);
|
||||
ui.hideErrorMessage();
|
||||
}
|
||||
}, 1000);
|
||||
|
||||
} finally {
|
||||
ui.hideLoader();
|
||||
chartModule.hideChartLoader();
|
||||
app.isRefreshing = false;
|
||||
|
||||
if (app.isAutoRefreshEnabled) {
|
||||
app.scheduleNextRefresh();
|
||||
}
|
||||
@@ -1191,7 +1385,6 @@ const app = {
|
||||
const nextRefreshSpan = document.getElementById('next-refresh-time');
|
||||
const labelElement = document.getElementById('next-refresh-label');
|
||||
const valueElement = document.getElementById('next-refresh-value');
|
||||
|
||||
if (nextRefreshSpan && labelElement && valueElement) {
|
||||
if (app.nextRefreshTime) {
|
||||
if (app.updateNextRefreshTimeRAF) {
|
||||
@@ -1278,24 +1471,30 @@ const app = {
|
||||
},
|
||||
|
||||
updateBTCPrice: async () => {
|
||||
//console.log('Updating BTC price...');
|
||||
console.log('Updating BTC price...');
|
||||
try {
|
||||
const priceData = await api.fetchCoinGeckoDataXHR();
|
||||
if (priceData.error) {
|
||||
//console.error('Error fetching BTC price:', priceData.error);
|
||||
app.btcPriceUSD = 0;
|
||||
} else if (priceData.btc && priceData.btc.current_price) {
|
||||
|
||||
if (priceData.error) {
|
||||
console.warn('API error when fetching BTC price:', priceData.error);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (priceData.btc && typeof priceData.btc.current_price === 'number') {
|
||||
app.btcPriceUSD = priceData.btc.current_price;
|
||||
} else {
|
||||
//console.error('Unexpected BTC data structure:', priceData);
|
||||
app.btcPriceUSD = 0;
|
||||
return true;
|
||||
} else if (priceData.bitcoin && typeof priceData.bitcoin.usd === 'number') {
|
||||
app.btcPriceUSD = priceData.bitcoin.usd;
|
||||
return true;
|
||||
}
|
||||
|
||||
console.warn('Unexpected BTC price data structure:', priceData);
|
||||
return false;
|
||||
|
||||
} catch (error) {
|
||||
//console.error('Error fetching BTC price:', error);
|
||||
app.btcPriceUSD = 0;
|
||||
console.error('Error fetching BTC price:', error);
|
||||
return false;
|
||||
}
|
||||
//console.log('Current BTC price:', app.btcPriceUSD);
|
||||
},
|
||||
|
||||
sortTable: (columnIndex) => {
|
||||
@@ -1492,10 +1691,3 @@ resolutionButtons.forEach(button => {
|
||||
|
||||
// LOAD
|
||||
app.init();
|
||||
|
||||
document.addEventListener('visibilitychange', () => {
|
||||
if (!document.hidden && chartModule.chart) {
|
||||
console.log('Page became visible, reinitializing chart');
|
||||
chartModule.updateChart(chartModule.currentCoin, true);
|
||||
}
|
||||
});
|
||||
|
||||
115
basicswap/static/js/tabs.js
Normal file
115
basicswap/static/js/tabs.js
Normal file
@@ -0,0 +1,115 @@
|
||||
(function(window) {
|
||||
'use strict';
|
||||
|
||||
class Tabs {
|
||||
constructor(tabsEl, items = [], options = {}) {
|
||||
this._tabsEl = tabsEl;
|
||||
this._items = items;
|
||||
this._activeTab = options.defaultTabId ? this.getTab(options.defaultTabId) : null;
|
||||
this._options = {
|
||||
defaultTabId: options.defaultTabId || null,
|
||||
activeClasses: options.activeClasses || 'text-blue-600 hover:text-blue-600 dark:text-blue-500 dark:hover:text-blue-500 border-blue-600 dark:border-blue-500',
|
||||
inactiveClasses: options.inactiveClasses || 'dark:border-transparent text-gray-500 hover:text-gray-600 dark:text-gray-400 border-gray-100 hover:border-gray-300 dark:border-gray-700 dark:hover:text-gray-300',
|
||||
onShow: options.onShow || function() {}
|
||||
};
|
||||
this._initialized = false;
|
||||
this.init();
|
||||
}
|
||||
|
||||
init() {
|
||||
if (this._items.length && !this._initialized) {
|
||||
if (!this._activeTab) {
|
||||
this.setActiveTab(this._items[0]);
|
||||
}
|
||||
|
||||
this.show(this._activeTab.id, true);
|
||||
|
||||
this._items.forEach(tab => {
|
||||
tab.triggerEl.addEventListener('click', () => {
|
||||
this.show(tab.id);
|
||||
});
|
||||
});
|
||||
|
||||
this._initialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
show(tabId, force = false) {
|
||||
const tab = this.getTab(tabId);
|
||||
|
||||
if ((tab !== this._activeTab) || force) {
|
||||
this._items.forEach(t => {
|
||||
if (t !== tab) {
|
||||
t.triggerEl.classList.remove(...this._options.activeClasses.split(' '));
|
||||
t.triggerEl.classList.add(...this._options.inactiveClasses.split(' '));
|
||||
t.targetEl.classList.add('hidden');
|
||||
t.triggerEl.setAttribute('aria-selected', false);
|
||||
}
|
||||
});
|
||||
|
||||
tab.triggerEl.classList.add(...this._options.activeClasses.split(' '));
|
||||
tab.triggerEl.classList.remove(...this._options.inactiveClasses.split(' '));
|
||||
tab.triggerEl.setAttribute('aria-selected', true);
|
||||
tab.targetEl.classList.remove('hidden');
|
||||
|
||||
this.setActiveTab(tab);
|
||||
this._options.onShow(this, tab);
|
||||
}
|
||||
}
|
||||
|
||||
getTab(id) {
|
||||
return this._items.find(t => t.id === id);
|
||||
}
|
||||
|
||||
getActiveTab() {
|
||||
return this._activeTab;
|
||||
}
|
||||
|
||||
setActiveTab(tab) {
|
||||
this._activeTab = tab;
|
||||
}
|
||||
}
|
||||
|
||||
function initTabs() {
|
||||
document.querySelectorAll('[data-tabs-toggle]').forEach(tabsEl => {
|
||||
const items = [];
|
||||
let defaultTabId = null;
|
||||
|
||||
tabsEl.querySelectorAll('[role="tab"]').forEach(triggerEl => {
|
||||
const isActive = triggerEl.getAttribute('aria-selected') === 'true';
|
||||
const tab = {
|
||||
id: triggerEl.getAttribute('data-tabs-target'),
|
||||
triggerEl: triggerEl,
|
||||
targetEl: document.querySelector(triggerEl.getAttribute('data-tabs-target'))
|
||||
};
|
||||
items.push(tab);
|
||||
|
||||
if (isActive) {
|
||||
defaultTabId = tab.id;
|
||||
}
|
||||
});
|
||||
|
||||
new Tabs(tabsEl, items, {
|
||||
defaultTabId: defaultTabId
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
const style = document.createElement('style');
|
||||
style.textContent = `
|
||||
[data-tabs-toggle] [role="tab"] {
|
||||
cursor: pointer;
|
||||
}
|
||||
`;
|
||||
document.head.appendChild(style);
|
||||
|
||||
if (document.readyState === 'loading') {
|
||||
document.addEventListener('DOMContentLoaded', initTabs);
|
||||
} else {
|
||||
initTabs();
|
||||
}
|
||||
|
||||
window.Tabs = Tabs;
|
||||
window.initTabs = initTabs;
|
||||
|
||||
})(window);
|
||||
309
basicswap/static/js/tooltips.js
Normal file
309
basicswap/static/js/tooltips.js
Normal file
@@ -0,0 +1,309 @@
|
||||
(function(window) {
|
||||
'use strict';
|
||||
|
||||
const tooltipContainer = document.createElement('div');
|
||||
tooltipContainer.className = 'tooltip-container';
|
||||
|
||||
const style = document.createElement('style');
|
||||
style.textContent = `
|
||||
[role="tooltip"] {
|
||||
position: absolute;
|
||||
z-index: 9999;
|
||||
transition: opacity 0.2s ease-in-out;
|
||||
pointer-events: auto;
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.tooltip-container {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 0;
|
||||
overflow: visible;
|
||||
pointer-events: none;
|
||||
z-index: 9999;
|
||||
}
|
||||
`;
|
||||
|
||||
function ensureContainerExists() {
|
||||
if (!document.body.contains(tooltipContainer)) {
|
||||
document.body.appendChild(tooltipContainer);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function rafThrottle(callback) {
|
||||
let requestId = null;
|
||||
let lastArgs = null;
|
||||
|
||||
const later = (context) => {
|
||||
requestId = null;
|
||||
callback.apply(context, lastArgs);
|
||||
};
|
||||
|
||||
return function(...args) {
|
||||
lastArgs = args;
|
||||
if (requestId === null) {
|
||||
requestId = requestAnimationFrame(() => later(this));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function positionElement(targetEl, triggerEl, placement = 'top', offsetDistance = 8) {
|
||||
const triggerRect = triggerEl.getBoundingClientRect();
|
||||
const targetRect = targetEl.getBoundingClientRect();
|
||||
let top, left;
|
||||
|
||||
switch (placement) {
|
||||
case 'top':
|
||||
top = triggerRect.top - targetRect.height - offsetDistance;
|
||||
left = triggerRect.left + (triggerRect.width - targetRect.width) / 2;
|
||||
break;
|
||||
case 'bottom':
|
||||
top = triggerRect.bottom + offsetDistance;
|
||||
left = triggerRect.left + (triggerRect.width - targetRect.width) / 2;
|
||||
break;
|
||||
case 'left':
|
||||
top = triggerRect.top + (triggerRect.height - targetRect.height) / 2;
|
||||
left = triggerRect.left - targetRect.width - offsetDistance;
|
||||
break;
|
||||
case 'right':
|
||||
top = triggerRect.top + (triggerRect.height - targetRect.height) / 2;
|
||||
left = triggerRect.right + offsetDistance;
|
||||
break;
|
||||
}
|
||||
|
||||
const viewport = {
|
||||
width: window.innerWidth,
|
||||
height: window.innerHeight
|
||||
};
|
||||
|
||||
if (left < 0) left = 0;
|
||||
if (top < 0) top = 0;
|
||||
if (left + targetRect.width > viewport.width)
|
||||
left = viewport.width - targetRect.width;
|
||||
if (top + targetRect.height > viewport.height)
|
||||
top = viewport.height - targetRect.height;
|
||||
|
||||
targetEl.style.transform = `translate(${Math.round(left)}px, ${Math.round(top)}px)`;
|
||||
}
|
||||
|
||||
const tooltips = new WeakMap();
|
||||
|
||||
class Tooltip {
|
||||
constructor(targetEl, triggerEl, options = {}) {
|
||||
ensureContainerExists();
|
||||
|
||||
this._targetEl = targetEl;
|
||||
this._triggerEl = triggerEl;
|
||||
this._options = {
|
||||
placement: options.placement || 'top',
|
||||
triggerType: options.triggerType || 'hover',
|
||||
offset: options.offset || 8,
|
||||
onShow: options.onShow || function() {},
|
||||
onHide: options.onHide || function() {}
|
||||
};
|
||||
this._visible = false;
|
||||
this._initialized = false;
|
||||
this._hideTimeout = null;
|
||||
this._showTimeout = null;
|
||||
|
||||
if (this._targetEl.parentNode !== tooltipContainer) {
|
||||
tooltipContainer.appendChild(this._targetEl);
|
||||
}
|
||||
|
||||
this._targetEl.style.visibility = 'hidden';
|
||||
this._targetEl.style.opacity = '0';
|
||||
|
||||
this._showHandler = this.show.bind(this);
|
||||
this._hideHandler = this._handleHide.bind(this);
|
||||
this._updatePosition = rafThrottle(() => {
|
||||
if (this._visible) {
|
||||
positionElement(
|
||||
this._targetEl,
|
||||
this._triggerEl,
|
||||
this._options.placement,
|
||||
this._options.offset
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
this.init();
|
||||
}
|
||||
|
||||
init() {
|
||||
if (!this._initialized) {
|
||||
this._setupEventListeners();
|
||||
this._initialized = true;
|
||||
positionElement(
|
||||
this._targetEl,
|
||||
this._triggerEl,
|
||||
this._options.placement,
|
||||
this._options.offset
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
_setupEventListeners() {
|
||||
this._triggerEl.addEventListener('mouseenter', this._showHandler);
|
||||
this._triggerEl.addEventListener('mouseleave', this._hideHandler);
|
||||
this._triggerEl.addEventListener('focus', this._showHandler);
|
||||
this._triggerEl.addEventListener('blur', this._hideHandler);
|
||||
|
||||
this._targetEl.addEventListener('mouseenter', () => {
|
||||
clearTimeout(this._hideTimeout);
|
||||
clearTimeout(this._showTimeout);
|
||||
this._visible = true;
|
||||
this._targetEl.style.visibility = 'visible';
|
||||
this._targetEl.style.opacity = '1';
|
||||
});
|
||||
|
||||
this._targetEl.addEventListener('mouseleave', this._hideHandler);
|
||||
|
||||
if (this._options.triggerType === 'click') {
|
||||
this._triggerEl.addEventListener('click', this._showHandler);
|
||||
}
|
||||
|
||||
window.addEventListener('scroll', this._updatePosition, { passive: true });
|
||||
document.addEventListener('scroll', this._updatePosition, { passive: true, capture: true });
|
||||
window.addEventListener('resize', this._updatePosition, { passive: true });
|
||||
|
||||
let rafId;
|
||||
const smoothUpdate = () => {
|
||||
if (this._visible) {
|
||||
this._updatePosition();
|
||||
rafId = requestAnimationFrame(smoothUpdate);
|
||||
}
|
||||
};
|
||||
|
||||
this._startSmoothUpdate = () => {
|
||||
if (!rafId) rafId = requestAnimationFrame(smoothUpdate);
|
||||
};
|
||||
|
||||
this._stopSmoothUpdate = () => {
|
||||
if (rafId) {
|
||||
cancelAnimationFrame(rafId);
|
||||
rafId = null;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
_handleHide() {
|
||||
clearTimeout(this._hideTimeout);
|
||||
clearTimeout(this._showTimeout);
|
||||
|
||||
this._hideTimeout = setTimeout(() => {
|
||||
if (this._visible) {
|
||||
this.hide();
|
||||
}
|
||||
}, 100);
|
||||
}
|
||||
|
||||
show() {
|
||||
clearTimeout(this._hideTimeout);
|
||||
clearTimeout(this._showTimeout);
|
||||
|
||||
this._showTimeout = setTimeout(() => {
|
||||
if (!this._visible) {
|
||||
positionElement(
|
||||
this._targetEl,
|
||||
this._triggerEl,
|
||||
this._options.placement,
|
||||
this._options.offset
|
||||
);
|
||||
|
||||
this._targetEl.style.visibility = 'visible';
|
||||
this._targetEl.style.opacity = '1';
|
||||
this._visible = true;
|
||||
this._startSmoothUpdate();
|
||||
this._options.onShow();
|
||||
}
|
||||
}, 20);
|
||||
}
|
||||
|
||||
hide() {
|
||||
this._targetEl.style.opacity = '0';
|
||||
this._targetEl.style.visibility = 'hidden';
|
||||
this._visible = false;
|
||||
this._stopSmoothUpdate();
|
||||
this._options.onHide();
|
||||
}
|
||||
|
||||
destroy() {
|
||||
clearTimeout(this._hideTimeout);
|
||||
clearTimeout(this._showTimeout);
|
||||
this._stopSmoothUpdate();
|
||||
|
||||
this._triggerEl.removeEventListener('mouseenter', this._showHandler);
|
||||
this._triggerEl.removeEventListener('mouseleave', this._hideHandler);
|
||||
this._triggerEl.removeEventListener('focus', this._showHandler);
|
||||
this._triggerEl.removeEventListener('blur', this._hideHandler);
|
||||
this._targetEl.removeEventListener('mouseenter', this._showHandler);
|
||||
this._targetEl.removeEventListener('mouseleave', this._hideHandler);
|
||||
|
||||
if (this._options.triggerType === 'click') {
|
||||
this._triggerEl.removeEventListener('click', this._showHandler);
|
||||
}
|
||||
|
||||
window.removeEventListener('scroll', this._updatePosition);
|
||||
document.removeEventListener('scroll', this._updatePosition, true);
|
||||
window.removeEventListener('resize', this._updatePosition);
|
||||
|
||||
this._targetEl.style.visibility = '';
|
||||
this._targetEl.style.opacity = '';
|
||||
this._targetEl.style.transform = '';
|
||||
|
||||
if (this._targetEl.parentNode === tooltipContainer) {
|
||||
document.body.appendChild(this._targetEl);
|
||||
}
|
||||
|
||||
this._initialized = false;
|
||||
}
|
||||
|
||||
toggle() {
|
||||
if (this._visible) {
|
||||
this.hide();
|
||||
} else {
|
||||
this.show();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
document.head.appendChild(style);
|
||||
|
||||
function initTooltips() {
|
||||
ensureContainerExists();
|
||||
|
||||
document.querySelectorAll('[data-tooltip-target]').forEach(triggerEl => {
|
||||
if (tooltips.has(triggerEl)) return;
|
||||
|
||||
const targetId = triggerEl.getAttribute('data-tooltip-target');
|
||||
const targetEl = document.getElementById(targetId);
|
||||
|
||||
if (targetEl) {
|
||||
const placement = triggerEl.getAttribute('data-tooltip-placement');
|
||||
const triggerType = triggerEl.getAttribute('data-tooltip-trigger');
|
||||
|
||||
const tooltip = new Tooltip(targetEl, triggerEl, {
|
||||
placement: placement || 'top',
|
||||
triggerType: triggerType || 'hover',
|
||||
offset: 8
|
||||
});
|
||||
|
||||
tooltips.set(triggerEl, tooltip);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (document.readyState === 'loading') {
|
||||
document.addEventListener('DOMContentLoaded', initTooltips);
|
||||
} else {
|
||||
initTooltips();
|
||||
}
|
||||
|
||||
window.Tooltip = Tooltip;
|
||||
window.initTooltips = initTooltips;
|
||||
|
||||
})(window);
|
||||
@@ -8,11 +8,16 @@
|
||||
{% endif %}
|
||||
<script src="/static/js/libs/chart.js"></script>
|
||||
<script src="/static/js/libs/chartjs-adapter-date-fns.bundle.min.js"></script>
|
||||
|
||||
<link type="text/css" media="all" href="/static/css/libs/flowbite.min.css" rel="stylesheet" />
|
||||
<link type="text/css" media="all" href="/static/css/libs/tailwind.min.css" rel="stylesheet">
|
||||
<link type="text/css" media="all" href="/static/css/style.css" rel="stylesheet">
|
||||
|
||||
<script src="/static/js/main.js"></script>
|
||||
<script src="/static/js/libs/flowbite.js"></script>
|
||||
|
||||
<script src="/static/js/tabs.js"></script>
|
||||
<script src="/static/js/dropdown.js"></script>
|
||||
<script src="/static/js/tooltips.js"></script>
|
||||
|
||||
<script>
|
||||
const isDarkMode =
|
||||
@@ -451,7 +456,6 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
|
||||
<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:</b> {{ summary.num_sent_offers }}</p>
|
||||
<div class="tooltip-arrow" data-popper-arrow></div>
|
||||
</div>
|
||||
|
||||
</li>
|
||||
@@ -468,7 +472,7 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
|
||||
<div id="tooltip-bids-received" 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:</b> {{ summary.num_recv_bids }}</p>
|
||||
<div class="tooltip-arrow" data-popper-arrow></div>
|
||||
|
||||
</div>
|
||||
<li>
|
||||
<a data-tooltip-target="tooltip-bids-sent" class="flex mr-10 items-center text-sm text-gray-400 hover:text-gray-600 dark:text-gray-100 dark:hover:text-gray-100" href="/sentbids">
|
||||
@@ -476,7 +480,7 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
<span>Bids Sent</span><span 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_sent_active_bids }}</span></a>
|
||||
<div id="tooltip-bids-sent" 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:</b> {{ summary.num_sent_bids }}</p>
|
||||
<div class="tooltip-arrow" data-popper-arrow></div>
|
||||
|
||||
</div>
|
||||
|
||||
</li>
|
||||
|
||||
@@ -137,7 +137,7 @@
|
||||
<td class="py-3 px-6 bold">{{ data.amt_to }} {{ data.tla_to }}</td>
|
||||
</tr>
|
||||
<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">Minimum Bid Amount</td>
|
||||
<td class="py-3 px-6 bold">Minimum Purchase</td>
|
||||
<td class="py-3 px-6">{{ data.amt_bid_min }} {{ data.tla_from }}</td>
|
||||
</tr>
|
||||
<tr class="opacity-100 text-gray-500 dark:text-gray-100 hover:bg-coolGray-200 dark:hover:bg-gray-600">
|
||||
@@ -747,40 +747,24 @@ function resetForm() {
|
||||
const bidAmountInput = document.getElementById('bid_amount');
|
||||
const bidRateInput = document.getElementById('bid_rate');
|
||||
const validMinsInput = document.querySelector('input[name="validmins"]');
|
||||
const addrFromSelect = document.querySelector('select[name="addr_from"]');
|
||||
|
||||
const amtVar = document.getElementById('amt_var')?.value === 'True';
|
||||
if (bidAmountSendInput) {
|
||||
const defaultSendAmount = bidAmountSendInput.getAttribute('max');
|
||||
bidAmountSendInput.value = defaultSendAmount;
|
||||
bidAmountSendInput.value = amtVar ? '' : bidAmountSendInput.getAttribute('max');
|
||||
}
|
||||
|
||||
if (bidAmountInput) {
|
||||
const defaultReceiveAmount = bidAmountInput.getAttribute('max');
|
||||
bidAmountInput.value = defaultReceiveAmount;
|
||||
bidAmountInput.value = amtVar ? '' : bidAmountInput.getAttribute('max');
|
||||
}
|
||||
|
||||
if (bidRateInput && !bidRateInput.disabled) {
|
||||
const defaultRate = document.getElementById('offer_rate')?.value || '';
|
||||
bidRateInput.value = defaultRate;
|
||||
}
|
||||
|
||||
if (validMinsInput) {
|
||||
validMinsInput.value = "60";
|
||||
}
|
||||
|
||||
if (addrFromSelect) {
|
||||
if (addrFromSelect.options.length > 1) {
|
||||
addrFromSelect.selectedIndex = 1;
|
||||
} else {
|
||||
addrFromSelect.selectedIndex = 0;
|
||||
}
|
||||
const selectedOption = addrFromSelect.options[addrFromSelect.selectedIndex];
|
||||
saveAddress(selectedOption.value, selectedOption.text);
|
||||
}
|
||||
|
||||
if (!amtVar) {
|
||||
updateBidParams('rate');
|
||||
}
|
||||
updateModalValues();
|
||||
|
||||
const errorMessages = document.querySelectorAll('.error-message');
|
||||
errorMessages.forEach(msg => msg.remove());
|
||||
|
||||
|
||||
@@ -293,7 +293,7 @@
|
||||
<div class="w-full md:flex-1 p-3">
|
||||
<div class="flex flex-wrap -m-3">
|
||||
<div class="w-full md:w-1/2 p-3">
|
||||
<p class="mb-1.5 font-medium text-base text-coolGray-800 dark:text-white">Minimum Bid Amount</p>
|
||||
<p class="mb-1.5 font-medium text-base text-coolGray-800 dark:text-white">Minimum Purchase</p>
|
||||
<div class="relative">
|
||||
<div class="flex absolute inset-y-0 left-0 items-center pl-3 pointer-events-none">
|
||||
{{ select_bid_amount_svg | safe }}
|
||||
|
||||
@@ -274,7 +274,7 @@ if (document.readyState === 'loading') {
|
||||
<div class="w-full md:flex-1 p-3">
|
||||
<div class="flex flex-wrap -m-3">
|
||||
<div class="w-full md:w-1/2 p-3">
|
||||
<p class="mb-1.5 font-medium text-base dark:text-white text-coolGray-800">Minimum Bid Amount</p>
|
||||
<p class="mb-1.5 font-medium text-base dark:text-white text-coolGray-800">Minimum Purchase</p>
|
||||
<div class="relative">
|
||||
<div class="flex absolute inset-y-0 left-0 items-center pl-3 pointer-events-none">
|
||||
{{ select_bid_amount_svg | safe }}
|
||||
|
||||
@@ -279,7 +279,7 @@
|
||||
<div class="w-full md:flex-1 p-3">
|
||||
<div class="flex flex-wrap -m-3">
|
||||
<div class="w-full md:w-1/2 p-3">
|
||||
<p class="mb-1.5 font-medium text-base text-coolGray-800 dark:text-white">Minimum Bid Amount</p>
|
||||
<p class="mb-1.5 font-medium text-base text-coolGray-800 dark:text-white">Minimum Purchase</p>
|
||||
<div class="relative">
|
||||
<div class="flex absolute inset-y-0 left-0 items-center pl-3 pointer-events-none">
|
||||
{{ select_bid_amount_svg | safe }}
|
||||
|
||||
@@ -25,11 +25,11 @@ function getWebSocketConfig() {
|
||||
</script>
|
||||
|
||||
{% if sent_offers %}
|
||||
<div class="container mx-auto">
|
||||
<section class="p-5 mt-5">
|
||||
<div class="flex flex-wrap items-center -m-2">
|
||||
<div class="w-full md:w-1/2 p-2">
|
||||
<ul class="flex flex-wrap items-center gap-x-3 mb-2">
|
||||
<div class="lg:container mx-auto">
|
||||
<section class="p-3 mt-2">
|
||||
<div class="flex items-center">
|
||||
<div class="w-full">
|
||||
<ul class="flex items-center gap-x-2 mb-1">
|
||||
<li><a class="flex font-medium text-xs text-coolGray-500 dark:text-gray-300 hover:text-coolGray-700" href="/">Home</a></li>
|
||||
<li>{{ breadcrumb_line_svg | safe }}</li>
|
||||
<li>
|
||||
@@ -46,21 +46,21 @@ function getWebSocketConfig() {
|
||||
{% endif %}
|
||||
|
||||
{% if sent_offers %}
|
||||
<section class="py-5">
|
||||
<section class="py-3">
|
||||
{% else %}
|
||||
<section class="py-5 mt-5">
|
||||
<section class="py-3 px-4 mt-3">
|
||||
{% endif %}
|
||||
<div class="container px-4 mx-auto">
|
||||
<div class="relative py-11 px-16 bg-coolGray-900 dark:bg-gray-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 -m-3">
|
||||
<div class="w-full md:w-1/2 p-3">
|
||||
<h2 class="mb-6 text-4xl font-bold text-white tracking-tighter">{{ page_type }}</h2>
|
||||
<div class="lg:container mx-auto">
|
||||
<div class="relative py-6 px-8 bg-coolGray-900 dark:bg-gray-500 rounded-md overflow-hidden">
|
||||
|
||||
<img class="absolute h-48 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 -m-2">
|
||||
<div class="w-full md:w-1/2 p-2">
|
||||
<h2 class="mb-3 text-3xl font-bold text-white tracking-tighter">{{ page_type }}</h2>
|
||||
<p class="font-normal text-coolGray-200 dark:text-white">{{ page_type_description }}</p>
|
||||
</div>
|
||||
<div class="rounded-full{{ page_button }} w-full md:w-1/2 p-3 p-6 container flex flex-wrap items-center justify-end items-center mx-auto">
|
||||
<a id="refresh" href="/newoffer" class="rounded-full flex flex-wrap justify-center px-5 py-3 bg-blue-500 hover:bg-green-600 hover:border-green-600 font-medium text-sm text-white border border-blue-500 rounded-md focus:ring-0 focus:outline-none">{{ place_new_offer_svg | safe }}<span>Place new Offer</span></a>
|
||||
<div class="rounded-full{{ page_button }} w-full md:w-1/2 p-2 lg:container flex flex-wrap items-center justify-end mx-auto">
|
||||
<a id="refresh" href="/newoffer" class="rounded-full flex flex-wrap justify-center px-4 py-2 bg-blue-500 hover:bg-green-600 hover:border-green-600 font-medium text-sm text-white border border-blue-500 rounded-md focus:ring-0 focus:outline-none">{{ place_new_offer_svg | safe }}<span>Place new Offer</span></a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -71,11 +71,11 @@ function getWebSocketConfig() {
|
||||
|
||||
{% if show_chart %}
|
||||
<section class="relative hidden md:block">
|
||||
<div class="pl-6 pr-6 pt-0 pb-0 mt-5 h-full overflow-hidden">
|
||||
<div class="px-6 py-0 mt-5 h-full overflow-hidden">
|
||||
<div class="pb-6 border-coolGray-100">
|
||||
<div class="flex flex-wrap items-center justify-between -m-2">
|
||||
<div class="flex flex-wrap items-center justify-between">
|
||||
<div class="w-full pt-2">
|
||||
<div class="container px-4 mx-auto">
|
||||
<div class="lg:container mx-auto">
|
||||
<div class="pt-6 pb-8 bg-coolGray-100 dark:bg-gray-500 rounded-xl container-to-blur">
|
||||
<div class="flex justify-between items-center mb-4 mr-10 ml-10">
|
||||
<div class="flex items-center justify-between">
|
||||
@@ -90,36 +90,28 @@ function getWebSocketConfig() {
|
||||
<span id="load-time hidden" class="mr-2 text-sm text-gray-600 dark:text-gray-300"></span>
|
||||
<span id="last-refreshed-time hidden" class="mr-2 text-sm text-gray-600 dark:text-gray-300"></span>
|
||||
<span id="cache-status hidden" class="mr-4 text-sm text-gray-600 dark:text-gray-300"></span>
|
||||
<span id="tor-status" class="mr-4 text-sm hidden {% if tor_established %}text-green-500{% else %}text-red-500{% endif %}"> Tor {% if tor_established %}ON{% else %}OFF{% endif %}
|
||||
<span id="tor-status" class="mr-4 text-sm hidden {% if tor_established %}text-green-500{% else %}text-red-500{% endif %}">
|
||||
Tor {% if tor_established %}ON{% else %}OFF{% endif %}
|
||||
<a href="https://academy.particl.io/en/latest/basicswap-guides/basicswapguides_tor.html" target="_blank" rel="noopener noreferrer" class="underline">(?)</a>
|
||||
</span>
|
||||
<span id="next-refresh-time" class="mr-4 text-sm text-gray-600 dark:text-gray-300">
|
||||
<span id="next-refresh-label"></span>
|
||||
<span id="next-refresh-value"></span>
|
||||
</span>
|
||||
<div id="tooltip-volume" 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">
|
||||
Toggle Coin Volume
|
||||
<div class="tooltip-arrow" data-popper-arrow></div>
|
||||
</div>
|
||||
<div id="tooltip-volume" 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">Toggle Coin Volume</div>
|
||||
<button id="toggle-volume" data-tooltip-target="tooltip-volume" class="text-white font-bold py-2 px-4 rounded mr-2 focus:outline-none focus:ring-0 transition-colors duration-200" title="Toggle Volume">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor">
|
||||
<path d="M2 11a1 1 0 011-1h2a1 1 0 011 1v5a1 1 0 01-1 1H3a1 1 0 01-1-1v-5zM8 7a1 1 0 011-1h2a1 1 0 011 1v9a1 1 0 01-1 1H9a1 1 0 01-1-1V7zM14 4a1 1 0 011-1h2a1 1 0 011 1v12a1 1 0 01-1 1h-2a1 1 0 01-1-1V4z" />
|
||||
</svg>
|
||||
</button>
|
||||
<div id="tooltip-refresh" 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">
|
||||
Refresh Charts/Prices & Clear Cache
|
||||
<div class="tooltip-arrow" data-popper-arrow></div>
|
||||
</div>
|
||||
<div id="tooltip-refresh" 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">Refresh Charts/Prices & Clear Cache</div>
|
||||
<button id="refresh-all" data-tooltip-target="tooltip-refresh" class="text-gray-600 dark:text-gray-400 font-bold py-2 px-4 rounded mr-2 focus:outline-none focus:ring-0 transition-colors duration-200" title="Refresh Data & Clear Cache">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15" />
|
||||
<circle cx="12" cy="12" r="3" />
|
||||
</svg>
|
||||
</button>
|
||||
<div id="tooltip-auto" 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">
|
||||
Auto Refresh Enable/Disable
|
||||
<div class="tooltip-arrow" data-popper-arrow></div>
|
||||
</div>
|
||||
<div id="tooltip-auto" 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">Auto Refresh Enable/Disable</div>
|
||||
<button id="toggle-auto-refresh" data-enabled="false" data-tooltip-target="tooltip-auto" class="text-white font-bold py-2 px-4 rounded mr-2 focus:outline-none focus:ring-0 transition-colors duration-200" title="Enable Auto-Refresh">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor">
|
||||
<path fill-rule="evenodd" d="M4 2a1 1 0 011 1v2.101a7.002 7.002 0 0111.601 2.566 1 1 0 11-1.885.666A5.002 5.002 0 005.999 7H9a1 1 0 010 2H4a1 1 0 01-1-1V3a1 1 0 011-1zm.008 9.057a1 1 0 011.276.61A5.002 5.002 0 0014.001 13H11a1 1 0 110-2h5a1 1 0 011 1v5a1 1 0 11-2 0v-2.101a7.002 7.002 0 01-11.601-2.566 1 1 0 01.61-1.276z" clip-rule="evenodd" />
|
||||
@@ -137,18 +129,25 @@ function getWebSocketConfig() {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="error-overlay" class="error-overlay hidden absolute inset-0 bg-black bg-opacity-50 flex items-center justify-center">
|
||||
<div id="error-content" class="error-content bg-white dark:bg-gray-800 rounded-lg p-6 w-full max-w-2xl mx-4 relative">
|
||||
<button id="close-error" class="absolute top-3 right-3 bg-red-500 text-white rounded-full p-2 hover:bg-red-600 focus:outline-none">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<div id="error-overlay" class="notice-overlay hidden absolute inset-0 bg-opacity-30 backdrop-blur-sm flex items-center justify-center">
|
||||
<div id="error-content" class="notice-content bg-white dark:bg-gray-800 rounded-xl p-6 w-full max-w-2xl mx-4 relative shadow-lg">
|
||||
<div class="absolute top-0 left-0 right-0 h-1 bg-gradient-to-r from-blue-400 via-blue-500 to-blue-600"></div>
|
||||
<button id="close-error" class="absolute top-3 right-3 text-gray-400 hover:text-gray-500 dark:text-gray-500 dark:hover:text-white rounded-full p-2 hover:bg-gray-100 dark:hover:bg-gray-700 transition-colors focus:outline-none">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
|
||||
</svg>
|
||||
</button>
|
||||
<p class="text-red-600 font-semibold text-xl mb-4">Error</p>
|
||||
<p id="error-message" class="text-gray-700 dark:text-gray-300 text-lg mb-6"></p>
|
||||
<p class="text-sm text-gray-600 dark:text-gray-400">To review or update your Chart API Key(s), navigate to<a href="/settings" class="text-blue-500 hover:underline">Settings & Tools > Settings > General (TAB)</a>.
|
||||
</p>
|
||||
<div class="flex items-center gap-3 mb-4">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 text-blue-500" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 17h5l-1.405-1.405A2.032 2.032 0 0118 14.158V11a6.002 6.002 0 00-4-5.659V5a2 2 0 10-4 0v.341C7.67 6.165 6 8.388 6 11v3.159c0 .538-.214 1.055-.595 1.436L4 17h5m6 0v1a3 3 0 11-6 0v-1m6 0H9" />
|
||||
</svg>
|
||||
<p class="text-lg font-medium text-gray-900 dark:text-gray-100">Notice</p>
|
||||
</div>
|
||||
<p id="error-message" class="text-gray-600 dark:text-gray-300 text-base mb-6"></p>
|
||||
<div class="text-sm text-gray-500 dark:text-gray-400">
|
||||
Need to update your (API) settings?
|
||||
<a href="/settings" class="text-blue-500 hover:text-blue-600 dark:hover:text-blue-400 font-medium ml-1 hover:underline">Go to Settings -> General</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -157,9 +156,9 @@ function getWebSocketConfig() {
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="py-4 flex flex-wrap justify-center overflow-hidden container-to-blur">
|
||||
<div class="container px-4 mx-auto">
|
||||
<div class="flex flex-wrap justify-center -m-3" id="coin-container">
|
||||
<section class="py-4 px-3 flex flex-wrap overflow-hidden container-to-blur">
|
||||
<div class="lg:container mx-auto">
|
||||
<div class="flex flex-wrap justify-center lg:justify-start xl:justify-center" id="coin-container">
|
||||
{% set coin_data = {
|
||||
'BTC': {'name': 'Bitcoin', 'symbol': 'BTC', 'image': 'Bitcoin.png', 'show': true},
|
||||
'XMR': {'name': 'Monero', 'symbol': 'XMR', 'image': 'Monero.png', 'show': true},
|
||||
@@ -192,9 +191,9 @@ function getWebSocketConfig() {
|
||||
|
||||
{% for coin_symbol in custom_order %}
|
||||
{% if coin_symbol in display_coins and coin_data[coin_symbol]['show'] %}
|
||||
<div class="w-full sm:w-1/2 lg:w-1/6 p-3" id="{{ coin_symbol.lower() }}-container">
|
||||
<div class="w-full sm:w-1/2 md:w-1/4 lg:w-1/5 xl:w-1/6 p-3" id="{{ coin_symbol.lower() }}-container">
|
||||
<div class="px-5 py-3 h-full bg-coolGray-100 dark:bg-gray-500 rounded-2xl dark:text-white {% if coin_symbol == 'BTC' %}active-container{% endif %}" style="min-height: 180px;">
|
||||
<div class="flex items-center">
|
||||
<div class="flex items-center h-10">
|
||||
<img src="/static/images/coins/{{ coin_data[coin_symbol]['image'] }}" class="rounded-xl" style="width: 28px; height: 28px; object-fit: contain;" alt="{{ coin_data[coin_symbol]['name'] }}">
|
||||
<p class="ml-1 text-black text-sm dark:text-white">
|
||||
{{ coin_data[coin_symbol]['name'] }} {% if coin_data[coin_symbol]['symbol'] != coin_data[coin_symbol]['name'] %}({{ coin_data[coin_symbol]['symbol'] }}){% endif %}
|
||||
@@ -213,12 +212,13 @@ function getWebSocketConfig() {
|
||||
</div>
|
||||
{% if coin_symbol != 'BTC' %}
|
||||
<div id="{{ coin_symbol.lower() }}-btc-price-div" class="flex items-center text-xs text-gray-600 dark:text-gray-300 mt-2 {% if coin_symbol == 'WOW' %}hidden{% endif %}">
|
||||
<span class="bold mr-2">BTC:</span> <span id="{{ coin_symbol.lower() }}-price-btc"></span>
|
||||
<span class="bold mr-2 ml-1">BTC:</span>
|
||||
<span id="{{ coin_symbol.lower() }}-price-btc"></span>
|
||||
</div>
|
||||
{% endif %}
|
||||
<div id="{{ coin_symbol.lower() }}-volume-div" class="flex items-center text-xs text-gray-600 dark:text-gray-300 mt-2">
|
||||
<span class="bold mr-2">VOL:</span>
|
||||
<div id="{{ coin_symbol.lower() }}-volume-24h"></div>
|
||||
<span class="bold mr-2 ml-1">VOL:</span>
|
||||
<span id="{{ coin_symbol.lower() }}-volume-24h"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -233,17 +233,17 @@ function getWebSocketConfig() {
|
||||
<script src="/static/js/pricechart.js"></script>
|
||||
|
||||
<section>
|
||||
<div class="pl-6 pr-6 pt-0 pb-0 mt-5 h-full overflow-hidden">
|
||||
<div class="px-6 py-0 mt-5 h-full overflow-hidden">
|
||||
<div class="border-coolGray-100">
|
||||
<div class="flex flex-wrap items-center justify-between -m-2">
|
||||
<div class="flex flex-wrap items-center justify-between">
|
||||
<div class="w-full mx-auto pt-2">
|
||||
<form method="post" id="filterForm">
|
||||
<div class="flex items-center justify-center pb-4 dark:text-white">
|
||||
<div class="rounded-b-md">
|
||||
<div class="w-full md:w-0/12">
|
||||
<div class="container flex flex-wrap">
|
||||
<div class="md:w-auto hover-container justify-center">
|
||||
<div class="flex flex-wrap justify-center">
|
||||
<div class="lg:container flex flex-wrap justify-center">
|
||||
<div class="md:w-auto hover-container">
|
||||
<div class="flex flex-wrap">
|
||||
<div class="pt-3 px-3 md:w-auto hover-container">
|
||||
<div class="flex">
|
||||
<button id="coin_to_button" class="bg-gray-50 text-gray-900 appearance-none w-10 dark:bg-gray-500 dark:text-white border-l border-t border-b border-gray-300 dark:border-gray-400 dark:text-gray-50 dark:placeholder-gray-50 text-sm rounded-l-lg flex items-center" disabled></button>
|
||||
@@ -302,14 +302,14 @@ function getWebSocketConfig() {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="w-full md:w-auto pt-3 px-3">
|
||||
<div class="w-full lg:w-auto pt-3 px-3">
|
||||
<div class="relative">
|
||||
<button type="button" id="clearFilters" class="transition-opacity duration-200 flex flex-wrap justify-center w-full px-4 py-2.5 font-medium text-sm hover:text-white dark:text-white dark:bg-gray-500 bg-coolGray-200 hover:bg-green-600 hover:border-green-600 rounded-lg transition duration-200 border border-coolGray-200 dark:border-gray-400 rounded-md shadow-button focus:ring-0 focus:outline-none" disabled>
|
||||
<span>Clear Filters</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="w-full md:w-auto pt-3 px-3">
|
||||
<div class="w-full lg:w-auto pt-3 px-3">
|
||||
<div class="relative">
|
||||
<button type="button" id="refreshOffers" class="flex flex-wrap justify-center w-full px-4 py-2.5 font-medium text-sm text-white bg-blue-600 hover:bg-green-600 hover:border-green-600 rounded-lg transition duration-200 border border-blue-500 rounded-md shadow-button focus:ring-0 focus:outline-none">
|
||||
<svg id="refreshIcon" class="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||
@@ -334,10 +334,10 @@ function getWebSocketConfig() {
|
||||
<div id="jsonView" class="hidden mb-4">
|
||||
<pre id="jsonContent" class="bg-gray-100 p-4 rounded overflow-auto" style="max-height: 300px;"></pre>
|
||||
</div>
|
||||
<div class="container mt-5 mx-auto px-4">
|
||||
<div class="mt-5 lg:container mx-auto lg:px-0 px-6">
|
||||
<div class="pt-0 pb-6 bg-coolGray-100 dark:bg-gray-500 rounded-xl">
|
||||
<div class="px-0">
|
||||
<div class="w-auto mt-6 overflow-x-auto">
|
||||
<div class="w-auto mt-6 overflow-auto lg:overflow-hidden">
|
||||
<table class="w-full min-w-max">
|
||||
<thead class="uppercase">
|
||||
<tr>
|
||||
|
||||
@@ -1,36 +1,37 @@
|
||||
{% include 'header.html' %}
|
||||
{% from 'style.html' import select_box_arrow_svg, select_box_class, circular_arrows_svg, circular_error_svg, circular_info_svg, cross_close_svg, breadcrumb_line_svg, withdraw_svg, utxo_groups_svg, create_utxo_svg, red_cross_close_svg, blue_cross_close_svg, circular_update_messages_svg, circular_error_messages_svg %}
|
||||
<script src="/static/js/libs//qrcode.js"></script>
|
||||
<div class="container mx-auto">
|
||||
<section class="p-5 mt-5">
|
||||
<div class="container mx-auto">
|
||||
<div class="flex flex-wrap items-center -m-2">
|
||||
<div class="w-full md:w-1/2 p-2">
|
||||
<ul class="flex flex-wrap items-center gap-x-3 mb-2">
|
||||
<li><a class="flex font-medium text-xs text-coolGray-500 dark:text-gray-300 hover:text-coolGray-700" href="/">Home</a></li>
|
||||
<li><a class="flex font-medium text-md lg:text-xs text-coolGray-500 dark:text-gray-300 hover:text-coolGray-700" href="/">Home</a></li>
|
||||
<li>{{ breadcrumb_line_svg | safe }}</li>
|
||||
<li><a class="flex font-medium text-xs text-coolGray-500 dark:text-gray-300 hover:text-coolGray-700" href="/wallets">Wallets</a></li>
|
||||
<li><a class="flex font-medium text-md lg:text-xs text-coolGray-500 dark:text-gray-300 hover:text-coolGray-700" href="/wallets">Wallets</a></li>
|
||||
<li>{{ breadcrumb_line_svg | safe }}</li>
|
||||
<li><a class="flex font-medium text-xs text-coolGray-500 dark:text-gray-300 hover:text-coolGray-700" href="/wallet/{{ w.ticker }}">{{ w.ticker }}</a></li>
|
||||
<li><a class="flex font-medium text-md lg:text-xs text-coolGray-500 dark:text-gray-300 hover:text-coolGray-700" href="/wallet/{{ w.ticker }}">{{ w.ticker }}</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section class="py-4">
|
||||
<div class="container px-4 mx-auto">
|
||||
<section class="py-4 px-6">
|
||||
<div class="lg:container mx-auto">
|
||||
<div class="relative py-11 px-16 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 -m-3">
|
||||
<div class="w-full md:w-1/2">
|
||||
<h2 class="text-3xl font-bold text-white"> <span class="inline-block align-middle"><img class="mr-2 h-16" src="/static/images/coins/{{ w.name }}.png" alt="{{ w.name }}"></span>({{ w.ticker }}) {{ w.name }} Wallet </h2>
|
||||
</div>
|
||||
<div class="w-full md:w-1/2 p-3 p-6 container flex flex-wrap items-center justify-end items-center mx-auto"> <a class="rounded-full mr-5 flex flex-wrap justify-center px-5 py-3 bg-blue-500 hover:bg-blue-600 font-medium text-sm text-white border dark:bg-gray-500 dark:hover:bg-gray-700 border-blue-500 rounded-md shadow-button focus:ring-0 focus:outline-none" id="refresh" href="/wallet/{{ w.ticker }}"> {{ circular_arrows_svg | safe }}<span>Refresh</span> </a> </div>
|
||||
<div class="w-full md:w-1/2 p-3 p-6 container flex flex-wrap items-center justify-end items-center mx-auto"> <a class="rounded-full mr-5 flex flex-wrap justify-center px-5 py-3 bg-blue-500 hover:bg-blue-600 font-medium text-lg lg:text-sm text-white border dark:bg-gray-500 dark:hover:bg-gray-700 border-blue-500 rounded-md shadow-button focus:ring-0 focus:outline-none" id="refresh" href="/wallet/{{ w.ticker }}"> {{ circular_arrows_svg | safe }}<span>Refresh</span> </a> </div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
{% include 'inc_messages.html' %}
|
||||
{% if w.updating %}
|
||||
<section class="py-4" id="messages_updating" role="alert">
|
||||
<div class="container px-4 mx-auto">
|
||||
<section class="py-4 px-6" id="messages_updating" role="alert">
|
||||
<div class="lg:container mx-auto">
|
||||
<div class="p-6 text-green-800 rounded-lg bg-blue-50 border border-blue-500 dark:bg-gray-500 dark:text-blue-400 rounded-md">
|
||||
<div class="flex flex-wrap justify-between items-center -m-2">
|
||||
<div class="flex-1 p-2">
|
||||
@@ -39,8 +40,8 @@
|
||||
{{ circular_update_messages_svg | safe }}
|
||||
</div>
|
||||
<ul class="ml-4 mt-1">
|
||||
<li class="font-semibold text-sm text-blue-500 error_msg"><span class="bold">UPDATING:</span></li>
|
||||
<li class="font-medium text-sm text-blue-500">Please wait...</li>
|
||||
<li class="font-semibold text-lg lg:text-sm text-blue-500 error_msg"><span class="bold">UPDATING:</span></li>
|
||||
<li class="font-medium text-lg lg:text-sm text-blue-500">Please wait...</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
@@ -56,8 +57,8 @@
|
||||
{% endif %}
|
||||
{% if w.havedata %}
|
||||
{% if w.error %}
|
||||
<section class="py-4" id="messages_error" role="alert">
|
||||
<div class="container px-4 mx-auto">
|
||||
<section class="py-4 px-6" id="messages_error" role="alert">
|
||||
<div class="lg:container mx-auto">
|
||||
<div class="p-6 text-green-800 rounded-lg bg-red-50 border border-red-400 dark:bg-gray-500 dark:text-red-400 rounded-md">
|
||||
<div class="flex flex-wrap justify-between items-center -m-2">
|
||||
<div class="flex-1 p-2">
|
||||
@@ -66,8 +67,8 @@
|
||||
{{ circular_error_messages_svg | safe }}
|
||||
</div>
|
||||
<ul class="ml-4 mt-1">
|
||||
<li class="font-semibold text-sm text-red-500 error_msg"><span class="bold">ERROR:</span></li>
|
||||
<li class="font-medium text-sm text-red-500 error_msg">{{ w.error }}</li>
|
||||
<li class="font-semibold text-lg lg:text-sm text-red-500 error_msg"><span class="bold">ERROR:</span></li>
|
||||
<li class="font-medium text-lg lg:text-sm text-red-500 error_msg">{{ w.error }}</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
@@ -83,8 +84,8 @@
|
||||
</section>
|
||||
{% else %}
|
||||
{% if w.cid == '18' %} {# DOGE #}
|
||||
<section class="py-4" id="messages_notice">
|
||||
<div class="container px-4 mx-auto">
|
||||
<section class="py-4 px-6" id="messages_notice">
|
||||
<div class="lg:container mx-auto">
|
||||
<div class="p-6 rounded-lg bg-coolGray-100 dark:bg-gray-500 shadow-sm">
|
||||
<div class="flex items-start">
|
||||
<svg class="w-6 h-6 text-blue-500 mt-1 mr-3 flex-shrink-0" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" d="M11.25 11.25l.041-.02a.75.75 0 011.063.852l-.708 2.836a.75.75 0 001.063.853l.041-.021M21 12a9 9 0 11-18 0 9 9 0 0118 0zm-9-3.75h.008v.008H12V8.25z"></path></svg>
|
||||
@@ -101,24 +102,24 @@
|
||||
</div>
|
||||
</section>
|
||||
{% endif %}
|
||||
<form method="post" autocomplete="off">
|
||||
<section>
|
||||
<div class="pl-6 pr-6 pt-0 pb-0 mt-5 h-full overflow-hidden">
|
||||
<form method="post" autocomplete="off">
|
||||
<div class="px-6 py-0 mt-5 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="container mt-5 mx-auto">
|
||||
<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">
|
||||
<div class="w-full mt-6 pb-6 overflow-x-auto">
|
||||
<table class="w-full min-w-max text-sm">
|
||||
<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">
|
||||
<div class="py-3 px-6 rounded-tl-xl bg-coolGray-200 dark:bg-gray-600"> <span class="text-xs text-gray-600 dark:text-gray-300 font-semibold">Wallet</span> </div>
|
||||
<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">Wallet</span> </div>
|
||||
</th>
|
||||
<th class="p-0">
|
||||
<div class="py-3 px-6 rounded-tr-xl bg-coolGray-200 dark:bg-gray-600"> <span class="text-xs text-gray-600 dark:text-gray-300 font-semibold">Details</span> </div>
|
||||
<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">Details</span> </div>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
@@ -129,7 +130,8 @@
|
||||
<span class="inline-block py-1 px-2 rounded-full bg-green-100 text-green-500 dark:bg-gray-500 dark:text-green-500">Pending: +{{ w.pending }} {{ w.ticker }} </span>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr> {% if w.cid == '1' %} {# PART #}
|
||||
</tr>
|
||||
{% if w.cid == '1' %} {# PART #}
|
||||
<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/{{ w.name }}.png" alt="{{ w.name }} Blind"> </span>Blind Balance: </td>
|
||||
<td class="py-3 px-6 bold coinname-value" data-coinname="{{ w.name }}">{{ w.blind_balance }} {{ w.ticker }} (<span class="usd-value"></span>)
|
||||
@@ -147,11 +149,10 @@
|
||||
</td>
|
||||
<td class="usd-value"></td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
{# / PART #}
|
||||
{% if w.cid == '3' %} {# LTC #}
|
||||
{% elif w.cid == '3' %} {# LTC #}
|
||||
<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="pr-3 inline-flex align-middle items-center justify-center w-9 h-10 bg-white-50 rounded"> <img class="h-7" src="/static/images/coins/{{ w.name }}.png" alt="{{ w.name }} MWEB"> </span>MWEB Balance: </td>
|
||||
<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/{{ w.name }}.png" alt="{{ w.name }} MWEB"> </span>MWEB Balance: </td>
|
||||
<td class="py-3 px-6 bold coinname-value" data-coinname="{{ w.name }}">{{ w.mweb_balance }} {{ w.ticker }} (<span class="usd-value"></span>)
|
||||
{% if w.mweb_pending %}
|
||||
<span class="inline-block py-1 px-2 rounded-full bg-green-100 text-green-500 dark:bg-gray-500 dark:text-green-500">Pending: +{{ w.mweb_pending }} {{ w.ticker }} </span>
|
||||
@@ -163,7 +164,7 @@
|
||||
{% if w.locked_utxos %}
|
||||
<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">Locked Outputs:</td>
|
||||
<td id='locked_utxos' class="py-3 px-6">{{ w.locked_utxos }}</td>
|
||||
<td id="locked_utxos" class="py-3 px-6">{{ w.locked_utxos }}</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
{# / locked_utxos #}
|
||||
@@ -176,7 +177,7 @@
|
||||
<td class="py-3 px-6">{{ w.version }}</td>
|
||||
</tr>
|
||||
<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">Blocks:</td>
|
||||
<td class="py-3 px-6 bold">Blockheight:</td>
|
||||
<td class="py-3 px-6">{{ w.blocks }}
|
||||
{% if w.known_block_count %} / {{ w.known_block_count }}
|
||||
{% endif %}
|
||||
@@ -206,10 +207,12 @@
|
||||
</tr>
|
||||
{% endif %}
|
||||
{# / encrypted #}
|
||||
{% if w.expected_seed != true %}
|
||||
<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">Expected Seed:</td>
|
||||
<td class="py-3 px-6">{{ w.expected_seed }}</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
@@ -219,18 +222,19 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</section>
|
||||
{% if block_unknown_seeds and w.expected_seed != true %} {# Only show addresses if wallet seed is correct #}
|
||||
<section class="pl-6 pr-6 pt-0 pb-0 h-full overflow-hidden">
|
||||
<section 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="container mt-5 mx-auto">
|
||||
<div class="pt-6 pb-6 bg-coolGray-100 dark:bg-gray-500 rounded-xl">
|
||||
<div class="lg:container mt-5 mx-auto">
|
||||
<div class="py-6 bg-coolGray-100 dark:bg-gray-500 rounded-xl">
|
||||
<div class="px-6">
|
||||
{% if w.cid != '4' %} {# DCR #}
|
||||
<div class="flex flex-wrap justify-end">
|
||||
<div class="w-full md:w-auto p-1.5"> <input class="flex flex-wrap justify-center w-full 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 cursor-pointer" type="submit" name="reseed_{{ w.cid }}" value="Reseed wallet" onclick="return confirmReseed();"> </div>
|
||||
<div class="w-full md:w-auto p-1.5"> <input class="flex flex-wrap justify-center w-full px-4 py-2.5 font-medium text-lg lg: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 cursor-pointer" type="submit" name="reseed_{{ w.cid }}" value="Reseed wallet" onclick="return confirmReseed();"> </div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
@@ -242,90 +246,53 @@
|
||||
</section>
|
||||
{% else %}
|
||||
<section class="p-6">
|
||||
<div class="lg:container mx-auto">
|
||||
<div class="flex items-center">
|
||||
<h4 class="font-semibold text-2xl text-black dark:text-white">Deposit</h4>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<form method="post" autocomplete="off">
|
||||
<section>
|
||||
<div class="pl-6 pr-6 pt-0 pb-0 overflow-hidden">
|
||||
<div class="px-6 py-0 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="container mt-5 mx-auto">
|
||||
<div class="lg:container mt-5 mx-auto">
|
||||
<div class="pb-6 bg-coolGray-100 dark:bg-gray-500 rounded-xl">
|
||||
<div class="px-6">
|
||||
<div class="container mx-auto">
|
||||
<div class="flex flex-wrap max-w-7xl mx-auto justify-center -m-3">
|
||||
{% if w.cid in '6, 9' %}
|
||||
{# XMR | WOW #}
|
||||
<div class="w-full md:w-1/2 p-3 flex justify-center items-center">
|
||||
<div class="h-full">
|
||||
<div class="flex flex-wrap -m-3">
|
||||
<div class="w-full p-3">
|
||||
<div class="mb-2 qrcode-container flex h-60 justify-center items-center">
|
||||
<div class="qrcode-border flex">
|
||||
{% if w.cid in '6, 9' %}
|
||||
{# XMR | WOW #}
|
||||
<div id="qrcode-monero-main" class="qrcode"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="font-normal bold text-gray-500 text-center dark:text-white mb-5">{{ w.name }} Main Address: </div>
|
||||
<div class="font-normal bold text-gray-500 text-center dark:text-white mb-5">Main Address: </div>
|
||||
<div class="relative flex justify-center items-center">
|
||||
<div data-tooltip-target="tooltip-copy-monero-main" class="input-like-container hover:border-blue-500 bg-gray-50 text-gray-900 appearance-none pr-10 dark:bg-gray-500 dark:text-white border border-gray-300 dark:border-gray-400 dark:text-gray-50 dark:placeholder-gray-400 text-sm rounded-lg outline-none focus:ring-blue-500 focus:border-blue-500 block w-full focus:ring-0" id="monero_main_address">{{ w.main_address }}</div>
|
||||
</div>
|
||||
<div id="tooltip-copy-monero-main" 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>Copy to clipboard</p>
|
||||
<div class="tooltip-arrow" data-popper-arrow></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="w-full md:w-1/2 p-3 flex justify-center items-center">
|
||||
<div class="h-full">
|
||||
<div class="flex flex-wrap -m-3">
|
||||
<div class="w-full p-3">
|
||||
<div class="mb-2 qrcode-container flex h-60 justify-center items-center">
|
||||
<div class="qrcode-border flex">
|
||||
<div id="qrcode-monero-sub" class="qrcode"> </div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="font-normal bold text-gray-500 text-center dark:text-white mb-5">{{ w.name }} Sub Address: </div>
|
||||
<div class="relative flex justify-center items-center">
|
||||
<div data-tooltip-target="tooltip-copy-monero-sub" class="input-like-container hover:border-blue-500 bg-gray-50 text-gray-900 appearance-none pr-10 dark:bg-gray-500 dark:text-white border border-gray-300 dark:border-gray-400 dark:text-gray-50 dark:placeholder-gray-400 text-sm rounded-lg outline-none focus:ring-blue-500 focus:border-blue-500 block w-full focus:ring-0" id="monero_sub_address">{{ w.deposit_address }}</div>
|
||||
</div>
|
||||
<div class="opacity-100 text-gray-500 dark:text-gray-100 flex justify-center items-center">
|
||||
<div class="py-3 px-6 bold mt-5">
|
||||
<button type="submit" class="flex justify-center py-2 px-4 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" name="newaddr_{{ w.cid }}" value="New Subaddress"> {{ circular_arrows_svg }} New {{ w.name }} Deposit Address</button>
|
||||
</div>
|
||||
</div>
|
||||
<div id="tooltip-copy-monero-sub" 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>Copy to clipboard</p>
|
||||
<div class="tooltip-arrow" data-popper-arrow></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div data-tooltip-target="tooltip-copy-monero-main" class="input-like-container hover:border-blue-500 bg-gray-50 text-gray-900 appearance-none pr-10 dark:bg-gray-500 dark:text-white border border-gray-300 dark:border-gray-400 dark:text-gray-50 dark:placeholder-gray-400 text-lg lg:text-sm rounded-lg outline-none focus:ring-blue-500 focus:border-blue-500 block w-full focus:ring-0" id="monero_main_address">{{ w.main_address }}</div>
|
||||
</div>
|
||||
<div id="tooltip-copy-monero-main" role="tooltip" class="inline-block absolute invisible z-10 py-2 px-3 text-lg lg:text-sm font-medium text-white bg-blue-500 rounded-lg shadow-sm opacity-0 transition-opacity duration-300 tooltip">
|
||||
{% else %}
|
||||
<div class="w-full md:w-1/2 p-3 flex justify-center items-center">
|
||||
<div class="h-full">
|
||||
<div class="flex flex-wrap -m-3">
|
||||
<div class="w-full p-3">
|
||||
<div class="mb-2 qrcode-container flex h-60 justify-center items-center">
|
||||
<div class="qrcode-border flex">
|
||||
<div id="qrcode-deposit" class="qrcode"> </div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="font-normal bold text-gray-500 text-center dark:text-white mb-5">{{ w.name }} Deposit Address: </div>
|
||||
<div class="font-normal bold text-gray-500 text-center dark:text-white mb-5">Deposit Address: </div>
|
||||
<div class="relative flex justify-center items-center">
|
||||
<div data-tooltip-target="tooltip-copy-default" class="input-like-container hover:border-blue-500 bg-gray-50 text-gray-900 appearance-none pr-10 dark:bg-gray-500 dark:text-white border border-gray-300 dark:border-gray-400 dark:text-gray-50 dark:placeholder-gray-400 text-sm rounded-lg outline-none focus:ring-blue-500 focus:border-blue-500 block w-full focus:ring-0" id="main_deposit_address">{{ w.deposit_address }}</div>
|
||||
<div data-tooltip-target="tooltip-copy-default" class="input-like-container hover:border-blue-500 bg-gray-50 text-gray-900 appearance-none pr-10 dark:bg-gray-500 dark:text-white border border-gray-300 dark:border-gray-400 dark:text-gray-50 dark:placeholder-gray-400 text-lg lg:text-sm rounded-lg outline-none focus:ring-blue-500 focus:border-blue-500 block w-full focus:ring-0" id="main_deposit_address">{{ w.deposit_address }}</div>
|
||||
</div>
|
||||
<div class="opacity-100 text-gray-500 dark:text-gray-100 flex justify-center items-center">
|
||||
<div class="py-3 px-6 bold mt-5">
|
||||
<button type="submit" class="flex justify-center py-2 px-4 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" name="newaddr_{{ w.cid }}" value="New Deposit Address"> {{ circular_arrows_svg }} New {{ w.name }} Deposit Address </button>
|
||||
</div>
|
||||
</div>
|
||||
<div id="tooltip-copy-default" 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">
|
||||
<div id="tooltip-copy-default" role="tooltip" class="inline-block absolute invisible z-10 py-2 px-3 text-lg lg:text-sm font-medium text-white bg-blue-500 rounded-lg shadow-sm opacity-0 transition-opacity duration-300 tooltip">
|
||||
{% endif %}
|
||||
<p>Copy to clipboard</p>
|
||||
<div class="tooltip-arrow" data-popper-arrow></div>
|
||||
</div>
|
||||
@@ -333,52 +300,58 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if w.cid == '1' %} {# PART #}
|
||||
{% if w.cid in '1, 3, 6, 9' %}
|
||||
{# PART | LTC | XMR | WOW | #}
|
||||
<div class="w-full md:w-1/2 p-3 flex justify-center items-center">
|
||||
<div class="h-full">
|
||||
<div class="flex flex-wrap -m-3">
|
||||
<div class="w-full p-3">
|
||||
<div class="mb-2 qrcode-container flex h-60 justify-center items-center">
|
||||
<div class="qrcode-border flex">
|
||||
{% if w.cid in '6, 9' %}
|
||||
{# XMR | WOW #}
|
||||
<div id="qrcode-monero-sub" class="qrcode"> </div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="font-normal bold text-gray-500 text-center dark:text-white mb-5">Subaddress: </div>
|
||||
<div class="relative flex justify-center items-center">
|
||||
<div data-tooltip-target="tooltip-copy-monero-sub" class="input-like-container hover:border-blue-500 bg-gray-50 text-gray-900 appearance-none pr-10 dark:bg-gray-500 dark:text-white border border-gray-300 dark:border-gray-400 dark:text-gray-50 dark:placeholder-gray-400 text-lg lg:text-sm rounded-lg outline-none focus:ring-blue-500 focus:border-blue-500 block w-full focus:ring-0" id="monero_sub_address">{{ w.deposit_address }}</div>
|
||||
</div>
|
||||
<div class="opacity-100 text-gray-500 dark:text-gray-100 flex justify-center items-center">
|
||||
<div class="py-3 px-6 bold mt-5">
|
||||
<button type="submit" class="flex justify-center py-2 px-4 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" name="newaddr_{{ w.cid }}" value="New Subaddress"> {{ circular_arrows_svg }} New {{ w.name }} Deposit Address</button>
|
||||
</div>
|
||||
</div>
|
||||
<div id="tooltip-copy-monero-sub" role="tooltip" class="inline-block absolute invisible z-10 py-2 px-3 text-lg lg:text-sm font-medium text-white bg-blue-500 rounded-lg shadow-sm opacity-0 transition-opacity duration-300 tooltip">
|
||||
{% elif w.cid == '1' %}
|
||||
{# PART #}
|
||||
<div id="qrcode-stealth" class="qrcode"> </div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="font-normal bold text-gray-500 text-center dark:text-white mb-5">{{ w.name }} Stealth Address: </div>
|
||||
<div class="font-normal bold text-gray-500 text-center dark:text-white mb-5">Stealth Address: </div>
|
||||
<div class="relative flex justify-center items-center">
|
||||
<div data-tooltip-target="tooltip-copy-particl-stealth" class="input-like-container hover:border-blue-500 bg-gray-50 text-gray-900 appearance-none pr-10 dark:bg-gray-500 dark:text-white border border-gray-300 dark:border-gray-400 dark:text-gray-50 dark:placeholder-gray-400 text-sm rounded-lg outline-none focus:ring-blue-500 focus:border-blue-500 block w-full p-10 focus:ring-0" id="stealth_address"> {{ w.stealth_address }}
|
||||
</div>
|
||||
</div>
|
||||
<div id="tooltip-copy-particl-stealth" 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>Copy to clipboard</p>
|
||||
<div class="tooltip-arrow" data-popper-arrow></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div data-tooltip-target="tooltip-copy-particl-stealth" class="input-like-container hover:border-blue-500 bg-gray-50 text-gray-900 appearance-none pr-10 dark:bg-gray-500 dark:text-white border border-gray-300 dark:border-gray-400 dark:text-gray-50 dark:placeholder-gray-400 text-lg lg:text-sm rounded-lg outline-none focus:ring-blue-500 focus:border-blue-500 block w-full p-10 focus:ring-0" id="stealth_address"> {{ w.stealth_address }}
|
||||
</div>
|
||||
<div id="tooltip-copy-particl-stealth" role="tooltip" class="inline-block absolute invisible z-10 py-2 px-3 text-lg lg:text-sm font-medium text-white bg-blue-500 rounded-lg shadow-sm opacity-0 transition-opacity duration-300 tooltip">
|
||||
{# / PART #}
|
||||
{% elif w.cid == '3' %}
|
||||
{# LTC #}
|
||||
<div class="w-full md:w-1/2 p-3 flex justify-center items-center">
|
||||
<div class="h-full">
|
||||
<div class="flex flex-wrap -m-3">
|
||||
<div class="w-full p-3">
|
||||
<div class="mb-2 qrcode-container flex h-60 justify-center items-center">
|
||||
<div class="qrcode-border flex">
|
||||
<div id="qrcode-mweb" class="qrcode"> </div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="font-normal bold text-gray-500 text-center dark:text-white mb-5">{{ w.name }} MWEB Address: </div>
|
||||
<div class="font-normal bold text-gray-500 text-center dark:text-white mb-5">MWEB Address: </div>
|
||||
<div class="text-center relative">
|
||||
<div data-tooltip-target="tooltip-copy-litecoin-mweb" class="input-like-container hover:border-blue-500 bg-gray-50 text-gray-900 appearance-none pr-10 dark:bg-gray-500 dark:text-white border border-gray-300 dark:border-gray-400 dark:text-gray-50 dark:placeholder-gray-400 text-sm rounded-lg outline-none focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 focus:ring-0" id="stealth_address">{{ w.mweb_address }}</div>
|
||||
<div data-tooltip-target="tooltip-copy-litecoin-mweb" class="input-like-container hover:border-blue-500 bg-gray-50 text-gray-900 appearance-none pr-10 dark:bg-gray-500 dark:text-white border border-gray-300 dark:border-gray-400 dark:text-gray-50 dark:placeholder-gray-400 text-lg lg:text-sm rounded-lg outline-none focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 focus:ring-0" id="stealth_address">{{ w.mweb_address }}</div>
|
||||
<span class="absolute inset-y-0 right-0 flex items-center pr-3 cursor-pointer" id="copyIcon"></span>
|
||||
</div>
|
||||
<div class="opacity-100 text-gray-500 dark:text-gray-100 flex justify-center items-center">
|
||||
<div class="py-3 px-6 bold mt-5">
|
||||
<button type="submit" class="flex justify-center py-2 px-4 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" name="newmwebaddr_{{ w.cid }}" value="New MWEB Address"> {{ circular_arrows_svg }} New MWEB Address </button>
|
||||
</div>
|
||||
<div id="tooltip-copy-litecoin-mweb" 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">
|
||||
</div>
|
||||
<div id="tooltip-copy-litecoin-mweb" role="tooltip" class="inline-block absolute invisible z-10 py-2 px-3 text-lg lg:text-sm font-medium text-white bg-blue-500 rounded-lg shadow-sm opacity-0 transition-opacity duration-300 tooltip">
|
||||
{# / LTC #}
|
||||
{% endif %}
|
||||
<p>Copy to clipboard</p>
|
||||
<div class="tooltip-arrow" data-popper-arrow></div>
|
||||
</div>
|
||||
@@ -386,9 +359,8 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{# / LTC #}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -410,8 +382,7 @@
|
||||
correctLevel: QRCode.CorrectLevel.L
|
||||
});
|
||||
</script>
|
||||
{% endif %}
|
||||
{% if w.cid == '3' %}
|
||||
{% elif w.cid == '3' %}
|
||||
{# LTC #}
|
||||
<script>
|
||||
// Litecoin MWEB
|
||||
@@ -529,61 +500,67 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
});
|
||||
</script>
|
||||
<section class="p-6">
|
||||
<div class="lg:container mx-auto">
|
||||
<div class="flex items-center">
|
||||
<h4 class="font-semibold text-2xl text-black dark:text-white">Withdraw</h4>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section>
|
||||
<div class="pl-6 pr-6 pt-0 pb-0 h-full overflow-hidden">
|
||||
<div class="px-6 py-0 h-full overflow-hidden">
|
||||
<div class="border-coolGray-100">
|
||||
<div class="flex flex-wrap items-center justify-between -m-2">
|
||||
<div class="w-full pt-2">
|
||||
<div class="container mt-5 mx-auto">
|
||||
<div class="pt-6 pb-6 bg-coolGray-100 dark:bg-gray-500 rounded-xl">
|
||||
<div class="lg:container mt-5 mx-auto">
|
||||
<div class="py-6 bg-coolGray-100 dark:bg-gray-500 rounded-xl">
|
||||
<div class="px-6">
|
||||
<div class="w-full mt-6 pb-6 overflow-x-auto">
|
||||
<table class="w-full min-w-max text-sm">
|
||||
<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">
|
||||
<div class="py-3 px-6 rounded-tl-xl bg-coolGray-200 dark:bg-gray-600"> <span class="text-xs text-gray-600 dark:text-gray-300 font-semibold">Options</span> </div>
|
||||
<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">Options</span> </div>
|
||||
</th>
|
||||
<th class="p-0">
|
||||
<div class="py-3 px-6 rounded-tr-xl bg-coolGray-200 dark:bg-gray-600"> <span class="text-xs text-gray-600 dark:text-gray-300 font-semibold">Input</span> </div>
|
||||
<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">Input</span> </div>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tr class="opacity-100 text-gray-500 dark:text-gray-100">
|
||||
<td class="py-4 pl-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/{{ w.name }}.png" alt="{{ w.name }}"> </span> {{ w.name }} Balance: </td>
|
||||
<td class="py-4 pl-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/{{ w.name }}.png" alt="{{ w.name }}"> </span>Balance: </td>
|
||||
<td class="py-3 px-6" data-coinname="{{ w.name }}">{{ w.balance }} {{ w.ticker }} </td>
|
||||
</tr>
|
||||
{% if w.cid == '3' %}
|
||||
{# LTC #}
|
||||
<tr class="opacity-100 text-gray-500 dark:text-gray-100">
|
||||
<td class="py-4 pl-6 bold w-1/4"> <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/{{ w.name }}.png" alt="{{ w.name }}"> </span> {{ w.name }} MWEB Balance: </td>
|
||||
<td class="py-4 pl-6 bold w-1/4"> <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/{{ w.name }}.png" alt="{{ w.name }}"> </span>MWEB Balance: </td>
|
||||
<td class="py-3 px-6" data-coinname="{{ w.name }}">{{ w.mweb_balance }} {{ w.ticker }} </td>
|
||||
{% endif %}
|
||||
{% if w.cid == '1' %}
|
||||
</tr>
|
||||
{% elif w.cid == '1' %}
|
||||
{# PART #}
|
||||
<tr class="opacity-100 text-gray-500 dark:text-gray-100">
|
||||
<td class="py-4 pl-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/{{ w.name }}.png" alt="{{ w.name }}"> </span> {{ w.name }} Blind Balance: </td>
|
||||
<td class="py-4 pl-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/{{ w.name }}.png" alt="{{ w.name }}"> </span>Blind Balance: </td>
|
||||
<td class="py-3 px-6" data-coinname="{{ w.name }}">{{ w.blind_balance }} {{ w.ticker }} </td>
|
||||
</tr>
|
||||
<tr class="opacity-100 text-gray-500 dark:text-gray-100">
|
||||
<td class="py-4 pl-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/{{ w.name }}.png" alt="{{ w.name }}"> </span> {{ w.name }} Anon Balance: </td>
|
||||
<td class="py-3 px-6" data-coinname="{{ w.name }}">{{ w.anon_balance }} {{ w.ticker }} </td> {% endif %}
|
||||
<td class="py-4 pl-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/{{ w.name }}.png" alt="{{ w.name }}"> </span>Anon Balance: </td>
|
||||
<td class="py-3 px-6" data-coinname="{{ w.name }}">{{ w.anon_balance }} {{ w.ticker }} </td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
<tr class="opacity-100 text-gray-500 dark:text-gray-100">
|
||||
<td class="py-4 pl-6 bold"> {{ w.name }} Address: </td>
|
||||
<td class="py-3 px-6"> <input placeholder="{{ w.ticker }} Address" class="hover:border-blue-500 bg-gray-50 text-gray-900 appearance-none pr-10 dark:bg-gray-500 dark:text-white border border-gray-300 dark:border-gray-400 dark:text-gray-50 dark:placeholder-gray-400 text-sm rounded-lg outline-none focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 focus:ring-0" type="text" name="to_{{ w.cid }}" value="{{ w.wd_address }}"> </td>
|
||||
<td class="py-3 px-6"> <input placeholder="{{ w.ticker }} Address" class="hover:border-blue-500 bg-gray-50 text-gray-900 appearance-none pr-10 dark:bg-gray-500 dark:text-white border border-gray-300 dark:border-gray-400 dark:text-gray-50 dark:placeholder-gray-400 text-lg lg:text-sm rounded-lg outline-none focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 focus:ring-0" type="text" name="to_{{ w.cid }}" value="{{ w.wd_address }}"> </td>
|
||||
</tr>
|
||||
<tr class="opacity-100 text-gray-500 dark:text-gray-100">
|
||||
<td class="py-4 pl-6 bold"> {{ w.name }} Amount:
|
||||
<td class="py-3 px-6">
|
||||
<div class="flex"> <input placeholder="{{ w.ticker }} Amount" class="hover:border-blue-500 bg-gray-50 text-gray-900 appearance-none pr-10 dark:bg-gray-500 dark:text-white border border-gray-300 dark:border-gray-400 dark:text-gray-50 dark:placeholder-gray-400 text-sm rounded-lg outline-none focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 focus:ring-0" type="text" id="amount" name="amt_{{ w.cid }}" value="{{ w.wd_value }}">
|
||||
<div class="flex"> <input placeholder="{{ w.ticker }} Amount" class="hover:border-blue-500 bg-gray-50 text-gray-900 appearance-none pr-10 dark:bg-gray-500 dark:text-white border border-gray-300 dark:border-gray-400 dark:text-gray-50 dark:placeholder-gray-400 text-lg lg:text-sm rounded-lg outline-none focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 focus:ring-0" type="text" id="amount" name="amt_{{ w.cid }}" value="{{ w.wd_value }}">
|
||||
<div class="ml-2 flex">
|
||||
{% if w.cid == '1' %}
|
||||
{# PART #}
|
||||
<button type="button" class="py-1 px-2 bg-blue-500 text-white text-sm rounded-md focus:outline-none" onclick="setAmount(0.25, '{{ w.balance }}', {{ w.cid }}, '{{ w.blind_balance }}', '{{ w.anon_balance }}')">25%</button>
|
||||
<button type="button" class="ml-2 py-1 px-2 bg-blue-500 text-white text-sm rounded-md focus:outline-none" onclick="setAmount(0.5, '{{ w.balance }}', {{ w.cid }}, '{{ w.blind_balance }}', '{{ w.anon_balance }}')">50%</button>
|
||||
<button type="button" class="ml-2 py-1 px-2 bg-blue-500 text-white text-sm rounded-md focus:outline-none" onclick="setAmount(1, '{{ w.balance }}', {{ w.cid }}, '{{ w.blind_balance }}', '{{ w.anon_balance }}')">100%</button>
|
||||
<button type="button" class="hidden md:block py-1 px-2 bg-blue-500 text-white text-lg lg:text-sm rounded-md focus:outline-none" onclick="setAmount(0.25, '{{ w.balance }}', {{ w.cid }}, '{{ w.blind_balance }}', '{{ w.anon_balance }}')">25%</button>
|
||||
<button type="button" class="hidden md:block ml-2 py-1 px-2 bg-blue-500 text-white text-lg lg:text-sm rounded-md focus:outline-none" onclick="setAmount(0.5, '{{ w.balance }}', {{ w.cid }}, '{{ w.blind_balance }}', '{{ w.anon_balance }}')">50%</button>
|
||||
<button type="button" class="ml-2 py-1 px-2 bg-blue-500 text-white text-lg lg:text-sm rounded-md focus:outline-none" onclick="setAmount(1, '{{ w.balance }}', {{ w.cid }}, '{{ w.blind_balance }}', '{{ w.anon_balance }}')">100%</button>
|
||||
<script>
|
||||
function setAmount(percent, balance, cid, blindBalance, anonBalance) {
|
||||
var amountInput = document.getElementById('amount');
|
||||
@@ -618,9 +595,9 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
{# / PART #}
|
||||
{% elif w.cid == '3' %}
|
||||
{# LTC #}
|
||||
<button type="button" class="py-1 px-2 bg-blue-500 text-white text-sm rounded-md focus:outline-none" onclick="setAmount(0.25, '{{ w.balance }}', {{ w.cid }}, '{{ w.mweb_balance }}')">25%</button>
|
||||
<button type="button" class="ml-2 py-1 px-2 bg-blue-500 text-white text-sm rounded-md focus:outline-none" onclick="setAmount(0.5, '{{ w.balance }}', {{ w.cid }}, '{{ w.mweb_balance }}')">50%</button>
|
||||
<button type="button" class="ml-2 py-1 px-2 bg-blue-500 text-white text-sm rounded-md focus:outline-none" onclick="setAmount(1, '{{ w.balance }}', {{ w.cid }}, '{{ w.mweb_balance }}')">100%</button>
|
||||
<button type="button" class="hidden md:block py-1 px-2 bg-blue-500 text-white text-lg lg:text-sm rounded-md focus:outline-none" onclick="setAmount(0.25, '{{ w.balance }}', {{ w.cid }}, '{{ w.mweb_balance }}')">25%</button>
|
||||
<button type="button" class="hidden md:block ml-2 py-1 px-2 bg-blue-500 text-white text-lg lg:text-sm rounded-md focus:outline-none" onclick="setAmount(0.5, '{{ w.balance }}', {{ w.cid }}, '{{ w.mweb_balance }}')">50%</button>
|
||||
<button type="button" class="ml-2 py-1 px-2 bg-blue-500 text-white text-lg lg:text-sm rounded-md focus:outline-none" onclick="setAmount(1, '{{ w.balance }}', {{ w.cid }}, '{{ w.mweb_balance }}')">100%</button>
|
||||
<script>
|
||||
function setAmount(percent, balance, cid, mwebBalance) {
|
||||
var amountInput = document.getElementById('amount');
|
||||
@@ -651,9 +628,9 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
</script>
|
||||
{# / LTC #}
|
||||
{% else %}
|
||||
<button type="button" class="py-1 px-2 bg-blue-500 text-white text-sm rounded-md focus:outline-none" onclick="setAmount(0.25, '{{ w.balance }}', {{ w.cid }})">25%</button>
|
||||
<button type="button" class="ml-2 py-1 px-2 bg-blue-500 text-white text-sm rounded-md focus:outline-none" onclick="setAmount(0.5, '{{ w.balance }}', {{ w.cid }})">50%</button>
|
||||
<button type="button" class="ml-2 py-1 px-2 bg-blue-500 text-white text-sm rounded-md focus:outline-none" onclick="setAmount(1, '{{ w.balance }}', {{ w.cid }})">100%</button>
|
||||
<button type="button" class="hidden md:block py-1 px-2 bg-blue-500 text-white text-lg lg:text-sm rounded-md focus:outline-none" onclick="setAmount(0.25, '{{ w.balance }}', {{ w.cid }})">25%</button>
|
||||
<button type="button" class="hidden md:block ml-2 py-1 px-2 bg-blue-500 text-white text-lg lg:text-sm rounded-md focus:outline-none" onclick="setAmount(0.5, '{{ w.balance }}', {{ w.cid }})">50%</button>
|
||||
<button type="button" class="ml-2 py-1 px-2 bg-blue-500 text-white text-lg lg:text-sm rounded-md focus:outline-none" onclick="setAmount(1, '{{ w.balance }}', {{ w.cid }})">100%</button>
|
||||
<script>
|
||||
function setAmount(percent, balance, cid) {
|
||||
var amountInput = document.getElementById('amount');
|
||||
@@ -701,18 +678,20 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="opacity-100 text-gray-500 dark:text-gray-100">
|
||||
{% if w.cid in '6, 9' %} {# XMR | WOW #}
|
||||
<td class="py-3 px-6 bold">Sweep All:</td>
|
||||
<td class="py-3 px-6">
|
||||
<input class="hover:border-blue-500 w-5 h-5 form-check-input text-blue-600 bg-gray-50 border-gray-300 rounded focus:ring-blue-500 dark:focus:ring-blue-600 dark:ring-offset-gray-800 focus:ring-1 dark:bg-gray-500 dark:border-gray-400" type="checkbox" id="sweepall" name="sweepall_{{ w.cid }}" {% if w.wd_sweepall==true %} checked=checked{% endif %}>
|
||||
<td class="hidden py-3 px-6 bold">Sweep All:</td>
|
||||
<td class="hidden py-3 px-6">
|
||||
<input class="hover:border-blue-500 w-5 h-5 form-check-input text-blue-600 bg-gray-50 border-gray-300 rounded focus:ring-blue-500 dark:focus:ring-blue-600 dark:ring-offset-gray-800 focus:ring-1 dark:bg-gray-500 dark:border-gray-400" type="checkbox" id="sweepall" name="sweepall_{{ w.cid }}" {% if w.wd_sweepall==true %} checked="checked"{% endif %}>
|
||||
</td>
|
||||
{% else %}
|
||||
<td class="py-3 px-6 bold">Subtract Fee:</td>
|
||||
<td class="py-3 px-6">
|
||||
<input class="hover:border-blue-500 w-5 h-5 form-check-input text-blue-600 bg-gray-50 border-gray-300 rounded focus:ring-blue-500 dark:focus:ring-blue-600 dark:ring-offset-gray-800 focus:ring-1 dark:bg-gray-500 dark:border-gray-400" type="checkbox" name="subfee_{{ w.cid }}" {% if w.wd_subfee==true %} checked=checked{% endif %}>
|
||||
<input class="hover:border-blue-500 w-5 h-5 form-check-input text-blue-600 bg-gray-50 border-gray-300 rounded focus:ring-blue-500 dark:focus:ring-blue-600 dark:ring-offset-gray-800 focus:ring-1 dark:bg-gray-500 dark:border-gray-400" type="checkbox" name="subfee_{{ w.cid }}" {% if w.wd_subfee==true %} checked="checked"{% endif %}>
|
||||
</td>
|
||||
{% endif %}
|
||||
<td>
|
||||
@@ -746,9 +725,8 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
{# / PART #}
|
||||
{% if w.cid == '3' %} {# LTC #}
|
||||
{% elif w.cid == '3' %} {# LTC #}
|
||||
<tr class="opacity-100 text-gray-500 dark:text-gray-100">
|
||||
<td class="py-3 px-6 bold">Type From:</td>
|
||||
<td class="py-3 px-6">
|
||||
@@ -762,14 +740,16 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
</tr>
|
||||
{% endif %}
|
||||
{# / LTC #}
|
||||
{% if w.cid not in '6,9' %} {# Not XMR WOW #}
|
||||
<tr class="opacity-100 text-gray-500 dark:text-gray-100">
|
||||
<td class="py-3 px-6 bold">Fee Rate:</td>
|
||||
<td class="py-3 px-6">{{ w.fee_rate }}</td>
|
||||
</tr>
|
||||
<tr class="opacity-100 text-gray-500 dark:text-gray-100">
|
||||
<td class="py-3 px-6 bold">Estimate Fee:</td>
|
||||
<td class="py-3 px-6 bold">Fee Estimate:</td>
|
||||
<td class="py-3 px-6"> {{ w.est_fee }} </td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
@@ -781,25 +761,24 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
</div>
|
||||
</section>
|
||||
<section>
|
||||
<div class="pl-6 pr-6 pt-0 pb-0 h-full overflow-hidden ">
|
||||
<div class="px-6 py-0 h-full overflow-hidden">
|
||||
<div class="pb-6 ">
|
||||
<div class="flex flex-wrap items-center justify-between -m-2">
|
||||
<div class="w-full pt-2">
|
||||
<div class="container mx-auto">
|
||||
<div class="pt-6 pb-6 bg-coolGray-100 border-t border-gray-100 dark:border-gray-400 dark:bg-gray-500 rounded-bl-xl rounded-br-xl">
|
||||
<div class="lg:container mx-auto">
|
||||
<div class="py-6 bg-coolGray-100 border-t border-gray-100 dark:border-gray-400 dark:bg-gray-500 rounded-bl-xl rounded-br-xl">
|
||||
<div class="px-6">
|
||||
<div class="flex flex-wrap justify-end">
|
||||
{% if w.cid not in '6, 9' %}
|
||||
{# !XMR | WOW #}
|
||||
{% if w.show_utxo_groups %}
|
||||
{% else %}
|
||||
<div class="w-full md:w-auto p-1.5"> <button type="submit" class="flex flex-wrap justify-center 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" id="showutxogroups" name="showutxogroups" value="Show UTXO Groups"> {{ utxo_groups_svg | safe }} Show UTXO Groups </button> </div>
|
||||
{% endif %} {% endif %}
|
||||
{% if w.cid in '6, 9' %}
|
||||
{# XMR | WOW #}
|
||||
<div class="w-full md:w-auto p-1.5 ml-2"> <button type="submit" class="flex flex-wrap justify-center w-full 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" name="estfee_{{ w.cid }}" value="Estimate Fee">Estimate {{ w.ticker }} Fee </button> </div>
|
||||
<div class="w-full md:w-auto p-1.5 mx-1"> <button type="submit" class="flex flex-wrap justify-center w-full px-4 py-2.5 bg-blue-500 hover:bg-blue-600 font-medium text-lg lg:text-sm text-white border border-blue-500 rounded-md shadow-button focus:ring-0 focus:outline-none" name="estfee_{{ w.cid }}" value="Estimate Fee">Estimate {{ w.ticker }} Fee </button> </div>
|
||||
{# / XMR | WOW #}
|
||||
{% elif w.show_utxo_groups %}
|
||||
{% else %}
|
||||
<div class="w-full md:w-auto p-1.5 mx-1"> <button type="submit" class="flex flex-wrap justify-center w-full px-4 py-2.5 bg-blue-500 hover:bg-blue-600 font-medium text-lg lg:text-sm text-white border border-blue-500 rounded-md shadow-button focus:ring-0 focus:outline-none" id="showutxogroups" name="showutxogroups" value="Show UTXO Groups"> {{ utxo_groups_svg | safe }} Show UTXO Groups </button> </div>
|
||||
{% endif %}
|
||||
{# / XMR | WOW #} <div class="w-full md:w-auto p-1.5 ml-2"> <button type="submit" class="flex flex-wrap justify-center w-full 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" name="withdraw_{{ w.cid }}" value="Withdraw" onclick="return confirmWithdrawal();">{{ withdraw_svg | safe }} Withdraw {{ w.ticker }} </div>
|
||||
<div class="w-full md:w-auto p-1.5 mx-1"> <button type="submit" class="flex flex-wrap justify-center w-full px-4 py-2.5 bg-blue-500 hover:bg-blue-600 font-medium text-lg lg:text-sm text-white border border-blue-500 rounded-md shadow-button focus:ring-0 focus:outline-none" name="withdraw_{{ w.cid }}" value="Withdraw" onclick="return confirmWithdrawal();">{{ withdraw_svg | safe }} Withdraw {{ w.ticker }} </button></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -808,41 +787,41 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
{% if w.cid not in '6, 9' %}
|
||||
{# !XMR | WOW #}
|
||||
{% if w.show_utxo_groups %}
|
||||
<section class="p-6">
|
||||
<div class="lg:container mx-auto">
|
||||
<div class="flex items-center">
|
||||
<h4 class="font-semibold text-2xl text-black dark:text-white">UTXO Groups</h4>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section>
|
||||
<div class="pl-6 pr-6 pt-0 pb-0 h-full overflow-hidden">
|
||||
<div class="px-6 py-0 h-full overflow-hidden">
|
||||
<div class="border-coolGray-100">
|
||||
<div class="flex flex-wrap items-center justify-between -m-2">
|
||||
<div class="w-full pt-2">
|
||||
<div class="container mt-5 mx-auto">
|
||||
<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">
|
||||
<div class="w-full mt-6 pb-6 overflow-x-auto">
|
||||
<table class="w-full min-w-max text-sm">
|
||||
<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">
|
||||
<div class="py-3 px-6 rounded-tl-xl bg-coolGray-200 dark:bg-gray-600"> <span class="text-xs text-gray-600 dark:text-gray-300 font-semibold">Options</span> </div>
|
||||
<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">Options</span> </div>
|
||||
</th>
|
||||
<th class="p-0">
|
||||
<div class="py-3 px-6 rounded-tr-xl bg-coolGray-200 dark:bg-gray-600"> <span class="text-xs text-gray-600 dark:text-gray-300 font-semibold p-10"></span> </div>
|
||||
<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 p-10"></span> </div>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tr class="opacity-100 text-gray-500 dark:text-gray-100">
|
||||
<td class="py-3 px-6 w-1/4 bold">UTXO Groups:</td>
|
||||
<td class="py-3 px-6"> <textarea class="hover:border-blue-500 bg-gray-50 text-gray-900 appearance-none pr-10 dark:bg-gray-500 dark:text-white border border-gray-300 dark:border-gray-400 dark:text-gray-50 dark:placeholder-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" id="tx_view" rows="10" readonly>{{ w.utxo_groups }} </textarea> </td>
|
||||
<td class="py-3 px-6"> <textarea class="hover:border-blue-500 bg-gray-50 text-gray-900 appearance-none pr-10 dark:bg-gray-500 dark:text-white border border-gray-300 dark:border-gray-400 dark:text-gray-50 dark:placeholder-gray-50 text-lg lg:text-sm rounded-lg outline-none focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 focus:ring-0" id="tx_view" rows="10" readonly>{{ w.utxo_groups }} </textarea> </td>
|
||||
</tr>
|
||||
<tr class="opacity-100 text-gray-500 dark:text-gray-100">
|
||||
<td class="py-3 px-6"> <button type="submit" class="flex flex-wrap justify-center px-4 py-2 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" id="create_utxo" name="create_utxo" value="Create UTXO" onclick="return confirmUTXOResize();"> {{ create_utxo_svg | safe }}Create UTXO </button> </td>
|
||||
<td class="py-3 px-6"> <input placeholder="Amount" class="hover:border-blue-500 bg-gray-50 text-gray-900 appearance-none pr-10 dark:bg-gray-500 dark:text-white border border-gray-300 dark:border-gray-400 dark:text-gray-50 dark:placeholder-gray-400 text-sm rounded-lg outline-none focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 focus:ring-0" type="text" name="utxo_value" value="{{ w.utxo_value }}"> </td>
|
||||
<td class="py-3 px-6"> <button type="submit" class="flex flex-wrap justify-center px-4 py-2 bg-blue-500 hover:bg-blue-600 font-medium text-lg lg:text-sm text-white border border-blue-500 rounded-md shadow-button focus:ring-0 focus:outline-none" id="create_utxo" name="create_utxo" value="Create UTXO" onclick="return confirmUTXOResize();"> {{ create_utxo_svg | safe }}Create UTXO </button> </td>
|
||||
<td class="py-3 px-6"> <input placeholder="Amount" class="hover:border-blue-500 bg-gray-50 text-gray-900 appearance-none pr-10 dark:bg-gray-500 dark:text-white border border-gray-300 dark:border-gray-400 dark:text-gray-50 dark:placeholder-gray-400 text-lg lg:text-sm rounded-lg outline-none focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 focus:ring-0" type="text" name="utxo_value" value="{{ w.utxo_value }}"> </td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
@@ -855,14 +834,14 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
</div>
|
||||
</section>
|
||||
<section>
|
||||
<div class="pl-6 pr-6 pt-0 pb-0 h-full overflow-hidden ">
|
||||
<div class="px-6 py-0 h-full overflow-hidden ">
|
||||
<div class="pb-6 ">
|
||||
<div class="flex flex-wrap items-center justify-between -m-2">
|
||||
<div class="w-full pt-2">
|
||||
<div class="container mx-auto">
|
||||
<div class="pt-6 pb-6 bg-coolGray-100 border-t border-gray-100 dark:border-gray-400 dark:bg-gray-500 rounded-bl-xl rounded-br-xl">
|
||||
<div class="lg:container mx-auto">
|
||||
<div class="py-6 bg-coolGray-100 border-t border-gray-100 dark:border-gray-400 dark:bg-gray-500 rounded-bl-xl rounded-br-xl">
|
||||
<div class="px-6">
|
||||
<div class="flex flex-wrap justify-end"> <button type="submit" class="flex flex-wrap justify-center 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" id="closeutxogroups" name="closeutxogroups" value="Close UTXO Groups"> {{ utxo_groups_svg | safe }} Close UTXO Groups </button> </div>
|
||||
<div class="flex flex-wrap justify-end"> <button type="submit" class="flex flex-wrap justify-center px-4 py-2.5 bg-blue-500 hover:bg-blue-600 font-medium text-lg lg:text-sm text-white border border-blue-500 rounded-md shadow-button focus:ring-0 focus:outline-none" id="closeutxogroups" name="closeutxogroups" value="Close UTXO Groups"> {{ utxo_groups_svg | safe }} Close UTXO Groups </button> </div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -876,7 +855,6 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
<input type="hidden" name="formid" value="{{ form_id }}">
|
||||
</form>
|
||||
<script>
|
||||
|
||||
@@ -131,9 +131,9 @@ def parseOfferFormData(swap_client, form_data, page_data, options={}):
|
||||
parsed_data["amt_bid_min"] < 0
|
||||
or parsed_data["amt_bid_min"] > parsed_data["amt_from"]
|
||||
):
|
||||
errors.append("Minimum Bid Amount out of range")
|
||||
errors.append("Minimum Purchase Quantity out of range")
|
||||
except Exception:
|
||||
errors.append("Minimum Bid Amount")
|
||||
errors.append("Minimum Purchase Quantity")
|
||||
|
||||
if have_data_entry(form_data, "rate") and not have_data_entry(form_data, "amt_to"):
|
||||
parsed_data["rate"] = ci_to.make_int(form_data["rate"], r=1)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
pyzmq==26.2.0
|
||||
python-gnupg==0.5.3
|
||||
python-gnupg==0.5.4
|
||||
Jinja2==3.1.5
|
||||
pycryptodome==3.21.0
|
||||
PySocks==1.7.1
|
||||
|
||||
@@ -190,9 +190,9 @@ pysocks==1.7.1 \
|
||||
--hash=sha256:2725bd0a9925919b9b51739eea5f9e2bae91e83288108a9ad338b2e3a4435ee5 \
|
||||
--hash=sha256:3f8804571ebe159c380ac6de37643bb4685970655d3bba243530d6558b799aa0
|
||||
# via -r requirements.in
|
||||
python-gnupg==0.5.3 \
|
||||
--hash=sha256:290d8ddb9cd63df96cfe9284b9b265f19fd6e145e5582dc58fd7271f026d0a47 \
|
||||
--hash=sha256:2f8a4c6f63766feca6cc1416408f8b84e1b914fe7b54514e570fc5cbe92e9248
|
||||
python-gnupg==0.5.4 \
|
||||
--hash=sha256:40ce25cde9df29af91fe931ce9df3ce544e14a37f62b13ca878c897217b2de6c \
|
||||
--hash=sha256:f2fdb5fb29615c77c2743e1cb3d9314353a6e87b10c37d238d91ae1c6feae086
|
||||
# via -r requirements.in
|
||||
pyzmq==26.2.0 \
|
||||
--hash=sha256:007137c9ac9ad5ea21e6ad97d3489af654381324d5d3ba614c323f60dab8fae6 \
|
||||
|
||||
@@ -31,7 +31,7 @@ Create offers
|
||||
# Optional
|
||||
"enabled": Set to false to ignore offer template.
|
||||
"swap_type": Type of swap, defaults to "adaptor_sig"
|
||||
"min_swap_amount": Sets "amt_bid_min" on the offer, minimum valid bid when offer amount is variable.
|
||||
"min_swap_amount": Sets "amt_bid_min" on the offer, minimum purchase quantity when offer amount is variable.
|
||||
"amount_step": If set offers will be created for amount values between "amount" and "min_coin_from_amt" in decrements of "amount_step".
|
||||
},
|
||||
...
|
||||
|
||||
Reference in New Issue
Block a user