Working on ui
This commit is contained in:
parent
6f46fce625
commit
f10ee66972
|
@ -418,6 +418,8 @@ small {
|
|||
border-radius: var(--border-radius);
|
||||
filter: grayscale(100%);
|
||||
transition: filter 0.3s ease;
|
||||
object-fit: cover;
|
||||
object-position: center;
|
||||
}
|
||||
|
||||
#thrower-banner:hover {
|
||||
|
@ -431,6 +433,8 @@ small {
|
|||
border: var(--border-width) solid var(--primary-color);
|
||||
filter: grayscale(100%);
|
||||
transition: filter 0.3s ease;
|
||||
object-fit: cover;
|
||||
object-position: center;
|
||||
}
|
||||
|
||||
#thrower-icon:hover {
|
||||
|
@ -449,6 +453,8 @@ small {
|
|||
border: var(--border-width) solid var(--primary-color);
|
||||
filter: grayscale(100%);
|
||||
transition: filter 0.3s ease;
|
||||
object-fit: cover;
|
||||
object-position: center;
|
||||
}
|
||||
|
||||
#profile-picture:hover {
|
||||
|
|
|
@ -542,7 +542,7 @@
|
|||
<div class="thrower-details-section collapsed" id="details-${index}">
|
||||
${thrower.icon ? `
|
||||
<div style="margin-bottom: 15px;">
|
||||
<img src="${thrower.icon}" style="width: 50px; height: 50px; border-radius: var(--border-radius); border: var(--border-width) solid var(--primary-color); filter: grayscale(100%); transition: filter 0.3s ease;" onmouseover="this.style.filter='grayscale(0%) saturate(50%)'" onmouseout="this.style.filter='grayscale(100%)'">
|
||||
<img src="${thrower.icon}" style="width: 50px; height: 50px; border-radius: var(--border-radius); border: var(--border-width) solid var(--primary-color); filter: grayscale(100%); transition: filter 0.3s ease; object-fit: cover; object-position: center;" onmouseover="this.style.filter='grayscale(0%) saturate(50%)'" onmouseout="this.style.filter='grayscale(100%)'">
|
||||
</div>
|
||||
` : ''}
|
||||
|
||||
|
@ -762,16 +762,17 @@
|
|||
select.removeChild(select.lastChild);
|
||||
}
|
||||
|
||||
// Add discovered throwers
|
||||
// Add discovered throwers with real-time online status check
|
||||
discoveredThrowers.forEach(thrower => {
|
||||
const option = document.createElement('option');
|
||||
option.value = thrower.pubkey;
|
||||
// Always check current online status when populating
|
||||
const onlineStatus = isThrowerOnline(thrower) ? '🟢' : '🔴';
|
||||
option.textContent = `${onlineStatus} ${thrower.name} (${thrower.pubkey.substring(0, 8)}...)`;
|
||||
select.appendChild(option);
|
||||
});
|
||||
|
||||
console.log('INFO', `Populated thrower dropdown for bounce ${bounceId} with ${discoveredThrowers.length} throwers`);
|
||||
console.log('INFO', `Populated thrower dropdown for bounce ${bounceId} with ${discoveredThrowers.length} throwers (with current online status)`);
|
||||
}
|
||||
|
||||
// Update all existing thrower dropdowns with current online status
|
||||
|
@ -904,7 +905,7 @@
|
|||
return null;
|
||||
}
|
||||
|
||||
// Populate relay dropdown with relays the selected thrower can read from
|
||||
// Populate relay dropdown with relays the selected thrower can throw to (write to)
|
||||
function populateRelayDropdown(bounceId, throwerPubkey) {
|
||||
const relaySelect = document.getElementById(`relay-select-${bounceId}`);
|
||||
const relayInput = document.getElementById(`bounce-relays-${bounceId}`);
|
||||
|
@ -927,17 +928,15 @@
|
|||
return;
|
||||
}
|
||||
|
||||
// Get relays the thrower can read from (read or both)
|
||||
const readableRelays = thrower.relayList.relays.filter(r => r.type === 'read' || r.type === 'both');
|
||||
// Get relays the thrower can write to (write or both)
|
||||
// Get relays the thrower can write to (write or both) - these are the relays it can throw to
|
||||
const writableRelays = thrower.relayList.relays.filter(r => r.type === 'write' || r.type === 'both');
|
||||
|
||||
if (readableRelays.length === 0) {
|
||||
relaySelect.innerHTML = '<option value="">-- This thrower cannot read from any relays --</option><option value="__manual__">⊕ Add manually (enter relay URL)</option>';
|
||||
// Show manual option since thrower has no readable relays
|
||||
if (writableRelays.length === 0) {
|
||||
relaySelect.innerHTML = '<option value="">-- This thrower cannot throw to any relays --</option><option value="__manual__">⊕ Add manually (enter relay URL)</option>';
|
||||
// Show manual option since thrower has no writable relays
|
||||
const manualOption = relaySelect.querySelector('option[value="__manual__"]');
|
||||
manualOption.classList.remove('hidden');
|
||||
console.log('WARN', `Thrower ${thrower.name} cannot read from any relays`);
|
||||
console.log('WARN', `Thrower ${thrower.name} cannot throw to any relays`);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -949,8 +948,8 @@
|
|||
relaySelect.appendChild(allRelaysOption);
|
||||
}
|
||||
|
||||
// Add readable relays to dropdown
|
||||
readableRelays.forEach(relay => {
|
||||
// Add writable relays to dropdown (these are the relays the thrower can throw to)
|
||||
writableRelays.forEach(relay => {
|
||||
const option = document.createElement('option');
|
||||
option.value = relay.url;
|
||||
option.textContent = relay.url;
|
||||
|
@ -967,7 +966,7 @@
|
|||
relayInput.value = '';
|
||||
relayManualDiv.classList.add('hidden');
|
||||
|
||||
console.log('INFO', `Populated relay dropdown for bounce ${bounceId} with ${readableRelays.length} readable relays and ${writableRelays.length} writable relays from ${thrower.name}`);
|
||||
console.log('INFO', `Populated relay dropdown for bounce ${bounceId} with ${writableRelays.length} writable relays that ${thrower.name} can throw to`);
|
||||
}
|
||||
|
||||
// Clear relay dropdown
|
||||
|
@ -1142,7 +1141,7 @@
|
|||
</div>
|
||||
</div>
|
||||
<div class="input-group">
|
||||
<label for="relay-select-${bounceId}">Target Relay:</label>
|
||||
<label for="relay-select-${bounceId}">Thrower throws to this relay(s):</label>
|
||||
<select id="relay-select-${bounceId}" onchange="onRelaySelect(${bounceId})" style="width: 100%; margin-bottom: 10px;">
|
||||
<option value="">-- Select a thrower first --</option>
|
||||
<option value="__manual__" class="hidden">⊕ Add manually (enter relay URL)</option>
|
||||
|
@ -1647,19 +1646,12 @@
|
|||
const throwerName = getThrowerName(bounce, bounceNumber);
|
||||
|
||||
if (isFirst) {
|
||||
// User sends the outermost routing event (first bounce created)
|
||||
// For the first bounce, we skip the user publishing step since it's handled by the button
|
||||
// Start directly with relay propagation after the button click
|
||||
const routingEventSize = JSON.stringify(bounce.routingEvent).length;
|
||||
const relays = getRelaysForBounce(bounceNumber);
|
||||
|
||||
// Step 1: User sends to relay
|
||||
flow.push({
|
||||
time: currentTime,
|
||||
actor: userName,
|
||||
action: `Publishes routing event`,
|
||||
size: routingEventSize
|
||||
});
|
||||
|
||||
// Step 2: Relay propagates (immediate)
|
||||
// Step 1: Relay propagates (immediate after button click)
|
||||
currentTime += 2000; // 2 seconds for relay propagation
|
||||
flow.push({
|
||||
time: currentTime,
|
||||
|
|
104
web/thrower.html
104
web/thrower.html
|
@ -48,7 +48,7 @@
|
|||
<div><strong>Events in Queue:</strong> <span id="events-queued">0</span></div>
|
||||
<div><strong>Info Status:</strong> <span id="thrower-info-status">Loading...</span></div>
|
||||
<div><strong>Last Updated:</strong> <span id="thrower-info-updated">Never</span></div>
|
||||
<div><strong>Refresh Rate:</strong> <span id="thrower-info-refresh">60 seconds</span></div>
|
||||
<div><strong>Refresh Rate:</strong> <span id="thrower-info-refresh">300 seconds</span></div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
@ -71,7 +71,7 @@
|
|||
<div class="input-group">
|
||||
<label>Add New Relay:</label>
|
||||
<div class="add-relay-form">
|
||||
<input type="url" id="new-relay-url" placeholder="wss://relay.example.com">
|
||||
<input type="url" id="new-relay-url" placeholder="wss://relay.example.com (or comma-separated list)">
|
||||
<select id="new-relay-type">
|
||||
<option value="">Both</option>
|
||||
<option value="read">Read</option>
|
||||
|
@ -151,7 +151,7 @@
|
|||
</div>
|
||||
<div class="input-group">
|
||||
<label for="edit-refresh-rate">Refresh Rate (seconds):</label>
|
||||
<input type="number" id="edit-refresh-rate" placeholder="60" value="60" min="10" max="3600">
|
||||
<input type="number" id="edit-refresh-rate" placeholder="300" value="300" min="10" max="3600">
|
||||
</div>
|
||||
<div class="input-group">
|
||||
<label for="edit-thrower-content">Additional Content (optional):</label>
|
||||
|
@ -616,35 +616,95 @@
|
|||
}
|
||||
|
||||
|
||||
// Add new relay
|
||||
// Add new relay (supports comma-separated list)
|
||||
function addRelay() {
|
||||
const url = document.getElementById('new-relay-url').value.trim();
|
||||
const input = document.getElementById('new-relay-url').value.trim();
|
||||
const type = document.getElementById('new-relay-type').value;
|
||||
|
||||
if (!url) {
|
||||
if (!input) {
|
||||
showStatus('relay-status', 'Please enter a relay URL', 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!url.startsWith('wss://') && !url.startsWith('ws://')) {
|
||||
showStatus('relay-status', 'Relay URL must start with wss:// or ws://', 'error');
|
||||
return;
|
||||
}
|
||||
// Check if input contains commas (multiple URLs)
|
||||
const urls = input.includes(',') ?
|
||||
input.split(',').map(url => url.trim()).filter(url => url.length > 0) :
|
||||
[input];
|
||||
|
||||
// Check for duplicates
|
||||
if (currentRelays.some(r => r.url === url)) {
|
||||
showStatus('relay-status', 'Relay already exists', 'error');
|
||||
return;
|
||||
}
|
||||
const results = {
|
||||
added: [],
|
||||
failed: [],
|
||||
duplicates: []
|
||||
};
|
||||
|
||||
currentRelays.push({ url, type });
|
||||
// Process each URL
|
||||
urls.forEach(url => {
|
||||
// Validate URL format
|
||||
if (!url.startsWith('wss://') && !url.startsWith('ws://')) {
|
||||
results.failed.push({ url, reason: 'Must start with wss:// or ws://' });
|
||||
return;
|
||||
}
|
||||
|
||||
// Check for duplicates
|
||||
if (currentRelays.some(r => r.url === url)) {
|
||||
results.duplicates.push(url);
|
||||
return;
|
||||
}
|
||||
|
||||
// Add relay
|
||||
currentRelays.push({ url, type, authStatus: 'unknown', lastTested: null });
|
||||
results.added.push(url);
|
||||
});
|
||||
|
||||
// Update display
|
||||
displayRelayList();
|
||||
|
||||
// Clear form
|
||||
document.getElementById('new-relay-url').value = '';
|
||||
document.getElementById('new-relay-type').value = '';
|
||||
|
||||
showStatus('relay-status', 'Relay added (remember to save)', 'info');
|
||||
// Provide detailed feedback
|
||||
const messages = [];
|
||||
|
||||
if (results.added.length > 0) {
|
||||
const typeText = type ? ` (${type === 'read' ? 'Read only' : type === 'write' ? 'Write only' : 'Both'})` : ' (Both)';
|
||||
if (results.added.length === 1) {
|
||||
messages.push(`✅ Added: ${results.added[0]}${typeText}`);
|
||||
} else {
|
||||
messages.push(`✅ Added ${results.added.length} relays${typeText}:`);
|
||||
results.added.forEach(url => messages.push(` • ${url}`));
|
||||
}
|
||||
}
|
||||
|
||||
if (results.duplicates.length > 0) {
|
||||
if (results.duplicates.length === 1) {
|
||||
messages.push(`⚠️ Duplicate skipped: ${results.duplicates[0]}`);
|
||||
} else {
|
||||
messages.push(`⚠️ ${results.duplicates.length} duplicates skipped:`);
|
||||
results.duplicates.forEach(url => messages.push(` • ${url}`));
|
||||
}
|
||||
}
|
||||
|
||||
if (results.failed.length > 0) {
|
||||
if (results.failed.length === 1) {
|
||||
messages.push(`❌ Failed: ${results.failed[0].url} (${results.failed[0].reason})`);
|
||||
} else {
|
||||
messages.push(`❌ ${results.failed.length} failed:`);
|
||||
results.failed.forEach(item => messages.push(` • ${item.url} (${item.reason})`));
|
||||
}
|
||||
}
|
||||
|
||||
// Show comprehensive status
|
||||
const statusMessage = messages.join('\n');
|
||||
const statusType = results.added.length > 0 ? 'info' :
|
||||
results.failed.length > 0 ? 'error' : 'info';
|
||||
|
||||
// Add reminder to save if any were added
|
||||
const finalMessage = results.added.length > 0 ?
|
||||
statusMessage + '\n\n💾 Remember to save your relay configuration!' :
|
||||
statusMessage;
|
||||
|
||||
showStatus('relay-status', finalMessage, statusType);
|
||||
}
|
||||
|
||||
// Remove relay
|
||||
|
@ -1067,7 +1127,7 @@
|
|||
version: '1.0.0',
|
||||
privacyPolicy: '',
|
||||
termsOfService: '',
|
||||
refreshRate: 60,
|
||||
refreshRate: 300,
|
||||
content: event.content || ''
|
||||
};
|
||||
|
||||
|
@ -1084,7 +1144,7 @@
|
|||
else if (tag[0] === 'version') currentThrowerInfo.version = tag[1] || '1.0.0';
|
||||
else if (tag[0] === 'privacy_policy') currentThrowerInfo.privacyPolicy = tag[1] || '';
|
||||
else if (tag[0] === 'terms_of_service') currentThrowerInfo.termsOfService = tag[1] || '';
|
||||
else if (tag[0] === 'refresh_rate') currentThrowerInfo.refreshRate = parseInt(tag[1]) || 60;
|
||||
else if (tag[0] === 'refresh_rate') currentThrowerInfo.refreshRate = parseInt(tag[1]) || 300;
|
||||
});
|
||||
|
||||
lastThrowerInfoPublish = event.created_at;
|
||||
|
@ -1103,7 +1163,7 @@
|
|||
version: '1.0.0',
|
||||
privacyPolicy: '',
|
||||
termsOfService: '',
|
||||
refreshRate: 60,
|
||||
refreshRate: 300,
|
||||
content: ''
|
||||
};
|
||||
displayThrowerInfo(currentThrowerInfo);
|
||||
|
@ -1148,7 +1208,7 @@
|
|||
document.getElementById('edit-version').value = currentThrowerInfo.version || '1.0.0';
|
||||
document.getElementById('edit-privacy-policy').value = currentThrowerInfo.privacyPolicy || '';
|
||||
document.getElementById('edit-terms-service').value = currentThrowerInfo.termsOfService || '';
|
||||
document.getElementById('edit-refresh-rate').value = currentThrowerInfo.refreshRate || 60;
|
||||
document.getElementById('edit-refresh-rate').value = currentThrowerInfo.refreshRate || 300;
|
||||
document.getElementById('edit-thrower-content').value = currentThrowerInfo.content || '';
|
||||
}
|
||||
|
||||
|
@ -1172,7 +1232,7 @@
|
|||
const version = document.getElementById('edit-version').value.trim();
|
||||
const privacyPolicy = document.getElementById('edit-privacy-policy').value.trim();
|
||||
const termsOfService = document.getElementById('edit-terms-service').value.trim();
|
||||
const refreshRate = parseInt(document.getElementById('edit-refresh-rate').value) || 60;
|
||||
const refreshRate = parseInt(document.getElementById('edit-refresh-rate').value) || 300;
|
||||
const content = document.getElementById('edit-thrower-content').value.trim();
|
||||
|
||||
try {
|
||||
|
|
Loading…
Reference in New Issue