feat: add customizable search relays setting
Replace hardcoded SEARCHABLE_RELAY_URLS with user-configurable search relays stored in localStorage. Add SearchRelaysSetting UI in System settings page with add/remove/reset functionality. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
7be7b30d52
commit
aae8fc2f17
27 changed files with 202 additions and 50 deletions
|
|
@ -1,8 +1,8 @@
|
|||
import KindFilter from '@/components/KindFilter'
|
||||
import NoteList, { TNoteListRef } from '@/components/NoteList'
|
||||
import Tabs from '@/components/Tabs'
|
||||
import { MAX_PINNED_NOTES, SEARCHABLE_RELAY_URLS } from '@/constants'
|
||||
import { getDefaultRelayUrls } from '@/lib/relay'
|
||||
import { MAX_PINNED_NOTES } from '@/constants'
|
||||
import { getDefaultRelayUrls, getSearchRelayUrls } from '@/lib/relay'
|
||||
import { generateBech32IdFromETag } from '@/lib/tag'
|
||||
import { isTouchDevice } from '@/lib/utils'
|
||||
import { useKindFilter } from '@/providers/KindFilterProvider'
|
||||
|
|
@ -125,7 +125,7 @@ export default function ProfileFeed({
|
|||
)
|
||||
setSubRequests([
|
||||
{
|
||||
urls: searchableRelays.concat(SEARCHABLE_RELAY_URLS).slice(0, 8),
|
||||
urls: searchableRelays.concat(getSearchRelayUrls()).slice(0, 8),
|
||||
filter: { authors: [pubkey], search }
|
||||
}
|
||||
])
|
||||
|
|
|
|||
103
src/components/SearchRelaysSetting/index.tsx
Normal file
103
src/components/SearchRelaysSetting/index.tsx
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
import { Button } from '@/components/ui/button'
|
||||
import { Input } from '@/components/ui/input'
|
||||
import { Label } from '@/components/ui/label'
|
||||
import { SEARCHABLE_RELAY_URLS } from '@/constants'
|
||||
import { isWebsocketUrl, normalizeUrl } from '@/lib/url'
|
||||
import storage from '@/services/local-storage.service'
|
||||
import { CircleX } from 'lucide-react'
|
||||
import { useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import RelayIcon from '../RelayIcon'
|
||||
|
||||
export default function SearchRelaysSetting() {
|
||||
const { t } = useTranslation()
|
||||
const [relayUrls, setRelayUrls] = useState<string[]>(storage.getSearchRelayUrls())
|
||||
const [newRelayUrl, setNewRelayUrl] = useState('')
|
||||
const [newRelayUrlError, setNewRelayUrlError] = useState<string | null>(null)
|
||||
|
||||
const removeRelayUrl = (url: string) => {
|
||||
const normalizedUrl = normalizeUrl(url)
|
||||
if (!normalizedUrl) return
|
||||
const newUrls = relayUrls.filter((u) => u !== normalizedUrl)
|
||||
setRelayUrls(newUrls)
|
||||
storage.setSearchRelayUrls(newUrls)
|
||||
}
|
||||
|
||||
const saveNewRelayUrl = () => {
|
||||
if (newRelayUrl === '') return
|
||||
const normalizedUrl = normalizeUrl(newRelayUrl)
|
||||
if (!normalizedUrl) {
|
||||
return setNewRelayUrlError(t('Invalid relay URL'))
|
||||
}
|
||||
if (relayUrls.includes(normalizedUrl)) {
|
||||
return setNewRelayUrlError(t('Relay already exists'))
|
||||
}
|
||||
if (!isWebsocketUrl(normalizedUrl)) {
|
||||
return setNewRelayUrlError(t('invalid relay URL'))
|
||||
}
|
||||
const newUrls = [...relayUrls, normalizedUrl]
|
||||
setRelayUrls(newUrls)
|
||||
storage.setSearchRelayUrls(newUrls)
|
||||
setNewRelayUrl('')
|
||||
}
|
||||
|
||||
const handleRelayUrlInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setNewRelayUrl(e.target.value)
|
||||
setNewRelayUrlError(null)
|
||||
}
|
||||
|
||||
const handleRelayUrlInputKeyDown = (event: React.KeyboardEvent<HTMLInputElement>) => {
|
||||
if (event.key === 'Enter') {
|
||||
event.preventDefault()
|
||||
saveNewRelayUrl()
|
||||
}
|
||||
}
|
||||
|
||||
const resetToDefault = () => {
|
||||
setRelayUrls(SEARCHABLE_RELAY_URLS)
|
||||
storage.setSearchRelayUrls(SEARCHABLE_RELAY_URLS)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="space-y-2">
|
||||
<div className="flex items-center justify-between">
|
||||
<Label className="text-base font-normal">{t('Search relays')}</Label>
|
||||
<Button variant="outline" size="sm" onClick={resetToDefault}>
|
||||
{t('Reset to default')}
|
||||
</Button>
|
||||
</div>
|
||||
<div className="text-xs text-muted-foreground">
|
||||
{t('Relays used for searching notes (NIP-50)')}
|
||||
</div>
|
||||
<div className="mt-1">
|
||||
{relayUrls.map((url, index) => (
|
||||
<div key={index} className="flex items-center justify-between py-1 pl-1 pr-3">
|
||||
<div className="flex w-0 flex-1 items-center gap-3">
|
||||
<RelayIcon url={url} className="h-4 w-4" />
|
||||
<div className="truncate text-sm text-muted-foreground">{url}</div>
|
||||
</div>
|
||||
<div className="shrink-0">
|
||||
<CircleX
|
||||
size={16}
|
||||
onClick={() => removeRelayUrl(url)}
|
||||
className="cursor-pointer text-muted-foreground hover:text-destructive"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<div className="mt-2 flex gap-2">
|
||||
<Input
|
||||
className={newRelayUrlError ? 'border-destructive' : ''}
|
||||
placeholder={t('Add a new relay')}
|
||||
value={newRelayUrl}
|
||||
onKeyDown={handleRelayUrlInputKeyDown}
|
||||
onChange={handleRelayUrlInputChange}
|
||||
onBlur={saveNewRelayUrl}
|
||||
/>
|
||||
<Button onClick={saveNewRelayUrl}>{t('Add')}</Button>
|
||||
</div>
|
||||
{newRelayUrlError && <div className="mt-1 text-xs text-destructive">{newRelayUrlError}</div>}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
import { SEARCHABLE_RELAY_URLS, SPECIAL_TRUST_SCORE_FILTER_ID } from '@/constants'
|
||||
import { getDefaultRelayUrls } from '@/lib/relay'
|
||||
import { SPECIAL_TRUST_SCORE_FILTER_ID } from '@/constants'
|
||||
import { getDefaultRelayUrls, getSearchRelayUrls } from '@/lib/relay'
|
||||
import { TSearchParams } from '@/types'
|
||||
import NormalFeed from '../NormalFeed'
|
||||
import Profile from '../Profile'
|
||||
|
|
@ -21,7 +21,7 @@ export default function SearchResult({ searchParams }: { searchParams: TSearchPa
|
|||
return (
|
||||
<NormalFeed
|
||||
trustScoreFilterId={SPECIAL_TRUST_SCORE_FILTER_ID.SEARCH}
|
||||
subRequests={[{ urls: SEARCHABLE_RELAY_URLS, filter: { search: searchParams.search } }]}
|
||||
subRequests={[{ urls: getSearchRelayUrls(), filter: { search: searchParams.search } }]}
|
||||
showRelayCloseReason
|
||||
/>
|
||||
)
|
||||
|
|
|
|||
|
|
@ -47,6 +47,7 @@ export const StorageKey = {
|
|||
MUTED_WORDS: 'mutedWords',
|
||||
MIN_TRUST_SCORE: 'minTrustScore',
|
||||
MIN_TRUST_SCORE_MAP: 'minTrustScoreMap',
|
||||
SEARCH_RELAY_URLS: 'searchRelayUrls',
|
||||
HIDE_INDIRECT_NOTIFICATIONS: 'hideIndirectNotifications',
|
||||
ENABLE_LIVE_FEED: 'enableLiveFeed', // deprecated
|
||||
HIDE_UNTRUSTED_NOTES: 'hideUntrustedNotes', // deprecated
|
||||
|
|
@ -76,11 +77,7 @@ export const BIG_RELAY_URLS = [
|
|||
'wss://offchain.pub/'
|
||||
]
|
||||
|
||||
export const SEARCHABLE_RELAY_URLS = [
|
||||
'wss://search.nos.today/',
|
||||
'wss://relay.ditto.pub/',
|
||||
'wss://relay.nostr.band/'
|
||||
]
|
||||
export const SEARCHABLE_RELAY_URLS = ['wss://search.nos.today/', 'wss://relay.nostr.band/']
|
||||
|
||||
export const TRENDING_NOTES_RELAY_URLS = ['wss://trending.relays.land/']
|
||||
|
||||
|
|
|
|||
|
|
@ -163,7 +163,7 @@ export default {
|
|||
'Send only to r': 'إرسال فقط إلى {{r}}',
|
||||
'Send only to these relays': 'إرسال فقط إلى هذه الريلايات',
|
||||
Explore: 'استكشاف',
|
||||
'Search relays': 'البحث في الريلايات',
|
||||
'Search relays': 'ريلايات البحث',
|
||||
relayInfoBadgeAuth: 'مصادقة',
|
||||
relayInfoBadgeSearch: 'بحث',
|
||||
relayInfoBadgePayment: 'دفع',
|
||||
|
|
@ -670,6 +670,7 @@ export default {
|
|||
'Hide indirect': 'إخفاء غير المباشرة',
|
||||
'Copy note content': 'نسخ محتوى الملاحظة',
|
||||
'Video loop': 'تكرار الفيديو',
|
||||
'Automatically replay videos when they end': 'إعادة تشغيل مقاطع الفيديو تلقائيًا عند انتهائها'
|
||||
'Automatically replay videos when they end': 'إعادة تشغيل مقاطع الفيديو تلقائيًا عند انتهائها',
|
||||
'Relays used for searching notes (NIP-50)': 'الريلايات المستخدمة للبحث عن الملاحظات (NIP-50)'
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -167,7 +167,7 @@ export default {
|
|||
'Send only to r': 'Nur an {{r}} senden',
|
||||
'Send only to these relays': 'Nur an diese Relays senden',
|
||||
Explore: 'Entdecken',
|
||||
'Search relays': 'Relays suchen',
|
||||
'Search relays': 'Such-Relays',
|
||||
relayInfoBadgeAuth: 'Auth',
|
||||
relayInfoBadgeSearch: 'Suche',
|
||||
relayInfoBadgePayment: 'Zahlung',
|
||||
|
|
@ -694,6 +694,7 @@ export default {
|
|||
'Hide indirect': 'Indirekte ausblenden',
|
||||
'Copy note content': 'Notizinhalt kopieren',
|
||||
'Video loop': 'Video-Schleife',
|
||||
'Automatically replay videos when they end': 'Videos automatisch wiederholen, wenn sie enden'
|
||||
'Automatically replay videos when they end': 'Videos automatisch wiederholen, wenn sie enden',
|
||||
'Relays used for searching notes (NIP-50)': 'Relays für die Notizsuche (NIP-50)'
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -676,6 +676,7 @@ export default {
|
|||
'Hide indirect': 'Hide indirect',
|
||||
'Copy note content': 'Copy note content',
|
||||
'Video loop': 'Video loop',
|
||||
'Automatically replay videos when they end': 'Automatically replay videos when they end'
|
||||
'Automatically replay videos when they end': 'Automatically replay videos when they end',
|
||||
'Relays used for searching notes (NIP-50)': 'Relays used for searching notes (NIP-50)'
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -167,7 +167,7 @@ export default {
|
|||
'Send only to r': 'Enviar únicamente a {{r}}',
|
||||
'Send only to these relays': 'Enviar únicamente a estos relés',
|
||||
Explore: 'Explorar',
|
||||
'Search relays': 'Buscar relés',
|
||||
'Search relays': 'Relés de búsqueda',
|
||||
relayInfoBadgeAuth: 'Autenticación',
|
||||
relayInfoBadgeSearch: 'Búsqueda',
|
||||
relayInfoBadgePayment: 'Pago',
|
||||
|
|
@ -687,6 +687,7 @@ export default {
|
|||
'Hide indirect': 'Ocultar indirectas',
|
||||
'Copy note content': 'Copiar contenido de la nota',
|
||||
'Video loop': 'Repetir video',
|
||||
'Automatically replay videos when they end': 'Reproducir automáticamente los videos cuando terminen'
|
||||
'Automatically replay videos when they end': 'Reproducir automáticamente los videos cuando terminen',
|
||||
'Relays used for searching notes (NIP-50)': 'Relés utilizados para buscar notas (NIP-50)'
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -165,7 +165,7 @@ export default {
|
|||
'Send only to r': 'فقط به {{r}} ارسال شود',
|
||||
'Send only to these relays': 'فقط به این رلهها ارسال شود',
|
||||
Explore: 'کاوش',
|
||||
'Search relays': 'جستجو رلهها',
|
||||
'Search relays': 'رلههای جستجو',
|
||||
relayInfoBadgeAuth: 'احراز هویت',
|
||||
relayInfoBadgeSearch: 'جستجو',
|
||||
relayInfoBadgePayment: 'پرداخت',
|
||||
|
|
@ -682,6 +682,7 @@ export default {
|
|||
'Hide indirect': 'پنهان کردن غیرمستقیم',
|
||||
'Copy note content': 'کپی محتوای یادداشت',
|
||||
'Video loop': 'تکرار ویدیو',
|
||||
'Automatically replay videos when they end': 'پخش خودکار ویدیوها پس از پایان'
|
||||
'Automatically replay videos when they end': 'پخش خودکار ویدیوها پس از پایان',
|
||||
'Relays used for searching notes (NIP-50)': 'رلههایی که برای جستجوی یادداشتها استفاده میشوند (NIP-50)'
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -166,7 +166,7 @@ export default {
|
|||
'Send only to r': 'Envoyer uniquement à {{r}}',
|
||||
'Send only to these relays': 'Envoyer uniquement à ces relais',
|
||||
Explore: 'Explorer',
|
||||
'Search relays': 'Rechercher des relais',
|
||||
'Search relays': 'Relais de recherche',
|
||||
relayInfoBadgeAuth: 'Auth',
|
||||
relayInfoBadgeSearch: 'Recherche',
|
||||
relayInfoBadgePayment: 'Paiement',
|
||||
|
|
@ -691,6 +691,7 @@ export default {
|
|||
'Hide indirect': 'Masquer indirects',
|
||||
'Copy note content': 'Copier le contenu de la note',
|
||||
'Video loop': 'Boucle vidéo',
|
||||
'Automatically replay videos when they end': 'Rejouer automatiquement les vidéos à la fin'
|
||||
'Automatically replay videos when they end': 'Rejouer automatiquement les vidéos à la fin',
|
||||
'Relays used for searching notes (NIP-50)': 'Relais utilisés pour rechercher des notes (NIP-50)'
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -166,7 +166,7 @@ export default {
|
|||
'Send only to r': 'केवल {{r}} को भेजें',
|
||||
'Send only to these relays': 'केवल इन रिले को भेजें',
|
||||
Explore: 'एक्सप्लोर करें',
|
||||
'Search relays': 'रिले खोजें',
|
||||
'Search relays': 'खोज रिले',
|
||||
relayInfoBadgeAuth: 'प्रमाणीकरण',
|
||||
relayInfoBadgeSearch: 'खोज',
|
||||
relayInfoBadgePayment: 'भुगतान',
|
||||
|
|
@ -682,6 +682,7 @@ export default {
|
|||
'Hide indirect': 'अप्रत्यक्ष छुपाएं',
|
||||
'Copy note content': 'नोट सामग्री कॉपी करें',
|
||||
'Video loop': 'वीडियो लूप',
|
||||
'Automatically replay videos when they end': 'वीडियो समाप्त होने पर स्वचालित रूप से दोबारा चलाएं'
|
||||
'Automatically replay videos when they end': 'वीडियो समाप्त होने पर स्वचालित रूप से दोबारा चलाएं',
|
||||
'Relays used for searching notes (NIP-50)': 'नोट्स खोजने के लिए उपयोग किए जाने वाले रिले (NIP-50)'
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -165,7 +165,7 @@ export default {
|
|||
'Send only to r': 'Küldés csak a {{r}} csomópontra',
|
||||
'Send only to these relays': 'Küldés csak ezekre a csomópontokra',
|
||||
Explore: 'Felderítés',
|
||||
'Search relays': 'Csomópontok kereséshez',
|
||||
'Search relays': 'Keresési csomópontok',
|
||||
relayInfoBadgeAuth: 'Auth',
|
||||
relayInfoBadgeSearch: 'Keresés',
|
||||
relayInfoBadgePayment: 'Fizetés',
|
||||
|
|
@ -676,6 +676,7 @@ export default {
|
|||
'Hide indirect': 'Közvetettek elrejtése',
|
||||
'Copy note content': 'Jegyzet tartalmának másolása',
|
||||
'Video loop': 'Videó ismétlése',
|
||||
'Automatically replay videos when they end': 'Videók automatikus újrajátszása, amikor véget érnek'
|
||||
'Automatically replay videos when they end': 'Videók automatikus újrajátszása, amikor véget érnek',
|
||||
'Relays used for searching notes (NIP-50)': 'Jegyzetek kereséséhez használt csomópontok (NIP-50)'
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -166,7 +166,7 @@ export default {
|
|||
'Send only to r': 'Invia solo a {{r}}',
|
||||
'Send only to these relays': 'Invia solo a questi relay',
|
||||
Explore: 'Esplora',
|
||||
'Search relays': 'Ricerca relay',
|
||||
'Search relays': 'Relay di ricerca',
|
||||
relayInfoBadgeAuth: 'Autorizzazione',
|
||||
relayInfoBadgeSearch: 'Ricerca',
|
||||
relayInfoBadgePayment: 'Pagamento',
|
||||
|
|
@ -687,6 +687,7 @@ export default {
|
|||
'Hide indirect': 'Nascondi indirette',
|
||||
'Copy note content': 'Copia contenuto della nota',
|
||||
'Video loop': 'Ripetizione video',
|
||||
'Automatically replay videos when they end': 'Riprodurre automaticamente i video quando terminano'
|
||||
'Automatically replay videos when they end': 'Riprodurre automaticamente i video quando terminano',
|
||||
'Relays used for searching notes (NIP-50)': 'Relay utilizzati per cercare le note (NIP-50)'
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -165,7 +165,7 @@ export default {
|
|||
'Send only to r': '{{r}} にのみ送信',
|
||||
'Send only to these relays': 'これらのリレイにのみ送信',
|
||||
Explore: '探索',
|
||||
'Search relays': 'リレイを検索',
|
||||
'Search relays': '検索リレー',
|
||||
relayInfoBadgeAuth: '認証',
|
||||
relayInfoBadgeSearch: '検索',
|
||||
relayInfoBadgePayment: '支払い',
|
||||
|
|
@ -682,6 +682,7 @@ export default {
|
|||
'Hide indirect': '間接通知を非表示',
|
||||
'Copy note content': 'ノート内容をコピー',
|
||||
'Video loop': 'ビデオループ',
|
||||
'Automatically replay videos when they end': 'ビデオ終了時に自動的にリプレイする'
|
||||
'Automatically replay videos when they end': 'ビデオ終了時に自動的にリプレイする',
|
||||
'Relays used for searching notes (NIP-50)': 'ノート検索に使用するリレー (NIP-50)'
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -166,7 +166,7 @@ export default {
|
|||
'Send only to r': '{{r}}에만 전송',
|
||||
'Send only to these relays': '이 릴레이에만 전송',
|
||||
Explore: '탐색',
|
||||
'Search relays': '릴레이 검색',
|
||||
'Search relays': '검색 릴레이',
|
||||
relayInfoBadgeAuth: '로그인 필요',
|
||||
relayInfoBadgeSearch: '검색 지원',
|
||||
relayInfoBadgePayment: '유료',
|
||||
|
|
@ -676,6 +676,7 @@ export default {
|
|||
'Hide indirect': '간접 숨기기',
|
||||
'Copy note content': '노트 내용 복사',
|
||||
'Video loop': '비디오 반복',
|
||||
'Automatically replay videos when they end': '비디오가 끝나면 자동으로 다시 재생'
|
||||
'Automatically replay videos when they end': '비디오가 끝나면 자동으로 다시 재생',
|
||||
'Relays used for searching notes (NIP-50)': '노트 검색에 사용되는 릴레이 (NIP-50)'
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -163,7 +163,7 @@ export default {
|
|||
'Send only to r': 'Wyślij tylko do {{r}}',
|
||||
'Send only to these relays': 'Wyślij tylko do tych transmiterów',
|
||||
Explore: 'Transmitery',
|
||||
'Search relays': 'Wyszukaj transmiter',
|
||||
'Search relays': 'Przekaźniki wyszukiwania',
|
||||
relayInfoBadgeAuth: '✔️',
|
||||
relayInfoBadgeSearch: 'Wyszukiwarka',
|
||||
relayInfoBadgePayment: 'Płatności',
|
||||
|
|
@ -688,6 +688,7 @@ export default {
|
|||
'Hide indirect': 'Ukryj pośrednie',
|
||||
'Copy note content': 'Kopiuj treść notatki',
|
||||
'Video loop': 'Zapętlanie wideo',
|
||||
'Automatically replay videos when they end': 'Automatycznie powtarzaj filmy po zakończeniu'
|
||||
'Automatically replay videos when they end': 'Automatycznie powtarzaj filmy po zakończeniu',
|
||||
'Relays used for searching notes (NIP-50)': 'Przekaźniki używane do wyszukiwania notatek (NIP-50)'
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -166,7 +166,7 @@ export default {
|
|||
'Send only to r': 'Enviar apenas para {{r}}',
|
||||
'Send only to these relays': 'Enviar apenas para estes relays',
|
||||
Explore: 'Explorar',
|
||||
'Search relays': 'Pesquisar relays',
|
||||
'Search relays': 'Relays de busca',
|
||||
relayInfoBadgeAuth: 'Auth',
|
||||
relayInfoBadgeSearch: 'Pesquisar',
|
||||
relayInfoBadgePayment: 'Pagamento',
|
||||
|
|
@ -685,6 +685,7 @@ export default {
|
|||
'Hide indirect': 'Ocultar indiretas',
|
||||
'Copy note content': 'Copiar conteúdo da nota',
|
||||
'Video loop': 'Repetir vídeo',
|
||||
'Automatically replay videos when they end': 'Reproduzir automaticamente os vídeos quando terminarem'
|
||||
'Automatically replay videos when they end': 'Reproduzir automaticamente os vídeos quando terminarem',
|
||||
'Relays used for searching notes (NIP-50)': 'Relays usados para buscar notas (NIP-50)'
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -166,7 +166,7 @@ export default {
|
|||
'Send only to r': 'Enviar apenas para {{r}}',
|
||||
'Send only to these relays': 'Enviar apenas para estes relés',
|
||||
Explore: 'Explorar',
|
||||
'Search relays': 'Pesquisar relés',
|
||||
'Search relays': 'Relés de pesquisa',
|
||||
relayInfoBadgeAuth: 'Auth',
|
||||
relayInfoBadgeSearch: 'Pesquisar',
|
||||
relayInfoBadgePayment: 'Pagamento',
|
||||
|
|
@ -688,6 +688,7 @@ export default {
|
|||
'Hide indirect': 'Ocultar indiretas',
|
||||
'Copy note content': 'Copiar conteúdo da nota',
|
||||
'Video loop': 'Repetir vídeo',
|
||||
'Automatically replay videos when they end': 'Reproduzir automaticamente os vídeos quando terminarem'
|
||||
'Automatically replay videos when they end': 'Reproduzir automaticamente os vídeos quando terminarem',
|
||||
'Relays used for searching notes (NIP-50)': 'Relés usados para pesquisar notas (NIP-50)'
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -168,7 +168,7 @@ export default {
|
|||
'Send only to r': 'Отправить только на {{r}}',
|
||||
'Send only to these relays': 'Отправить только на эти ретрансляторы',
|
||||
Explore: 'Обзор',
|
||||
'Search relays': 'Поиск ретрансляторов',
|
||||
'Search relays': 'Ретрансляторы для поиска',
|
||||
relayInfoBadgeAuth: 'Авторизация',
|
||||
relayInfoBadgeSearch: 'Поиск',
|
||||
relayInfoBadgePayment: 'Платежи',
|
||||
|
|
@ -687,6 +687,7 @@ export default {
|
|||
'Hide indirect': 'Скрыть косвенные',
|
||||
'Copy note content': 'Скопировать содержимое заметки',
|
||||
'Video loop': 'Зацикливание видео',
|
||||
'Automatically replay videos when they end': 'Автоматически воспроизводить видео заново после окончания'
|
||||
'Automatically replay videos when they end': 'Автоматически воспроизводить видео заново после окончания',
|
||||
'Relays used for searching notes (NIP-50)': 'Ретрансляторы для поиска заметок (NIP-50)'
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -163,7 +163,7 @@ export default {
|
|||
'Send only to r': 'ส่งเฉพาะไปยัง {{r}}',
|
||||
'Send only to these relays': 'ส่งเฉพาะไปยังรีเลย์เหล่านี้',
|
||||
Explore: 'สำรวจ',
|
||||
'Search relays': 'ค้นหารีเลย์',
|
||||
'Search relays': 'รีเลย์สำหรับค้นหา',
|
||||
relayInfoBadgeAuth: 'ยืนยันตัวตน',
|
||||
relayInfoBadgeSearch: 'ค้นหา',
|
||||
relayInfoBadgePayment: 'ชำระเงิน',
|
||||
|
|
@ -672,6 +672,7 @@ export default {
|
|||
'Hide indirect': 'ซ่อนทางอ้อม',
|
||||
'Copy note content': 'คัดลอกเนื้อหาโน้ต',
|
||||
'Video loop': 'เล่นวิดีโอซ้ำ',
|
||||
'Automatically replay videos when they end': 'เล่นวิดีโอซ้ำอัตโนมัติเมื่อจบ'
|
||||
'Automatically replay videos when they end': 'เล่นวิดีโอซ้ำอัตโนมัติเมื่อจบ',
|
||||
'Relays used for searching notes (NIP-50)': 'รีเลย์ที่ใช้สำหรับค้นหาโน้ต (NIP-50)'
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -654,6 +654,7 @@ export default {
|
|||
'Hide indirect': '隱藏間接通知',
|
||||
'Copy note content': '複製筆記內容',
|
||||
'Video loop': '影片循環',
|
||||
'Automatically replay videos when they end': '影片播放結束後自動重新播放'
|
||||
'Automatically replay videos when they end': '影片播放結束後自動重新播放',
|
||||
'Relays used for searching notes (NIP-50)': '用於搜尋筆記的伺服器 (NIP-50)'
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -659,6 +659,7 @@ export default {
|
|||
'Hide indirect': '隐藏间接通知',
|
||||
'Copy note content': '复制笔记内容',
|
||||
'Video loop': '视频循环',
|
||||
'Automatically replay videos when they end': '视频播放结束后自动重新播放'
|
||||
'Automatically replay videos when they end': '视频播放结束后自动重新播放',
|
||||
'Relays used for searching notes (NIP-50)': '用于搜索笔记的服务器 (NIP-50)'
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,10 @@ export function getDefaultRelayUrls() {
|
|||
return storage.getDefaultRelayUrls()
|
||||
}
|
||||
|
||||
export function getSearchRelayUrls() {
|
||||
return storage.getSearchRelayUrls()
|
||||
}
|
||||
|
||||
export function checkAlgoRelay(relayInfo: TRelayInfo | undefined) {
|
||||
return relayInfo?.software === 'https://github.com/bitvora/algo-relay' // hardcode for now
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,11 +1,10 @@
|
|||
import { Favicon } from '@/components/Favicon'
|
||||
import NormalFeed from '@/components/NormalFeed'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { SEARCHABLE_RELAY_URLS } from '@/constants'
|
||||
import SecondaryPageLayout from '@/layouts/SecondaryPageLayout'
|
||||
import { toProfileList } from '@/lib/link'
|
||||
import { fetchPubkeysFromDomain, getWellKnownNip05Url } from '@/lib/nip05'
|
||||
import { getDefaultRelayUrls } from '@/lib/relay'
|
||||
import { getDefaultRelayUrls, getSearchRelayUrls } from '@/lib/relay'
|
||||
import { useSecondaryPage } from '@/PageManager'
|
||||
import { useNostr } from '@/providers/NostrProvider'
|
||||
import client from '@/services/client.service'
|
||||
|
|
@ -60,7 +59,7 @@ const NoteListPage = forwardRef(({ index }: { index?: number }, ref) => {
|
|||
setSubRequests([
|
||||
{
|
||||
filter: { search, ...(kinds.length > 0 ? { kinds } : {}) },
|
||||
urls: SEARCHABLE_RELAY_URLS
|
||||
urls: getSearchRelayUrls()
|
||||
}
|
||||
])
|
||||
return
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import DefaultRelaysSetting from '@/components/DefaultRelaysSetting'
|
||||
import SearchRelaysSetting from '@/components/SearchRelaysSetting'
|
||||
import { Input } from '@/components/ui/input'
|
||||
import { Label } from '@/components/ui/label'
|
||||
import { Switch } from '@/components/ui/switch'
|
||||
|
|
@ -47,6 +48,9 @@ const SystemSettingsPage = forwardRef(({ index }: { index?: number }, ref) => {
|
|||
<div className="space-y-2 px-4">
|
||||
<DefaultRelaysSetting />
|
||||
</div>
|
||||
<div className="space-y-2 px-4">
|
||||
<SearchRelaysSetting />
|
||||
</div>
|
||||
</div>
|
||||
</SecondaryPageLayout>
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { ExtendedKind, SEARCHABLE_RELAY_URLS } from '@/constants'
|
||||
import { ExtendedKind } from '@/constants'
|
||||
import {
|
||||
compareEvents,
|
||||
getReplaceableCoordinate,
|
||||
|
|
@ -7,7 +7,7 @@ import {
|
|||
} from '@/lib/event'
|
||||
import { getProfileFromEvent, getRelayListFromEvent } from '@/lib/event-metadata'
|
||||
import { formatPubkey, isValidPubkey, pubkeyToNpub, userIdToPubkey } from '@/lib/pubkey'
|
||||
import { filterOutBigRelays, getDefaultRelayUrls } from '@/lib/relay'
|
||||
import { filterOutBigRelays, getDefaultRelayUrls, getSearchRelayUrls } from '@/lib/relay'
|
||||
import { SmartPool } from '@/lib/smart-pool'
|
||||
import { getPubkeysFromPTags, getServersFromServerTags, tagNameEquals } from '@/lib/tag'
|
||||
import { mergeTimelines } from '@/lib/timeline'
|
||||
|
|
@ -159,7 +159,7 @@ class ClientService extends EventTarget {
|
|||
|
||||
async determineRelaysByFilter(filter: Filter) {
|
||||
if (filter.search) {
|
||||
return SEARCHABLE_RELAY_URLS
|
||||
return getSearchRelayUrls()
|
||||
} else if (filter.authors?.length) {
|
||||
const relayLists = await this.fetchRelayLists(filter.authors)
|
||||
return Array.from(new Set(relayLists.flatMap((list) => list.write.slice(0, 5))))
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import {
|
|||
NOTIFICATION_LIST_STYLE,
|
||||
NSFW_DISPLAY_POLICY,
|
||||
PROFILE_PICTURE_AUTO_LOAD_POLICY,
|
||||
SEARCHABLE_RELAY_URLS,
|
||||
StorageKey,
|
||||
TPrimaryColor
|
||||
} from '@/constants'
|
||||
|
|
@ -66,6 +67,7 @@ class LocalStorageService {
|
|||
private quickReactionEmoji: string | TEmoji = '+'
|
||||
private nsfwDisplayPolicy: TNsfwDisplayPolicy = NSFW_DISPLAY_POLICY.HIDE_CONTENT
|
||||
private defaultRelayUrls: string[] = BIG_RELAY_URLS
|
||||
private searchRelayUrls: string[] = SEARCHABLE_RELAY_URLS
|
||||
private mutedWords: string[] = []
|
||||
private minTrustScore: number = 0
|
||||
private minTrustScoreMap: Record<string, number> = {}
|
||||
|
|
@ -310,6 +312,22 @@ class LocalStorageService {
|
|||
}
|
||||
}
|
||||
|
||||
const searchRelayUrlsStr = window.localStorage.getItem(StorageKey.SEARCH_RELAY_URLS)
|
||||
if (searchRelayUrlsStr) {
|
||||
try {
|
||||
const urls = JSON.parse(searchRelayUrlsStr)
|
||||
if (
|
||||
Array.isArray(urls) &&
|
||||
urls.length > 0 &&
|
||||
urls.every((url) => typeof url === 'string')
|
||||
) {
|
||||
this.searchRelayUrls = urls
|
||||
}
|
||||
} catch {
|
||||
// Invalid JSON, use default
|
||||
}
|
||||
}
|
||||
|
||||
const mutedWordsStr = window.localStorage.getItem(StorageKey.MUTED_WORDS)
|
||||
if (mutedWordsStr) {
|
||||
try {
|
||||
|
|
@ -691,6 +709,15 @@ class LocalStorageService {
|
|||
window.localStorage.setItem(StorageKey.DEFAULT_RELAY_URLS, JSON.stringify(urls))
|
||||
}
|
||||
|
||||
getSearchRelayUrls() {
|
||||
return this.searchRelayUrls
|
||||
}
|
||||
|
||||
setSearchRelayUrls(urls: string[]) {
|
||||
this.searchRelayUrls = urls
|
||||
window.localStorage.setItem(StorageKey.SEARCH_RELAY_URLS, JSON.stringify(urls))
|
||||
}
|
||||
|
||||
getMutedWords() {
|
||||
return this.mutedWords
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue