- Fix NIP-07 extension login using NDKNip07Signer - Fix NDK initialization to not block on relay connection - Read relay URLs from VITE_DEFAULT_RELAYS env variable - Add proper error handling for all login methods - Remove NIP-55 option (Android only, not for web) - Add vite-env.d.ts for TypeScript support - Update .gitignore to exclude dist/ and .env
This commit is contained in:
35
.gitignore
vendored
Normal file
35
.gitignore
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
# Dependencies
|
||||
node_modules/
|
||||
|
||||
# Build output
|
||||
dist/
|
||||
|
||||
# Environment variables
|
||||
.env
|
||||
.env.local
|
||||
.env.*.local
|
||||
|
||||
# Editor directories and files
|
||||
.vscode/*
|
||||
!.vscode/extensions.json
|
||||
.idea/
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
|
||||
# OS files
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
|
||||
# Logs
|
||||
logs/
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
|
||||
# Cache
|
||||
.cache/
|
||||
*.tsbuildinfo
|
||||
1
dist/assets/index-0ed31904.css
vendored
1
dist/assets/index-0ed31904.css
vendored
File diff suppressed because one or more lines are too long
189
dist/assets/index-91eab0ce.js
vendored
189
dist/assets/index-91eab0ce.js
vendored
File diff suppressed because one or more lines are too long
81
dist/assets/ndk-40656944.js
vendored
81
dist/assets/ndk-40656944.js
vendored
File diff suppressed because one or more lines are too long
59
dist/assets/vendor-beb84f6c.js
vendored
59
dist/assets/vendor-beb84f6c.js
vendored
File diff suppressed because one or more lines are too long
20
dist/index.html
vendored
20
dist/index.html
vendored
@@ -1,20 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>NostrCount - Track Life Milestones</title>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet">
|
||||
<script type="module" crossorigin src="/assets/index-91eab0ce.js"></script>
|
||||
<link rel="modulepreload" crossorigin href="/assets/ndk-40656944.js">
|
||||
<link rel="modulepreload" crossorigin href="/assets/vendor-beb84f6c.js">
|
||||
<link rel="stylesheet" href="/assets/index-0ed31904.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
6
dist/vite.svg
vendored
6
dist/vite.svg
vendored
@@ -1,6 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M3 3v18h18"/>
|
||||
<path d="M18 17V9"/>
|
||||
<path d="M13 17V5"/>
|
||||
<path d="M8 17v-3"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 260 B |
@@ -1,5 +1,5 @@
|
||||
import React, { useState } from 'react';
|
||||
import { X, Key, Zap, Plus, Copy, ExternalLink } from 'lucide-react';
|
||||
import { X, Key, Zap, Plus, Copy } from 'lucide-react';
|
||||
import { useNDK } from '../contexts/NDKContext';
|
||||
import { nip19, generateSecretKey, getPublicKey } from 'nostr-tools';
|
||||
|
||||
@@ -8,11 +8,11 @@ interface LoginModalProps {
|
||||
onClose: () => void;
|
||||
}
|
||||
|
||||
type LoginMethod = 'extension' | 'keys' | 'create' | 'nip55';
|
||||
type LoginMethod = 'extension' | 'keys' | 'create';
|
||||
type CreateStep = 'username' | 'keys';
|
||||
|
||||
export const LoginModal: React.FC<LoginModalProps> = ({ isOpen, onClose }) => {
|
||||
const { login, loginWithKeys, loginWithNip55, updateUserProfile } = useNDK();
|
||||
const { login, loginWithKeys, updateUserProfile, ndk } = useNDK();
|
||||
const [loginMethod, setLoginMethod] = useState<LoginMethod>('extension');
|
||||
const [createStep, setCreateStep] = useState<CreateStep>('username');
|
||||
|
||||
@@ -25,12 +25,12 @@ export const LoginModal: React.FC<LoginModalProps> = ({ isOpen, onClose }) => {
|
||||
const [newPrivateKey, setNewPrivateKey] = useState('');
|
||||
const [newPublicKey, setNewPublicKey] = useState('');
|
||||
|
||||
// NIP-55
|
||||
const [nip55Url, setNip55Url] = useState('');
|
||||
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
// Check if NDK is ready - just need the ndk instance, not waiting for all relays
|
||||
const isNdkReady = ndk !== null;
|
||||
|
||||
const detectKeyType = (input: string): 'npub' | 'nsec' | 'unknown' => {
|
||||
if (input.startsWith('npub')) return 'npub';
|
||||
if (input.startsWith('nsec')) return 'nsec';
|
||||
@@ -124,11 +124,16 @@ export const LoginModal: React.FC<LoginModalProps> = ({ isOpen, onClose }) => {
|
||||
// Login with the new keys
|
||||
await loginWithKeys(newPublicKey, newPrivateKey);
|
||||
|
||||
// Update user profile with the username
|
||||
await updateUserProfile({
|
||||
name: username,
|
||||
display_name: username,
|
||||
});
|
||||
// Try to update user profile with the username, but don't fail if it doesn't work
|
||||
try {
|
||||
await updateUserProfile({
|
||||
name: username,
|
||||
display_name: username,
|
||||
});
|
||||
} catch (profileErr) {
|
||||
console.warn('Could not update profile, but login was successful:', profileErr);
|
||||
// Don't show error to user - the login worked, profile update is optional
|
||||
}
|
||||
|
||||
onClose();
|
||||
} catch (err) {
|
||||
@@ -139,25 +144,6 @@ export const LoginModal: React.FC<LoginModalProps> = ({ isOpen, onClose }) => {
|
||||
}
|
||||
};
|
||||
|
||||
const handleNip55Login = async () => {
|
||||
if (!nip55Url.trim()) {
|
||||
setError('Please enter a NIP-55 URL');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
await loginWithNip55(nip55Url);
|
||||
onClose();
|
||||
} catch (err) {
|
||||
console.error('NIP-55 login failed:', err);
|
||||
setError(err instanceof Error ? err.message : 'Login failed');
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const copyToClipboard = (text: string) => {
|
||||
navigator.clipboard.writeText(text);
|
||||
};
|
||||
@@ -168,7 +154,6 @@ export const LoginModal: React.FC<LoginModalProps> = ({ isOpen, onClose }) => {
|
||||
setUsername('');
|
||||
setNewPrivateKey('');
|
||||
setNewPublicKey('');
|
||||
setNip55Url('');
|
||||
setError(null);
|
||||
setCreateStep('username');
|
||||
};
|
||||
@@ -197,6 +182,15 @@ export const LoginModal: React.FC<LoginModalProps> = ({ isOpen, onClose }) => {
|
||||
</div>
|
||||
|
||||
<div className="p-6">
|
||||
{/* Show warning if NDK is not ready */}
|
||||
{!isNdkReady && (
|
||||
<div className="mb-4 p-3 bg-yellow-50 border border-yellow-200 rounded-md">
|
||||
<p className="text-yellow-700 text-sm">
|
||||
Connecting to Nostr relays... Please wait.
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="flex gap-2 mb-6">
|
||||
<button
|
||||
onClick={() => setLoginMethod('extension')}
|
||||
@@ -231,17 +225,6 @@ export const LoginModal: React.FC<LoginModalProps> = ({ isOpen, onClose }) => {
|
||||
<Plus className="w-4 h-4 inline mr-1" />
|
||||
Create
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setLoginMethod('nip55')}
|
||||
className={`flex-1 px-3 py-2 rounded-md font-medium transition-colors text-sm ${
|
||||
loginMethod === 'nip55'
|
||||
? 'bg-blue-600 text-white'
|
||||
: 'bg-gray-100 text-gray-700 hover:bg-gray-200'
|
||||
}`}
|
||||
>
|
||||
<ExternalLink className="w-4 h-4 inline mr-1" />
|
||||
NIP-55
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{loginMethod === 'extension' && (
|
||||
@@ -249,12 +232,19 @@ export const LoginModal: React.FC<LoginModalProps> = ({ isOpen, onClose }) => {
|
||||
<p className="text-gray-600 mb-4">
|
||||
Connect using your Nostr browser extension (Alby, nos2x, etc.)
|
||||
</p>
|
||||
{typeof window !== 'undefined' && !window.nostr && (
|
||||
<div className="mb-4 p-3 bg-orange-50 border border-orange-200 rounded-md">
|
||||
<p className="text-orange-700 text-sm">
|
||||
No Nostr extension detected. Install <a href="https://getalby.com" target="_blank" rel="noopener noreferrer" className="underline">Alby</a> or another NIP-07 extension.
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
<button
|
||||
onClick={handleExtensionLogin}
|
||||
disabled={loading}
|
||||
disabled={loading || !isNdkReady}
|
||||
className="w-full px-4 py-2 bg-blue-600 text-white rounded-md hover:bg-blue-700 transition-colors font-medium disabled:opacity-50"
|
||||
>
|
||||
{loading ? 'Connecting...' : 'Connect Extension'}
|
||||
{loading ? 'Connecting...' : !isNdkReady ? 'Waiting for relays...' : 'Connect Extension'}
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
@@ -263,29 +253,29 @@ export const LoginModal: React.FC<LoginModalProps> = ({ isOpen, onClose }) => {
|
||||
<div className="space-y-4">
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
||||
Public or Private Key
|
||||
Private Key (nsec)
|
||||
</label>
|
||||
<input
|
||||
type="password"
|
||||
value={keyInput}
|
||||
onChange={(e) => handleKeyInputChange(e.target.value)}
|
||||
placeholder="npub1... or nsec1..."
|
||||
placeholder="nsec1..."
|
||||
className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||
disabled={loading}
|
||||
/>
|
||||
{keyType !== 'unknown' && (
|
||||
<p className="text-xs text-gray-500 mt-1">
|
||||
Detected: {keyType === 'npub' ? 'Public Key (Read-only)' : 'Private Key (Full Access)'}
|
||||
Detected: {keyType === 'npub' ? 'Public Key (Read-only - not supported)' : 'Private Key (Full Access)'}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<button
|
||||
onClick={handleKeysLogin}
|
||||
disabled={loading || keyType === 'unknown'}
|
||||
disabled={loading || keyType === 'unknown' || keyType === 'npub' || !isNdkReady}
|
||||
className="w-full px-4 py-2 bg-blue-600 text-white rounded-md hover:bg-blue-700 transition-colors font-medium disabled:opacity-50"
|
||||
>
|
||||
{loading ? 'Connecting...' : 'Connect with Keys'}
|
||||
{loading ? 'Connecting...' : !isNdkReady ? 'Waiting for relays...' : 'Connect with Keys'}
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
@@ -322,9 +312,9 @@ export const LoginModal: React.FC<LoginModalProps> = ({ isOpen, onClose }) => {
|
||||
{loginMethod === 'create' && createStep === 'keys' && (
|
||||
<div className="space-y-4">
|
||||
<div className="bg-yellow-50 border border-yellow-200 rounded-md p-4">
|
||||
<h3 className="font-semibold text-yellow-800 mb-2">Save Your Keys!</h3>
|
||||
<h3 className="font-semibold text-yellow-800 mb-2">⚠️ Save Your Keys!</h3>
|
||||
<p className="text-sm text-yellow-700 mb-3">
|
||||
Store these keys safely. You'll need them to access your account.
|
||||
Store these keys safely. You'll need them to access your account. If you lose them, you lose access forever!
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -342,6 +332,7 @@ export const LoginModal: React.FC<LoginModalProps> = ({ isOpen, onClose }) => {
|
||||
<button
|
||||
onClick={() => copyToClipboard(newPrivateKey)}
|
||||
className="absolute right-2 top-2 p-1 hover:bg-gray-200 rounded"
|
||||
title="Copy to clipboard"
|
||||
>
|
||||
<Copy className="w-4 h-4 text-gray-500" />
|
||||
</button>
|
||||
@@ -362,6 +353,7 @@ export const LoginModal: React.FC<LoginModalProps> = ({ isOpen, onClose }) => {
|
||||
<button
|
||||
onClick={() => copyToClipboard(newPublicKey)}
|
||||
className="absolute right-2 top-2 p-1 hover:bg-gray-200 rounded"
|
||||
title="Copy to clipboard"
|
||||
>
|
||||
<Copy className="w-4 h-4 text-gray-500" />
|
||||
</button>
|
||||
@@ -370,39 +362,10 @@ export const LoginModal: React.FC<LoginModalProps> = ({ isOpen, onClose }) => {
|
||||
|
||||
<button
|
||||
onClick={handleUseNewKeys}
|
||||
disabled={loading}
|
||||
disabled={loading || !isNdkReady}
|
||||
className="w-full px-4 py-2 bg-blue-600 text-white rounded-md hover:bg-blue-700 transition-colors font-medium disabled:opacity-50"
|
||||
>
|
||||
{loading ? 'Connecting...' : 'Use These Keys'}
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{loginMethod === 'nip55' && (
|
||||
<div className="space-y-4">
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
||||
NIP-55 URL
|
||||
</label>
|
||||
<input
|
||||
type="url"
|
||||
value={nip55Url}
|
||||
onChange={(e) => setNip55Url(e.target.value)}
|
||||
placeholder="https://example.com/.well-known/nostr.json"
|
||||
className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||
disabled={loading}
|
||||
/>
|
||||
<p className="text-xs text-gray-500 mt-1">
|
||||
Enter a NIP-55 compatible URL to connect with an external signer
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<button
|
||||
onClick={handleNip55Login}
|
||||
disabled={loading || !nip55Url.trim()}
|
||||
className="w-full px-4 py-2 bg-blue-600 text-white rounded-md hover:bg-blue-700 transition-colors font-medium disabled:opacity-50"
|
||||
>
|
||||
{loading ? 'Connecting...' : 'Connect with NIP-55'}
|
||||
{loading ? 'Connecting...' : !isNdkReady ? 'Waiting for relays...' : 'I Saved My Keys - Continue'}
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React, { createContext, useContext, useEffect, useState } from 'react';
|
||||
import NDK, { NDKEvent, NDKUser, NDKSigner, NDKPrivateKeySigner } from '@nostr-dev-kit/ndk';
|
||||
import NDK, { NDKEvent, NDKUser, NDKPrivateKeySigner, NDKNip07Signer } from '@nostr-dev-kit/ndk';
|
||||
import NDKCacheAdapterDexie from '@nostr-dev-kit/ndk-cache-dexie';
|
||||
import { getPublicKey, nip19 } from 'nostr-tools';
|
||||
import { DEFAULT_RELAYS } from '../utils/nostr';
|
||||
@@ -44,7 +44,7 @@ export const NDKProvider: React.FC<NDKProviderProps> = ({ children }) => {
|
||||
useEffect(() => {
|
||||
const initNDK = async () => {
|
||||
try {
|
||||
console.log('Initializing NDK with Dexie cache...');
|
||||
console.log('Initializing NDK with relays:', DEFAULT_RELAYS);
|
||||
|
||||
// Initialize Dexie cache adapter
|
||||
const dexieAdapter = new NDKCacheAdapterDexie({
|
||||
@@ -56,11 +56,22 @@ export const NDKProvider: React.FC<NDKProviderProps> = ({ children }) => {
|
||||
cacheAdapter: dexieAdapter,
|
||||
});
|
||||
|
||||
console.log('Connecting to relays...');
|
||||
await ndkInstance.connect();
|
||||
console.log('NDK connected successfully with cache');
|
||||
|
||||
// Set NDK instance immediately so UI can render
|
||||
setNdk(ndkInstance);
|
||||
|
||||
console.log('Connecting to relays...');
|
||||
// Connect without waiting - NDK handles relay connections in background
|
||||
ndkInstance.connect().then(() => {
|
||||
console.log('NDK connect() completed');
|
||||
}).catch((err) => {
|
||||
console.warn('NDK connect() had issues:', err);
|
||||
});
|
||||
|
||||
// Give relays a moment to connect, but don't block forever
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
|
||||
console.log('NDK initialized, relay connections in progress');
|
||||
setIsConnected(true);
|
||||
setIsLoading(false);
|
||||
} catch (error) {
|
||||
console.error('Failed to initialize NDK:', error);
|
||||
@@ -72,56 +83,39 @@ export const NDKProvider: React.FC<NDKProviderProps> = ({ children }) => {
|
||||
}, []);
|
||||
|
||||
const login = async () => {
|
||||
if (!ndk) return;
|
||||
if (!ndk) {
|
||||
throw new Error('NDK not initialized. Please wait for connection to relays.');
|
||||
}
|
||||
|
||||
try {
|
||||
setIsLoading(true);
|
||||
console.log('Attempting login with NIP-07 extension...');
|
||||
|
||||
// Check for NIP-07 extension
|
||||
if (window.nostr) {
|
||||
console.log('Nostr extension found, getting public key...');
|
||||
const pubkey = await window.nostr.getPublicKey();
|
||||
console.log('Got public key:', pubkey);
|
||||
if (typeof window !== 'undefined' && window.nostr) {
|
||||
console.log('Nostr extension found, creating NIP-07 signer...');
|
||||
|
||||
const ndkUser = ndk.getUser({ pubkey });
|
||||
// Use NDK's built-in NIP-07 signer
|
||||
const nip07Signer = new NDKNip07Signer();
|
||||
ndk.signer = nip07Signer;
|
||||
|
||||
// Set signer
|
||||
ndk.signer = {
|
||||
user: async () => ndkUser,
|
||||
sign: async (event: NDKEvent) => {
|
||||
console.log('Signing event with NIP-07 extension...');
|
||||
if (!window.nostr) throw new Error('Nostr extension not available');
|
||||
|
||||
// Get the raw event data for signing
|
||||
const rawEvent = {
|
||||
kind: event.kind,
|
||||
created_at: event.created_at || Math.floor(Date.now() / 1000),
|
||||
tags: event.tags,
|
||||
content: event.content,
|
||||
pubkey: event.pubkey,
|
||||
};
|
||||
|
||||
console.log('Raw event to sign:', rawEvent);
|
||||
|
||||
const signedEvent = await window.nostr.signEvent(rawEvent as any);
|
||||
console.log('Event signed successfully:', signedEvent);
|
||||
|
||||
event.sig = signedEvent.sig;
|
||||
return event.sig;
|
||||
},
|
||||
blockUntilReady: async () => true,
|
||||
} as unknown as NDKSigner;
|
||||
// Wait for the signer to be ready and get the user
|
||||
const ndkUser = await nip07Signer.user();
|
||||
console.log('Got user from signer:', ndkUser.pubkey);
|
||||
|
||||
setUser(ndkUser);
|
||||
setIsConnected(true);
|
||||
console.log('Login successful with NIP-07');
|
||||
|
||||
// Fetch user profile
|
||||
const profile = await fetchUserProfile(pubkey);
|
||||
setUserProfile(profile);
|
||||
// Fetch user profile (don't fail if this fails)
|
||||
try {
|
||||
const profile = await fetchUserProfile(ndkUser.pubkey);
|
||||
setUserProfile(profile);
|
||||
} catch (profileError) {
|
||||
console.warn('Could not fetch profile:', profileError);
|
||||
}
|
||||
} else {
|
||||
throw new Error('No Nostr extension found');
|
||||
throw new Error('No Nostr extension found. Please install a Nostr browser extension like Alby or nos2x.');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Login failed:', error);
|
||||
@@ -132,7 +126,9 @@ export const NDKProvider: React.FC<NDKProviderProps> = ({ children }) => {
|
||||
};
|
||||
|
||||
const loginWithKeys = async (npub: string, nsec: string) => {
|
||||
if (!ndk) return;
|
||||
if (!ndk) {
|
||||
throw new Error('NDK not initialized. Please wait for connection to relays.');
|
||||
}
|
||||
|
||||
try {
|
||||
setIsLoading(true);
|
||||
@@ -191,9 +187,13 @@ export const NDKProvider: React.FC<NDKProviderProps> = ({ children }) => {
|
||||
setIsConnected(true);
|
||||
console.log('Login successful with keys');
|
||||
|
||||
// Fetch user profile
|
||||
const profile = await fetchUserProfile(pubkey);
|
||||
setUserProfile(profile);
|
||||
// Fetch user profile (don't fail if this fails)
|
||||
try {
|
||||
const profile = await fetchUserProfile(pubkey);
|
||||
setUserProfile(profile);
|
||||
} catch (profileError) {
|
||||
console.warn('Could not fetch profile:', profileError);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Keys login failed:', error);
|
||||
throw error;
|
||||
@@ -202,26 +202,13 @@ export const NDKProvider: React.FC<NDKProviderProps> = ({ children }) => {
|
||||
}
|
||||
};
|
||||
|
||||
const loginWithNip55 = async (url: string) => {
|
||||
if (!ndk) return;
|
||||
|
||||
try {
|
||||
setIsLoading(true);
|
||||
console.log('Attempting NIP-55 login with URL:', url);
|
||||
|
||||
// TODO: Implement NIP-55 login
|
||||
// This would involve:
|
||||
// 1. Fetching the NIP-55 JSON from the URL
|
||||
// 2. Validating the response
|
||||
// 3. Setting up the signer for external signing
|
||||
|
||||
throw new Error('NIP-55 login not yet implemented');
|
||||
} catch (error) {
|
||||
console.error('NIP-55 login failed:', error);
|
||||
throw error;
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
const loginWithNip55 = async (_url: string) => {
|
||||
if (!ndk) {
|
||||
throw new Error('NDK not initialized. Please wait for connection to relays.');
|
||||
}
|
||||
|
||||
// NIP-55 is for Android Signer apps - not applicable for web browsers
|
||||
throw new Error('NIP-55 is for Android signer apps. Please use the Extension or Keys login method instead.');
|
||||
};
|
||||
|
||||
const logout = () => {
|
||||
|
||||
@@ -169,13 +169,17 @@ export function getCounterFilter(pubkey?: string, isPublic?: boolean): NDKFilter
|
||||
return filter;
|
||||
}
|
||||
|
||||
export const DEFAULT_RELAYS = [
|
||||
'wss://relay.azzamo.net',
|
||||
'wss://relay.damus.io',
|
||||
'wss://nostr.oxtr.dev',
|
||||
'wss://nos.lol',
|
||||
'wss://relay.snort.social',
|
||||
];
|
||||
// Read relays from environment variable, fallback to defaults
|
||||
const envRelays = import.meta.env.VITE_DEFAULT_RELAYS;
|
||||
export const DEFAULT_RELAYS: string[] = envRelays
|
||||
? envRelays.split(',').map((r: string) => r.trim()).filter((r: string) => r.length > 0)
|
||||
: [
|
||||
'wss://relay.azzamo.net',
|
||||
'wss://relay.damus.io',
|
||||
'wss://nostr.oxtr.dev',
|
||||
'wss://nos.lol',
|
||||
'wss://relay.snort.social',
|
||||
];
|
||||
|
||||
export function parseNip05(nip05: string): { name: string; domain: string } | null {
|
||||
if (!nip05 || !nip05.includes('@')) return null;
|
||||
|
||||
13
src/vite-env.d.ts
vendored
Normal file
13
src/vite-env.d.ts
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
/// <reference types="vite/client" />
|
||||
|
||||
interface ImportMetaEnv {
|
||||
readonly VITE_DEFAULT_RELAYS: string;
|
||||
readonly VITE_APP_NAME: string;
|
||||
readonly VITE_APP_DESCRIPTION: string;
|
||||
readonly VITE_APP_URL: string;
|
||||
}
|
||||
|
||||
interface ImportMeta {
|
||||
readonly env: ImportMetaEnv;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user