mirror of
https://github.com/dergigi/boris.git
synced 2025-12-19 07:34:28 +01:00
feat: add local relay support and centralize relay configuration
This commit is contained in:
26
src/App.tsx
26
src/App.tsx
@@ -11,6 +11,7 @@ import { createAddressLoader } from 'applesauce-loaders/loaders'
|
|||||||
import Bookmarks from './components/Bookmarks'
|
import Bookmarks from './components/Bookmarks'
|
||||||
import Toast from './components/Toast'
|
import Toast from './components/Toast'
|
||||||
import { useToast } from './hooks/useToast'
|
import { useToast } from './hooks/useToast'
|
||||||
|
import { ALL_RELAYS, PROFILE_RELAYS } from './config/relays'
|
||||||
|
|
||||||
const DEFAULT_ARTICLE = import.meta.env.VITE_DEFAULT_ARTICLE_NADDR ||
|
const DEFAULT_ARTICLE = import.meta.env.VITE_DEFAULT_ARTICLE_NADDR ||
|
||||||
'naddr1qvzqqqr4gupzqmjxss3dld622uu8q25gywum9qtg4w4cv4064jmg20xsac2aam5nqqxnzd3cxqmrzv3exgmr2wfesgsmew'
|
'naddr1qvzqqqr4gupzqmjxss3dld622uu8q25gywum9qtg4w4cv4064jmg20xsac2aam5nqqxnzd3cxqmrzv3exgmr2wfesgsmew'
|
||||||
@@ -94,33 +95,16 @@ function App() {
|
|||||||
|
|
||||||
const pool = new RelayPool()
|
const pool = new RelayPool()
|
||||||
|
|
||||||
// Define relay URLs for bookmark fetching
|
|
||||||
const relayUrls = [
|
|
||||||
'wss://relay.damus.io',
|
|
||||||
'wss://nos.lol',
|
|
||||||
'wss://relay.nostr.band',
|
|
||||||
'wss://relay.dergigi.com',
|
|
||||||
'wss://wot.dergigi.com',
|
|
||||||
'wss://relay.snort.social',
|
|
||||||
'wss://relay.current.fyi',
|
|
||||||
'wss://nostr-pub.wellorder.net'
|
|
||||||
]
|
|
||||||
|
|
||||||
// Create a relay group for better event deduplication and management
|
// Create a relay group for better event deduplication and management
|
||||||
// This follows the applesauce-relay documentation pattern
|
// This follows the applesauce-relay documentation pattern
|
||||||
// Note: We could use pool.group(relayUrls) for direct requests in the future
|
pool.group(ALL_RELAYS)
|
||||||
pool.group(relayUrls)
|
console.log('Created relay group with', ALL_RELAYS.length, 'relays (including local)')
|
||||||
console.log('Created relay group with', relayUrls.length, 'relays')
|
console.log('Relay URLs:', ALL_RELAYS)
|
||||||
console.log('Relay URLs:', relayUrls)
|
|
||||||
|
|
||||||
// Attach address/replaceable loaders so ProfileModel can fetch profiles
|
// Attach address/replaceable loaders so ProfileModel can fetch profiles
|
||||||
const addressLoader = createAddressLoader(pool, {
|
const addressLoader = createAddressLoader(pool, {
|
||||||
eventStore: store,
|
eventStore: store,
|
||||||
lookupRelays: [
|
lookupRelays: PROFILE_RELAYS
|
||||||
'wss://purplepag.es',
|
|
||||||
'wss://relay.primal.net',
|
|
||||||
'wss://relay.nostr.band'
|
|
||||||
]
|
|
||||||
})
|
})
|
||||||
store.addressableLoader = addressLoader
|
store.addressableLoader = addressLoader
|
||||||
store.replaceableLoader = addressLoader
|
store.replaceableLoader = addressLoader
|
||||||
|
|||||||
70
src/config/relays.ts
Normal file
70
src/config/relays.ts
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
/**
|
||||||
|
* Centralized relay configuration
|
||||||
|
* All relay URLs used throughout the application
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Local relay URL (hardcoded for development)
|
||||||
|
export const LOCAL_RELAY = 'ws://localhost:7777'
|
||||||
|
|
||||||
|
// Public relays for general use
|
||||||
|
export const PUBLIC_RELAYS = [
|
||||||
|
'wss://relay.damus.io',
|
||||||
|
'wss://nos.lol',
|
||||||
|
'wss://relay.nostr.band',
|
||||||
|
'wss://relay.dergigi.com',
|
||||||
|
'wss://wot.dergigi.com',
|
||||||
|
'wss://relay.snort.social',
|
||||||
|
'wss://relay.current.fyi',
|
||||||
|
'wss://nostr-pub.wellorder.net'
|
||||||
|
]
|
||||||
|
|
||||||
|
// Relays for profile lookups
|
||||||
|
export const PROFILE_RELAYS = [
|
||||||
|
'wss://purplepag.es',
|
||||||
|
'wss://relay.primal.net',
|
||||||
|
'wss://relay.nostr.band'
|
||||||
|
]
|
||||||
|
|
||||||
|
// Relays for highlights (read and write)
|
||||||
|
export const HIGHLIGHT_RELAYS = [
|
||||||
|
LOCAL_RELAY,
|
||||||
|
'wss://relay.damus.io',
|
||||||
|
'wss://nos.lol',
|
||||||
|
'wss://relay.nostr.band',
|
||||||
|
'wss://relay.snort.social',
|
||||||
|
'wss://purplepag.es'
|
||||||
|
]
|
||||||
|
|
||||||
|
// Relays for articles
|
||||||
|
export const ARTICLE_RELAYS = [
|
||||||
|
LOCAL_RELAY,
|
||||||
|
'wss://relay.damus.io',
|
||||||
|
'wss://nos.lol',
|
||||||
|
'wss://relay.nostr.band',
|
||||||
|
'wss://relay.primal.net'
|
||||||
|
]
|
||||||
|
|
||||||
|
// Relays for settings (read and write)
|
||||||
|
export const SETTINGS_RELAYS = [
|
||||||
|
LOCAL_RELAY,
|
||||||
|
'wss://relay.damus.io',
|
||||||
|
'wss://nos.lol',
|
||||||
|
'wss://relay.nostr.band',
|
||||||
|
'wss://relay.dergigi.com',
|
||||||
|
'wss://wot.dergigi.com'
|
||||||
|
]
|
||||||
|
|
||||||
|
// All relays including local (for general operations and relay pool initialization)
|
||||||
|
export const ALL_RELAYS = [LOCAL_RELAY, ...PUBLIC_RELAYS]
|
||||||
|
|
||||||
|
// All write relays (where we publish events)
|
||||||
|
export const WRITE_RELAYS = [
|
||||||
|
LOCAL_RELAY,
|
||||||
|
'wss://relay.damus.io',
|
||||||
|
'wss://nos.lol',
|
||||||
|
'wss://relay.nostr.band',
|
||||||
|
'wss://relay.snort.social',
|
||||||
|
'wss://purplepag.es',
|
||||||
|
'wss://relay.dergigi.com'
|
||||||
|
]
|
||||||
|
|
||||||
@@ -5,11 +5,7 @@ import { EventFactory } from 'applesauce-factory'
|
|||||||
import { AccountManager } from 'applesauce-accounts'
|
import { AccountManager } from 'applesauce-accounts'
|
||||||
import { UserSettings, loadSettings, saveSettings, watchSettings } from '../services/settingsService'
|
import { UserSettings, loadSettings, saveSettings, watchSettings } from '../services/settingsService'
|
||||||
import { loadFont, getFontFamily } from '../utils/fontLoader'
|
import { loadFont, getFontFamily } from '../utils/fontLoader'
|
||||||
|
import { SETTINGS_RELAYS } from '../config/relays'
|
||||||
const RELAY_URLS = [
|
|
||||||
'wss://relay.damus.io', 'wss://nos.lol', 'wss://relay.nostr.band',
|
|
||||||
'wss://relay.dergigi.com', 'wss://wot.dergigi.com'
|
|
||||||
]
|
|
||||||
|
|
||||||
interface UseSettingsParams {
|
interface UseSettingsParams {
|
||||||
relayPool: RelayPool | null
|
relayPool: RelayPool | null
|
||||||
@@ -29,7 +25,7 @@ export function useSettings({ relayPool, eventStore, pubkey, accountManager }: U
|
|||||||
|
|
||||||
const loadAndWatch = async () => {
|
const loadAndWatch = async () => {
|
||||||
try {
|
try {
|
||||||
const loadedSettings = await loadSettings(relayPool, eventStore, pubkey, RELAY_URLS)
|
const loadedSettings = await loadSettings(relayPool, eventStore, pubkey, SETTINGS_RELAYS)
|
||||||
if (loadedSettings) setSettings(loadedSettings)
|
if (loadedSettings) setSettings(loadedSettings)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('Failed to load settings:', err)
|
console.error('Failed to load settings:', err)
|
||||||
@@ -65,7 +61,7 @@ export function useSettings({ relayPool, eventStore, pubkey, accountManager }: U
|
|||||||
const fullAccount = accountManager.getActive()
|
const fullAccount = accountManager.getActive()
|
||||||
if (!fullAccount) throw new Error('No active account')
|
if (!fullAccount) throw new Error('No active account')
|
||||||
const factory = new EventFactory({ signer: fullAccount })
|
const factory = new EventFactory({ signer: fullAccount })
|
||||||
await saveSettings(relayPool, eventStore, factory, newSettings, RELAY_URLS)
|
await saveSettings(relayPool, eventStore, factory, newSettings, SETTINGS_RELAYS)
|
||||||
setSettings(newSettings)
|
setSettings(newSettings)
|
||||||
setToastType('success')
|
setToastType('success')
|
||||||
setToastMessage('Settings saved')
|
setToastMessage('Settings saved')
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import {
|
|||||||
getArticlePublished,
|
getArticlePublished,
|
||||||
getArticleSummary
|
getArticleSummary
|
||||||
} from 'applesauce-core/helpers'
|
} from 'applesauce-core/helpers'
|
||||||
|
import { ARTICLE_RELAYS } from '../config/relays'
|
||||||
|
|
||||||
export interface ArticleContent {
|
export interface ArticleContent {
|
||||||
title: string
|
title: string
|
||||||
@@ -95,15 +96,10 @@ export async function fetchArticleByNaddr(
|
|||||||
|
|
||||||
const pointer = decoded.data as AddressPointer
|
const pointer = decoded.data as AddressPointer
|
||||||
|
|
||||||
// Define relays to query
|
// Define relays to query - prefer relays from naddr, fallback to configured relays (including local)
|
||||||
const relays = pointer.relays && pointer.relays.length > 0
|
const relays = pointer.relays && pointer.relays.length > 0
|
||||||
? pointer.relays
|
? pointer.relays
|
||||||
: [
|
: ARTICLE_RELAYS
|
||||||
'wss://relay.damus.io',
|
|
||||||
'wss://nos.lol',
|
|
||||||
'wss://relay.nostr.band',
|
|
||||||
'wss://relay.primal.net'
|
|
||||||
]
|
|
||||||
|
|
||||||
// Fetch the article event
|
// Fetch the article event
|
||||||
const filter = {
|
const filter = {
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import { RelayPool } from 'applesauce-relay'
|
|||||||
import { IAccount } from 'applesauce-accounts'
|
import { IAccount } from 'applesauce-accounts'
|
||||||
import { AddressPointer } from 'nostr-tools/nip19'
|
import { AddressPointer } from 'nostr-tools/nip19'
|
||||||
import { NostrEvent } from 'nostr-tools'
|
import { NostrEvent } from 'nostr-tools'
|
||||||
|
import { WRITE_RELAYS } from '../config/relays'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates and publishes a highlight event (NIP-84)
|
* Creates and publishes a highlight event (NIP-84)
|
||||||
@@ -36,18 +37,10 @@ export async function createHighlight(
|
|||||||
// Sign the event
|
// Sign the event
|
||||||
const signedEvent = await factory.sign(highlightEvent)
|
const signedEvent = await factory.sign(highlightEvent)
|
||||||
|
|
||||||
// Publish to relays
|
// Publish to relays (including local relay)
|
||||||
const relayUrls = [
|
await relayPool.publish(WRITE_RELAYS, signedEvent)
|
||||||
'wss://relay.damus.io',
|
|
||||||
'wss://nos.lol',
|
|
||||||
'wss://relay.nostr.band',
|
|
||||||
'wss://relay.snort.social',
|
|
||||||
'wss://purplepag.es'
|
|
||||||
]
|
|
||||||
|
|
||||||
await relayPool.publish(relayUrls, signedEvent)
|
|
||||||
|
|
||||||
console.log('✅ Highlight published:', signedEvent)
|
console.log('✅ Highlight published to', WRITE_RELAYS.length, 'relays (including local):', signedEvent)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import {
|
|||||||
getHighlightAttributions
|
getHighlightAttributions
|
||||||
} from 'applesauce-core/helpers'
|
} from 'applesauce-core/helpers'
|
||||||
import { Highlight } from '../types/highlights'
|
import { Highlight } from '../types/highlights'
|
||||||
|
import { HIGHLIGHT_RELAYS } from '../config/relays'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Deduplicate highlight events by ID
|
* Deduplicate highlight events by ID
|
||||||
@@ -42,18 +43,9 @@ export const fetchHighlightsForArticle = async (
|
|||||||
onHighlight?: (highlight: Highlight) => void
|
onHighlight?: (highlight: Highlight) => void
|
||||||
): Promise<Highlight[]> => {
|
): Promise<Highlight[]> => {
|
||||||
try {
|
try {
|
||||||
// Use well-known relays for highlights even if user isn't logged in
|
|
||||||
const highlightRelays = [
|
|
||||||
'wss://relay.damus.io',
|
|
||||||
'wss://nos.lol',
|
|
||||||
'wss://relay.nostr.band',
|
|
||||||
'wss://relay.snort.social',
|
|
||||||
'wss://purplepag.es'
|
|
||||||
]
|
|
||||||
|
|
||||||
console.log('🔍 Fetching highlights (kind 9802) for article:', articleCoordinate)
|
console.log('🔍 Fetching highlights (kind 9802) for article:', articleCoordinate)
|
||||||
console.log('🔍 Event ID:', eventId || 'none')
|
console.log('🔍 Event ID:', eventId || 'none')
|
||||||
console.log('🔍 From relays:', highlightRelays)
|
console.log('🔍 From relays (including local):', HIGHLIGHT_RELAYS)
|
||||||
|
|
||||||
const seenIds = new Set<string>()
|
const seenIds = new Set<string>()
|
||||||
const processEvent = (event: NostrEvent): Highlight | null => {
|
const processEvent = (event: NostrEvent): Highlight | null => {
|
||||||
@@ -89,7 +81,7 @@ export const fetchHighlightsForArticle = async (
|
|||||||
// Query for highlights that reference this article via the 'a' tag
|
// Query for highlights that reference this article via the 'a' tag
|
||||||
const aTagEvents = await lastValueFrom(
|
const aTagEvents = await lastValueFrom(
|
||||||
relayPool
|
relayPool
|
||||||
.req(highlightRelays, { kinds: [9802], '#a': [articleCoordinate] })
|
.req(HIGHLIGHT_RELAYS, { kinds: [9802], '#a': [articleCoordinate] })
|
||||||
.pipe(
|
.pipe(
|
||||||
onlyEvents(),
|
onlyEvents(),
|
||||||
tap((event: NostrEvent) => {
|
tap((event: NostrEvent) => {
|
||||||
@@ -111,7 +103,7 @@ export const fetchHighlightsForArticle = async (
|
|||||||
if (eventId) {
|
if (eventId) {
|
||||||
eTagEvents = await lastValueFrom(
|
eTagEvents = await lastValueFrom(
|
||||||
relayPool
|
relayPool
|
||||||
.req(highlightRelays, { kinds: [9802], '#e': [eventId] })
|
.req(HIGHLIGHT_RELAYS, { kinds: [9802], '#e': [eventId] })
|
||||||
.pipe(
|
.pipe(
|
||||||
onlyEvents(),
|
onlyEvents(),
|
||||||
tap((event: NostrEvent) => {
|
tap((event: NostrEvent) => {
|
||||||
|
|||||||
Reference in New Issue
Block a user