diff --git a/package.json b/package.json index b2531a5..321d02a 100644 --- a/package.json +++ b/package.json @@ -8,9 +8,9 @@ "preview": "vite preview" }, "dependencies": { + "nostr-tools": "^2.24.2", "react": "^18.2.0", "react-dom": "^18.2.0", - "nostr-tools": "^2.0.0", "react-router-dom": "^6.20.0" }, "devDependencies": { diff --git a/src/components/NostrConnect.jsx b/src/components/NostrConnect.jsx index 2c064ef..3fc1a76 100644 --- a/src/components/NostrConnect.jsx +++ b/src/components/NostrConnect.jsx @@ -4,44 +4,83 @@ const NostrConnect = ({ setConnectedPubkey }) => { const [pubkey, setPubkey] = useState(null); const [loading, setLoading] = useState(false); + // Check if already connected + useEffect(() => { + if (window.nostr?.getPublicKey) { + window.nostr.getPublicKey() + .then(pk => { + setPubkey(pk); + setConnectedPubkey(pk); + }) + .catch(() => {}); + } + }, []); + const connect = async () => { setLoading(true); try { if (!window.nostr) { throw new Error('Please install a Nostr extension (Alby, Nos2x, etc.)'); } - - const pubkey = await window.nostr.getPublicKey(); - setPubkey(pubkey); - setConnectedPubkey(pubkey); + const pk = await window.nostr.getPublicKey(); + setPubkey(pk); + setConnectedPubkey(pk); + setLoading(false); } catch (error) { alert('Failed to connect: ' + error.message); - } finally { setLoading(false); } }; - useEffect(() => { - // Check if already connected - if (window.nostr) { - window.nostr.getPublicKey().then(pk => { - setPubkey(pk); - setConnectedPubkey(pk); - }).catch(() => {}); - } - }, []); + const disconnect = () => { + setPubkey(null); + setConnectedPubkey(null); + }; if (pubkey) { return ( -
- ✅ Connected: {pubkey.slice(0, 8)}...{pubkey.slice(-8)} +
+ + ✅ Connected: {pubkey.slice(0, 8)}...{pubkey.slice(-8)} + +
); } return ( - ); }; diff --git a/src/components/TemplateApplier.jsx b/src/components/TemplateApplier.jsx index 097ef55..fc50d53 100644 --- a/src/components/TemplateApplier.jsx +++ b/src/components/TemplateApplier.jsx @@ -2,23 +2,52 @@ import React, { useState, useEffect } from 'react'; import { Link, useParams } from 'react-router-dom'; import { decodeTemplate, validateTemplate } from '../utils/templates'; import useNostr from '../hooks/useNostr'; +import NostrConnect from './NostrConnect'; -// Define BLAST_RELAYS here -const BLAST_RELAYS = [ - 'wss://relay.primal.net', - 'wss://relay.damus.io', - 'wss://nos.lol' -]; - -const TemplateApplier = ({ connectedPubkey, setConnectedPubkey }) => { +const TemplateApplier = ({ setConnectedPubkey }) => { const { encoded } = useParams(); + const nostr = useNostr(); + const [template, setTemplate] = useState(null); const [error, setError] = useState(''); const [applying, setApplying] = useState(false); const [results, setResults] = useState(null); - const [isConnecting, setIsConnecting] = useState(false); - const { publishKind10002, publishKind10050, publishKind10063, isConnected, connect, pubkey } = useNostr(); + // ============================================================ + // CONNECTION CHECK + // ============================================================ + useEffect(() => { + const checkConnection = async () => { + if (nostr.pubkey) { + setConnectedPubkey(nostr.pubkey); + return; + } + + if (window.nostr?.getPublicKey) { + try { + const pk = await window.nostr.getPublicKey(); + if (pk) { + await nostr.connect(); + setConnectedPubkey(pk); + } + } catch (e) { + console.log('Extension not authorized'); + } + } + }; + + checkConnection(); + }, []); + + useEffect(() => { + if (nostr.pubkey) { + setConnectedPubkey(nostr.pubkey); + } + }, [nostr.pubkey, setConnectedPubkey]); + + // ============================================================ + // LOAD TEMPLATE + // ============================================================ useEffect(() => { if (!encoded) { setError('No template provided'); @@ -36,25 +65,9 @@ const TemplateApplier = ({ connectedPubkey, setConnectedPubkey }) => { } }, [encoded]); - useEffect(() => { - if (pubkey && !connectedPubkey) { - setConnectedPubkey(pubkey); - } - }, [pubkey, connectedPubkey, setConnectedPubkey]); - - const handleConnect = async () => { - setIsConnecting(true); - try { - const pk = await connect(); - setConnectedPubkey(pk); - setError(''); - } catch (err) { - setError('Failed to connect: ' + err.message); - } finally { - setIsConnecting(false); - } - }; - + // ============================================================ + // PUBLISH FUNCTIONS + // ============================================================ const handleApplyTemplate = async () => { if (!template) return; @@ -63,20 +76,12 @@ const TemplateApplier = ({ connectedPubkey, setConnectedPubkey }) => { setResults(null); try { - let pubkeyToUse = connectedPubkey || pubkey; - - if (!pubkeyToUse) { - try { - pubkeyToUse = await connect(); - setConnectedPubkey(pubkeyToUse); - } catch (err) { - throw new Error('Please connect your Nostr extension first'); - } + if (!nostr.pubkey) { + throw new Error('Please connect your Nostr signer first'); } - if (!pubkeyToUse) { - throw new Error('No Nostr public key available. Please connect your extension.'); - } + // ✅ Collect ALL user relay URLs from the main relays + const allUserRelays = template.relays.map(r => r.url); const allResults = []; const publishConfigs = []; @@ -86,28 +91,25 @@ const TemplateApplier = ({ connectedPubkey, setConnectedPubkey }) => { publishConfigs.push({ name: 'Relays (kind 10002)', kind: '10002', - relays: template.relays, - publishFn: () => publishKind10002(template.relays, pubkeyToUse) + publishFn: () => nostr.publishKind10002(template.relays, nostr.pubkey) }); } - // 2. Publish kind 10063 (blossom servers) + // 2. Publish kind 10063 (blossom servers) - ✅ Pass all user relays if (template.blossomServers && template.blossomServers.length > 0) { publishConfigs.push({ name: 'Blossom Servers (kind 10063)', kind: '10063', - relays: template.blossomServers.map(s => ({ url: s.url, read: true, write: true })), - publishFn: () => publishKind10063(template.blossomServers, pubkeyToUse) + publishFn: () => nostr.publishKind10063(template.blossomServers, nostr.pubkey, allUserRelays) }); } - // 3. Publish kind 10050 (DM relays) + // 3. Publish kind 10050 (DM relays) - ✅ Pass all user relays if (template.dmRelays && template.dmRelays.length > 0) { publishConfigs.push({ name: 'DM Relays (kind 10050)', kind: '10050', - relays: template.dmRelays.map(r => ({ url: r.url, read: true, write: true })), - publishFn: () => publishKind10050(template.dmRelays, pubkeyToUse) + publishFn: () => nostr.publishKind10050(template.dmRelays, nostr.pubkey, allUserRelays) }); } @@ -160,6 +162,10 @@ const TemplateApplier = ({ connectedPubkey, setConnectedPubkey }) => { } }; + // ============================================================ + // RENDER + // ============================================================ + if (error && !template) { return (
@@ -183,6 +189,8 @@ const TemplateApplier = ({ connectedPubkey, setConnectedPubkey }) => { ); } + const isConnected = nostr.isConnected && nostr.pubkey; + return (

Apply Relay Template

@@ -204,8 +212,9 @@ const TemplateApplier = ({ connectedPubkey, setConnectedPubkey }) => {
  • {relay.url} - {relay.read ? '📖 Read' : ''} - {relay.write ? ' âœī¸ Write' : ''} + {relay.read && relay.write ? 'đŸ“–âœī¸' : + relay.read ? '📖' : + relay.write ? 'âœī¸' : ''}
  • ))} @@ -221,7 +230,7 @@ const TemplateApplier = ({ connectedPubkey, setConnectedPubkey }) => {
  • {server.url} - #{index + 1} {index === 0 ? '⭐ Perferred' : ''} + #{index + 1} {index === 0 ? '⭐ Preferred' : ''}
  • ))} @@ -244,29 +253,56 @@ const TemplateApplier = ({ connectedPubkey, setConnectedPubkey }) => { )}
    - {/* Connection Status */} -
    - {(connectedPubkey || pubkey) ? ( -
    - ✅ Connected as: {(connectedPubkey || pubkey).slice(0, 8)}...{(connectedPubkey || pubkey).slice(-8)} -
    - ) : ( -
    - âš ī¸ Not connected to Nostr. Please connect your extension to publish. -
    + )} + + {/* CONNECTED STATUS */} + {isConnected && ( +
    +
    + + ✅ Connected: {nostr.pubkey.slice(0, 8)}...{nostr.pubkey.slice(-8)} + +
    - )} -
    +
    + )} - {/* Publish Button */} - {!results && (connectedPubkey || pubkey) && ( + {/* PUBLISH BUTTON */} + {!results && isConnected && ( )} @@ -291,7 +323,7 @@ const TemplateApplier = ({ connectedPubkey, setConnectedPubkey }) => {
    )} - {/* Results */} + {/* RESULTS */} {results && results.events && (

    @@ -343,7 +375,6 @@ const TemplateApplier = ({ connectedPubkey, setConnectedPubkey }) => { }}> {result.success ? '✅' : '❌'} {result.url} {result.error && ` - ${result.error}`} - {BLAST_RELAYS.includes(result.url) && ' đŸ”Ĩ'} ))} @@ -374,4 +405,4 @@ const TemplateApplier = ({ connectedPubkey, setConnectedPubkey }) => { ); }; -export default TemplateApplier; +export default TemplateApplier; \ No newline at end of file diff --git a/src/components/TemplateCreator.jsx b/src/components/TemplateCreator.jsx index 958c369..61beaa7 100644 --- a/src/components/TemplateCreator.jsx +++ b/src/components/TemplateCreator.jsx @@ -1,34 +1,21 @@ import React, { useState } from 'react'; import { encodeTemplate, generateTemplateId, validateTemplate, getCommunityTemplates } from '../utils/templates'; import RelayList from './RelayList'; -import NostrConnect from './NostrConnect'; -// Configure your blast relays here (hidden from UI) -// These will be used to publish the events, but won't appear in the template -const BLAST_RELAYS = [ - 'wss://relay.primal.net', - 'wss://relay.damus.io', - 'wss://nos.lol' - // Add your preferred blast relays here -]; - -const TemplateCreator = ({ setConnectedPubkey }) => { +const TemplateCreator = () => { const [templateName, setTemplateName] = useState(''); const [description, setDescription] = useState(''); const [relays, setRelays] = useState([ - { url: 'wss://relay.damus.io', read: true, write: true }, - { url: 'wss://nos.lol', read: true, write: true } + { url: 'wss://your-cool.relay', read: true, write: true } ]); - // Blossom servers (kind 10063) const [blossomServers, setBlossomServers] = useState([ - { url: 'https://cdn.satellite.earth' } + { url: 'https://your-blossom.community' } ]); const [showBlossom, setShowBlossom] = useState(true); - // DM relays (kind 10050) const [dmRelays, setDmRelays] = useState([ - { url: 'wss://relay.private-msgs.com' } + { url: 'wss://your-dm.relay' } ]); const [showDmRelays, setShowDmRelays] = useState(true); @@ -64,31 +51,6 @@ const TemplateCreator = ({ setConnectedPubkey }) => { } }; - const loadCommunityTemplate = (templateKey) => { - const template = communityTemplates[templateKey]; - if (template) { - setRelays(template.relays); - setTemplateName(template.name); - setDescription(template.description); - if (template.blossomServers && template.blossomServers.length > 0) { - setBlossomServers(template.blossomServers); - setShowBlossom(true); - } else { - setBlossomServers([{ url: '' }]); - setShowBlossom(false); - } - if (template.dmRelays && template.dmRelays.length > 0) { - setDmRelays(template.dmRelays); - setShowDmRelays(true); - } else { - setDmRelays([{ url: '' }]); - setShowDmRelays(false); - } - setSelectedTemplate(templateKey); - setError(''); - } - }; - // Relay functions const addRelay = () => { setRelays([...relays, { url: '', read: true, write: true }]); @@ -104,6 +66,12 @@ const TemplateCreator = ({ setConnectedPubkey }) => { setRelays(updated); }; + const addCommonRelay = (url) => { + if (!relays.some(r => r.url === url)) { + setRelays([...relays, { url, read: true, write: true }]); + } + }; + // Blossom server functions const addBlossomServer = () => { setBlossomServers([...blossomServers, { url: '' }]); @@ -134,17 +102,34 @@ const TemplateCreator = ({ setConnectedPubkey }) => { setDmRelays(updated); }; - const addCommonRelay = (url) => { - if (!relays.some(r => r.url === url)) { - setRelays([...relays, { url, read: true, write: true }]); + const loadCommunityTemplate = (templateKey) => { + const template = communityTemplates[templateKey]; + if (template) { + setRelays(template.relays); + setTemplateName(template.name); + setDescription(template.description); + if (template.blossomServers && template.blossomServers.length > 0) { + setBlossomServers(template.blossomServers); + setShowBlossom(true); + } else { + setBlossomServers([{ url: '' }]); + setShowBlossom(false); + } + if (template.dmRelays && template.dmRelays.length > 0) { + setDmRelays(template.dmRelays); + setShowDmRelays(true); + } else { + setDmRelays([{ url: '' }]); + setShowDmRelays(false); + } + setSelectedTemplate(templateKey); + setError(''); } }; return (

    Create Relay Template

    - - {/* Community Templates Section */}
    { {/* Main Relays Section */}
    - + { ); }; -export default TemplateCreator; \ No newline at end of file +export default TemplateCreator; diff --git a/src/hooks/useNostr.js b/src/hooks/useNostr.js index ef7b17c..765b423 100644 --- a/src/hooks/useNostr.js +++ b/src/hooks/useNostr.js @@ -1,13 +1,13 @@ import { useState, useEffect, useCallback } from 'react'; -// Blast relays - configure these once and they'll be used for publishing -// These are hidden from the UI and used to blast events to reliable relays +// Blast relays - configured once, used for publishing const BLAST_RELAYS = [ 'wss://relay.primal.net', 'wss://relay.damus.io', + 'wss://relay.ditto.pub', + 'wss://offchain.pub', 'wss://sendit.nosflare.com', 'wss://nostr.mom', - 'wss://relay.ditto.pub', 'wss://nos.lol' ]; @@ -16,143 +16,144 @@ export const useNostr = () => { const [isConnected, setIsConnected] = useState(false); const [error, setError] = useState(null); const [extension, setExtension] = useState(null); + const [isConnecting, setIsConnecting] = useState(false); + // ============================================================ + // INIT - Check for existing extension + // ============================================================ useEffect(() => { - const checkExtension = async () => { + const checkConnection = async () => { + if (isConnected && pubkey) return; + try { - if (window.nostr) { - setExtension(window.nostr); + if (window.nostr?.getPublicKey) { try { const pk = await window.nostr.getPublicKey(); + setExtension(window.nostr); setPubkey(pk); setIsConnected(true); - } catch (err) { + console.log('✅ Connected via extension:', pk.slice(0, 8) + '...'); + return; + } catch (e) { console.log('Extension available but not authorized'); } } - } catch (err) { - setError('No Nostr extension found'); + console.log('â„šī¸ No extension found'); + } catch (e) { + console.error('Init error:', e); } }; - checkExtension(); - }, []); + checkConnection(); + + const interval = setInterval(() => { + if (window.nostr?.getPublicKey && !isConnected) { + window.nostr.getPublicKey().then(pk => { + setExtension(window.nostr); + setPubkey(pk); + setIsConnected(true); + console.log('✅ Extension connected:', pk.slice(0, 8) + '...'); + }).catch(() => {}); + } + }, 2000); + + return () => clearInterval(interval); + }, [isConnected, pubkey]); + + // ============================================================ + // CONNECT + // ============================================================ const connect = useCallback(async () => { try { + setIsConnecting(true); if (!window.nostr) { throw new Error('Please install a Nostr extension (Alby, Nos2x, etc.)'); } const pk = await window.nostr.getPublicKey(); + setExtension(window.nostr); setPubkey(pk); setIsConnected(true); - setExtension(window.nostr); setError(null); + setIsConnecting(false); return pk; - } catch (err) { - setError(err.message); - setIsConnected(false); - throw err; + } catch (e) { + setError(e.message); + setIsConnecting(false); + throw e; } }, []); + // ============================================================ + // SIGN EVENT + // ============================================================ const signEvent = useCallback(async (event) => { if (!extension) { throw new Error('No Nostr extension connected'); } - return await extension.signEvent(event); + try { + console.log('📱 Signing event...'); + const signed = await extension.signEvent(event); + console.log('✅ Event signed'); + return signed; + } catch (e) { + console.error('Signing failed:', e); + throw e; + } }, [extension]); + // ============================================================ + // PUBLISH HELPERS + // ============================================================ const publishToSingleRelay = useCallback((url, event) => { return new Promise((resolve) => { - let ws = null; + const ws = new WebSocket(url); let resolved = false; - let timeoutIds = []; - - try { - ws = new WebSocket(url); - - const connectTimeout = setTimeout(() => { - if (!resolved) { - resolved = true; - if (ws && ws.readyState !== WebSocket.CLOSED) { - try { ws.close(); } catch (e) {} - } - resolve({ url, success: false, error: 'Connection timeout' }); - } - }, 10000); - timeoutIds.push(connectTimeout); - - ws.onopen = () => { - const message = JSON.stringify(['EVENT', event]); - ws.send(message); - - const publishTimeout = setTimeout(() => { - if (!resolved) { - resolved = true; - console.log(`⏰ Publish timeout for ${url}`); - try { ws.close(); } catch (e) {} - resolve({ url, success: false, error: 'Publish timeout' }); - } - }, 15000); - timeoutIds.push(publishTimeout); - - ws.onmessage = (msg) => { - try { - const data = JSON.parse(msg.data); - if (Array.isArray(data) && data[0] === 'OK' && data[1] === event.id) { - if (!resolved) { - resolved = true; - console.log(`✅ Published to ${url}`); - timeoutIds.forEach(id => clearTimeout(id)); - try { ws.close(); } catch (e) {} - resolve({ url, success: true }); - } - } - } catch (e) {} - }; - - ws.onerror = () => { - if (!resolved) { - resolved = true; - timeoutIds.forEach(id => clearTimeout(id)); - try { ws.close(); } catch (e) {} - resolve({ url, success: false, error: 'WebSocket error' }); - } - }; - - ws.onclose = () => { - if (!resolved) { - resolved = true; - timeoutIds.forEach(id => clearTimeout(id)); - resolve({ url, success: false, error: 'Connection closed' }); - } - }; - }; - - ws.onerror = () => { - if (!resolved) { - resolved = true; - timeoutIds.forEach(id => clearTimeout(id)); - resolve({ url, success: false, error: 'Connection error' }); - } - }; - - } catch (err) { + + const timeout = setTimeout(() => { if (!resolved) { resolved = true; - timeoutIds.forEach(id => clearTimeout(id)); - if (ws) { - try { ws.close(); } catch (e) {} - } - resolve({ url, success: false, error: err.message }); + try { ws.close(); } catch (e) {} + resolve({ url, success: false, error: 'Timeout' }); } - } + }, 10000); + + ws.onopen = () => { + ws.send(JSON.stringify(['EVENT', event])); + const pubTimeout = setTimeout(() => { + if (!resolved) { + resolved = true; + try { ws.close(); } catch (e) {} + resolve({ url, success: false, error: 'Publish timeout' }); + } + }, 15000); + + ws.onmessage = (msg) => { + try { + const data = JSON.parse(msg.data); + if (data[0] === 'OK' && data[1] === event.id) { + if (!resolved) { + resolved = true; + clearTimeout(timeout); + clearTimeout(pubTimeout); + try { ws.close(); } catch (e) {} + resolve({ url, success: true }); + } + } + } catch (e) {} + }; + }; + + ws.onerror = () => { + if (!resolved) { + resolved = true; + clearTimeout(timeout); + try { ws.close(); } catch (e) {} + resolve({ url, success: false, error: 'WebSocket error' }); + } + }; }); }, []); - /** - * Publish to multiple relays with detailed results - */ const publishToRelays = useCallback(async (event, relayUrls, label = '') => { console.log(`Publishing ${label} to ${relayUrls.length} relays...`); @@ -169,8 +170,9 @@ export const useNostr = () => { return [{ url: 'none', success: false, error: 'No valid relay URLs' }]; } - const publishPromises = validUrls.map(url => publishToSingleRelay(url, event)); - const results = await Promise.all(publishPromises); + const results = await Promise.all( + validUrls.map(url => publishToSingleRelay(url, event)) + ); const successCount = results.filter(r => r.success).length; console.log(`Published ${label} to ${successCount}/${results.length} relays`); @@ -178,8 +180,16 @@ export const useNostr = () => { return results; }, [publishToSingleRelay]); + // ============================================================ + // PUBLISH KIND FUNCTIONS + // ============================================================ + /** - * Generic publish function that publishes to BOTH blast relays AND user relays + * Generic publish function that publishes to ALL relays + * @param {number} kind - Event kind + * @param {object} data - Event data with tags and content + * @param {string} pubkey - User's pubkey + * @param {string[]} userRelayUrls - User's relay URLs (all of them, not just for this kind) */ const publishKind = useCallback(async (kind, data, pubkey, userRelayUrls = []) => { if (!extension) { @@ -187,105 +197,100 @@ export const useNostr = () => { } const event = { - kind: kind, - pubkey: pubkey, + kind, + pubkey, created_at: Math.floor(Date.now() / 1000), tags: data.tags || [], content: data.content || '' }; console.log(`Signing kind ${kind} event...`); - const signedEvent = await extension.signEvent(event); + const signedEvent = await signEvent(event); console.log(`Kind ${kind} event signed successfully`); - // Combine blast relays and user relays (deduplicated) + // ✅ Combine ALL relays: blast + ALL user relays const allRelays = [...new Set([...BLAST_RELAYS, ...userRelayUrls])]; console.log(`Publishing kind ${kind} to ${allRelays.length} relays (${BLAST_RELAYS.length} blast + ${userRelayUrls.length} user)`); - // Publish to all relays const results = await publishToRelays(signedEvent, allRelays, `kind ${kind}`); - // Separate results for better reporting const blastResults = results.filter(r => BLAST_RELAYS.includes(r.url)); const userResults = results.filter(r => userRelayUrls.includes(r.url)); - console.log(`Kind ${kind} results:`, { - blast: `${blastResults.filter(r => r.success).length}/${blastResults.length}`, - user: `${userResults.filter(r => r.success).length}/${userResults.length}` - }); - return { event: signedEvent, - results: results, - blastResults: blastResults, - userResults: userResults + results, + blastResults, + userResults }; - }, [extension, publishToRelays]); + }, [extension, signEvent, publishToRelays]); // Kind 10002 - Relays const publishKind10002 = useCallback(async (relays, pubkey) => { const validRelays = relays.filter(r => r.url && r.url.trim() !== ''); - if (validRelays.length === 0) { throw new Error('No valid relays to publish'); } - const tags = validRelays.map(relay => { const params = []; if (!relay.read) params.push('read'); if (!relay.write) params.push('write'); return ['r', relay.url, ...params]; }); - - // Extract user relay URLs for combined publishing + // ✅ Pass ALL user relay URLs (they're the same as the relays being published) const userRelayUrls = validRelays.map(r => r.url); - return await publishKind(10002, { tags }, pubkey, userRelayUrls); }, [publishKind]); // Kind 10063 - Blossom Servers - const publishKind10063 = useCallback(async (servers, pubkey) => { + const publishKind10063 = useCallback(async (servers, pubkey, allUserRelays = []) => { const validServers = servers.filter(s => s.url && s.url.trim() !== ''); - if (validServers.length === 0) { throw new Error('No valid blossom servers'); } - const tags = validServers.map(server => ['server', server.url]); - - // For blossom servers, we want to publish to blast relays - // The user's main relays are used for kind 10002, not kind 10063 - return await publishKind(10063, { tags }, pubkey, []); + // ✅ Pass ALL user relays (main relays, not just blossom servers) + return await publishKind(10063, { tags }, pubkey, allUserRelays); }, [publishKind]); // Kind 10050 - DM Relays - const publishKind10050 = useCallback(async (relays, pubkey) => { + const publishKind10050 = useCallback(async (relays, pubkey, allUserRelays = []) => { const validRelays = relays.filter(r => r.url && r.url.trim() !== ''); - if (validRelays.length === 0) { throw new Error('No valid DM relays'); } - const tags = validRelays.map(relay => ['relay', relay.url]); - - // For DM relays, we want to publish to blast relays - // The user's main relays are used for kind 10002, not kind 10050 - return await publishKind(10050, { tags }, pubkey, []); + // ✅ Pass ALL user relays (main relays, not just DM relays) + return await publishKind(10050, { tags }, pubkey, allUserRelays); }, [publishKind]); + // ============================================================ + // DISCONNECT + // ============================================================ + const disconnect = useCallback(() => { + setPubkey(null); + setIsConnected(false); + setExtension(null); + console.log('👋 Disconnected'); + }, []); + + // ============================================================ + // RETURN + // ============================================================ return { pubkey, isConnected, error, - extension, + isConnecting, connect, signEvent, + disconnect, publishToRelays, publishKind, publishKind10002, publishKind10063, - publishKind10050 + publishKind10050, }; }; -export default useNostr; +export default useNostr; \ No newline at end of file