11import { useState , useEffect } from "react" ;
22import Button from "./button" ;
3+ import useLogin from "../hooks/login" ;
4+ import { useBlossomServers } from "../hooks/use-blossom-servers" ;
35
46interface ServerConfigProps {
57 onServersChange : ( servers : string [ ] ) => void ;
68}
79
8- const STORAGE_KEY = "blossom-servers" ;
9-
10- // Default servers from BUD-03 specification
11- // https://github.com/hzrd149/blossom/blob/master/buds/03.md
12- const DEFAULT_SERVERS = [
13- "https://cdn.satellite.earth" ,
14- "https://cdn.self.hosted"
15- ] ;
16-
1710export default function ServerConfig ( { onServersChange } : ServerConfigProps ) {
18- const [ servers , setServers ] = useState < string [ ] > ( [ ] ) ;
11+ const login = useLogin ( ) ;
12+ const nostrServers = useBlossomServers ( ) ; // Servers from nostr event kind 10063
13+ const [ manualServers , setManualServers ] = useState < string [ ] > ( [ ] ) ;
1914 const [ newServer , setNewServer ] = useState ( "" ) ;
2015 const [ error , setError ] = useState < string > ( ) ;
2116
22- useEffect ( ( ) => {
23- // Load servers from localStorage or use defaults
24- try {
25- const stored = localStorage . getItem ( STORAGE_KEY ) ;
26- if ( stored ) {
27- const parsedServers = JSON . parse ( stored ) ;
28- if ( Array . isArray ( parsedServers ) ) {
29- setServers ( parsedServers ) ;
30- onServersChange ( parsedServers ) ;
31- return ;
32- }
33- }
34-
35- // If no stored servers, use defaults from BUD-03
36- setServers ( DEFAULT_SERVERS ) ;
37- onServersChange ( DEFAULT_SERVERS ) ;
38- localStorage . setItem ( STORAGE_KEY , JSON . stringify ( DEFAULT_SERVERS ) ) ;
39- } catch ( e ) {
40- console . error ( "Failed to load servers from localStorage:" , e ) ;
41- // Fallback to defaults
42- setServers ( DEFAULT_SERVERS ) ;
43- onServersChange ( DEFAULT_SERVERS ) ;
44- }
45- } , [ onServersChange ] ) ;
17+ // Combine nostr servers with manually added servers
18+ const allServers = [ ...nostrServers , ...manualServers ] ;
4619
47- function saveServers ( newServers : string [ ] ) {
48- try {
49- localStorage . setItem ( STORAGE_KEY , JSON . stringify ( newServers ) ) ;
50- setServers ( newServers ) ;
51- onServersChange ( newServers ) ;
52- } catch ( e ) {
53- setError ( "Failed to save server configuration" ) ;
54- console . error ( "Failed to save servers to localStorage:" , e ) ;
55- }
56- }
20+ useEffect ( ( ) => {
21+ onServersChange ( allServers ) ;
22+ } , [ allServers , onServersChange ] ) ;
5723
5824 function addServer ( ) {
5925 if ( ! newServer . trim ( ) ) {
@@ -65,13 +31,13 @@ export default function ServerConfig({ onServersChange }: ServerConfigProps) {
6531 const url = new URL ( newServer . trim ( ) ) ;
6632 const serverUrl = url . toString ( ) . replace ( / \/ $ / , "" ) ; // Remove trailing slash
6733
68- if ( servers . includes ( serverUrl ) ) {
34+ if ( allServers . includes ( serverUrl ) ) {
6935 setError ( "Server already added" ) ;
7036 return ;
7137 }
7238
73- const updatedServers = [ ...servers , serverUrl ] ;
74- saveServers ( updatedServers ) ;
39+ const updatedServers = [ ...manualServers , serverUrl ] ;
40+ setManualServers ( updatedServers ) ;
7541 setNewServer ( "" ) ;
7642 setError ( undefined ) ;
7743 } catch ( e ) {
@@ -80,15 +46,22 @@ export default function ServerConfig({ onServersChange }: ServerConfigProps) {
8046 }
8147
8248 function removeServer ( serverToRemove : string ) {
83- const updatedServers = servers . filter ( s => s !== serverToRemove ) ;
84- saveServers ( updatedServers ) ;
49+ // Only allow removing manually added servers
50+ if ( nostrServers . includes ( serverToRemove ) ) {
51+ setError ( "Cannot remove servers from nostr profile. Update your kind 10063 event to remove them." ) ;
52+ return ;
53+ }
54+
55+ const updatedServers = manualServers . filter ( s => s !== serverToRemove ) ;
56+ setManualServers ( updatedServers ) ;
57+ setError ( undefined ) ;
8558 }
8659
8760 function addCurrentServer ( ) {
8861 const currentUrl = `${ window . location . protocol } //${ window . location . host } ` ;
89- if ( ! servers . includes ( currentUrl ) ) {
90- const updatedServers = [ ...servers , currentUrl ] ;
91- saveServers ( updatedServers ) ;
62+ if ( ! allServers . includes ( currentUrl ) ) {
63+ const updatedServers = [ ...manualServers , currentUrl ] ;
64+ setManualServers ( updatedServers ) ;
9265 }
9366 }
9467
@@ -97,10 +70,15 @@ export default function ServerConfig({ onServersChange }: ServerConfigProps) {
9770 < h3 className = "text-lg font-semibold mb-4" > Blossom Servers</ h3 >
9871 < p className = "text-gray-400 mb-4" >
9972 Configure your blossom servers to get mirror suggestions across them.
100- Defaults loaded from < a href = "https://github.com/hzrd149/blossom/blob/master/buds/03.md"
101- target = "_blank" rel = "noopener noreferrer" className = "text-blue-400 hover:text-blue-300" > BUD-03</ a > .
73+ Servers are loaded from your nostr profile event (kind 10063).
10274 </ p >
10375
76+ { ! login ?. pubkey && (
77+ < div className = "bg-yellow-900/20 border border-yellow-800 text-yellow-400 px-4 py-3 rounded-lg mb-4" >
78+ Please log in to configure your server list.
79+ </ div >
80+ ) }
81+
10482 { error && (
10583 < div className = "bg-red-900/20 border border-red-800 text-red-400 px-4 py-3 rounded-lg mb-4" >
10684 { error }
@@ -109,45 +87,65 @@ export default function ServerConfig({ onServersChange }: ServerConfigProps) {
10987
11088 < div className = "space-y-4" >
11189 { /* Current servers */ }
112- { servers . length > 0 && (
90+ { allServers . length > 0 && (
11391 < div >
11492 < h4 className = "text-sm font-medium text-gray-300 mb-2" > Configured Servers:</ h4 >
11593 < div className = "space-y-2" >
116- { servers . map ( ( server ) => (
117- < div key = { server } className = "flex items-center justify-between bg-gray-800 px-3 py-2 rounded" >
118- < span className = "text-sm text-gray-300" > { server } </ span >
119- < Button
120- onClick = { ( ) => removeServer ( server ) }
121- className = "btn-secondary text-xs py-1 px-2"
122- >
123- Remove
124- </ Button >
125- </ div >
126- ) ) }
94+ { allServers . map ( ( server ) => {
95+ const isFromNostr = nostrServers . includes ( server ) ;
96+ return (
97+ < div key = { server } className = "flex items-center justify-between bg-gray-800 px-3 py-2 rounded" >
98+ < div className = "flex items-center gap-2" >
99+ < span className = "text-sm text-gray-300" > { server } </ span >
100+ { isFromNostr && (
101+ < span className = "text-xs bg-blue-900/50 text-blue-300 px-2 py-1 rounded" >
102+ from nostr
103+ </ span >
104+ ) }
105+ </ div >
106+ { ! isFromNostr && (
107+ < Button
108+ onClick = { ( ) => removeServer ( server ) }
109+ className = "btn-secondary text-xs py-1 px-2"
110+ >
111+ Remove
112+ </ Button >
113+ ) }
114+ </ div >
115+ ) ;
116+ } ) }
127117 </ div >
128118 </ div >
129119 ) }
130120
121+ { login ?. pubkey && allServers . length === 0 && (
122+ < div className = "text-gray-400 text-sm" >
123+ No servers configured. Add servers below to enable mirror suggestions, or create a kind 10063 nostr event with server tags.
124+ </ div >
125+ ) }
126+
131127 { /* Add server form */ }
132- < div >
133- < h4 className = "text-sm font-medium text-gray-300 mb-2" > Add Server:</ h4 >
134- < div className = "flex gap-2" >
135- < input
136- type = "url"
137- value = { newServer }
138- onChange = { ( e ) => setNewServer ( e . target . value ) }
139- placeholder = "https://example.com"
140- className = "flex-1 bg-gray-800 border border-gray-700 rounded px-3 py-2 text-sm text-gray-100 placeholder-gray-500 focus:outline-none focus:ring-2 focus:ring-blue-500"
141- onKeyPress = { ( e ) => e . key === "Enter" && addServer ( ) }
142- />
143- < Button onClick = { addServer } className = "btn-primary" >
144- Add
145- </ Button >
128+ { login ?. pubkey && (
129+ < div >
130+ < h4 className = "text-sm font-medium text-gray-300 mb-2" > Add Server:</ h4 >
131+ < div className = "flex gap-2" >
132+ < input
133+ type = "url"
134+ value = { newServer }
135+ onChange = { ( e ) => setNewServer ( e . target . value ) }
136+ placeholder = "https://example.com"
137+ className = "flex-1 bg-gray-800 border border-gray-700 rounded px-3 py-2 text-sm text-gray-100 placeholder-gray-500 focus:outline-none focus:ring-2 focus:ring-blue-500"
138+ onKeyPress = { ( e ) => e . key === "Enter" && addServer ( ) }
139+ />
140+ < Button onClick = { addServer } className = "btn-primary" >
141+ Add
142+ </ Button >
143+ </ div >
146144 </ div >
147- </ div >
145+ ) }
148146
149147 { /* Quick add current server */ }
150- { ! servers . includes ( `${ window . location . protocol } //${ window . location . host } ` ) && (
148+ { login ?. pubkey && ! allServers . includes ( `${ window . location . protocol } //${ window . location . host } ` ) && (
151149 < div className = "pt-2 border-t border-gray-700" >
152150 < Button onClick = { addCurrentServer } className = "btn-secondary text-sm" >
153151 Add Current Server ({ window . location . host } )
0 commit comments