diff --git a/src/components/Profile/ProfileFeed.tsx b/src/components/Profile/ProfileFeed.tsx index 6800d3b..b87c72e 100644 --- a/src/components/Profile/ProfileFeed.tsx +++ b/src/components/Profile/ProfileFeed.tsx @@ -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 } } ]) diff --git a/src/components/SearchRelaysSetting/index.tsx b/src/components/SearchRelaysSetting/index.tsx new file mode 100644 index 0000000..3d0d650 --- /dev/null +++ b/src/components/SearchRelaysSetting/index.tsx @@ -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(storage.getSearchRelayUrls()) + const [newRelayUrl, setNewRelayUrl] = useState('') + const [newRelayUrlError, setNewRelayUrlError] = useState(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) => { + setNewRelayUrl(e.target.value) + setNewRelayUrlError(null) + } + + const handleRelayUrlInputKeyDown = (event: React.KeyboardEvent) => { + if (event.key === 'Enter') { + event.preventDefault() + saveNewRelayUrl() + } + } + + const resetToDefault = () => { + setRelayUrls(SEARCHABLE_RELAY_URLS) + storage.setSearchRelayUrls(SEARCHABLE_RELAY_URLS) + } + + return ( +
+
+ + +
+
+ {t('Relays used for searching notes (NIP-50)')} +
+
+ {relayUrls.map((url, index) => ( +
+
+ +
{url}
+
+
+ removeRelayUrl(url)} + className="cursor-pointer text-muted-foreground hover:text-destructive" + /> +
+
+ ))} +
+
+ + +
+ {newRelayUrlError &&
{newRelayUrlError}
} +
+ ) +} diff --git a/src/components/SearchResult/index.tsx b/src/components/SearchResult/index.tsx index 823f8d8..70349c4 100644 --- a/src/components/SearchResult/index.tsx +++ b/src/components/SearchResult/index.tsx @@ -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 ( ) diff --git a/src/constants.ts b/src/constants.ts index 32f907d..e75aa3a 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -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/'] diff --git a/src/i18n/locales/ar.ts b/src/i18n/locales/ar.ts index 1ea497f..0413279 100644 --- a/src/i18n/locales/ar.ts +++ b/src/i18n/locales/ar.ts @@ -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)' } } diff --git a/src/i18n/locales/de.ts b/src/i18n/locales/de.ts index a5036ae..8f884ad 100644 --- a/src/i18n/locales/de.ts +++ b/src/i18n/locales/de.ts @@ -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)' } } diff --git a/src/i18n/locales/en.ts b/src/i18n/locales/en.ts index 891a001..7e5558d 100644 --- a/src/i18n/locales/en.ts +++ b/src/i18n/locales/en.ts @@ -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)' } } diff --git a/src/i18n/locales/es.ts b/src/i18n/locales/es.ts index 264f0cd..2ec5a63 100644 --- a/src/i18n/locales/es.ts +++ b/src/i18n/locales/es.ts @@ -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)' } } diff --git a/src/i18n/locales/fa.ts b/src/i18n/locales/fa.ts index 8d4ef6b..9201c6e 100644 --- a/src/i18n/locales/fa.ts +++ b/src/i18n/locales/fa.ts @@ -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)' } } diff --git a/src/i18n/locales/fr.ts b/src/i18n/locales/fr.ts index 163963f..9aed909 100644 --- a/src/i18n/locales/fr.ts +++ b/src/i18n/locales/fr.ts @@ -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)' } } diff --git a/src/i18n/locales/hi.ts b/src/i18n/locales/hi.ts index 2a698d8..82f8f5f 100644 --- a/src/i18n/locales/hi.ts +++ b/src/i18n/locales/hi.ts @@ -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)' } } diff --git a/src/i18n/locales/hu.ts b/src/i18n/locales/hu.ts index f8c6e53..ddd7d22 100644 --- a/src/i18n/locales/hu.ts +++ b/src/i18n/locales/hu.ts @@ -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)' } } diff --git a/src/i18n/locales/it.ts b/src/i18n/locales/it.ts index a3be547..e06b7ef 100644 --- a/src/i18n/locales/it.ts +++ b/src/i18n/locales/it.ts @@ -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)' } } diff --git a/src/i18n/locales/ja.ts b/src/i18n/locales/ja.ts index 063ac42..d27b6e9 100644 --- a/src/i18n/locales/ja.ts +++ b/src/i18n/locales/ja.ts @@ -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)' } } diff --git a/src/i18n/locales/ko.ts b/src/i18n/locales/ko.ts index f8f299f..2bfde05 100644 --- a/src/i18n/locales/ko.ts +++ b/src/i18n/locales/ko.ts @@ -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)' } } diff --git a/src/i18n/locales/pl.ts b/src/i18n/locales/pl.ts index eab8699..19ba0ae 100644 --- a/src/i18n/locales/pl.ts +++ b/src/i18n/locales/pl.ts @@ -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)' } } diff --git a/src/i18n/locales/pt-BR.ts b/src/i18n/locales/pt-BR.ts index f3f4958..cc671e8 100644 --- a/src/i18n/locales/pt-BR.ts +++ b/src/i18n/locales/pt-BR.ts @@ -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)' } } diff --git a/src/i18n/locales/pt-PT.ts b/src/i18n/locales/pt-PT.ts index 0312df8..06e8086 100644 --- a/src/i18n/locales/pt-PT.ts +++ b/src/i18n/locales/pt-PT.ts @@ -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)' } } diff --git a/src/i18n/locales/ru.ts b/src/i18n/locales/ru.ts index 72f166c..3772519 100644 --- a/src/i18n/locales/ru.ts +++ b/src/i18n/locales/ru.ts @@ -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)' } } diff --git a/src/i18n/locales/th.ts b/src/i18n/locales/th.ts index e21913c..74680e4 100644 --- a/src/i18n/locales/th.ts +++ b/src/i18n/locales/th.ts @@ -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)' } } diff --git a/src/i18n/locales/zh-TW.ts b/src/i18n/locales/zh-TW.ts index 7cff1d3..b9e00dc 100644 --- a/src/i18n/locales/zh-TW.ts +++ b/src/i18n/locales/zh-TW.ts @@ -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)' } } diff --git a/src/i18n/locales/zh.ts b/src/i18n/locales/zh.ts index 3100c72..8a96710 100644 --- a/src/i18n/locales/zh.ts +++ b/src/i18n/locales/zh.ts @@ -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)' } } diff --git a/src/lib/relay.ts b/src/lib/relay.ts index fae5270..b34b5e9 100644 --- a/src/lib/relay.ts +++ b/src/lib/relay.ts @@ -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 } diff --git a/src/pages/secondary/NoteListPage/index.tsx b/src/pages/secondary/NoteListPage/index.tsx index e875ee2..1222110 100644 --- a/src/pages/secondary/NoteListPage/index.tsx +++ b/src/pages/secondary/NoteListPage/index.tsx @@ -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 diff --git a/src/pages/secondary/SystemSettingsPage/index.tsx b/src/pages/secondary/SystemSettingsPage/index.tsx index 32ef490..fcd7478 100644 --- a/src/pages/secondary/SystemSettingsPage/index.tsx +++ b/src/pages/secondary/SystemSettingsPage/index.tsx @@ -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) => {
+
+ +
) diff --git a/src/services/client.service.ts b/src/services/client.service.ts index 18f05ad..2b2d902 100644 --- a/src/services/client.service.ts +++ b/src/services/client.service.ts @@ -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)))) diff --git a/src/services/local-storage.service.ts b/src/services/local-storage.service.ts index 8364f68..bd9c788 100644 --- a/src/services/local-storage.service.ts +++ b/src/services/local-storage.service.ts @@ -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 = {} @@ -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 }