v0.7.24 - Fix admin API subscription issues: NIP-17 historical events and relay pubkey timing

This commit is contained in:
Your Name
2025-10-16 06:27:01 -04:00
parent 18b0ac44bf
commit 6c38aaebf3
8 changed files with 456 additions and 72 deletions

View File

@@ -22,6 +22,7 @@ let currentConfig = null;
// Global subscription state
let relayPool = null;
let subscriptionId = null;
let isSubscribed = false; // Flag to prevent multiple simultaneous subscriptions
// Relay connection state
let relayInfo = null;
let isRelayConnected = false;
@@ -353,6 +354,9 @@ async function setupAutomaticRelayConnection(showSections = false) {
// Mark as connected
isRelayConnected = true;
// Update relay info in header
updateRelayInfoInHeader();
// Only show admin sections if explicitly requested
if (showSections) {
updateAdminSectionsVisibility();
@@ -747,6 +751,8 @@ async function logout() {
}
relayPool = null;
subscriptionId = null;
// Reset subscription flag
isSubscribed = false;
}
await nlLite.logout();
@@ -758,6 +764,8 @@ async function logout() {
// Reset relay connection state
isRelayConnected = false;
relayPubkey = null;
// Reset subscription flag
isSubscribed = false;
// Reset UI - hide profile and show login modal
hideProfileFromHeader();
@@ -798,6 +806,12 @@ async function subscribeToConfiguration() {
try {
console.log('=== STARTING SIMPLEPOOL CONFIGURATION SUBSCRIPTION ===');
// Prevent multiple simultaneous subscription attempts
if (isSubscribed) {
console.log('Subscription already established, skipping duplicate subscription attempt');
return true;
}
if (!isLoggedIn) {
console.log('WARNING: Not logged in, but proceeding with subscription test');
}
@@ -810,16 +824,14 @@ async function subscribeToConfiguration() {
console.log(`Connecting to relay via SimplePool: ${url}`);
// Clean up existing pool
if (relayPool) {
console.log('Closing existing pool connection');
relayPool.close([url]);
relayPool = null;
subscriptionId = null;
// Reuse existing pool if available, otherwise create new one
if (!relayPool) {
console.log('Creating new SimplePool instance');
relayPool = new window.NostrTools.SimplePool();
} else {
console.log('Reusing existing SimplePool instance');
}
// Create new SimplePool instance
relayPool = new window.NostrTools.SimplePool();
subscriptionId = generateSubId();
console.log(`Generated subscription ID: ${subscriptionId}`);
@@ -838,6 +850,7 @@ async function subscribeToConfiguration() {
"#p": [userPubkey], // Only DMs directed to this user
limit: 50
}, {
since: Math.floor(Date.now() / 1000), // Start from current time
kinds: [1059], // NIP-17 GiftWrap events
"#p": [userPubkey], // Only GiftWrap events addressed to this user
limit: 50
@@ -940,6 +953,9 @@ async function subscribeToConfiguration() {
// Store subscription for cleanup
relayPool.currentSubscription = subscription;
// Mark as subscribed to prevent duplicate attempts
isSubscribed = true;
console.log('SimplePool subscription established');
return true;
@@ -1101,6 +1117,9 @@ function handleConfigQueryResponse(responseData) {
// Display the configuration using the original display function
displayConfiguration(syntheticEvent);
// Update relay info in header with config data
updateStoredRelayInfo(responseData);
log(`Configuration loaded: ${responseData.total_results} parameters`, 'INFO');
} else {
console.log('No configuration data received');
@@ -1335,14 +1354,16 @@ async function fetchConfiguration() {
throw new Error('Must be connected to relay to fetch configuration. Please use the Relay Connection section first.');
}
// First establish subscription to receive responses
// First establish subscription to receive responses (only if not already subscribed)
const subscriptionResult = await subscribeToConfiguration();
if (!subscriptionResult) {
throw new Error('Failed to establish admin response subscription');
}
// Wait a moment for subscription to be established
await new Promise(resolve => setTimeout(resolve, 500));
// Wait a moment for subscription to be established (only if we just created it)
if (!isSubscribed) {
await new Promise(resolve => setTimeout(resolve, 500));
}
// Send config query command if logged in
if (isLoggedIn && userPubkey && relayPool) {
@@ -1698,6 +1719,43 @@ if (logoutBtn) {
});
}
// Initialize dark mode button handler
const darkModeBtn = document.getElementById('dark-mode-btn');
if (darkModeBtn) {
darkModeBtn.addEventListener('click', function(e) {
e.stopPropagation(); // Prevent profile area click
toggleDarkMode();
});
}
// Initialize relay pubkey container click handler for clipboard copy
const relayPubkeyContainer = document.getElementById('relay-pubkey-container');
if (relayPubkeyContainer) {
relayPubkeyContainer.addEventListener('click', async function() {
const relayPubkeyElement = document.getElementById('relay-pubkey');
if (relayPubkeyElement && relayPubkeyElement.textContent !== 'Loading...') {
try {
// Get the full npub (remove line breaks for clipboard)
const fullNpub = relayPubkeyElement.textContent.replace(/\n/g, '');
await navigator.clipboard.writeText(fullNpub);
// Add copied class for visual feedback
relayPubkeyContainer.classList.add('copied');
// Remove the class after animation completes
setTimeout(() => {
relayPubkeyContainer.classList.remove('copied');
}, 500);
log('Relay npub copied to clipboard', 'INFO');
} catch (error) {
log('Failed to copy relay npub to clipboard', 'ERROR');
}
}
});
}
// Event handlers
fetchConfigBtn.addEventListener('click', function (e) {
e.preventDefault();
@@ -3181,16 +3239,90 @@ function addMessageToInbox(direction, message, timestamp, pubkey = null) {
}
}
// Update relay info in header
function updateRelayInfoInHeader() {
const relayNameElement = document.getElementById('relay-name');
const relayPubkeyElement = document.getElementById('relay-pubkey');
const relayDescriptionElement = document.getElementById('relay-description');
if (!relayNameElement || !relayPubkeyElement || !relayDescriptionElement) {
return;
}
// Get relay info from NIP-11 data or use defaults
const relayInfo = getRelayInfo();
const relayName = relayInfo.name || 'C-Relay';
const relayDescription = relayInfo.description || 'Nostr Relay';
// Convert relay pubkey to npub
let relayNpub = 'Loading...';
if (relayPubkey) {
try {
relayNpub = window.NostrTools.nip19.npubEncode(relayPubkey);
} catch (error) {
console.log('Failed to encode relay pubkey to npub:', error.message);
relayNpub = relayPubkey.substring(0, 16) + '...';
}
}
// Format npub into 3 lines of 21 characters each
let formattedNpub = relayNpub;
if (relayNpub.length === 63) {
formattedNpub = relayNpub.substring(0, 21) + '\n' +
relayNpub.substring(21, 42) + '\n' +
relayNpub.substring(42, 63);
}
relayNameElement.textContent = relayName;
relayPubkeyElement.textContent = formattedNpub;
relayDescriptionElement.textContent = relayDescription;
}
// Global variable to store relay info from NIP-11 or config
let relayInfoData = null;
// Helper function to get relay info from stored data
function getRelayInfo() {
// Return stored relay info if available, otherwise defaults
if (relayInfoData) {
return relayInfoData;
}
// Default values
return {
name: 'C-Relay',
description: 'Nostr Relay',
pubkey: relayPubkey
};
}
// Update stored relay info when config is loaded
function updateStoredRelayInfo(configData) {
if (configData && configData.data) {
// Extract relay info from config data
const relayName = configData.data.find(item => item.key === 'relay_name')?.value || 'C-Relay';
const relayDescription = configData.data.find(item => item.key === 'relay_description')?.value || 'Nostr Relay';
relayInfoData = {
name: relayName,
description: relayDescription,
pubkey: relayPubkey
};
// Update header immediately
updateRelayInfoInHeader();
}
}
// Helper function to get relay pubkey
function getRelayPubkey() {
// Use the dynamically fetched relay pubkey if available
if (relayPubkey && isRelayConnected) {
if (relayPubkey) {
return relayPubkey;
}
// Fallback to hardcoded value for testing/development
log('Warning: Using hardcoded relay pubkey. Please connect to relay first.', 'WARNING');
return '4f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa';
// No fallback - throw error if relay pubkey not available
throw new Error('Relay pubkey not available. Please connect to relay first.');
}
// Enhanced SimplePool message handler to capture test responses
@@ -3695,10 +3827,53 @@ document.addEventListener('DOMContentLoaded', () => {
});
// Dark mode functionality
function toggleDarkMode() {
const body = document.body;
const isDarkMode = body.classList.contains('dark-mode');
if (isDarkMode) {
body.classList.remove('dark-mode');
localStorage.setItem('darkMode', 'false');
updateDarkModeButton(false);
log('Switched to light mode', 'INFO');
} else {
body.classList.add('dark-mode');
localStorage.setItem('darkMode', 'true');
updateDarkModeButton(true);
log('Switched to dark mode', 'INFO');
}
}
function updateDarkModeButton(isDarkMode) {
const darkModeBtn = document.getElementById('dark-mode-btn');
if (darkModeBtn) {
darkModeBtn.textContent = isDarkMode ? 'LIGHT MODE' : 'DARK MODE';
}
}
function initializeDarkMode() {
const savedDarkMode = localStorage.getItem('darkMode');
const prefersDark = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches;
const shouldBeDark = savedDarkMode === 'true' || (savedDarkMode === null && prefersDark);
if (shouldBeDark) {
document.body.classList.add('dark-mode');
updateDarkModeButton(true);
} else {
updateDarkModeButton(false);
}
}
// Initialize the app
document.addEventListener('DOMContentLoaded', () => {
console.log('C-Relay Admin API interface loaded');
// Initialize dark mode
initializeDarkMode();
// Start RELAY letter animation
startRelayAnimation();
// Ensure admin sections are hidden by default on page load
updateAdminSectionsVisibility();
@@ -3708,4 +3883,39 @@ document.addEventListener('DOMContentLoaded', () => {
// Enhance SimplePool for testing after initialization
setTimeout(enhancePoolForTesting, 2000);
}, 100);
});
});
// RELAY letter animation function
function startRelayAnimation() {
const letters = document.querySelectorAll('.relay-letter');
let currentIndex = 0;
function animateLetter() {
// Remove underline from all letters first
letters.forEach(letter => letter.classList.remove('underlined'));
// Add underline to current letter
if (letters[currentIndex]) {
letters[currentIndex].classList.add('underlined');
}
// Move to next letter
currentIndex++;
// If we've gone through all letters, remove all underlines and wait 4000ms then restart
if (currentIndex > letters.length) {
// Remove all underlines before the pause
letters.forEach(letter => letter.classList.remove('underlined'));
setTimeout(() => {
currentIndex = 0;
animateLetter();
}, 4000);
} else {
// Otherwise, continue to next letter after 200ms
setTimeout(animateLetter, 100);
}
}
// Start the animation
animateLetter();
}